Graph batches and fragment slots
spat.graph_compute_batch expresses several lane computations with one body. In this example, two lanes apply ReLU to different four-column slices of one input:
%fragments = spat.graph_compute_batch %lane = 0 to 2 [] = [] (%arg) = (%x) shared_outs(%out) crossbarWeights 0 : [] (tensor<1x8xf32>) -> tensor<2x1x4xf32> {
%offset = affine.apply affine_map<(d0) -> (d0 * 4)>(%lane)
%fragment = tensor.extract_slice %arg[0, %offset] [1, 4] [1, 1] : tensor<1x8xf32> to tensor<1x4xf32>
%v = spat.relu(%fragment) : tensor<1x4xf32> -> tensor<1x4xf32>
spat.in_parallel {
tensor.parallel_insert_slice %v into %out[%lane, 0, 0] [1, 1, 4] [1, 1, 1] : tensor<1x4xf32> into tensor<2x1x4xf32>
}
}
%lane = 0 to 2 gives the lane indices 0 and 1; the upper bound is exclusive. The lane argument has type index, an integer SSA type used here to calculate slice offsets. Lane 0 reads columns 0–3, and lane 1 reads columns 4–7.
Each lane produces a tensor<1x4xf32> fragment. shared_outs(%out) names the body output argument. Inside spat.in_parallel, each insertion publishes one fragment into slot %lane.
The batch produces one SSA result, %fragments, with type tensor<2x1x4xf32>. Its leading dimension counts two fragment publications. The remaining dimensions describe one fragment. For N lanes producing fragments of type F, each graph result has shape [N] + shape(F).
This is a physical fragment collection in the graph IR. It describes SSA publications before memory allocation or core placement. Its slots need an explicit mapping to logical tensor coordinates. For this example, spat.blueprint places the two fragments next to each other:
%logical = spat.blueprint fragments(%fragments) layout nchw physical fragmented offsets [0, 0, 0, 4] sizes [1, 4, 1, 4] map explicit mode fragment_assembly operandIndices [0, 0] sourceSlots [0, 1] sourceOffsets [0, 0] strides [1, 1, 1, 1] conflict disjoint coverage complete : (tensor<2x1x4xf32>) -> tensor<1x8xf32>
The flattened offsets and sizes lists contain one rank-2 destination slice per fragment:
| Source slot | Logical offset | Slice size | Logical columns |
| 0 | [0, 0] | [1, 4] | 0–3 |
| 1 | [0, 4] | [1, 4] | 4–7 |
The result %logical has shape 1×8 because this descriptor defines that reconstruction. The graph fragment invariant specifies publication and reconstruction requirements for every graph batch result.
spat.add_plan — Plan element-wise addition
%y = "spat.add_plan"(%a, %b) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>, tensor<1x2x3x4xf32>) -> tensor<1x2x3x4xf32>
- Inputs
- lhs (
%a) and rhs (%b): matching NCHW tensors, each with shape 1×2×3×4 in the example.
- Outputs
- output (
%y): the logical result with the printed shape. Physical representation is selected through layout planning.
- Attributes
logicalLayout: required logical coordinate order, nchw in the example. See planning operations and attributes.
- Semantics
- Adds corresponding elements. In the example, each of the 24 output elements is the sum of two input elements.
- Constraints
- Verified: static ranked rank-4 tensors with identical complete types; nchw layout.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = "spat.bias_add_plan"(%x, %bias) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>, tensor<2xf32>) -> tensor<1x2x3x4xf32>
- Inputs
- input (
%x): an NCHW tensor with shape 1×2×3×4. bias (%bias): two channel bias values in the example.
- Outputs
- output (
%y): the logical result with the printed shape. Physical representation is selected through layout planning.
- Attributes
logicalLayout: required logical coordinate order, nchw in the example. See planning operations and attributes.
- Semantics
- Adds a scalar or per-channel bias across the input spatial coordinates. Here, bias[0] applies to channel 0 and bias[1] applies to channel 1.
- Constraints
- Verified: static ranked tensors; input/output complete types identical and rank 4; bias element type matches; nchw layout. Allowed bias shapes: scalar rank 0, [C], [1,C], [1,C,1,1]. Other broadcasting shapes are rejected.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.block_yield — Structural scheduled block terminator
spat.block_yield :
- Inputs
- outputs: optional values forwarded to a successor block. A successor is written as
next ^bb after those values. The example has neither values nor a successor.
- Outputs
- No SSA results on this operation.
- Attributes
- None required.
- Semantics
- Ends a structural scheduled block. With a successor, it forwards the operands to that block. The shown empty form ends the structural sequence. The parent restriction below limits its use.
- Constraints
- Verified: parent is
spat.scheduled_compute; at most one successor; forwarded operand count and types match successor arguments. However, the scheduled-compute verifier requires one block ending in spat.yield. Therefore, this declared structural terminator is not accepted in an ordinarily verified scheduled_compute body.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.blueprint — Describe logical reconstruction
%logical = spat.blueprint fragments(%y) layout nchw physical fragmented offsets [0, 0, 0, 4] sizes [1, 4, 1, 4] map explicit mode fragment_assembly operandIndices [0, 0] sourceSlots [0, 1] sourceOffsets [0, 0] strides [1, 1, 1, 1] conflict disjoint coverage complete : (tensor<2x1x4xf32>) -> tensor<1x8xf32>
- Inputs
- input (
%y): the first fragment collection. Optional fragments add further collections to the printed fragments(...) list. Here, one collection contains two 1×4 fragments. See the publication and reconstruction example.
- Outputs
- output (
%logical): the reconstructed logical tensor. Here, the descriptor places two 1×4 source fragments into a 1×8 result. The reconstruction table shows their destination coordinates.
- Attributes
layout and physical print logicalLayout and physicalLayout. offsets and sizes print flattened destination slices in logical element coordinates. map names indexMap. mode selects physical_view or fragment_assembly. In fragment_assembly, operandIndices selects source collections, and sourceSlots selects their publications. sourceOffsets gives flat element offsets within each slot. strides prints fragmentStrides. conflict and coverage print conflictPolicy and coveragePolicy, which control destination overlap and coverage. The mapping table expands the example destination geometry.
- Semantics
- Maps fragment publications to logical tensor regions. Here, source slot 0 supplies columns 0–3, and slot 1 supplies columns 4–7. Reversing sourceSlots changes that placement while preserving the collection type. The fragment_assembly mode records explicit source and destination geometry. The physical_view mode describes a collection with one leading slot dimension. Reconstruction describes placement; arithmetic combinations require a following compute operation. Later lowering determines storage and any required data movement.
- Constraints
Verified geometry. Forms with destination geometry require offset and size lists of equal length, divisible by the positive output rank. Each destination slice stays within logical bounds. Contiguous row-major mappings also require canonical geometry and physical ordering.
physical_view. Input and output are static ranked tensors, and input rank equals output rank plus one. Layouts are known. This form uses one input collection, empty offset/size arrays, and no assembly metadata.
fragment_assembly. Physical operands are static ranked tensors with rank equal to output rank plus one. Operand indices and source slots are valid, and source element offsets are nonnegative. Source slices fit each fragment and match the output element type. Every operand contributes at least one fragment.
Destination strides are one, and conflictPolicy is disjoint. coveragePolicy can be complete or partial. Destination slices are disjoint; complete coverage fills the entire static output. Arithmetic combinations belong in compute operations, as described under graph reconstruction.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = spat.channel_receive channel %channel from %source to %target : tensor<1x4xf32>
- Inputs
- channelId (
%channel), sourceCoreId (%source), and targetCoreId (%target): index SSA operands for this transfer.
- Outputs
- output: received payload.
- Attributes
- None required. The channel and endpoint IDs appear as SSA operands in the example.
- Semantics
- Receives the complete payload from the paired
spat.channel_send. The result type specifies its shape and element type. Target lowering implements the pair with core-addressed SEND/RECV instructions.
- Constraints
- ODS checks shaped and index operand types; this operation has no custom verifier. Scheduling and communication verification check endpoint pairing, payload types and sizes, core mappings, transfer order, and deadlock freedom. ID expressions must resolve statically at emission. Lane bodies use these scalar channel operations.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.channel_send %x channel %channel from %source to %target : tensor<1x4xf32>
- Inputs
- input (
%x): the complete payload. channelId (%channel), sourceCoreId (%source), and targetCoreId (%target): index SSA operands for this transfer.
- Outputs
- No SSA results; communication effect.
- Attributes
- None required. The channel and endpoint IDs appear as SSA operands in the example.
- Semantics
- Sends the complete payload on a logical channel. Pair it with
spat.channel_receive using the same channel and endpoints. Target lowering implements the pair with core-addressed SEND/RECV instructions.
- Constraints
- ODS checks shaped and index operand types; this operation has no custom verifier. Scheduling and communication verification check endpoint pairing, payload types and sizes, core mappings, transfer order, and deadlock freedom. ID expressions must resolve statically at emission. Lane bodies use these scalar channel operations.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.concat — Join tensors along one axis
%y = spat.concat axis 1 %a, %b : (tensor<1x2xf32> x2) -> tensor<1x4xf32>
- Inputs
- inputs (
%a, %b): ordered shaped values, each 1×2 in the example. The compact type suffix x2 is explained under multiple results and compact lists.
- Outputs
- output: concatenation of the inputs along axis.
- Attributes
- axis: required i64 printed after axis; cannot also be supplied in the attribute dictionary.
- Semantics
- Joins inputs in operand order along axis. Here, axis 1 joins two 1×2 tensors into one 1×4 tensor. Other dimensions preserve their coordinates.
- Constraints
- Verified: at least one input and an axis in [0,outputRank). Producers must normalize negative axes before this operation. Input ranks and element types match the output. Static non-axis dimensions match. When all axis sizes are static, their sum equals the output axis size. Dynamic dimensions receive the comparisons that can be established statically. The parser checks expanded input and type counts. Tensor and memref container kinds need not all match in the Spatial verifier. Code generation requires static addressable storage.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = "spat.concat_plan"(%a, %b) <{axis = 1 : i64, logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>, tensor<1x2x3x4xf32>) -> tensor<1x4x3x4xf32>
- Inputs
- inputs (
%a, %b): NCHW tensors in concatenation order. Each example input has two channels and spatial shape 3×4.
- Outputs
- output (
%y): the logical result with the printed shape. Physical representation is selected through layout planning.
- Attributes
logicalLayout: required logical coordinate order, nchw in the example. See planning operations and attributes. axis = 1 : i64 selects the channel dimension.
- Semantics
- Joins the inputs along the channel axis. The example places the two channels of %a before the two channels of %b.
- Constraints
- Verified: nchw layout, a static rank-4 output, axis 1, and at least one input. Input ranks and element types match the output. Static non-axis dimensions match. When input axis sizes are static, their sum equals the output axis size. Layout selection and lowering can require further static geometry.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = "spat.conv2d_plan"(%x, %weight) <{pads = array<i64: 0, 0, 0, 0>, strides = array<i64: 1, 1>, dilations = array<i64: 1, 1>, group = 1 : i64, logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x4x4xf32>, tensor<3x2x3x3xf32>) -> tensor<1x3x2x2xf32>
- Inputs
- input (
%x): shape [N,Cin,H,W], here [1,2,4,4]. weight (%weight): shape [Cout,Cin/group,Kh,Kw], here [3,2,3,3]. An optional bias supplies output-channel offsets.
- Outputs
- output (
%y): the logical result with the printed shape. Physical representation is selected through layout planning.
- Attributes
logicalLayout: required logical coordinate order, nchw in the example. See planning operations and attributes. pads: [top,left,bottom,right] in elements. strides and dilations: [height,width] element steps. The array<i64: ...> notation stores these integer lists. group: number of channel groups; 1 in the example.
- Semantics
- Computes NCHW cross-correlation. For each spatial axis, output = floor((input+padBegin+padEnd-dilation*(kernel-1)-1)/stride)+1. The example uses 3×3 kernels and zero padding, reducing each spatial axis from four to two. The three filters combine both input channels into three output channels.
- Constraints
- Verified: rank-4 tensor input, weight, and output; matching element types; four pads; two strides; two dilations; group>=1; known logical layout. Optional bias is ranked and has a matching element type. Assumed: positive strides and dilations, valid channel groups, compatible bias shape, and output dimensions that satisfy the formula above. Selected lowering also requires supported static geometry.
Sources: definition, assembly, verifiers, arithmetic lowering.
The custom form below appears in compiler dumps. Its printer omits source block-argument names, so use the explicit generic form below for parseable input.
%y = spat.deferred_communication %x : (tensor<1x4xf32>) -> tensor<1x4xf32> {
spat.yield %arg0 : tensor<1x4xf32>
}
- Inputs
- sources (
%x): published shaped values bound to source arguments in the body. The example body refers to its source as %arg0. Grouped specialization adds a final index argument.
- Outputs
- One SSA payload value. The ordinary form returns the body fragment type. Grouped specialization adds a leading dimension for specialization cases, as shown below. This count is independent of graph lanes and physical cores.
- Attributes
specialization_count: optional positive i64, default 1. Values greater than one add a body index argument and a result dimension for specialization cases.
- Semantics
- Records source selection and constant shaping for a scheduled consumer. Here, the body forwards its complete source through
spat.yield. Later phases use this body to derive routing and realize the transfer. For several sources, spat.deferred_source_select records which source a specialization uses.
- Constraints
- Verified: at least one source; one body block; exact source argument types; specialization argument is index when count>1. The body ends in
spat.yield with exactly one value. Ordinary yield/output types match. Grouped output and fragment are static ranked tensors, with output shape [specialization_count]+fragmentShape and matching element types. Lowering requires statically analyzable body shaping and source selection.
For example, two specialization cases can select different sources. Generic assembly declares the source arguments and the case index explicitly:
%cases = "spat.deferred_communication"(%a, %b) ({
^bb0(%a0: tensor<1x4xf32>, %b0: tensor<1x4xf32>, %case: index):
%selected = spat.deferred_source_select %case of %a0, %b0 : tensor<1x4xf32>
spat.yield %selected : tensor<1x4xf32>
}) {specialization_count = 2 : i64} : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<2x1x4xf32>
Case 0 selects %a, and case 1 selects %b. Each case yields a 1×4 fragment. The result shape 2×1×4 counts those two cases. Print this explicit form with onnx-mlir-opt --maccel=PIM --mlir-print-op-generic.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = spat.deferred_source_select %selector of %a, %b : tensor<1x4xf32>
- Inputs
- selector (
%selector): an index SSA value. sources (%a, %b): the ordered, nonempty source list.
- Outputs
- output: selected source value of the common source type.
- Attributes
- None required.
- Semantics
- Selects sources[selector] during compile-time communication planning. In the example, selector 0 chooses %a and selector 1 chooses %b. The enclosing
spat.deferred_communication or scheduled compute supplies a statically analyzable selector.
- Constraints
- Verified: index selector; nonempty sources; all source and output types match. Parent must be
spat.deferred_communication, spat.scheduled_compute, or spat.scheduled_compute_batch. Producer assumption: selector is statically analyzable and in [0,sourceCount). The operation verifier does not check selector bounds.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = "spat.flatten_plan"(%x) <{axis = 1 : i64, logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>) -> tensor<1x24xf32>
- Inputs
- input (
%x): a logical tensor, here with NCHW shape 1×2×3×4.
- Outputs
- output (
%y): the logical result with the printed shape. Physical representation is selected through layout planning.
- Attributes
logicalLayout: required logical coordinate order, nchw in the example. See planning operations and attributes. axis = 1 : i64 selects the division between the two dimension products.
- Semantics
- Preserves logical element order while combining dimensions around axis. The dimensions before axis form the first output dimension; the remaining dimensions form the second. Here, axis 1 produces shape 1×24. Fragmented inputs require the reconstruction described under compute input representations.
- Constraints
- Verified: static ranked tensor input, rank-2 output, matching element types, positive dimensions, and nchw layout. The normalized axis lies in [0,inputRank], and both output dimensions match their corresponding dimension products. Producers must preserve logical order through the input representation mapping.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = "spat.global_average_pool_plan"(%x) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>) -> tensor<1x2x1x1xf32>
- Inputs
- input (
%x): an NCHW tensor, here with two channels and spatial shape 3×4.
- Outputs
- output (
%y): the logical result with the printed shape. Physical representation is selected through layout planning.
- Attributes
logicalLayout: required logical coordinate order, nchw in the example. See planning operations and attributes.
- Semantics
- Averages all H×W elements independently for each channel. The example averages 12 elements per channel and returns shape 1×2×1×1.
- Constraints
- Verified: static rank-4 tensors, matching element types, batch dimension 1, matching input/output channels, output H=W=1, nchw layout. Nonempty spatial domains and supported numeric element types remain producer/lowering requirements.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = spat.graph_compute [] = [] (%arg) = (%x) crossbarWeights 0 : [] (tensor<1x4xf32>) -> tensor<1x4xf32> {
%v = spat.relu(%arg) : tensor<1x4xf32> -> tensor<1x4xf32>
spat.yield %v : tensor<1x4xf32>
}
- Inputs
- inputs (
%x): runtime values bound to body arguments, here %arg. The empty square brackets declare no weights in this example. Weighted bindings are shown under compute regions and bindings. Input coordinates follow the producer representation.
- Outputs
- output (
%y): the value returned by spat.yield, with the same complete type. Here, the body returns one 1×4 tensor. The scalar compute introduces no fragment-slot dimension.
- Attributes
coreId: optional placement ID. crossbarWeights: the derived count of distinct resident weights used in the body; see compute regions and bindings.
- Semantics
- Describes a graph computation before scheduling. The example applies
spat.relu to the bound input and returns its result through spat.yield. Weight and input bindings define dataflow; memory allocation occurs later.
- Constraints
Verified: a nonempty body with weight arguments followed by input arguments in each block. Argument types exactly match operand types. Terminator values match the result count and complete types, including encodings. A single-block body must use every input argument.
Weights must be compile-time computable. Nonconstant external captures require explicit input bindings, except recorded deferred communication sources. Direct capture of another compute result is rejected. Graph regions can contain structural blocks; scheduling must resolve their dependencies.
Target lowering and emission check hardware capacity for the selected target.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = spat.graph_compute_batch %lane = 0 to 2 [] = [] (%arg) = (%x) shared_outs(%out) crossbarWeights 0 : [] (tensor<1x8xf32>) -> tensor<2x1x4xf32> {
%offset = affine.apply affine_map<(d0) -> (d0 * 4)>(%lane)
%fragment = tensor.extract_slice %arg[0, %offset] [1, 4] [1, 1] : tensor<1x8xf32> to tensor<1x4xf32>
%v = spat.relu(%fragment) : tensor<1x4xf32> -> tensor<1x4xf32>
spat.in_parallel {
tensor.parallel_insert_slice %v into %out[%lane, 0, 0] [1, 1, 4] [1, 1, 1] : tensor<1x4xf32> into tensor<2x1x4xf32>
}
}
- Inputs
- input (
%x): one logical 1×8 tensor bound as %arg for both lanes. The lane index is the first body argument. Each weight or input binding preserves its exact operand type. Bodies may select several slices or weight tiles and perform reductions. See input representations for other supported coordinate forms.
- Outputs
- output (
%y): one SSA collection with two fragment slots, each containing a 1×4 tensor. In general, each result has shape [laneCount]+shape(fragment). shared_outs(%out) declares the corresponding body argument without an external destination operand. Resultful bodies publish through spat.in_parallel; resultless bodies end with an empty spat.yield.
- Attributes
laneCount: positive i32 printed as the exclusive upper bound of the lane range. coreIds: optional dense i32 placement list with one distinct nonnegative core ID per lane. crossbarWeights: a derived resident-weight count. The scheduling example distinguishes lanes and cores.
- Semantics
- Runs the same body for each graph lane. Lane 0 applies ReLU to columns 0–3; lane 1 applies ReLU to columns 4–7. Each publishes one 1×4 fragment into the 2×1×4 result. The graph batch example shows how
spat.blueprint reconstructs the logical 1×8 tensor. A statically analyzable slot permutation can separate publication order from lane order. Shared SSA bindings determine dataflow; allocation and placement remain later decisions.
- Constraints
- Verified: positive laneCount; nonempty body; argument order is lane, weights, inputs, outputs. All resource and output argument types match their outer types exactly. Weights are compile-time computable and nonconstant captures are rejected. Optional coreIds have length laneCount, distinct nonnegative entries. Resultful blocks end in
spat.in_parallel; resultless blocks end in spat.yield with no values. Graph contract: one equal-type fragment publication per lane per result; result shape [N]+shape(F), offsets [slot,0,...], sizes [1]+shape(F), unit strides. Direct extract_slice operations require static ranked tensors, static sizes, unit strides, and supported static or lane-dependent offsets. The graph fragment contract defines construction and reconstruction requirements.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.host_store_sync %x from %source to %target host_offset %offset event %event : tensor<1x4xf32>
- Inputs
- input (
%x): the tensor payload. sourceCoreId (%source), targetCoreId (%target), hostOffset (%offset), and eventRegister (%event): index SSA operands.
- Outputs
- No SSA results.
- Attributes
- None required. The hostOffset operand gives the byte offset within the pipeline host buffer.
- Semantics
- Stores the payload at hostOffset, then signals its availability to the consumer. Lowers to
pim.memcp_dh followed by pim.sync. The matching spat.host_wait_load waits before reading this host slot.
- Constraints
- ODS checks index operand types; this operation has no custom verifier. Operands must resolve statically at emission. Register allocation and transfer planning must satisfy the signal/wait contract. The synchronization invariant requires exact counts, separate event roles, host-slot lifetime protection, and restart ordering. These requirements prevent overshoot, register collisions, missing signals, and deadlock. Assumed: source placement is correct; the host buffer exists; the static payload fits its host slice. Before the next overwrite, the writer must receive the FREE signals for all readers.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = spat.host_wait_load from %source to %target host_offset %offset event %event count %count : tensor<1x4xf32>
- Inputs
- sourceCoreId (
%source), targetCoreId (%target), hostOffset (%offset), eventRegister (%event), and waitValue (%count): index SSA operands.
- Outputs
- output: payload loaded into local storage.
- Attributes
- None required. The hostOffset operand gives the byte offset within the pipeline host buffer.
- Semantics
- Waits for the READY event from
spat.host_store_sync, then loads the host payload. Lowers to pim.wait followed by pim.memcp_hd. For repeated execution, reader-entry FREE signals protect host-slot reuse, as described in the synchronization contract.
- Constraints
- ODS checks index operand types; this operation has no custom verifier. Operands must resolve statically at emission. Register allocation and transfer planning must satisfy the signal/wait contract. The synchronization invariant requires exact counts, separate event roles, host-slot lifetime protection, and restart ordering. These requirements prevent overshoot, register collisions, missing signals, and deadlock. Assumed: a matching writer uses the same host offset, complete payload type and size, and event count. The host data must remain live until the read completes.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.in_parallel {
tensor.parallel_insert_slice %v into %out[%lane, 0, 0] [1, 1, 4] [1, 1, 1] : tensor<1x4xf32> into tensor<2x1x4xf32>
}
- Inputs
- The nested insertion reads
%v, a 1×4 lane fragment, and targets %out, the parent batch output argument. These are operands of tensor.parallel_insert_slice; spat.in_parallel has no direct operands.
- Outputs
- The enclosed insertions publish into the parent batch results. This example fills one slot of a 2×1×4 graph result. See the complete graph batch example.
- Attributes
- None required.
- Semantics
- Terminates a resultful batch body with its lane publications. The example inserts one fragment at [%lane,0,0] with size [1,1,4] and unit strides. For graph batches, these are physical publication slots. A following
spat.blueprint supplies logical placement; arithmetic reductions belong in compute operations.
- Constraints
- Verified: the parent is spat.graph_compute_batch or spat.scheduled_compute_batch with results. The region contains only tensor.parallel_insert_slice operations targeting parent output arguments. Tensors and insertion sizes are static, and strides are one. Offsets must be supported static or lane expressions. Graph publications follow the fragment-slot contract. Independent lanes must write disjoint destinations.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = "spat.materialize_layout"(%x) <{sourcePhysicalLayout = #spat<physical_layout dense_nchw>, targetPhysicalLayout = #spat<physical_layout nhwc_row_strip>, logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>) -> tensor<1x2x3x4xf32>
- Inputs
- input (
%x): a logical tensor in sourcePhysicalLayout. The example uses logical NCHW shape 1×2×3×4.
- Outputs
- output (
%y): the logical result with the printed shape. Physical representation is selected through layout planning.
- Attributes
logicalLayout: required logical coordinate order, nchw in the example. See planning operations and attributes. sourcePhysicalLayout and targetPhysicalLayout identify the source and destination storage layouts. Supported conversions are listed in the layout reference.
- Semantics
- Converts storage from sourcePhysicalLayout to targetPhysicalLayout while preserving logical values. Here, dense_nchw becomes nhwc_row_strip. The logical shape stays 1×2×3×4. This explicit conversion also forms a boundary for physical layout planning.
- Constraints
- Verified: ranked tensor input and output, matching element types, and known logical and physical layouts. Physical enums are dense_nchw, nchw_row_strip, nhwc_row_strip, and fragmented. Lowering must implement the selected source/target conversion and preserve logical shape. See the supported conversion list.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = "spat.max_pool2d_plan"(%x) <{kernelShape = array<i64: 2, 2>, pads = array<i64: 0, 0, 0, 0>, strides = array<i64: 2, 2>, dilations = array<i64: 1, 1>, logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x4x4xf32>) -> tensor<1x2x2x2xf32>
- Inputs
- input (
%x): an NCHW tensor, here with two channels and spatial shape 4×4.
- Outputs
- output (
%y): the logical result with the printed shape. Physical representation is selected through layout planning.
- Attributes
logicalLayout: required logical coordinate order, nchw in the example. See planning operations and attributes. kernelShape: [height,width] window size in elements. pads: [top,left,bottom,right] in elements. strides and dilations: [height,width] element steps. The array<i64: ...> notation stores these integer lists.
- Semantics
- Takes the maximum over each spatial window independently within each channel. The example uses 2×2 windows with stride 2 and returns a 2×2 spatial result. Dilations determine the sample spacing within each window.
- Constraints
- Verified: static rank-4 tensors, matching element types and batch/channel dimensions, and nchw layout. Kernel, stride, and dilation arrays each have two entries; pads has four. Assumed: positive window parameters, correct spatial output dimensions, and padding supported by lowering. The single result contains pooled values.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.relu — Element-wise ReLU
%y = spat.relu(%x) : tensor<1x4xf32> -> tensor<1x4xf32>
- Inputs
- input (
%x): the complete computation slice, here a 1×4 tensor.
- Outputs
- output (
%y): one value per input element, here a 1×4 tensor.
- Attributes
- None required.
- Semantics
- y[i] = max(x[i],0). The formula applies independently to all four elements in this example.
- Constraints
- ODS checks shaped operand and result types; this operation has no custom verifier. Assumed: matching input/output shapes and compatible numeric element types. The code generation requirements apply to static storage and backend element types.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = "spat.relu_plan"(%x) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>) -> tensor<1x2x3x4xf32>
- Inputs
- input (
%x): a logical tensor, here with NCHW shape 1×2×3×4.
- Outputs
- output (
%y): the logical result with the printed shape. Physical representation is selected through layout planning.
- Attributes
logicalLayout: required logical coordinate order, nchw in the example. See planning operations and attributes.
- Semantics
- Applies y[i] = max(x[i],0) independently to each element. Logical coordinates and shape are preserved.
- Constraints
- Verified: ranked tensor input and output, matching element types, and known logical layout. Assumed: the producer preserves the element-wise shape. Shape equality is not independently checked by this verifier. The selected layout and lowering must support the shape.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = "spat.resize_nearest_plan"(%x) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x2x2xf32>) -> tensor<1x2x4x4xf32>
- Inputs
- input (
%x): an NCHW tensor, here with two channels and spatial shape 2×2.
- Outputs
- output (
%y): the logical result with the printed shape. Physical representation is selected through layout planning.
- Attributes
logicalLayout: required logical coordinate order, nchw in the example. See planning operations and attributes.
- Semantics
- Uses nearest-neighbor sampling with asymmetric coordinates. The result shape supplies the target spatial sizes. The example expands each 2×2 plane to 4×4; input/output scale determines the source coordinates.
- Constraints
- Verified: static rank-4 tensors, matching element types, positive dimensions, and nchw layout. Assumed: batch and channel dimensions are preserved. Lowering must support the nearest-neighbor sampling and asymmetric coordinates described above.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = spat.scheduled_compute [] = [] (%arg) = (%x) crossbarWeights 0 : [] (tensor<1x4xf32>) -> tensor<1x4xf32> {
%v = spat.relu(%arg) : tensor<1x4xf32> -> tensor<1x4xf32>
spat.yield %v : tensor<1x4xf32>
}
- Inputs
- inputs (
%x): runtime operands bound to body arguments, here %arg. Square brackets bind constant weight resources when present. See compute regions and bindings.
- Outputs
- output (
%y): the value returned by spat.yield, with the same complete type. The example returns a 1×4 ReLU result.
- Attributes
coreId: optional placement ID. crossbarWeights: the derived count of distinct resident weights used in the body; see compute regions and bindings.
- Semantics
- Describes a computation after scheduling. The example applies
spat.relu on its assigned core. Inputs become available through local SSA forwarding or explicit spat.channel_send and spat.channel_receive operations.
- Constraints
Verified: a nonempty body with weight arguments followed by input arguments in each block. Argument types exactly match operand types. Terminator values match the result count and complete types, including encodings. A single-block body must use every input argument.
Weights must be compile-time computable. Nonconstant external captures require explicit input bindings, except recorded deferred communication sources. Direct capture of another compute result is rejected. Scheduled scalar regions require exactly one block ending in spat.yield.
Target lowering and emission check hardware capacity for the selected target.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = spat.scheduled_compute_batch %lane = 0 to 2 [] = [] (%arg) = (%x) shared_outs(%out) crossbarWeights 0 : [] (tensor<2x1x4xf32>) -> tensor<2x1x4xf32> {
%fragment = tensor.extract_slice %arg[%lane, 0, 0] [1, 1, 4] [1, 1, 1] : tensor<2x1x4xf32> to tensor<1x4xf32>
%v = spat.relu(%fragment) : tensor<1x4xf32> -> tensor<1x4xf32>
spat.in_parallel {
tensor.parallel_insert_slice %v into %out[%lane, 0, 0] [1, 1, 4] [1, 1, 1] : tensor<1x4xf32> into tensor<2x1x4xf32>
}
}
- Inputs
- input (
%x): a 2×1×4 collection bound as %arg. Each lane extracts one 1×4 fragment in this example. Weight and input bindings preserve exact types; other input shapes follow the body accesses. See input representations.
- Outputs
- output (
%y): one SSA result with body argument %out. Here, lane insertions fill a 2×1×4 result. Other result shapes follow scheduled execution and need not begin with laneCount. Resultful bodies use spat.in_parallel; resultless bodies use an empty spat.yield.
- Attributes
laneCount: positive i32 printed as the exclusive upper bound of the lane range. coreIds: optional dense i32 placement list with one distinct nonnegative core ID per lane. crossbarWeights: a derived resident-weight count. The scheduling example distinguishes lanes and cores.
- Semantics
- Groups equivalent scheduled computations in one body. Here, each lane reads a fragment, applies ReLU, and publishes the updated fragment. Scheduled lane placement determines which core executes each instance. Local forwarding and explicit communication determine input availability.
- Constraints
- Verified: positive laneCount; nonempty body; argument order is lane, weights, inputs, outputs. All resource and output argument types match their outer types exactly. Weights are compile-time computable and nonconstant captures are rejected. Optional coreIds have length laneCount, distinct nonnegative entries. Resultful blocks end in
spat.in_parallel; resultless blocks end in spat.yield with no values. Scheduled batches skip the graph-only extract-slice offset restriction. Spatial-to-PIM currently requires channelized, resultless scheduled batches with an empty yield.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = spat.sigmoid(%x) : tensor<1x4xf32> -> tensor<1x4xf32>
- Inputs
- input (
%x): the complete computation slice, here a 1×4 tensor.
- Outputs
- output (
%y): one value per input element, here a 1×4 tensor.
- Attributes
- None required.
- Semantics
- y[i] = 1/(1+exp(-x[i])). The formula applies independently to all four elements in this example.
- Constraints
- ODS checks shaped operand and result types; this operation has no custom verifier. Assumed: matching input/output shapes and compatible numeric element types. The code generation requirements apply to static storage and backend element types.
Sources: definition, assembly, verifiers, arithmetic lowering.
%y = "spat.silu_plan"(%x) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>) -> tensor<1x2x3x4xf32>
- Inputs
- input (
%x): a logical tensor, here with NCHW shape 1×2×3×4.
- Outputs
- output (
%y): the logical result with the printed shape. Physical representation is selected through layout planning.
- Attributes
logicalLayout: required logical coordinate order, nchw in the example. See planning operations and attributes.
- Semantics
- Applies y[i] = x[i]/(1+exp(-x[i])) independently to each element. Logical coordinates and shape are preserved.
- Constraints
- Verified: ranked tensor input and output, matching element types, and known logical layout. Assumed: the producer preserves the element-wise shape. Shape equality is not independently checked by this verifier. The selected layout and lowering must support the shape.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.softmax — Softmax of a complete slice
%y = spat.softmax(%x) : tensor<1x4xf32> -> tensor<1x4xf32>
- Inputs
- input (
%x): the complete computation slice, here a 1×4 tensor.
- Outputs
- output (
%y): one value per input element, here a 1×4 tensor.
- Attributes
- None required.
- Semantics
- y[i] = exp(x[i])/sum_j exp(x[j]). The sum covers all four input elements in this example.
- Constraints
- ODS checks shaped operand and result types; this operation has no custom verifier. Assumed: identical input/output shapes and compatible numeric element types. The whole input forms one normalization domain. Producers must extract separate slices before this operation for axis-specific softmax. Numerical stability depends on the backend. Final PIM code generation requires static storage and supported element types.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.sync — Signal an event register
spat.sync %target event %event
- Inputs
- targetCoreId (
%target): the destination core ID. eventRegister (%event): its local event-register index. Both are index SSA operands.
- Outputs
- No SSA results.
- Attributes
- None required.
- Semantics
- Increments the selected event register on the target core. Lowers to
pim.sync; the matching spat.wait executes on that target. See the paired signal and wait examples.
- Constraints
- ODS checks index operand types; this operation has no custom verifier. Operands must resolve statically at emission. Register allocation and transfer planning must satisfy the signal/wait contract. The synchronization invariant requires exact counts, separate event roles, host-slot lifetime protection, and restart ordering. These requirements prevent overshoot, register collisions, missing signals, and deadlock.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.vadd — Element-wise addition
%y = spat.vadd %a, %b : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- lhs (
%a) and rhs (%b): shaped numeric operands, each 1×4 in the example.
- Outputs
- output (
%y): the element-wise result, here with shape 1×4.
- Attributes
- None required.
- Semantics
- y[i] = lhs[i] + rhs[i], independently for every element in the supplied slice.
- Constraints
- Verified: lhs, rhs, and output have exactly the same complete type. Producers must expand any scalar or ONNX broadcast to matching shapes before this operation. Code generation requires static sizes and compatible numeric data; see code generation requirements.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.vavg — Average a complete slice
%y = spat.vavg(%x) : tensor<1x4xf32> -> tensor<1x1xf32>
- Inputs
- input (
%x): the complete computation slice, here a 1×4 tensor.
- Outputs
- output (
%y): one average value in a 1×1 tensor.
- Attributes
- None required.
- Semantics
- y[0,0] = sum(x)/number_of_elements(x). The sum covers all four input elements in this example.
- Constraints
- ODS checks shaped operand and result types; this operation has no custom verifier. Assumed: a nonempty input and a one-element output with compatible element type, conventionally 1×1. The operation averages the whole input slice. Final PIM code generation requires static storage and supported element types.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.vmax — Element-wise maximum
%y = spat.vmax %a, %b : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- lhs (
%a) and rhs (%b): shaped numeric operands, each 1×4 in the example.
- Outputs
- output (
%y): the element-wise result, here with shape 1×4.
- Attributes
- None required.
- Semantics
- y[i] = max(lhs[i], rhs[i]), independently for every element in the supplied slice.
- Constraints
- Verified: lhs, rhs, and output have exactly the same complete type. Producers must expand broadcasts to matching shapes before this operation. The code generation requirements apply to static sizes and numeric types.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.vmul — Element-wise multiplication
%y = spat.vmul %a, %b : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- lhs (
%a) and rhs (%b): shaped numeric operands, each 1×4 in the example.
- Outputs
- output (
%y): the element-wise result, here with shape 1×4.
- Attributes
- None required.
- Semantics
- y[i] = lhs[i] * rhs[i], independently for every element in the supplied slice.
- Constraints
- ODS checks shaped operand types; this operation has no custom shape verifier. Lowering to
pim.vvmul reads both operands for the full result length. Producers must therefore supply matching shapes and expand any broadcast before this operation. Code generation requires static sizes and compatible numeric data.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.vsub — Element-wise subtraction
%y = spat.vsub %a, %b : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- lhs (
%a) and rhs (%b): shaped numeric operands, each 1×4 in the example.
- Outputs
- output (
%y): the element-wise result, here with shape 1×4.
- Attributes
- None required.
- Semantics
- y[i] = lhs[i] - rhs[i].
- Constraints
- Verified: lhs, rhs, and output have exactly the same complete type. Producers must expand any broadcast to matching shapes before this operation.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.vvdmul — Dot product of runtime vectors
%y = spat.vvdmul %a, %b : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x1xf32>
- Inputs
- lhs (
%a) and rhs (%b): rank-2 row vectors with matching shape 1×N, here 1×4.
- Outputs
- output (
%y): a 1×1 tensor containing the dot product.
- Attributes
- None required.
- Semantics
- output[0,0] = sum_i lhs[0,i]*rhs[0,i]. Both vectors are runtime operands; no resident crossbar weight is required.
- Constraints
- Verified: ranked shaped types; rank 2 throughout; identical element types; lhs/rhs shapes equal 1×N with N>0; output exactly 1×1.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.wait — Wait for an exact event count
spat.wait %event value %count
- Inputs
- eventRegister (
%event): a register on the executing core. waitValue (%count): the exact expected signal count. Both are index SSA operands.
- Outputs
- No SSA results.
- Attributes
- None required.
- Semantics
- Blocks until the local event register equals waitValue, then resets it to zero. Lowers to
pim.wait. The corresponding spat.sync operations must supply exactly that count.
- Constraints
- ODS checks index operand types; this operation has no custom verifier. Operands must resolve statically at emission. Register allocation and transfer planning must satisfy the signal/wait contract. The synchronization invariant requires exact counts, separate event roles, host-slot lifetime protection, and restart ordering. These requirements prevent overshoot, register collisions, missing signals, and deadlock.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.wvmm — Row vector times explicit constant matrix
%y = spat.wvmm[%weight](%x) : (tensor<4x3xf32>, tensor<1x4xf32>) -> tensor<1x3xf32>
- Inputs
- weight (
%weight): a matrix W with shape N×M, here 4×3. input (%x): a row vector with shape 1×N, here 1×4. The weighted compute example shows their region bindings.
- Outputs
- output (
%y): a row vector with shape 1×M, here 1×3.
- Attributes
- None required. The weight matrix is the explicit operand in square brackets.
- Semantics
- Computes y[0,j] = sum_i x[0,i]*W[i,j]. In the example, each of the three output elements sums four products. A following operation can add bias or apply an activation.
- Constraints
- Verified: weight/input/output rank 2; N,M>0; input exactly 1×N; output exactly 1×M. Numeric type compatibility is a producer assumption. Compute parents require attached weights to be statically computable. Spatial defines logical shapes; PIM lowering selects physical array groups and padding.
Sources: definition, assembly, verifiers, arithmetic lowering.
spat.yield — Return region values
spat.yield %v : tensor<1x4xf32>
- Inputs
- outputs (
%v): values returned by the enclosing region. The example returns one 1×4 tensor; each expanded operand has a corresponding printed type.
- Outputs
- The operation has no direct SSA results. Its operands become the enclosing compute or deferred-region results.
- Attributes
- None required.
- Semantics
- Terminates the enclosing region and returns its values, as shown in the compute example. A resultless batch uses the empty form
spat.yield :. A resultful batch publishes through spat.in_parallel instead.
- Constraints
- Terminator trait applies. Custom parser checks operand/type counts. Scalar compute parents check exact result types and counts. Deferred communication yields exactly one payload. Resultful compute batches use
spat.in_parallel instead.
Sources: definition, assembly, verifiers, arithmetic lowering.
pim.concat — Concatenate into a destination
%y = pim.concat axis 1 %a, %b into %dst : (tensor<1x2xf32> x2) -> tensor<1x4xf32>
- Inputs
- inputs (
%a, %b): ordered 1×2 tensors. outputBuffer (%dst): the destination after into, with type inferred from the 1×4 result.
- Outputs
- output (
%y): the updated destination, following the PIM destination contract.
- Attributes
- axis: required i64 printed positionally; duplicate attr-dict axis is rejected.
- Semantics
- Joins the inputs along axis into the destination. Here, axis 1 produces a 1×4 row from two 1×2 rows. Lowering uses contiguous block copies. For an inner axis with several outer coordinates, it copies one block per outer coordinate.
- Constraints
- Verified: at least one input and an axis in [0,outputRank). Producers must normalize negative axes before this operation. Input ranks and element types match the output. Static non-axis dimensions match. When all axis sizes are static, their sum equals the output axis size. Dynamic dimensions receive the comparisons that can be established statically. The parser checks expanded input and type counts. All values use the same tensor-or-memref container kind. Destination and result match in kind, element type, and shape; this helper does not compare layout or encoding. Code generation requires static addressable storage. Assumed: local buffers are contiguous and input/destination overlap does not destroy unread input.
Sources: definition, assembly, verifiers, emission.
pim.core — Execute one core stream
pim.core [] = [] coreId 0 : [] -> () {
pim.halt
}
- Inputs
- weights: constant resources bound to body arguments in square brackets. The shown
[] = [] binding is empty. The weighted compute example explains the same binding convention.
- Outputs
- No SSA results; results are communicated or written to host memory.
- Attributes
coreId: required i32 placement ID, printed as coreId 0 in the example. Before emission, memory planning adds pim.local_memory_size and allocation addresses.
- Semantics
- Executes one PIM instruction stream. This example contains only
pim.halt. A stream with weighted arithmetic passes a bound weight operand to pim.vmm. Code generation can remap the core ID.
- Constraints
- Verified: one block with one exact-type argument per weight. External body references must be constants or explicit host-transfer operands. Code generation additionally requires static weights, valid local allocations and memory plans, legal host/device transfers, sufficient hardware resources, and a terminating
pim.halt.
Sources: definition, assembly, verifiers, emission.
pim.core_batch — Execute equivalent core streams
pim.core_batch %lane = 0 to 2 [] = [] () = () coreIds [0, 1] : [] () -> () {
pim.halt
}
- Inputs
- The first body argument is the index lane. Weight and input operands bind to exact-type body arguments after it; the example has empty binding lists. Lane-dependent accesses select each instance’s data. Shared SSA operands do not establish shared device-local storage or shared physical crossbars.
- Outputs
- No SSA results; each lane executes its stream and produces memory/communication effects.
- Attributes
laneCount: positive i32 printed as the exclusive upper bound, here 2. coreIds: dense i32 placement list required by code generation, here [0,1]. See lane and core ranges.
- Semantics
- Retains one common body for equivalent core streams. In the example, lane 0 executes on core 0 and lane 1 executes on core 1. Both execute
pim.halt. For larger bodies, lane expressions determine static addresses and peers while weight operands retain their common SSA definitions.
- Constraints
- Verified: one block, positive laneCount, and body arguments ordered as lane, weights, then inputs. The lane has type index, and other arguments exactly match operand types. The core capture rules apply. Later verification requires a valid core mapping, static lane expressions, constant weight resources, and explicit local memory plans. Transfers must satisfy the communication and synchronization contract. Each body ends with pim.halt.
Sources: definition, assembly, verifiers, emission.
pim.halt — Stop a core stream
pim.halt
- Inputs
- None.
- Outputs
- None.
- Attributes
- None required.
- Semantics
- Terminates the enclosing core stream and emits the backend halt instruction. The
pim.core and pim.core_batch examples show its position at the end of the body.
- Constraints
- The terminator trait requires this operation at the end of its block. It is intended for
pim.core and pim.core_batch bodies; its declaration has no custom parent check.
Sources: definition, assembly, verifiers, emission.
pim.memcp — Copy device-local bytes
%y = pim.memcp[%dstOffset, %srcOffset](%dst, %src) {size = 16 : i32} : tensor<1x4xf32>, tensor<1x4xf32> -> tensor<1x4xf32>
- Inputs
%dst: local destination. %src: local source. %dstOffset and %srcOffset: their respective index SSA byte offsets. The destination appears before the source.
- Outputs
- output (
%y): the updated full destination. Its type describes the destination buffer, including bytes outside the copied range.
- Attributes
size: required i32 byte count, here 16. See the copy example and units.
- Semantics
- Copies size bytes from %src+%srcOffset to %dst+%dstOffset. The example copies 16 bytes, enough for four f32 elements. Destination bytes outside that range retain their contents.
- Constraints
- Source and destination are local to the executing core. This operation has no custom verifier. Later verification and emission require static nonnegative offsets and size, in-bounds memory ranges, and addressable contiguous storage. Destination and result types must match. Overlapping-copy behavior is not guaranteed; producers must avoid destructive overlap.
Sources: definition, assembly, verifiers, emission.
%y = pim.memcp_dh[%dstOffset, %srcOffset](%dst, %src) {size = 16 : i32} : tensor<1x4xf32>, tensor<1x4xf32> -> tensor<1x4xf32>
- Inputs
%dst: host destination. %src: local device source. %dstOffset and %srcOffset: their respective index SSA byte offsets. The destination appears before the source.
- Outputs
- output (
%y): the updated full destination. Its type describes the destination buffer, including bytes outside the copied range.
- Attributes
size: required i32 byte count, here 16. See the copy example and units.
- Semantics
- Copies size bytes from %src+%srcOffset to %dst+%dstOffset. The example copies 16 bytes, enough for four f32 elements. Destination bytes outside that range retain their contents.
- Constraints
- The destination is an explicit host operand; the source is local to the executing core. This operation has no custom verifier. Later verification and emission require static nonnegative offsets and size, in-bounds memory ranges, and addressable contiguous storage. Destination and result types must match. Overlapping-copy behavior is not guaranteed; producers must avoid destructive overlap.
Sources: definition, assembly, verifiers, emission.
%y = pim.memcp_hd[%dstOffset, %srcOffset](%dst, %src) {size = 16 : i32} : tensor<1x4xf32>, tensor<1x4xf32> -> tensor<1x4xf32>
- Inputs
%dst: local device destination. %src: host source. %dstOffset and %srcOffset: their respective index SSA byte offsets. The destination appears before the source.
- Outputs
- output (
%y): the updated full destination. Its type describes the destination buffer, including bytes outside the copied range.
- Attributes
size: required i32 byte count, here 16. See the copy example and units.
- Semantics
- Copies size bytes from %src+%srcOffset to %dst+%dstOffset. The example copies 16 bytes, enough for four f32 elements. Destination bytes outside that range retain their contents.
- Constraints
- The destination is local to the executing core; the source is an explicit host operand. This operation has no custom verifier. Later verification and emission require static nonnegative offsets and size, in-bounds memory ranges, and addressable contiguous storage. Destination and result types must match. Overlapping-copy behavior is not guaranteed; producers must avoid destructive overlap.
Sources: definition, assembly, verifiers, emission.
pim.receive — Receive into local storage
%y = pim.receive[%offset](%dst, %source) {size = 16 : i32} : tensor<1x4xf32> -> tensor<1x4xf32>
- Inputs
- outputBuffer (
%dst): local destination storage. outputOffset (%offset): index SSA byte offset. sourceCoreId (%source): index SSA ID of the sending core.
- Outputs
- output (
%y): the updated destination, following the PIM destination contract.
- Attributes
size: required i32 byte count, here 16.
- Semantics
- Receives size bytes from the source core into %dst+%offset. The matching
pim.send supplies the payload. See the direct-transfer example.
- Constraints
- ODS/interface checks only; no custom per-operation shape/bounds verifier. Later verification/emission require statically resolvable peers and offsets, valid mapped cores, nonnegative in-bounds byte ranges, contiguous local storage, and matching SEND/RECV counts/order. Receive result type must match the full destination. Incorrect transfer order can deadlock.
Sources: definition, assembly, verifiers, emission.
pim.send — Send local data
pim.send(%x, %target) {size = 16 : i32} : tensor<1x4xf32> -> ()
- Inputs
- input (
%x): the local payload. targetCoreId (%target): the index SSA ID of the receiving core.
- Outputs
- No SSA results.
- Attributes
size: required i32 byte count, here 16.
- Semantics
- Sends size bytes from the start of the input to the target core. Pair it with
pim.receive using the same byte count. The direct-transfer example shows both sides.
- Constraints
- ODS and interface checks apply; there is no custom shape or bounds verifier. Later verification and emission require a static peer ID, a valid core mapping, and an in-bounds range in contiguous local storage. The paired
pim.receive must use matching endpoints, byte count, and order. Incorrect transfer order can deadlock.
Sources: definition, assembly, verifiers, emission.
pim.sync — Signal an event register
pim.sync %target event %event
- Inputs
- targetCoreId (
%target): the destination core ID. eventRegister (%event): its local event-register index. Both are index SSA operands.
- Outputs
- No SSA results.
- Attributes
- None required.
- Semantics
- Emits SYNC to increment the selected target-core event register. A matching
pim.wait consumes the expected count on that core. Synchronization-disable mode omits this instruction.
- Constraints
- ODS checks index operand types; this operation has no custom verifier. Operands must resolve statically at emission. Register allocation and transfer planning must satisfy the signal/wait contract. The synchronization invariant requires exact counts, separate event roles, host-slot lifetime protection, and restart ordering. These requirements prevent overshoot, register collisions, missing signals, and deadlock.
Sources: definition, assembly, verifiers, emission.
pim.transpose — Permute dimensions into a destination
%y = pim.transpose(%x, %dst) {permutation = [1, 0]} : (tensor<2x3xf32>, tensor<3x2xf32>) -> tensor<3x2xf32>
- Inputs
- input (
%x): the 2×3 source. outputBuffer (%dst): the 3×2 destination in the example.
- Outputs
- output (
%y): the destination containing the permuted data, following the PIM destination contract.
- Attributes
permutation: required i64 array with one source dimension index per output dimension. Here, [1,0] exchanges rows and columns.
- Semantics
- Maps output dimension d to input dimension permutation[d]. The example maps y[j,i] = x[i,j], changing shape 2×3 to 3×2. Instruction selection uses one
pim.memcp for a storage-preserving case. Other permutations use static runs of pim.memcp or pim.vmv before final emission.
- Constraints
- The operation has no custom verifier. Before instruction selection, operands must be static memrefs. Instruction selection checks permutation length against source rank and requires every dimension exactly once, with no negative or out-of-range indices. Checked size conversions must fit backend fields. Producer assumptions: destination/result shape follows the permutation, element types match, storage is local and contiguous, and source/destination do not overlap destructively. A remaining
pim.transpose is not a final executable instruction.
Sources: definition, assembly, verifiers, emission.
pim.vavg — Average a complete vector
%y = pim.vavg(%x, %dst) : (tensor<1x4xf32>, tensor<1x1xf32>) -> tensor<1x1xf32>
- Inputs
- input (
%x): the complete slice, here 1×4. outputBuffer (%dst): the destination, here 1×1.
- Outputs
- output (
%y): the updated destination, following the PIM destination contract.
- Attributes
- None required.
- Semantics
- y[0,0] = sum(x)/number_of_elements(x). The example processes all four input elements and writes one average to the 1×1 destination.
- Constraints
- This operation has no custom shape verifier. Assumed: nonempty input and a one-element destination/result with compatible numeric type. The result refers to the destination storage. Code generation derives the processed element count from the input and requires static contiguous device-local buffers. Element counts and bitwidths determine instruction lengths. The code generation requirements specify storage and backend limits.
Sources: definition, assembly, verifiers, emission.
pim.vmm — Multiply using a resident array group
%y = pim.vmm[%weight](%x, %dst) : (tensor<4x3xf32>, tensor<1x4xf32>, tensor<1x3xf32>) -> tensor<1x3xf32>
- Inputs
- weight (
%weight): a resident matrix resource, here 4×3. input (%x): a positive-width rank-2 row vector, here 1×4. outputBuffer (%dst): a 1×3 destination.
- Outputs
- output (
%y): the updated destination, with width equal to the matrix column count.
- Attributes
- None required. The square-bracket operand selects the resident weight resource.
- Semantics
- Computes a row-vector/matrix product using a resident array group. The example maps a 1×4 row and 4×3 matrix to a 1×3 row. Weight planning maps the logical matrix to physical crossbars and supplies required padding. The vector widths follow that resource plan rather than a fixed --crossbar-size shape.
- Constraints
- Verified: destination/result have equal container kind, element type and shape; weight/input/output rank 2; positive weight dimensions; input 1×positiveWidth; output 1×M for weight N×M. The verifier does not enforce inputWidth=N or one-crossbar capacity. Resource planning, input padding, matching numeric types, constant weight provenance, and legal physical array-group dimensions are later-stage contracts. Buffers must be local and addressable.
Sources: definition, assembly, verifiers, emission.
pim.vmv — Gather a strided local vector
%y = pim.vmv[%dstOffset, %srcOffset, %stride](%dst, %src) {length = 4 : i32} : tensor<1x4xf32>, tensor<1x8xf32> -> tensor<1x4xf32>
- Inputs
- target (
%dst): contiguous destination. source (%src): source storage. %dstOffset and %srcOffset: index SSA byte offsets. %stride: index SSA stride measured in source elements.
- Outputs
- output (
%y): the updated full destination.
- Attributes
length: required positive i32 element count, here 4. The strided-copy example shows how element stride relates to byte offsets.
- Semantics
- For each i in [0,length), reads one element at source+sourceOffset+i*sourceStride*elementBytes and writes the next contiguous destination element. With stride 2 and f32 data, the example selects four elements at eight-byte intervals.
- Constraints
- Verified: positive length; target/result match container kind, element type, and shape. Source/target use the same container kind and element type. Later verification/emission require static, nonnegative offsets/stride, valid element bitwidth and in-bounds source/destination ranges. Stride counts source elements; for f32, stride 2 selects every second element, advancing 8 bytes. Arbitrary overlapping gather/copy behavior is not guaranteed.
Sources: definition, assembly, verifiers, emission.
pim.vrelu — Element-wise ReLU
%y = pim.vrelu(%x, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- input (
%x): the complete slice, here 1×4. outputBuffer (%dst): the destination, here 1×4.
- Outputs
- output (
%y): the updated destination, following the PIM destination contract.
- Attributes
- None required.
- Semantics
- y[i] = max(x[i],0). The example processes all four input elements and writes four values to the 1×4 destination.
- Constraints
- This operation has no custom shape verifier. Assumed: matching input, destination, and result shapes with compatible numeric types. The result refers to the destination storage. Code generation derives the processed element count from the input and requires static contiguous device-local buffers. Element counts and bitwidths determine instruction lengths. The code generation requirements specify storage and backend limits.
Sources: definition, assembly, verifiers, emission.
pim.vsigm — Element-wise sigmoid
%y = pim.vsigm(%x, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- input (
%x): the complete slice, here 1×4. outputBuffer (%dst): the destination, here 1×4.
- Outputs
- output (
%y): the updated destination, following the PIM destination contract.
- Attributes
- None required.
- Semantics
- y[i] = 1/(1+exp(-x[i])). The example processes all four input elements and writes four values to the 1×4 destination.
- Constraints
- This operation has no custom shape verifier. Assumed: matching input, destination, and result shapes with compatible numeric types. The result refers to the destination storage. Code generation derives the processed element count from the input and requires static contiguous device-local buffers. Element counts and bitwidths determine instruction lengths. The code generation requirements specify storage and backend limits.
Sources: definition, assembly, verifiers, emission.
pim.vsoftmax — Softmax of a complete vector
%y = pim.vsoftmax(%x, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- input (
%x): the complete slice, here 1×4. outputBuffer (%dst): the destination, here 1×4.
- Outputs
- output (
%y): the updated destination, following the PIM destination contract.
- Attributes
- None required.
- Semantics
- y[i] = exp(x[i])/sum_j exp(x[j]). The example processes all four input elements and writes four values to the 1×4 destination. The whole input is one normalization domain. Producers must extract separate slices for axis-specific softmax. Numerical stability depends on the executing backend.
- Constraints
- This operation has no custom shape verifier. Assumed: matching input, destination, and result shapes with compatible numeric types. The result refers to the destination storage. Code generation derives the processed element count from the input and requires static contiguous device-local buffers. Element counts and bitwidths determine instruction lengths. The code generation requirements specify storage and backend limits.
Sources: definition, assembly, verifiers, emission.
pim.vtanh — Element-wise hyperbolic tangent
%y = pim.vtanh(%x, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- input (
%x): the complete slice, here 1×4. outputBuffer (%dst): the destination, here 1×4.
- Outputs
- output (
%y): the updated destination, following the PIM destination contract.
- Attributes
- None required.
- Semantics
- y[i] = tanh(x[i]). The example processes all four input elements and writes four values to the 1×4 destination.
- Constraints
- This operation has no custom shape verifier. Assumed: matching input, destination, and result shapes with compatible numeric types. The result refers to the destination storage. Code generation derives the processed element count from the input and requires static contiguous device-local buffers. Element counts and bitwidths determine instruction lengths. The code generation requirements specify storage and backend limits.
Sources: definition, assembly, verifiers, emission.
pim.vvadd — Element-wise addition
%y = pim.vvadd(%a, %b, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- lhs (
%a) and rhs (%b): matching numeric slices, here 1×4. outputBuffer (%dst): the third operand, here 1×4.
- Outputs
- output (
%y): the updated destination, containing one value per input element. See PIM destinations.
- Attributes
- None required.
- Semantics
- y[i] = lhs[i]+rhs[i]. Code generation uses the lhs element count to read both inputs. The example reads four elements from each input and writes four output elements.
- Constraints
- ODS and destination-interface checks apply; this operation has no custom shape verifier. Assumed: matching lhs/rhs shapes and compatible numeric types. Destination and result types must match and provide the complete result shape. Producers must expand broadcasts before this operation because both inputs are read for the full lhs length. Code generation requires static contiguous device-local storage. See instruction units and backend requirements.
Sources: definition, assembly, verifiers, emission.
%y = pim.vvdmul(%a, %b, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>, tensor<1x1xf32>) -> tensor<1x1xf32>
- Inputs
- lhs (
%a) and rhs (%b): matching numeric slices, here 1×4. outputBuffer (%dst): the third operand, here 1×1.
- Outputs
- output (
%y): the updated destination, containing one dot-product value. See PIM destinations.
- Attributes
- None required.
- Semantics
- y[0,0] = sum_i lhs[i]*rhs[i]. Code generation uses the lhs element count to read both inputs. The example reads four elements from each input and writes one dot product.
- Constraints
- ODS and destination-interface checks apply; this operation has no custom shape verifier. Assumed: matching lhs/rhs shapes and compatible numeric types. Destination and result types must match and provide one element. Producers must expand broadcasts before this operation because both inputs are read for the full lhs length. Code generation requires static contiguous device-local storage. See instruction units and backend requirements.
Sources: definition, assembly, verifiers, emission.
pim.vvmax — Element-wise maximum
%y = pim.vvmax(%a, %b, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- lhs (
%a) and rhs (%b): matching numeric slices, here 1×4. outputBuffer (%dst): the third operand, here 1×4.
- Outputs
- output (
%y): the updated destination, containing one value per input element. See PIM destinations.
- Attributes
- None required.
- Semantics
- y[i] = max(lhs[i],rhs[i]). Code generation uses the lhs element count to read both inputs. The example reads four elements from each input and writes four output elements.
- Constraints
- ODS and destination-interface checks apply; this operation has no custom shape verifier. Assumed: matching lhs/rhs shapes and compatible numeric types. Destination and result types must match and provide the complete result shape. Producers must expand broadcasts before this operation because both inputs are read for the full lhs length. Code generation requires static contiguous device-local storage. See instruction units and backend requirements.
Sources: definition, assembly, verifiers, emission.
pim.vvmul — Element-wise multiplication
%y = pim.vvmul(%a, %b, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- lhs (
%a) and rhs (%b): matching numeric slices, here 1×4. outputBuffer (%dst): the third operand, here 1×4.
- Outputs
- output (
%y): the updated destination, containing one value per input element. See PIM destinations.
- Attributes
- None required.
- Semantics
- y[i] = lhs[i]*rhs[i]. Code generation uses the lhs element count to read both inputs. The example reads four elements from each input and writes four output elements.
- Constraints
- ODS and destination-interface checks apply; this operation has no custom shape verifier. Assumed: matching lhs/rhs shapes and compatible numeric types. Destination and result types must match and provide the complete result shape. Producers must expand broadcasts before this operation because both inputs are read for the full lhs length. Code generation requires static contiguous device-local storage. See instruction units and backend requirements.
Sources: definition, assembly, verifiers, emission.
pim.vvsub — Element-wise subtraction
%y = pim.vvsub(%a, %b, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32>
- Inputs
- lhs (
%a) and rhs (%b): matching numeric slices, here 1×4. outputBuffer (%dst): the third operand, here 1×4.
- Outputs
- output (
%y): the updated destination, containing one value per input element. See PIM destinations.
- Attributes
- None required.
- Semantics
- y[i] = lhs[i]-rhs[i]. Code generation uses the lhs element count to read both inputs. The example reads four elements from each input and writes four output elements.
- Constraints
- ODS and destination-interface checks apply; this operation has no custom shape verifier. Assumed: matching lhs/rhs shapes and compatible numeric types. Destination and result types must match and provide the complete result shape. Producers must expand broadcasts before this operation because both inputs are read for the full lhs length. Code generation requires static contiguous device-local storage. See instruction units and backend requirements.
Sources: definition, assembly, verifiers, emission.
pim.wait — Wait for an exact event count
pim.wait %event value %count
- Inputs
- eventRegister (
%event): a register on the executing core. waitValue (%count): the exact expected signal count. Both are index SSA operands.
- Outputs
- No SSA results.
- Attributes
- None required.
- Semantics
- Emits WAIT, which blocks until the local register equals waitValue and then resets it to zero. Matching
pim.sync operations supply the count. Emission omits zero-count waits and omits all waits in synchronization-disable mode.
- Constraints
- ODS checks index operand types; this operation has no custom verifier. Operands must resolve statically at emission. Register allocation and transfer planning must satisfy the signal/wait contract. The synchronization invariant requires exact counts, separate event roles, host-slot lifetime protection, and restart ordering. These requirements prevent overshoot, register collisions, missing signals, and deadlock.
Sources: definition, assembly, verifiers, emission.