diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-07-17 02:14:18 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-17 02:14:18 +0200 |
| commit | 680358767e30ac386e6727ffc9295820c598d6b9 (patch) | |
| tree | 038619f013a1ec2e8bd1a5f409ca277048bb0b3c /src/codegen | |
| parent | d772c062720c96cc4e1c601814f422dfa1efb5a5 (diff) | |
| parent | af084e537adbf571d3d1f69ef5ee3de7abd27ccd (diff) | |
| download | zig-680358767e30ac386e6727ffc9295820c598d6b9.tar.gz zig-680358767e30ac386e6727ffc9295820c598d6b9.zip | |
Merge pull request #24419 from ziglang/asm-clobbers
inline assembly: use types for clobbers
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/c.zig | 43 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 327 | ||||
| -rw-r--r-- | src/codegen/spirv.zig | 18 |
3 files changed, 324 insertions, 64 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 61bd5259ae..955a83bc33 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -5545,11 +5545,11 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const zcu = pt.zcu; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = f.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; - const clobbers_len: u31 = @truncate(extra.data.flags); + const is_volatile = extra.data.flags.is_volatile; + const outputs_len = extra.data.flags.outputs_len; const gpa = f.object.dg.gpa; var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra_i..][0..extra.data.outputs_len]); + const outputs: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra_i..][0..outputs_len]); extra_i += outputs.len; const inputs: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; @@ -5645,12 +5645,6 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try f.object.newline(); } } - for (0..clobbers_len) |_| { - const clobber = mem.sliceTo(mem.sliceAsBytes(f.air.extra.items[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - } { const asm_source = mem.sliceAsBytes(f.air.extra.items[extra_i..])[0..extra.data.source_len]; @@ -5757,17 +5751,28 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try w.writeByte(')'); } try w.writeByte(':'); - for (0..clobbers_len) |clobber_i| { - const clobber = mem.sliceTo(mem.sliceAsBytes(f.air.extra.items[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - if (clobber.len == 0) continue; - - if (clobber_i > 0) try w.writeByte(','); - try w.print(" {f}", .{fmtStringLiteral(clobber, null)}); + const ip = &zcu.intern_pool; + const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const struct_type: Type = .fromInterned(aggregate.ty); + switch (aggregate.storage) { + .elems => |elems| for (elems, 0..) |elem, i| switch (elem) { + .bool_true => { + const name = struct_type.structFieldName(i, zcu).toSlice(ip).?; + assert(name.len != 0); + try w.print(" {f}", .{fmtStringLiteral(name, null)}); + (try w.writableArray(1))[0] = ','; + }, + .bool_false => continue, + else => unreachable, + }, + .repeated_elem => |elem| switch (elem) { + .bool_true => @panic("TODO"), + .bool_false => {}, + else => unreachable, + }, + .bytes => @panic("TODO"), } + w.undo(1); // erase the last comma try w.writeAll(");"); try f.object.newline(); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e726a2bdd3..b3391cfb6a 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -7241,19 +7241,20 @@ pub const FuncGen = struct { const o = self.ng.object; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; - const clobbers_len: u31 = @truncate(extra.data.flags); + const is_volatile = extra.data.flags.is_volatile; + const outputs_len = extra.data.flags.outputs_len; + const gpa = self.gpa; var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.outputs_len]); + const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..outputs_len]); extra_i += outputs.len; const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; var llvm_constraints: std.ArrayListUnmanaged(u8) = .empty; - defer llvm_constraints.deinit(self.gpa); + defer llvm_constraints.deinit(gpa); - var arena_allocator = std.heap.ArenaAllocator.init(self.gpa); + var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -7276,7 +7277,7 @@ pub const FuncGen = struct { var llvm_ret_i: usize = 0; var llvm_param_i: usize = 0; - var total_i: u16 = 0; + var total_i: usize = 0; var name_map: std.StringArrayHashMapUnmanaged(u16) = .empty; try name_map.ensureUnusedCapacity(arena, max_param_count); @@ -7290,7 +7291,7 @@ pub const FuncGen = struct { // for the string, we still use the next u32 for the null terminator. extra_i += (constraint.len + name.len + (2 + 3)) / 4; - try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 3); + try llvm_constraints.ensureUnusedCapacity(gpa, constraint.len + 3); if (total_i != 0) { llvm_constraints.appendAssumeCapacity(','); } @@ -7358,7 +7359,7 @@ pub const FuncGen = struct { if (!std.mem.eql(u8, name, "_")) { const gop = name_map.getOrPutAssumeCapacity(name); if (gop.found_existing) return self.todo("duplicate asm output name '{s}'", .{name}); - gop.value_ptr.* = total_i; + gop.value_ptr.* = @intCast(total_i); } total_i += 1; } @@ -7399,7 +7400,7 @@ pub const FuncGen = struct { } } - try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); + try llvm_constraints.ensureUnusedCapacity(gpa, constraint.len + 1); if (total_i != 0) { llvm_constraints.appendAssumeCapacity(','); } @@ -7413,7 +7414,7 @@ pub const FuncGen = struct { if (!std.mem.eql(u8, name, "_")) { const gop = name_map.getOrPutAssumeCapacity(name); if (gop.found_existing) return self.todo("duplicate asm input name '{s}'", .{name}); - gop.value_ptr.* = total_i; + gop.value_ptr.* = @intCast(total_i); } // In the case of indirect inputs, LLVM requires the callsite to have @@ -7456,7 +7457,7 @@ pub const FuncGen = struct { llvm_param_types[llvm_param_i] = llvm_elem_ty; } - try llvm_constraints.print(self.gpa, ",{d}", .{output_index}); + try llvm_constraints.print(gpa, ",{d}", .{output_index}); // In the case of indirect inputs, LLVM requires the callsite to have // an elementtype(<ty>) attribute. @@ -7466,24 +7467,30 @@ pub const FuncGen = struct { total_i += 1; } - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4); - if (total_i != 0) { - llvm_constraints.appendAssumeCapacity(','); + const ip = &zcu.intern_pool; + const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const struct_type: Type = .fromInterned(aggregate.ty); + if (total_i != 0) try llvm_constraints.append(gpa, ','); + switch (aggregate.storage) { + .elems => |elems| for (elems, 0..) |elem, i| { + switch (elem) { + .bool_true => { + const name = struct_type.structFieldName(i, zcu).toSlice(ip).?; + total_i += try appendConstraints(gpa, &llvm_constraints, name, target); + }, + .bool_false => continue, + else => unreachable, } - llvm_constraints.appendSliceAssumeCapacity("~{"); - llvm_constraints.appendSliceAssumeCapacity(clobber); - llvm_constraints.appendSliceAssumeCapacity("}"); - - total_i += 1; - } + }, + .repeated_elem => |elem| switch (elem) { + .bool_true => for (0..struct_type.structFieldCount(zcu)) |i| { + const name = struct_type.structFieldName(i, zcu).toSlice(ip).?; + total_i += try appendConstraints(gpa, &llvm_constraints, name, target); + }, + .bool_false => {}, + else => unreachable, + }, + .bytes => @panic("TODO"), } // We have finished scanning through all inputs/outputs, so the number of @@ -7497,22 +7504,22 @@ pub const FuncGen = struct { // to be buggy and regress often. switch (target.cpu.arch) { .x86_64, .x86 => { - if (total_i != 0) try llvm_constraints.append(self.gpa, ','); - try llvm_constraints.appendSlice(self.gpa, "~{dirflag},~{fpsr},~{flags}"); + try llvm_constraints.appendSlice(gpa, "~{dirflag},~{fpsr},~{flags},"); total_i += 3; }, .mips, .mipsel, .mips64, .mips64el => { - if (total_i != 0) try llvm_constraints.append(self.gpa, ','); - try llvm_constraints.appendSlice(self.gpa, "~{$1}"); + try llvm_constraints.appendSlice(gpa, "~{$1},"); total_i += 1; }, else => {}, } + if (std.mem.endsWith(u8, llvm_constraints.items, ",")) llvm_constraints.items.len -= 1; + const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len]; // hackety hacks until stage2 has proper inline asm in the frontend. - var rendered_template = std.ArrayList(u8).init(self.gpa); + var rendered_template = std.ArrayList(u8).init(gpa); defer rendered_template.deinit(); const State = enum { start, percent, input, modifier }; @@ -13188,3 +13195,257 @@ fn maxIntConst(b: *Builder, max_ty: Type, as_ty: Builder.Type, zcu: *const Zcu) try res.setTwosCompIntLimit(.max, info.signedness, info.bits); return b.bigIntConst(as_ty, res.toConst()); } + +/// Appends zero or more LLVM constraints to `llvm_constraints`, returning how many were added. +fn appendConstraints( + gpa: Allocator, + llvm_constraints: *std.ArrayListUnmanaged(u8), + zig_name: []const u8, + target: *const std.Target, +) error{OutOfMemory}!usize { + switch (target.cpu.arch) { + .mips, .mipsel, .mips64, .mips64el => if (mips_clobber_overrides.get(zig_name)) |llvm_tag| { + const llvm_name = @tagName(llvm_tag); + try llvm_constraints.ensureUnusedCapacity(gpa, llvm_name.len + 4); + llvm_constraints.appendSliceAssumeCapacity("~{"); + llvm_constraints.appendSliceAssumeCapacity(llvm_name); + llvm_constraints.appendSliceAssumeCapacity("},"); + return 1; + }, + else => {}, + } + + try llvm_constraints.ensureUnusedCapacity(gpa, zig_name.len + 4); + llvm_constraints.appendSliceAssumeCapacity("~{"); + llvm_constraints.appendSliceAssumeCapacity(zig_name); + llvm_constraints.appendSliceAssumeCapacity("},"); + return 1; +} + +const mips_clobber_overrides = std.StaticStringMap(enum { + @"$msair", + @"$msacsr", + @"$msaaccess", + @"$msasave", + @"$msamodify", + @"$msarequest", + @"$msamap", + @"$msaunmap", + @"$f0", + @"$f1", + @"$f2", + @"$f3", + @"$f4", + @"$f5", + @"$f6", + @"$f7", + @"$f8", + @"$f9", + @"$f10", + @"$f11", + @"$f12", + @"$f13", + @"$f14", + @"$f15", + @"$f16", + @"$f17", + @"$f18", + @"$f19", + @"$f20", + @"$f21", + @"$f22", + @"$f23", + @"$f24", + @"$f25", + @"$f26", + @"$f27", + @"$f28", + @"$f29", + @"$f30", + @"$f31", + @"$fcc0", + @"$fcc1", + @"$fcc2", + @"$fcc3", + @"$fcc4", + @"$fcc5", + @"$fcc6", + @"$fcc7", + @"$w0", + @"$w1", + @"$w2", + @"$w3", + @"$w4", + @"$w5", + @"$w6", + @"$w7", + @"$w8", + @"$w9", + @"$w10", + @"$w11", + @"$w12", + @"$w13", + @"$w14", + @"$w15", + @"$w16", + @"$w17", + @"$w18", + @"$w19", + @"$w20", + @"$w21", + @"$w22", + @"$w23", + @"$w24", + @"$w25", + @"$w26", + @"$w27", + @"$w28", + @"$w29", + @"$w30", + @"$w31", + @"$0", + @"$1", + @"$2", + @"$3", + @"$4", + @"$5", + @"$6", + @"$7", + @"$8", + @"$9", + @"$10", + @"$11", + @"$12", + @"$13", + @"$14", + @"$15", + @"$16", + @"$17", + @"$18", + @"$19", + @"$20", + @"$21", + @"$22", + @"$23", + @"$24", + @"$25", + @"$26", + @"$27", + @"$28", + @"$29", + @"$30", + @"$31", +}).initComptime(.{ + .{ "msa_ir", .@"$msair" }, + .{ "msa_csr", .@"$msacsr" }, + .{ "msa_access", .@"$msaaccess" }, + .{ "msa_save", .@"$msasave" }, + .{ "msa_modify", .@"$msamodify" }, + .{ "msa_request", .@"$msarequest" }, + .{ "msa_map", .@"$msamap" }, + .{ "msa_unmap", .@"$msaunmap" }, + .{ "f0", .@"$f0" }, + .{ "f1", .@"$f1" }, + .{ "f2", .@"$f2" }, + .{ "f3", .@"$f3" }, + .{ "f4", .@"$f4" }, + .{ "f5", .@"$f5" }, + .{ "f6", .@"$f6" }, + .{ "f7", .@"$f7" }, + .{ "f8", .@"$f8" }, + .{ "f9", .@"$f9" }, + .{ "f10", .@"$f10" }, + .{ "f11", .@"$f11" }, + .{ "f12", .@"$f12" }, + .{ "f13", .@"$f13" }, + .{ "f14", .@"$f14" }, + .{ "f15", .@"$f15" }, + .{ "f16", .@"$f16" }, + .{ "f17", .@"$f17" }, + .{ "f18", .@"$f18" }, + .{ "f19", .@"$f19" }, + .{ "f20", .@"$f20" }, + .{ "f21", .@"$f21" }, + .{ "f22", .@"$f22" }, + .{ "f23", .@"$f23" }, + .{ "f24", .@"$f24" }, + .{ "f25", .@"$f25" }, + .{ "f26", .@"$f26" }, + .{ "f27", .@"$f27" }, + .{ "f28", .@"$f28" }, + .{ "f29", .@"$f29" }, + .{ "f30", .@"$f30" }, + .{ "f31", .@"$f31" }, + .{ "fcc0", .@"$fcc0" }, + .{ "fcc1", .@"$fcc1" }, + .{ "fcc2", .@"$fcc2" }, + .{ "fcc3", .@"$fcc3" }, + .{ "fcc4", .@"$fcc4" }, + .{ "fcc5", .@"$fcc5" }, + .{ "fcc6", .@"$fcc6" }, + .{ "fcc7", .@"$fcc7" }, + .{ "w0", .@"$w0" }, + .{ "w1", .@"$w1" }, + .{ "w2", .@"$w2" }, + .{ "w3", .@"$w3" }, + .{ "w4", .@"$w4" }, + .{ "w5", .@"$w5" }, + .{ "w6", .@"$w6" }, + .{ "w7", .@"$w7" }, + .{ "w8", .@"$w8" }, + .{ "w9", .@"$w9" }, + .{ "w10", .@"$w10" }, + .{ "w11", .@"$w11" }, + .{ "w12", .@"$w12" }, + .{ "w13", .@"$w13" }, + .{ "w14", .@"$w14" }, + .{ "w15", .@"$w15" }, + .{ "w16", .@"$w16" }, + .{ "w17", .@"$w17" }, + .{ "w18", .@"$w18" }, + .{ "w19", .@"$w19" }, + .{ "w20", .@"$w20" }, + .{ "w21", .@"$w21" }, + .{ "w22", .@"$w22" }, + .{ "w23", .@"$w23" }, + .{ "w24", .@"$w24" }, + .{ "w25", .@"$w25" }, + .{ "w26", .@"$w26" }, + .{ "w27", .@"$w27" }, + .{ "w28", .@"$w28" }, + .{ "w29", .@"$w29" }, + .{ "w30", .@"$w30" }, + .{ "w31", .@"$w31" }, + .{ "r0", .@"$0" }, + .{ "r1", .@"$1" }, + .{ "r2", .@"$2" }, + .{ "r3", .@"$3" }, + .{ "r4", .@"$4" }, + .{ "r5", .@"$5" }, + .{ "r6", .@"$6" }, + .{ "r7", .@"$7" }, + .{ "r8", .@"$8" }, + .{ "r9", .@"$9" }, + .{ "r10", .@"$10" }, + .{ "r11", .@"$11" }, + .{ "r12", .@"$12" }, + .{ "r13", .@"$13" }, + .{ "r14", .@"$14" }, + .{ "r15", .@"$15" }, + .{ "r16", .@"$16" }, + .{ "r17", .@"$17" }, + .{ "r18", .@"$18" }, + .{ "r19", .@"$19" }, + .{ "r20", .@"$20" }, + .{ "r21", .@"$21" }, + .{ "r22", .@"$22" }, + .{ "r23", .@"$23" }, + .{ "r24", .@"$24" }, + .{ "r25", .@"$25" }, + .{ "r26", .@"$26" }, + .{ "r27", .@"$27" }, + .{ "r28", .@"$28" }, + .{ "r29", .@"$29" }, + .{ "r30", .@"$30" }, + .{ "r31", .@"$31" }, +}); diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 255e957c8b..292f5a62fc 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -6387,13 +6387,13 @@ const NavGen = struct { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; - const clobbers_len: u31 = @truncate(extra.data.flags); + const is_volatile = extra.data.flags.is_volatile; + const outputs_len = extra.data.flags.outputs_len; if (!is_volatile and self.liveness.isUnused(inst)) return null; var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.outputs_len]); + const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..outputs_len]); extra_i += outputs.len; const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; @@ -6402,7 +6402,7 @@ const NavGen = struct { return self.todo("implement inline asm with more than 1 output", .{}); } - var as = SpvAssembler{ + var as: SpvAssembler = .{ .gpa = self.gpa, .spv = self.spv, .func = &self.func, @@ -6486,14 +6486,8 @@ const NavGen = struct { } } - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - extra_i += clobber.len / 4 + 1; - // TODO: Record clobber and use it somewhere. - } - } + // TODO: do something with clobbers + _ = extra.data.clobbers; const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len]; |
