890 lines
148 KiB
HTML
890 lines
148 KiB
HTML
<!doctype html>
|
||
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Raptor dialect operation reference</title>
|
||
<style>
|
||
:root { color-scheme: dark; --bg: #090f0c; --panel: #111e17; --fg: #e8eadb; --muted: #acb9a5; --line: #334436; --accent: #e6bb79; --fern: #a4c68d; }
|
||
* { box-sizing: border-box; } body { margin: 0; background: var(--bg); color: var(--fg); font: 16px/1.65 system-ui, sans-serif; }
|
||
body::before, body::after { content: ""; position: absolute; inset-block: 0; width: min(420px, calc((100vw - 1100px) / 2 - 24px)); background: url("jurassic-border.svg") center top / 100% auto repeat-y; pointer-events: none; z-index: -1; }
|
||
body { position: relative; min-height: 100vh; isolation: isolate; } body::before { left: 0; } body::after { right: 0; transform: scaleX(-1); }
|
||
::selection { background: #445c37; color: #fff6db; }
|
||
a { color: var(--accent); } a:focus-visible, input:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
|
||
.skip { position: absolute; left: -9999px; } .skip:focus { left: 1rem; top: 1rem; background: var(--panel); padding: 1rem; }
|
||
header, main { max-width: 1100px; margin: auto; padding: 2rem; } header { padding-bottom: 0; }
|
||
h1 { font: 500 clamp(2rem, 4vw, 3rem)/1.2 Georgia, serif; color: #f2e4c6; } h2 { margin-top: 2.5rem; padding-bottom: .4rem; border-bottom: 1px solid var(--line); font-family: Georgia, serif; font-weight: 500; color: #d7d9b4; } h3 { margin: 0; font-size: 1.3rem; }
|
||
header { border-top: 3px solid #8f794c; } header > p:first-child { font-size: .8rem; letter-spacing: .08em; text-transform: uppercase; }
|
||
.sources { color: var(--muted); } .toolbar { position: sticky; top: 0; z-index: 1; background: var(--bg); padding: 1rem 0; border-bottom: 1px solid var(--line); }
|
||
input { display: block; width: 100%; font: inherit; padding: .65rem; border: 1px solid var(--line); border-radius: .4rem; background: var(--panel); color: var(--fg); }
|
||
.contents ul { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); gap: .5rem 2rem; list-style: none; padding: 0; } .contents a { font-family: inherit; }
|
||
h2[id], section[id] { scroll-margin-top: 10rem; }
|
||
#operation-index ul { display: flex; flex-wrap: wrap; gap: .5rem 1.2rem; list-style: none; padding: 0; } nav a { font-family: ui-monospace, monospace; color: var(--fern); } nav a, h3 code { overflow-wrap: anywhere; }
|
||
article { background: var(--panel); border: 1px solid var(--line); border-top-color: #596444; border-radius: .6rem; margin: 1.5rem 0; padding: 1.5rem; scroll-margin-top: 10rem; box-shadow: 0 8px 28px #0002; }
|
||
dt { font-weight: 700; margin-top: 1rem; color: var(--fern); } dd { margin-left: 0; } code { font-family: ui-monospace, monospace; font-size: .92em; }
|
||
pre { overflow-x: auto; padding: 1rem; background: var(--bg); border: 1px solid var(--line); border-radius: .3rem; line-height: 1.5; }
|
||
table { border-collapse: collapse; width: 100%; } th, td { text-align: left; border-bottom: 1px solid var(--line); padding: .6rem; vertical-align: top; overflow-wrap: anywhere; }
|
||
[hidden] { display: none !important; } @media (max-width: 600px) { header, main { padding: 1rem; } article { padding: 1rem; } }
|
||
@media (max-width: 1279px) { body::before, body::after { display: none; } }
|
||
@media print { body::before, body::after, .toolbar, .skip { display: none; } article { break-inside: avoid; } article[hidden], nav li[hidden] { display: block !important; } }
|
||
</style>
|
||
</head><body><a class="skip" href="#reference">Skip to operation reference</a>
|
||
<header><p><a href="../../README.md">Raptor repository</a> / Developer reference</p>
|
||
<h1>Raptor dialect reference</h1></header>
|
||
<main id="reference">
|
||
<nav class="contents" aria-label="Table of contents">
|
||
<h2 id="contents">Contents</h2>
|
||
<ul>
|
||
<li><a href="#reading">Values, operations, and types</a></li>
|
||
<li><a href="#compact">Multiple results and compact lists</a></li>
|
||
<li><a href="#planning">Planning operations and attributes</a></li>
|
||
<li><a href="#compute-regions">Compute regions and bindings</a></li>
|
||
<li><a href="#graph-batches">Graph batches and fragment slots</a></li>
|
||
<li><a href="#input-representations">Compute input representations</a></li>
|
||
<li><a href="#scheduling">Scheduled lanes and physical cores</a></li>
|
||
<li><a href="#destinations">PIM destinations and storage</a></li>
|
||
<li><a href="#memory-units">Memory offsets and units</a></li>
|
||
<li><a href="#communication">Transfers and synchronization</a></li>
|
||
<li><a href="#contracts">Verification and code generation</a></li>
|
||
<li><a href="#spatial-index">Spatial operation index</a></li>
|
||
<li><a href="#pim-index">PIM operation index</a></li>
|
||
</ul></nav>
|
||
|
||
<section id="reading"><h2>Values, operations, and types</h2>
|
||
<p>The Spatial dialect describes computations with <code>spat.</code> operations. For example, <a href="#spat.vadd"><code>spat.vadd</code></a> adds two tensors element by element:</p>
|
||
<pre data-example="addition"><code>%y = spat.vadd %a, %b : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre>
|
||
<p>The operands <code>%a</code> and <code>%b</code> are values defined earlier in the enclosing function or <a href="#compute-regions">region</a>. The operation produces <code>%y</code>, a new single static assignment (SSA) value. Each SSA value has one definition and can have several uses.</p>
|
||
<p>After the colon, the two types in parentheses describe the operands in order. The type after <code>-></code> describes the result. Here, every value has type <code>tensor<1x4xf32></code>: a rank-2 tensor with one row, four columns, and 32-bit floating-point elements.</p>
|
||
<p>The result has the same shape as the operands because addition produces one value for each pair of elements. Other operations can change the shape or produce several results.</p>
|
||
</section>
|
||
|
||
<section id="compact"><h2>Multiple results and compact lists</h2>
|
||
<p><a href="#spat.extract_rows"><code>spat.extract_rows</code></a> produces a separate SSA value for each matrix row. This example extracts two rows and joins them along their column axis:</p>
|
||
<pre data-example="rows"><code>%rows:2 = spat.extract_rows %matrix : tensor<2x4xf32> -> tensor<1x4xf32> x2
|
||
%joined = spat.concat axis 1 %rows#0, %rows#1 : (tensor<1x4xf32> x2) -> tensor<1x8xf32></code></pre>
|
||
<p><code>%rows:2</code> names a group of two results. <code>%rows#0</code> selects the first result, and <code>%rows#1</code> selects the second. Each result is a rank-2 tensor with one row and four columns.</p>
|
||
<p>The suffix <code>x2</code> repeats the preceding type twice. Thus, the input type list for <a href="#spat.concat"><code>spat.concat</code></a> contains two <code>tensor<1x4xf32></code> types. The <code>x</code> characters inside that tensor type separate its dimensions and element type.</p>
|
||
<p>These operations use the <a href="../../src/PIM/Common/IR/CompactAsmUtils.hpp">compact list syntax</a> for repeated types and operands. Integer ranges in core lists use the same helpers; the <a href="#scheduling">core batch example</a> shows their bounds.</p>
|
||
</section>
|
||
|
||
<section id="planning"><h2>Planning operations and attributes</h2>
|
||
<p>Before it creates individual compute regions, the compiler records model operations with their logical tensor shapes. For example, <a href="#spat.add_plan"><code>spat.add_plan</code></a> records addition in NCHW coordinates:</p>
|
||
<pre data-example="plan"><code>%y = "spat.add_plan"(%a, %b) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>, tensor<1x2x3x4xf32>) -> tensor<1x2x3x4xf32></code></pre>
|
||
<p>In NCHW order, the dimensions count model examples (N), channels (C), height (H), and width (W). The example has one model example, two channels, three rows, and four columns.</p>
|
||
<p>The <code><{...}></code> dictionary holds operation properties known at compile time. Here, <code>logicalLayout</code> identifies the coordinate order. Planning operations use this quoted operation form; their entries show the properties for each operation.</p>
|
||
<p>The planner selects a physical representation separately from these logical coordinates. <a href="#spat.materialize_layout"><code>spat.materialize_layout</code></a> makes a physical layout conversion explicit. The <a href="../compiler/spatial-layouts.md">layout reference</a> lists supported representations and conversions.</p>
|
||
</section>
|
||
|
||
<section id="compute-regions"><h2>Compute regions and bindings</h2>
|
||
<p>The compiler lowers planned work into compute regions. A region contains operations that use bound inputs and return results through <a href="#spat.yield"><code>spat.yield</code></a>. This <a href="#spat.graph_compute"><code>spat.graph_compute</code></a> multiplies an input row by a constant matrix:</p>
|
||
<pre data-example="weighted"><code>%w = arith.constant dense<1.0> : tensor<4x3xf32>
|
||
%y = spat.graph_compute [%warg] = [%w] (%arg) = (%x) crossbarWeights 1 : [tensor<4x3xf32>] (tensor<1x4xf32>) -> tensor<1x3xf32> {
|
||
%v = spat.wvmm[%warg](%arg) : (tensor<4x3xf32>, tensor<1x4xf32>) -> tensor<1x3xf32>
|
||
spat.yield %v : tensor<1x3xf32>
|
||
}</code></pre>
|
||
<p>The square-bracket binding gives the outer constant <code>%w</code> the body name <code>%warg</code>. The parenthesis binding gives the outer input <code>%x</code> the body name <code>%arg</code>. The type lists after the colon follow the same weight-then-input order.</p>
|
||
<p>The braces enclose the region body. Within that body, <a href="#spat.wvmm"><code>spat.wvmm</code></a> takes the bound matrix and input row. Its result <code>%v</code> becomes the outer result <code>%y</code> through <code>spat.yield</code>.</p>
|
||
<p><code>crossbarWeights 1</code> reports one distinct resident weight used by the body. The printer derives this count from the computation. Physical weight planning later maps resident weights to crossbar resources.</p>
|
||
<p>The graph and scheduled compute operations use these bindings. PIM core regions also bind resident weights, as described under <a href="#pim.core"><code>pim.core</code></a>.</p>
|
||
</section>
|
||
|
||
<section id="graph-batches"><h2>Graph batches and fragment slots</h2>
|
||
<p><a href="#spat.graph_compute_batch"><code>spat.graph_compute_batch</code></a> expresses several lane computations with one body. In this example, two lanes apply ReLU to different four-column slices of one input:</p>
|
||
<pre data-example="graph-batch"><code>%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>
|
||
}
|
||
}</code></pre>
|
||
<p><code>%lane = 0 to 2</code> gives the lane indices 0 and 1; the upper bound is exclusive. The lane argument has type <code>index</code>, an integer SSA type used here to calculate slice offsets. Lane 0 reads columns 0–3, and lane 1 reads columns 4–7.</p>
|
||
<p>Each lane produces a <code>tensor<1x4xf32></code> fragment. <code>shared_outs(%out)</code> names the body output argument. Inside <a href="#spat.in_parallel"><code>spat.in_parallel</code></a>, each insertion publishes one fragment into slot <code>%lane</code>.</p>
|
||
<p>The batch produces one SSA result, <code>%fragments</code>, with type <code>tensor<2x1x4xf32></code>. 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 <code>[N] + shape(F)</code>.</p>
|
||
<p>This is a <strong>physical fragment collection</strong> 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, <a href="#spat.blueprint"><code>spat.blueprint</code></a> places the two fragments next to each other:</p>
|
||
<pre data-example="reconstruction"><code>%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></code></pre>
|
||
<p>The flattened <code>offsets</code> and <code>sizes</code> lists contain one rank-2 destination slice per fragment:</p>
|
||
<table><thead><tr><th>Source slot</th><th>Logical offset</th><th>Slice size</th><th>Logical columns</th></tr></thead><tbody>
|
||
<tr><td>0</td><td>[0, 0]</td><td>[1, 4]</td><td>0–3</td></tr>
|
||
<tr><td>1</td><td>[0, 4]</td><td>[1, 4]</td><td>4–7</td></tr>
|
||
</tbody></table>
|
||
<p>The result <code>%logical</code> has shape 1×8 because this descriptor defines that reconstruction. The <a href="../../.agents/invariants/GRAPH_COMPUTE_BATCH_INVARIANT.md">graph fragment invariant</a> specifies publication and reconstruction requirements for every graph batch result.</p>
|
||
</section>
|
||
|
||
<section id="input-representations"><h2>Compute input representations</h2>
|
||
<p>The graph batch above binds one logical <code>tensor<1x8xf32></code> input and returns a physical fragment collection. Thus, its input and result have different representations. A later compute can consume either representation, provided its body selects the correct coordinates:</p>
|
||
<table><thead><tr><th>Input example</th><th>How the body accesses it</th><th>Related implementation</th></tr></thead><tbody>
|
||
<tr><td>Logical <code>%x : tensor<1x8xf32></code></td><td>The graph example extracts four columns using a logical column offset. The input has no leading lane dimension.</td><td><a href="../../src/PIM/Conversion/ONNXToSpatial/Patterns/Math/Gemm.cpp">GEMM lowering</a> similarly binds logical matrix inputs and selects tiles.</td></tr>
|
||
<tr><td>Physical <code>%fragments : tensor<2x1x4xf32></code></td><td>Dimension 0 selects a published fragment slot. The remaining dimensions address elements within that fragment.</td><td><a href="../../src/PIM/Conversion/ONNXToSpatial/Common/ComputeRegionBuilder.hpp">mapGraphBatchFragments</a> creates consumers that select one fragment per lane.</td></tr>
|
||
<tr><td>Logical <code>%logical : tensor<1x8xf32></code> described by a blueprint</td><td>The blueprint retains the mapping from logical slices to physical source slots. The logical type alone does not establish dense physical storage.</td><td><a href="../../src/PIM/Conversion/ONNXToSpatial/Common/RowStripLayoutUtils.cpp">Row-strip lowering</a> follows blueprint mappings to their fragment sources.</td></tr>
|
||
</tbody></table>
|
||
<p>The input binding preserves the complete operand type. A consumer lane can select several producer fragments, and several consumer lanes can read the same fragment. Producer slot count and consumer lane count can therefore differ. Shared bindings describe SSA reuse; physical memory and crossbar allocation are later decisions.</p>
|
||
</section>
|
||
|
||
<section id="scheduling"><h2>Scheduled lanes and physical cores</h2>
|
||
<p>Scheduling assigns graph computations to physical cores and introduces explicit communication between cores. <a href="#spat.scheduled_compute"><code>spat.scheduled_compute</code></a> describes one scheduled computation; <a href="#spat.scheduled_compute_batch"><code>spat.scheduled_compute_batch</code></a> groups equivalent scheduled instances.</p>
|
||
<p>The following identities describe different parts of that transition:</p>
|
||
<table><thead><tr><th>Identity</th><th>Meaning</th></tr></thead><tbody>
|
||
<tr><td>Graph lane</td><td>One instance of a graph batch body. In the ReLU example, lane 1 processes columns 4–7.</td></tr>
|
||
<tr><td>Fragment slot</td><td>One publication position in a graph result. The example publishes lane 1 into slot 1; other batches can use a static permutation.</td></tr>
|
||
<tr><td>Scheduled lane</td><td>One instance in a scheduled batch. Scheduling determines which graph work belongs to that instance.</td></tr>
|
||
<tr><td>Physical core</td><td>The hardware core assigned to execute an instance. A placement array maps lanes to core IDs.</td></tr>
|
||
</tbody></table>
|
||
<p>At the processing-in-memory (PIM) stage, <a href="#pim.core_batch"><code>pim.core_batch</code></a> retains a common body for several core streams. This minimal example contains only their terminating instruction:</p>
|
||
<pre data-example="core-batch"><code>pim.core_batch %lane = 0 to 4 [] = [] () = () coreIds [0 to 3] : [] () -> () {
|
||
pim.halt
|
||
}</code></pre>
|
||
<p>The lane range <code>0 to 4</code> is exclusive and creates four instances. The integer list <code>[0 to 3]</code> is inclusive and expands to <code>[0, 1, 2, 3]</code>. Its entries assign those instances to physical cores. Each stream ends with <a href="#pim.halt"><code>pim.halt</code></a>.</p>
|
||
<p>Scheduled batches describe placed execution and communication. Their result shapes follow that execution contract; the graph-only fragment-slot rule does not apply to them.</p>
|
||
</section>
|
||
|
||
<section id="destinations"><h2>PIM destinations and storage</h2>
|
||
<p>PIM arithmetic operations write into an explicit destination. For example, <a href="#pim.vrelu"><code>pim.vrelu</code></a> writes the ReLU result into <code>%dst</code>:</p>
|
||
<pre data-example="destination"><code>%y = pim.vrelu(%x, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre>
|
||
<p><code>%y</code> is the updated destination value. Operations that use the result consume <code>%y</code>. During bufferization, tensor values become memory references:</p>
|
||
<pre data-example="bufferized"><code>%y = pim.vrelu(%x, %dst) : (memref<1x4xf32>, memref<1x4xf32>) -> memref<1x4xf32></code></pre>
|
||
<p><code>memref<1x4xf32></code> describes addressable storage for the same element shape. The result refers to the destination storage. Memory planning assigns local allocations and addresses before code generation.</p>
|
||
<p>Partial writes preserve destination bytes outside the written range. Each operation's constraints specify its storage and overlap requirements; destination binding alone does not permit arbitrary input/output overlap.</p>
|
||
</section>
|
||
|
||
<section id="memory-units"><h2>Memory offsets and units</h2>
|
||
<p><a href="#pim.memcp"><code>pim.memcp</code></a> copies a byte range between local buffers. This example defines zero byte offsets and copies four 32-bit floating-point elements:</p>
|
||
<pre data-example="copy"><code>%zero = arith.constant 0 : index
|
||
%y = pim.memcp[%zero, %zero](%dst, %src) {size = 16 : i32} : memref<1x4xf32>, memref<1x4xf32> -> memref<1x4xf32></code></pre>
|
||
<p>The first offset belongs to the destination, and the second belongs to the source. Both are SSA operands of type <code>index</code>. The <code>{size = 16 : i32}</code> attribute dictionary gives a compile-time byte count with integer type <code>i32</code>. Four <code>f32</code> elements occupy 16 bytes.</p>
|
||
<p><a href="#pim.vmv"><code>pim.vmv</code></a> uses byte offsets too, but its source stride and length count elements. The following copy selects every second source element:</p>
|
||
<pre data-example="gather"><code>%stride = arith.constant 2 : index
|
||
%y = pim.vmv[%zero, %zero, %stride](%dst, %src) {length = 4 : i32} : memref<1x4xf32>, memref<1x8xf32> -> memref<1x4xf32></code></pre>
|
||
<p>Here, <code>%zero</code> has the definition shown above. Source positions 0, 2, 4, and 6 produce four contiguous destination elements. For <code>f32</code>, the stride advances eight bytes between source elements.</p>
|
||
<p>Vector arithmetic also measures instruction lengths in elements. Copy and transfer operations measure their sizes in bytes. Backend configuration determines element bitwidths; the <a href="#contracts">code generation requirements</a> apply to both groups.</p>
|
||
</section>
|
||
|
||
<section id="communication"><h2>Transfers and synchronization</h2>
|
||
<p>Scheduled work uses <a href="#spat.channel_send"><code>spat.channel_send</code></a> and <a href="#spat.channel_receive"><code>spat.channel_receive</code></a> for explicit dataflow between cores. PIM lowering turns these transfers into core instructions. For a direct transfer, the source sends a payload and the target receives the same byte count:</p>
|
||
<pre data-example="send"><code>// Source core; %target is the target core ID.
|
||
pim.send(%x, %target) {size = 16 : i32} : tensor<1x4xf32> -> ()</code></pre>
|
||
<pre data-example="receive"><code>// Target core; %source is the source core ID, and %zero is a byte offset.
|
||
%y = pim.receive[%zero](%dst, %source) {size = 16 : i32} : tensor<1x4xf32> -> tensor<1x4xf32></code></pre>
|
||
<p>The peer operands have type <code>index</code> and must resolve statically. The sender and receiver must agree on core IDs, byte count, and transfer order. Their <a href="#pim.send">operation entries</a> give the storage requirements.</p>
|
||
<p>Host-routed transfers use a shared host slot instead. <a href="#spat.host_store_sync"><code>spat.host_store_sync</code></a> stores a payload and signals its availability. <a href="#spat.host_wait_load"><code>spat.host_wait_load</code></a> waits for that signal before loading the payload.</p>
|
||
<p>The signal and wait use the following PIM operations in their respective core streams:</p>
|
||
<pre data-example="signal"><code>// Writer core; signal the event register on %target.
|
||
pim.sync %target event %event</code></pre>
|
||
<pre data-example="wait"><code>// Reader core; wait for the expected number of signals.
|
||
pim.wait %event value %count</code></pre>
|
||
<p><a href="#pim.sync"><code>pim.sync</code></a> increments a register local to the target core. <a href="#pim.wait"><code>pim.wait</code></a> proceeds when that register equals <code>%count</code>, then resets it to zero. These operands must resolve to static integer values.</p>
|
||
<p>Registers start at zero and persist across stream restarts. Repeated execution must protect each host slot until all readers finish with its previous contents. The <a href="../../.agents/invariants/PIM_SYNCHRONIZATION_INVARIANT.md">synchronization contract</a> defines the READY/FREE protocol, restart ordering, and register separation required for that reuse.</p>
|
||
</section>
|
||
|
||
<section id="contracts"><h2>Verification and code generation</h2>
|
||
<p>The examples follow the compiler stages: logical plans, graph computations, scheduled work, PIM tensors, and bufferized memory operations. Code generation then emits the per-core instruction streams. The <a href="../compiler/architecture.md">compiler architecture</a> describes the passes between these stages.</p>
|
||
<p>The Operation Definition Specification (ODS) declares operand types and traits used to generate basic checks. Operation-specific verifiers enforce additional constraints.</p>
|
||
<p>Each operation entry separates its enforced checks from requirements on producers and later passes. <strong>Verified</strong> identifies checks in the operation verifier, parser, or declared traits. <strong>Assumed</strong> identifies requirements that the producer or later lowering must satisfy. Acceptance by an operation verifier alone does not establish executable PIM code.</p>
|
||
<p>For emission, address, lane, peer, and event expressions must resolve statically. Code generation evaluates supported constant loops and lane expressions before emitting instructions. Local storage must have an explicit memory plan with valid contiguous addresses.</p>
|
||
<p>Memory verification requires nonnegative addresses, four-byte allocation alignment, and ranges within the local arena. Allocations that are live simultaneously must not overlap. Weight resources must be compile-time constants materialized for the selected target. Numeric element types must be supported by the executing backend.</p>
|
||
<p>The entries below apply these common requirements to each operation. Their source links identify the assembly, verification, and lowering implementations.</p>
|
||
</section>
|
||
|
||
<div class="toolbar"><label for="search">Find an operation, input, or constraint</label><input id="search" type="search" placeholder="e.g. graph_compute_batch, byte, blueprint" autocomplete="off" disabled><span id="count" role="status" aria-live="polite">All 63 operations</span><noscript><p>Use your browser’s Find command to search.</p></noscript></div>
|
||
<nav id="operation-index" aria-label="Operation index">
|
||
|
||
<h2 id="spatial-index">Spatial operation index</h2><ul>
|
||
<li><a href="#spat.add_plan">spat.add_plan</a></li>
|
||
<li><a href="#spat.bias_add_plan">spat.bias_add_plan</a></li>
|
||
<li><a href="#spat.block_yield">spat.block_yield</a></li>
|
||
<li><a href="#spat.blueprint">spat.blueprint</a></li>
|
||
<li><a href="#spat.channel_receive">spat.channel_receive</a></li>
|
||
<li><a href="#spat.channel_send">spat.channel_send</a></li>
|
||
<li><a href="#spat.concat">spat.concat</a></li>
|
||
<li><a href="#spat.concat_plan">spat.concat_plan</a></li>
|
||
<li><a href="#spat.conv2d_plan">spat.conv2d_plan</a></li>
|
||
<li><a href="#spat.deferred_communication">spat.deferred_communication</a></li>
|
||
<li><a href="#spat.deferred_source_select">spat.deferred_source_select</a></li>
|
||
<li><a href="#spat.extract_rows">spat.extract_rows</a></li>
|
||
<li><a href="#spat.flatten_plan">spat.flatten_plan</a></li>
|
||
<li><a href="#spat.global_average_pool_plan">spat.global_average_pool_plan</a></li>
|
||
<li><a href="#spat.graph_compute">spat.graph_compute</a></li>
|
||
<li><a href="#spat.graph_compute_batch">spat.graph_compute_batch</a></li>
|
||
<li><a href="#spat.host_store_sync">spat.host_store_sync</a></li>
|
||
<li><a href="#spat.host_wait_load">spat.host_wait_load</a></li>
|
||
<li><a href="#spat.in_parallel">spat.in_parallel</a></li>
|
||
<li><a href="#spat.materialize_layout">spat.materialize_layout</a></li>
|
||
<li><a href="#spat.max_pool2d_plan">spat.max_pool2d_plan</a></li>
|
||
<li><a href="#spat.relu">spat.relu</a></li>
|
||
<li><a href="#spat.relu_plan">spat.relu_plan</a></li>
|
||
<li><a href="#spat.resize_nearest_plan">spat.resize_nearest_plan</a></li>
|
||
<li><a href="#spat.scheduled_compute">spat.scheduled_compute</a></li>
|
||
<li><a href="#spat.scheduled_compute_batch">spat.scheduled_compute_batch</a></li>
|
||
<li><a href="#spat.sigmoid">spat.sigmoid</a></li>
|
||
<li><a href="#spat.silu_plan">spat.silu_plan</a></li>
|
||
<li><a href="#spat.softmax">spat.softmax</a></li>
|
||
<li><a href="#spat.sync">spat.sync</a></li>
|
||
<li><a href="#spat.vadd">spat.vadd</a></li>
|
||
<li><a href="#spat.vavg">spat.vavg</a></li>
|
||
<li><a href="#spat.vmax">spat.vmax</a></li>
|
||
<li><a href="#spat.vmul">spat.vmul</a></li>
|
||
<li><a href="#spat.vsub">spat.vsub</a></li>
|
||
<li><a href="#spat.vvdmul">spat.vvdmul</a></li>
|
||
<li><a href="#spat.wait">spat.wait</a></li>
|
||
<li><a href="#spat.wvmm">spat.wvmm</a></li>
|
||
<li><a href="#spat.yield">spat.yield</a></li>
|
||
</ul>
|
||
<h2 id="pim-index">PIM operation index</h2><ul>
|
||
<li><a href="#pim.concat">pim.concat</a></li>
|
||
<li><a href="#pim.core">pim.core</a></li>
|
||
<li><a href="#pim.core_batch">pim.core_batch</a></li>
|
||
<li><a href="#pim.halt">pim.halt</a></li>
|
||
<li><a href="#pim.memcp">pim.memcp</a></li>
|
||
<li><a href="#pim.memcp_dh">pim.memcp_dh</a></li>
|
||
<li><a href="#pim.memcp_hd">pim.memcp_hd</a></li>
|
||
<li><a href="#pim.receive">pim.receive</a></li>
|
||
<li><a href="#pim.send">pim.send</a></li>
|
||
<li><a href="#pim.sync">pim.sync</a></li>
|
||
<li><a href="#pim.transpose">pim.transpose</a></li>
|
||
<li><a href="#pim.vavg">pim.vavg</a></li>
|
||
<li><a href="#pim.vmm">pim.vmm</a></li>
|
||
<li><a href="#pim.vmv">pim.vmv</a></li>
|
||
<li><a href="#pim.vrelu">pim.vrelu</a></li>
|
||
<li><a href="#pim.vsigm">pim.vsigm</a></li>
|
||
<li><a href="#pim.vsoftmax">pim.vsoftmax</a></li>
|
||
<li><a href="#pim.vtanh">pim.vtanh</a></li>
|
||
<li><a href="#pim.vvadd">pim.vvadd</a></li>
|
||
<li><a href="#pim.vvdmul">pim.vvdmul</a></li>
|
||
<li><a href="#pim.vvmax">pim.vvmax</a></li>
|
||
<li><a href="#pim.vvmul">pim.vvmul</a></li>
|
||
<li><a href="#pim.vvsub">pim.vvsub</a></li>
|
||
<li><a href="#pim.wait">pim.wait</a></li>
|
||
</ul>
|
||
</nav><section aria-label="Operation details">
|
||
<article id="spat.add_plan">
|
||
<h3><a href="#spat.add_plan"><code>spat.add_plan</code></a> — Plan element-wise addition</h3>
|
||
<pre data-field="syntax"><code>%y = "spat.add_plan"(%a, %b) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>, tensor<1x2x3x4xf32>) -> tensor<1x2x3x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">lhs (<code>%a</code>) and rhs (<code>%b</code>): matching NCHW tensors, each with shape 1×2×3×4 in the example.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the logical result with the printed shape. Physical representation is selected through <a href="#planning">layout planning</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>logicalLayout</code>: required logical coordinate order, <code>nchw</code> in the example. See <a href="#planning">planning operations and attributes</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Adds corresponding elements. In the example, each of the 24 output elements is the sum of two input elements.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints">Verified: static ranked rank-4 tensors with identical complete types; nchw layout.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.bias_add_plan">
|
||
<h3><a href="#spat.bias_add_plan"><code>spat.bias_add_plan</code></a> — Plan channel bias addition</h3>
|
||
<pre data-field="syntax"><code>%y = "spat.bias_add_plan"(%x, %bias) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>, tensor<2xf32>) -> tensor<1x2x3x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): an NCHW tensor with shape 1×2×3×4. bias (<code>%bias</code>): two channel bias values in the example.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the logical result with the printed shape. Physical representation is selected through <a href="#planning">layout planning</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>logicalLayout</code>: required logical coordinate order, <code>nchw</code> in the example. See <a href="#planning">planning operations and attributes</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.block_yield">
|
||
<h3><a href="#spat.block_yield"><code>spat.block_yield</code></a> — Structural scheduled block terminator</h3>
|
||
<pre data-field="syntax"><code>spat.block_yield :</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">outputs: optional values forwarded to a successor block. A successor is written as <code>next ^bb</code> after those values. The example has neither values nor a successor.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">No SSA results on this operation.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints">Verified: parent is <a href="#spat.scheduled_compute"><code>spat.scheduled_compute</code></a>; at most one successor; forwarded operand count and types match successor arguments. However, the scheduled-compute verifier requires one block ending in <a href="#spat.yield"><code>spat.yield</code></a>. Therefore, this declared structural terminator is not accepted in an ordinarily verified scheduled_compute body.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.blueprint">
|
||
<h3><a href="#spat.blueprint"><code>spat.blueprint</code></a> — Describe logical reconstruction</h3>
|
||
<pre data-field="syntax"><code>%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></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%y</code>): the first fragment collection. Optional fragments add further collections to the printed <code>fragments(...)</code> list. Here, one collection contains two 1×4 fragments. See the <a href="#graph-batches">publication and reconstruction example</a>.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%logical</code>): the reconstructed logical tensor. Here, the descriptor places two 1×4 source fragments into a 1×8 result. The <a href="#graph-batches">reconstruction table</a> shows their destination coordinates.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>layout</code> and <code>physical</code> print logicalLayout and physicalLayout. <code>offsets</code> and <code>sizes</code> print flattened destination slices in logical element coordinates. <code>map</code> names indexMap. <code>mode</code> 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. <code>strides</code> prints fragmentStrides. <code>conflict</code> and <code>coverage</code> print conflictPolicy and coveragePolicy, which control destination overlap and coverage. The <a href="#graph-batches">mapping table</a> expands the example destination geometry.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints"><p><strong>Verified geometry.</strong> 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.</p>
|
||
<p><strong>physical_view.</strong> 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.</p>
|
||
<p><strong>fragment_assembly.</strong> 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.</p>
|
||
<p>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 <a href="#graph-batches">graph reconstruction</a>.</p></dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.channel_receive">
|
||
<h3><a href="#spat.channel_receive"><code>spat.channel_receive</code></a> — Receive a logical payload</h3>
|
||
<pre data-field="syntax"><code>%y = spat.channel_receive channel %channel from %source to %target : tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">channelId (<code>%channel</code>), sourceCoreId (<code>%source</code>), and targetCoreId (<code>%target</code>): index SSA operands for this transfer.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output: received payload.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required. The channel and endpoint IDs appear as SSA operands in the example.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Receives the complete payload from the paired <a href="#spat.channel_send"><code>spat.channel_send</code></a>. The result type specifies its shape and element type. Target lowering implements the pair with core-addressed SEND/RECV instructions.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.channel_send">
|
||
<h3><a href="#spat.channel_send"><code>spat.channel_send</code></a> — Send a logical payload</h3>
|
||
<pre data-field="syntax"><code>spat.channel_send %x channel %channel from %source to %target : tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the complete payload. channelId (<code>%channel</code>), sourceCoreId (<code>%source</code>), and targetCoreId (<code>%target</code>): index SSA operands for this transfer.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">No SSA results; communication effect.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required. The channel and endpoint IDs appear as SSA operands in the example.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Sends the complete payload on a logical channel. Pair it with <a href="#spat.channel_receive"><code>spat.channel_receive</code></a> using the same channel and endpoints. Target lowering implements the pair with core-addressed SEND/RECV instructions.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.concat">
|
||
<h3><a href="#spat.concat"><code>spat.concat</code></a> — Join tensors along one axis</h3>
|
||
<pre data-field="syntax"><code>%y = spat.concat axis 1 %a, %b : (tensor<1x2xf32> x2) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">inputs (<code>%a</code>, <code>%b</code>): ordered shaped values, each 1×2 in the example. The compact type suffix <code>x2</code> is explained under <a href="#compact">multiple results and compact lists</a>.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output: concatenation of the inputs along axis.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">axis: required i64 printed after axis; cannot also be supplied in the attribute dictionary.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.concat_plan">
|
||
<h3><a href="#spat.concat_plan"><code>spat.concat_plan</code></a> — Plan channel concatenation</h3>
|
||
<pre data-field="syntax"><code>%y = "spat.concat_plan"(%a, %b) <{axis = 1 : i64, logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>, tensor<1x2x3x4xf32>) -> tensor<1x4x3x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">inputs (<code>%a</code>, <code>%b</code>): NCHW tensors in concatenation order. Each example input has two channels and spatial shape 3×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the logical result with the printed shape. Physical representation is selected through <a href="#planning">layout planning</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>logicalLayout</code>: required logical coordinate order, <code>nchw</code> in the example. See <a href="#planning">planning operations and attributes</a>. <code>axis = 1 : i64</code> selects the channel dimension.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Joins the inputs along the channel axis. The example places the two channels of %a before the two channels of %b.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.conv2d_plan">
|
||
<h3><a href="#spat.conv2d_plan"><code>spat.conv2d_plan</code></a> — Plan a grouped 2D convolution</h3>
|
||
<pre data-field="syntax"><code>%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></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): shape [N,Cin,H,W], here [1,2,4,4]. weight (<code>%weight</code>): shape [Cout,Cin/group,Kh,Kw], here [3,2,3,3]. An optional bias supplies output-channel offsets.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the logical result with the printed shape. Physical representation is selected through <a href="#planning">layout planning</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>logicalLayout</code>: required logical coordinate order, <code>nchw</code> in the example. See <a href="#planning">planning operations and attributes</a>. <code>pads</code>: [top,left,bottom,right] in elements. <code>strides</code> and <code>dilations</code>: [height,width] element steps. The <code>array<i64: ...></code> notation stores these integer lists. <code>group</code>: number of channel groups; 1 in the example.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.deferred_communication">
|
||
<h3><a href="#spat.deferred_communication"><code>spat.deferred_communication</code></a> — Describe a future payload transfer</h3>
|
||
<p>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.</p>
|
||
<pre data-field="syntax"><code>%y = spat.deferred_communication %x : (tensor<1x4xf32>) -> tensor<1x4xf32> {
|
||
spat.yield %arg0 : tensor<1x4xf32>
|
||
}</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">sources (<code>%x</code>): published shaped values bound to source arguments in the body. The example body refers to its source as <code>%arg0</code>. Grouped specialization adds a final index argument.</dd>
|
||
<dt>Outputs</dt><dd data-field="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.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>specialization_count</code>: optional positive i64, default 1. Values greater than one add a body index argument and a result dimension for specialization cases.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Records source selection and constant shaping for a scheduled consumer. Here, the body forwards its complete source through <a href="#spat.yield"><code>spat.yield</code></a>. Later phases use this body to derive routing and realize the transfer. For several sources, <a href="#spat.deferred_source_select"><code>spat.deferred_source_select</code></a> records which source a specialization uses.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints">Verified: at least one source; one body block; exact source argument types; specialization argument is index when count>1. The body ends in <a href="#spat.yield"><code>spat.yield</code></a> 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.</dd>
|
||
</dl>
|
||
<p>For example, two specialization cases can select different sources. Generic assembly declares the source arguments and the case index explicitly:</p>
|
||
<pre data-example="deferred-group"><code>%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></code></pre>
|
||
<p>Case 0 selects <code>%a</code>, and case 1 selects <code>%b</code>. Each case yields a 1×4 fragment. The result shape 2×1×4 counts those two cases. Print this explicit form with <code>onnx-mlir-opt --maccel=PIM --mlir-print-op-generic</code>.</p>
|
||
<p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.deferred_source_select">
|
||
<h3><a href="#spat.deferred_source_select"><code>spat.deferred_source_select</code></a> — Select a published source</h3>
|
||
<pre data-field="syntax"><code>%y = spat.deferred_source_select %selector of %a, %b : tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">selector (<code>%selector</code>): an index SSA value. sources (<code>%a</code>, <code>%b</code>): the ordered, nonempty source list.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output: selected source value of the common source type.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Selects sources[selector] during compile-time communication planning. In the example, selector 0 chooses %a and selector 1 chooses %b. The enclosing <a href="#spat.deferred_communication"><code>spat.deferred_communication</code></a> or scheduled compute supplies a statically analyzable selector.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints">Verified: index selector; nonempty sources; all source and output types match. Parent must be <a href="#spat.deferred_communication"><code>spat.deferred_communication</code></a>, <a href="#spat.scheduled_compute"><code>spat.scheduled_compute</code></a>, or <a href="#spat.scheduled_compute_batch"><code>spat.scheduled_compute_batch</code></a>. Producer assumption: selector is statically analyzable and in [0,sourceCount). The operation verifier does not check selector bounds.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.extract_rows">
|
||
<h3><a href="#spat.extract_rows"><code>spat.extract_rows</code></a> — Split a matrix into row values</h3>
|
||
<pre data-field="syntax"><code>%rows:2 = spat.extract_rows %matrix : tensor<2x4xf32> -> tensor<1x4xf32> x2</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%matrix</code>): a rank-2 matrix with shape R×C, here 2×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">outputs (<code>%rows#0</code>, <code>%rows#1</code>): R separate rank-2 tensors with shape 1×C, in row order. See <a href="#compact">multiple-result notation</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Result i contains input[i:i+1, :]. The example produces two 1×4 row values and retains the size-one row dimension.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints">Verified: rank-2 input; output count equals R when R is static. Each output has rank 2, first dimension 1, matching element type, and matching C when C is static. Dynamic dimensions receive the checks that can be established statically. Lowering creates static tensor.extract_slice operations and requires static ranked tensors before <a href="#destinations">bufferization</a>. Although ODS accepts memrefs, this lowering requires tensors.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.flatten_plan">
|
||
<h3><a href="#spat.flatten_plan"><code>spat.flatten_plan</code></a> — Plan logical flattening</h3>
|
||
<pre data-field="syntax"><code>%y = "spat.flatten_plan"(%x) <{axis = 1 : i64, logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>) -> tensor<1x24xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): a logical tensor, here with NCHW shape 1×2×3×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the logical result with the printed shape. Physical representation is selected through <a href="#planning">layout planning</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>logicalLayout</code>: required logical coordinate order, <code>nchw</code> in the example. See <a href="#planning">planning operations and attributes</a>. <code>axis = 1 : i64</code> selects the division between the two dimension products.</dd>
|
||
<dt>Semantics</dt><dd data-field="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 <a href="#input-representations">compute input representations</a>.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#input-representations">input representation mapping</a>.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.global_average_pool_plan">
|
||
<h3><a href="#spat.global_average_pool_plan"><code>spat.global_average_pool_plan</code></a> — Plan global spatial mean</h3>
|
||
<pre data-field="syntax"><code>%y = "spat.global_average_pool_plan"(%x) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>) -> tensor<1x2x1x1xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): an NCHW tensor, here with two channels and spatial shape 3×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the logical result with the printed shape. Physical representation is selected through <a href="#planning">layout planning</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>logicalLayout</code>: required logical coordinate order, <code>nchw</code> in the example. See <a href="#planning">planning operations and attributes</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Averages all H×W elements independently for each channel. The example averages 12 elements per channel and returns shape 1×2×1×1.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.graph_compute">
|
||
<h3><a href="#spat.graph_compute"><code>spat.graph_compute</code></a> — Logical compute node</h3>
|
||
<pre data-field="syntax"><code>%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>
|
||
}</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">inputs (<code>%x</code>): runtime values bound to body arguments, here <code>%arg</code>. The empty square brackets declare no weights in this example. Weighted bindings are shown under <a href="#compute-regions">compute regions and bindings</a>. Input coordinates follow the <a href="#input-representations">producer representation</a>.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the value returned by <a href="#spat.yield"><code>spat.yield</code></a>, with the same complete type. Here, the body returns one 1×4 tensor. The scalar compute introduces no fragment-slot dimension.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>coreId</code>: optional placement ID. <code>crossbarWeights</code>: the derived count of distinct resident weights used in the body; see <a href="#compute-regions">compute regions and bindings</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Describes a graph computation before scheduling. The example applies <a href="#spat.relu"><code>spat.relu</code></a> to the bound input and returns its result through <a href="#spat.yield"><code>spat.yield</code></a>. Weight and input bindings define dataflow; memory allocation occurs later.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints"><p>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.</p><p>Weights must be compile-time computable. Nonconstant external captures require explicit input bindings, except recorded <a href="#spat.deferred_communication">deferred communication sources</a>. Direct capture of another compute result is rejected. Graph regions can contain structural blocks; scheduling must resolve their dependencies.</p><p>Target lowering and emission check hardware capacity for the selected target.</p></dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.graph_compute_batch">
|
||
<h3><a href="#spat.graph_compute_batch"><code>spat.graph_compute_batch</code></a> — Logical batch with an SSA fragment collection</h3>
|
||
<pre data-field="syntax"><code>%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>
|
||
}
|
||
}</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): one logical 1×8 tensor bound as <code>%arg</code> 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 <a href="#input-representations">input representations</a> for other supported coordinate forms.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): one SSA collection with two fragment slots, each containing a 1×4 tensor. In general, each result has shape [laneCount]+shape(fragment). <code>shared_outs(%out)</code> declares the corresponding body argument without an external destination operand. Resultful bodies publish through <a href="#spat.in_parallel"><code>spat.in_parallel</code></a>; resultless bodies end with an empty <a href="#spat.yield"><code>spat.yield</code></a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>laneCount</code>: positive i32 printed as the exclusive upper bound of the lane range. <code>coreIds</code>: optional dense i32 placement list with one distinct nonnegative core ID per lane. <code>crossbarWeights</code>: a derived resident-weight count. The <a href="#scheduling">scheduling example</a> distinguishes lanes and cores.</dd>
|
||
<dt>Semantics</dt><dd data-field="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 <a href="#graph-batches">graph batch example</a> shows how <a href="#spat.blueprint"><code>spat.blueprint</code></a> 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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#spat.in_parallel"><code>spat.in_parallel</code></a>; resultless blocks end in <a href="#spat.yield"><code>spat.yield</code></a> 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 <a href="#graph-batches">graph fragment contract</a> defines construction and reconstruction requirements.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.host_store_sync">
|
||
<h3><a href="#spat.host_store_sync"><code>spat.host_store_sync</code></a> — Publish through shared host memory</h3>
|
||
<pre data-field="syntax"><code>spat.host_store_sync %x from %source to %target host_offset %offset event %event : tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the tensor payload. sourceCoreId (<code>%source</code>), targetCoreId (<code>%target</code>), hostOffset (<code>%offset</code>), and eventRegister (<code>%event</code>): index SSA operands.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">No SSA results.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required. The hostOffset operand gives the byte offset within the pipeline host buffer.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Stores the payload at hostOffset, then signals its availability to the consumer. Lowers to <a href="#pim.memcp_dh"><code>pim.memcp_dh</code></a> followed by <a href="#pim.sync"><code>pim.sync</code></a>. The matching <a href="#spat.host_wait_load"><code>spat.host_wait_load</code></a> waits before reading this host slot.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#communication">signal/wait contract</a>. The <a href="../../.agents/invariants/PIM_SYNCHRONIZATION_INVARIANT.md">synchronization invariant</a> 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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.host_wait_load">
|
||
<h3><a href="#spat.host_wait_load"><code>spat.host_wait_load</code></a> — Wait and read a host payload</h3>
|
||
<pre data-field="syntax"><code>%y = spat.host_wait_load from %source to %target host_offset %offset event %event count %count : tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">sourceCoreId (<code>%source</code>), targetCoreId (<code>%target</code>), hostOffset (<code>%offset</code>), eventRegister (<code>%event</code>), and waitValue (<code>%count</code>): index SSA operands.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output: payload loaded into local storage.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required. The hostOffset operand gives the byte offset within the pipeline host buffer.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Waits for the READY event from <a href="#spat.host_store_sync"><code>spat.host_store_sync</code></a>, then loads the host payload. Lowers to <a href="#pim.wait"><code>pim.wait</code></a> followed by <a href="#pim.memcp_hd"><code>pim.memcp_hd</code></a>. For repeated execution, reader-entry FREE signals protect host-slot reuse, as described in the <a href="#communication">synchronization contract</a>.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#communication">signal/wait contract</a>. The <a href="../../.agents/invariants/PIM_SYNCHRONIZATION_INVARIANT.md">synchronization invariant</a> 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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.in_parallel">
|
||
<h3><a href="#spat.in_parallel"><code>spat.in_parallel</code></a> — Publish lane fragments</h3>
|
||
<pre data-field="syntax"><code>spat.in_parallel {
|
||
tensor.parallel_insert_slice %v into %out[%lane, 0, 0] [1, 1, 4] [1, 1, 1] : tensor<1x4xf32> into tensor<2x1x4xf32>
|
||
}</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">The nested insertion reads <code>%v</code>, a 1×4 lane fragment, and targets <code>%out</code>, the parent batch output argument. These are operands of tensor.parallel_insert_slice; <a href="#spat.in_parallel"><code>spat.in_parallel</code></a> has no direct operands.</dd>
|
||
<dt>Outputs</dt><dd data-field="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 <a href="#graph-batches">graph batch example</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="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 <a href="#spat.blueprint"><code>spat.blueprint</code></a> supplies logical placement; arithmetic reductions belong in compute operations.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints">Verified: the parent is <a href="#spat.graph_compute_batch">spat.graph_compute_batch</a> or <a href="#spat.scheduled_compute_batch">spat.scheduled_compute_batch</a> 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 <a href="#graph-batches">fragment-slot contract</a>. Independent lanes must write disjoint destinations.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.materialize_layout">
|
||
<h3><a href="#spat.materialize_layout"><code>spat.materialize_layout</code></a> — Convert a physical layout</h3>
|
||
<pre data-field="syntax"><code>%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></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): a logical tensor in sourcePhysicalLayout. The example uses logical NCHW shape 1×2×3×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the logical result with the printed shape. Physical representation is selected through <a href="#planning">layout planning</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>logicalLayout</code>: required logical coordinate order, <code>nchw</code> in the example. See <a href="#planning">planning operations and attributes</a>. <code>sourcePhysicalLayout</code> and <code>targetPhysicalLayout</code> identify the source and destination storage layouts. Supported conversions are listed in the <a href="../compiler/spatial-layouts.md">layout reference</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="../compiler/spatial-layouts.md">supported conversion list</a>.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.max_pool2d_plan">
|
||
<h3><a href="#spat.max_pool2d_plan"><code>spat.max_pool2d_plan</code></a> — Plan 2D max pooling</h3>
|
||
<pre data-field="syntax"><code>%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></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): an NCHW tensor, here with two channels and spatial shape 4×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the logical result with the printed shape. Physical representation is selected through <a href="#planning">layout planning</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>logicalLayout</code>: required logical coordinate order, <code>nchw</code> in the example. See <a href="#planning">planning operations and attributes</a>. <code>kernelShape</code>: [height,width] window size in elements. <code>pads</code>: [top,left,bottom,right] in elements. <code>strides</code> and <code>dilations</code>: [height,width] element steps. The <code>array<i64: ...></code> notation stores these integer lists.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.relu">
|
||
<h3><a href="#spat.relu"><code>spat.relu</code></a> — Element-wise ReLU</h3>
|
||
<pre data-field="syntax"><code>%y = spat.relu(%x) : tensor<1x4xf32> -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the complete computation slice, here a 1×4 tensor.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): one value per input element, here a 1×4 tensor.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">y[i] = max(x[i],0). The formula applies independently to all four elements in this example.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#contracts">code generation requirements</a> apply to static storage and backend element types.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.relu_plan">
|
||
<h3><a href="#spat.relu_plan"><code>spat.relu_plan</code></a> — Plan ReLU</h3>
|
||
<pre data-field="syntax"><code>%y = "spat.relu_plan"(%x) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>) -> tensor<1x2x3x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): a logical tensor, here with NCHW shape 1×2×3×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the logical result with the printed shape. Physical representation is selected through <a href="#planning">layout planning</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>logicalLayout</code>: required logical coordinate order, <code>nchw</code> in the example. See <a href="#planning">planning operations and attributes</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Applies y[i] = max(x[i],0) independently to each element. Logical coordinates and shape are preserved.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.resize_nearest_plan">
|
||
<h3><a href="#spat.resize_nearest_plan"><code>spat.resize_nearest_plan</code></a> — Plan nearest-neighbor resize</h3>
|
||
<pre data-field="syntax"><code>%y = "spat.resize_nearest_plan"(%x) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x2x2xf32>) -> tensor<1x2x4x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): an NCHW tensor, here with two channels and spatial shape 2×2.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the logical result with the printed shape. Physical representation is selected through <a href="#planning">layout planning</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>logicalLayout</code>: required logical coordinate order, <code>nchw</code> in the example. See <a href="#planning">planning operations and attributes</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.scheduled_compute">
|
||
<h3><a href="#spat.scheduled_compute"><code>spat.scheduled_compute</code></a> — Placed compute node</h3>
|
||
<pre data-field="syntax"><code>%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>
|
||
}</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">inputs (<code>%x</code>): runtime operands bound to body arguments, here <code>%arg</code>. Square brackets bind constant weight resources when present. See <a href="#compute-regions">compute regions and bindings</a>.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the value returned by <a href="#spat.yield"><code>spat.yield</code></a>, with the same complete type. The example returns a 1×4 ReLU result.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>coreId</code>: optional placement ID. <code>crossbarWeights</code>: the derived count of distinct resident weights used in the body; see <a href="#compute-regions">compute regions and bindings</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Describes a computation after scheduling. The example applies <a href="#spat.relu"><code>spat.relu</code></a> on its assigned core. Inputs become available through local SSA forwarding or explicit <a href="#spat.channel_send"><code>spat.channel_send</code></a> and <a href="#spat.channel_receive"><code>spat.channel_receive</code></a> operations.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints"><p>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.</p><p>Weights must be compile-time computable. Nonconstant external captures require explicit input bindings, except recorded <a href="#spat.deferred_communication">deferred communication sources</a>. Direct capture of another compute result is rejected. Scheduled scalar regions require exactly one block ending in <a href="#spat.yield">spat.yield</a>.</p><p>Target lowering and emission check hardware capacity for the selected target.</p></dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.scheduled_compute_batch">
|
||
<h3><a href="#spat.scheduled_compute_batch"><code>spat.scheduled_compute_batch</code></a> — Placed batch of equivalent computations</h3>
|
||
<pre data-field="syntax"><code>%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>
|
||
}
|
||
}</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): a 2×1×4 collection bound as <code>%arg</code>. 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 <a href="#input-representations">input representations</a>.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): one SSA result with body argument <code>%out</code>. Here, lane insertions fill a 2×1×4 result. Other result shapes follow scheduled execution and need not begin with laneCount. Resultful bodies use <a href="#spat.in_parallel"><code>spat.in_parallel</code></a>; resultless bodies use an empty <a href="#spat.yield"><code>spat.yield</code></a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>laneCount</code>: positive i32 printed as the exclusive upper bound of the lane range. <code>coreIds</code>: optional dense i32 placement list with one distinct nonnegative core ID per lane. <code>crossbarWeights</code>: a derived resident-weight count. The <a href="#scheduling">scheduling example</a> distinguishes lanes and cores.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Groups equivalent scheduled computations in one body. Here, each lane reads a fragment, applies ReLU, and publishes the updated fragment. <a href="#scheduling">Scheduled lane placement</a> determines which core executes each instance. Local forwarding and explicit communication determine input availability.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#spat.in_parallel"><code>spat.in_parallel</code></a>; resultless blocks end in <a href="#spat.yield"><code>spat.yield</code></a> 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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.sigmoid">
|
||
<h3><a href="#spat.sigmoid"><code>spat.sigmoid</code></a> — Element-wise sigmoid</h3>
|
||
<pre data-field="syntax"><code>%y = spat.sigmoid(%x) : tensor<1x4xf32> -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the complete computation slice, here a 1×4 tensor.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): one value per input element, here a 1×4 tensor.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">y[i] = 1/(1+exp(-x[i])). The formula applies independently to all four elements in this example.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#contracts">code generation requirements</a> apply to static storage and backend element types.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.silu_plan">
|
||
<h3><a href="#spat.silu_plan"><code>spat.silu_plan</code></a> — Plan SiLU</h3>
|
||
<pre data-field="syntax"><code>%y = "spat.silu_plan"(%x) <{logicalLayout = #spat<logical_layout nchw>}> : (tensor<1x2x3x4xf32>) -> tensor<1x2x3x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): a logical tensor, here with NCHW shape 1×2×3×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the logical result with the printed shape. Physical representation is selected through <a href="#planning">layout planning</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>logicalLayout</code>: required logical coordinate order, <code>nchw</code> in the example. See <a href="#planning">planning operations and attributes</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Applies y[i] = x[i]/(1+exp(-x[i])) independently to each element. Logical coordinates and shape are preserved.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.softmax">
|
||
<h3><a href="#spat.softmax"><code>spat.softmax</code></a> — Softmax of a complete slice</h3>
|
||
<pre data-field="syntax"><code>%y = spat.softmax(%x) : tensor<1x4xf32> -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the complete computation slice, here a 1×4 tensor.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): one value per input element, here a 1×4 tensor.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">y[i] = exp(x[i])/sum_j exp(x[j]). The sum covers all four input elements in this example.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.sync">
|
||
<h3><a href="#spat.sync"><code>spat.sync</code></a> — Signal an event register</h3>
|
||
<pre data-field="syntax"><code>spat.sync %target event %event</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">targetCoreId (<code>%target</code>): the destination core ID. eventRegister (<code>%event</code>): its local event-register index. Both are index SSA operands.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">No SSA results.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Increments the selected event register on the target core. Lowers to <a href="#pim.sync"><code>pim.sync</code></a>; the matching <a href="#spat.wait"><code>spat.wait</code></a> executes on that target. See the <a href="#communication">paired signal and wait examples</a>.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#communication">signal/wait contract</a>. The <a href="../../.agents/invariants/PIM_SYNCHRONIZATION_INVARIANT.md">synchronization invariant</a> requires exact counts, separate event roles, host-slot lifetime protection, and restart ordering. These requirements prevent overshoot, register collisions, missing signals, and deadlock.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.vadd">
|
||
<h3><a href="#spat.vadd"><code>spat.vadd</code></a> — Element-wise addition</h3>
|
||
<pre data-field="syntax"><code>%y = spat.vadd %a, %b : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">lhs (<code>%a</code>) and rhs (<code>%b</code>): shaped numeric operands, each 1×4 in the example.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the element-wise result, here with shape 1×4.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">y[i] = lhs[i] + rhs[i], independently for every element in the supplied slice.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#contracts">code generation requirements</a>.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.vavg">
|
||
<h3><a href="#spat.vavg"><code>spat.vavg</code></a> — Average a complete slice</h3>
|
||
<pre data-field="syntax"><code>%y = spat.vavg(%x) : tensor<1x4xf32> -> tensor<1x1xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the complete computation slice, here a 1×4 tensor.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): one average value in a 1×1 tensor.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">y[0,0] = sum(x)/number_of_elements(x). The sum covers all four input elements in this example.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.vmax">
|
||
<h3><a href="#spat.vmax"><code>spat.vmax</code></a> — Element-wise maximum</h3>
|
||
<pre data-field="syntax"><code>%y = spat.vmax %a, %b : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">lhs (<code>%a</code>) and rhs (<code>%b</code>): shaped numeric operands, each 1×4 in the example.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the element-wise result, here with shape 1×4.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">y[i] = max(lhs[i], rhs[i]), independently for every element in the supplied slice.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints">Verified: lhs, rhs, and output have exactly the same complete type. Producers must expand broadcasts to matching shapes before this operation. The <a href="#contracts">code generation requirements</a> apply to static sizes and numeric types.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.vmul">
|
||
<h3><a href="#spat.vmul"><code>spat.vmul</code></a> — Element-wise multiplication</h3>
|
||
<pre data-field="syntax"><code>%y = spat.vmul %a, %b : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">lhs (<code>%a</code>) and rhs (<code>%b</code>): shaped numeric operands, each 1×4 in the example.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the element-wise result, here with shape 1×4.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">y[i] = lhs[i] * rhs[i], independently for every element in the supplied slice.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints">ODS checks shaped operand types; this operation has no custom shape verifier. Lowering to <a href="#pim.vvmul"><code>pim.vvmul</code></a> 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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.vsub">
|
||
<h3><a href="#spat.vsub"><code>spat.vsub</code></a> — Element-wise subtraction</h3>
|
||
<pre data-field="syntax"><code>%y = spat.vsub %a, %b : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">lhs (<code>%a</code>) and rhs (<code>%b</code>): shaped numeric operands, each 1×4 in the example.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the element-wise result, here with shape 1×4.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">y[i] = lhs[i] - rhs[i].</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints">Verified: lhs, rhs, and output have exactly the same complete type. Producers must expand any broadcast to matching shapes before this operation.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.vvdmul">
|
||
<h3><a href="#spat.vvdmul"><code>spat.vvdmul</code></a> — Dot product of runtime vectors</h3>
|
||
<pre data-field="syntax"><code>%y = spat.vvdmul %a, %b : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x1xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">lhs (<code>%a</code>) and rhs (<code>%b</code>): rank-2 row vectors with matching shape 1×N, here 1×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): a 1×1 tensor containing the dot product.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">output[0,0] = sum_i lhs[0,i]*rhs[0,i]. Both vectors are runtime operands; no resident crossbar weight is required.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints">Verified: ranked shaped types; rank 2 throughout; identical element types; lhs/rhs shapes equal 1×N with N>0; output exactly 1×1.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.wait">
|
||
<h3><a href="#spat.wait"><code>spat.wait</code></a> — Wait for an exact event count</h3>
|
||
<pre data-field="syntax"><code>spat.wait %event value %count</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">eventRegister (<code>%event</code>): a register on the executing core. waitValue (<code>%count</code>): the exact expected signal count. Both are index SSA operands.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">No SSA results.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Blocks until the local event register equals waitValue, then resets it to zero. Lowers to <a href="#pim.wait"><code>pim.wait</code></a>. The corresponding <a href="#spat.sync"><code>spat.sync</code></a> operations must supply exactly that count.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#communication">signal/wait contract</a>. The <a href="../../.agents/invariants/PIM_SYNCHRONIZATION_INVARIANT.md">synchronization invariant</a> requires exact counts, separate event roles, host-slot lifetime protection, and restart ordering. These requirements prevent overshoot, register collisions, missing signals, and deadlock.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.wvmm">
|
||
<h3><a href="#spat.wvmm"><code>spat.wvmm</code></a> — Row vector times explicit constant matrix</h3>
|
||
<pre data-field="syntax"><code>%y = spat.wvmm[%weight](%x) : (tensor<4x3xf32>, tensor<1x4xf32>) -> tensor<1x3xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">weight (<code>%weight</code>): a matrix W with shape N×M, here 4×3. input (<code>%x</code>): a row vector with shape 1×N, here 1×4. The <a href="#compute-regions">weighted compute example</a> shows their region bindings.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): a row vector with shape 1×M, here 1×3.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required. The weight matrix is the explicit operand in square brackets.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="spat.yield">
|
||
<h3><a href="#spat.yield"><code>spat.yield</code></a> — Return region values</h3>
|
||
<pre data-field="syntax"><code>spat.yield %v : tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">outputs (<code>%v</code>): values returned by the enclosing region. The example returns one 1×4 tensor; each expanded operand has a corresponding printed type.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">The operation has no direct SSA results. Its operands become the enclosing compute or deferred-region results.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Terminates the enclosing region and returns its values, as shown in the <a href="#compute-regions">compute example</a>. A resultless batch uses the empty form <code>spat.yield :</code>. A resultful batch publishes through <a href="#spat.in_parallel"><code>spat.in_parallel</code></a> instead.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#spat.in_parallel"><code>spat.in_parallel</code></a> instead.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Spatial/Spatial.td">definition</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Spatial/SpatialOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Conversion/SpatialToPim/SpatialToPim.td">arithmetic lowering</a>.</p></article>
|
||
<article id="pim.concat">
|
||
<h3><a href="#pim.concat"><code>pim.concat</code></a> — Concatenate into a destination</h3>
|
||
<pre data-field="syntax"><code>%y = pim.concat axis 1 %a, %b into %dst : (tensor<1x2xf32> x2) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">inputs (<code>%a</code>, <code>%b</code>): ordered 1×2 tensors. outputBuffer (<code>%dst</code>): the destination after <code>into</code>, with type inferred from the 1×4 result.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, following the <a href="#destinations">PIM destination contract</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">axis: required i64 printed positionally; duplicate attr-dict axis is rejected.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.core">
|
||
<h3><a href="#pim.core"><code>pim.core</code></a> — Execute one core stream</h3>
|
||
<pre data-field="syntax"><code>pim.core [] = [] coreId 0 : [] -> () {
|
||
pim.halt
|
||
}</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">weights: constant resources bound to body arguments in square brackets. The shown <code>[] = []</code> binding is empty. The <a href="#compute-regions">weighted compute example</a> explains the same binding convention.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">No SSA results; results are communicated or written to host memory.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>coreId</code>: required i32 placement ID, printed as coreId 0 in the example. Before emission, <a href="#contracts">memory planning</a> adds pim.local_memory_size and allocation addresses.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Executes one PIM instruction stream. This example contains only <a href="#pim.halt"><code>pim.halt</code></a>. A stream with weighted arithmetic passes a bound weight operand to <a href="#pim.vmm"><code>pim.vmm</code></a>. Code generation can remap the core ID.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#pim.halt"><code>pim.halt</code></a>.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.core_batch">
|
||
<h3><a href="#pim.core_batch"><code>pim.core_batch</code></a> — Execute equivalent core streams</h3>
|
||
<pre data-field="syntax"><code>pim.core_batch %lane = 0 to 2 [] = [] () = () coreIds [0, 1] : [] () -> () {
|
||
pim.halt
|
||
}</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="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.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">No SSA results; each lane executes its stream and produces memory/communication effects.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>laneCount</code>: positive i32 printed as the exclusive upper bound, here 2. <code>coreIds</code>: dense i32 placement list required by code generation, here [0,1]. See <a href="#scheduling">lane and core ranges</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="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 <a href="#pim.halt"><code>pim.halt</code></a>. For larger bodies, lane expressions determine static addresses and peers while weight operands retain their common SSA definitions.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#pim.core">core capture rules</a> apply. Later verification requires a valid core mapping, static lane expressions, constant weight resources, and explicit local memory plans. Transfers must satisfy the <a href="#communication">communication and synchronization contract</a>. Each body ends with <a href="#pim.halt">pim.halt</a>.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.halt">
|
||
<h3><a href="#pim.halt"><code>pim.halt</code></a> — Stop a core stream</h3>
|
||
<pre data-field="syntax"><code>pim.halt</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">None.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">None.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Terminates the enclosing core stream and emits the backend halt instruction. The <a href="#pim.core"><code>pim.core</code></a> and <a href="#pim.core_batch"><code>pim.core_batch</code></a> examples show its position at the end of the body.</dd>
|
||
<dt>Constraints</dt><dd data-field="constraints">The terminator trait requires this operation at the end of its block. It is intended for <a href="#pim.core"><code>pim.core</code></a> and <a href="#pim.core_batch"><code>pim.core_batch</code></a> bodies; its declaration has no custom parent check.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.memcp">
|
||
<h3><a href="#pim.memcp"><code>pim.memcp</code></a> — Copy device-local bytes</h3>
|
||
<pre data-field="syntax"><code>%y = pim.memcp[%dstOffset, %srcOffset](%dst, %src) {size = 16 : i32} : tensor<1x4xf32>, tensor<1x4xf32> -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs"><code>%dst</code>: local destination. <code>%src</code>: local source. <code>%dstOffset</code> and <code>%srcOffset</code>: their respective index SSA byte offsets. The destination appears before the source.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated full destination. Its type describes the destination buffer, including bytes outside the copied range.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>size</code>: required i32 byte count, here 16. See the <a href="#memory-units">copy example and units</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.memcp_dh">
|
||
<h3><a href="#pim.memcp_dh"><code>pim.memcp_dh</code></a> — Copy device to host</h3>
|
||
<pre data-field="syntax"><code>%y = pim.memcp_dh[%dstOffset, %srcOffset](%dst, %src) {size = 16 : i32} : tensor<1x4xf32>, tensor<1x4xf32> -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs"><code>%dst</code>: host destination. <code>%src</code>: local device source. <code>%dstOffset</code> and <code>%srcOffset</code>: their respective index SSA byte offsets. The destination appears before the source.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated full destination. Its type describes the destination buffer, including bytes outside the copied range.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>size</code>: required i32 byte count, here 16. See the <a href="#memory-units">copy example and units</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.memcp_hd">
|
||
<h3><a href="#pim.memcp_hd"><code>pim.memcp_hd</code></a> — Copy host to device</h3>
|
||
<pre data-field="syntax"><code>%y = pim.memcp_hd[%dstOffset, %srcOffset](%dst, %src) {size = 16 : i32} : tensor<1x4xf32>, tensor<1x4xf32> -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs"><code>%dst</code>: local device destination. <code>%src</code>: host source. <code>%dstOffset</code> and <code>%srcOffset</code>: their respective index SSA byte offsets. The destination appears before the source.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated full destination. Its type describes the destination buffer, including bytes outside the copied range.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>size</code>: required i32 byte count, here 16. See the <a href="#memory-units">copy example and units</a>.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.receive">
|
||
<h3><a href="#pim.receive"><code>pim.receive</code></a> — Receive into local storage</h3>
|
||
<pre data-field="syntax"><code>%y = pim.receive[%offset](%dst, %source) {size = 16 : i32} : tensor<1x4xf32> -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">outputBuffer (<code>%dst</code>): local destination storage. outputOffset (<code>%offset</code>): index SSA byte offset. sourceCoreId (<code>%source</code>): index SSA ID of the sending core.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, following the <a href="#destinations">PIM destination contract</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>size</code>: required i32 byte count, here 16.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Receives size bytes from the source core into %dst+%offset. The matching <a href="#pim.send"><code>pim.send</code></a> supplies the payload. See the <a href="#communication">direct-transfer example</a>.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.send">
|
||
<h3><a href="#pim.send"><code>pim.send</code></a> — Send local data</h3>
|
||
<pre data-field="syntax"><code>pim.send(%x, %target) {size = 16 : i32} : tensor<1x4xf32> -> ()</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the local payload. targetCoreId (<code>%target</code>): the index SSA ID of the receiving core.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">No SSA results.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>size</code>: required i32 byte count, here 16.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Sends size bytes from the start of the input to the target core. Pair it with <a href="#pim.receive"><code>pim.receive</code></a> using the same byte count. The <a href="#communication">direct-transfer example</a> shows both sides.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#pim.receive"><code>pim.receive</code></a> must use matching endpoints, byte count, and order. Incorrect transfer order can deadlock.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.sync">
|
||
<h3><a href="#pim.sync"><code>pim.sync</code></a> — Signal an event register</h3>
|
||
<pre data-field="syntax"><code>pim.sync %target event %event</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">targetCoreId (<code>%target</code>): the destination core ID. eventRegister (<code>%event</code>): its local event-register index. Both are index SSA operands.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">No SSA results.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Emits SYNC to increment the selected target-core event register. A matching <a href="#pim.wait"><code>pim.wait</code></a> consumes the expected count on that core. Synchronization-disable mode omits this instruction.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#communication">signal/wait contract</a>. The <a href="../../.agents/invariants/PIM_SYNCHRONIZATION_INVARIANT.md">synchronization invariant</a> requires exact counts, separate event roles, host-slot lifetime protection, and restart ordering. These requirements prevent overshoot, register collisions, missing signals, and deadlock.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.transpose">
|
||
<h3><a href="#pim.transpose"><code>pim.transpose</code></a> — Permute dimensions into a destination</h3>
|
||
<pre data-field="syntax"><code>%y = pim.transpose(%x, %dst) {permutation = [1, 0]} : (tensor<2x3xf32>, tensor<3x2xf32>) -> tensor<3x2xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the 2×3 source. outputBuffer (<code>%dst</code>): the 3×2 destination in the example.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the destination containing the permuted data, following the <a href="#destinations">PIM destination contract</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>permutation</code>: required i64 array with one source dimension index per output dimension. Here, [1,0] exchanges rows and columns.</dd>
|
||
<dt>Semantics</dt><dd data-field="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 <a href="#pim.memcp"><code>pim.memcp</code></a> for a storage-preserving case. Other permutations use static runs of <a href="#pim.memcp"><code>pim.memcp</code></a> or <a href="#pim.vmv"><code>pim.vmv</code></a> before final emission.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#pim.transpose"><code>pim.transpose</code></a> is not a final executable instruction.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vavg">
|
||
<h3><a href="#pim.vavg"><code>pim.vavg</code></a> — Average a complete vector</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vavg(%x, %dst) : (tensor<1x4xf32>, tensor<1x1xf32>) -> tensor<1x1xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the complete slice, here 1×4. outputBuffer (<code>%dst</code>): the destination, here 1×1.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, following the <a href="#destinations">PIM destination contract</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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. <a href="#memory-units">Element counts and bitwidths</a> determine instruction lengths. The <a href="#contracts">code generation requirements</a> specify storage and backend limits.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vmm">
|
||
<h3><a href="#pim.vmm"><code>pim.vmm</code></a> — Multiply using a resident array group</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vmm[%weight](%x, %dst) : (tensor<4x3xf32>, tensor<1x4xf32>, tensor<1x3xf32>) -> tensor<1x3xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">weight (<code>%weight</code>): a resident matrix resource, here 4×3. input (<code>%x</code>): a positive-width rank-2 row vector, here 1×4. outputBuffer (<code>%dst</code>): a 1×3 destination.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, with width equal to the matrix column count.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required. The square-bracket operand selects the resident weight resource.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vmv">
|
||
<h3><a href="#pim.vmv"><code>pim.vmv</code></a> — Gather a strided local vector</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vmv[%dstOffset, %srcOffset, %stride](%dst, %src) {length = 4 : i32} : tensor<1x4xf32>, tensor<1x8xf32> -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">target (<code>%dst</code>): contiguous destination. source (<code>%src</code>): source storage. <code>%dstOffset</code> and <code>%srcOffset</code>: index SSA byte offsets. <code>%stride</code>: index SSA stride measured in source elements.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated full destination.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes"><code>length</code>: required positive i32 element count, here 4. The <a href="#memory-units">strided-copy example</a> shows how element stride relates to byte offsets.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vrelu">
|
||
<h3><a href="#pim.vrelu"><code>pim.vrelu</code></a> — Element-wise ReLU</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vrelu(%x, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the complete slice, here 1×4. outputBuffer (<code>%dst</code>): the destination, here 1×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, following the <a href="#destinations">PIM destination contract</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">y[i] = max(x[i],0). The example processes all four input elements and writes four values to the 1×4 destination.</dd>
|
||
<dt>Constraints</dt><dd data-field="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. <a href="#memory-units">Element counts and bitwidths</a> determine instruction lengths. The <a href="#contracts">code generation requirements</a> specify storage and backend limits.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vsigm">
|
||
<h3><a href="#pim.vsigm"><code>pim.vsigm</code></a> — Element-wise sigmoid</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vsigm(%x, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the complete slice, here 1×4. outputBuffer (<code>%dst</code>): the destination, here 1×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, following the <a href="#destinations">PIM destination contract</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">y[i] = 1/(1+exp(-x[i])). The example processes all four input elements and writes four values to the 1×4 destination.</dd>
|
||
<dt>Constraints</dt><dd data-field="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. <a href="#memory-units">Element counts and bitwidths</a> determine instruction lengths. The <a href="#contracts">code generation requirements</a> specify storage and backend limits.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vsoftmax">
|
||
<h3><a href="#pim.vsoftmax"><code>pim.vsoftmax</code></a> — Softmax of a complete vector</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vsoftmax(%x, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the complete slice, here 1×4. outputBuffer (<code>%dst</code>): the destination, here 1×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, following the <a href="#destinations">PIM destination contract</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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. <a href="#memory-units">Element counts and bitwidths</a> determine instruction lengths. The <a href="#contracts">code generation requirements</a> specify storage and backend limits.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vtanh">
|
||
<h3><a href="#pim.vtanh"><code>pim.vtanh</code></a> — Element-wise hyperbolic tangent</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vtanh(%x, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">input (<code>%x</code>): the complete slice, here 1×4. outputBuffer (<code>%dst</code>): the destination, here 1×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, following the <a href="#destinations">PIM destination contract</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">y[i] = tanh(x[i]). The example processes all four input elements and writes four values to the 1×4 destination.</dd>
|
||
<dt>Constraints</dt><dd data-field="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. <a href="#memory-units">Element counts and bitwidths</a> determine instruction lengths. The <a href="#contracts">code generation requirements</a> specify storage and backend limits.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vvadd">
|
||
<h3><a href="#pim.vvadd"><code>pim.vvadd</code></a> — Element-wise addition</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vvadd(%a, %b, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">lhs (<code>%a</code>) and rhs (<code>%b</code>): matching numeric slices, here 1×4. outputBuffer (<code>%dst</code>): the third operand, here 1×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, containing one value per input element. See <a href="#destinations">PIM destinations</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#memory-units">instruction units</a> and <a href="#contracts">backend requirements</a>.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vvdmul">
|
||
<h3><a href="#pim.vvdmul"><code>pim.vvdmul</code></a> — Dot product</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vvdmul(%a, %b, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>, tensor<1x1xf32>) -> tensor<1x1xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">lhs (<code>%a</code>) and rhs (<code>%b</code>): matching numeric slices, here 1×4. outputBuffer (<code>%dst</code>): the third operand, here 1×1.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, containing one dot-product value. See <a href="#destinations">PIM destinations</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#memory-units">instruction units</a> and <a href="#contracts">backend requirements</a>.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vvmax">
|
||
<h3><a href="#pim.vvmax"><code>pim.vvmax</code></a> — Element-wise maximum</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vvmax(%a, %b, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">lhs (<code>%a</code>) and rhs (<code>%b</code>): matching numeric slices, here 1×4. outputBuffer (<code>%dst</code>): the third operand, here 1×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, containing one value per input element. See <a href="#destinations">PIM destinations</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#memory-units">instruction units</a> and <a href="#contracts">backend requirements</a>.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vvmul">
|
||
<h3><a href="#pim.vvmul"><code>pim.vvmul</code></a> — Element-wise multiplication</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vvmul(%a, %b, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">lhs (<code>%a</code>) and rhs (<code>%b</code>): matching numeric slices, here 1×4. outputBuffer (<code>%dst</code>): the third operand, here 1×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, containing one value per input element. See <a href="#destinations">PIM destinations</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#memory-units">instruction units</a> and <a href="#contracts">backend requirements</a>.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.vvsub">
|
||
<h3><a href="#pim.vvsub"><code>pim.vvsub</code></a> — Element-wise subtraction</h3>
|
||
<pre data-field="syntax"><code>%y = pim.vvsub(%a, %b, %dst) : (tensor<1x4xf32>, tensor<1x4xf32>, tensor<1x4xf32>) -> tensor<1x4xf32></code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">lhs (<code>%a</code>) and rhs (<code>%b</code>): matching numeric slices, here 1×4. outputBuffer (<code>%dst</code>): the third operand, here 1×4.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">output (<code>%y</code>): the updated destination, containing one value per input element. See <a href="#destinations">PIM destinations</a>.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="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.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#memory-units">instruction units</a> and <a href="#contracts">backend requirements</a>.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
<article id="pim.wait">
|
||
<h3><a href="#pim.wait"><code>pim.wait</code></a> — Wait for an exact event count</h3>
|
||
<pre data-field="syntax"><code>pim.wait %event value %count</code></pre><dl>
|
||
<dt>Inputs</dt><dd data-field="inputs">eventRegister (<code>%event</code>): a register on the executing core. waitValue (<code>%count</code>): the exact expected signal count. Both are index SSA operands.</dd>
|
||
<dt>Outputs</dt><dd data-field="outputs">No SSA results.</dd>
|
||
<dt>Attributes</dt><dd data-field="attributes">None required.</dd>
|
||
<dt>Semantics</dt><dd data-field="semantics">Emits WAIT, which blocks until the local register equals waitValue and then resets it to zero. Matching <a href="#pim.sync"><code>pim.sync</code></a> operations supply the count. Emission omits zero-count waits and omits all waits in synchronization-disable mode.</dd>
|
||
<dt>Constraints</dt><dd data-field="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 <a href="#communication">signal/wait contract</a>. The <a href="../../.agents/invariants/PIM_SYNCHRONIZATION_INVARIANT.md">synchronization invariant</a> requires exact counts, separate event roles, host-slot lifetime protection, and restart ordering. These requirements prevent overshoot, register collisions, missing signals, and deadlock.</dd>
|
||
</dl><p class="sources">Sources: <a href="../../src/PIM/Dialect/Pim/Pim.td">definition</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsAsm.cpp">assembly</a>, <a href="../../src/PIM/Dialect/Pim/PimOpsVerify.cpp">verifiers</a>, <a href="../../src/PIM/Compiler/PimCodeGen.cpp">emission</a>.</p></article>
|
||
</section><footer>
|
||
<p>Shared implementation sources: <a href="../../src/PIM/Common/IR/CompactAsmUtils.hpp">compact assembly</a>, <a href="../../src/PIM/Dialect/Pim/Passes/Transforms/Bufferization/OpBufferizationInterfaces.cpp">bufferization</a>, <a href="../../src/PIM/Dialect/Pim/Passes/Transforms/InstructionSelection/InstructionSelectionPass.cpp">instruction selection</a>, <a href="../../src/PIM/Dialect/Pim/Passes/Transforms/Verification/VerificationPass.cpp">PIM verification</a>, <a href="../../src/PIM/Conversion/SpatialToPim/Patterns/ChannelLowering.cpp">communication lowering</a>, <a href="../../src/PIM/Compiler/PimCoreProgram.cpp">core programs</a>.</p></footer></main>
|
||
<script>
|
||
const search = document.getElementById('search');
|
||
const entries = [...document.querySelectorAll('article')];
|
||
const links = [...document.querySelectorAll('#operation-index li')];
|
||
search.disabled = false;
|
||
search.addEventListener('input', () => {
|
||
const query = search.value.trim().toLowerCase();
|
||
const visible = new Set();
|
||
for (const entry of entries) {
|
||
entry.hidden = !entry.textContent.toLowerCase().includes(query);
|
||
if (!entry.hidden) visible.add(entry.id);
|
||
}
|
||
for (const link of links) link.hidden = !visible.has(link.querySelector('a').hash.slice(1));
|
||
document.getElementById('count').textContent = `${visible.size} of ${entries.length} operations`;
|
||
});
|
||
// A shared deep link stays usable even if an active search hid its target.
|
||
window.addEventListener('hashchange', () => {
|
||
const target = document.getElementById(location.hash.slice(1));
|
||
if (target && target.hidden) {
|
||
search.value = '';
|
||
search.dispatchEvent(new Event('input'));
|
||
target.scrollIntoView();
|
||
}
|
||
});
|
||
</script>
|
||
</body></html>
|