From 03128b0e6180c1e13182803204d73f510de67ff4 Mon Sep 17 00:00:00 2001 From: xtex Date: Fri, 16 Oct 2026 08:40:44 +0800 Subject: [PATCH 01/12] loongarch: implement constant load of slice fields --- src/codegen/loongarch/Select.zig | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index 3b017bf03e115c640e5250ed2c8d1387e1b4264b..f4d5eedb5a52ce3e8ab9cc137227699a52d24484 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -6615,8 +6615,23 @@ fn moveConstant(isel: *Select, dst: Value.Location, init_constant: Constant, ini .field => |field_idx| { var base_ptr = ip.indexToKey(field_idx.base).ptr; const agg_ty: ZigType = .fromInterned(ip.indexToKey(base_ptr.ty).ptr_type.child); - base_ptr.byte_offset += agg_ty.structFieldOffset(@intCast(field_idx.index), zcu) + ptr.byte_offset; - continue :constant_key .{ .ptr = base_ptr }; + switch (ip.indexToKey(agg_ty.toIntern())) { + .ptr_type => |ptr_ty| { + assert(ptr_ty.flags.size == .slice); + const field_offset = switch (field_idx.index) { + Constant.slice_ptr_index => 0, + Constant.slice_len_index => isel.gprSize(), + else => unreachable, + }; + base_ptr.byte_offset += ptr.byte_offset + field_offset; + continue :constant_key .{ .ptr = base_ptr }; + }, + .struct_type, .tuple_type, .union_type => { + base_ptr.byte_offset += agg_ty.structFieldOffset(@intCast(field_idx.index), zcu) + ptr.byte_offset; + continue :constant_key .{ .ptr = base_ptr }; + }, + else => unreachable, + } }, .comptime_alloc, .comptime_field, .arr_elem => unreachable, }; -- 2.54.0 From f039581a2d9c4072db3f5d97c238a1a02d027c00 Mon Sep 17 00:00:00 2001 From: xtex Date: Fri, 16 Oct 2026 09:17:18 +0800 Subject: [PATCH 02/12] loongarch: use failUnimplemented for i128 sign-ext So it can be trapped by debug_trap_unimplemented_code --- src/codegen/loongarch/Select.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index f4d5eedb5a52ce3e8ab9cc137227699a52d24484..2b86fb195ffeb8ee280fcf3735a9b824bb0f8519 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -895,7 +895,7 @@ pub const Value = struct { }; const unused_bits = @max(new_unused_bits, old_unused_bits); if (dst_ext == src_ext and bit_size <= old_bits) return; - tracking_log.debug("{f}: {t} ({t}) -> {t} ({t}), {d}b -> {d}b", .{ vi, src_ext, new_ext, dst_ext, old_ext, old_bits, bit_size }); + tracking_log.debug("{f}: in {t} ({t}) -> out {t} ({t}), {d}b -> {d}b", .{ vi, src_ext, new_ext, dst_ext, old_ext, old_bits, bit_size }); // avoid setting extension to .garbage to reduce MIR for sequences like // zero_ext -> garbage -> zero_ext @@ -914,7 +914,10 @@ pub const Value = struct { try vi_mat.finish(isel); } else { const unused_bytes = std.math.divCeil(u64, unused_bits, 8) catch unreachable; - assert(unused_bytes <= isel.gprSize()); // TODO larger extending + if (unused_bytes > isel.gprSize()) { + try isel.failUnimplemented("unimplemented sign-extend width > GPR", .{}); + return; + } const used_bytes = vi.size(isel) - unused_bytes; var hit = false; -- 2.54.0 From 415bc9e8fe70c488db4298d9aa47751f6659dfb2 Mon Sep 17 00:00:00 2001 From: xtex Date: Fri, 16 Oct 2026 09:18:27 +0800 Subject: [PATCH 03/12] loongarch: fix post-materialization sign-extend sequence --- src/codegen/loongarch/Select.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index 2b86fb195ffeb8ee280fcf3735a9b824bb0f8519..6320fef5f2de0e068205e555a5225c7ed45b2920 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -2127,13 +2127,6 @@ pub const Value = struct { .large => value.location_payload.large.stack_slot = vi.allocStackSlot(isel), } } - switch (value.flags.location_tag) { - .extreme => unreachable, - .small => {}, - .large => { - try vi.reextendAdvanced(isel, vi.bitSize(isel), mat.extension, vi.extension(isel)); - }, - } const vi_loc = vi.location(isel).?; const maybe_loc_reg = vi_loc.asRegister(); if (maybe_loc_reg) |loc_reg| { @@ -2155,6 +2148,13 @@ pub const Value = struct { assert(loc_live.* == .allocating); loc_live.* = vi; } + switch (value.flags.location_tag) { + .extreme => unreachable, + .small => {}, + .large => { + try vi.reextendAdvanced(isel, vi.bitSize(isel), mat.extension, vi.extension(isel)); + }, + } }, .value => unreachable, .address => |addr_vi| { -- 2.54.0 From 19e0c4b97fe0dba2e0e940413599c2668aa9a179 Mon Sep 17 00:00:00 2001 From: xtex Date: Fri, 16 Oct 2026 09:35:33 +0800 Subject: [PATCH 04/12] loongarch: remove broken support for sign-extend width > GPR --- src/codegen/loongarch/Select.zig | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index 6320fef5f2de0e068205e555a5225c7ed45b2920..4b1967198cc53cf258847867e33f8aa19cae121e 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -913,36 +913,8 @@ pub const Value = struct { ); try vi_mat.finish(isel); } else { - const unused_bytes = std.math.divCeil(u64, unused_bits, 8) catch unreachable; - if (unused_bytes > isel.gprSize()) { - try isel.failUnimplemented("unimplemented sign-extend width > GPR", .{}); - return; - } - const used_bytes = vi.size(isel) - unused_bytes; - - var hit = false; - var walker = vi.walk(isel, .{}); - while (walker.next()) |part_vi| { - const part_offset = part_vi.offsetIn(isel, vi); - const part_size = part_vi.size(isel); - const part_end = part_offset + part_size; - if (part_end <= used_bytes) continue; - if (part_size > isel.gprSize()) continue; - - walker.skipChildren(part_vi); - - const part_mat = try part_vi.mat(isel, .{ .pref = .only_reg }); - try isel.fillUnusedBits( - part_mat.reg(), - part_mat.reg(), - dst_ext, - src_ext, - @intCast(unused_bits - ((vi.size(isel) - part_end) * 8)), - ); - try part_mat.finish(isel); - if (hit) unreachable; // TODO - hit = true; - } + try isel.failUnimplemented("unimplemented sign-extend width > GPR", .{}); + return; } } -- 2.54.0 From d62ffc3487adc03981a40898545ca70c0354e88f Mon Sep 17 00:00:00 2001 From: xtex Date: Fri, 16 Oct 2026 09:38:10 +0800 Subject: [PATCH 05/12] loongarch: implement bit_cast between ints and floats --- src/codegen/loongarch/Select.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index 4b1967198cc53cf258847867e33f8aa19cae121e..68624bb44bb81dad28772e63354c5c7d8e3a303b 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -3779,6 +3779,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) codegen.Error!void 64 => .@"movfr2gr.d"(dst_reg, src_reg), }); try src_mat.finish(isel); + } else if ((dst_ty.isAbiInt(zcu) and src_tag == .float) or ((dst_tag == .float and src_ty.isAbiInt(zcu)))) { + try dst_vi.value.defMove(isel, ty_op.operand); } else if (dst_ty.isAbiInt(zcu) and src_tag == .array and src_ty.childType(zcu).isAbiInt(zcu)) { const dst_int_info = dst_ty.intInfo(zcu); const src_child_int_info = src_ty.childType(zcu).intInfo(zcu); -- 2.54.0 From 6d7b8dbcc6544c0f9cdbeaa1fecd29596d647db3 Mon Sep 17 00:00:00 2001 From: xtex Date: Fri, 16 Oct 2026 09:43:59 +0800 Subject: [PATCH 06/12] loongarch: implement analysis for legalize_compiler_rt_call --- src/codegen/loongarch/Select.zig | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index 68624bb44bb81dad28772e63354c5c7d8e3a303b..1fa209aabb92c34b1d8d5680aaa46c45011216ae 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -2733,6 +2733,33 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { try isel.analyzeUse(arg); } }, + .legalize_compiler_rt_call => { + const compiler_rt_call = isel.air.unwrapCompilerRtCall(air_inst_index); + + for (compiler_rt_call.args) |arg| + try isel.analyzeUse(arg); + + var cc_it: CallAbiIterator = .{ + .isel = isel, + .cc = &compiler_rt_call.func.@"callconv"(isel.target), + }; + if (try cc_it.resolve(compiler_rt_call.func.returnType(), true)) |ret_vi| { + tracking_log.debug("{f} <- %{d} (call return)", .{ ret_vi, @backingInt(air_inst_index) }); + switch (ret_vi.parent(isel)) { + .none => {}, + .value, .constant => unreachable, + .address => |address_vi| { + defer address_vi.deref(isel); + const ret_value = ret_vi.get(isel); + ret_value.flags.parent_tag = .none; + ret_value.parent_payload = .{ .none = {} }; + }, + } + try isel.live_values.putNoClobber(gpa, air_inst_index, ret_vi); + + try isel.def_order.putNoClobber(gpa, air_inst_index, {}); + } + }, .cond_br => { const pl_op = air_data[@backingInt(air_inst_index)].pl_op; const extra = isel.air.extraData(Air.CondBr, pl_op.payload); -- 2.54.0 From ff31d556a25b04f9a7bfdf60879149334d440e11 Mon Sep 17 00:00:00 2001 From: xtex Date: Fri, 16 Oct 2026 09:44:31 +0800 Subject: [PATCH 07/12] loongarch: enable f128 legalization --- src/codegen/loongarch.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/codegen/loongarch.zig b/src/codegen/loongarch.zig index 1f032a19d8322c6280bf7e45c8a595025a901488..7b32af1057769c1a675cb342da183976d9545b7f 100644 --- a/src/codegen/loongarch.zig +++ b/src/codegen/loongarch.zig @@ -26,6 +26,7 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { .soft_f32, .soft_f64, .soft_f80, + .soft_f128, }); } -- 2.54.0 From 43b16923a541e91881eac08b0443065815fe6156 Mon Sep 17 00:00:00 2001 From: xtex Date: Fri, 16 Oct 2026 09:46:05 +0800 Subject: [PATCH 08/12] loongarch: implement call conv resolve for c_longdouble --- src/codegen/loongarch/Select.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index 1fa209aabb92c34b1d8d5680aaa46c45011216ae..ff91a9ae62e676a8ad810b78ae7e5d1d24b863a0 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -7075,7 +7075,7 @@ pub const CallAbiIterator = struct { .signedness = .unsigned, .bits = zcu.errorSetBits(), } }, - .f16, .f32, .f64 => { + .f16, .f32, .f64, .c_longdouble => { const bits = ty.floatBits(isel.target); if (isel.canUseFprForFloat(bits)) { if (it.allocReg(.fpr)) |reg| { @@ -7090,7 +7090,6 @@ pub const CallAbiIterator = struct { continue :type_key .{ .int_type = .{ .signedness = .unsigned, .bits = bits } }; } }, - .c_longdouble => return isel.fail("CallAbiIterator.resolve({t})", .{simple_type}), else => return isel.fail("CallAbiIterator.resolve({t})", .{simple_type}), }, .struct_type => { -- 2.54.0 From 44b107863393eebb23283c1403726b57ed43598a Mon Sep 17 00:00:00 2001 From: xtex Date: Fri, 16 Oct 2026 15:58:54 +0800 Subject: [PATCH 09/12] loongarch: implement analysis for div_ceil[_optimized] --- src/codegen/loongarch/Select.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index ff91a9ae62e676a8ad810b78ae7e5d1d24b863a0..a84c36d1daf4129f8d483d7c0cd3e11791ea0596 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -2380,6 +2380,8 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { .div_trunc_optimized, .div_floor, .div_floor_optimized, + .div_ceil, + .div_ceil_optimized, .div_exact, .div_exact_optimized, .rem, -- 2.54.0 From 557a4cdc3af6cab7ba3390b1f057bb8f6f8cdcef Mon Sep 17 00:00:00 2001 From: xtex Date: Fri, 16 Oct 2026 15:59:19 +0800 Subject: [PATCH 10/12] loongarch: implement constant loads for some floats --- src/codegen/loongarch/Select.zig | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index a84c36d1daf4129f8d483d7c0cd3e11791ea0596..9dc81cacb9bc83b1f2ffd6fb23430785c9e2eba2 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -6540,7 +6540,28 @@ fn moveConstant(isel: *Select, dst: Value.Location, init_constant: Constant, ini } }, .enum_tag => |enum_tag| continue :constant_key .{ .int = ip.indexToKey(enum_tag.int).int }, - .float => return isel.fail("float unimplemented", .{}), + .float => |float| { + if (isel.canUseFprForFloat(ZigType.fromInterned(float.ty).floatBits(isel.target))) { + return isel.fail("float unimplemented", .{}); + } else { + switch (float.ty) { + .f16_type => continue :constant_key .{ .int = .{ + .ty = .u16_type, + .storage = .{ .u64 = @as(u16, @bitCast(float.storage.f16)) }, + } }, + .f32_type => continue :constant_key .{ .int = .{ + .ty = .u32_type, + .storage = .{ .u64 = @as(u32, @bitCast(float.storage.f32)) }, + } }, + .f64_type => continue :constant_key .{ .int = .{ + .ty = .u64_type, + .storage = .{ .u64 = @as(u64, @bitCast(float.storage.f64)) }, + } }, + .f80_type, .f128_type => {}, + else => unreachable, + } + } + }, .ptr => |ptr| { assert(offset == 0 and size == isel.gprSize()); const dst_ra: Register.Alias, const use_tmp_reg = select_tmp: { @@ -7390,7 +7411,7 @@ fn gprAlignment(isel: *Select) std.mem.Alignment { }; } -fn fprBits(isel: *Select) u7 { +fn targetFprBits(isel: *Select) u7 { const cpu = &isel.target.cpu; if (cpu.has(.loongarch, .d)) { return 64; @@ -7408,12 +7429,12 @@ fn vectorBits(isel: *Select) u7 { } else if (cpu.has(.loongarch, .lsx)) { return 128; } else { - return isel.fprBits(); + return isel.targetFprBits(); } } fn canUseFprForFloat(isel: *Select, bits: u16) bool { - return bits <= isel.fprBits(); + return bits <= isel.targetFprBits(); } fn typeOfField(isel: *Select, ty: ZigType, offset: u64) ?ZigType { -- 2.54.0 From 70648bc4b8440abd22161148afc41645e1ab3fd0 Mon Sep 17 00:00:00 2001 From: xtex Date: Sat, 17 Oct 2026 09:42:36 +0800 Subject: [PATCH 11/12] loongarch: use failUnimplemented for u128 min/max --- src/codegen/loongarch/Select.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index 9dc81cacb9bc83b1f2ffd6fb23430785c9e2eba2..696c9b75836c3ba2f9e454c53ce0871c7fdf3e22 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -4115,7 +4115,11 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) codegen.Error!void if (!ty.isRuntimeFloat()) { if (!ty.isAbiInt(zcu)) return isel.fail("bad {t} {f}", .{ air_tag, isel.fmtType(ty) }); const int_info = ty.intInfo(zcu); - if (int_info.bits > 64) return isel.fail("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }); + if (int_info.bits > 64) { + _ = res_vi.value.takeLocationMarkWritten(isel); + try isel.failUnimplemented("too big {t} {f}", .{ air_tag, isel.fmtType(ty) }); + break :unused; + } try res_vi.value.reextendToGarbage(isel); const res_reg = try res_vi.value.defRegMod(isel, .integer) orelse break :unused; -- 2.54.0 From e9ab04c89ae3079cf106d34571a825e76f46bd58 Mon Sep 17 00:00:00 2001 From: xtex Date: Sat, 17 Oct 2026 10:14:44 +0800 Subject: [PATCH 12/12] loongarch: is_non_null materialize only the required part Materialize the has_value part instead of picking a byte from the optional. This makes the zero_ext modifier only apply to the has_value part rather than to the whole optional. --- src/codegen/loongarch/Select.zig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/codegen/loongarch/Select.zig b/src/codegen/loongarch/Select.zig index 696c9b75836c3ba2f9e454c53ce0871c7fdf3e22..a764c0776a64e540299744722f9389bd57d6c23e 100644 --- a/src/codegen/loongarch/Select.zig +++ b/src/codegen/loongarch/Select.zig @@ -1300,7 +1300,6 @@ pub const Value = struct { /// Materializes a value fn mat(vi: Value.Index, isel: *Select, opts: MatOptions) codegen.Error!Mat { - // try vi.split(isel, true); const mat_size = @min(opts.size, @as(u32, @intCast(vi.size(isel) - opts.offset))); const loc_pref = if (opts.extension == .garbage) opts.pref @@ -4064,9 +4063,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) codegen.Error!void .{ 0, @as(u32, @intCast(payload_size)) }; const opt_vi = try isel.use(un_op); - const has_value_mat = try opt_vi.mat(isel, .{ - .offset = has_value_offset, - .size = has_value_size, + const has_value_vi = try opt_vi.partExact(isel, has_value_offset, has_value_size); + const has_value_mat = try has_value_vi.mat(isel, .{ .pref = .only_reg, .reg_mod = .integer, .extension = .zero_ext, -- 2.54.0