mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
Merge pull request #10446 from ziglang/stage2-x86_64-mov-mem-imm
stage2: more fixes to x86_64 codegen, mir and isel
This commit is contained in:
commit
06286b05a4
@ -329,7 +329,11 @@ pub fn generate(
|
||||
if (builtin.mode == .Debug and bin_file.options.module.?.comp.verbose_mir) {
|
||||
const w = std.io.getStdErr().writer();
|
||||
w.print("# Begin Function MIR: {s}:\n", .{module_fn.owner_decl.name}) catch {};
|
||||
const print = @import("./PrintMir.zig"){ .mir = mir };
|
||||
const PrintMir = @import("PrintMir.zig");
|
||||
const print = PrintMir{
|
||||
.mir = mir,
|
||||
.bin_file = bin_file,
|
||||
};
|
||||
print.printMir(w, function.mir_to_air_map, air) catch {}; // we don't care if the debug printing fails
|
||||
w.print("# End Function MIR: {s}\n\n", .{module_fn.owner_decl.name}) catch {};
|
||||
}
|
||||
@ -1642,11 +1646,11 @@ fn genBinMathOpMir(
|
||||
});
|
||||
},
|
||||
.immediate => |imm| {
|
||||
// TODO I am not quite sure why we need to set the size of the register here...
|
||||
const abi_size = dst_ty.abiSize(self.target.*);
|
||||
_ = try self.addInst(.{
|
||||
.tag = mir_tag,
|
||||
.ops = (Mir.Ops{
|
||||
.reg1 = registerAlias(dst_reg, 4),
|
||||
.reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)),
|
||||
}).encode(),
|
||||
.data = .{ .imm = @intCast(i32, imm) },
|
||||
});
|
||||
@ -1751,13 +1755,14 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
|
||||
});
|
||||
},
|
||||
.immediate => |imm| {
|
||||
// TODO take into account the type's ABI size when selecting the register alias
|
||||
// register, immediate
|
||||
if (imm <= math.maxInt(i32)) {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .imul_complex,
|
||||
.ops = (Mir.Ops{
|
||||
.reg1 = dst_reg,
|
||||
.reg2 = dst_reg,
|
||||
.reg1 = dst_reg.to32(),
|
||||
.reg2 = dst_reg.to32(),
|
||||
.flags = 0b10,
|
||||
}).encode(),
|
||||
.data = .{ .imm = @intCast(i32, imm) },
|
||||
@ -2147,7 +2152,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
|
||||
// This instruction supports only signed 32-bit immediates at most.
|
||||
const src_mcv = try self.limitImmediateType(bin_op.rhs, i32);
|
||||
|
||||
try self.genBinMathOpMir(.cmp, Type.initTag(.bool), dst_mcv, src_mcv);
|
||||
try self.genBinMathOpMir(.cmp, ty, dst_mcv, src_mcv);
|
||||
break :result switch (ty.isSignedInt()) {
|
||||
true => MCValue{ .compare_flags_signed = op },
|
||||
false => MCValue{ .compare_flags_unsigned = op },
|
||||
@ -2792,25 +2797,24 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
return self.fail("TODO implement set stack variable with large stack offset", .{});
|
||||
}
|
||||
switch (abi_size) {
|
||||
1 => {
|
||||
return self.fail("TODO implement set abi_size=1 stack variable with immediate", .{});
|
||||
},
|
||||
2 => {
|
||||
return self.fail("TODO implement set abi_size=2 stack variable with immediate", .{});
|
||||
},
|
||||
4 => {
|
||||
1, 2, 4 => {
|
||||
// We have a positive stack offset value but we want a twos complement negative
|
||||
// offset from rbp, which is at the top of the stack frame.
|
||||
// mov DWORD PTR [rbp+offset], immediate
|
||||
// mov [rbp+offset], immediate
|
||||
const payload = try self.addExtra(Mir.ImmPair{
|
||||
.dest_off = -@intCast(i32, adj_off),
|
||||
.operand = @bitCast(i32, @intCast(u32, x_big)),
|
||||
});
|
||||
_ = try self.addInst(.{
|
||||
.tag = .mov,
|
||||
.tag = .mov_mem_imm,
|
||||
.ops = (Mir.Ops{
|
||||
.reg1 = .rbp,
|
||||
.flags = 0b11,
|
||||
.flags = switch (abi_size) {
|
||||
1 => 0b00,
|
||||
2 => 0b01,
|
||||
4 => 0b10,
|
||||
else => unreachable,
|
||||
},
|
||||
}).encode(),
|
||||
.data = .{ .payload = payload },
|
||||
});
|
||||
@ -2828,10 +2832,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
.operand = @bitCast(i32, @truncate(u32, x_big >> 32)),
|
||||
});
|
||||
_ = try self.addInst(.{
|
||||
.tag = .mov,
|
||||
.tag = .mov_mem_imm,
|
||||
.ops = (Mir.Ops{
|
||||
.reg1 = .rbp,
|
||||
.flags = 0b11,
|
||||
.flags = 0b10,
|
||||
}).encode(),
|
||||
.data = .{ .payload = payload },
|
||||
});
|
||||
@ -2842,10 +2846,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
.operand = @bitCast(i32, @truncate(u32, x_big)),
|
||||
});
|
||||
_ = try self.addInst(.{
|
||||
.tag = .mov,
|
||||
.tag = .mov_mem_imm,
|
||||
.ops = (Mir.Ops{
|
||||
.reg1 = .rbp,
|
||||
.flags = 0b11,
|
||||
.flags = 0b10,
|
||||
}).encode(),
|
||||
.data = .{ .payload = payload },
|
||||
});
|
||||
@ -2954,12 +2958,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
|
||||
return;
|
||||
}
|
||||
if (x <= math.maxInt(i32)) {
|
||||
const abi_size = ty.abiSize(self.target.*);
|
||||
// Next best case: if we set the lower four bytes, the upper four will be zeroed.
|
||||
// TODO I am not quite sure why we need to set the size of the register here...
|
||||
_ = try self.addInst(.{
|
||||
.tag = .mov,
|
||||
.ops = (Mir.Ops{
|
||||
.reg1 = registerAlias(reg, 4),
|
||||
.reg1 = registerAlias(reg, @intCast(u32, abi_size)),
|
||||
}).encode(),
|
||||
.data = .{ .imm = @intCast(i32, x) },
|
||||
});
|
||||
@ -2985,9 +2989,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
|
||||
// We need the offset from RIP in a signed i32 twos complement.
|
||||
const payload = try self.addExtra(Mir.Imm64.encode(code_offset));
|
||||
_ = try self.addInst(.{
|
||||
.tag = .lea_rip,
|
||||
.tag = .lea,
|
||||
.ops = (Mir.Ops{
|
||||
.reg1 = reg,
|
||||
.flags = 0b01,
|
||||
}).encode(),
|
||||
.data = .{ .payload = payload },
|
||||
});
|
||||
@ -3011,10 +3016,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
|
||||
if (self.bin_file.options.pie) {
|
||||
// TODO we should flag up `x` as GOT symbol entry explicitly rather than as a hack.
|
||||
_ = try self.addInst(.{
|
||||
.tag = .lea_rip,
|
||||
.tag = .lea,
|
||||
.ops = (Mir.Ops{
|
||||
.reg1 = reg,
|
||||
.flags = 0b01,
|
||||
.flags = 0b10,
|
||||
}).encode(),
|
||||
.data = .{ .got_entry = @intCast(u32, x) },
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -38,13 +38,18 @@ pub const Inst = struct {
|
||||
/// 0b01 reg1, [reg2 + imm32]
|
||||
/// 0b01 reg1, [ds:imm32]
|
||||
/// 0b10 [reg1 + imm32], reg2
|
||||
/// 0b10 [reg1 + 0], imm32
|
||||
/// 0b11 [reg1 + imm32], imm32
|
||||
/// Notes:
|
||||
/// * If reg2 is `none` then it means Data field `imm` is used as the immediate.
|
||||
/// * When two imm32 values are required, Data field `payload` points at `ImmPair`.
|
||||
adc,
|
||||
|
||||
/// ops flags: form:
|
||||
/// 0b00 byte ptr [reg1 + imm32], imm8
|
||||
/// 0b01 word ptr [reg1 + imm32], imm16
|
||||
/// 0b10 dword ptr [reg1 + imm32], imm32
|
||||
/// 0b11 qword ptr [reg1 + imm32], imm32 (sign-extended to imm64)
|
||||
adc_mem_imm,
|
||||
|
||||
/// form: reg1, [reg2 + scale*rcx + imm32]
|
||||
/// ops flags scale
|
||||
/// 0b00 1
|
||||
@ -77,74 +82,95 @@ pub const Inst = struct {
|
||||
// The following instructions all have the same encoding as `adc`.
|
||||
|
||||
add,
|
||||
add_mem_imm,
|
||||
add_scale_src,
|
||||
add_scale_dst,
|
||||
add_scale_imm,
|
||||
sub,
|
||||
sub_mem_imm,
|
||||
sub_scale_src,
|
||||
sub_scale_dst,
|
||||
sub_scale_imm,
|
||||
xor,
|
||||
xor_mem_imm,
|
||||
xor_scale_src,
|
||||
xor_scale_dst,
|
||||
xor_scale_imm,
|
||||
@"and",
|
||||
and_mem_imm,
|
||||
and_scale_src,
|
||||
and_scale_dst,
|
||||
and_scale_imm,
|
||||
@"or",
|
||||
or_mem_imm,
|
||||
or_scale_src,
|
||||
or_scale_dst,
|
||||
or_scale_imm,
|
||||
rol,
|
||||
rol_mem_imm,
|
||||
rol_scale_src,
|
||||
rol_scale_dst,
|
||||
rol_scale_imm,
|
||||
ror,
|
||||
ror_mem_imm,
|
||||
ror_scale_src,
|
||||
ror_scale_dst,
|
||||
ror_scale_imm,
|
||||
rcl,
|
||||
rcl_mem_imm,
|
||||
rcl_scale_src,
|
||||
rcl_scale_dst,
|
||||
rcl_scale_imm,
|
||||
rcr,
|
||||
rcr_mem_imm,
|
||||
rcr_scale_src,
|
||||
rcr_scale_dst,
|
||||
rcr_scale_imm,
|
||||
shl,
|
||||
shl_mem_imm,
|
||||
shl_scale_src,
|
||||
shl_scale_dst,
|
||||
shl_scale_imm,
|
||||
sal,
|
||||
sal_mem_imm,
|
||||
sal_scale_src,
|
||||
sal_scale_dst,
|
||||
sal_scale_imm,
|
||||
shr,
|
||||
shr_mem_imm,
|
||||
shr_scale_src,
|
||||
shr_scale_dst,
|
||||
shr_scale_imm,
|
||||
sar,
|
||||
sar_mem_imm,
|
||||
sar_scale_src,
|
||||
sar_scale_dst,
|
||||
sar_scale_imm,
|
||||
sbb,
|
||||
sbb_mem_imm,
|
||||
sbb_scale_src,
|
||||
sbb_scale_dst,
|
||||
sbb_scale_imm,
|
||||
cmp,
|
||||
cmp_mem_imm,
|
||||
cmp_scale_src,
|
||||
cmp_scale_dst,
|
||||
cmp_scale_imm,
|
||||
mov,
|
||||
mov_mem_imm,
|
||||
mov_scale_src,
|
||||
mov_scale_dst,
|
||||
mov_scale_imm,
|
||||
|
||||
/// ops flags: form:
|
||||
/// 0b00 reg1, [reg2 + imm32]
|
||||
/// 0b00 reg1, [ds:imm32]
|
||||
/// 0b01 reg1, [rip + imm32]
|
||||
/// 0b10 reg1, [rip + reloc]
|
||||
/// Notes:
|
||||
/// * if flags are 0b10, `Data` contains `got_entry` for the linker to generate
|
||||
/// a valid relocation for.
|
||||
lea,
|
||||
lea_scale_src,
|
||||
lea_scale_dst,
|
||||
lea_scale_imm,
|
||||
|
||||
/// ops flags: form:
|
||||
/// 0bX0 reg1
|
||||
@ -160,15 +186,6 @@ pub const Inst = struct {
|
||||
/// 0b11 reg1, [reg2 + imm32], imm32
|
||||
imul_complex,
|
||||
|
||||
/// ops flags: form:
|
||||
/// 0bX0 reg1, [rip + imm32]
|
||||
/// 0bX1 reg1, [rip + reloc]
|
||||
/// Notes:
|
||||
/// * if flags are 0bX1, `Data` contains `got_entry` for linker to generate
|
||||
/// valid relocation for.
|
||||
/// TODO handle more cases
|
||||
lea_rip,
|
||||
|
||||
/// ops flags: form:
|
||||
/// 0bX0 reg1, imm64
|
||||
/// 0bX1 rax, moffs64
|
||||
@ -233,16 +250,8 @@ pub const Inst = struct {
|
||||
syscall,
|
||||
|
||||
/// ops flags: form:
|
||||
/// 0b00 reg1, reg2
|
||||
/// 0b00 reg1, imm32
|
||||
/// 0b01 reg1, [reg2 + imm32]
|
||||
/// 0b01 reg1, [ds:imm32]
|
||||
/// 0b10 [reg1 + imm32], reg2
|
||||
/// 0b10 [reg1 + 0], imm32
|
||||
/// 0b11 [reg1 + imm32], imm32
|
||||
/// Notes:
|
||||
/// * If reg2 is `none` then it means Data field `imm` is used as the immediate.
|
||||
/// * When two imm32 values are required, Data field `payload` points at `ImmPair`.
|
||||
/// TODO handle more cases
|
||||
@"test",
|
||||
|
||||
/// Breakpoint
|
||||
|
||||
@ -25,6 +25,7 @@ const Type = @import("../../type.zig").Type;
|
||||
const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
|
||||
|
||||
mir: Mir,
|
||||
bin_file: *link.File,
|
||||
|
||||
pub fn printMir(print: *const Print, w: anytype, mir_to_air_map: std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index), air: Air) !void {
|
||||
const instruction_bytes = print.mir.instructions.len *
|
||||
@ -64,6 +65,15 @@ pub fn printMir(print: *const Print, w: anytype, mir_to_air_map: std.AutoHashMap
|
||||
.sbb => try print.mirArith(.sbb, inst, w),
|
||||
.cmp => try print.mirArith(.cmp, inst, w),
|
||||
|
||||
.adc_mem_imm => try print.mirArithMemImm(.adc, inst, w),
|
||||
.add_mem_imm => try print.mirArithMemImm(.add, inst, w),
|
||||
.sub_mem_imm => try print.mirArithMemImm(.sub, inst, w),
|
||||
.xor_mem_imm => try print.mirArithMemImm(.xor, inst, w),
|
||||
.and_mem_imm => try print.mirArithMemImm(.@"and", inst, w),
|
||||
.or_mem_imm => try print.mirArithMemImm(.@"or", inst, w),
|
||||
.sbb_mem_imm => try print.mirArithMemImm(.sbb, inst, w),
|
||||
.cmp_mem_imm => try print.mirArithMemImm(.cmp, inst, w),
|
||||
|
||||
.adc_scale_src => try print.mirArithScaleSrc(.adc, inst, w),
|
||||
.add_scale_src => try print.mirArithScaleSrc(.add, inst, w),
|
||||
.sub_scale_src => try print.mirArithScaleSrc(.sub, inst, w),
|
||||
@ -98,7 +108,6 @@ pub fn printMir(print: *const Print, w: anytype, mir_to_air_map: std.AutoHashMap
|
||||
.movabs => try print.mirMovabs(inst, w),
|
||||
|
||||
.lea => try print.mirLea(inst, w),
|
||||
.lea_rip => try print.mirLeaRip(inst, w),
|
||||
|
||||
.imul_complex => try print.mirIMulComplex(inst, w),
|
||||
|
||||
@ -108,15 +117,15 @@ pub fn printMir(print: *const Print, w: anytype, mir_to_air_map: std.AutoHashMap
|
||||
.jmp => try print.mirJmpCall(.jmp, inst, w),
|
||||
.call => try print.mirJmpCall(.call, inst, w),
|
||||
|
||||
// .cond_jmp_greater_less => try print.mirCondJmp(.cond_jmp_greater_less, inst, w),
|
||||
// .cond_jmp_above_below => try print.mirCondJmp(.cond_jmp_above_below, inst, w),
|
||||
// .cond_jmp_eq_ne => try print.mirCondJmp(.cond_jmp_eq_ne, inst, w),
|
||||
.cond_jmp_greater_less => try print.mirCondJmp(.cond_jmp_greater_less, inst, w),
|
||||
.cond_jmp_above_below => try print.mirCondJmp(.cond_jmp_above_below, inst, w),
|
||||
.cond_jmp_eq_ne => try print.mirCondJmp(.cond_jmp_eq_ne, inst, w),
|
||||
|
||||
// .cond_set_byte_greater_less => try print.mirCondSetByte(.cond_set_byte_greater_less, inst, w),
|
||||
// .cond_set_byte_above_below => try print.mirCondSetByte(.cond_set_byte_above_below, inst, w),
|
||||
// .cond_set_byte_eq_ne => try print.mirCondSetByte(.cond_set_byte_eq_ne, inst, w),
|
||||
.cond_set_byte_greater_less => try print.mirCondSetByte(.cond_set_byte_greater_less, inst, w),
|
||||
.cond_set_byte_above_below => try print.mirCondSetByte(.cond_set_byte_above_below, inst, w),
|
||||
.cond_set_byte_eq_ne => try print.mirCondSetByte(.cond_set_byte_eq_ne, inst, w),
|
||||
|
||||
// .@"test" => try print.mirTest(inst, w),
|
||||
.@"test" => try print.mirTest(inst, w),
|
||||
|
||||
.brk => try w.writeAll("brk\n"),
|
||||
.ret => try w.writeAll("ret\n"),
|
||||
@ -201,254 +210,24 @@ fn mirJmpCall(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: a
|
||||
try w.print("{s}\n", .{@tagName(ops.reg1)});
|
||||
}
|
||||
|
||||
const CondType = enum {
|
||||
/// greater than or equal
|
||||
gte,
|
||||
|
||||
/// greater than
|
||||
gt,
|
||||
|
||||
/// less than
|
||||
lt,
|
||||
|
||||
/// less than or equal
|
||||
lte,
|
||||
|
||||
/// above or equal
|
||||
ae,
|
||||
|
||||
/// above
|
||||
a,
|
||||
|
||||
/// below
|
||||
b,
|
||||
|
||||
/// below or equal
|
||||
be,
|
||||
|
||||
/// not equal
|
||||
ne,
|
||||
|
||||
/// equal
|
||||
eq,
|
||||
|
||||
fn fromTagAndFlags(tag: Mir.Inst.Tag, flags: u2) CondType {
|
||||
return switch (tag) {
|
||||
.cond_jmp_greater_less,
|
||||
.cond_set_byte_greater_less,
|
||||
=> switch (flags) {
|
||||
0b00 => CondType.gte,
|
||||
0b01 => CondType.gt,
|
||||
0b10 => CondType.lt,
|
||||
0b11 => CondType.lte,
|
||||
},
|
||||
.cond_jmp_above_below,
|
||||
.cond_set_byte_above_below,
|
||||
=> switch (flags) {
|
||||
0b00 => CondType.ae,
|
||||
0b01 => CondType.a,
|
||||
0b10 => CondType.b,
|
||||
0b11 => CondType.be,
|
||||
},
|
||||
.cond_jmp_eq_ne,
|
||||
.cond_set_byte_eq_ne,
|
||||
=> switch (@truncate(u1, flags)) {
|
||||
0b0 => CondType.ne,
|
||||
0b1 => CondType.eq,
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
inline fn getCondOpCode(tag: Mir.Inst.Tag, cond: CondType) u8 {
|
||||
switch (cond) {
|
||||
.gte => return switch (tag) {
|
||||
.cond_jmp_greater_less => 0x8d,
|
||||
.cond_set_byte_greater_less => 0x9d,
|
||||
else => unreachable,
|
||||
},
|
||||
.gt => return switch (tag) {
|
||||
.cond_jmp_greater_less => 0x8f,
|
||||
.cond_set_byte_greater_less => 0x9f,
|
||||
else => unreachable,
|
||||
},
|
||||
.lt => return switch (tag) {
|
||||
.cond_jmp_greater_less => 0x8c,
|
||||
.cond_set_byte_greater_less => 0x9c,
|
||||
else => unreachable,
|
||||
},
|
||||
.lte => return switch (tag) {
|
||||
.cond_jmp_greater_less => 0x8e,
|
||||
.cond_set_byte_greater_less => 0x9e,
|
||||
else => unreachable,
|
||||
},
|
||||
.ae => return switch (tag) {
|
||||
.cond_jmp_above_below => 0x83,
|
||||
.cond_set_byte_above_below => 0x93,
|
||||
else => unreachable,
|
||||
},
|
||||
.a => return switch (tag) {
|
||||
.cond_jmp_above_below => 0x87,
|
||||
.cond_set_byte_greater_less => 0x97,
|
||||
else => unreachable,
|
||||
},
|
||||
.b => return switch (tag) {
|
||||
.cond_jmp_above_below => 0x82,
|
||||
.cond_set_byte_greater_less => 0x92,
|
||||
else => unreachable,
|
||||
},
|
||||
.be => return switch (tag) {
|
||||
.cond_jmp_above_below => 0x86,
|
||||
.cond_set_byte_greater_less => 0x96,
|
||||
else => unreachable,
|
||||
},
|
||||
.eq => return switch (tag) {
|
||||
.cond_jmp_eq_ne => 0x84,
|
||||
.cond_set_byte_eq_ne => 0x94,
|
||||
else => unreachable,
|
||||
},
|
||||
.ne => return switch (tag) {
|
||||
.cond_jmp_eq_ne => 0x85,
|
||||
.cond_set_byte_eq_ne => 0x95,
|
||||
else => unreachable,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn mirCondJmp(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void {
|
||||
_ = w; // TODO
|
||||
const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]);
|
||||
const target = print.mir.instructions.items(.data)[inst].inst;
|
||||
const cond = CondType.fromTagAndFlags(tag, ops.flags);
|
||||
const opc = getCondOpCode(tag, cond);
|
||||
const source = print.code.items.len;
|
||||
const encoder = try Encoder.init(print.code, 6);
|
||||
encoder.opcode_2byte(0x0f, opc);
|
||||
try print.relocs.append(print.bin_file.allocator, .{
|
||||
.source = source,
|
||||
.target = target,
|
||||
.offset = print.code.items.len,
|
||||
.length = 6,
|
||||
});
|
||||
encoder.imm32(0);
|
||||
_ = print;
|
||||
_ = tag;
|
||||
_ = inst;
|
||||
try w.writeAll("TODO print mirCondJmp\n");
|
||||
}
|
||||
|
||||
fn mirCondSetByte(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void {
|
||||
_ = w; // TODO
|
||||
const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]);
|
||||
const cond = CondType.fromTagAndFlags(tag, ops.flags);
|
||||
const opc = getCondOpCode(tag, cond);
|
||||
const encoder = try Encoder.init(print.code, 4);
|
||||
encoder.rex(.{
|
||||
.w = true,
|
||||
.b = ops.reg1.isExtended(),
|
||||
});
|
||||
encoder.opcode_2byte(0x0f, opc);
|
||||
encoder.modRm_direct(0x0, ops.reg1.lowId());
|
||||
_ = tag;
|
||||
_ = inst;
|
||||
_ = print;
|
||||
try w.writeAll("TODO print mirCondSetByte\n");
|
||||
}
|
||||
|
||||
fn mirTest(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void {
|
||||
_ = w; // TODO
|
||||
const tag = print.mir.instructions.items(.tag)[inst];
|
||||
assert(tag == .@"test");
|
||||
const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]);
|
||||
switch (ops.flags) {
|
||||
0b00 => blk: {
|
||||
if (ops.reg2 == .none) {
|
||||
// TEST r/m64, imm32
|
||||
const imm = print.mir.instructions.items(.data)[inst].imm;
|
||||
if (ops.reg1.to64() == .rax) {
|
||||
// TODO reduce the size of the instruction if the immediate
|
||||
// is smaller than 32 bits
|
||||
const encoder = try Encoder.init(print.code, 6);
|
||||
encoder.rex(.{
|
||||
.w = true,
|
||||
});
|
||||
encoder.opcode_1byte(0xa9);
|
||||
encoder.imm32(imm);
|
||||
break :blk;
|
||||
}
|
||||
const opc: u8 = if (ops.reg1.size() == 8) 0xf6 else 0xf7;
|
||||
const encoder = try Encoder.init(print.code, 7);
|
||||
encoder.rex(.{
|
||||
.w = true,
|
||||
.b = ops.reg1.isExtended(),
|
||||
});
|
||||
encoder.opcode_1byte(opc);
|
||||
encoder.modRm_direct(0, ops.reg1.lowId());
|
||||
encoder.imm8(@intCast(i8, imm));
|
||||
break :blk;
|
||||
}
|
||||
// TEST r/m64, r64
|
||||
return print.fail("TODO TEST r/m64, r64", .{});
|
||||
},
|
||||
else => return print.fail("TODO more TEST alternatives", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
const EncType = enum {
|
||||
/// OP r/m64, imm32
|
||||
mi,
|
||||
|
||||
/// OP r/m64, r64
|
||||
mr,
|
||||
|
||||
/// OP r64, r/m64
|
||||
rm,
|
||||
};
|
||||
|
||||
const OpCode = struct {
|
||||
opc: u8,
|
||||
/// Only used if `EncType == .mi`.
|
||||
modrm_ext: u3,
|
||||
};
|
||||
|
||||
inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode {
|
||||
switch (enc) {
|
||||
.mi => return switch (tag) {
|
||||
.adc => .{ .opc = 0x81, .modrm_ext = 0x2 },
|
||||
.add => .{ .opc = 0x81, .modrm_ext = 0x0 },
|
||||
.sub => .{ .opc = 0x81, .modrm_ext = 0x5 },
|
||||
.xor => .{ .opc = 0x81, .modrm_ext = 0x6 },
|
||||
.@"and" => .{ .opc = 0x81, .modrm_ext = 0x4 },
|
||||
.@"or" => .{ .opc = 0x81, .modrm_ext = 0x1 },
|
||||
.sbb => .{ .opc = 0x81, .modrm_ext = 0x3 },
|
||||
.cmp => .{ .opc = 0x81, .modrm_ext = 0x7 },
|
||||
.mov => .{ .opc = 0xc7, .modrm_ext = 0x0 },
|
||||
else => unreachable,
|
||||
},
|
||||
.mr => {
|
||||
const opc: u8 = switch (tag) {
|
||||
.adc => 0x11,
|
||||
.add => 0x01,
|
||||
.sub => 0x29,
|
||||
.xor => 0x31,
|
||||
.@"and" => 0x21,
|
||||
.@"or" => 0x09,
|
||||
.sbb => 0x19,
|
||||
.cmp => 0x39,
|
||||
.mov => 0x89,
|
||||
else => unreachable,
|
||||
};
|
||||
return .{ .opc = opc, .modrm_ext = undefined };
|
||||
},
|
||||
.rm => {
|
||||
const opc: u8 = switch (tag) {
|
||||
.adc => 0x13,
|
||||
.add => 0x03,
|
||||
.sub => 0x2b,
|
||||
.xor => 0x33,
|
||||
.@"and" => 0x23,
|
||||
.@"or" => 0x0b,
|
||||
.sbb => 0x1b,
|
||||
.cmp => 0x3b,
|
||||
.mov => 0x8b,
|
||||
else => unreachable,
|
||||
};
|
||||
return .{ .opc = opc, .modrm_ext = undefined };
|
||||
},
|
||||
}
|
||||
_ = print;
|
||||
_ = inst;
|
||||
try w.writeAll("TODO print mirTest\n");
|
||||
}
|
||||
|
||||
fn mirArith(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void {
|
||||
@ -465,31 +244,63 @@ fn mirArith(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: any
|
||||
0b01 => {
|
||||
const imm = print.mir.instructions.items(.data)[inst].imm;
|
||||
if (ops.reg2 == .none) {
|
||||
try w.print("{s}, [ds:{d}]", .{ @tagName(ops.reg1), imm });
|
||||
try w.print("{s}, ", .{@tagName(ops.reg1)});
|
||||
switch (ops.reg1.size()) {
|
||||
8 => try w.print("byte ptr ", .{}),
|
||||
16 => try w.print("word ptr ", .{}),
|
||||
32 => try w.print("dword ptr ", .{}),
|
||||
64 => try w.print("qword ptr ", .{}),
|
||||
else => unreachable,
|
||||
}
|
||||
try w.print("[ds:{d}]", .{imm});
|
||||
} else {
|
||||
try w.print("{s}, [{s} + {d}]", .{ @tagName(ops.reg1), @tagName(ops.reg2), imm });
|
||||
try w.print("{s}, ", .{@tagName(ops.reg1)});
|
||||
switch (ops.reg1.size()) {
|
||||
8 => try w.print("byte ptr ", .{}),
|
||||
16 => try w.print("word ptr ", .{}),
|
||||
32 => try w.print("dword ptr ", .{}),
|
||||
64 => try w.print("qword ptr ", .{}),
|
||||
else => unreachable,
|
||||
}
|
||||
try w.print("[{s} + {d}]", .{ @tagName(ops.reg2), imm });
|
||||
}
|
||||
},
|
||||
0b10 => {
|
||||
const imm = print.mir.instructions.items(.data)[inst].imm;
|
||||
if (ops.reg2 == .none) {
|
||||
try w.print("[{s} + 0], {d}", .{ @tagName(ops.reg1), imm });
|
||||
try w.writeAll("unused variant");
|
||||
} else {
|
||||
switch (ops.reg2.size()) {
|
||||
8 => try w.print("byte ptr ", .{}),
|
||||
16 => try w.print("word ptr ", .{}),
|
||||
32 => try w.print("dword ptr ", .{}),
|
||||
64 => try w.print("qword ptr ", .{}),
|
||||
else => unreachable,
|
||||
}
|
||||
try w.print("[{s} + {d}], {s}", .{ @tagName(ops.reg1), imm, @tagName(ops.reg2) });
|
||||
}
|
||||
},
|
||||
0b11 => {
|
||||
if (ops.reg2 == .none) {
|
||||
const payload = print.mir.instructions.items(.data)[inst].payload;
|
||||
const imm_pair = print.mir.extraData(Mir.ImmPair, payload).data;
|
||||
try w.print("[{s} + {d}], {d}", .{ @tagName(ops.reg1), imm_pair.dest_off, imm_pair.operand });
|
||||
}
|
||||
try w.writeAll("TODO");
|
||||
try w.writeAll("unused variant");
|
||||
},
|
||||
}
|
||||
try w.writeByte('\n');
|
||||
}
|
||||
|
||||
fn mirArithMemImm(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void {
|
||||
const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]);
|
||||
const payload = print.mir.instructions.items(.data)[inst].payload;
|
||||
const imm_pair = print.mir.extraData(Mir.ImmPair, payload).data;
|
||||
try w.print("{s} ", .{@tagName(tag)});
|
||||
switch (ops.flags) {
|
||||
0b00 => try w.print("byte ptr ", .{}),
|
||||
0b01 => try w.print("word ptr ", .{}),
|
||||
0b10 => try w.print("dword ptr ", .{}),
|
||||
0b11 => try w.print("qword ptr ", .{}),
|
||||
}
|
||||
try w.print("[{s} + {d}], {d}\n", .{ @tagName(ops.reg1), imm_pair.dest_off, imm_pair.operand });
|
||||
}
|
||||
|
||||
fn mirArithScaleSrc(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void {
|
||||
const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]);
|
||||
const scale = ops.flags;
|
||||
@ -560,19 +371,57 @@ fn mirIMulComplex(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void {
|
||||
}
|
||||
|
||||
fn mirLea(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void {
|
||||
const tag = print.mir.instructions.items(.tag)[inst];
|
||||
assert(tag == .lea);
|
||||
const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]);
|
||||
assert(ops.flags == 0b01);
|
||||
const imm = print.mir.instructions.items(.data)[inst].imm;
|
||||
|
||||
try w.print("lea {s} [{s} + {d}]\n", .{ @tagName(ops.reg1), @tagName(ops.reg2), imm });
|
||||
}
|
||||
|
||||
fn mirLeaRip(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void {
|
||||
_ = print;
|
||||
_ = inst;
|
||||
return w.writeAll("TODO lea_rip\n");
|
||||
try w.writeAll("lea ");
|
||||
switch (ops.flags) {
|
||||
0b00 => {
|
||||
const imm = print.mir.instructions.items(.data)[inst].imm;
|
||||
try w.print("{s} [", .{@tagName(ops.reg1)});
|
||||
if (ops.reg2 != .none) {
|
||||
try w.print("{s} + ", .{@tagName(ops.reg2)});
|
||||
} else {
|
||||
try w.print("ds:", .{});
|
||||
}
|
||||
try w.print("{d}]\n", .{imm});
|
||||
},
|
||||
0b01 => {
|
||||
try w.print("{s}, ", .{@tagName(ops.reg1)});
|
||||
switch (ops.reg1.size()) {
|
||||
8 => try w.print("byte ptr ", .{}),
|
||||
16 => try w.print("word ptr ", .{}),
|
||||
32 => try w.print("dword ptr ", .{}),
|
||||
64 => try w.print("qword ptr ", .{}),
|
||||
else => unreachable,
|
||||
}
|
||||
try w.print("[rip + 0x0] ", .{});
|
||||
const payload = print.mir.instructions.items(.data)[inst].payload;
|
||||
const imm = print.mir.extraData(Mir.Imm64, payload).data.decode();
|
||||
try w.print("target@{x}", .{imm});
|
||||
},
|
||||
0b10 => {
|
||||
try w.print("{s}, ", .{@tagName(ops.reg1)});
|
||||
switch (ops.reg1.size()) {
|
||||
8 => try w.print("byte ptr ", .{}),
|
||||
16 => try w.print("word ptr ", .{}),
|
||||
32 => try w.print("dword ptr ", .{}),
|
||||
64 => try w.print("qword ptr ", .{}),
|
||||
else => unreachable,
|
||||
}
|
||||
try w.print("[rip + 0x0] ", .{});
|
||||
const got_entry = print.mir.instructions.items(.data)[inst].got_entry;
|
||||
if (print.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
const target = macho_file.locals.items[got_entry];
|
||||
const target_name = macho_file.getString(target.n_strx);
|
||||
try w.print("target@{s}", .{target_name});
|
||||
} else {
|
||||
try w.writeAll("TODO lea reg, [rip + reloc] for linking backends different than MachO");
|
||||
}
|
||||
},
|
||||
0b11 => {
|
||||
try w.writeAll("unused variant\n");
|
||||
},
|
||||
}
|
||||
try w.writeAll("\n");
|
||||
}
|
||||
|
||||
fn mirCallExtern(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void {
|
||||
|
||||
@ -1604,6 +1604,64 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
":2:28: error: cannot set address space of local variable 'foo'",
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("saving vars of different ABI size to stack", target);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub fn main() void {
|
||||
\\ assert(callMe(2) == 24);
|
||||
\\}
|
||||
\\
|
||||
\\fn callMe(a: u8) u8 {
|
||||
\\ var b: u8 = a + 10;
|
||||
\\ const c = 2 * b;
|
||||
\\ return c;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub fn main() void {
|
||||
\\ assert(callMe(2) == 24);
|
||||
\\}
|
||||
\\
|
||||
\\fn callMe(a: u16) u16 {
|
||||
\\ var b: u16 = a + 10;
|
||||
\\ const c = 2 * b;
|
||||
\\ return c;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub fn main() void {
|
||||
\\ assert(callMe(2) == 24);
|
||||
\\}
|
||||
\\
|
||||
\\fn callMe(a: u32) u32 {
|
||||
\\ var b: u32 = a + 10;
|
||||
\\ const c = 2 * b;
|
||||
\\ return c;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user