inline assembly: use types

until now these were stringly typed.

it's kinda obvious when you think about it.
This commit is contained in:
Andrew Kelley
2025-07-11 22:03:00 -07:00
parent 6002514b72
commit fcafc63f3d
25 changed files with 1574 additions and 251 deletions

View File

@@ -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();

View File

@@ -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();
@@ -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(',');
}
@@ -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(',');
}
@@ -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,41 @@ 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;
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).?;
try llvm_constraints.ensureUnusedCapacity(gpa, name.len + 4);
if (total_i != 0) llvm_constraints.appendAssumeCapacity(',');
llvm_constraints.appendSliceAssumeCapacity("~{");
llvm_constraints.appendSliceAssumeCapacity(name);
llvm_constraints.appendSliceAssumeCapacity("}");
try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);
if (total_i != 0) {
llvm_constraints.appendAssumeCapacity(',');
total_i += 1;
},
.bool_false => continue,
else => unreachable,
}
llvm_constraints.appendSliceAssumeCapacity("~{");
llvm_constraints.appendSliceAssumeCapacity(clobber);
llvm_constraints.appendSliceAssumeCapacity("}");
},
.repeated_elem => |elem| switch (elem) {
.bool_true => for (0..struct_type.structFieldCount(zcu)) |i| {
const name = struct_type.structFieldName(i, zcu).toSlice(ip).?;
try llvm_constraints.ensureUnusedCapacity(gpa, name.len + 4);
if (total_i != 0) llvm_constraints.appendAssumeCapacity(',');
llvm_constraints.appendSliceAssumeCapacity("~{");
llvm_constraints.appendSliceAssumeCapacity(name);
llvm_constraints.appendSliceAssumeCapacity("}");
total_i += 1;
}
total_i += 1;
},
.bool_false => {},
else => unreachable,
},
.bytes => @panic("TODO"),
}
// We have finished scanning through all inputs/outputs, so the number of
@@ -7497,13 +7515,13 @@ 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}");
if (total_i != 0) try llvm_constraints.append(gpa, ',');
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}");
if (total_i != 0) try llvm_constraints.append(gpa, ',');
try llvm_constraints.appendSlice(gpa, "~{$1}");
total_i += 1;
},
else => {},
@@ -7512,7 +7530,7 @@ pub const FuncGen = struct {
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 };

View File

@@ -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];