authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-04-02 20:36:41-04:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-03 17:45:16+02:00
logf5f0d95f0e882ce43146b269f994c6c242f9ed3b
tree05a6a5a6e7c75d6651765bba56c5b1f2843a1a67
parenta329450aa4107d376ebda753be8a5579dd600c69

x86_64: implement a more generic assembler for inline assembly


1 files changed, 135 insertions(+), 64 deletions(-)

src/arch/x86_64/CodeGen.zig+135-64
...@@ -28,10 +28,11 @@ const Type = @import("../../type.zig").Type;...@@ -28,10 +28,11 @@ const Type = @import("../../type.zig").Type;
28const TypedValue = @import("../../TypedValue.zig");28const TypedValue = @import("../../TypedValue.zig");
29const Value = @import("../../value.zig").Value;29const Value = @import("../../value.zig").Value;
3030
31const bits = @import("bits.zig");
32const abi = @import("abi.zig");31const abi = @import("abi.zig");
33const errUnionPayloadOffset = codegen.errUnionPayloadOffset;32const bits = @import("bits.zig");
33const encoder = @import("encoder.zig");
34const errUnionErrorOffset = codegen.errUnionErrorOffset;34const errUnionErrorOffset = codegen.errUnionErrorOffset;
35const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
3536
36const Condition = bits.Condition;37const Condition = bits.Condition;
37const Immediate = bits.Immediate;38const Immediate = bits.Immediate;
...@@ -6570,7 +6571,8 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -6570,7 +6571,8 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
6570 return self.fail("unrecognized constraint: '{s}'", .{constraint});6571 return self.fail("unrecognized constraint: '{s}'", .{constraint});
6571 args.putAssumeCapacity(name, mcv);6572 args.putAssumeCapacity(name, mcv);
6572 switch (mcv) {6573 switch (mcv) {
6573 .register => |reg| _ = self.register_manager.lockRegAssumeUnused(reg),6574 .register => |reg| _ = if (RegisterManager.indexOfRegIntoTracked(reg)) |_|
6575 self.register_manager.lockRegAssumeUnused(reg),
6574 else => {},6576 else => {},
6575 }6577 }
6576 if (output == .none) result = mcv;6578 if (output == .none) result = mcv;
...@@ -6609,70 +6611,139 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -6609,70 +6611,139 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
6609 }6611 }
66106612
6611 const asm_source = mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];6613 const asm_source = mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
6612 var line_it = mem.tokenize(u8, asm_source, "\n\r");6614 var line_it = mem.tokenize(u8, asm_source, "\n\r;");
6613 while (line_it.next()) |line| {6615 while (line_it.next()) |line| {
6614 var mnem_it = mem.tokenize(u8, line, " \t");6616 var mnem_it = mem.tokenize(u8, line, " \t");
6615 const mnem = mnem_it.next() orelse continue;6617 const mnem_str = mnem_it.next() orelse continue;
6616 if (mem.startsWith(u8, mnem, "#")) continue;6618 if (mem.startsWith(u8, mnem_str, "#")) continue;
6617 var arg_it = mem.tokenize(u8, mnem_it.rest(), ", ");6619
6618 if (std.ascii.eqlIgnoreCase(mnem, "syscall")) {6620 const mnem_size: ?Memory.PtrSize = if (mem.endsWith(u8, mnem_str, "b"))
6619 if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#"))6621 .byte
6620 return self.fail("Too many operands: '{s}'", .{line});6622 else if (mem.endsWith(u8, mnem_str, "w"))
6621 try self.asmOpOnly(.syscall);6623 .word
6622 } else if (std.ascii.eqlIgnoreCase(mnem, "push")) {6624 else if (mem.endsWith(u8, mnem_str, "l"))
6623 const src = arg_it.next() orelse6625 .dword
6624 return self.fail("Not enough operands: '{s}'", .{line});6626 else if (mem.endsWith(u8, mnem_str, "q"))
6625 if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#"))6627 .qword
6626 return self.fail("Too many operands: '{s}'", .{line});6628 else
6627 if (mem.startsWith(u8, src, "$")) {6629 null;
6628 const imm = std.fmt.parseInt(u32, src["$".len..], 0) catch6630 const mnem = std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse
6629 return self.fail("Invalid immediate: '{s}'", .{src});6631 (if (mnem_size) |_|
6630 try self.asmImmediate(.push, Immediate.u(imm));6632 std.meta.stringToEnum(Mir.Inst.Tag, mnem_str[0 .. mnem_str.len - 1])
6631 } else if (mem.startsWith(u8, src, "%%")) {6633 else
6632 const reg = parseRegName(src["%%".len..]) orelse6634 null) orelse return self.fail("Invalid mnemonic: '{s}'", .{mnem_str});
6633 return self.fail("Invalid register: '{s}'", .{src});6635
6634 try self.asmRegister(.push, reg);6636 var op_it = mem.tokenize(u8, mnem_it.rest(), ",");
6635 } else return self.fail("Unsupported operand: '{s}'", .{src});6637 var ops = [1]encoder.Instruction.Operand{.none} ** 4;
6636 } else if (std.ascii.eqlIgnoreCase(mnem, "pop")) {6638 for (&ops) |*op| {
6637 const dst = arg_it.next() orelse6639 const op_str = mem.trim(u8, op_it.next() orelse break, " \t");
6638 return self.fail("Not enough operands: '{s}'", .{line});6640 if (mem.startsWith(u8, op_str, "#")) break;
6639 if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#"))6641 if (mem.startsWith(u8, op_str, "%%")) {
6640 return self.fail("Too many operands: '{s}'", .{line});6642 const colon = mem.indexOfScalarPos(u8, op_str, "%%".len + 2, ':');
6641 if (mem.startsWith(u8, dst, "%%")) {6643 const reg = parseRegName(op_str["%%".len .. colon orelse op_str.len]) orelse
6642 const reg = parseRegName(dst["%%".len..]) orelse6644 return self.fail("Invalid register: '{s}'", .{op_str});
6643 return self.fail("Invalid register: '{s}'", .{dst});
6644 try self.asmRegister(.pop, reg);
6645 } else return self.fail("Unsupported operand: '{s}'", .{dst});
6646 } else if (std.ascii.eqlIgnoreCase(mnem, "movq")) {
6647 const src = arg_it.next() orelse
6648 return self.fail("Not enough operands: '{s}'", .{line});
6649 const dst = arg_it.next() orelse
6650 return self.fail("Not enough operands: '{s}'", .{line});
6651 if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#"))
6652 return self.fail("Too many operands: '{s}'", .{line});
6653 if (mem.startsWith(u8, src, "%%")) {
6654 const colon = mem.indexOfScalarPos(u8, src, "%%".len + 2, ':');
6655 const src_reg = parseRegName(src["%%".len .. colon orelse src.len]) orelse
6656 return self.fail("Invalid register: '{s}'", .{src});
6657 if (colon) |colon_pos| {6645 if (colon) |colon_pos| {
6658 const src_disp = std.fmt.parseInt(i32, src[colon_pos + 1 ..], 0) catch6646 const disp = std.fmt.parseInt(i32, op_str[colon_pos + 1 ..], 0) catch
6659 return self.fail("Invalid immediate: '{s}'", .{src});6647 return self.fail("Invalid displacement: '{s}'", .{op_str});
6660 if (mem.startsWith(u8, dst, "%[") and mem.endsWith(u8, dst, "]")) {6648 op.* = .{ .mem = Memory.sib(
6661 switch (args.get(dst["%[".len .. dst.len - "]".len]) orelse6649 mnem_size orelse return self.fail("Unknown size: '{s}'", .{op_str}),
6662 return self.fail("no matching constraint for: '{s}'", .{dst})) {6650 .{ .base = reg, .disp = disp },
6663 .register => |dst_reg| try self.asmRegisterMemory(6651 ) };
6664 .mov,6652 } else {
6665 dst_reg,6653 if (mnem_size) |size| if (reg.bitSize() != size.bitSize())
6666 Memory.sib(.qword, .{ .base = src_reg, .disp = src_disp }),6654 return self.fail("Invalid register size: '{s}'", .{op_str});
6667 ),6655 op.* = .{ .reg = reg };
6668 else => return self.fail("Invalid constraint: '{s}'", .{dst}),6656 }
6669 }6657 } else if (mem.startsWith(u8, op_str, "%[") and mem.endsWith(u8, op_str, "]")) {
6670 } else return self.fail("Unsupported operand: '{s}'", .{dst});6658 switch (args.get(op_str["%[".len .. op_str.len - "]".len]) orelse
6671 } else return self.fail("Unsupported operand: '{s}'", .{src});6659 return self.fail("No matching constraint: '{s}'", .{op_str})) {
6672 }6660 .register => |reg| op.* = .{ .reg = reg },
6673 } else {6661 else => return self.fail("Invalid constraint: '{s}'", .{op_str}),
6674 return self.fail("Unsupported instruction: '{s}'", .{mnem});6662 }
6675 }6663 } else if (mem.startsWith(u8, op_str, "$")) {
6664 if (std.fmt.parseInt(i32, op_str["$".len..], 0)) |s| {
6665 if (mnem_size) |size| {
6666 const max = @as(u64, std.math.maxInt(u64)) >>
6667 @intCast(u6, 64 - (size.bitSize() - 1));
6668 if ((if (s < 0) ~s else s) > max)
6669 return self.fail("Invalid immediate size: '{s}'", .{op_str});
6670 }
6671 op.* = .{ .imm = Immediate.s(s) };
6672 } else |_| if (std.fmt.parseInt(u64, op_str["$".len..], 0)) |u| {
6673 if (mnem_size) |size| {
6674 const max = @as(u64, std.math.maxInt(u64)) >>
6675 @intCast(u6, 64 - size.bitSize());
6676 if (u > max)
6677 return self.fail("Invalid immediate size: '{s}'", .{op_str});
6678 }
6679 op.* = .{ .imm = Immediate.u(u) };
6680 } else |_| return self.fail("Invalid immediate: '{s}'", .{op_str});
6681 } else return self.fail("Invalid operand: '{s}'", .{op_str});
6682 } else if (op_it.next()) |op_str| return self.fail("Extra operand: '{s}'", .{op_str});
6683
6684 (switch (ops[0]) {
6685 .none => self.asmOpOnly(mnem),
6686 .reg => |reg0| switch (ops[1]) {
6687 .none => self.asmRegister(mnem, reg0),
6688 .reg => |reg1| switch (ops[2]) {
6689 .none => self.asmRegisterRegister(mnem, reg1, reg0),
6690 .reg => |reg2| switch (ops[3]) {
6691 .none => self.asmRegisterRegisterRegister(mnem, reg2, reg1, reg0),
6692 else => error.InvalidInstruction,
6693 },
6694 .mem => |mem2| switch (ops[3]) {
6695 .none => self.asmMemoryRegisterRegister(mnem, mem2, reg1, reg0),
6696 else => error.InvalidInstruction,
6697 },
6698 else => error.InvalidInstruction,
6699 },
6700 .mem => |mem1| switch (ops[2]) {
6701 .none => self.asmMemoryRegister(mnem, mem1, reg0),
6702 else => error.InvalidInstruction,
6703 },
6704 else => error.InvalidInstruction,
6705 },
6706 .mem => |mem0| switch (ops[1]) {
6707 .none => self.asmMemory(mnem, mem0),
6708 .reg => |reg1| switch (ops[2]) {
6709 .none => self.asmRegisterMemory(mnem, reg1, mem0),
6710 else => error.InvalidInstruction,
6711 },
6712 else => error.InvalidInstruction,
6713 },
6714 .imm => |imm0| switch (ops[1]) {
6715 .none => self.asmImmediate(mnem, imm0),
6716 .reg => |reg1| switch (ops[2]) {
6717 .none => self.asmRegisterImmediate(mnem, reg1, imm0),
6718 .reg => |reg2| switch (ops[3]) {
6719 .none => self.asmRegisterRegisterImmediate(mnem, reg2, reg1, imm0),
6720 else => error.InvalidInstruction,
6721 },
6722 .mem => |mem2| switch (ops[3]) {
6723 .none => self.asmMemoryRegisterImmediate(mnem, mem2, reg1, imm0),
6724 else => error.InvalidInstruction,
6725 },
6726 else => error.InvalidInstruction,
6727 },
6728 .mem => |mem1| switch (ops[2]) {
6729 .none => self.asmMemoryImmediate(mnem, mem1, imm0),
6730 else => error.InvalidInstruction,
6731 },
6732 else => error.InvalidInstruction,
6733 },
6734 }) catch |err| switch (err) {
6735 error.InvalidInstruction => return self.fail(
6736 "Invalid instruction: '{s} {s} {s} {s} {s}'",
6737 .{
6738 @tagName(mnem),
6739 @tagName(ops[0]),
6740 @tagName(ops[1]),
6741 @tagName(ops[2]),
6742 @tagName(ops[3]),
6743 },
6744 ),
6745 else => |e| return e,
6746 };
6676 }6747 }
6677 }6748 }
66786749