lang: rename signed integer types sN -> iN
Surface rename of the signed integer family: s1..s64 become i1..i64
(u1..u64, usize, isize unchanged). 'string' keeps the s-prefix arm in
name classification; width parsing moves to the i-prefix arm next to
isize.
Internal TypeId tags follow the surface (.s8/.s16/.s32/.s64 ->
.i8/.i16/.i32/.i64), as do mono-key mangle fragments (ptr_i64,
tu_i64_bool) and all display/diagnostic formatting (i{d}).
Migrated in the same sweep: stdlib + examples + issue repros + FFI C
companions (shared symbol names like ffi_id_i64), expected
stdout/stderr/ir snapshots, specs.md, readme.md, CLAUDE.md/AGENTS.md,
implementation_plan.md, docs/, issue writeups. Vendored stb_image and
historical flow state left untouched.
zig build test: 426/426; examples suite: 595/595.
This commit is contained in:
@@ -29,12 +29,12 @@ test "emit: main() returns 42" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func main() -> s64 { return 42; }
|
||||
_ = b.beginFunction(str(&module, "main"), &.{}, .s64);
|
||||
// func main() -> i64 { return 42; }
|
||||
_ = b.beginFunction(str(&module, "main"), &.{}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
const c42 = b.constInt(42, .s64);
|
||||
b.ret(c42, .s64);
|
||||
const c42 = b.constInt(42, .i64);
|
||||
b.ret(c42, .i64);
|
||||
b.finalize();
|
||||
|
||||
// Emit to LLVM
|
||||
@@ -49,7 +49,7 @@ test "emit: main() returns 42" {
|
||||
const ir_str = emitter.dumpToString();
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "define") != null);
|
||||
// `main` is emitted with the C entry-point convention: it returns i32, so
|
||||
// the s64 const 42 is truncated to `ret i32 42`.
|
||||
// the i64 const 42 is truncated to `ret i32 42`.
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "ret i32 42") != null);
|
||||
}
|
||||
|
||||
@@ -60,12 +60,12 @@ test "emit: add(a, b) returns a + b" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func add(a: s64, b: s64) -> s64 { return a + b; }
|
||||
// func add(a: i64, b: i64) -> i64 { return a + b; }
|
||||
const params = &[_]Function.Param{
|
||||
.{ .name = str(&module, "a"), .ty = .s64 },
|
||||
.{ .name = str(&module, "b"), .ty = .s64 },
|
||||
.{ .name = str(&module, "a"), .ty = .i64 },
|
||||
.{ .name = str(&module, "b"), .ty = .i64 },
|
||||
};
|
||||
_ = b.beginFunction(str(&module, "add"), params, .s64);
|
||||
_ = b.beginFunction(str(&module, "add"), params, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
@@ -78,10 +78,10 @@ test "emit: add(a, b) returns a + b" {
|
||||
// at 0, and params are accessed differently. The lowering pass emits
|
||||
// alloca+store for params. For this test, we use const_int to test
|
||||
// the add instruction directly.
|
||||
const a = b.constInt(10, .s64);
|
||||
const a_b = b.constInt(32, .s64);
|
||||
const sum = b.add(a, a_b, .s64);
|
||||
b.ret(sum, .s64);
|
||||
const a = b.constInt(10, .i64);
|
||||
const a_b = b.constInt(32, .i64);
|
||||
const sum = b.add(a, a_b, .i64);
|
||||
b.ret(sum, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_add", .{});
|
||||
@@ -138,14 +138,14 @@ test "emit: negation" {
|
||||
|
||||
// Negating a constant folds; negate a param so `sub 0, %x` is emitted.
|
||||
_ = b.beginFunction(str(&module, "negate"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "x"), .ty = .s64 },
|
||||
}, .s64);
|
||||
.{ .name = str(&module, "x"), .ty = .i64 },
|
||||
}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const val = Ref.fromIndex(0);
|
||||
const neg = b.emit(.{ .neg = .{ .operand = val } }, .s64);
|
||||
b.ret(neg, .s64);
|
||||
const neg = b.emit(.{ .neg = .{ .operand = val } }, .i64);
|
||||
b.ret(neg, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_neg", .{});
|
||||
@@ -189,16 +189,16 @@ test "emit: alloca, store, load" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f() -> s64 { var x: s64 = 10; return x; }
|
||||
_ = b.beginFunction(str(&module, "f"), &.{}, .s64);
|
||||
// func f() -> i64 { var x: i64 = 10; return x; }
|
||||
_ = b.beginFunction(str(&module, "f"), &.{}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const x_ptr = b.alloca(.s64); // alloca s64 → *s64
|
||||
const ten = b.constInt(10, .s64);
|
||||
const x_ptr = b.alloca(.i64); // alloca i64 → *i64
|
||||
const ten = b.constInt(10, .i64);
|
||||
b.store(x_ptr, ten); // store 10 → *x
|
||||
const loaded = b.load(x_ptr, .s64); // load *x → s64
|
||||
b.ret(loaded, .s64);
|
||||
const loaded = b.load(x_ptr, .i64); // load *x → i64
|
||||
b.ret(loaded, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_mem", .{});
|
||||
@@ -221,12 +221,12 @@ test "emit: comparison and branch" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f(a, b) -> s64 { if (a < b) return 1; else return 0; }
|
||||
// func f(a, b) -> i64 { if (a < b) return 1; else return 0; }
|
||||
// Params (not constants) so the icmp isn't folded.
|
||||
_ = b.beginFunction(str(&module, "cmpfn"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "a"), .ty = .s64 },
|
||||
.{ .name = str(&module, "b"), .ty = .s64 },
|
||||
}, .s64);
|
||||
.{ .name = str(&module, "a"), .ty = .i64 },
|
||||
.{ .name = str(&module, "b"), .ty = .i64 },
|
||||
}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
const then_bb = b.appendBlock(str(&module, "then"), &.{});
|
||||
const else_bb = b.appendBlock(str(&module, "else"), &.{});
|
||||
@@ -238,12 +238,12 @@ test "emit: comparison and branch" {
|
||||
b.condBr(cond, then_bb, &.{}, else_bb, &.{});
|
||||
|
||||
b.switchToBlock(then_bb);
|
||||
const one = b.constInt(1, .s64);
|
||||
b.ret(one, .s64);
|
||||
const one = b.constInt(1, .i64);
|
||||
b.ret(one, .i64);
|
||||
|
||||
b.switchToBlock(else_bb);
|
||||
const zero = b.constInt(0, .s64);
|
||||
b.ret(zero, .s64);
|
||||
const zero = b.constInt(0, .i64);
|
||||
b.ret(zero, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_cmp", .{});
|
||||
@@ -264,27 +264,27 @@ test "emit: function call" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func add(a: s64, b: s64) -> s64 { return a + b; } (using constants)
|
||||
// func add(a: i64, b: i64) -> i64 { return a + b; } (using constants)
|
||||
const add_id = b.beginFunction(str(&module, "addfn"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "a"), .ty = .s64 },
|
||||
.{ .name = str(&module, "b"), .ty = .s64 },
|
||||
}, .s64);
|
||||
.{ .name = str(&module, "a"), .ty = .i64 },
|
||||
.{ .name = str(&module, "b"), .ty = .i64 },
|
||||
}, .i64);
|
||||
const add_entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(add_entry);
|
||||
const p0 = b.constInt(0, .s64); // placeholder
|
||||
const p1 = b.constInt(0, .s64);
|
||||
const sum = b.add(p0, p1, .s64);
|
||||
b.ret(sum, .s64);
|
||||
const p0 = b.constInt(0, .i64); // placeholder
|
||||
const p1 = b.constInt(0, .i64);
|
||||
const sum = b.add(p0, p1, .i64);
|
||||
b.ret(sum, .i64);
|
||||
b.finalize();
|
||||
|
||||
// func main() -> s64 { return addfn(3, 4); }
|
||||
_ = b.beginFunction(str(&module, "main"), &.{}, .s64);
|
||||
// func main() -> i64 { return addfn(3, 4); }
|
||||
_ = b.beginFunction(str(&module, "main"), &.{}, .i64);
|
||||
const main_entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(main_entry);
|
||||
const three = b.constInt(3, .s64);
|
||||
const four = b.constInt(4, .s64);
|
||||
const result = b.call(add_id, &.{ three, four }, .s64);
|
||||
b.ret(result, .s64);
|
||||
const three = b.constInt(3, .i64);
|
||||
const four = b.constInt(4, .i64);
|
||||
const result = b.call(add_id, &.{ three, four }, .i64);
|
||||
b.ret(result, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_call", .{});
|
||||
@@ -298,7 +298,7 @@ test "emit: function call" {
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "addfn") != null);
|
||||
}
|
||||
|
||||
test "emit: widen conversion s32 to s64" {
|
||||
test "emit: widen conversion i32 to i64" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
@@ -307,14 +307,14 @@ test "emit: widen conversion s32 to s64" {
|
||||
|
||||
// sext of a constant folds; widen a param so `sext` is emitted.
|
||||
_ = b.beginFunction(str(&module, "wfn"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "x"), .ty = .s32 },
|
||||
}, .s64);
|
||||
.{ .name = str(&module, "x"), .ty = .i32 },
|
||||
}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const val = Ref.fromIndex(0);
|
||||
const wide = b.widen(val, .s32, .s64);
|
||||
b.ret(wide, .s64);
|
||||
const wide = b.widen(val, .i32, .i64);
|
||||
b.ret(wide, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_widen", .{});
|
||||
@@ -338,10 +338,10 @@ test "emit: type conversion toLLVMType" {
|
||||
// Just verify toLLVMType doesn't crash for all builtin types
|
||||
_ = emitter.toLLVMType(.void);
|
||||
_ = emitter.toLLVMType(.bool);
|
||||
_ = emitter.toLLVMType(.s8);
|
||||
_ = emitter.toLLVMType(.s16);
|
||||
_ = emitter.toLLVMType(.s32);
|
||||
_ = emitter.toLLVMType(.s64);
|
||||
_ = emitter.toLLVMType(.i8);
|
||||
_ = emitter.toLLVMType(.i16);
|
||||
_ = emitter.toLLVMType(.i32);
|
||||
_ = emitter.toLLVMType(.i64);
|
||||
_ = emitter.toLLVMType(.u8);
|
||||
_ = emitter.toLLVMType(.u16);
|
||||
_ = emitter.toLLVMType(.u32);
|
||||
@@ -381,12 +381,12 @@ test "emit: abiCoerceParamType coerces C-ABI structs by size bucket" {
|
||||
defer module.deinit();
|
||||
|
||||
// Intern the shapes before building the emitter (toLLVMType reads live).
|
||||
const small = internStruct(&module, "Small", &.{ .s32, .s32 }); // 8 bytes
|
||||
const mid = internStruct(&module, "Mid", &.{ .s64, .s64 }); // 16 bytes
|
||||
const big = internStruct(&module, "Big", &.{ .s64, .s64, .s64 }); // 24 bytes
|
||||
const small = internStruct(&module, "Small", &.{ .i32, .i32 }); // 8 bytes
|
||||
const mid = internStruct(&module, "Mid", &.{ .i64, .i64 }); // 16 bytes
|
||||
const big = internStruct(&module, "Big", &.{ .i64, .i64, .i64 }); // 24 bytes
|
||||
const hfa_f = internStruct(&module, "HfaF", &.{ .f32, .f32, .f32, .f32 }); // 16, all-float
|
||||
const hfa_d = internStruct(&module, "HfaD", &.{ .f64, .f64 }); // 16, all-double
|
||||
const sl = module.types.sliceOf(.s32);
|
||||
const sl = module.types.sliceOf(.i32);
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_abi", .{});
|
||||
defer emitter.deinit();
|
||||
@@ -404,7 +404,7 @@ test "emit: abiCoerceParamType coerces C-ABI structs by size bucket" {
|
||||
try std.testing.expect(emitter.abiCoerceParamType(.string, emitter.toLLVMType(.string)) == emitter.cached_ptr);
|
||||
try std.testing.expect(emitter.abiCoerceParamType(sl, emitter.toLLVMType(sl)) == emitter.cached_ptr);
|
||||
// Scalars pass through unchanged.
|
||||
try std.testing.expect(emitter.abiCoerceParamType(.s32, emitter.toLLVMType(.s32)) == emitter.toLLVMType(.s32));
|
||||
try std.testing.expect(emitter.abiCoerceParamType(.i32, emitter.toLLVMType(.i32)) == emitter.toLLVMType(.i32));
|
||||
}
|
||||
|
||||
test "emit: needsByval only for > 16-byte non-HFA structs" {
|
||||
@@ -412,11 +412,11 @@ test "emit: needsByval only for > 16-byte non-HFA structs" {
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
const small = internStruct(&module, "Small", &.{ .s32, .s32 });
|
||||
const mid = internStruct(&module, "Mid", &.{ .s64, .s64 });
|
||||
const big = internStruct(&module, "Big", &.{ .s64, .s64, .s64 });
|
||||
const small = internStruct(&module, "Small", &.{ .i32, .i32 });
|
||||
const mid = internStruct(&module, "Mid", &.{ .i64, .i64 });
|
||||
const big = internStruct(&module, "Big", &.{ .i64, .i64, .i64 });
|
||||
const hfa_d = internStruct(&module, "HfaD", &.{ .f64, .f64 });
|
||||
const sl = module.types.sliceOf(.s32);
|
||||
const sl = module.types.sliceOf(.i32);
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_byval", .{});
|
||||
defer emitter.deinit();
|
||||
@@ -427,7 +427,7 @@ test "emit: needsByval only for > 16-byte non-HFA structs" {
|
||||
try std.testing.expect(!emitter.needsByval(hfa_d, emitter.toLLVMType(hfa_d))); // HFA
|
||||
try std.testing.expect(!emitter.needsByval(.string, emitter.toLLVMType(.string)));
|
||||
try std.testing.expect(!emitter.needsByval(sl, emitter.toLLVMType(sl)));
|
||||
try std.testing.expect(!emitter.needsByval(.s32, emitter.toLLVMType(.s32))); // non-struct
|
||||
try std.testing.expect(!emitter.needsByval(.i32, emitter.toLLVMType(.i32))); // non-struct
|
||||
}
|
||||
|
||||
// ── Struct/Enum/Union tests ─────────────────────────────────────────
|
||||
@@ -437,10 +437,10 @@ test "emit: struct_init and struct_get" {
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
// Create a struct type: Point { x: s64, y: s64 }
|
||||
// Create a struct type: Point { x: i64, y: i64 }
|
||||
const fields = &[_]types.TypeInfo.StructInfo.Field{
|
||||
.{ .name = str(&module, "x"), .ty = .s64 },
|
||||
.{ .name = str(&module, "y"), .ty = .s64 },
|
||||
.{ .name = str(&module, "x"), .ty = .i64 },
|
||||
.{ .name = str(&module, "y"), .ty = .i64 },
|
||||
};
|
||||
const owned_fields = alloc.dupe(types.TypeInfo.StructInfo.Field, fields) catch unreachable;
|
||||
defer alloc.free(owned_fields);
|
||||
@@ -451,20 +451,20 @@ test "emit: struct_init and struct_get" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f(v) -> s64 { p = Point{v, 20}; return p.y; }
|
||||
// func f(v) -> i64 { p = Point{v, 20}; return p.y; }
|
||||
// A param operand keeps the aggregate non-constant so insertvalue /
|
||||
// extractvalue survive (a fully-constant struct would be folded).
|
||||
_ = b.beginFunction(str(&module, "f"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "v"), .ty = .s64 },
|
||||
}, .s64);
|
||||
.{ .name = str(&module, "v"), .ty = .i64 },
|
||||
}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const x = Ref.fromIndex(0);
|
||||
const y = b.constInt(20, .s64);
|
||||
const y = b.constInt(20, .i64);
|
||||
const p = b.structInit(&.{ x, y }, point_ty);
|
||||
const py = b.structGet(p, 1, .s64);
|
||||
b.ret(py, .s64);
|
||||
const py = b.structGet(p, 1, .i64);
|
||||
b.ret(py, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_struct", .{});
|
||||
@@ -486,8 +486,8 @@ test "emit: struct_gep (pointer to field)" {
|
||||
|
||||
// Create struct type
|
||||
const fields = &[_]types.TypeInfo.StructInfo.Field{
|
||||
.{ .name = str(&module, "x"), .ty = .s64 },
|
||||
.{ .name = str(&module, "y"), .ty = .s64 },
|
||||
.{ .name = str(&module, "x"), .ty = .i64 },
|
||||
.{ .name = str(&module, "y"), .ty = .i64 },
|
||||
};
|
||||
const owned_fields = alloc.dupe(types.TypeInfo.StructInfo.Field, fields) catch unreachable;
|
||||
defer alloc.free(owned_fields);
|
||||
@@ -495,21 +495,21 @@ test "emit: struct_gep (pointer to field)" {
|
||||
.name = str(&module, "Point"),
|
||||
.fields = owned_fields,
|
||||
} });
|
||||
const ptr_s64 = module.types.ptrTo(.s64);
|
||||
const ptr_i64 = module.types.ptrTo(.i64);
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f() -> s64 { var p: Point; p.y = 42; return p.y; }
|
||||
_ = b.beginFunction(str(&module, "gepfn"), &.{}, .s64);
|
||||
// func f() -> i64 { var p: Point; p.y = 42; return p.y; }
|
||||
_ = b.beginFunction(str(&module, "gepfn"), &.{}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const p_ptr = b.alloca(point_ty);
|
||||
const y_ptr = b.structGepTyped(p_ptr, 1, ptr_s64, point_ty);
|
||||
const c42 = b.constInt(42, .s64);
|
||||
const y_ptr = b.structGepTyped(p_ptr, 1, ptr_i64, point_ty);
|
||||
const c42 = b.constInt(42, .i64);
|
||||
b.store(y_ptr, c42);
|
||||
const loaded = b.load(y_ptr, .s64);
|
||||
b.ret(loaded, .s64);
|
||||
const loaded = b.load(y_ptr, .i64);
|
||||
b.ret(loaded, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_gep", .{});
|
||||
@@ -544,16 +544,16 @@ test "emit: enum_init and enum_tag (plain enum)" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f() -> s64 { c = Color.Green; return tag(c); }
|
||||
_ = b.beginFunction(str(&module, "enumfn"), &.{}, .s64);
|
||||
// func f() -> i64 { c = Color.Green; return tag(c); }
|
||||
_ = b.beginFunction(str(&module, "enumfn"), &.{}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const green = b.enumInit(1, Ref.none, color_ty); // Green = tag 1
|
||||
const tag = b.enumTag(green, .s32);
|
||||
// Widen tag from s32 to s64 for the return
|
||||
const wide = b.widen(tag, .s32, .s64);
|
||||
b.ret(wide, .s64);
|
||||
const tag = b.enumTag(green, .i32);
|
||||
// Widen tag from i32 to i64 for the return
|
||||
const wide = b.widen(tag, .i32, .i64);
|
||||
b.ret(wide, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_enum", .{});
|
||||
@@ -572,17 +572,17 @@ test "emit: tagged union (enum_init with payload, enum_tag, enum_payload)" {
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
// Create a tagged union: Shape { Circle: f64, Rect: s64 }
|
||||
// Create a tagged union: Shape { Circle: f64, Rect: i64 }
|
||||
const ufields = &[_]types.TypeInfo.StructInfo.Field{
|
||||
.{ .name = str(&module, "Circle"), .ty = .f64 },
|
||||
.{ .name = str(&module, "Rect"), .ty = .s64 },
|
||||
.{ .name = str(&module, "Rect"), .ty = .i64 },
|
||||
};
|
||||
const owned_ufields = alloc.dupe(types.TypeInfo.StructInfo.Field, ufields) catch unreachable;
|
||||
defer alloc.free(owned_ufields);
|
||||
const shape_ty = module.types.intern(.{ .tagged_union = .{
|
||||
.name = str(&module, "Shape"),
|
||||
.fields = owned_ufields,
|
||||
.tag_type = .s64,
|
||||
.tag_type = .i64,
|
||||
} });
|
||||
|
||||
var b = Builder.init(&module);
|
||||
@@ -597,7 +597,7 @@ test "emit: tagged union (enum_init with payload, enum_tag, enum_payload)" {
|
||||
|
||||
const radius = Ref.fromIndex(0);
|
||||
const shape = b.enumInit(0, radius, shape_ty); // Circle = tag 0
|
||||
const tag = b.emit(.{ .enum_tag = .{ .operand = shape } }, .s64);
|
||||
const tag = b.emit(.{ .enum_tag = .{ .operand = shape } }, .i64);
|
||||
_ = tag; // tag is used but we just check it doesn't crash
|
||||
const payload = b.emit(.{ .enum_payload = .{ .base = shape, .field_index = 0 } }, .f64);
|
||||
b.ret(payload, .f64);
|
||||
@@ -623,9 +623,9 @@ test "emit: union_get (reinterpret union field)" {
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
// Untagged union: Data { as_int: s64, as_float: f64 }
|
||||
// Untagged union: Data { as_int: i64, as_float: f64 }
|
||||
const ufields = &[_]types.TypeInfo.StructInfo.Field{
|
||||
.{ .name = str(&module, "as_int"), .ty = .s64 },
|
||||
.{ .name = str(&module, "as_int"), .ty = .i64 },
|
||||
.{ .name = str(&module, "as_float"), .ty = .f64 },
|
||||
};
|
||||
const owned_ufields = alloc.dupe(types.TypeInfo.StructInfo.Field, ufields) catch unreachable;
|
||||
@@ -637,15 +637,15 @@ test "emit: union_get (reinterpret union field)" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f() -> s64 { d = Data.as_int(42); return union_get(d, 0) as s64; }
|
||||
_ = b.beginFunction(str(&module, "ugfn"), &.{}, .s64);
|
||||
// func f() -> i64 { d = Data.as_int(42); return union_get(d, 0) as i64; }
|
||||
_ = b.beginFunction(str(&module, "ugfn"), &.{}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const val = b.constInt(42, .s64);
|
||||
const val = b.constInt(42, .i64);
|
||||
const d = b.enumInit(0, val, data_ty);
|
||||
const got = b.emit(.{ .union_get = .{ .base = d, .field_index = 0 } }, .s64);
|
||||
b.ret(got, .s64);
|
||||
const got = b.emit(.{ .union_get = .{ .base = d, .field_index = 0 } }, .i64);
|
||||
b.ret(got, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_union_get", .{});
|
||||
@@ -667,19 +667,19 @@ test "emit: array index_get" {
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
const arr_ty = module.types.arrayOf(.s64, 3);
|
||||
const arr_ty = module.types.arrayOf(.i64, 3);
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f() -> s64 { arr: [3]s64 = ---; return arr[1]; }
|
||||
_ = b.beginFunction(str(&module, "arr_idx"), &.{}, .s64);
|
||||
// func f() -> i64 { arr: [3]i64 = ---; return arr[1]; }
|
||||
_ = b.beginFunction(str(&module, "arr_idx"), &.{}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const undef_arr = b.emit(.{ .const_undef = {} }, arr_ty);
|
||||
const idx = b.constInt(1, .s64);
|
||||
const elem = b.emit(.{ .index_get = .{ .lhs = undef_arr, .rhs = idx } }, .s64);
|
||||
b.ret(elem, .s64);
|
||||
const idx = b.constInt(1, .i64);
|
||||
const elem = b.emit(.{ .index_get = .{ .lhs = undef_arr, .rhs = idx } }, .i64);
|
||||
b.ret(elem, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_arr_idx", .{});
|
||||
@@ -700,17 +700,17 @@ test "emit: length on slice" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f(s: string) -> s64 { return s.len; }
|
||||
// func f(s: string) -> i64 { return s.len; }
|
||||
// A string param keeps the value non-constant so extractvalue survives.
|
||||
_ = b.beginFunction(str(&module, "strlen"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "s"), .ty = .string },
|
||||
}, .s64);
|
||||
}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const s = Ref.fromIndex(0);
|
||||
const len = b.emit(.{ .length = .{ .operand = s } }, .s64);
|
||||
b.ret(len, .s64);
|
||||
const len = b.emit(.{ .length = .{ .operand = s } }, .i64);
|
||||
b.ret(len, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_len", .{});
|
||||
@@ -762,12 +762,12 @@ test "emit: array_to_slice" {
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
const arr_ty = module.types.arrayOf(.s64, 4);
|
||||
const slice_ty = module.types.sliceOf(.s64);
|
||||
const arr_ty = module.types.arrayOf(.i64, 4);
|
||||
const slice_ty = module.types.sliceOf(.i64);
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f() -> []s64 { var arr: [4]s64 = ---; return arr[:]; }
|
||||
// func f() -> []i64 { var arr: [4]i64 = ---; return arr[:]; }
|
||||
_ = b.beginFunction(str(&module, "a2s"), &.{}, slice_ty);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
@@ -798,13 +798,13 @@ test "emit: subslice" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f(s: []u8, lo: s64, hi: s64) -> []u8 { return s[lo..hi]; }
|
||||
// func f(s: []u8, lo: i64, hi: i64) -> []u8 { return s[lo..hi]; }
|
||||
// All operands are params: a constant base folds the GEP, and constant
|
||||
// lo/hi fold the `hi - lo` subtraction.
|
||||
_ = b.beginFunction(str(&module, "ssfn"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "s"), .ty = slice_ty },
|
||||
.{ .name = str(&module, "lo"), .ty = .s64 },
|
||||
.{ .name = str(&module, "hi"), .ty = .s64 },
|
||||
.{ .name = str(&module, "lo"), .ty = .i64 },
|
||||
.{ .name = str(&module, "hi"), .ty = .i64 },
|
||||
}, slice_ty);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
@@ -836,22 +836,22 @@ test "emit: optional_wrap and optional_unwrap (value type)" {
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
const opt_ty = module.types.optionalOf(.s64);
|
||||
const opt_ty = module.types.optionalOf(.i64);
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f(v) -> s64 { opt = wrap(v); return unwrap(opt); }
|
||||
// func f(v) -> i64 { opt = wrap(v); return unwrap(opt); }
|
||||
// Param value keeps the optional non-constant (else insertvalue folds).
|
||||
_ = b.beginFunction(str(&module, "optfn"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "v"), .ty = .s64 },
|
||||
}, .s64);
|
||||
.{ .name = str(&module, "v"), .ty = .i64 },
|
||||
}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const val = Ref.fromIndex(0);
|
||||
const wrapped = b.optionalWrap(val, opt_ty);
|
||||
const unwrapped = b.optionalUnwrap(wrapped, .s64);
|
||||
b.ret(unwrapped, .s64);
|
||||
const unwrapped = b.optionalUnwrap(wrapped, .i64);
|
||||
b.ret(unwrapped, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_opt", .{});
|
||||
@@ -872,13 +872,13 @@ test "emit: optional_has_value" {
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
const opt_ty = module.types.optionalOf(.s64);
|
||||
const opt_ty = module.types.optionalOf(.i64);
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// Param value keeps the optional non-constant (else extractvalue folds).
|
||||
_ = b.beginFunction(str(&module, "hasfn"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "v"), .ty = .s64 },
|
||||
.{ .name = str(&module, "v"), .ty = .i64 },
|
||||
}, .bool);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
@@ -909,15 +909,15 @@ test "emit: switch_br" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f(x: s64) -> s64 { match x { 0 => 10, 1 => 20, _ => 30 } }
|
||||
_ = b.beginFunction(str(&module, "swfn"), &.{}, .s64);
|
||||
// func f(x: i64) -> i64 { match x { 0 => 10, 1 => 20, _ => 30 } }
|
||||
_ = b.beginFunction(str(&module, "swfn"), &.{}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
const case0 = b.appendBlock(str(&module, "case0"), &.{});
|
||||
const case1 = b.appendBlock(str(&module, "case1"), &.{});
|
||||
const default_bb = b.appendBlock(str(&module, "default"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const x = b.constInt(1, .s64);
|
||||
const x = b.constInt(1, .i64);
|
||||
const cases = alloc.dupe(inst_mod.SwitchBranch.Case, &.{
|
||||
.{ .value = 0, .target = case0, .args = &.{} },
|
||||
.{ .value = 1, .target = case1, .args = &.{} },
|
||||
@@ -931,13 +931,13 @@ test "emit: switch_br" {
|
||||
} }, .void);
|
||||
|
||||
b.switchToBlock(case0);
|
||||
b.ret(b.constInt(10, .s64), .s64);
|
||||
b.ret(b.constInt(10, .i64), .i64);
|
||||
|
||||
b.switchToBlock(case1);
|
||||
b.ret(b.constInt(20, .s64), .s64);
|
||||
b.ret(b.constInt(20, .i64), .i64);
|
||||
|
||||
b.switchToBlock(default_bb);
|
||||
b.ret(b.constInt(30, .s64), .s64);
|
||||
b.ret(b.constInt(30, .i64), .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_switch", .{});
|
||||
@@ -957,18 +957,18 @@ test "emit: closure_create" {
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
const closure_ty = module.types.closureType(&.{.s64}, .s64);
|
||||
const closure_ty = module.types.closureType(&.{.i64}, .i64);
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// Create a dummy trampoline function
|
||||
const tramp_id = b.beginFunction(str(&module, "tramp"), &[_]inst_mod.Function.Param{
|
||||
.{ .name = str(&module, "env"), .ty = .s64 },
|
||||
.{ .name = str(&module, "x"), .ty = .s64 },
|
||||
}, .s64);
|
||||
.{ .name = str(&module, "env"), .ty = .i64 },
|
||||
.{ .name = str(&module, "x"), .ty = .i64 },
|
||||
}, .i64);
|
||||
const tramp_entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(tramp_entry);
|
||||
b.ret(b.constInt(0, .s64), .s64);
|
||||
b.ret(b.constInt(0, .i64), .i64);
|
||||
b.finalize();
|
||||
|
||||
// func f(e: *void) -> closure { return closure_create(tramp, e); }
|
||||
@@ -1004,18 +1004,18 @@ test "emit: box_any and unbox_any" {
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f(v) -> s64 { a = box(v); return unbox(a); }
|
||||
// func f(v) -> i64 { a = box(v); return unbox(a); }
|
||||
// Param value keeps the boxed Any non-constant (else insertvalue folds).
|
||||
_ = b.beginFunction(str(&module, "anyfn"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "v"), .ty = .s64 },
|
||||
}, .s64);
|
||||
.{ .name = str(&module, "v"), .ty = .i64 },
|
||||
}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const val = Ref.fromIndex(0);
|
||||
const boxed = b.emit(.{ .box_any = .{ .operand = val, .source_type = .s64 } }, .any);
|
||||
const unboxed = b.emit(.{ .unbox_any = .{ .operand = boxed } }, .s64);
|
||||
b.ret(unboxed, .s64);
|
||||
const boxed = b.emit(.{ .box_any = .{ .operand = val, .source_type = .i64 } }, .any);
|
||||
const unboxed = b.emit(.{ .unbox_any = .{ .operand = boxed } }, .i64);
|
||||
b.ret(unboxed, .i64);
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_any", .{});
|
||||
@@ -1036,15 +1036,15 @@ test "emit: ERR E3.0 — DWARF debug info (compile unit + subprogram + per-inst
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func main() -> s64 { return 42; } — with the `return` instruction
|
||||
// func main() -> i64 { return 42; } — with the `return` instruction
|
||||
// carrying a span that lands on line 3 of the source map below.
|
||||
_ = b.beginFunction(str(&module, "main"), &.{}, .s64);
|
||||
_ = b.beginFunction(str(&module, "main"), &.{}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
// "a\nb\nXYZ" — byte offset 4 ('X') is line 3, col 1.
|
||||
b.current_span = .{ .start = 4, .end = 5 };
|
||||
const c42 = b.constInt(42, .s64);
|
||||
b.ret(c42, .s64);
|
||||
const c42 = b.constInt(42, .i64);
|
||||
b.ret(c42, .i64);
|
||||
b.finalize();
|
||||
|
||||
// Source map keyed on the main file. setDebugContext + opt none
|
||||
@@ -1080,10 +1080,10 @@ test "emit: ERR E3.0 — no DWARF without a debug context (unit-test default)" {
|
||||
defer module.deinit();
|
||||
|
||||
var b = Builder.init(&module);
|
||||
_ = b.beginFunction(str(&module, "main"), &.{}, .s64);
|
||||
_ = b.beginFunction(str(&module, "main"), &.{}, .i64);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
b.ret(b.constInt(42, .s64), .s64);
|
||||
b.ret(b.constInt(42, .i64), .i64);
|
||||
b.finalize();
|
||||
|
||||
// No setDebugContext call → no source map → debug info off even at
|
||||
@@ -1111,9 +1111,9 @@ test "emit: argIRTypeOrFail surfaces .unresolved for an unresolvable FFI arg ref
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func ffifn(a: s64, b: f64) -> void { <entry> }
|
||||
// func ffifn(a: i64, b: f64) -> void { <entry> }
|
||||
const fid = b.beginFunction(str(&module, "ffifn"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "a"), .ty = .s64 },
|
||||
.{ .name = str(&module, "a"), .ty = .i64 },
|
||||
.{ .name = str(&module, "b"), .ty = .f64 },
|
||||
}, .void);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
@@ -1127,7 +1127,7 @@ test "emit: argIRTypeOrFail surfaces .unresolved for an unresolvable FFI arg ref
|
||||
|
||||
// Happy path: a real arg ref (param 0 / param 1) resolves byte-identically
|
||||
// to its declared IR type — the FFI fast path is unchanged.
|
||||
try std.testing.expectEqual(TypeId.s64, emitter.argIRTypeOrFail(Ref.fromIndex(0)));
|
||||
try std.testing.expectEqual(TypeId.i64, emitter.argIRTypeOrFail(Ref.fromIndex(0)));
|
||||
try std.testing.expectEqual(TypeId.f64, emitter.argIRTypeOrFail(Ref.fromIndex(1)));
|
||||
|
||||
// A ref past every param and instruction is unresolvable.
|
||||
@@ -1145,12 +1145,12 @@ test "emit: argIRTypeOrFail surfaces .unresolved for an unresolvable FFI arg ref
|
||||
try std.testing.expect(emitter.argIRTypeOrFail(bogus) != .void);
|
||||
}
|
||||
|
||||
// ── reflection-builtin arg-type lookup must fail loudly, never `.s64` ──
|
||||
// ── reflection-builtin arg-type lookup must fail loudly, never `.i64` ──
|
||||
// `reflectArgRepr` backs the `type_name` / `type_eq` reflection builtins, which read
|
||||
// their `Type` arg as a boxed `Any` aggregate (`.any` → extract value field) or a bare
|
||||
// i64 TypeId index. A ref it cannot resolve is a codegen invariant violation; it must
|
||||
// surface `.unresolved` (which the emit site hard-panics on) instead of the old silent
|
||||
// `getRefIRType(arg) orelse .s64` default that would mis-classify a boxed arg as bare
|
||||
// `getRefIRType(arg) orelse .i64` default that would mis-classify a boxed arg as bare
|
||||
// and read the wrong value with no diagnostic.
|
||||
test "emit: reflectArgRepr surfaces .unresolved for an unresolvable reflection arg ref (issue 0075)" {
|
||||
const alloc = std.testing.allocator;
|
||||
@@ -1159,10 +1159,10 @@ test "emit: reflectArgRepr surfaces .unresolved for an unresolvable reflection a
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func reflfn(boxed: any, bare: s64) -> void { <entry> }
|
||||
// func reflfn(boxed: any, bare: i64) -> void { <entry> }
|
||||
const fid = b.beginFunction(str(&module, "reflfn"), &[_]Function.Param{
|
||||
.{ .name = str(&module, "boxed"), .ty = .any },
|
||||
.{ .name = str(&module, "bare"), .ty = .s64 },
|
||||
.{ .name = str(&module, "bare"), .ty = .i64 },
|
||||
}, .void);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
@@ -1174,7 +1174,7 @@ test "emit: reflectArgRepr surfaces .unresolved for an unresolvable reflection a
|
||||
emitter.current_func_idx = fid.index();
|
||||
|
||||
// Happy path: a boxed `.any` Type arg classifies as `.boxed` (extract value
|
||||
// field); a bare `.s64` TypeId arg classifies as `.bare` (use directly).
|
||||
// field); a bare `.i64` TypeId arg classifies as `.bare` (use directly).
|
||||
// These decisions are byte-identical to the pre-fix `== .any` gate.
|
||||
try std.testing.expectEqual(LLVMEmitter.ReflectArgRepr.boxed, emitter.reflectArgRepr(Ref.fromIndex(0)));
|
||||
try std.testing.expectEqual(LLVMEmitter.ReflectArgRepr.bare, emitter.reflectArgRepr(Ref.fromIndex(1)));
|
||||
@@ -1183,11 +1183,11 @@ test "emit: reflectArgRepr surfaces .unresolved for an unresolvable reflection a
|
||||
const bogus = Ref.fromIndex(100_000);
|
||||
try std.testing.expectEqual(@as(?TypeId, null), emitter.getRefIRType(bogus));
|
||||
|
||||
// Fail-before: the old `getRefIRType(arg) orelse .s64` would silently yield
|
||||
// `.s64` here — which `!= .any`, so the reflection arm would treat a failed
|
||||
// Fail-before: the old `getRefIRType(arg) orelse .i64` would silently yield
|
||||
// `.i64` here — which `!= .any`, so the reflection arm would treat a failed
|
||||
// lookup as a bare i64 and read the wrong value with no diagnostic.
|
||||
try std.testing.expectEqual(TypeId.s64, emitter.getRefIRType(bogus) orelse TypeId.s64);
|
||||
try std.testing.expect((emitter.getRefIRType(bogus) orelse TypeId.s64) != .any);
|
||||
try std.testing.expectEqual(TypeId.i64, emitter.getRefIRType(bogus) orelse TypeId.i64);
|
||||
try std.testing.expect((emitter.getRefIRType(bogus) orelse TypeId.i64) != .any);
|
||||
|
||||
// Pass-after: the classifier returns the dedicated `.unresolved` variant,
|
||||
// never `.bare`, so the emit site trips its hard panic instead of silently
|
||||
|
||||
Reference in New Issue
Block a user