Step 04 · LLVM-dialect mapping
Our emitter speaks one dialect: llvm. The mapping is mechanical:
| TAC | MLIR llvm |
|---|---|
| Numeric constant | llvm.mlir.constant(N : i64) : i64 |
Named local x | llvm.alloca in entry block, llvm.load/llvm.store thereafter |
Add a,b | llvm.add %a, %b : i64 |
Sub/Mul/Div/Mod | llvm.sub/mul/sdiv/srem |
And/Or | llvm.and/or |
Eq/Ne/Lt/... | llvm.icmp "eq"/"ne"/"slt"/... then llvm.zext _ : i1 to i64 |
Neg | llvm.sub %zero, %a |
Not | llvm.icmp "eq" %a, %zero then zext |
LoadGlobal x | llvm.mlir.addressof @x then llvm.load |
StoreGlobal x, v | llvm.mlir.addressof @x then llvm.store |
Print v | llvm.call @printf(@fmt, v) vararg(...) : (!llvm.ptr, i64) -> i32 |
Call f(args) | llvm.call @f(args) : (...) -> i64 |
Jump bb | llvm.br ^bbN |
CondJump v, T, F | llvm.icmp "ne" v, 0 then llvm.cond_br ... ^bbT, ^bbF |
Return v | llvm.return %v : i64 (i32 0 for main) |
Globals
llvm.mlir.global internal @x(0 : i64) : i64
internal linkage; initial value 0. Stores at main-time install the
user's initialiser. llvm.mlir.addressof @x reifies the global as a
!llvm.ptr value.
printf
llvm.mlir.global internal constant @fmt("%lld\0A\00") {addr_space = 0 : i32}
llvm.func @printf(!llvm.ptr, ...) -> i32
Variadic call sites must spell their vararg signature:
%r = llvm.call @printf(%f, %v) vararg(!llvm.func<i32 (ptr, ...)>)
: (!llvm.ptr, i64) -> i32
That's MLIR's way of preserving variadic information that LLVM's
function type would otherwise carry as (...).