Graph IR

One function, straight-line SSA. Values are %name: inputs keep their source names, op results are numbered %0, %1, .... Every type is printed without spaces (i8[4,8]).

func @main(%A: i8[4,8], %B: i8[8,4]) -> (i32[4,4]) {
  %0 = matmul %A, %B : i32[4,4]  loc("mm_relu.tiny":4:10)
  %1 = relu %0 : i32[4,4]        loc("mm_relu.tiny":4:5)
  return %1
}
Op Syntax
const %x = const [[1, 2], [3, 4]] : i8[2,2]
matmul %x = matmul %a, %b : T
add %x = add %a, %b : T
relu %x = relu %a : T
fused matmul %x = matmul{epilogue=bias,relu} %a, %b, %bias : T (made by the fuse pass)

A fused matmul applies its epilogue stages in order to the matmul result: bias adds a rank-1 [N] operand (a third operand) to every row, relu clamps at zero. Each stage’s result must fit in i32, exactly as if the ops were separate. Allowed epilogues: bias, relu, bias,relu.

Code map

Module Role
ir/types.py TensorType and infer_result_type, the one source of typing rules
ir/graph.py Value, Op, Func, FuncBuilder
ir/verifier.py structural + type checks; reports every problem
ir/ref.py the oracle: numpy int64 evaluation, RangeError on i32 overflow
ir/interp.py independent scalar interpreter (plain Python ints), cross-checked against the oracle

Inspect a program: tinyc examples/mm_relu.tiny --locs (or --emit ast).