把 81 个 .vp 文件的中文 # 注释与三引号文档串改写为简洁英文,每个连续注释块 <=2 行;运行时中文字符串字面量(testcheck.begin/print 等用户可见消息)保持原样。骨架比对 HEAD 与工作区 112 文件漂移 0,全量 77/77 顺序回归通过。
1388 lines
67 KiB
Plaintext
1388 lines
67 KiB
Plaintext
from stdint import *
|
||
import stdio
|
||
import string
|
||
import stdlib
|
||
import w32.fileio as fileio
|
||
import memhub
|
||
import testcheck
|
||
import viperlib
|
||
import mlirlite
|
||
|
||
|
||
|
||
define POOL_SIZE = 83886080
|
||
define SUB_POOL_SIZE = 2097136
|
||
|
||
|
||
_DUMP_SEQ: int = 0
|
||
|
||
|
||
def _dump_is_module(text: str) -> int:
|
||
if text is None: return 0
|
||
p: str = text
|
||
while p[0] != '\0':
|
||
ch: char = p[0]
|
||
if ch == ' ' or ch == '\t' or ch == '\n' or ch == '\r':
|
||
p = p + 1
|
||
continue
|
||
if ch == '/' and p[1] == '/':
|
||
while p[0] != '\0' and p[0] != '\n':
|
||
p = p + 1
|
||
continue
|
||
break
|
||
if p[0] == '\0': return 0
|
||
if string.strncmp(p, "module", 6) == 0: return 1
|
||
return 0
|
||
|
||
|
||
def dump_mlir(pool: memhub.MemBuddy*, name: str, buf: str):
|
||
global _DUMP_SEQ
|
||
_DUMP_SEQ += 1
|
||
path: str = pool.alloc(256)
|
||
if path is None:
|
||
stdio.printf("[mlir] WARN: alloc path failed\n")
|
||
return
|
||
if name is None:
|
||
viperlib.snprintf(path, 256, "Test/MLIRLiteTest/output/auto_%02d.mlir", _DUMP_SEQ)
|
||
else:
|
||
viperlib.snprintf(path, 256, "Test/MLIRLiteTest/output/%s", name)
|
||
if _dump_is_module(buf) == 0:
|
||
cut: sizet = string.strlen(path)
|
||
d2: sizet = cut
|
||
while d2 > 0:
|
||
d2 -= 1
|
||
if path[d2] == '.':
|
||
path[d2] = '\0'
|
||
break
|
||
ext_txt: str = pool.alloc(8)
|
||
if ext_txt is not None:
|
||
string.strcpy(ext_txt, ".txt")
|
||
tmp_path: str = pool.alloc(256)
|
||
if tmp_path is not None:
|
||
viperlib.snprintf(tmp_path, 256, "%s%s", path, ext_txt)
|
||
string.strcpy(path, tmp_path)
|
||
f: fileio.File = fileio.File(path, fileio.MODE.W)
|
||
if f.closed:
|
||
stdio.printf("[mlir] WARN: open failed: %s\n", path)
|
||
return
|
||
hdr: str = pool.alloc(128)
|
||
if hdr is not None:
|
||
viperlib.snprintf(hdr, 128, "// dumped by MLIRLiteTest #%d\n", _DUMP_SEQ)
|
||
f.write(hdr, string.strlen(hdr))
|
||
f.write(buf, string.strlen(buf))
|
||
f.close()
|
||
stdio.printf(" [mlir] wrote %s (%d bytes)\n", path, int(string.strlen(buf)))
|
||
|
||
|
||
# Test 1: TypePrint - MLIR LLVM dialect type printing
|
||
def test_type_print(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 1: TypePrint (MLIR types)")
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
buf: str = pool.alloc(256)
|
||
|
||
# i32
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
buf[0] = 0
|
||
core.PrintType(buf, 256, ty_i32)
|
||
testcheck.check(string.strcmp(buf, "i32") == 0,
|
||
"Int32 -> i32",
|
||
"Int32 fail")
|
||
|
||
# Opaque pointer: Ptr(Int32) -> !llvm.ptr
|
||
ty_ptr: mlirlite.LLVMType* = core.Ptr(ty_i32)
|
||
buf[0] = 0
|
||
core.PrintType(buf, 256, ty_ptr)
|
||
testcheck.check(string.strcmp(buf, "!llvm.ptr") == 0,
|
||
"Ptr(Int32) -> !llvm.ptr",
|
||
"Ptr fail")
|
||
|
||
# Void -> none
|
||
ty_void: mlirlite.LLVMType* = core.Void()
|
||
buf[0] = 0
|
||
core.PrintType(buf, 256, ty_void)
|
||
testcheck.check(string.strcmp(buf, "none") == 0,
|
||
"Void -> none",
|
||
"Void fail")
|
||
|
||
# [4 x i32] -> !llvm.array<4 x i32>
|
||
ty_arr: mlirlite.LLVMType* = core.Array(ty_i32, 4)
|
||
buf[0] = 0
|
||
core.PrintType(buf, 256, ty_arr)
|
||
testcheck.check(string.strcmp(buf, "!llvm.array<4 x i32>") == 0,
|
||
"Array(i32,4) -> !llvm.array<4 x i32>",
|
||
"Array fail")
|
||
|
||
# [2 x [3 x i8]] -> !llvm.array<2 x !llvm.array<3 x i8>>
|
||
ty_i8: mlirlite.LLVMType* = core.Int8()
|
||
ty_inner: mlirlite.LLVMType* = core.Array(ty_i8, 3)
|
||
ty_nested: mlirlite.LLVMType* = core.Array(ty_inner, 2)
|
||
buf[0] = 0
|
||
core.PrintType(buf, 256, ty_nested)
|
||
testcheck.check(string.strcmp(buf, "!llvm.array<2 x !llvm.array<3 x i8>>") == 0,
|
||
"nested array -> !llvm.array<2 x !llvm.array<3 x i8>>",
|
||
"nested array fail")
|
||
|
||
# {i32, i8*} -> !llvm.struct<(i32, !llvm.ptr)>
|
||
ty_i8ptr: mlirlite.LLVMType* = core.Ptr(ty_i8)
|
||
n1: mlirlite.ParamNode* = core.NewParamNode(ty_i32)
|
||
n2: mlirlite.ParamNode* = core.NewParamNode(ty_i8ptr)
|
||
head: mlirlite.ParamNode* = core.ParamListAppend(None, None, n1)
|
||
core.ParamListAppend(head, n1, n2)
|
||
ty_struct: mlirlite.LLVMType* = core.Struct(head, 2)
|
||
buf[0] = 0
|
||
core.PrintType(buf, 256, ty_struct)
|
||
testcheck.check(string.strcmp(buf, "!llvm.struct<(i32, !llvm.ptr)>") == 0,
|
||
"struct {i32, i8*} -> !llvm.struct<(i32, !llvm.ptr)>",
|
||
"struct fail")
|
||
|
||
# Empty struct -> !llvm.struct<()>
|
||
ty_empty: mlirlite.LLVMType* = core.Struct(None, 0)
|
||
buf[0] = 0
|
||
core.PrintType(buf, 256, ty_empty)
|
||
testcheck.check(string.strcmp(buf, "!llvm.struct<()>") == 0,
|
||
"empty struct -> !llvm.struct<()>",
|
||
"empty struct fail")
|
||
|
||
# Function type -> !llvm.func<i32 (i32, !llvm.ptr)>
|
||
fn1: mlirlite.ParamNode* = core.NewParamNode(ty_i32)
|
||
fn2: mlirlite.ParamNode* = core.NewParamNode(ty_i8ptr)
|
||
fhead: mlirlite.ParamNode* = core.ParamListAppend(None, None, fn1)
|
||
core.ParamListAppend(fhead, fn1, fn2)
|
||
ty_func: mlirlite.LLVMType* = core.Func(ty_i32, fhead, 2)
|
||
buf[0] = 0
|
||
core.PrintType(buf, 256, ty_func)
|
||
dump_mlir(pool, "T01_types.mlir", buf)
|
||
# Embed the actual value in the failure message so the FAIL line shows the
|
||
# real form directly, avoiding guesswork from a bare "fail"
|
||
ft_msg: str = pool.alloc(512)
|
||
if ft_msg is not None:
|
||
viperlib.snprintf(ft_msg, 512,
|
||
"func type fail: actual='%s' expected='!llvm.func<i32 (i32, !llvm.ptr)>'",
|
||
buf)
|
||
testcheck.check(string.strcmp(buf, "!llvm.func<i32 (i32, !llvm.ptr)>") == 0,
|
||
"func type -> !llvm.func<i32 (i32, !llvm.ptr)>",
|
||
ft_msg)
|
||
|
||
|
||
# Test 2: ValuePrint - value printing (diagnostics only)
|
||
def test_value_print(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 2: ValuePrint (MLIR)")
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
buf: str = pool.alloc(128)
|
||
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
|
||
# Constant 42 -> "42 : i32"
|
||
val42: mlirlite.Value* = mcore.ConstInt32(42)
|
||
buf[0] = 0
|
||
mcore.PrintValue(buf, 128, val42)
|
||
testcheck.check(string.strcmp(buf, "42 : i32") == 0,
|
||
"ConstInt(42) -> '42 : i32'",
|
||
"ConstInt fail")
|
||
|
||
# SSA value -> "%0" (MLIR operands carry no type)
|
||
val_ssa: mlirlite.Value* = mcore.SSAValue(ty_i32, "%0")
|
||
buf[0] = 0
|
||
mcore.PrintValue(buf, 128, val_ssa)
|
||
dump_mlir(pool, "T02_value.mlir", buf)
|
||
testcheck.check(string.strcmp(buf, "%0") == 0,
|
||
"SSAValue(%0) -> '%0'",
|
||
"SSAValue fail")
|
||
|
||
|
||
# Test 3: build add function (llvm.func @add + llvm.add + llvm.return)
|
||
def test_build_add(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 3: build_add function")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_add")
|
||
func: mlirlite.Function* = mcore.CreateFunction(mod, "add", ty_i32)
|
||
|
||
mcore.AddParam(func, ty_i32, "%a")
|
||
mcore.AddParam(func, ty_i32, "%b")
|
||
|
||
entry: mlirlite.BasicBlock* = mcore.CreateBlock(func, "entry")
|
||
|
||
builder: mlirlite.IRBuilder* = mcore.NewBuilder(func)
|
||
mlirlite.position_at_end(builder, entry)
|
||
|
||
v_a: mlirlite.Value* = mcore.SSAValue(ty_i32, "%a")
|
||
v_b: mlirlite.Value* = mcore.SSAValue(ty_i32, "%b")
|
||
|
||
v_c: mlirlite.Value* = mlirlite.build_add(builder, v_a, v_b)
|
||
mlirlite.build_ret(builder, v_c)
|
||
|
||
buf: str = pool.alloc(4096)
|
||
buf[0] = 0
|
||
mcore.PrintFunction(buf, 4096, func)
|
||
dump_mlir(pool, None, buf)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T03_add.mlir", buf)
|
||
|
||
testcheck.check(string.strstr(buf, "llvm.func @add(%a: i32, %b: i32) -> i32") is not None,
|
||
"contains 'llvm.func @add(%a: i32, %b: i32) -> i32'",
|
||
"missing MLIR function signature")
|
||
testcheck.check(string.strstr(buf, "llvm.add %a, %b : i32") is not None,
|
||
"contains 'llvm.add %a, %b : i32'",
|
||
"missing 'llvm.add'")
|
||
testcheck.check(string.strstr(buf, "llvm.return") is not None,
|
||
"contains 'llvm.return'",
|
||
"missing 'llvm.return'")
|
||
testcheck.check(string.strstr(buf, "entry:") is None,
|
||
"entry 块不带标签",
|
||
"entry block should not be labelled")
|
||
|
||
|
||
# Test 4: build if-then-else (max)
|
||
def test_build_if_else(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 4: build if-then-else (max)")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_max")
|
||
func: mlirlite.Function* = mcore.CreateFunction(mod, "max", ty_i32)
|
||
mcore.AddParam(func, ty_i32, "%a")
|
||
mcore.AddParam(func, ty_i32, "%b")
|
||
|
||
entry: mlirlite.BasicBlock* = mcore.CreateBlock(func, "entry")
|
||
then_blk: mlirlite.BasicBlock* = mcore.CreateBlock(func, "then")
|
||
else_blk: mlirlite.BasicBlock* = mcore.CreateBlock(func, "else")
|
||
|
||
builder: mlirlite.IRBuilder* = mcore.NewBuilder(func)
|
||
|
||
mlirlite.position_at_end(builder, entry)
|
||
v_a: mlirlite.Value* = mcore.SSAValue(ty_i32, "%a")
|
||
v_b: mlirlite.Value* = mcore.SSAValue(ty_i32, "%b")
|
||
v_cond: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_SGT, v_a, v_b)
|
||
mlirlite.build_cond_br(builder, v_cond, then_blk, else_blk)
|
||
|
||
mlirlite.position_at_end(builder, then_blk)
|
||
mlirlite.build_ret(builder, v_a)
|
||
|
||
mlirlite.position_at_end(builder, else_blk)
|
||
mlirlite.build_ret(builder, v_b)
|
||
|
||
buf: str = pool.alloc(4096)
|
||
buf[0] = 0
|
||
mcore.PrintFunction(buf, 4096, func)
|
||
dump_mlir(pool, None, buf)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T04_ifelse.mlir", buf)
|
||
|
||
testcheck.check(string.strstr(buf, "llvm.icmp \"sgt\"") is not None,
|
||
"contains 'llvm.icmp \"sgt\"'",
|
||
"missing 'llvm.icmp \"sgt\"'")
|
||
testcheck.check(string.strstr(buf, "llvm.cond_br") is not None,
|
||
"contains 'llvm.cond_br'",
|
||
"missing 'llvm.cond_br'")
|
||
testcheck.check(string.strstr(buf, "^then:") is not None,
|
||
"contains '^then:'",
|
||
"missing block label '^then:'")
|
||
testcheck.check(string.strstr(buf, "^else:") is not None,
|
||
"contains '^else:'",
|
||
"missing block label '^else:'")
|
||
|
||
|
||
# Test 5: void function
|
||
def test_build_void(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 5: void function")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_void: mlirlite.LLVMType* = core.Void()
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_void")
|
||
func: mlirlite.Function* = mcore.CreateFunction(mod, "noop", ty_void)
|
||
entry: mlirlite.BasicBlock* = mcore.CreateBlock(func, "entry")
|
||
builder: mlirlite.IRBuilder* = mcore.NewBuilder(func)
|
||
mlirlite.position_at_end(builder, entry)
|
||
mlirlite.build_ret_void(builder)
|
||
|
||
buf: str = pool.alloc(4096)
|
||
buf[0] = 0
|
||
mcore.PrintFunction(buf, 4096, func)
|
||
dump_mlir(pool, None, buf)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T05_void.mlir", buf)
|
||
|
||
testcheck.check(string.strstr(buf, "llvm.func @noop() {") is not None,
|
||
"contains 'llvm.func @noop() {'(void 返回省略 -> ty)",
|
||
"missing void function signature")
|
||
testcheck.check(string.strstr(buf, "llvm.return") is not None,
|
||
"contains 'llvm.return'",
|
||
"missing 'llvm.return'")
|
||
|
||
|
||
# Test 6: module printing (module / declare vs define)
|
||
def test_module_print(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 6: ModulePrint")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_module")
|
||
mlirlite.module_set_target(mod, "x86_64-pc-windows-gnu")
|
||
mlirlite.module_set_datalayout(mod, "e-m:w-p270:32:32")
|
||
|
||
# declare: llvm.func @extern_func(%x: i32) -> i32 (no body)
|
||
func_decl: mlirlite.Function* = mcore.CreateDeclare(mod, "extern_func", ty_i32)
|
||
mcore.AddParam(func_decl, ty_i32, "%x")
|
||
|
||
# define: llvm.func @add(%a: i32, %b: i32) -> i32 { … }
|
||
func_add: mlirlite.Function* = mcore.CreateFunction(mod, "add", ty_i32)
|
||
mcore.AddParam(func_add, ty_i32, "%a")
|
||
mcore.AddParam(func_add, ty_i32, "%b")
|
||
entry1: mlirlite.BasicBlock* = mcore.CreateBlock(func_add, "entry")
|
||
builder1: mlirlite.IRBuilder* = mcore.NewBuilder(func_add)
|
||
mlirlite.position_at_end(builder1, entry1)
|
||
v_a1: mlirlite.Value* = mcore.SSAValue(ty_i32, "%a")
|
||
v_b1: mlirlite.Value* = mcore.SSAValue(ty_i32, "%b")
|
||
v_sum: mlirlite.Value* = mlirlite.build_add(builder1, v_a1, v_b1)
|
||
mlirlite.build_ret(builder1, v_sum)
|
||
|
||
buf: str = pool.alloc(65536)
|
||
buf[0] = 0
|
||
mcore.PrintModule(buf, 65536, mod)
|
||
dump_mlir(pool, None, buf)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T06_module.mlir", buf)
|
||
|
||
testcheck.check(string.strstr(buf, "module {") is not None,
|
||
"contains 'module {'",
|
||
"missing 'module {'")
|
||
testcheck.check(string.strstr(buf, "llvm.func @add") is not None,
|
||
"contains 'llvm.func @add'",
|
||
"missing add function")
|
||
testcheck.check(string.strstr(buf, "llvm.func @extern_func(%x: i32) -> i32") is not None,
|
||
"declare 形态 'llvm.func @extern_func(%x: i32) -> i32'",
|
||
"missing declare")
|
||
testcheck.check(string.strstr(buf, "llvm.func @extern_func(%x: i32) -> i32 {") is None,
|
||
"declare 无函数体(不出现 '{')",
|
||
"declare should not have a body")
|
||
testcheck.check(string.strstr(buf, "llvm.func @add(%a: i32, %b: i32) -> i32 {") is not None,
|
||
"define 形态带函数体",
|
||
"missing define body")
|
||
|
||
|
||
# Test 7: constant materialization (operand 42 -> llvm.mlir.constant)
|
||
def test_const_materialize(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 7: constant materialization")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_const")
|
||
func: mlirlite.Function* = mcore.CreateFunction(mod, "const_test", ty_i32)
|
||
mcore.AddParam(func, ty_i32, "%a")
|
||
entry: mlirlite.BasicBlock* = mcore.CreateBlock(func, "entry")
|
||
builder: mlirlite.IRBuilder* = mcore.NewBuilder(func)
|
||
mlirlite.position_at_end(builder, entry)
|
||
|
||
v_a: mlirlite.Value* = mcore.SSAValue(ty_i32, "%a")
|
||
v_42: mlirlite.Value* = mcore.ConstInt32(42)
|
||
v_sum: mlirlite.Value* = mlirlite.build_add(builder, v_a, v_42)
|
||
mlirlite.build_ret(builder, v_sum)
|
||
|
||
buf: str = pool.alloc(4096)
|
||
buf[0] = 0
|
||
mcore.PrintFunction(buf, 4096, func)
|
||
dump_mlir(pool, None, buf)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T07_consts.mlir", buf)
|
||
|
||
testcheck.check(string.strstr(buf, "llvm.mlir.constant(42 : i32) : i32") is not None,
|
||
"contains 'llvm.mlir.constant(42 : i32) : i32'",
|
||
"constant not materialized")
|
||
testcheck.check(string.strstr(buf, "llvm.add %a, %c0 : i32") is not None,
|
||
"add 行引用物化后的 SSA 名 '%c0'",
|
||
"add line does not reference materialized constant")
|
||
|
||
# Same (type, text) constant materializes once: both add operands are 42
|
||
func2: mlirlite.Function* = mcore.CreateFunction(mod, "const_twice", ty_i32)
|
||
entry2: mlirlite.BasicBlock* = mcore.CreateBlock(func2, "entry")
|
||
builder2: mlirlite.IRBuilder* = mcore.NewBuilder(func2)
|
||
mlirlite.position_at_end(builder2, entry2)
|
||
v_42b: mlirlite.Value* = mcore.ConstInt32(42)
|
||
v_42c: mlirlite.Value* = mcore.ConstInt32(42)
|
||
v_sum2: mlirlite.Value* = mlirlite.build_add(builder2, v_42b, v_42c)
|
||
mlirlite.build_ret(builder2, v_sum2)
|
||
buf[0] = 0
|
||
mcore.PrintFunction(buf, 4096, func2)
|
||
testcheck.check(string.strstr(buf, "llvm.add %c0, %c0 : i32") is not None,
|
||
"相同 (类型,文本) 常量只物化一次(llvm.add %c0, %c0)",
|
||
"constant dedup failed")
|
||
|
||
|
||
# Test 8: phi -> block arguments
|
||
def test_phi_block_args(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 8: phi -> block arguments")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_merge")
|
||
func: mlirlite.Function* = mcore.CreateFunction(mod, "merge_test", ty_i32)
|
||
entry: mlirlite.BasicBlock* = mcore.CreateBlock(func, "entry")
|
||
then_blk: mlirlite.BasicBlock* = mcore.CreateBlock(func, "then")
|
||
else_blk: mlirlite.BasicBlock* = mcore.CreateBlock(func, "else")
|
||
merge: mlirlite.BasicBlock* = mcore.CreateBlock(func, "merge")
|
||
builder: mlirlite.IRBuilder* = mcore.NewBuilder(func)
|
||
|
||
# entry: br label %then
|
||
mlirlite.position_at_end(builder, entry)
|
||
mlirlite.build_br(builder, then_blk)
|
||
|
||
# then: br label %merge
|
||
mlirlite.position_at_end(builder, then_blk)
|
||
v_1: mlirlite.Value* = mcore.ConstInt32(1)
|
||
mlirlite.build_br(builder, merge)
|
||
|
||
# else: br label %merge
|
||
mlirlite.position_at_end(builder, else_blk)
|
||
v_2: mlirlite.Value* = mcore.ConstInt32(2)
|
||
mlirlite.build_br(builder, merge)
|
||
|
||
# merge: %r = phi i32 [1, %then], [2, %else]; ret i32 %r
|
||
mlirlite.position_at_end(builder, merge)
|
||
inc1: mlirlite.PhiIncoming* = mlirlite.new_phi_incoming(pool, v_1, then_blk)
|
||
inc2: mlirlite.PhiIncoming* = mlirlite.new_phi_incoming(pool, v_2, else_blk)
|
||
inc1.Next = inc2
|
||
v_r: mlirlite.Value* = mlirlite.build_phi(builder, ty_i32, inc1, 2)
|
||
mlirlite.build_ret(builder, v_r)
|
||
|
||
buf: str = pool.alloc(4096)
|
||
buf[0] = 0
|
||
mcore.PrintFunction(buf, 4096, func)
|
||
dump_mlir(pool, None, buf)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T08_phi.mlir", buf)
|
||
|
||
testcheck.check(string.strstr(buf, "^merge(%") is not None,
|
||
"块参数形态 '^merge(%'",
|
||
"missing block arguments")
|
||
testcheck.check(string.strstr(buf, "llvm.br ^merge(%") is not None,
|
||
"跳转实参形态 'llvm.br ^merge(%'",
|
||
"missing branch block operands")
|
||
testcheck.check(string.strstr(buf, " = phi ") is None,
|
||
"phi 行不再输出",
|
||
"phi line should not be emitted")
|
||
|
||
|
||
# Test 9: memory / call / cast
|
||
def test_mem_call_cast(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 9: memory / call / cast")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
ty_i64: mlirlite.LLVMType* = core.Int64()
|
||
ty_i8: mlirlite.LLVMType* = core.Int8()
|
||
ty_i8ptr: mlirlite.LLVMType* = core.Ptr(ty_i8)
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_mem")
|
||
func: mlirlite.Function* = mcore.CreateFunction(mod, "mem_test", ty_i32)
|
||
mcore.AddParam(func, ty_i8ptr, "%s")
|
||
mcore.AddParam(func, ty_i32, "%n")
|
||
entry: mlirlite.BasicBlock* = mcore.CreateBlock(func, "entry")
|
||
builder: mlirlite.IRBuilder* = mcore.NewBuilder(func)
|
||
mlirlite.position_at_end(builder, entry)
|
||
|
||
v_s: mlirlite.Value* = mcore.SSAValue(ty_i8ptr, "%s")
|
||
v_n: mlirlite.Value* = mcore.SSAValue(ty_i32, "%n")
|
||
|
||
# %0 = alloca i32 ; store i32 %n, !llvm.ptr %0 ; %1 = load i32, !llvm.ptr %0
|
||
v_slot: mlirlite.Value* = mlirlite.build_alloca(builder, ty_i32)
|
||
mlirlite.build_store(builder, v_n, v_slot, 0)
|
||
v_loaded: mlirlite.Value* = mlirlite.build_load(builder, ty_i32, v_slot, 0)
|
||
|
||
# %2 = getelementptr i8, !llvm.ptr %s, i32 %1
|
||
v_gep: mlirlite.Value* = mlirlite.build_gep(builder, ty_i8, v_s, v_loaded)
|
||
|
||
# %3 = zext i32 %1 to i64 ; %4 = ptrtoint !llvm.ptr %s to i64
|
||
v_zext: mlirlite.Value* = mlirlite.build_zext(builder, v_loaded, ty_i64)
|
||
v_p2i: mlirlite.Value* = mlirlite.build_ptrtoint(builder, v_s, ty_i64)
|
||
|
||
# %5 = call i32 @callee(i32 %1)
|
||
v_call: mlirlite.Value* = mlirlite.build_call(builder, "callee", v_loaded, 1, ty_i32, 0)
|
||
mlirlite.build_ret(builder, v_call)
|
||
|
||
buf: str = pool.alloc(4096)
|
||
buf[0] = 0
|
||
mcore.PrintFunction(buf, 4096, func)
|
||
dump_mlir(pool, None, buf)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T09_mem_call_cast.mlir", buf)
|
||
|
||
# Save the whole module: mlir-opt requires callees declared at module scope,
|
||
# otherwise single-function text without declare fails symbol resolution
|
||
decl: mlirlite.Function* = mcore.CreateDeclare(mod, "callee", ty_i32)
|
||
mcore.AddParam(decl, ty_i32, "%x")
|
||
mbuf: str = pool.alloc(65536)
|
||
if mbuf is not None:
|
||
mbuf[0] = 0
|
||
mcore.PrintModule(mbuf, 65536, mod)
|
||
dump_mlir(pool, "T09_mem_module.mlir", mbuf)
|
||
|
||
testcheck.check(string.strstr(buf, "llvm.alloca") is not None,
|
||
"contains 'llvm.alloca'", "missing 'llvm.alloca'")
|
||
testcheck.check(string.strstr(buf, "llvm.store %n, %") is not None,
|
||
"contains 'llvm.store'", "missing 'llvm.store'")
|
||
testcheck.check(string.strstr(buf, "llvm.load %") is not None,
|
||
"contains 'llvm.load'", "missing 'llvm.load'")
|
||
testcheck.check(string.strstr(buf, "llvm.getelementptr") is not None,
|
||
"contains 'llvm.getelementptr'", "missing 'llvm.getelementptr'")
|
||
testcheck.check(string.strstr(buf, "llvm.call @callee(") is not None,
|
||
"contains 'llvm.call @callee('", "missing 'llvm.call'")
|
||
testcheck.check(string.strstr(buf, "llvm.zext") is not None,
|
||
"contains 'llvm.zext'", "missing 'llvm.zext'")
|
||
testcheck.check(string.strstr(buf, "llvm.ptrtoint") is not None,
|
||
"contains 'llvm.ptrtoint'", "missing 'llvm.ptrtoint'")
|
||
|
||
|
||
# Test 10: global variables (string constant / int / int array)
|
||
def test_globals(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 10: Global Variables")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_void: mlirlite.LLVMType* = core.Void()
|
||
ty_i8: mlirlite.LLVMType* = core.Int8()
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_globals")
|
||
mlirlite.module_set_target(mod, "x86_64-pc-windows-gnu")
|
||
|
||
# @.str = private unnamed_addr constant [6 x i8] c"hello\00"
|
||
mcore.CreateGlobalString(mod, ".str", "hello")
|
||
|
||
# @counter = global i32 0
|
||
mcore.CreateGlobalInt(mod, "counter", 32, 0)
|
||
|
||
# @arr = [i8 1, i8 2] -> [1 : i8, 2 : i8] (elements carry type annotations);
|
||
# dense<...> is illegal for !llvm.array (it only accepts builtin shaped types)
|
||
ty_arr2: mlirlite.LLVMType* = core.Array(ty_i8, 2)
|
||
gv_arr: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "arr", ty_arr2)
|
||
init_arr: str = pool.alloc(32)
|
||
string.strcpy(init_arr, "[i8 1, i8 2]")
|
||
gv_arr.Initializer = init_arr
|
||
|
||
func: mlirlite.Function* = mcore.CreateFunction(mod, "use_global", ty_void)
|
||
entry: mlirlite.BasicBlock* = mcore.CreateBlock(func, "entry")
|
||
builder: mlirlite.IRBuilder* = mcore.NewBuilder(func)
|
||
mlirlite.position_at_end(builder, entry)
|
||
mlirlite.build_ret_void(builder)
|
||
|
||
buf: str = pool.alloc(65536)
|
||
buf[0] = 0
|
||
mcore.PrintModule(buf, 65536, mod)
|
||
dump_mlir(pool, None, buf)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T10_globals.mlir", buf)
|
||
|
||
testcheck.check(string.strstr(buf, "llvm.mlir.global") is not None,
|
||
"contains 'llvm.mlir.global'", "missing 'llvm.mlir.global'")
|
||
# A '.'-prefixed symbol name must be quoted (MLIR bare names start with a letter or _)
|
||
# Actual form: llvm.mlir.global ... constant @".str"("hello\00") : !llvm.array<6 x i8>
|
||
testcheck.check(string.strstr(buf, "@\".str\"(") is not None,
|
||
"字符串全局名引号形态 '@\".str\"('", "missing quoted string global name")
|
||
# The string attribute length must equal the i8 array length, so the trailing
|
||
# NUL must stay: mlir-opt rejects stripping \00, and dropping it breaks the ABI
|
||
testcheck.check(string.strstr(buf, "hello\\00") is not None,
|
||
"字符串初值保留 NUL 'hello\\00'", "missing string initializer")
|
||
testcheck.check(string.strstr(buf, "!llvm.array<6 x i8>") is not None,
|
||
"contains '!llvm.array<6 x i8>'", "missing string global type")
|
||
testcheck.check(string.strstr(buf, "@counter(0 : i32) : i32") is not None,
|
||
"整数全局 '@counter(0 : i32) : i32'", "missing integer global")
|
||
# Array attribute: elements carry type annotations (dense<...> invalid for !llvm.array)
|
||
testcheck.check(string.strstr(buf, "[1 : i8, 2 : i8]") is not None,
|
||
"整型数组初值 '[1 : i8, 2 : i8]'", "missing array initializer")
|
||
|
||
|
||
# Test 11: SSA verification (valid function, 0 errors)
|
||
def test_verify_valid(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 11: Verify valid function")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("verify_valid")
|
||
func: mlirlite.Function* = mcore.CreateFunction(mod, "add", ty_i32)
|
||
mcore.AddParam(func, ty_i32, "%a")
|
||
mcore.AddParam(func, ty_i32, "%b")
|
||
entry: mlirlite.BasicBlock* = mcore.CreateBlock(func, "entry")
|
||
builder: mlirlite.IRBuilder* = mcore.NewBuilder(func)
|
||
mlirlite.position_at_end(builder, entry)
|
||
v_a: mlirlite.Value* = mcore.SSAValue(ty_i32, "%a")
|
||
v_b: mlirlite.Value* = mcore.SSAValue(ty_i32, "%b")
|
||
v_c: mlirlite.Value* = mlirlite.build_add(builder, v_a, v_b)
|
||
mlirlite.build_ret(builder, v_c)
|
||
|
||
result: mlirlite.VerifyResult* = mcore.NewVerifyResult(4096)
|
||
err_count: int = mcore.VerifyFunction(func, result)
|
||
|
||
# Render once via PrintFunction for on-disk acceptance (no assertion involved)
|
||
buf: str = pool.alloc(4096)
|
||
buf[0] = 0
|
||
mcore.PrintFunction(buf, 4096, func)
|
||
dump_mlir(pool, "T11_verify.mlir", buf)
|
||
|
||
testcheck.check(err_count == 0,
|
||
"valid add function: 0 errors",
|
||
"valid function should pass verification")
|
||
testcheck.check(result.ErrorCode == mlirlite.VERIFY_OK,
|
||
"error code = VERIFY_OK",
|
||
"wrong error code for valid function")
|
||
|
||
|
||
# Test 12: struct/vtable globals lower to initializer regions
|
||
# Covers vtable bitcast refs, GEP addressof, quoted names, bare null/zeroinitializer
|
||
def test_struct_global(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 12: struct/vtable global initializer (region)")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i8: mlirlite.LLVMType* = core.Int8()
|
||
ty_i8ptr: mlirlite.LLVMType* = core.Ptr(ty_i8)
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_struct_global")
|
||
|
||
# ---- (1) vtable struct { i8*, i8*, i8* } ----
|
||
# Initializer is a TPV-generated LLVM syntax aggregate: null + bitcast symbol refs
|
||
vt1: mlirlite.ParamNode* = core.NewParamNode(ty_i8ptr)
|
||
vt2: mlirlite.ParamNode* = core.NewParamNode(ty_i8ptr)
|
||
vt3: mlirlite.ParamNode* = core.NewParamNode(ty_i8ptr)
|
||
vt_head: mlirlite.ParamNode* = core.ParamListAppend(None, None, vt1)
|
||
core.ParamListAppend(vt_head, vt1, vt2)
|
||
core.ParamListAppend(vt_head, vt2, vt3)
|
||
ty_vt: mlirlite.LLVMType* = core.Struct(vt_head, 3)
|
||
|
||
gv_vt: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "vt", ty_vt)
|
||
init_vt: str = pool.alloc(256)
|
||
string.strcpy(init_vt,
|
||
"{ i8* null, i8* bitcast (i8* (i8*)* @\"m0\" to i8*), i8* bitcast (i8* (i8*)* @m1 to i8*) }")
|
||
gv_vt.Initializer = init_vt
|
||
# IsConstant=1 with default external linkage: a non-empty initializer omits the keyword
|
||
mcore.GlobalSetConstant(gv_vt, 1)
|
||
# Declare referenced method symbols so the module is self-consistent for mlir-opt
|
||
d_m0: mlirlite.Function* = mcore.CreateDeclare(mod, "m0", ty_i8ptr)
|
||
mcore.AddParam(d_m0, ty_i8ptr, "%self")
|
||
d_m1: mlirlite.Function* = mcore.CreateDeclare(mod, "m1", ty_i8ptr)
|
||
mcore.AddParam(d_m1, ty_i8ptr, "%self")
|
||
|
||
# ---- (3) re-quoting branch: LLVM bare name @9m (digit start) -> MLIR @"9m" ----
|
||
vq1: mlirlite.ParamNode* = core.NewParamNode(ty_i8ptr)
|
||
ty_vtq: mlirlite.LLVMType* = core.Struct(vq1, 1)
|
||
gv_vtq: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "vtq", ty_vtq)
|
||
init_vtq: str = pool.alloc(128)
|
||
string.strcpy(init_vtq, "{ i8* bitcast (i8* (i8*)* @9m to i8*) }")
|
||
gv_vtq.Initializer = init_vtq
|
||
mcore.GlobalSetConstant(gv_vtq, 1)
|
||
mcore.GlobalSetLinkage(gv_vtq, "internal")
|
||
d_9m: mlirlite.Function* = mcore.CreateDeclare(mod, "9m", ty_i8ptr)
|
||
mcore.AddParam(d_9m, ty_i8ptr, "%self")
|
||
|
||
# ---- (2) pointer array + GEP constant expression ----
|
||
# @s0 = [3 x i8], the target of the GEP constant expression
|
||
mcore.CreateGlobalString(mod, "s0", "ab")
|
||
ty_arr2: mlirlite.LLVMType* = core.Array(ty_i8ptr, 2)
|
||
gv_arr: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "arr", ty_arr2)
|
||
init_arr: str = pool.alloc(256)
|
||
string.strcpy(init_arr,
|
||
"[i8* getelementptr inbounds ([3 x i8], [3 x i8]* @s0, i32 0, i32 0), i8* null]")
|
||
gv_arr.Initializer = init_arr
|
||
mcore.GlobalSetLinkage(gv_arr, "internal")
|
||
|
||
# ---- (2') root bare GEP (TPV pointer-global form) ----
|
||
# Initializer has no type prefix; it is a bare getelementptr constant expression
|
||
gv_sp: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "sp", ty_i8ptr)
|
||
init_sp: str = pool.alloc(192)
|
||
string.strcpy(init_sp,
|
||
"getelementptr inbounds ([3 x i8], [3 x i8]* @s0, i32 0, i32 0)")
|
||
gv_sp.Initializer = init_sp
|
||
|
||
# ---- (4) root bare null / zeroinitializer globals ----
|
||
# Lowered to a region with llvm.mlir.zero (mlir-opt rejects the (zeroinitializer) attr form)
|
||
gv_pn: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "pnull", ty_i8ptr)
|
||
init_pn: str = pool.alloc(16)
|
||
string.strcpy(init_pn, "null")
|
||
gv_pn.Initializer = init_pn
|
||
|
||
# Int array + bare zeroinitializer: top-level type comes from gv.Ty (!llvm.array<2 x i32>)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
ty_arr_i32: mlirlite.LLVMType* = core.Array(ty_i32, 2)
|
||
gv_za: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "zarr", ty_arr_i32)
|
||
init_za: str = pool.alloc(24)
|
||
string.strcpy(init_za, "zeroinitializer")
|
||
gv_za.Initializer = init_za
|
||
|
||
buf: str = pool.alloc(65536)
|
||
buf[0] = 0
|
||
mcore.PrintModule(buf, 65536, mod)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T12_struct_global.mlir", buf)
|
||
|
||
# (1) vtable: region form + top-level aggregate type; element annotations from type tree
|
||
testcheck.check(string.strstr(buf, "@vt() : !llvm.struct<(!llvm.ptr, !llvm.ptr, !llvm.ptr)>") is not None,
|
||
"vtable 全局 region 形态 '@vt() : !llvm.struct<(…, …, …)>'",
|
||
"missing vtable global region header")
|
||
testcheck.check(string.strstr(buf, "llvm.mlir.undef : !llvm.struct<(!llvm.ptr, !llvm.ptr, !llvm.ptr)>") is not None,
|
||
"含 'llvm.mlir.undef'(insertvalue 链起点)",
|
||
"missing llvm.mlir.undef")
|
||
# LLVM @"m0" is unquoted to bare @m0 (a valid MLIR bare name)
|
||
testcheck.check(string.strstr(buf, "llvm.mlir.addressof @m0 : !llvm.ptr") is not None,
|
||
"bitcast 中的 @\"m0\" → 'llvm.mlir.addressof @m0 : !llvm.ptr'",
|
||
"missing addressof for m0")
|
||
testcheck.check(string.strstr(buf, "llvm.mlir.addressof @m1 : !llvm.ptr") is not None,
|
||
"bitcast 中的 @m1 → 'llvm.mlir.addressof @m1 : !llvm.ptr'",
|
||
"missing addressof for m1")
|
||
testcheck.check(string.strstr(buf, "i8* null") is None,
|
||
"LLVM 语法初值串不再出现(i8* null 已转 llvm.mlir.zero)",
|
||
"raw LLVM initializer text leaked")
|
||
testcheck.check(string.strstr(buf, "llvm.insertvalue") is not None,
|
||
"含 'llvm.insertvalue'",
|
||
"missing llvm.insertvalue")
|
||
testcheck.check(string.strstr(buf, "llvm.return") is not None,
|
||
"含 'llvm.return'",
|
||
"missing llvm.return")
|
||
testcheck.check(string.strstr(buf, "external") is None,
|
||
"external + 有初值 ⇒ 不输出 linkage 关键字",
|
||
"unexpected 'external' keyword")
|
||
# (3) digit-leading symbol names need quotes (per _ll_name_needs_quote)
|
||
testcheck.check(string.strstr(buf, "llvm.mlir.addressof @\"9m\" : !llvm.ptr") is not None,
|
||
"数字开头符号名重加引号 'llvm.mlir.addressof @\"9m\" : !llvm.ptr'",
|
||
"missing quoted addressof for 9m")
|
||
|
||
# (2) pointer array + GEP: GEP constant -> addressof; array global type + index path
|
||
testcheck.check(string.strstr(buf, "@arr() : !llvm.array<2 x !llvm.ptr>") is not None,
|
||
"数组全局 region 形态 '@arr() : !llvm.array<2 x !llvm.ptr>'",
|
||
"missing array global region header")
|
||
testcheck.check(string.strstr(buf, "llvm.mlir.addressof @s0 : !llvm.ptr") is not None,
|
||
"GEP 常量表达式 → 'llvm.mlir.addressof @s0 : !llvm.ptr'",
|
||
"missing addressof for s0")
|
||
testcheck.check(string.strstr(buf, "%undef[0] : !llvm.array<2 x !llvm.ptr>") is not None,
|
||
"下标 0 形态 '%undef[0] : !llvm.array<2 x !llvm.ptr>'",
|
||
"missing index [0] insertvalue")
|
||
testcheck.check(string.strstr(buf, "[1] : !llvm.array<2 x !llvm.ptr>") is not None,
|
||
"下标 1 形态 '[1] : !llvm.array<2 x !llvm.ptr>'",
|
||
"missing index [1] insertvalue")
|
||
|
||
# (2') root bare GEP: top-level leaf region, no undef/insertvalue
|
||
testcheck.check(string.strstr(buf, "@sp() : !llvm.ptr") is not None,
|
||
"根级裸 GEP → '@sp() : !llvm.ptr'(顶层叶子 region)",
|
||
"missing root bare-GEP global")
|
||
|
||
# (4) root bare null pointer global: region form, empty parens, type from gv.Ty
|
||
testcheck.check(string.strstr(buf, "@pnull() : !llvm.ptr") is not None,
|
||
"根级裸 null → '@pnull() : !llvm.ptr'(region 形态)",
|
||
"missing bare-null pointer global region header")
|
||
testcheck.check(string.strstr(buf, "llvm.mlir.zero : !llvm.ptr") is not None,
|
||
"根级裸 null → 'llvm.mlir.zero : !llvm.ptr'",
|
||
"missing llvm.mlir.zero for bare-null global")
|
||
testcheck.check(string.strstr(buf, "llvm.return") is not None,
|
||
"region 内含 'llvm.return'",
|
||
"missing llvm.return in region")
|
||
|
||
# (4') bare zeroinitializer int array: region form, zero type is the top-level type
|
||
testcheck.check(string.strstr(buf, "@zarr() : !llvm.array<2 x i32>") is not None,
|
||
"裸 zeroinitializer → '@zarr() : !llvm.array<2 x i32>'(region 形态)",
|
||
"missing zeroinitializer array global region header")
|
||
testcheck.check(string.strstr(buf, "llvm.mlir.zero : !llvm.array<2 x i32>") is not None,
|
||
"裸 zeroinitializer → 'llvm.mlir.zero : !llvm.array<2 x i32>'",
|
||
"missing llvm.mlir.zero for zeroinitializer array global")
|
||
|
||
# Both must never fall back to the attribute form (mlir-opt rejects it)
|
||
testcheck.check(string.strstr(buf, "(zeroinitializer)") is None,
|
||
"属性形态 '(zeroinitializer)' 不再出现",
|
||
"rejected attribute form '(zeroinitializer)' leaked")
|
||
|
||
|
||
# Test 13: alloca alignment / switch / inline_asm MLIR forms
|
||
# Forms verified against mlir-opt 22.1.8; switch phi targets carry block operands
|
||
def test_alloca_align_switch_asm(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 13: alloca align / switch / inline_asm")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
ty_void: mlirlite.LLVMType* = core.Void()
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_align_switch_asm")
|
||
|
||
# ---- (1) alloca alignment + (3) inline_asm (builder + TPV-injected forms) ----
|
||
f1: mlirlite.Function* = mcore.CreateFunction(mod, "align_asm", ty_i32)
|
||
mcore.AddParam(f1, ty_i32, "%x")
|
||
e1: mlirlite.BasicBlock* = mcore.CreateBlock(f1, "entry")
|
||
b1: mlirlite.IRBuilder* = mcore.NewBuilder(f1)
|
||
mlirlite.position_at_end(b1, e1)
|
||
|
||
# %0 = alloca i32, align 16
|
||
v_slot: mlirlite.Value* = mlirlite.build_alloca_aligned(b1, ty_i32, 16)
|
||
|
||
# call none asm sideeffect "nop", ""() (void form)
|
||
mlirlite.build_inline_asm(b1, ty_void, "nop", "", 1)
|
||
|
||
# %1 = call i32 asm sideeffect "mov $0, $1", "=r,r"() (returning form)
|
||
v_asm: mlirlite.Value* = mlirlite.build_inline_asm(b1, ty_i32, "mov $0, $1", "=r,r", 1)
|
||
|
||
v_x: mlirlite.Value* = mcore.SSAValue(ty_i32, "%x")
|
||
mlirlite.build_store(b1, v_x, v_slot, 0)
|
||
|
||
# TPV CallGenCBuiltin injected form (inteldialect + typed input operands)
|
||
mlirlite.block_append_text(pool, e1,
|
||
"%7 = call i32 asm sideeffect inteldialect \"mov $0, $1\", \"=r,r\"(i32 %x)")
|
||
|
||
mlirlite.build_ret(b1, v_asm)
|
||
|
||
# ---- (2) switch: 3 cases + default (no phi) ----
|
||
f2: mlirlite.Function* = mcore.CreateFunction(mod, "sw", ty_i32)
|
||
mcore.AddParam(f2, ty_i32, "%v")
|
||
e2: mlirlite.BasicBlock* = mcore.CreateBlock(f2, "entry")
|
||
c0: mlirlite.BasicBlock* = mcore.CreateBlock(f2, "case0")
|
||
c1: mlirlite.BasicBlock* = mcore.CreateBlock(f2, "case1")
|
||
c2: mlirlite.BasicBlock* = mcore.CreateBlock(f2, "case2")
|
||
dflt: mlirlite.BasicBlock* = mcore.CreateBlock(f2, "default")
|
||
b2: mlirlite.IRBuilder* = mcore.NewBuilder(f2)
|
||
|
||
mlirlite.position_at_end(b2, e2)
|
||
v_v: mlirlite.Value* = mcore.SSAValue(ty_i32, "%v")
|
||
sc0: mlirlite.SwitchCase* = mlirlite.new_switch_case(pool, mcore.ConstInt32(0), c0)
|
||
sc1: mlirlite.SwitchCase* = mlirlite.new_switch_case(pool, mcore.ConstInt32(1), c1)
|
||
sc2: mlirlite.SwitchCase* = mlirlite.new_switch_case(pool, mcore.ConstInt32(2), c2)
|
||
sc0.Next = sc1
|
||
sc1.Next = sc2
|
||
mlirlite.build_switch(b2, v_v, dflt, sc0, 3)
|
||
|
||
mlirlite.position_at_end(b2, c0)
|
||
mlirlite.build_ret(b2, v_v)
|
||
mlirlite.position_at_end(b2, c1)
|
||
mlirlite.build_ret(b2, v_v)
|
||
mlirlite.position_at_end(b2, c2)
|
||
mlirlite.build_ret(b2, v_v)
|
||
mlirlite.position_at_end(b2, dflt)
|
||
mlirlite.build_ret(b2, v_v)
|
||
|
||
# ---- (2') switch: case and default targets carry phi -> block operands ----
|
||
f3: mlirlite.Function* = mcore.CreateFunction(mod, "sw_phi", ty_i32)
|
||
mcore.AddParam(f3, ty_i32, "%v")
|
||
e3: mlirlite.BasicBlock* = mcore.CreateBlock(f3, "entry")
|
||
sk: mlirlite.BasicBlock* = mcore.CreateBlock(f3, "scase")
|
||
sd: mlirlite.BasicBlock* = mcore.CreateBlock(f3, "sdef")
|
||
b3: mlirlite.IRBuilder* = mcore.NewBuilder(f3)
|
||
|
||
mlirlite.position_at_end(b3, e3)
|
||
v_v3: mlirlite.Value* = mcore.SSAValue(ty_i32, "%v")
|
||
skc: mlirlite.SwitchCase* = mlirlite.new_switch_case(pool, mcore.ConstInt32(0), sk)
|
||
mlirlite.build_switch(b3, v_v3, sd, skc, 1)
|
||
|
||
# scase: %p0 = phi i32 [1, %entry]; ret i32 %p0
|
||
mlirlite.position_at_end(b3, sk)
|
||
in1: mlirlite.PhiIncoming* = mlirlite.new_phi_incoming(pool, mcore.ConstInt32(1), e3)
|
||
v_p0: mlirlite.Value* = mlirlite.build_phi(b3, ty_i32, in1, 1)
|
||
mlirlite.build_ret(b3, v_p0)
|
||
|
||
# sdef: %p1 = phi i32 [2, %entry]; ret i32 %p1
|
||
mlirlite.position_at_end(b3, sd)
|
||
in2: mlirlite.PhiIncoming* = mlirlite.new_phi_incoming(pool, mcore.ConstInt32(2), e3)
|
||
v_p1: mlirlite.Value* = mlirlite.build_phi(b3, ty_i32, in2, 1)
|
||
mlirlite.build_ret(b3, v_p1)
|
||
|
||
buf: str = pool.alloc(65536)
|
||
buf[0] = 0
|
||
mcore.PrintModule(buf, 65536, mod)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T13_align_switch_asm.mlir", buf)
|
||
|
||
# (1) alloca alignment: form is {alignment = 16 : i64} (LLVM ', align 16' is gone)
|
||
testcheck.check(string.strstr(buf, "llvm.alloca %c0 x i32 {alignment = 16 : i64} : (i32) -> !llvm.ptr") is not None,
|
||
"alloca 对齐 'llvm.alloca %c0 x i32 {alignment = 16 : i64} : (i32) -> !llvm.ptr'",
|
||
"missing alloca alignment attribute")
|
||
testcheck.check(string.strstr(buf, ", align 16") is None,
|
||
"LLVM 词法 ', align 16' 不再出现(已转写为属性)",
|
||
"raw LLVM alloca align leaked")
|
||
|
||
# (2) switch: value type follows the value (%v : i32,) plus case/default labels
|
||
testcheck.check(string.strstr(buf, "llvm.switch %v : i32, ^default [0: ^case0, 1: ^case1, 2: ^case2]") is not None,
|
||
"switch 实测形态 'llvm.switch %v : i32, ^default [0: ^case0, 1: ^case1, 2: ^case2]'",
|
||
"missing llvm.switch")
|
||
testcheck.check(string.strstr(buf, "^case0") is not None,
|
||
"contains '^case0'", "missing '^case0'")
|
||
testcheck.check(string.strstr(buf, "^case1") is not None,
|
||
"contains '^case1'", "missing '^case1'")
|
||
testcheck.check(string.strstr(buf, "^case2") is not None,
|
||
"contains '^case2'", "missing '^case2'")
|
||
|
||
# (2') switch phi targets -> block operands (carried by default and case)
|
||
testcheck.check(string.strstr(buf, "llvm.switch %v : i32, ^sdef(%c1 : i32) [0: ^scase(%c0 : i32)]") is not None,
|
||
"switch 块实参 'llvm.switch %v : i32, ^sdef(%c1 : i32) [0: ^scase(%c0 : i32)]'",
|
||
"missing switch block operands")
|
||
# Block-argument SSA names depend on renumbering, so assert only the shape
|
||
# "target label carries arguments", not specific names, avoiding brittle asserts
|
||
testcheck.check(string.strstr(buf, "^scase(%") is not None,
|
||
"case 目标块带参数 '^scase(%…: i32):'",
|
||
"missing block argument for switch case target")
|
||
testcheck.check(string.strstr(buf, "^sdef(%") is not None,
|
||
"default 目标块带参数 '^sdef(%…: i32):'",
|
||
"missing block argument for switch default target")
|
||
|
||
# (3) inline_asm: void / returning / inteldialect -> asm_dialect = intel
|
||
testcheck.check(string.strstr(buf, "llvm.inline_asm has_side_effects \"nop\", \"\" : () -> ()") is not None,
|
||
"无返回值形态 'llvm.inline_asm has_side_effects \"nop\", \"\" : () -> ()'",
|
||
"missing void inline_asm")
|
||
testcheck.check(string.strstr(buf, "llvm.inline_asm has_side_effects \"mov $0, $1\", \"=r,r\" : () -> i32") is not None,
|
||
"有返回值形态 '… \"=r,r\" : () -> i32'",
|
||
"missing returning inline_asm")
|
||
testcheck.check(string.strstr(buf, "asm_dialect = intel") is not None,
|
||
"LLVM 的 inteldialect → 'asm_dialect = intel'",
|
||
"missing asm_dialect for inteldialect")
|
||
testcheck.check(string.strstr(buf, "asm_dialect = intel \"mov $0, $1\", \"=r,r\" %x : (i32) -> i32") is not None,
|
||
"注入形态操作数不带括号 'asm_dialect = intel \"…\", \"…\" %x : (i32) -> i32'",
|
||
"missing operand form of inline_asm")
|
||
testcheck.check(string.strstr(buf, "inteldialect") is None,
|
||
"LLVM 词法关键字 'inteldialect' 不再出现",
|
||
"raw LLVM inteldialect keyword leaked")
|
||
|
||
|
||
# Test 14: vararg function/call, va_arg, cmpxchg, global attribute dict
|
||
# Five MLIR forms verified with mlir-opt 22.1.8; the attr dict precedes the type
|
||
def test_vararg_vaarg_cmpxchg(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 14: vararg / va_arg / cmpxchg / global attrs")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
ty_i8: mlirlite.LLVMType* = core.Int8()
|
||
ty_i32ptr: mlirlite.LLVMType* = core.Ptr(ty_i32)
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_vararg_vaarg_cmpxchg")
|
||
|
||
# ---- (1) vararg declaration: llvm.func @printf(%p: !llvm.ptr, ...) -> i32 ----
|
||
f_printf: mlirlite.Function* = mcore.CreateDeclare(mod, "printf", ty_i32)
|
||
mcore.AddParam(f_printf, ty_i32ptr, "%p")
|
||
f_printf.IsVarArg = 1
|
||
|
||
# ---- ③ va_arg: %0 = "llvm.va_arg"(%ap) : (!llvm.ptr) -> i32 ----
|
||
f_va: mlirlite.Function* = mcore.CreateFunction(mod, "va_reader", ty_i32)
|
||
mcore.AddParam(f_va, ty_i32ptr, "%ap")
|
||
e_va: mlirlite.BasicBlock* = mcore.CreateBlock(f_va, "entry")
|
||
b_va: mlirlite.IRBuilder* = mcore.NewBuilder(f_va)
|
||
mlirlite.position_at_end(b_va, e_va)
|
||
v_ap: mlirlite.Value* = mcore.SSAValue(ty_i32ptr, "%ap")
|
||
v_arg: mlirlite.Value* = mlirlite.build_va_arg(b_va, v_ap, ty_i32)
|
||
mlirlite.build_ret(b_va, v_arg)
|
||
|
||
# ---- (2) vararg call + (4) cmpxchg (builder single-result form) ----
|
||
f_cc: mlirlite.Function* = mcore.CreateFunction(mod, "vcall_cmpx", ty_i32)
|
||
mcore.AddParam(f_cc, ty_i32ptr, "%p")
|
||
mcore.AddParam(f_cc, ty_i32, "%n")
|
||
e_cc: mlirlite.BasicBlock* = mcore.CreateBlock(f_cc, "entry")
|
||
b_cc: mlirlite.IRBuilder* = mcore.NewBuilder(f_cc)
|
||
mlirlite.position_at_end(b_cc, e_cc)
|
||
v_p: mlirlite.Value* = mcore.SSAValue(ty_i32ptr, "%p")
|
||
v_n: mlirlite.Value* = mcore.SSAValue(ty_i32, "%n")
|
||
|
||
# %0 = cmpxchg !llvm.ptr %p, i32 %n, i32 %n acq_rel monotonic
|
||
# -> llvm.cmpxchg + two extractvalue; %0 binds to [0] (old value)
|
||
v_old: mlirlite.Value* = mlirlite.build_cmpxchg(b_cc, v_p, v_n, v_n,
|
||
mlirlite.ATOMIC_ORDER_ACQ_REL,
|
||
mlirlite.ATOMIC_ORDER_MONOTONIC)
|
||
# %1 = call i32 (i8*, i32, ...) @printf(i8* %p, i32 %n)
|
||
# -> %1 = llvm.call @printf(%p, %n) vararg(!llvm.func<i32 (!llvm.ptr, i32, ...)>)
|
||
mlirlite.value_set_next(v_p, v_n)
|
||
v_call: mlirlite.Value* = mlirlite.build_call(b_cc, "printf", v_p, 2, ty_i32, 1)
|
||
# ret uses the cmpxchg result to assert alias binding (text: %_0_v = extractvalue[0])
|
||
mlirlite.build_ret(b_cc, v_old)
|
||
|
||
# ---- (4') cmpxchg dual-result prefix (both names bind one extractvalue each) ----
|
||
f_cc2: mlirlite.Function* = mcore.CreateFunction(mod, "cmpx2", ty_i32)
|
||
mcore.AddParam(f_cc2, ty_i32ptr, "%p")
|
||
mcore.AddParam(f_cc2, ty_i32, "%n")
|
||
e_cc2: mlirlite.BasicBlock* = mcore.CreateBlock(f_cc2, "entry")
|
||
mlirlite.block_append_text(pool, e_cc2,
|
||
"%a, %b = cmpxchg !llvm.ptr %p, i32 %n, i32 %n acq_rel monotonic")
|
||
# %b = success bit [1]; %a = old value [0]
|
||
mlirlite.block_append_text(pool, e_cc2, "%r = zext i1 %b to i32")
|
||
mlirlite.block_append_text(pool, e_cc2, "ret i32 %a")
|
||
|
||
# ---- (5) global attr dict: section/alignment precede the type; unnamed_addr is a keyword ----
|
||
ty_arr3: mlirlite.LLVMType* = core.Array(ty_i8, 3)
|
||
init_s: str = pool.alloc(32)
|
||
string.strcpy(init_s, "c\"ab\\00\"")
|
||
|
||
gv_s: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "s", ty_arr3)
|
||
gv_s.Initializer = init_s
|
||
mlirlite.global_set_linkage(gv_s, "internal")
|
||
mlirlite.global_set_constant(gv_s, 1)
|
||
mlirlite.global_set_section(gv_s, "data")
|
||
gv_s.Align = 16
|
||
|
||
gv_t: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "t", ty_arr3)
|
||
gv_t.Initializer = init_s
|
||
mlirlite.global_set_linkage(gv_t, "internal")
|
||
mlirlite.global_set_constant(gv_t, 1)
|
||
mlirlite.global_set_unnamed_addr(gv_t, 1)
|
||
|
||
buf: str = pool.alloc(65536)
|
||
buf[0] = 0
|
||
mcore.PrintModule(buf, 65536, mod)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T14_vararg_vaarg_cmpxchg.mlir", buf)
|
||
|
||
# (1) vararg declaration: parameter list ends with ...
|
||
testcheck.check(string.strstr(buf, "llvm.func @printf(%p: !llvm.ptr, ...) -> i32") is not None,
|
||
"变参声明 'llvm.func @printf(%p: !llvm.ptr, ...) -> i32'",
|
||
"missing vararg function declaration")
|
||
testcheck.check(string.strstr(buf, ", ...) -> i32") is not None,
|
||
"参数表尾 '..., ...)' 形态(', ...)')",
|
||
"missing vararg ellipsis in declaration")
|
||
|
||
# (2) vararg call: vararg(full MLIR function type)
|
||
testcheck.check(string.strstr(buf, "vararg(!llvm.func<") is not None,
|
||
"变参调用 'vararg(!llvm.func<'", "missing vararg(...) attribute")
|
||
testcheck.check(string.strstr(buf,
|
||
"vararg(!llvm.func<i32 (!llvm.ptr, i32, ...)>) : (!llvm.ptr, i32) -> i32") is not None,
|
||
"vararg 完整函数类型 'vararg(!llvm.func<i32 (!llvm.ptr, i32, ...)>) : (!llvm.ptr, i32) -> i32'",
|
||
"missing vararg function type")
|
||
|
||
# (3) va_arg: generic form (deliberate; custom asm cannot express opaque pointers)
|
||
testcheck.check(string.strstr(buf, "\"llvm.va_arg\"(") is not None,
|
||
"va_arg generic 形态 '\"llvm.va_arg\"('", "missing generic llvm.va_arg")
|
||
testcheck.check(string.strstr(buf, ") : (!llvm.ptr) -> i32") is not None,
|
||
"va_arg 类型串 ') : (!llvm.ptr) -> i32'", "missing va_arg type list")
|
||
|
||
# (4) cmpxchg: single result + two extractvalue ([0]=old value, [1]=i1 success bit)
|
||
testcheck.check(string.strstr(buf, "llvm.cmpxchg") is not None,
|
||
"contains 'llvm.cmpxchg'", "missing llvm.cmpxchg")
|
||
testcheck.check(string.strstr(buf, "llvm.extractvalue ") is not None,
|
||
"contains 'llvm.extractvalue '", "missing llvm.extractvalue")
|
||
testcheck.check(string.strstr(buf, "[0] : !llvm.struct<(i32, i1)>") is not None,
|
||
"旧值抽取 '[0] : !llvm.struct<(i32, i1)>'", "missing extractvalue [0]")
|
||
testcheck.check(string.strstr(buf, "[1] : !llvm.struct<(i32, i1)>") is not None,
|
||
"成功位抽取 '[1] : !llvm.struct<(i32, i1)>'", "missing extractvalue [1]")
|
||
# extractvalue result names derive deterministically from the struct result name
|
||
# (%0 -> %_0_v/%_0_ok, %a -> %_a_v/%_a_ok; MLIR requires the _ name prefix)
|
||
testcheck.check(string.strstr(buf, "%_0_v = llvm.extractvalue %0[0] : !llvm.struct<(i32, i1)>") is not None,
|
||
"旧值定义 '%_0_v = llvm.extractvalue %0[0] …'",
|
||
"cmpxchg single result not deterministically named")
|
||
testcheck.check(string.strstr(buf, "%_0_ok = llvm.extractvalue %0[1] : !llvm.struct<(i32, i1)>") is not None,
|
||
"成功位定义 '%_0_ok = llvm.extractvalue %0[1] …'",
|
||
"cmpxchg ok bit not deterministically named")
|
||
testcheck.check(string.strstr(buf, "%_a_v = llvm.extractvalue %a[0] : !llvm.struct<(i32, i1)>") is not None,
|
||
"双结果首名定义 '%_a_v = llvm.extractvalue %a[0] …'",
|
||
"cmpxchg double-result old value not deterministically named")
|
||
testcheck.check(string.strstr(buf, "%_a_ok = llvm.extractvalue %a[1] : !llvm.struct<(i32, i1)>") is not None,
|
||
"双结果次名定义 '%_a_ok = llvm.extractvalue %a[1] …'",
|
||
"cmpxchg double-result ok bit not deterministically named")
|
||
# Reference side: aliases rewrite %0/%a/%b to those names (def and use match)
|
||
testcheck.check(string.strstr(buf, "llvm.return %_0_v : i32") is not None,
|
||
"单结果引用改写 'llvm.return %_0_v : i32'",
|
||
"cmpxchg result not bound to old value")
|
||
testcheck.check(string.strstr(buf, "llvm.zext %_a_ok : i1 to i32") is not None,
|
||
"双结果次名引用改写 'llvm.zext %_a_ok : i1 to i32'",
|
||
"cmpxchg second name not bound to success bit")
|
||
testcheck.check(string.strstr(buf, "llvm.return %_a_v : i32") is not None,
|
||
"双结果首名引用改写 'llvm.return %_a_v : i32'",
|
||
"cmpxchg first name not bound to old value")
|
||
|
||
# (5) attr dict precedes the type (whole-string assert); unnamed_addr is not in the dict
|
||
testcheck.check(string.strstr(buf,
|
||
"(\"ab\\00\") {section = \"data\", alignment = 16 : i64} : !llvm.array<3 x i8>") is not None,
|
||
"属性字典在类型之前 '(\"ab\\00\") {section = \"data\", alignment = 16 : i64} : !llvm.array<3 x i8>'",
|
||
"global attribute dict not before type")
|
||
testcheck.check(string.strstr(buf, "!llvm.array<3 x i8> {section") is None,
|
||
"字典不在类型之后('!llvm.array<3 x i8> {section' 不出现)",
|
||
"global attribute dict leaked after type")
|
||
testcheck.check(string.strstr(buf, "unnamed_addr constant @t(") is not None,
|
||
"unnamed_addr 作关键字 'unnamed_addr constant @t('",
|
||
"missing unnamed_addr keyword")
|
||
testcheck.check(string.strstr(buf, "{unnamed_addr") is None,
|
||
"unnamed_addr 不进属性字典('{unnamed_addr' 不出现)",
|
||
"unnamed_addr leaked into attribute dict")
|
||
|
||
|
||
# Test 15: named struct types (LLVM %"name" = type) in MLIR
|
||
# MLIR has no type defs: drop def lines, inline by value, keep pointers opaque !llvm.ptr
|
||
def test_named_struct(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 15: named struct type")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
ty_i8: mlirlite.LLVMType* = core.Int8()
|
||
ty_i8ptr: mlirlite.LLVMType* = core.Ptr(ty_i8)
|
||
|
||
# Named struct %"aa11bb22cc33dd44.Cls" = type { i32, i8* } (sha1-style prefix with '.')
|
||
ns1: mlirlite.ParamNode* = core.NewParamNode(ty_i32)
|
||
ns2: mlirlite.ParamNode* = core.NewParamNode(ty_i8ptr)
|
||
ns_head: mlirlite.ParamNode* = core.ParamListAppend(None, None, ns1)
|
||
core.ParamListAppend(ns_head, ns1, ns2)
|
||
ty_named: mlirlite.LLVMType* = core.Struct(ns_head, 2, "aa11bb22cc33dd44.Cls")
|
||
ty_named_ptr: mlirlite.LLVMType* = core.Ptr(ty_named)
|
||
|
||
# Packed named struct %"aa11bb22cc33dd44.Packed" = type <{ i32, i8 }>
|
||
# MLIR: !llvm.struct<"aa11bb22cc33dd44.Packed", packed (i32, i8)>
|
||
pk1: mlirlite.ParamNode* = core.NewParamNode(ty_i32)
|
||
pk2: mlirlite.ParamNode* = core.NewParamNode(ty_i8)
|
||
pk_head: mlirlite.ParamNode* = core.ParamListAppend(None, None, pk1)
|
||
core.ParamListAppend(pk_head, pk1, pk2)
|
||
ty_packed: mlirlite.LLVMType* = core.Struct(pk_head, 2, "aa11bb22cc33dd44.Packed")
|
||
|
||
# ---- type text: by value = inline named form; pointer = opaque !llvm.ptr ----
|
||
tbuf: str = pool.alloc(256)
|
||
tbuf[0] = 0
|
||
core.PrintType(tbuf, 256, ty_named)
|
||
testcheck.check(string.strstr(tbuf, "!llvm.struct<\"aa11bb22cc33dd44.Cls\", (i32, !llvm.ptr)>") is not None,
|
||
"具名结构体按值形态 '!llvm.struct<\"aa11bb22cc33dd44.Cls\", (i32, !llvm.ptr)>'",
|
||
"named struct by-value form missing")
|
||
# A '.' / digit-leading name needs no extra escaping or quotes (verified)
|
||
testcheck.check(string.strstr(tbuf, "<\"aa11bb22cc33dd44.Cls\",") is not None,
|
||
"名字直接作字符串字面量(不额外转义)",
|
||
"named struct name literal form missing")
|
||
tbuf[0] = 0
|
||
core.PrintType(tbuf, 256, ty_named_ptr)
|
||
testcheck.check(string.strcmp(tbuf, "!llvm.ptr") == 0,
|
||
"Ptr(具名结构体) -> '!llvm.ptr'(不透明指针,不回退为指针类型)",
|
||
"named struct pointer must stay opaque !llvm.ptr")
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_named_struct")
|
||
# Real flow: register named structs via module_add_named_type for the print-time table
|
||
mlirlite.module_add_named_type(mod, pool, ty_named)
|
||
# Only place the packed flag is set (after add_named_type; carried by the table)
|
||
mlirlite.module_add_named_type(mod, pool, ty_packed)
|
||
mlirlite.module_set_named_type_packed(mod, pool, ty_packed, 1)
|
||
|
||
# ---- (1) by-value global (inlines the named body) + (2) pointer global to it ----
|
||
gv_v: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "g_cls", ty_named)
|
||
gv_p: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "g_cls_p", ty_named_ptr)
|
||
# (3) packed by-value global
|
||
gv_pk: mlirlite.GlobalVariable* = mcore.NewGlobal(mod, "g_packed", ty_packed)
|
||
|
||
# ---- (1) by-value parameter + (2) pointer parameter ----
|
||
fn: mlirlite.Function* = mcore.CreateFunction(mod, "use_named", ty_i32)
|
||
mcore.AddParam(fn, ty_named, "%v")
|
||
mcore.AddParam(fn, ty_named_ptr, "%p")
|
||
entry: mlirlite.BasicBlock* = mcore.CreateBlock(fn, "entry")
|
||
b: mlirlite.IRBuilder* = mcore.NewBuilder(fn)
|
||
mlirlite.position_at_end(b, entry)
|
||
|
||
# ---- (4) bare-name form in mid text (hand-written / TPV-injected) ----
|
||
# Print time expands it via the table; pointer form %"name"* stays !llvm.ptr
|
||
mlirlite.block_append_text(pool, entry, "%0 = alloca %\"aa11bb22cc33dd44.Cls\"")
|
||
mlirlite.block_append_text(pool, entry, "%1 = load i32, %\"aa11bb22cc33dd44.Cls\"* %0")
|
||
# (5) packed named struct text form (same table slot carries the packed flag)
|
||
mlirlite.block_append_text(pool, entry, "%2 = alloca %\"aa11bb22cc33dd44.Packed\"")
|
||
v_1: mlirlite.Value* = mcore.SSAValue(ty_i32, "%1")
|
||
mlirlite.build_ret(b, v_1)
|
||
|
||
# ---- (6) by-value packed parameter: separate declare, keeping use_named asserts ----
|
||
fn_pk: mlirlite.Function* = mcore.CreateDeclare(mod, "use_packed", ty_i32)
|
||
mcore.AddParam(fn_pk, ty_packed, "%pk")
|
||
|
||
buf: str = pool.alloc(65536)
|
||
buf[0] = 0
|
||
mcore.PrintModule(buf, 65536, mod)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T15_named_struct.mlir", buf)
|
||
|
||
# (1) by-value parameter / global: inline the named body at the use site
|
||
testcheck.check(string.strstr(buf, "llvm.func @use_named(%v: !llvm.struct<\"aa11bb22cc33dd44.Cls\", (i32, !llvm.ptr)>, %p: !llvm.ptr) -> i32") is not None,
|
||
"按值形参 'llvm.func @use_named(%v: !llvm.struct<\"aa11bb22cc33dd44.Cls\", (i32, !llvm.ptr)>, %p: !llvm.ptr) -> i32'",
|
||
"missing inline named struct parameter")
|
||
testcheck.check(string.strstr(buf, "@g_cls() : !llvm.struct<\"aa11bb22cc33dd44.Cls\", (i32, !llvm.ptr)>") is not None,
|
||
"按值全局 '@g_cls() : !llvm.struct<\"aa11bb22cc33dd44.Cls\", (i32, !llvm.ptr)>'",
|
||
"missing inline named struct global")
|
||
# (2) pointer: remains opaque !llvm.ptr
|
||
testcheck.check(string.strstr(buf, "@g_cls_p() : !llvm.ptr") is not None,
|
||
"指针全局 '@g_cls_p() : !llvm.ptr'",
|
||
"named struct pointer global must be !llvm.ptr")
|
||
testcheck.check(string.strstr(buf, "!llvm.struct<\"aa11bb22cc33dd44.Cls\", (i32, !llvm.ptr)>*") is None,
|
||
"不得出现指向具名结构体的指针类型(MLIR 不透明指针)",
|
||
"pointer-to-named-struct type leaked")
|
||
|
||
# (4) %"name" text expands (alloca element / load pointer type); no def line emitted
|
||
testcheck.check(string.strstr(buf, "%0 = llvm.alloca %c0 x !llvm.struct<\"aa11bb22cc33dd44.Cls\", (i32, !llvm.ptr)> : (i32) -> !llvm.ptr") is not None,
|
||
"文本 %\"name\" 展开为内联具名体(alloca 元素类型)",
|
||
"named struct type text not expanded in alloca")
|
||
testcheck.check(string.strstr(buf, "%1 = llvm.load %0 : !llvm.ptr -> i32") is not None,
|
||
"文本 %\"name\"* 仍为不透明指针('%1 = llvm.load %0 : !llvm.ptr -> i32')",
|
||
"named struct pointer text not collapsed to !llvm.ptr")
|
||
testcheck.check(string.strstr(buf, "%\"aa11bb22cc33dd44.Cls\"") is None,
|
||
"LLVM 口径的 %\"name\" 形态不再出现(已全部转写)",
|
||
"raw LLVM named type text leaked")
|
||
# (3) def lines dropped: MLIR has no type-def syntax (mlir-translate emits at use)
|
||
testcheck.check(string.strstr(buf, "\"aa11bb22cc33dd44.Cls\" = type") is None,
|
||
"模块不输出 '\"name\" = type {…}' 定义行",
|
||
"named type definition line should be dropped")
|
||
|
||
# (7) packed named struct: the table carries IsPacked and adds the 'packed ' keyword
|
||
testcheck.check(string.strstr(buf, "@g_packed() : !llvm.struct<\"aa11bb22cc33dd44.Packed\", packed (i32, i8)>") is not None,
|
||
"packed 按值全局 '@g_packed() : !llvm.struct<\"aa11bb22cc33dd44.Packed\", packed (i32, i8)>'",
|
||
"missing packed inline named struct global")
|
||
testcheck.check(string.strstr(buf, "llvm.func @use_packed(%pk: !llvm.struct<\"aa11bb22cc33dd44.Packed\", packed (i32, i8)>) -> i32") is not None,
|
||
"packed 按值形参 'llvm.func @use_packed(%pk: !llvm.struct<\"aa11bb22cc33dd44.Packed\", packed (i32, i8)>) -> i32'",
|
||
"missing packed inline named struct parameter")
|
||
testcheck.check(string.strstr(buf, "x !llvm.struct<\"aa11bb22cc33dd44.Packed\", packed (i32, i8)> : (i32) -> !llvm.ptr") is not None,
|
||
"文本 %\"name\" 展开为 packed 内联具名体(alloca 元素类型)",
|
||
"packed named struct type text not expanded with packed")
|
||
# Negative: a non-packed named struct type string must not contain 'packed'
|
||
testcheck.check(string.strstr(buf, "!llvm.struct<\"aa11bb22cc33dd44.Cls\", packed") is None,
|
||
"非 packed 具名结构体类型串不含 'packed'",
|
||
"packed keyword leaked into non-packed named struct")
|
||
# Negative: a packed named struct must not render without the packed keyword
|
||
testcheck.check(string.strstr(buf, "!llvm.struct<\"aa11bb22cc33dd44.Packed\", (i32, i8)>") is None,
|
||
"packed 具名结构体不得渲染为无 packed 的形态",
|
||
"packed named struct rendered without packed keyword")
|
||
|
||
|
||
# Test 16: extractvalue for multi-result inline asm
|
||
# MLIR uses a single struct-typed result plus extractvalue with [idx] index lists
|
||
def test_extractvalue_multi_asm(pool: memhub.MemBuddy*):
|
||
testcheck.section("Test 16: extractvalue (multi-result inline asm)")
|
||
|
||
core: mlirlite.LLVMTypeCore* = mlirlite.LLVMTypeCore(pool)
|
||
mcore: mlirlite.LLVMModuleCore* = mlirlite.LLVMModuleCore(pool)
|
||
ty_i32: mlirlite.LLVMType* = core.Int32()
|
||
|
||
mod: mlirlite.LLVMModule* = mcore.NewModule("test_extractvalue_multi_asm")
|
||
func: mlirlite.Function* = mcore.CreateFunction(mod, "multi_asm", ty_i32)
|
||
entry: mlirlite.BasicBlock* = mcore.CreateBlock(func, "entry")
|
||
|
||
# (1) TPV real form: multi-result inline asm + two extractvalue (single index)
|
||
mlirlite.block_append_text(pool, entry,
|
||
"%2 = call {i32, i32} asm sideeffect \"mov $0, $1; mov $1, $1\", \"=r,r\"()")
|
||
mlirlite.block_append_text(pool, entry,
|
||
"%a = extractvalue {i32, i32} %2, 0")
|
||
mlirlite.block_append_text(pool, entry,
|
||
"%b = extractvalue {i32, i32} %2, 1")
|
||
# (2) multi-level index [1, 0] into a nested struct result (verified legal)
|
||
mlirlite.block_append_text(pool, entry,
|
||
"%3 = call {i32, {i32, i32}} asm sideeffect \"mov $0, $1\", \"=r,r\"()")
|
||
mlirlite.block_append_text(pool, entry,
|
||
"%d = extractvalue {i32, {i32, i32}} %3, 1, 0")
|
||
# Reference %a so the extractvalue result is actually used
|
||
mlirlite.block_append_text(pool, entry, "ret i32 %a")
|
||
|
||
buf: str = pool.alloc(65536)
|
||
buf[0] = 0
|
||
mcore.PrintModule(buf, 65536, mod)
|
||
testcheck.info(buf)
|
||
dump_mlir(pool, "T16_extractvalue.mlir", buf)
|
||
|
||
# (1) multi-result inline asm -> single struct-typed result
|
||
testcheck.check(string.strstr(buf, ": () -> !llvm.struct<(i32, i32)>") is not None,
|
||
"多结果内联汇编 '… : () -> !llvm.struct<(i32, i32)>'",
|
||
"multi-result inline_asm struct result missing")
|
||
testcheck.check(string.strstr(buf, "llvm.inline_asm has_side_effects") is not None,
|
||
"contains 'llvm.inline_asm has_side_effects'",
|
||
"missing llvm.inline_asm")
|
||
# (2) extractvalue: index inside [], aggregate type in MLIR form
|
||
testcheck.check(string.strstr(buf, "llvm.extractvalue ") is not None,
|
||
"contains 'llvm.extractvalue '", "missing llvm.extractvalue")
|
||
testcheck.check(string.strstr(buf, "[0] : !llvm.struct<(i32, i32)>") is not None,
|
||
"字段 0 抽取 '[0] : !llvm.struct<(i32, i32)>'",
|
||
"missing extractvalue [0]")
|
||
testcheck.check(string.strstr(buf, "[1] : !llvm.struct<(i32, i32)>") is not None,
|
||
"字段 1 抽取 '[1] : !llvm.struct<(i32, i32)>'",
|
||
"missing extractvalue [1]")
|
||
testcheck.check(string.strstr(buf, ": !llvm.struct<(i32, i32)>") is not None,
|
||
"聚合类型为 MLIR 结构体 ': !llvm.struct<(i32, i32)>'",
|
||
"aggregate type not rendered as MLIR struct")
|
||
# (3) multi-level index [1, 0] (comma-separated in MLIR, verified legal)
|
||
testcheck.check(string.strstr(buf, "[1, 0] : !llvm.struct<(i32, !llvm.struct<(i32, i32)>)>") is not None,
|
||
"多级索引 '[1, 0] : !llvm.struct<(i32, !llvm.struct<(i32, i32)>)>'",
|
||
"multi-level extractvalue index missing")
|
||
# Negative: LLVM <agg type> <value>, <index> must not appear (type text rewritten)
|
||
testcheck.check(string.strstr(buf, "{i32, i32} %") is None,
|
||
"LLVM 词法聚合类型文本不再出现('{i32, i32} %' 不出现)",
|
||
"raw LLVM aggregate type text leaked")
|
||
# Reference side: the extractvalue result name is returned as-is
|
||
testcheck.check(string.strstr(buf, "llvm.return %a : i32") is not None,
|
||
"extractvalue 结果被引用 'llvm.return %a : i32'",
|
||
"extractvalue result not referenced")
|
||
|
||
|
||
def _sub_arena(main_pool: memhub.MemBuddy*) -> rawptr:
|
||
"""Carve a sub-pool arena from the main pool and return its raw pointer.
|
||
|
||
MemBuddy is a stack value here, so returning one would dangle; fail fast if alloc fails.
|
||
"""
|
||
mem: rawptr = main_pool.alloc(SUB_POOL_SIZE)
|
||
if mem is None:
|
||
stdio.printf("FAIL: sub pool arena alloc failed (raise POOL_SIZE)\n")
|
||
stdlib.exit(1)
|
||
return mem
|
||
|
||
|
||
def main() -> int:
|
||
testcheck.begin("MLIRLiteTest: mlirlite 库(MLIR llvm 方言文本生成)")
|
||
|
||
# Init main pool: used only to carve a sub-pool per test (allocs use their sub-pool)
|
||
pool_mem: bytes = stdlib.malloc(POOL_SIZE)
|
||
if pool_mem is None:
|
||
stdio.printf("FAIL: malloc for pool failed\n")
|
||
return 1
|
||
pool: memhub.MemBuddy* = memhub.MemBuddy(pool_mem, POOL_SIZE)
|
||
if pool is None:
|
||
stdio.printf("FAIL: memhub.MemBuddy init failed\n")
|
||
return 1
|
||
|
||
# Sub-pools must be built in main's frame (see _sub_arena): MemBuddy is a stack
|
||
# value, so its pointer cannot cross functions. One per test, not overlapping.
|
||
sub: memhub.MemBuddy* = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_type_print(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_value_print(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_build_add(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_build_if_else(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_build_void(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_module_print(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_const_materialize(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_phi_block_args(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_mem_call_cast(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_globals(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_verify_valid(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_struct_global(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_alloca_align_switch_asm(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_vararg_vaarg_cmpxchg(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_named_struct(sub)
|
||
|
||
sub = memhub.MemBuddy(_sub_arena(pool), SUB_POOL_SIZE)
|
||
test_extractvalue_multi_asm(sub)
|
||
|
||
return testcheck.end()
|