aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-07-17 02:14:18 +0200
committerGitHub <noreply@github.com>2025-07-17 02:14:18 +0200
commit680358767e30ac386e6727ffc9295820c598d6b9 (patch)
tree038619f013a1ec2e8bd1a5f409ca277048bb0b3c /src/codegen
parentd772c062720c96cc4e1c601814f422dfa1efb5a5 (diff)
parentaf084e537adbf571d3d1f69ef5ee3de7abd27ccd (diff)
downloadzig-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.zig43
-rw-r--r--src/codegen/llvm.zig327
-rw-r--r--src/codegen/spirv.zig18
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];