mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
x86_64: improve inline assembly support
* instruction prefixes * mnemonic fixes * labels * memory operands * read-write constraint modifier * register and memory alternative constraint
This commit is contained in:
parent
7436f3efc9
commit
b19fd485b1
@ -32,10 +32,10 @@ const Target = std.Target;
|
||||
const Type = @import("../../type.zig").Type;
|
||||
const TypedValue = @import("../../TypedValue.zig");
|
||||
const Value = @import("../../value.zig").Value;
|
||||
const Instruction = @import("encoder.zig").Instruction;
|
||||
|
||||
const abi = @import("abi.zig");
|
||||
const bits = @import("bits.zig");
|
||||
const encoder = @import("encoder.zig");
|
||||
const errUnionErrorOffset = codegen.errUnionErrorOffset;
|
||||
const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
|
||||
|
||||
@ -1275,6 +1275,17 @@ fn asmJccReloc(self: *Self, target: Mir.Inst.Index, cc: bits.Condition) !Mir.Ins
|
||||
});
|
||||
}
|
||||
|
||||
fn asmReloc(self: *Self, tag: Mir.Inst.FixedTag, target: Mir.Inst.Index) !void {
|
||||
_ = try self.addInst(.{
|
||||
.tag = tag[1],
|
||||
.ops = .inst,
|
||||
.data = .{ .inst = .{
|
||||
.fixes = tag[0],
|
||||
.inst = target,
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
fn asmPlaceholder(self: *Self) !Mir.Inst.Index {
|
||||
return self.addInst(.{
|
||||
.tag = .pseudo,
|
||||
@ -2174,7 +2185,7 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
|
||||
const ret_reg = param_regs[0];
|
||||
const enum_mcv = MCValue{ .register = param_regs[1] };
|
||||
|
||||
var exitlude_jump_relocs = try self.gpa.alloc(u32, enum_ty.enumFieldCount(mod));
|
||||
var exitlude_jump_relocs = try self.gpa.alloc(Mir.Inst.Index, enum_ty.enumFieldCount(mod));
|
||||
defer self.gpa.free(exitlude_jump_relocs);
|
||||
|
||||
const data_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
|
||||
@ -5887,7 +5898,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr
|
||||
|
||||
try self.genCopy(src_ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv);
|
||||
},
|
||||
.air_ref => |ptr_ref| try self.store(ptr_ty, ptr_mcv, try self.resolveInst(ptr_ref)),
|
||||
.air_ref => |ptr_ref| try self.store(ptr_ty, try self.resolveInst(ptr_ref), src_mcv),
|
||||
}
|
||||
}
|
||||
|
||||
@ -9313,7 +9324,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, .unreach, .{ operand, .none, .none });
|
||||
}
|
||||
|
||||
fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
|
||||
fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !Mir.Inst.Index {
|
||||
const mod = self.bin_file.options.module.?;
|
||||
const abi_size = ty.abiSize(mod);
|
||||
switch (mcv) {
|
||||
@ -9695,7 +9706,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const loop = self.air.extraData(Air.Block, ty_pl.payload);
|
||||
const body = self.air.extra[loop.end..][0..loop.data.body_len];
|
||||
const jmp_target: u32 = @intCast(self.mir_instructions.len);
|
||||
const jmp_target: Mir.Inst.Index = @intCast(self.mir_instructions.len);
|
||||
|
||||
self.scope_generation += 1;
|
||||
const state = try self.saveState();
|
||||
@ -9772,7 +9783,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
|
||||
extra_index = case.end + items.len + case_body.len;
|
||||
|
||||
var relocs = try self.gpa.alloc(u32, items.len);
|
||||
var relocs = try self.gpa.alloc(Mir.Inst.Index, items.len);
|
||||
defer self.gpa.free(relocs);
|
||||
|
||||
try self.spillEflagsIfOccupied();
|
||||
@ -9929,22 +9940,35 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
.none => self.typeOfIndex(inst),
|
||||
else => self.typeOf(output).childType(mod),
|
||||
};
|
||||
const arg_maybe_reg: ?Register = if (mem.eql(u8, constraint, "=r"))
|
||||
const is_read = switch (constraint[0]) {
|
||||
'=' => false,
|
||||
'+' => read: {
|
||||
if (output == .none) return self.fail(
|
||||
"read-write constraint unsupported for asm result: '{s}'",
|
||||
.{constraint},
|
||||
);
|
||||
break :read true;
|
||||
},
|
||||
else => return self.fail("invalid constraint: '{s}'", .{constraint}),
|
||||
};
|
||||
const arg_maybe_reg: ?Register = if (mem.eql(u8, constraint[1..], "r"))
|
||||
self.register_manager.tryAllocReg(maybe_inst, self.regClassForType(ty)) orelse
|
||||
return self.fail("ran out of registers lowering inline asm", .{})
|
||||
else if (mem.eql(u8, constraint, "=m"))
|
||||
else if (mem.eql(u8, constraint[1..], "m"))
|
||||
if (output != .none) null else return self.fail(
|
||||
"memory constraint unsupported for asm result",
|
||||
.{},
|
||||
"memory constraint unsupported for asm result: '{s}'",
|
||||
.{constraint},
|
||||
)
|
||||
else if (mem.eql(u8, constraint, "=g"))
|
||||
else if (mem.eql(u8, constraint[1..], "g") or
|
||||
mem.eql(u8, constraint[1..], "rm") or mem.eql(u8, constraint[1..], "mr") or
|
||||
mem.eql(u8, constraint[1..], "r,m") or mem.eql(u8, constraint[1..], "m,r"))
|
||||
self.register_manager.tryAllocReg(maybe_inst, self.regClassForType(ty)) orelse
|
||||
if (output != .none) null else return self.fail(
|
||||
"ran out of register lowering inline asm",
|
||||
.{},
|
||||
)
|
||||
else if (mem.startsWith(u8, constraint, "={") and mem.endsWith(u8, constraint, "}"))
|
||||
parseRegName(constraint["={".len .. constraint.len - "}".len]) orelse
|
||||
if (output != .none)
|
||||
null
|
||||
else
|
||||
return self.fail("ran out of registers lowering inline asm", .{})
|
||||
else if (mem.startsWith(u8, constraint[1..], "{") and mem.endsWith(u8, constraint[1..], "}"))
|
||||
parseRegName(constraint[1 + "{".len .. constraint.len - "}".len]) orelse
|
||||
return self.fail("invalid register constraint: '{s}'", .{constraint})
|
||||
else
|
||||
return self.fail("invalid constraint: '{s}'", .{constraint});
|
||||
@ -9965,6 +9989,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len));
|
||||
args.appendAssumeCapacity(arg_mcv);
|
||||
if (output == .none) result = arg_mcv;
|
||||
if (is_read) try self.load(arg_mcv, self.typeOf(output), .{ .air_ref = output });
|
||||
}
|
||||
|
||||
for (inputs) |input| {
|
||||
@ -9999,7 +10024,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
};
|
||||
try self.genSetReg(addr_reg, Type.usize, input_mcv.address());
|
||||
break :arg .{ .indirect = .{ .reg = addr_reg } };
|
||||
} else if (mem.eql(u8, constraint, "g")) arg: {
|
||||
} else if (mem.eql(u8, constraint, "g") or
|
||||
mem.eql(u8, constraint, "rm") or mem.eql(u8, constraint, "mr") or
|
||||
mem.eql(u8, constraint, "r,m") or mem.eql(u8, constraint, "m,r"))
|
||||
arg: {
|
||||
switch (input_mcv) {
|
||||
.register, .indirect, .load_frame => break :arg input_mcv,
|
||||
.memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_|
|
||||
@ -10038,44 +10066,141 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
}
|
||||
}
|
||||
|
||||
const Label = struct {
|
||||
target: Mir.Inst.Index = undefined,
|
||||
pending_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{},
|
||||
|
||||
const Kind = enum { definition, reference };
|
||||
|
||||
fn isValid(kind: Kind, name: []const u8) bool {
|
||||
for (name, 0..) |c, i| switch (c) {
|
||||
else => return false,
|
||||
'$' => if (i == 0) return false,
|
||||
'.' => {},
|
||||
'0'...'9' => if (i == 0) switch (kind) {
|
||||
.definition => if (name.len != 1) return false,
|
||||
.reference => {
|
||||
if (name.len != 2) return false;
|
||||
switch (name[1]) {
|
||||
else => return false,
|
||||
'B', 'F', 'b', 'f' => {},
|
||||
}
|
||||
},
|
||||
},
|
||||
'@', 'A'...'Z', '_', 'a'...'z' => {},
|
||||
};
|
||||
return name.len > 0;
|
||||
}
|
||||
};
|
||||
var labels: std.StringHashMapUnmanaged(Label) = .{};
|
||||
defer {
|
||||
var label_it = labels.valueIterator();
|
||||
while (label_it.next()) |label| label.pending_relocs.deinit(self.gpa);
|
||||
labels.deinit(self.gpa);
|
||||
}
|
||||
|
||||
const asm_source = mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
|
||||
var line_it = mem.tokenizeAny(u8, asm_source, "\n\r;");
|
||||
while (line_it.next()) |line| {
|
||||
next_line: while (line_it.next()) |line| {
|
||||
var mnem_it = mem.tokenizeAny(u8, line, " \t");
|
||||
const mnem_str = mnem_it.next() orelse continue;
|
||||
if (mem.startsWith(u8, mnem_str, "#")) continue;
|
||||
|
||||
const mnem_size: ?Memory.PtrSize = if (mem.endsWith(u8, mnem_str, "b"))
|
||||
.byte
|
||||
else if (mem.endsWith(u8, mnem_str, "w"))
|
||||
.word
|
||||
else if (mem.endsWith(u8, mnem_str, "l"))
|
||||
.dword
|
||||
else if (mem.endsWith(u8, mnem_str, "q"))
|
||||
.qword
|
||||
else
|
||||
null;
|
||||
const mnem_tag = Mir.Inst.FixedTag{ ._, mnem: {
|
||||
if (mnem_size) |_| {
|
||||
if (std.meta.stringToEnum(Mir.Inst.Tag, mnem_str[0 .. mnem_str.len - 1])) |mnem| {
|
||||
break :mnem mnem;
|
||||
}
|
||||
var prefix: Instruction.Prefix = .none;
|
||||
const mnem_str = while (mnem_it.next()) |mnem_str| {
|
||||
if (mem.startsWith(u8, mnem_str, "#")) continue :next_line;
|
||||
if (std.meta.stringToEnum(Instruction.Prefix, mnem_str)) |pre| {
|
||||
if (prefix != .none) return self.fail("extra prefix: '{s}'", .{mnem_str});
|
||||
prefix = pre;
|
||||
continue;
|
||||
}
|
||||
break :mnem std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse
|
||||
return self.fail("invalid mnemonic: '{s}'", .{mnem_str});
|
||||
} };
|
||||
if (!mem.endsWith(u8, mnem_str, ":")) break mnem_str;
|
||||
const label_name = mnem_str[0 .. mnem_str.len - ":".len];
|
||||
if (!Label.isValid(.definition, label_name))
|
||||
return self.fail("invalid label: '{s}'", .{label_name});
|
||||
const label_gop = try labels.getOrPut(self.gpa, label_name);
|
||||
if (!label_gop.found_existing) label_gop.value_ptr.* = .{} else {
|
||||
const anon = std.ascii.isDigit(label_name[0]);
|
||||
if (!anon and label_gop.value_ptr.pending_relocs.items.len == 0)
|
||||
return self.fail("redefined label: '{s}'", .{label_name});
|
||||
for (label_gop.value_ptr.pending_relocs.items) |pending_reloc|
|
||||
try self.performReloc(pending_reloc);
|
||||
if (anon)
|
||||
label_gop.value_ptr.pending_relocs.clearRetainingCapacity()
|
||||
else
|
||||
label_gop.value_ptr.pending_relocs.clearAndFree(self.gpa);
|
||||
}
|
||||
label_gop.value_ptr.target = @intCast(self.mir_instructions.len);
|
||||
} else continue;
|
||||
|
||||
var op_it = mem.tokenizeScalar(u8, mnem_it.rest(), ',');
|
||||
var ops = [1]encoder.Instruction.Operand{.none} ** 4;
|
||||
for (&ops) |*op| {
|
||||
const op_str = mem.trim(u8, op_it.next() orelse break, " \t");
|
||||
if (mem.startsWith(u8, op_str, "#")) break;
|
||||
var mnem_size: ?Memory.PtrSize = null;
|
||||
const mnem_tag = mnem: {
|
||||
mnem_size = if (mem.endsWith(u8, mnem_str, "b"))
|
||||
.byte
|
||||
else if (mem.endsWith(u8, mnem_str, "w"))
|
||||
.word
|
||||
else if (mem.endsWith(u8, mnem_str, "l"))
|
||||
.dword
|
||||
else if (mem.endsWith(u8, mnem_str, "q"))
|
||||
.qword
|
||||
else if (mem.endsWith(u8, mnem_str, "t"))
|
||||
.tbyte
|
||||
else
|
||||
break :mnem null;
|
||||
break :mnem std.meta.stringToEnum(Instruction.Mnemonic, mnem_str[0 .. mnem_str.len - 1]);
|
||||
} orelse mnem: {
|
||||
mnem_size = null;
|
||||
break :mnem std.meta.stringToEnum(Instruction.Mnemonic, mnem_str);
|
||||
} orelse return self.fail("invalid mnemonic: '{s}'", .{mnem_str});
|
||||
const mnem_name = @tagName(mnem_tag);
|
||||
const mnem_fixed_tag: Mir.Inst.FixedTag = for (std.enums.values(Mir.Inst.Fixes)) |fixes| {
|
||||
const fixes_name = @tagName(fixes);
|
||||
const space_i = mem.indexOfScalar(u8, fixes_name, ' ');
|
||||
const fixes_prefix = if (space_i) |i|
|
||||
std.meta.stringToEnum(Instruction.Prefix, fixes_name[0..i]).?
|
||||
else
|
||||
.none;
|
||||
if (fixes_prefix != prefix) continue;
|
||||
const pattern = fixes_name[if (space_i) |i| i + " ".len else 0..];
|
||||
const wildcard_i = mem.indexOfScalar(u8, pattern, '_').?;
|
||||
const mnem_prefix = pattern[0..wildcard_i];
|
||||
const mnem_suffix = pattern[wildcard_i + "_".len ..];
|
||||
if (!mem.startsWith(u8, mnem_name, mnem_prefix)) continue;
|
||||
if (!mem.endsWith(u8, mnem_name, mnem_suffix)) continue;
|
||||
break .{ fixes, std.meta.stringToEnum(
|
||||
Mir.Inst.Tag,
|
||||
mnem_name[mnem_prefix.len .. mnem_name.len - mnem_suffix.len],
|
||||
) orelse continue };
|
||||
} else {
|
||||
assert(prefix != .none);
|
||||
return self.fail("invalid prefix for mnemonic: '{s} {s}'", .{
|
||||
@tagName(prefix), mnem_str,
|
||||
});
|
||||
};
|
||||
|
||||
const Operand = union(enum) {
|
||||
none,
|
||||
reg: Register,
|
||||
mem: Memory,
|
||||
imm: Immediate,
|
||||
inst: Mir.Inst.Index,
|
||||
};
|
||||
var ops: [4]Operand = .{.none} ** 4;
|
||||
|
||||
var last_op = false;
|
||||
var op_it = mem.splitScalar(u8, mnem_it.rest(), ',');
|
||||
next_op: for (&ops) |*op| {
|
||||
const op_str = while (!last_op) {
|
||||
const full_str = op_it.next() orelse break :next_op;
|
||||
const trim_str = mem.trim(u8, if (mem.indexOfScalar(u8, full_str, '#')) |hash| hash: {
|
||||
last_op = true;
|
||||
break :hash full_str[0..hash];
|
||||
} else full_str, " \t");
|
||||
if (trim_str.len > 0) break trim_str;
|
||||
} else break;
|
||||
if (mem.startsWith(u8, op_str, "%%")) {
|
||||
const colon = mem.indexOfScalarPos(u8, op_str, "%%".len + 2, ':');
|
||||
const reg = parseRegName(op_str["%%".len .. colon orelse op_str.len]) orelse
|
||||
return self.fail("invalid register: '{s}'", .{op_str});
|
||||
if (colon) |colon_pos| {
|
||||
const disp = std.fmt.parseInt(i32, op_str[colon_pos + 1 ..], 0) catch
|
||||
const disp = std.fmt.parseInt(i32, op_str[colon_pos + ":".len ..], 0) catch
|
||||
return self.fail("invalid displacement: '{s}'", .{op_str});
|
||||
op.* = .{ .mem = Memory.sib(
|
||||
mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}),
|
||||
@ -10089,7 +10214,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
} else if (mem.startsWith(u8, op_str, "%[") and mem.endsWith(u8, op_str, "]")) {
|
||||
const colon = mem.indexOfScalarPos(u8, op_str, "%[".len, ':');
|
||||
const modifier = if (colon) |colon_pos|
|
||||
op_str[colon_pos + 1 .. op_str.len - "]".len]
|
||||
op_str[colon_pos + ":".len .. op_str.len - "]".len]
|
||||
else
|
||||
"";
|
||||
op.* = switch (args.items[
|
||||
@ -10140,64 +10265,113 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
}
|
||||
op.* = .{ .imm = Immediate.u(u) };
|
||||
} else |_| return self.fail("invalid immediate: '{s}'", .{op_str});
|
||||
} else if (mem.endsWith(u8, op_str, ")")) {
|
||||
const open = mem.indexOfScalar(u8, op_str, '(') orelse
|
||||
return self.fail("invalid operand: '{s}'", .{op_str});
|
||||
var sib_it = mem.splitScalar(u8, op_str[open + "(".len .. op_str.len - ")".len], ',');
|
||||
const base_str = sib_it.next() orelse
|
||||
return self.fail("invalid memory operand: '{s}'", .{op_str});
|
||||
if (base_str.len > 0 and !mem.startsWith(u8, base_str, "%%"))
|
||||
return self.fail("invalid memory operand: '{s}'", .{op_str});
|
||||
const index_str = sib_it.next() orelse "";
|
||||
if (index_str.len > 0 and !mem.startsWith(u8, base_str, "%%"))
|
||||
return self.fail("invalid memory operand: '{s}'", .{op_str});
|
||||
const scale_str = sib_it.next() orelse "";
|
||||
if (index_str.len == 0 and scale_str.len > 0)
|
||||
return self.fail("invalid memory operand: '{s}'", .{op_str});
|
||||
const scale = if (scale_str.len > 0) switch (std.fmt.parseInt(u4, scale_str, 10) catch
|
||||
return self.fail("invalid scale: '{s}'", .{op_str})) {
|
||||
1, 2, 4, 8 => |scale| scale,
|
||||
else => return self.fail("invalid scale: '{s}'", .{op_str}),
|
||||
} else 1;
|
||||
if (sib_it.next()) |_| return self.fail("invalid memory operand: '{s}'", .{op_str});
|
||||
op.* = .{ .mem = Memory.sib(mnem_size orelse
|
||||
return self.fail("unknown size: '{s}'", .{op_str}), .{
|
||||
.disp = if (open > 0) std.fmt.parseInt(i32, op_str[0..open], 0) catch
|
||||
return self.fail("invalid displacement: '{s}'", .{op_str}) else 0,
|
||||
.base = if (base_str.len > 0) .{ .reg = parseRegName(base_str["%%".len..]) orelse
|
||||
return self.fail("invalid base register: '{s}'", .{base_str}) } else .none,
|
||||
.scale_index = if (index_str.len > 0) .{
|
||||
.index = parseRegName(index_str["%%".len..]) orelse
|
||||
return self.fail("invalid index register: '{s}'", .{op_str}),
|
||||
.scale = scale,
|
||||
} else null,
|
||||
}) };
|
||||
} else if (Label.isValid(.reference, op_str)) {
|
||||
const anon = std.ascii.isDigit(op_str[0]);
|
||||
const label_gop = try labels.getOrPut(self.gpa, op_str[0..if (anon) 1 else op_str.len]);
|
||||
if (!label_gop.found_existing) label_gop.value_ptr.* = .{};
|
||||
if (anon and (op_str[1] == 'b' or op_str[1] == 'B') and !label_gop.found_existing)
|
||||
return self.fail("undefined label: '{s}'", .{op_str});
|
||||
const pending_relocs = &label_gop.value_ptr.pending_relocs;
|
||||
if (if (anon)
|
||||
op_str[1] == 'f' or op_str[1] == 'F'
|
||||
else
|
||||
!label_gop.found_existing or pending_relocs.items.len > 0)
|
||||
try pending_relocs.append(self.gpa, @intCast(self.mir_instructions.len));
|
||||
op.* = .{ .inst = label_gop.value_ptr.target };
|
||||
} else return self.fail("invalid operand: '{s}'", .{op_str});
|
||||
} else if (op_it.next()) |op_str| return self.fail("extra operand: '{s}'", .{op_str});
|
||||
|
||||
(switch (ops[0]) {
|
||||
.none => self.asmOpOnly(mnem_tag),
|
||||
.none => self.asmOpOnly(mnem_fixed_tag),
|
||||
.reg => |reg0| switch (ops[1]) {
|
||||
.none => self.asmRegister(mnem_tag, reg0),
|
||||
.none => self.asmRegister(mnem_fixed_tag, reg0),
|
||||
.reg => |reg1| switch (ops[2]) {
|
||||
.none => self.asmRegisterRegister(mnem_tag, reg1, reg0),
|
||||
.none => self.asmRegisterRegister(mnem_fixed_tag, reg1, reg0),
|
||||
.reg => |reg2| switch (ops[3]) {
|
||||
.none => self.asmRegisterRegisterRegister(mnem_tag, reg2, reg1, reg0),
|
||||
.none => self.asmRegisterRegisterRegister(mnem_fixed_tag, reg2, reg1, reg0),
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
.mem => |mem2| switch (ops[3]) {
|
||||
.none => self.asmMemoryRegisterRegister(mnem_tag, mem2, reg1, reg0),
|
||||
.none => self.asmMemoryRegisterRegister(mnem_fixed_tag, mem2, reg1, reg0),
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
.mem => |mem1| switch (ops[2]) {
|
||||
.none => self.asmMemoryRegister(mnem_tag, mem1, reg0),
|
||||
.none => self.asmMemoryRegister(mnem_fixed_tag, mem1, reg0),
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
.mem => |mem0| switch (ops[1]) {
|
||||
.none => self.asmMemory(mnem_tag, mem0),
|
||||
.none => self.asmMemory(mnem_fixed_tag, mem0),
|
||||
.reg => |reg1| switch (ops[2]) {
|
||||
.none => self.asmRegisterMemory(mnem_tag, reg1, mem0),
|
||||
.none => self.asmRegisterMemory(mnem_fixed_tag, reg1, mem0),
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
.imm => |imm0| switch (ops[1]) {
|
||||
.none => self.asmImmediate(mnem_tag, imm0),
|
||||
.none => self.asmImmediate(mnem_fixed_tag, imm0),
|
||||
.reg => |reg1| switch (ops[2]) {
|
||||
.none => self.asmRegisterImmediate(mnem_tag, reg1, imm0),
|
||||
.none => self.asmRegisterImmediate(mnem_fixed_tag, reg1, imm0),
|
||||
.reg => |reg2| switch (ops[3]) {
|
||||
.none => self.asmRegisterRegisterImmediate(mnem_tag, reg2, reg1, imm0),
|
||||
.none => self.asmRegisterRegisterImmediate(mnem_fixed_tag, reg2, reg1, imm0),
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
.mem => |mem2| switch (ops[3]) {
|
||||
.none => self.asmMemoryRegisterImmediate(mnem_tag, mem2, reg1, imm0),
|
||||
.none => self.asmMemoryRegisterImmediate(mnem_fixed_tag, mem2, reg1, imm0),
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
.mem => |mem1| switch (ops[2]) {
|
||||
.none => self.asmMemoryImmediate(mnem_tag, mem1, imm0),
|
||||
.none => self.asmMemoryImmediate(mnem_fixed_tag, mem1, imm0),
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
.inst => |inst0| switch (ops[1]) {
|
||||
.none => self.asmReloc(mnem_fixed_tag, inst0),
|
||||
else => error.InvalidInstruction,
|
||||
},
|
||||
}) catch |err| switch (err) {
|
||||
error.InvalidInstruction => return self.fail(
|
||||
"Invalid instruction: '{s} {s} {s} {s} {s}'",
|
||||
"invalid instruction: '{s} {s} {s} {s} {s}'",
|
||||
.{
|
||||
@tagName(mnem_tag[1]),
|
||||
mnem_str,
|
||||
@tagName(ops[0]),
|
||||
@tagName(ops[1]),
|
||||
@tagName(ops[2]),
|
||||
@ -10208,7 +10382,11 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
};
|
||||
}
|
||||
|
||||
for (outputs, args.items[0..outputs.len]) |output, mcv| {
|
||||
var label_it = labels.iterator();
|
||||
while (label_it.next()) |label| if (label.value_ptr.pending_relocs.items.len > 0)
|
||||
return self.fail("undefined label: '{s}'", .{label.key_ptr.*});
|
||||
|
||||
for (outputs, args.items[0..outputs.len]) |output, arg_mcv| {
|
||||
const extra_bytes = mem.sliceAsBytes(self.air.extra[outputs_extra_i..]);
|
||||
const constraint =
|
||||
mem.sliceTo(mem.sliceAsBytes(self.air.extra[outputs_extra_i..]), 0);
|
||||
@ -10218,8 +10396,8 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
outputs_extra_i += (constraint.len + name.len + (2 + 3)) / 4;
|
||||
|
||||
if (output == .none) continue;
|
||||
if (mcv != .register) continue;
|
||||
try self.store(self.typeOf(output), try self.resolveInst(output), mcv);
|
||||
if (arg_mcv != .register) continue;
|
||||
try self.store(self.typeOf(output), .{ .air_ref = output }, arg_mcv);
|
||||
}
|
||||
|
||||
simple: {
|
||||
@ -11510,7 +11688,7 @@ fn atomicOp(
|
||||
defer self.register_manager.unlockReg(tmp_lock);
|
||||
|
||||
try self.asmRegisterMemory(.{ ._, .mov }, registerAlias(.rax, val_abi_size), ptr_mem);
|
||||
const loop: u32 = @intCast(self.mir_instructions.len);
|
||||
const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len);
|
||||
if (rmw_op != std.builtin.AtomicRmwOp.Xchg) {
|
||||
try self.genSetReg(tmp_reg, val_ty, .{ .register = .rax });
|
||||
}
|
||||
@ -11584,7 +11762,7 @@ fn atomicOp(
|
||||
.scale_index = ptr_mem.scaleIndex(),
|
||||
.disp = ptr_mem.sib.disp + 8,
|
||||
}));
|
||||
const loop: u32 = @intCast(self.mir_instructions.len);
|
||||
const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len);
|
||||
const val_mem_mcv: MCValue = switch (val_mcv) {
|
||||
.memory, .indirect, .load_frame => val_mcv,
|
||||
else => .{ .indirect = .{
|
||||
|
||||
@ -42,7 +42,6 @@ test "module level assembly" {
|
||||
|
||||
test "output constraint modifiers" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
@ -66,7 +65,6 @@ test "output constraint modifiers" {
|
||||
|
||||
test "alternative constraints" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
@ -163,8 +161,8 @@ export fn derp() i32 {
|
||||
}
|
||||
|
||||
test "rw constraint (x86_64)" {
|
||||
if (builtin.target.cpu.arch != .x86_64 or builtin.zig_backend != .stage2_llvm)
|
||||
return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.target.cpu.arch != .x86_64) return error.SkipZigTest;
|
||||
|
||||
var res: i32 = 5;
|
||||
asm ("addl %[b], %[a]"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user