diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2022-01-15 22:27:56 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-01-15 22:27:56 +0100 |
| commit | 3901b6fb000b909e90347f6910ea6315ee03e220 (patch) | |
| tree | 2ff3fac79d429e6442c755717905a7a79a38f910 /src | |
| parent | e23f7c01ee91321bd7845397d45f189a3e161806 (diff) | |
| parent | 3af4e28dda7e4537255516fd1d575c2a34d904e7 (diff) | |
| download | zig-3901b6fb000b909e90347f6910ea6315ee03e220.tar.gz zig-3901b6fb000b909e90347f6910ea6315ee03e220.zip | |
Merge pull request #10598 from ziglang/stage2-x86_64-imm-sizes
stage2: rename Isel back to Emit, and fix immediate casts in Emit for x86 64
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 102 | ||||
| -rw-r--r-- | src/arch/x86_64/Emit.zig (renamed from src/arch/x86_64/Isel.zig) | 996 | ||||
| -rw-r--r-- | src/arch/x86_64/Mir.zig | 6 |
3 files changed, 545 insertions, 559 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 9f4feb56e6..f4ecc001e8 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -17,7 +17,7 @@ const DW = std.dwarf; const ErrorMsg = Module.ErrorMsg; const FnResult = @import("../../codegen.zig").FnResult; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; -const Isel = @import("Isel.zig"); +const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); const Mir = @import("Mir.zig"); const Module = @import("../../Module.zig"); @@ -309,7 +309,7 @@ pub fn generate( }; defer mir.deinit(bin_file.allocator); - var isel = Isel{ + var emit = Emit{ .mir = mir, .bin_file = bin_file, .debug_output = debug_output, @@ -320,9 +320,9 @@ pub fn generate( .prev_di_line = module_fn.lbrace_line, .prev_di_column = module_fn.lbrace_column, }; - defer isel.deinit(); - isel.lowerMir() catch |err| switch (err) { - error.IselFail => return FnResult{ .fail = isel.err_msg.? }, + defer emit.deinit(); + emit.lowerMir() catch |err| switch (err) { + error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, else => |e| return e, }; @@ -497,14 +497,14 @@ fn gen(self: *Self) InnerError!void { .ops = (Mir.Ops{ .reg1 = .rsp, }).encode(), - .data = .{ .imm = @intCast(i32, aligned_stack_end) + stack_adjustment }, + .data = .{ .imm = @bitCast(u32, @intCast(i32, aligned_stack_end) + stack_adjustment) }, }); self.mir_instructions.set(backpatch_stack_add, .{ .tag = .add, .ops = (Mir.Ops{ .reg1 = .rsp, }).encode(), - .data = .{ .imm = @intCast(i32, aligned_stack_end) + stack_adjustment }, + .data = .{ .imm = @bitCast(u32, @intCast(i32, aligned_stack_end) + stack_adjustment) }, }); } } else { @@ -1347,7 +1347,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { .reg2 = .rbp, .flags = 0b01, }).encode(), - .data = .{ .imm = -@intCast(i32, off + 16) }, + .data = .{ .imm = @bitCast(u32, -@intCast(i32, off + 16)) }, }); // add addr, offset _ = try self.addInst(.{ @@ -1555,7 +1555,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); return self.genInlineMemcpy( - -@intCast(i32, off + abi_size), + @bitCast(u32, -@intCast(i32, off + abi_size)), registerAlias(addr_reg, @divExact(reg.size(), 8)), count_reg.to64(), tmp_reg.to8(), @@ -1637,7 +1637,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type // introduce new MIR tag specifically for mov [reg + 0], imm const payload = try self.addExtra(Mir.ImmPair{ .dest_off = 0, - .operand = @bitCast(i32, @intCast(u32, imm)), + .operand = @truncate(u32, imm), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -1821,11 +1821,11 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: const dst_ty = self.air.typeOfIndex(inst); const air_tags = self.air.instructions.items(.tag); switch (air_tags[inst]) { - .add, .addwrap => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv), - .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv), - .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv), - .sub, .subwrap => try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, src_mcv), - .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv), + .add, .addwrap => try self.genBinMathOpMir(.add, dst_ty, .unsigned, dst_mcv, src_mcv), + .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, .unsigned, dst_mcv, src_mcv), + .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, .unsigned, dst_mcv, src_mcv), + .sub, .subwrap => try self.genBinMathOpMir(.sub, dst_ty, .unsigned, dst_mcv, src_mcv), + .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, .unsigned, dst_mcv, src_mcv), .mul, .mulwrap => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv), else => unreachable, } @@ -1837,6 +1837,7 @@ fn genBinMathOpMir( self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, + signedness: std.builtin.Signedness, dst_mcv: MCValue, src_mcv: MCValue, ) !void { @@ -1856,11 +1857,16 @@ fn genBinMathOpMir( .ptr_stack_offset => unreachable, .ptr_embedded_in_code => unreachable, .register => |src_reg| { + // TODO think more carefully about this: is this actually correct? + const reg_size = if (mir_tag == .cmp and signedness == .signed) + @divExact(dst_reg.size(), 8) + else + @divExact(src_reg.size(), 8); _ = try self.addInst(.{ .tag = mir_tag, .ops = (Mir.Ops{ - .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)), - .reg2 = src_reg, + .reg1 = registerAlias(dst_reg, reg_size), + .reg2 = registerAlias(src_reg, reg_size), }).encode(), .data = undefined, }); @@ -1872,7 +1878,7 @@ fn genBinMathOpMir( .ops = (Mir.Ops{ .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), }).encode(), - .data = .{ .imm = @intCast(i32, imm) }, + .data = .{ .imm = @truncate(u32, imm) }, }); }, .embedded_in_code, .memory => { @@ -1891,7 +1897,7 @@ fn genBinMathOpMir( .reg2 = .rbp, .flags = 0b01, }).encode(), - .data = .{ .imm = -@intCast(i32, adj_off) }, + .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) }, }); }, .compare_flags_unsigned => { @@ -1926,7 +1932,7 @@ fn genBinMathOpMir( .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), .flags = 0b10, }).encode(), - .data = .{ .imm = -@intCast(i32, adj_off) }, + .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) }, }); }, .immediate => |imm| { @@ -1947,8 +1953,8 @@ fn genBinMathOpMir( else => unreachable, }; const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = -@intCast(i32, adj_off), - .operand = @bitCast(i32, @intCast(u32, imm)), + .dest_off = @bitCast(u32, -@intCast(i32, adj_off)), + .operand = @truncate(u32, imm), }); _ = try self.addInst(.{ .tag = tag, @@ -2015,7 +2021,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .reg2 = dst_reg.to32(), .flags = 0b10, }).encode(), - .data = .{ .imm = @intCast(i32, imm) }, + .data = .{ .imm = @truncate(u32, imm) }, }); } else { // TODO verify we don't spill and assign to the same register as dst_mcv @@ -2088,7 +2094,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { const mcv = self.args[arg_index]; const payload = try self.addExtra(Mir.ArgDbgInfo{ .air_inst = inst, - .arg_index = @intCast(u32, arg_index), // TODO can arg_index: u32? + .arg_index = @truncate(u32, arg_index), // TODO can arg_index: u32? }); _ = try self.addInst(.{ .tag = .arg_dbg_info, @@ -2196,7 +2202,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { .ops = (Mir.Ops{ .flags = 0b01, }).encode(), - .data = .{ .imm = @bitCast(i32, got_addr) }, + .data = .{ .imm = @truncate(u32, got_addr) }, }); } else if (func_value.castTag(.extern_fn)) |_| { return self.fail("TODO implement calling extern functions", .{}); @@ -2446,7 +2452,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, ty, dst_mcv, src_mcv); + try self.genBinMathOpMir(.cmp, ty, signedness, dst_mcv, src_mcv); break :result switch (signedness) { .signed => MCValue{ .compare_flags_signed = op }, .unsigned => MCValue{ .compare_flags_unsigned = op }, @@ -2669,7 +2675,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { } fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { - try self.genBinMathOpMir(.cmp, ty, operand, MCValue{ .immediate = 0 }); + try self.genBinMathOpMir(.cmp, ty, .unsigned, operand, MCValue{ .immediate = 0 }); return MCValue{ .compare_flags_unsigned = .eq }; } @@ -2686,7 +2692,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { return MCValue{ .immediate = 0 }; // always false } else if (!payload_type.hasCodeGenBits()) { if (err_type.abiSize(self.target.*) <= 8) { - try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); + try self.genBinMathOpMir(.cmp, err_type, .unsigned, operand, MCValue{ .immediate = 0 }); return MCValue{ .compare_flags_unsigned = .gt }; } else { return self.fail("TODO isErr for errors with size larger than register size", .{}); @@ -3121,8 +3127,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro // offset from rbp, which is at the top of the stack frame. // mov [rbp+offset], immediate const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = -@intCast(i32, adj_off), - .operand = @bitCast(i32, @intCast(u32, x_big)), + .dest_off = @bitCast(u32, -@intCast(i32, adj_off)), + .operand = @truncate(u32, x_big), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -3147,8 +3153,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro // insted just use two 32 bit writes to avoid register allocation { const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = negative_offset + 4, - .operand = @bitCast(i32, @truncate(u32, x_big >> 32)), + .dest_off = @bitCast(u32, negative_offset + 4), + .operand = @truncate(u32, x_big >> 32), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -3161,8 +3167,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro } { const payload = try self.addExtra(Mir.ImmPair{ - .dest_off = negative_offset, - .operand = @bitCast(i32, @truncate(u32, x_big)), + .dest_off = @bitCast(u32, negative_offset), + .operand = @truncate(u32, x_big), }); _ = try self.addInst(.{ .tag = .mov_mem_imm, @@ -3192,7 +3198,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro .reg2 = registerAlias(reg, @intCast(u32, abi_size)), .flags = 0b10, }).encode(), - .data = .{ .imm = -@intCast(i32, adj_off) }, + .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) }, }); }, .memory, .embedded_in_code => { @@ -3228,14 +3234,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro .reg1 = addr_reg.to64(), .reg2 = .rbp, }).encode(), - .data = .{ .imm = -@intCast(i32, off + abi_size) }, + .data = .{ .imm = @bitCast(u32, -@intCast(i32, off + abi_size)) }, }); // TODO allow for abi_size to be u64 try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) }); return self.genInlineMemcpy( - -@intCast(i32, stack_offset + abi_size), + @bitCast(u32, -@intCast(i32, stack_offset + abi_size)), addr_reg.to64(), count_reg.to64(), tmp_reg.to8(), @@ -3246,7 +3252,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro fn genInlineMemcpy( self: *Self, - stack_offset: i32, + stack_offset: u32, addr_reg: Register, count_reg: Register, tmp_reg: Register, @@ -3361,7 +3367,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg1 = registerAlias(reg, @intCast(u32, ptr_abi_size)), .reg2 = .rbp, }).encode(), - .data = .{ .imm = -@intCast(i32, off) }, + .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) }, }); }, .ptr_embedded_in_code => unreachable, @@ -3378,7 +3384,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void else => unreachable, } }, - .compare_flags_unsigned => |op| { + .compare_flags_unsigned, + .compare_flags_signed, + => |op| { const tag: Mir.Inst.Tag = switch (op) { .gte, .gt, .lt, .lte => .cond_set_byte_above_below, .eq, .neq => .cond_set_byte_eq_ne, @@ -3400,10 +3408,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = undefined, }); }, - .compare_flags_signed => |op| { - _ = op; - return self.fail("TODO set register with compare flags value (signed)", .{}); - }, .immediate => |x| { // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit // register is the fastest way to zero a register. @@ -3426,7 +3430,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .ops = (Mir.Ops{ .reg1 = registerAlias(reg, @intCast(u32, abi_size)), }).encode(), - .data = .{ .imm = @intCast(i32, x) }, + .data = .{ .imm = @truncate(u32, x) }, }); return; } @@ -3441,7 +3445,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void _ = try self.addInst(.{ .tag = .movabs, .ops = (Mir.Ops{ - .reg1 = reg, + .reg1 = reg.to64(), }).encode(), .data = .{ .payload = payload }, }); @@ -3482,7 +3486,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg1 = reg, .flags = 0b10, }).encode(), - .data = .{ .got_entry = @intCast(u32, x) }, + .data = .{ .got_entry = @truncate(u32, x) }, }); // MOV reg, [reg] _ = try self.addInst(.{ @@ -3502,7 +3506,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg1 = reg, .flags = 0b01, }).encode(), - .data = .{ .imm = @intCast(i32, x) }, + .data = .{ .imm = @truncate(u32, x) }, }); } else { // If this is RAX, we can use a direct load. @@ -3561,7 +3565,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg2 = .rbp, .flags = 0b01, }).encode(), - .data = .{ .imm = -@intCast(i32, off) }, + .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) }, }); }, } diff --git a/src/arch/x86_64/Isel.zig b/src/arch/x86_64/Emit.zig index acae794563..d192288dc4 100644 --- a/src/arch/x86_64/Isel.zig +++ b/src/arch/x86_64/Emit.zig @@ -1,7 +1,7 @@ //! This file contains the functionality for lowering x86_64 MIR into //! machine code -const Isel = @This(); +const Emit = @This(); const std = @import("std"); const assert = std.debug.assert; @@ -44,185 +44,183 @@ relocs: std.ArrayListUnmanaged(Reloc) = .{}, const InnerError = error{ OutOfMemory, - Overflow, - IselFail, + EmitFail, }; const Reloc = struct { /// Offset of the instruction. - source: u64, + source: usize, /// Target of the relocation. target: Mir.Inst.Index, /// Offset of the relocation within the instruction. - offset: u64, + offset: usize, /// Length of the instruction. length: u5, }; -pub fn lowerMir(isel: *Isel) InnerError!void { - const mir_tags = isel.mir.instructions.items(.tag); +pub fn lowerMir(emit: *Emit) InnerError!void { + const mir_tags = emit.mir.instructions.items(.tag); for (mir_tags) |tag, index| { const inst = @intCast(u32, index); - try isel.code_offset_mapping.putNoClobber(isel.bin_file.allocator, inst, isel.code.items.len); + try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len); switch (tag) { - .adc => try isel.mirArith(.adc, inst), - .add => try isel.mirArith(.add, inst), - .sub => try isel.mirArith(.sub, inst), - .xor => try isel.mirArith(.xor, inst), - .@"and" => try isel.mirArith(.@"and", inst), - .@"or" => try isel.mirArith(.@"or", inst), - .sbb => try isel.mirArith(.sbb, inst), - .cmp => try isel.mirArith(.cmp, inst), - .mov => try isel.mirArith(.mov, inst), - - .adc_mem_imm => try isel.mirArithMemImm(.adc, inst), - .add_mem_imm => try isel.mirArithMemImm(.add, inst), - .sub_mem_imm => try isel.mirArithMemImm(.sub, inst), - .xor_mem_imm => try isel.mirArithMemImm(.xor, inst), - .and_mem_imm => try isel.mirArithMemImm(.@"and", inst), - .or_mem_imm => try isel.mirArithMemImm(.@"or", inst), - .sbb_mem_imm => try isel.mirArithMemImm(.sbb, inst), - .cmp_mem_imm => try isel.mirArithMemImm(.cmp, inst), - .mov_mem_imm => try isel.mirArithMemImm(.mov, inst), - - .adc_scale_src => try isel.mirArithScaleSrc(.adc, inst), - .add_scale_src => try isel.mirArithScaleSrc(.add, inst), - .sub_scale_src => try isel.mirArithScaleSrc(.sub, inst), - .xor_scale_src => try isel.mirArithScaleSrc(.xor, inst), - .and_scale_src => try isel.mirArithScaleSrc(.@"and", inst), - .or_scale_src => try isel.mirArithScaleSrc(.@"or", inst), - .sbb_scale_src => try isel.mirArithScaleSrc(.sbb, inst), - .cmp_scale_src => try isel.mirArithScaleSrc(.cmp, inst), - .mov_scale_src => try isel.mirArithScaleSrc(.mov, inst), - - .adc_scale_dst => try isel.mirArithScaleDst(.adc, inst), - .add_scale_dst => try isel.mirArithScaleDst(.add, inst), - .sub_scale_dst => try isel.mirArithScaleDst(.sub, inst), - .xor_scale_dst => try isel.mirArithScaleDst(.xor, inst), - .and_scale_dst => try isel.mirArithScaleDst(.@"and", inst), - .or_scale_dst => try isel.mirArithScaleDst(.@"or", inst), - .sbb_scale_dst => try isel.mirArithScaleDst(.sbb, inst), - .cmp_scale_dst => try isel.mirArithScaleDst(.cmp, inst), - .mov_scale_dst => try isel.mirArithScaleDst(.mov, inst), - - .adc_scale_imm => try isel.mirArithScaleImm(.adc, inst), - .add_scale_imm => try isel.mirArithScaleImm(.add, inst), - .sub_scale_imm => try isel.mirArithScaleImm(.sub, inst), - .xor_scale_imm => try isel.mirArithScaleImm(.xor, inst), - .and_scale_imm => try isel.mirArithScaleImm(.@"and", inst), - .or_scale_imm => try isel.mirArithScaleImm(.@"or", inst), - .sbb_scale_imm => try isel.mirArithScaleImm(.sbb, inst), - .cmp_scale_imm => try isel.mirArithScaleImm(.cmp, inst), - .mov_scale_imm => try isel.mirArithScaleImm(.mov, inst), - - .movabs => try isel.mirMovabs(inst), - - .lea => try isel.mirLea(inst), - - .imul_complex => try isel.mirIMulComplex(inst), - - .push => try isel.mirPushPop(.push, inst), - .pop => try isel.mirPushPop(.pop, inst), - - .jmp => try isel.mirJmpCall(.jmp_near, inst), - .call => try isel.mirJmpCall(.call_near, inst), + .adc => try emit.mirArith(.adc, inst), + .add => try emit.mirArith(.add, inst), + .sub => try emit.mirArith(.sub, inst), + .xor => try emit.mirArith(.xor, inst), + .@"and" => try emit.mirArith(.@"and", inst), + .@"or" => try emit.mirArith(.@"or", inst), + .sbb => try emit.mirArith(.sbb, inst), + .cmp => try emit.mirArith(.cmp, inst), + .mov => try emit.mirArith(.mov, inst), + + .adc_mem_imm => try emit.mirArithMemImm(.adc, inst), + .add_mem_imm => try emit.mirArithMemImm(.add, inst), + .sub_mem_imm => try emit.mirArithMemImm(.sub, inst), + .xor_mem_imm => try emit.mirArithMemImm(.xor, inst), + .and_mem_imm => try emit.mirArithMemImm(.@"and", inst), + .or_mem_imm => try emit.mirArithMemImm(.@"or", inst), + .sbb_mem_imm => try emit.mirArithMemImm(.sbb, inst), + .cmp_mem_imm => try emit.mirArithMemImm(.cmp, inst), + .mov_mem_imm => try emit.mirArithMemImm(.mov, inst), + + .adc_scale_src => try emit.mirArithScaleSrc(.adc, inst), + .add_scale_src => try emit.mirArithScaleSrc(.add, inst), + .sub_scale_src => try emit.mirArithScaleSrc(.sub, inst), + .xor_scale_src => try emit.mirArithScaleSrc(.xor, inst), + .and_scale_src => try emit.mirArithScaleSrc(.@"and", inst), + .or_scale_src => try emit.mirArithScaleSrc(.@"or", inst), + .sbb_scale_src => try emit.mirArithScaleSrc(.sbb, inst), + .cmp_scale_src => try emit.mirArithScaleSrc(.cmp, inst), + .mov_scale_src => try emit.mirArithScaleSrc(.mov, inst), + + .adc_scale_dst => try emit.mirArithScaleDst(.adc, inst), + .add_scale_dst => try emit.mirArithScaleDst(.add, inst), + .sub_scale_dst => try emit.mirArithScaleDst(.sub, inst), + .xor_scale_dst => try emit.mirArithScaleDst(.xor, inst), + .and_scale_dst => try emit.mirArithScaleDst(.@"and", inst), + .or_scale_dst => try emit.mirArithScaleDst(.@"or", inst), + .sbb_scale_dst => try emit.mirArithScaleDst(.sbb, inst), + .cmp_scale_dst => try emit.mirArithScaleDst(.cmp, inst), + .mov_scale_dst => try emit.mirArithScaleDst(.mov, inst), + + .adc_scale_imm => try emit.mirArithScaleImm(.adc, inst), + .add_scale_imm => try emit.mirArithScaleImm(.add, inst), + .sub_scale_imm => try emit.mirArithScaleImm(.sub, inst), + .xor_scale_imm => try emit.mirArithScaleImm(.xor, inst), + .and_scale_imm => try emit.mirArithScaleImm(.@"and", inst), + .or_scale_imm => try emit.mirArithScaleImm(.@"or", inst), + .sbb_scale_imm => try emit.mirArithScaleImm(.sbb, inst), + .cmp_scale_imm => try emit.mirArithScaleImm(.cmp, inst), + .mov_scale_imm => try emit.mirArithScaleImm(.mov, inst), + + .movabs => try emit.mirMovabs(inst), + + .lea => try emit.mirLea(inst), + + .imul_complex => try emit.mirIMulComplex(inst), + + .push => try emit.mirPushPop(.push, inst), + .pop => try emit.mirPushPop(.pop, inst), + + .jmp => try emit.mirJmpCall(.jmp_near, inst), + .call => try emit.mirJmpCall(.call_near, inst), .cond_jmp_greater_less, .cond_jmp_above_below, .cond_jmp_eq_ne, - => try isel.mirCondJmp(tag, inst), + => try emit.mirCondJmp(tag, inst), .cond_set_byte_greater_less, .cond_set_byte_above_below, .cond_set_byte_eq_ne, - => try isel.mirCondSetByte(tag, inst), + => try emit.mirCondSetByte(tag, inst), - .ret => try isel.mirRet(inst), + .ret => try emit.mirRet(inst), - .syscall => try isel.mirSyscall(), + .syscall => try emit.mirSyscall(), - .@"test" => try isel.mirTest(inst), + .@"test" => try emit.mirTest(inst), - .brk => try isel.mirBrk(), - .nop => try isel.mirNop(), + .brk => try emit.mirBrk(), + .nop => try emit.mirNop(), - .call_extern => try isel.mirCallExtern(inst), + .call_extern => try emit.mirCallExtern(inst), - .dbg_line => try isel.mirDbgLine(inst), - .dbg_prologue_end => try isel.mirDbgPrologueEnd(inst), - .dbg_epilogue_begin => try isel.mirDbgEpilogueBegin(inst), - .arg_dbg_info => try isel.mirArgDbgInfo(inst), + .dbg_line => try emit.mirDbgLine(inst), + .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst), + .dbg_epilogue_begin => try emit.mirDbgEpilogueBegin(inst), + .arg_dbg_info => try emit.mirArgDbgInfo(inst), - .push_regs_from_callee_preserved_regs => try isel.mirPushPopRegsFromCalleePreservedRegs(.push, inst), - .pop_regs_from_callee_preserved_regs => try isel.mirPushPopRegsFromCalleePreservedRegs(.pop, inst), + .push_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.push, inst), + .pop_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.pop, inst), else => { - return isel.fail("Implement MIR->Isel lowering for x86_64 for pseudo-inst: {s}", .{tag}); + return emit.fail("Implement MIR->Emit lowering for x86_64 for pseudo-inst: {s}", .{tag}); }, } } - try isel.fixupRelocs(); + try emit.fixupRelocs(); } -pub fn deinit(isel: *Isel) void { - isel.relocs.deinit(isel.bin_file.allocator); - isel.code_offset_mapping.deinit(isel.bin_file.allocator); - isel.* = undefined; +pub fn deinit(emit: *Emit) void { + emit.relocs.deinit(emit.bin_file.allocator); + emit.code_offset_mapping.deinit(emit.bin_file.allocator); + emit.* = undefined; } -fn fail(isel: *Isel, comptime format: []const u8, args: anytype) InnerError { +fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { @setCold(true); - assert(isel.err_msg == null); - isel.err_msg = try ErrorMsg.create(isel.bin_file.allocator, isel.src_loc, format, args); - return error.IselFail; + assert(emit.err_msg == null); + emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); + return error.EmitFail; } -fn failWithLoweringError(isel: *Isel, err: LoweringError) InnerError { +fn failWithLoweringError(emit: *Emit, err: LoweringError) InnerError { return switch (err) { - error.RaxOperandExpected => isel.fail("Register.rax expected as destination operand", .{}), - error.OperandSizeMismatch => isel.fail("operand size mismatch", .{}), + error.RaxOperandExpected => emit.fail("Register.rax expected as destination operand", .{}), + error.OperandSizeMismatch => emit.fail("operand size mismatch", .{}), else => |e| e, }; } -fn fixupRelocs(isel: *Isel) InnerError!void { +fn fixupRelocs(emit: *Emit) InnerError!void { // TODO this function currently assumes all relocs via JMP/CALL instructions are 32bit in size. // This should be reversed like it is done in aarch64 MIR emit code: start with the smallest // possible resolution, i.e., 8bit, and iteratively converge on the minimum required resolution // until the entire decl is correctly emitted with all JMP/CALL instructions within range. - for (isel.relocs.items) |reloc| { - const offset = try math.cast(usize, reloc.offset); - const target = isel.code_offset_mapping.get(reloc.target) orelse - return isel.fail("JMP/CALL relocation target not found!", .{}); + for (emit.relocs.items) |reloc| { + const target = emit.code_offset_mapping.get(reloc.target) orelse + return emit.fail("JMP/CALL relocation target not found!", .{}); const disp = @intCast(i32, @intCast(i64, target) - @intCast(i64, reloc.source + reloc.length)); - mem.writeIntLittle(i32, isel.code.items[offset..][0..4], disp); + mem.writeIntLittle(i32, emit.code.items[reloc.offset..][0..4], disp); } } -fn mirBrk(isel: *Isel) InnerError!void { - return lowerToZoEnc(.brk, isel.code) catch |err| isel.failWithLoweringError(err); +fn mirBrk(emit: *Emit) InnerError!void { + return lowerToZoEnc(.brk, emit.code) catch |err| emit.failWithLoweringError(err); } -fn mirNop(isel: *Isel) InnerError!void { - return lowerToZoEnc(.nop, isel.code) catch |err| isel.failWithLoweringError(err); +fn mirNop(emit: *Emit) InnerError!void { + return lowerToZoEnc(.nop, emit.code) catch |err| emit.failWithLoweringError(err); } -fn mirSyscall(isel: *Isel) InnerError!void { - return lowerToZoEnc(.syscall, isel.code) catch |err| isel.failWithLoweringError(err); +fn mirSyscall(emit: *Emit) InnerError!void { + return lowerToZoEnc(.syscall, emit.code) catch |err| emit.failWithLoweringError(err); } -fn mirPushPop(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); +fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { // PUSH/POP reg - return lowerToOEnc(tag, ops.reg1, isel.code) catch |err| isel.failWithLoweringError(err); + return lowerToOEnc(tag, ops.reg1, emit.code) catch |err| emit.failWithLoweringError(err); }, 0b01 => { // PUSH/POP r/m64 - const imm = isel.mir.instructions.items(.data)[inst].imm; + const imm = emit.mir.instructions.items(.data)[inst].imm; const ptr_size: Memory.PtrSize = switch (immOpSize(imm)) { 16 => .word_ptr, else => .qword_ptr, @@ -230,26 +228,26 @@ fn mirPushPop(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void { return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm, .base = ops.reg1, - }), isel.code) catch |err| isel.failWithLoweringError(err); + }), emit.code) catch |err| emit.failWithLoweringError(err); }, 0b10 => { // PUSH imm32 assert(tag == .push); - const imm = isel.mir.instructions.items(.data)[inst].imm; - return lowerToIEnc(.push, imm, isel.code) catch |err| - isel.failWithLoweringError(err); + const imm = emit.mir.instructions.items(.data)[inst].imm; + return lowerToIEnc(.push, imm, emit.code) catch |err| + emit.failWithLoweringError(err); }, 0b11 => unreachable, } } -fn mirPushPopRegsFromCalleePreservedRegs(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const callee_preserved_regs = bits.callee_preserved_regs; - const regs = isel.mir.instructions.items(.data)[inst].regs_to_push_or_pop; + const regs = emit.mir.instructions.items(.data)[inst].regs_to_push_or_pop; if (tag == .push) { for (callee_preserved_regs) |reg, i| { if ((regs >> @intCast(u5, i)) & 1 == 0) continue; - lowerToOEnc(.push, reg, isel.code) catch |err| - return isel.failWithLoweringError(err); + lowerToOEnc(.push, reg, emit.code) catch |err| + return emit.failWithLoweringError(err); } } else { // pop in the reverse direction @@ -257,56 +255,56 @@ fn mirPushPopRegsFromCalleePreservedRegs(isel: *Isel, tag: Tag, inst: Mir.Inst.I while (i > 0) : (i -= 1) { const reg = callee_preserved_regs[i - 1]; if ((regs >> @intCast(u5, i - 1)) & 1 == 0) continue; - lowerToOEnc(.pop, reg, isel.code) catch |err| - return isel.failWithLoweringError(err); + lowerToOEnc(.pop, reg, emit.code) catch |err| + return emit.failWithLoweringError(err); } } } -fn mirJmpCall(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); +fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { - const target = isel.mir.instructions.items(.data)[inst].inst; - const source = isel.code.items.len; - lowerToDEnc(tag, 0, isel.code) catch |err| - return isel.failWithLoweringError(err); - try isel.relocs.append(isel.bin_file.allocator, .{ + const target = emit.mir.instructions.items(.data)[inst].inst; + const source = emit.code.items.len; + lowerToDEnc(tag, 0, emit.code) catch |err| + return emit.failWithLoweringError(err); + try emit.relocs.append(emit.bin_file.allocator, .{ .source = source, .target = target, - .offset = isel.code.items.len - 4, + .offset = emit.code.items.len - 4, .length = 5, }); }, 0b01 => { if (ops.reg1 == .none) { // JMP/CALL [imm] - const imm = isel.mir.instructions.items(.data)[inst].imm; + const imm = emit.mir.instructions.items(.data)[inst].imm; const ptr_size: Memory.PtrSize = switch (immOpSize(imm)) { 16 => .word_ptr, else => .qword_ptr, }; - return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), isel.code) catch |err| - isel.failWithLoweringError(err); + return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), emit.code) catch |err| + emit.failWithLoweringError(err); } // JMP/CALL reg - return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), isel.code) catch |err| isel.failWithLoweringError(err); + return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code) catch |err| emit.failWithLoweringError(err); }, 0b10 => { // JMP/CALL r/m64 - const imm = isel.mir.instructions.items(.data)[inst].imm; + const imm = emit.mir.instructions.items(.data)[inst].imm; return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ .disp = imm, .base = ops.reg1, - }), isel.code) catch |err| isel.failWithLoweringError(err); + }), emit.code) catch |err| emit.failWithLoweringError(err); }, - 0b11 => return isel.fail("TODO unused JMP/CALL variant 0b11", .{}), + 0b11 => return emit.fail("TODO unused JMP/CALL variant 0b11", .{}), } } -fn mirCondJmp(isel: *Isel, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); - const target = isel.mir.instructions.items(.data)[inst].inst; +fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const target = emit.mir.instructions.items(.data)[inst].inst; const tag = switch (mir_tag) { .cond_jmp_greater_less => switch (ops.flags) { 0b00 => Tag.jge, @@ -326,19 +324,19 @@ fn mirCondJmp(isel: *Isel, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerErr }, else => unreachable, }; - const source = isel.code.items.len; - lowerToDEnc(tag, 0, isel.code) catch |err| - return isel.failWithLoweringError(err); - try isel.relocs.append(isel.bin_file.allocator, .{ + const source = emit.code.items.len; + lowerToDEnc(tag, 0, emit.code) catch |err| + return emit.failWithLoweringError(err); + try emit.relocs.append(emit.bin_file.allocator, .{ .source = source, .target = target, - .offset = isel.code.items.len - 4, + .offset = emit.code.items.len - 4, .length = 6, }); } -fn mirCondSetByte(isel: *Isel, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); +fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); const tag = switch (mir_tag) { .cond_set_byte_greater_less => switch (ops.flags) { 0b00 => Tag.setge, @@ -358,111 +356,111 @@ fn mirCondSetByte(isel: *Isel, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne }, else => unreachable, }; - return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), isel.code) catch |err| - isel.failWithLoweringError(err); + return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code) catch |err| + emit.failWithLoweringError(err); } -fn mirTest(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { - const tag = isel.mir.instructions.items(.tag)[inst]; +fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .@"test"); - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { if (ops.reg2 == .none) { // TEST r/m64, imm32 // MI - const imm = isel.mir.instructions.items(.data)[inst].imm; + const imm = emit.mir.instructions.items(.data)[inst].imm; if (ops.reg1.to64() == .rax) { // TEST rax, imm32 // I - return lowerToIEnc(.@"test", imm, isel.code) catch |err| - isel.failWithLoweringError(err); + return lowerToIEnc(.@"test", imm, emit.code) catch |err| + emit.failWithLoweringError(err); } - return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, isel.code) catch |err| - isel.failWithLoweringError(err); + return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code) catch |err| + emit.failWithLoweringError(err); } // TEST r/m64, r64 - return isel.fail("TODO TEST r/m64, r64", .{}); + return emit.fail("TODO TEST r/m64, r64", .{}); }, - else => return isel.fail("TODO more TEST alternatives", .{}), + else => return emit.fail("TODO more TEST alternatives", .{}), } } -fn mirRet(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { - const tag = isel.mir.instructions.items(.tag)[inst]; +fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .ret); - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { // RETF imm16 // I - const imm = isel.mir.instructions.items(.data)[inst].imm; - return lowerToIEnc(.ret_far, imm, isel.code) catch |err| isel.failWithLoweringError(err); + const imm = emit.mir.instructions.items(.data)[inst].imm; + return lowerToIEnc(.ret_far, imm, emit.code) catch |err| emit.failWithLoweringError(err); }, 0b01 => { - return lowerToZoEnc(.ret_far, isel.code) catch |err| isel.failWithLoweringError(err); + return lowerToZoEnc(.ret_far, emit.code) catch |err| emit.failWithLoweringError(err); }, 0b10 => { // RET imm16 // I - const imm = isel.mir.instructions.items(.data)[inst].imm; - return lowerToIEnc(.ret_near, imm, isel.code) catch |err| isel.failWithLoweringError(err); + const imm = emit.mir.instructions.items(.data)[inst].imm; + return lowerToIEnc(.ret_near, imm, emit.code) catch |err| emit.failWithLoweringError(err); }, 0b11 => { - return lowerToZoEnc(.ret_near, isel.code) catch |err| isel.failWithLoweringError(err); + return lowerToZoEnc(.ret_near, emit.code) catch |err| emit.failWithLoweringError(err); }, } } -fn mirArith(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); +fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { if (ops.reg2 == .none) { // mov reg1, imm32 // MI - const imm = isel.mir.instructions.items(.data)[inst].imm; - return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, isel.code) catch |err| - isel.failWithLoweringError(err); + const imm = emit.mir.instructions.items(.data)[inst].imm; + return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code) catch |err| + emit.failWithLoweringError(err); } // mov reg1, reg2 // RM - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), isel.code) catch |err| - isel.failWithLoweringError(err); + return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code) catch |err| + emit.failWithLoweringError(err); }, 0b01 => { // mov reg1, [reg2 + imm32] // RM - const imm = isel.mir.instructions.items(.data)[inst].imm; + const imm = emit.mir.instructions.items(.data)[inst].imm; const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2; return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ .disp = imm, .base = src_reg, - }), isel.code) catch |err| isel.failWithLoweringError(err); + }), emit.code) catch |err| emit.failWithLoweringError(err); }, 0b10 => { if (ops.reg2 == .none) { - return isel.fail("TODO unused variant: mov reg1, none, 0b10", .{}); + return emit.fail("TODO unused variant: mov reg1, none, 0b10", .{}); } // mov [reg1 + imm32], reg2 // MR - const imm = isel.mir.instructions.items(.data)[inst].imm; + const imm = emit.mir.instructions.items(.data)[inst].imm; return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{ .disp = imm, .base = ops.reg1, - }), ops.reg2, isel.code) catch |err| isel.failWithLoweringError(err); + }), ops.reg2, emit.code) catch |err| emit.failWithLoweringError(err); }, 0b11 => { - return isel.fail("TODO unused variant: mov reg1, reg2, 0b11", .{}); + return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{}); }, } } -fn mirArithMemImm(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); +fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); assert(ops.reg2 == .none); - const payload = isel.mir.instructions.items(.data)[inst].payload; - const imm_pair = isel.mir.extraData(Mir.ImmPair, payload).data; + const payload = emit.mir.instructions.items(.data)[inst].payload; + const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; const ptr_size: Memory.PtrSize = switch (ops.flags) { 0b00 => .byte_ptr, 0b01 => .word_ptr, @@ -472,7 +470,7 @@ fn mirArithMemImm(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void { return lowerToMiEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm_pair.dest_off, .base = ops.reg1, - }), imm_pair.operand, isel.code) catch |err| isel.failWithLoweringError(err); + }), imm_pair.operand, emit.code) catch |err| emit.failWithLoweringError(err); } inline fn setRexWRegister(reg: Register) bool { @@ -483,26 +481,21 @@ inline fn setRexWRegister(reg: Register) bool { }; } -inline fn immOpSize(imm: i64) u8 { - blk: { - _ = math.cast(i8, imm) catch break :blk; +inline fn immOpSize(u_imm: u32) u8 { + const imm = @bitCast(i32, u_imm); + if (math.minInt(i8) <= imm and imm <= math.maxInt(i8)) { return 8; } - blk: { - _ = math.cast(i16, imm) catch break :blk; + if (math.minInt(i16) <= imm and imm <= math.maxInt(i16)) { return 16; } - blk: { - _ = math.cast(i32, imm) catch break :blk; - return 32; - } - return 64; + return 32; } -fn mirArithScaleSrc(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); +fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); const scale = ops.flags; - const imm = isel.mir.instructions.items(.data)[inst].imm; + const imm = emit.mir.instructions.items(.data)[inst].imm; // OP reg1, [reg2 + scale*rcx + imm32] const scale_index = ScaleIndex{ .scale = scale, @@ -512,13 +505,13 @@ fn mirArithScaleSrc(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void .disp = imm, .base = ops.reg2, .scale_index = scale_index, - }), isel.code) catch |err| isel.failWithLoweringError(err); + }), emit.code) catch |err| emit.failWithLoweringError(err); } -fn mirArithScaleDst(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); +fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); const scale = ops.flags; - const imm = isel.mir.instructions.items(.data)[inst].imm; + const imm = emit.mir.instructions.items(.data)[inst].imm; const scale_index = ScaleIndex{ .scale = scale, .index = .rax, @@ -529,21 +522,21 @@ fn mirArithScaleDst(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void .disp = 0, .base = ops.reg1, .scale_index = scale_index, - }), imm, isel.code) catch |err| isel.failWithLoweringError(err); + }), imm, emit.code) catch |err| emit.failWithLoweringError(err); } // OP [reg1 + scale*rax + imm32], reg2 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{ .disp = imm, .base = ops.reg1, .scale_index = scale_index, - }), ops.reg2, isel.code) catch |err| isel.failWithLoweringError(err); + }), ops.reg2, emit.code) catch |err| emit.failWithLoweringError(err); } -fn mirArithScaleImm(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); +fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); const scale = ops.flags; - const payload = isel.mir.instructions.items(.data)[inst].payload; - const imm_pair = isel.mir.extraData(Mir.ImmPair, payload).data; + const payload = emit.mir.instructions.items(.data)[inst].payload; + const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; const scale_index = ScaleIndex{ .scale = scale, .index = .rax, @@ -553,60 +546,60 @@ fn mirArithScaleImm(isel: *Isel, tag: Tag, inst: Mir.Inst.Index) InnerError!void .disp = imm_pair.dest_off, .base = ops.reg1, .scale_index = scale_index, - }), imm_pair.operand, isel.code) catch |err| isel.failWithLoweringError(err); + }), imm_pair.operand, emit.code) catch |err| emit.failWithLoweringError(err); } -fn mirMovabs(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { - const tag = isel.mir.instructions.items(.tag)[inst]; +fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .movabs); - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); - const imm: i64 = if (ops.reg1.size() == 64) blk: { - const payload = isel.mir.instructions.items(.data)[inst].payload; - const imm = isel.mir.extraData(Mir.Imm64, payload).data; - break :blk @bitCast(i64, imm.decode()); - } else isel.mir.instructions.items(.data)[inst].imm; + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const imm: u64 = if (ops.reg1.size() == 64) blk: { + const payload = emit.mir.instructions.items(.data)[inst].payload; + const imm = emit.mir.extraData(Mir.Imm64, payload).data; + break :blk imm.decode(); + } else emit.mir.instructions.items(.data)[inst].imm; if (ops.flags == 0b00) { // movabs reg, imm64 // OI - return lowerToOiEnc(.mov, ops.reg1, imm, isel.code) catch |err| isel.failWithLoweringError(err); + return lowerToOiEnc(.mov, ops.reg1, imm, emit.code) catch |err| emit.failWithLoweringError(err); } if (ops.reg1 == .none) { // movabs moffs64, rax // TD - return lowerToTdEnc(.mov, imm, ops.reg2, isel.code) catch |err| isel.failWithLoweringError(err); + return lowerToTdEnc(.mov, imm, ops.reg2, emit.code) catch |err| emit.failWithLoweringError(err); } // movabs rax, moffs64 // FD - return lowerToFdEnc(.mov, ops.reg1, imm, isel.code) catch |err| isel.failWithLoweringError(err); + return lowerToFdEnc(.mov, ops.reg1, imm, emit.code) catch |err| emit.failWithLoweringError(err); } -fn mirIMulComplex(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { - const tag = isel.mir.instructions.items(.tag)[inst]; +fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .imul_complex); - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { - return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), isel.code) catch |err| - isel.failWithLoweringError(err); + return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code) catch |err| + emit.failWithLoweringError(err); }, 0b10 => { - const imm = isel.mir.instructions.items(.data)[inst].imm; - return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, isel.code) catch |err| - isel.failWithLoweringError(err); + const imm = emit.mir.instructions.items(.data)[inst].imm; + return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code) catch |err| + emit.failWithLoweringError(err); }, - else => return isel.fail("TODO implement imul", .{}), + else => return emit.fail("TODO implement imul", .{}), } } -fn mirLea(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { - const tag = isel.mir.instructions.items(.tag)[inst]; +fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .lea); - const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { // lea reg1, [reg2 + imm32] // RM - const imm = isel.mir.instructions.items(.data)[inst].imm; + const imm = emit.mir.instructions.items(.data)[inst].imm; const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2; return lowerToRmEnc( .lea, @@ -615,25 +608,25 @@ fn mirLea(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { .disp = imm, .base = src_reg, }), - isel.code, - ) catch |err| isel.failWithLoweringError(err); + emit.code, + ) catch |err| emit.failWithLoweringError(err); }, 0b01 => { // lea reg1, [rip + imm32] // RM - const start_offset = isel.code.items.len; + const start_offset = emit.code.items.len; lowerToRmEnc( .lea, ops.reg1, RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0), - isel.code, - ) catch |err| return isel.failWithLoweringError(err); - const end_offset = isel.code.items.len; + emit.code, + ) catch |err| return emit.failWithLoweringError(err); + const end_offset = emit.code.items.len; // Backpatch the displacement - const payload = isel.mir.instructions.items(.data)[inst].payload; - const imm = isel.mir.extraData(Mir.Imm64, payload).data.decode(); + const payload = emit.mir.instructions.items(.data)[inst].payload; + const imm = emit.mir.extraData(Mir.Imm64, payload).data.decode(); const disp = @intCast(i32, @intCast(i64, imm) - @intCast(i64, end_offset - start_offset)); - mem.writeIntLittle(i32, isel.code.items[end_offset - 4 ..][0..4], disp); + mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); }, 0b10 => { // lea reg1, [rip + reloc] @@ -642,14 +635,14 @@ fn mirLea(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { .lea, ops.reg1, RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0), - isel.code, - ) catch |err| return isel.failWithLoweringError(err); - const end_offset = isel.code.items.len; - const got_entry = isel.mir.instructions.items(.data)[inst].got_entry; - if (isel.bin_file.cast(link.File.MachO)) |macho_file| { + emit.code, + ) catch |err| return emit.failWithLoweringError(err); + const end_offset = emit.code.items.len; + const got_entry = emit.mir.instructions.items(.data)[inst].got_entry; + if (emit.bin_file.cast(link.File.MachO)) |macho_file| { // TODO I think the reloc might be in the wrong place. const decl = macho_file.active_decl.?; - try decl.link.macho.relocs.append(isel.bin_file.allocator, .{ + try decl.link.macho.relocs.append(emit.bin_file.allocator, .{ .offset = @intCast(u32, end_offset - 4), .target = .{ .local = got_entry }, .addend = 0, @@ -659,7 +652,7 @@ fn mirLea(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), }); } else { - return isel.fail( + return emit.fail( "TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{}, ); @@ -667,7 +660,7 @@ fn mirLea(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { }, 0b11 => { // lea reg, [rbp + rcx + imm32] - const imm = isel.mir.instructions.items(.data)[inst].imm; + const imm = emit.mir.instructions.items(.data)[inst].imm; const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2; const scale_index = ScaleIndex{ .scale = 0, @@ -681,25 +674,25 @@ fn mirLea(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { .base = src_reg, .scale_index = scale_index, }), - isel.code, - ) catch |err| isel.failWithLoweringError(err); + emit.code, + ) catch |err| emit.failWithLoweringError(err); }, } } -fn mirCallExtern(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { - const tag = isel.mir.instructions.items(.tag)[inst]; +fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .call_extern); - const n_strx = isel.mir.instructions.items(.data)[inst].extern_fn; + const n_strx = emit.mir.instructions.items(.data)[inst].extern_fn; const offset = blk: { // callq - lowerToDEnc(.call_near, 0, isel.code) catch |err| - return isel.failWithLoweringError(err); - break :blk @intCast(u32, isel.code.items.len) - 4; + lowerToDEnc(.call_near, 0, emit.code) catch |err| + return emit.failWithLoweringError(err); + break :blk @intCast(u32, emit.code.items.len) - 4; }; - if (isel.bin_file.cast(link.File.MachO)) |macho_file| { + if (emit.bin_file.cast(link.File.MachO)) |macho_file| { // Add relocation to the decl. - try macho_file.active_decl.?.link.macho.relocs.append(isel.bin_file.allocator, .{ + try macho_file.active_decl.?.link.macho.relocs.append(emit.bin_file.allocator, .{ .offset = offset, .target = .{ .global = n_strx }, .addend = 0, @@ -709,22 +702,22 @@ fn mirCallExtern(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), }); } else { - return isel.fail("TODO implement call_extern for linking backends different than MachO", .{}); + return emit.fail("TODO implement call_extern for linking backends different than MachO", .{}); } } -fn mirDbgLine(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { - const tag = isel.mir.instructions.items(.tag)[inst]; +fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .dbg_line); - const payload = isel.mir.instructions.items(.data)[inst].payload; - const dbg_line_column = isel.mir.extraData(Mir.DbgLineColumn, payload).data; - try isel.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column); + const payload = emit.mir.instructions.items(.data)[inst].payload; + const dbg_line_column = emit.mir.extraData(Mir.DbgLineColumn, payload).data; + try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column); } -fn dbgAdvancePCAndLine(isel: *Isel, line: u32, column: u32) InnerError!void { - const delta_line = @intCast(i32, line) - @intCast(i32, isel.prev_di_line); - const delta_pc: usize = isel.code.items.len - isel.prev_di_pc; - switch (isel.debug_output) { +fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { + const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line); + const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; + switch (emit.debug_output) { .dwarf => |dbg_out| { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to @@ -737,15 +730,15 @@ fn dbgAdvancePCAndLine(isel: *Isel, line: u32, column: u32) InnerError!void { leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; } dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); - isel.prev_di_pc = isel.code.items.len; - isel.prev_di_line = line; - isel.prev_di_column = column; - isel.prev_di_pc = isel.code.items.len; + emit.prev_di_pc = emit.code.items.len; + emit.prev_di_line = line; + emit.prev_di_column = column; + emit.prev_di_pc = emit.code.items.len; }, .plan9 => |dbg_out| { if (delta_pc <= 0) return; // only do this when the pc changes // we have already checked the target in the linker to make sure it is compatable - const quant = @import("../../link/Plan9/aout.zig").getPCQuant(isel.target.cpu.arch) catch unreachable; + const quant = @import("../../link/Plan9/aout.zig").getPCQuant(emit.target.cpu.arch) catch unreachable; // increasing the line number try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line); @@ -770,62 +763,62 @@ fn dbgAdvancePCAndLine(isel: *Isel, line: u32, column: u32) InnerError!void { // we don't need to do anything, because adding the quant does it for us } else unreachable; if (dbg_out.start_line.* == null) - dbg_out.start_line.* = isel.prev_di_line; + dbg_out.start_line.* = emit.prev_di_line; dbg_out.end_line.* = line; // only do this if the pc changed - isel.prev_di_line = line; - isel.prev_di_column = column; - isel.prev_di_pc = isel.code.items.len; + emit.prev_di_line = line; + emit.prev_di_column = column; + emit.prev_di_pc = emit.code.items.len; }, .none => {}, } } -fn mirDbgPrologueEnd(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { - const tag = isel.mir.instructions.items(.tag)[inst]; +fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .dbg_prologue_end); - switch (isel.debug_output) { + switch (emit.debug_output) { .dwarf => |dbg_out| { try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); - try isel.dbgAdvancePCAndLine(isel.prev_di_line, isel.prev_di_column); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, .none => {}, } } -fn mirDbgEpilogueBegin(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { - const tag = isel.mir.instructions.items(.tag)[inst]; +fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .dbg_epilogue_begin); - switch (isel.debug_output) { + switch (emit.debug_output) { .dwarf => |dbg_out| { try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); - try isel.dbgAdvancePCAndLine(isel.prev_di_line, isel.prev_di_column); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, .none => {}, } } -fn mirArgDbgInfo(isel: *Isel, inst: Mir.Inst.Index) InnerError!void { - const tag = isel.mir.instructions.items(.tag)[inst]; +fn mirArgDbgInfo(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .arg_dbg_info); - const payload = isel.mir.instructions.items(.data)[inst].payload; - const arg_dbg_info = isel.mir.extraData(Mir.ArgDbgInfo, payload).data; - const mcv = isel.mir.function.args[arg_dbg_info.arg_index]; - try isel.genArgDbgInfo(arg_dbg_info.air_inst, mcv); + const payload = emit.mir.instructions.items(.data)[inst].payload; + const arg_dbg_info = emit.mir.extraData(Mir.ArgDbgInfo, payload).data; + const mcv = emit.mir.function.args[arg_dbg_info.arg_index]; + try emit.genArgDbgInfo(arg_dbg_info.air_inst, mcv); } -fn genArgDbgInfo(isel: *Isel, inst: Air.Inst.Index, mcv: MCValue) !void { - const ty_str = isel.mir.function.air.instructions.items(.data)[inst].ty_str; - const zir = &isel.mir.function.mod_fn.owner_decl.getFileScope().zir; +fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue) !void { + const ty_str = emit.mir.function.air.instructions.items(.data)[inst].ty_str; + const zir = &emit.mir.function.mod_fn.owner_decl.getFileScope().zir; const name = zir.nullTerminatedString(ty_str.str); const name_with_null = name.ptr[0 .. name.len + 1]; - const ty = isel.mir.function.air.getRefType(ty_str.ty); + const ty = emit.mir.function.air.getRefType(ty_str.ty); switch (mcv) { .register => |reg| { - switch (isel.debug_output) { + switch (emit.debug_output) { .dwarf => |dbg_out| { try dbg_out.dbg_info.ensureUnusedCapacity(3); dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter); @@ -834,7 +827,7 @@ fn genArgDbgInfo(isel: *Isel, inst: Air.Inst.Index, mcv: MCValue) !void { reg.dwarfLocOp(), }); try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try isel.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + try emit.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .plan9 => {}, @@ -842,7 +835,7 @@ fn genArgDbgInfo(isel: *Isel, inst: Air.Inst.Index, mcv: MCValue) !void { } }, .stack_offset => { - switch (isel.debug_output) { + switch (emit.debug_output) { .dwarf => {}, .plan9 => {}, .none => {}, @@ -854,21 +847,21 @@ fn genArgDbgInfo(isel: *Isel, inst: Air.Inst.Index, mcv: MCValue) !void { /// Adds a Type to the .debug_info at the current position. The bytes will be populated later, /// after codegen for this symbol is done. -fn addDbgInfoTypeReloc(isel: *Isel, ty: Type) !void { - switch (isel.debug_output) { +fn addDbgInfoTypeReloc(emit: *Emit, ty: Type) !void { + switch (emit.debug_output) { .dwarf => |dbg_out| { assert(ty.hasCodeGenBits()); const index = dbg_out.dbg_info.items.len; try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - const gop = try dbg_out.dbg_info_type_relocs.getOrPut(isel.bin_file.allocator, ty); + const gop = try dbg_out.dbg_info_type_relocs.getOrPut(emit.bin_file.allocator, ty); if (!gop.found_existing) { gop.value_ptr.* = .{ .off = undefined, .relocs = .{}, }; } - try gop.value_ptr.relocs.append(isel.bin_file.allocator, @intCast(u32, index)); + try gop.value_ptr.relocs.append(emit.bin_file.allocator, @intCast(u32, index)); }, .plan9 => {}, .none => {}, @@ -1233,7 +1226,7 @@ const ScaleIndex = struct { const Memory = struct { base: ?Register, rip: bool = false, - disp: i32, + disp: u32, ptr_size: PtrSize, scale_index: ?ScaleIndex = null, @@ -1283,7 +1276,7 @@ const Memory = struct { } else { encoder.sib_baseDisp8(dst); } - encoder.disp8(@intCast(i8, mem_op.disp)); + encoder.disp8(@bitCast(i8, @truncate(u8, mem_op.disp))); } else { encoder.modRm_SIBDisp32(src); if (mem_op.scale_index) |si| { @@ -1291,17 +1284,17 @@ const Memory = struct { } else { encoder.sib_baseDisp32(dst); } - encoder.disp32(mem_op.disp); + encoder.disp32(@bitCast(i32, mem_op.disp)); } } else { if (mem_op.disp == 0) { encoder.modRm_indirectDisp0(src, dst); } else if (immOpSize(mem_op.disp) == 8) { encoder.modRm_indirectDisp8(src, dst); - encoder.disp8(@intCast(i8, mem_op.disp)); + encoder.disp8(@bitCast(i8, @truncate(u8, mem_op.disp))); } else { encoder.modRm_indirectDisp32(src, dst); - encoder.disp32(mem_op.disp); + encoder.disp32(@bitCast(i32, mem_op.disp)); } } } else { @@ -1315,16 +1308,16 @@ const Memory = struct { encoder.sib_disp32(); } } - encoder.disp32(mem_op.disp); + encoder.disp32(@bitCast(i32, mem_op.disp)); } } }; -fn encodeImm(encoder: Encoder, imm: i32, size: u64) void { +fn encodeImm(encoder: Encoder, imm: u32, size: u64) void { switch (size) { - 8 => encoder.imm8(@intCast(i8, imm)), - 16 => encoder.imm16(@intCast(i16, imm)), - 32, 64 => encoder.imm32(imm), + 8 => encoder.imm8(@bitCast(i8, @truncate(u8, imm))), + 16 => encoder.imm16(@bitCast(i16, @truncate(u16, imm))), + 32, 64 => encoder.imm32(@bitCast(i32, imm)), else => unreachable, } } @@ -1338,7 +1331,7 @@ const RegisterOrMemory = union(enum) { } fn mem(ptr_size: Memory.PtrSize, args: struct { - disp: i32, + disp: u32, base: ?Register = null, scale_index: ?ScaleIndex = null, }) RegisterOrMemory { @@ -1352,7 +1345,7 @@ const RegisterOrMemory = union(enum) { }; } - fn rip(ptr_size: Memory.PtrSize, disp: i32) RegisterOrMemory { + fn rip(ptr_size: Memory.PtrSize, disp: u32) RegisterOrMemory { return .{ .memory = .{ .base = null, @@ -1366,7 +1359,6 @@ const RegisterOrMemory = union(enum) { const LoweringError = error{ OutOfMemory, - Overflow, OperandSizeMismatch, RaxOperandExpected, }; @@ -1377,12 +1369,12 @@ fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) LoweringError!void { opc.encode(encoder); } -fn lowerToIEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) LoweringError!void { +fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) LoweringError!void { if (tag == .ret_far or tag == .ret_near) { const encoder = try Encoder.init(code, 3); const opc = getOpCode(tag, .i, false).?; opc.encode(encoder); - encoder.imm16(@intCast(i16, imm)); + encoder.imm16(@bitCast(i16, @truncate(u16, imm))); return; } const opc = getOpCode(tag, .i, immOpSize(imm) == 8).?; @@ -1410,11 +1402,11 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) LoweringError! opc.encodeWithReg(encoder, reg); } -fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) LoweringError!void { +fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) LoweringError!void { const opc = getOpCode(tag, .d, false).?; const encoder = try Encoder.init(code, 6); opc.encode(encoder); - encoder.imm32(imm); + encoder.imm32(@bitCast(i32, imm)); } fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) LoweringError!void { @@ -1467,21 +1459,18 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) } } -fn lowerToTdEnc(tag: Tag, moffs: i64, reg: Register, code: *std.ArrayList(u8)) LoweringError!void { +fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) LoweringError!void { return lowerToTdFdEnc(tag, reg, moffs, code, true); } -fn lowerToFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8)) LoweringError!void { +fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) LoweringError!void { return lowerToTdFdEnc(tag, reg, moffs, code, false); } -fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), td: bool) LoweringError!void { +fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) LoweringError!void { if (reg.lowId() != Register.rax.lowId()) { return error.RaxOperandExpected; } - if (reg.size() != immOpSize(moffs)) { - return error.OperandSizeMismatch; - } const opc = if (td) getOpCode(tag, .td, reg.size() == 8).? else @@ -1495,29 +1484,15 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), }); opc.encode(encoder); switch (reg.size()) { - 8 => { - const moffs8 = try math.cast(i8, moffs); - encoder.imm8(moffs8); - }, - 16 => { - const moffs16 = try math.cast(i16, moffs); - encoder.imm16(moffs16); - }, - 32 => { - const moffs32 = try math.cast(i32, moffs); - encoder.imm32(moffs32); - }, - 64 => { - encoder.imm64(@bitCast(u64, moffs)); - }, + 8 => encoder.imm8(@bitCast(i8, @truncate(u8, moffs))), + 16 => encoder.imm16(@bitCast(i16, @truncate(u16, moffs))), + 32 => encoder.imm32(@bitCast(i32, @truncate(u32, moffs))), + 64 => encoder.imm64(moffs), else => unreachable, } } -fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) LoweringError!void { - if (reg.size() != immOpSize(imm)) { - return error.OperandSizeMismatch; - } +fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) LoweringError!void { const opc = getOpCode(tag, .oi, reg.size() == 8).?; const encoder = try Encoder.init(code, 10); if (reg.size() == 16) { @@ -1529,26 +1504,15 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Low }); opc.encodeWithReg(encoder, reg); switch (reg.size()) { - 8 => { - const imm8 = try math.cast(i8, imm); - encoder.imm8(imm8); - }, - 16 => { - const imm16 = try math.cast(i16, imm); - encoder.imm16(imm16); - }, - 32 => { - const imm32 = try math.cast(i32, imm); - encoder.imm32(imm32); - }, - 64 => { - encoder.imm64(@bitCast(u64, imm)); - }, + 8 => encoder.imm8(@bitCast(i8, @truncate(u8, imm))), + 16 => encoder.imm16(@bitCast(i16, @truncate(u16, imm))), + 32 => encoder.imm32(@bitCast(i32, @truncate(u32, imm))), + 64 => encoder.imm64(imm), else => unreachable, } } -fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.ArrayList(u8)) LoweringError!void { +fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) LoweringError!void { const modrm_ext = getModRmExt(tag).?; switch (reg_or_mem) { .register => |dst_reg| { @@ -1700,7 +1664,7 @@ fn lowerToRmiEnc( tag: Tag, reg: Register, reg_or_mem: RegisterOrMemory, - imm: i32, + imm: u32, code: *std.ArrayList(u8), ) LoweringError!void { if (reg.size() == 8) { @@ -1772,171 +1736,186 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []co return error.TestFailed; } -const TestIsel = struct { +const TestEmit = struct { code_buffer: std.ArrayList(u8), next: usize = 0, - fn init() TestIsel { + fn init() TestEmit { return .{ .code_buffer = std.ArrayList(u8).init(testing.allocator), }; } - fn deinit(isel: *TestIsel) void { - isel.code_buffer.deinit(); - isel.next = undefined; + fn deinit(emit: *TestEmit) void { + emit.code_buffer.deinit(); + emit.next = undefined; } - fn code(isel: *TestIsel) *std.ArrayList(u8) { - isel.next = isel.code_buffer.items.len; - return &isel.code_buffer; + fn code(emit: *TestEmit) *std.ArrayList(u8) { + emit.next = emit.code_buffer.items.len; + return &emit.code_buffer; } - fn lowered(isel: TestIsel) []const u8 { - return isel.code_buffer.items[isel.next..]; + fn lowered(emit: TestEmit) []const u8 { + return emit.code_buffer.items[emit.next..]; } }; test "lower MI encoding" { - var isel = TestIsel.init(); - defer isel.deinit(); - try lowerToMiEnc(.mov, RegisterOrMemory.reg(.rax), 0x10, isel.code()); - try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", isel.lowered(), "mov rax, 0x10"); - try lowerToMiEnc(.mov, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0, .base = .r11 }), 0x10, isel.code()); - try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", isel.lowered(), "mov dword ptr [r11 + 0], 0x10"); - try lowerToMiEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = -8, .base = .rdx }), 0x10, isel.code()); - try expectEqualHexStrings("\x81\x42\xF8\x10\x00\x00\x00", isel.lowered(), "add dword ptr [rdx - 8], 0x10"); + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToMiEnc(.mov, RegisterOrMemory.reg(.rax), 0x10, emit.code()); + try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", emit.lowered(), "mov rax, 0x10"); + try lowerToMiEnc(.mov, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0, .base = .r11 }), 0x10, emit.code()); + try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", emit.lowered(), "mov dword ptr [r11 + 0], 0x10"); + try lowerToMiEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ + .disp = @bitCast(u32, @as(i32, -8)), + .base = .rdx, + }), 0x10, emit.code()); + try expectEqualHexStrings("\x81\x42\xF8\x10\x00\x00\x00", emit.lowered(), "add dword ptr [rdx - 8], 0x10"); try lowerToMiEnc(.sub, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000, .base = .r11, - }), 0x10, isel.code()); + }), 0x10, emit.code()); try expectEqualHexStrings( "\x41\x81\xab\x00\x00\x00\x10\x10\x00\x00\x00", - isel.lowered(), + emit.lowered(), "sub dword ptr [r11 + 0x10000000], 0x10", ); - try lowerToMiEnc(.@"and", RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), 0x10, isel.code()); + try lowerToMiEnc(.@"and", RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), 0x10, emit.code()); try expectEqualHexStrings( "\x81\x24\x25\x00\x00\x00\x10\x10\x00\x00\x00", - isel.lowered(), + emit.lowered(), "and dword ptr [ds:0x10000000], 0x10", ); try lowerToMiEnc(.@"and", RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000, .base = .r12, - }), 0x10, isel.code()); + }), 0x10, emit.code()); try expectEqualHexStrings( "\x41\x81\xA4\x24\x00\x00\x00\x10\x10\x00\x00\x00", - isel.lowered(), + emit.lowered(), "and dword ptr [r12 + 0x10000000], 0x10", ); - try lowerToMiEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), 0x10, isel.code()); + try lowerToMiEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), 0x10, emit.code()); try expectEqualHexStrings( "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00", - isel.lowered(), + emit.lowered(), "mov qword ptr [rip + 0x10], 0x10", ); - try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = -8, .base = .rbp }), 0x10, isel.code()); + try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ + .disp = @bitCast(u32, @as(i32, -8)), + .base = .rbp, + }), 0x10, emit.code()); try expectEqualHexStrings( "\x48\xc7\x45\xf8\x10\x00\x00\x00", - isel.lowered(), + emit.lowered(), "mov qword ptr [rbp - 8], 0x10", ); - try lowerToMiEnc(.mov, RegisterOrMemory.mem(.word_ptr, .{ .disp = -2, .base = .rbp }), 0x10, isel.code()); - try expectEqualHexStrings("\x66\xC7\x45\xFE\x10\x00", isel.lowered(), "mov word ptr [rbp - 2], 0x10"); - try lowerToMiEnc(.mov, RegisterOrMemory.mem(.byte_ptr, .{ .disp = -1, .base = .rbp }), 0x10, isel.code()); - try expectEqualHexStrings("\xC6\x45\xFF\x10", isel.lowered(), "mov byte ptr [rbp - 1], 0x10"); + try lowerToMiEnc(.mov, RegisterOrMemory.mem(.word_ptr, .{ + .disp = @bitCast(u32, @as(i32, -2)), + .base = .rbp, + }), 0x10, emit.code()); + try expectEqualHexStrings("\x66\xC7\x45\xFE\x10\x00", emit.lowered(), "mov word ptr [rbp - 2], 0x10"); + try lowerToMiEnc(.mov, RegisterOrMemory.mem(.byte_ptr, .{ + .disp = @bitCast(u32, @as(i32, -1)), + .base = .rbp, + }), 0x10, emit.code()); + try expectEqualHexStrings("\xC6\x45\xFF\x10", emit.lowered(), "mov byte ptr [rbp - 1], 0x10"); try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000, .scale_index = .{ .scale = 1, .index = .rcx, }, - }), 0x10, isel.code()); + }), 0x10, emit.code()); try expectEqualHexStrings( "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00", - isel.lowered(), + emit.lowered(), "mov qword ptr [rcx*2 + 0x10000000], 0x10", ); } test "lower RM encoding" { - var isel = TestIsel.init(); - defer isel.deinit(); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.reg(.rbx), isel.code()); - try expectEqualHexStrings("\x48\x8b\xc3", isel.lowered(), "mov rax, rbx"); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r11 }), isel.code()); - try expectEqualHexStrings("\x49\x8b\x03", isel.lowered(), "mov rax, qword ptr [r11 + 0]"); - try lowerToRmEnc(.add, .r11, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000 }), isel.code()); + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToRmEnc(.mov, .rax, RegisterOrMemory.reg(.rbx), emit.code()); + try expectEqualHexStrings("\x48\x8b\xc3", emit.lowered(), "mov rax, rbx"); + try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r11 }), emit.code()); + try expectEqualHexStrings("\x49\x8b\x03", emit.lowered(), "mov rax, qword ptr [r11 + 0]"); + try lowerToRmEnc(.add, .r11, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000 }), emit.code()); try expectEqualHexStrings( "\x4C\x03\x1C\x25\x00\x00\x00\x10", - isel.lowered(), + emit.lowered(), "add r11, qword ptr [ds:0x10000000]", ); - try lowerToRmEnc(.add, .r12b, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), isel.code()); + try lowerToRmEnc(.add, .r12b, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), emit.code()); try expectEqualHexStrings( "\x44\x02\x24\x25\x00\x00\x00\x10", - isel.lowered(), + emit.lowered(), "add r11b, byte ptr [ds:0x10000000]", ); try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000, .base = .r13, - }), isel.code()); + }), emit.code()); try expectEqualHexStrings( "\x4D\x2B\x9D\x00\x00\x00\x10", - isel.lowered(), + emit.lowered(), "sub r11, qword ptr [r13 + 0x10000000]", ); try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000, .base = .r12, - }), isel.code()); + }), emit.code()); try expectEqualHexStrings( "\x4D\x2B\x9C\x24\x00\x00\x00\x10", - isel.lowered(), + emit.lowered(), "sub r11, qword ptr [r12 + 0x10000000]", ); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = -4, .base = .rbp }), isel.code()); - try expectEqualHexStrings("\x48\x8B\x45\xFC", isel.lowered(), "mov rax, qword ptr [rbp - 4]"); - try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(.qword_ptr, 0x10), isel.code()); - try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", isel.lowered(), "lea rax, [rip + 0x10]"); try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = -8, + .disp = @bitCast(u32, @as(i32, -4)), + .base = .rbp, + }), emit.code()); + try expectEqualHexStrings("\x48\x8B\x45\xFC", emit.lowered(), "mov rax, qword ptr [rbp - 4]"); + try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); + try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", emit.lowered(), "lea rax, [rip + 0x10]"); + try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ + .disp = @bitCast(u32, @as(i32, -8)), .base = .rbp, .scale_index = .{ .scale = 0, .index = .rcx, }, - }), isel.code()); - try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", isel.lowered(), "mov rax, qword ptr [rbp + rcx*1 - 8]"); + }), emit.code()); + try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*1 - 8]"); try lowerToRmEnc(.mov, .eax, RegisterOrMemory.mem(.dword_ptr, .{ - .disp = -4, + .disp = @bitCast(u32, @as(i32, -4)), .base = .rbp, .scale_index = .{ .scale = 2, .index = .rdx, }, - }), isel.code()); - try expectEqualHexStrings("\x8B\x44\x95\xFC", isel.lowered(), "mov eax, dword ptr [rbp + rdx*4 - 4]"); + }), emit.code()); + try expectEqualHexStrings("\x8B\x44\x95\xFC", emit.lowered(), "mov eax, dword ptr [rbp + rdx*4 - 4]"); try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = -8, + .disp = @bitCast(u32, @as(i32, -8)), .base = .rbp, .scale_index = .{ .scale = 3, .index = .rcx, }, - }), isel.code()); - try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", isel.lowered(), "mov rax, qword ptr [rbp + rcx*8 - 8]"); + }), emit.code()); + try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*8 - 8]"); try lowerToRmEnc(.mov, .r8b, RegisterOrMemory.mem(.byte_ptr, .{ - .disp = -24, + .disp = @bitCast(u32, @as(i32, -24)), .base = .rsi, .scale_index = .{ .scale = 0, .index = .rcx, }, - }), isel.code()); - try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", isel.lowered(), "mov r8b, byte ptr [rsi + rcx*1 - 24]"); + }), emit.code()); + try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", emit.lowered(), "mov r8b, byte ptr [rsi + rcx*1 - 24]"); try lowerToRmEnc(.lea, .rsi, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .rbp, @@ -1944,145 +1923,148 @@ test "lower RM encoding" { .scale = 0, .index = .rcx, }, - }), isel.code()); - try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", isel.lowered(), "lea rsi, qword ptr [rbp + rcx*1 + 0]"); + }), emit.code()); + try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", emit.lowered(), "lea rsi, qword ptr [rbp + rcx*1 + 0]"); } test "lower MR encoding" { - var isel = TestIsel.init(); - defer isel.deinit(); - try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), .rbx, isel.code()); - try expectEqualHexStrings("\x48\x89\xd8", isel.lowered(), "mov rax, rbx"); - try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = -4, .base = .rbp }), .r11, isel.code()); - try expectEqualHexStrings("\x4c\x89\x5d\xfc", isel.lowered(), "mov qword ptr [rbp - 4], r11"); - try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), .r12b, isel.code()); + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), .rbx, emit.code()); + try expectEqualHexStrings("\x48\x89\xd8", emit.lowered(), "mov rax, rbx"); + try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ + .disp = @bitCast(u32, @as(i32, -4)), + .base = .rbp, + }), .r11, emit.code()); + try expectEqualHexStrings("\x4c\x89\x5d\xfc", emit.lowered(), "mov qword ptr [rbp - 4], r11"); + try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), .r12b, emit.code()); try expectEqualHexStrings( "\x44\x00\x24\x25\x00\x00\x00\x10", - isel.lowered(), + emit.lowered(), "add byte ptr [ds:0x10000000], r12b", ); - try lowerToMrEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), .r12d, isel.code()); + try lowerToMrEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), .r12d, emit.code()); try expectEqualHexStrings( "\x44\x01\x24\x25\x00\x00\x00\x10", - isel.lowered(), + emit.lowered(), "add dword ptr [ds:0x10000000], r12d", ); try lowerToMrEnc(.sub, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000, .base = .r11, - }), .r12, isel.code()); + }), .r12, emit.code()); try expectEqualHexStrings( "\x4D\x29\xA3\x00\x00\x00\x10", - isel.lowered(), + emit.lowered(), "sub qword ptr [r11 + 0x10000000], r12", ); - try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), .r12, isel.code()); - try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", isel.lowered(), "mov qword ptr [rip + 0x10], r12"); + try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), .r12, emit.code()); + try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", emit.lowered(), "mov qword ptr [rip + 0x10], r12"); } test "lower OI encoding" { - var isel = TestIsel.init(); - defer isel.deinit(); - try lowerToOiEnc(.mov, .rax, 0x1000000000000000, isel.code()); + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToOiEnc(.mov, .rax, 0x1000000000000000, emit.code()); try expectEqualHexStrings( "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10", - isel.lowered(), + emit.lowered(), "movabs rax, 0x1000000000000000", ); - try lowerToOiEnc(.mov, .r11, 0x1000000000000000, isel.code()); + try lowerToOiEnc(.mov, .r11, 0x1000000000000000, emit.code()); try expectEqualHexStrings( "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10", - isel.lowered(), + emit.lowered(), "movabs r11, 0x1000000000000000", ); - try lowerToOiEnc(.mov, .r11d, 0x10000000, isel.code()); - try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", isel.lowered(), "mov r11d, 0x10000000"); - try lowerToOiEnc(.mov, .r11w, 0x1000, isel.code()); - try expectEqualHexStrings("\x66\x41\xBB\x00\x10", isel.lowered(), "mov r11w, 0x1000"); - try lowerToOiEnc(.mov, .r11b, 0x10, isel.code()); - try expectEqualHexStrings("\x41\xB3\x10", isel.lowered(), "mov r11b, 0x10"); + try lowerToOiEnc(.mov, .r11d, 0x10000000, emit.code()); + try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", emit.lowered(), "mov r11d, 0x10000000"); + try lowerToOiEnc(.mov, .r11w, 0x1000, emit.code()); + try expectEqualHexStrings("\x66\x41\xBB\x00\x10", emit.lowered(), "mov r11w, 0x1000"); + try lowerToOiEnc(.mov, .r11b, 0x10, emit.code()); + try expectEqualHexStrings("\x41\xB3\x10", emit.lowered(), "mov r11b, 0x10"); } test "lower FD/TD encoding" { - var isel = TestIsel.init(); - defer isel.deinit(); - try lowerToFdEnc(.mov, .rax, 0x1000000000000000, isel.code()); + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToFdEnc(.mov, .rax, 0x1000000000000000, emit.code()); try expectEqualHexStrings( "\x48\xa1\x00\x00\x00\x00\x00\x00\x00\x10", - isel.lowered(), + emit.lowered(), "mov rax, ds:0x1000000000000000", ); - try lowerToFdEnc(.mov, .eax, 0x10000000, isel.code()); - try expectEqualHexStrings("\xa1\x00\x00\x00\x10", isel.lowered(), "mov eax, ds:0x10000000"); - try lowerToFdEnc(.mov, .ax, 0x1000, isel.code()); - try expectEqualHexStrings("\x66\xa1\x00\x10", isel.lowered(), "mov ax, ds:0x1000"); - try lowerToFdEnc(.mov, .al, 0x10, isel.code()); - try expectEqualHexStrings("\xa0\x10", isel.lowered(), "mov al, ds:0x10"); + try lowerToFdEnc(.mov, .eax, 0x10000000, emit.code()); + try expectEqualHexStrings("\xa1\x00\x00\x00\x10", emit.lowered(), "mov eax, ds:0x10000000"); + try lowerToFdEnc(.mov, .ax, 0x1000, emit.code()); + try expectEqualHexStrings("\x66\xa1\x00\x10", emit.lowered(), "mov ax, ds:0x1000"); + try lowerToFdEnc(.mov, .al, 0x10, emit.code()); + try expectEqualHexStrings("\xa0\x10", emit.lowered(), "mov al, ds:0x10"); } test "lower M encoding" { - var isel = TestIsel.init(); - defer isel.deinit(); - try lowerToMEnc(.jmp_near, RegisterOrMemory.reg(.r12), isel.code()); - try expectEqualHexStrings("\x41\xFF\xE4", isel.lowered(), "jmp r12"); - try lowerToMEnc(.jmp_near, RegisterOrMemory.reg(.r12w), isel.code()); - try expectEqualHexStrings("\x66\x41\xFF\xE4", isel.lowered(), "jmp r12w"); - try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r12 }), isel.code()); - try expectEqualHexStrings("\x41\xFF\x24\x24", isel.lowered(), "jmp qword ptr [r12]"); - try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.word_ptr, .{ .disp = 0, .base = .r12 }), isel.code()); - try expectEqualHexStrings("\x66\x41\xFF\x24\x24", isel.lowered(), "jmp word ptr [r12]"); - try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10, .base = .r12 }), isel.code()); - try expectEqualHexStrings("\x41\xFF\x64\x24\x10", isel.lowered(), "jmp qword ptr [r12 + 0x10]"); + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToMEnc(.jmp_near, RegisterOrMemory.reg(.r12), emit.code()); + try expectEqualHexStrings("\x41\xFF\xE4", emit.lowered(), "jmp r12"); + try lowerToMEnc(.jmp_near, RegisterOrMemory.reg(.r12w), emit.code()); + try expectEqualHexStrings("\x66\x41\xFF\xE4", emit.lowered(), "jmp r12w"); + try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r12 }), emit.code()); + try expectEqualHexStrings("\x41\xFF\x24\x24", emit.lowered(), "jmp qword ptr [r12]"); + try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.word_ptr, .{ .disp = 0, .base = .r12 }), emit.code()); + try expectEqualHexStrings("\x66\x41\xFF\x24\x24", emit.lowered(), "jmp word ptr [r12]"); + try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10, .base = .r12 }), emit.code()); + try expectEqualHexStrings("\x41\xFF\x64\x24\x10", emit.lowered(), "jmp qword ptr [r12 + 0x10]"); try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x1000, .base = .r12, - }), isel.code()); + }), emit.code()); try expectEqualHexStrings( "\x41\xFF\xA4\x24\x00\x10\x00\x00", - isel.lowered(), + emit.lowered(), "jmp qword ptr [r12 + 0x1000]", ); - try lowerToMEnc(.jmp_near, RegisterOrMemory.rip(.qword_ptr, 0x10), isel.code()); - try expectEqualHexStrings("\xFF\x25\x10\x00\x00\x00", isel.lowered(), "jmp qword ptr [rip + 0x10]"); - try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10 }), isel.code()); - try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", isel.lowered(), "jmp qword ptr [ds:0x10]"); - try lowerToMEnc(.seta, RegisterOrMemory.reg(.r11b), isel.code()); - try expectEqualHexStrings("\x41\x0F\x97\xC3", isel.lowered(), "seta r11b"); + try lowerToMEnc(.jmp_near, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); + try expectEqualHexStrings("\xFF\x25\x10\x00\x00\x00", emit.lowered(), "jmp qword ptr [rip + 0x10]"); + try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10 }), emit.code()); + try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", emit.lowered(), "jmp qword ptr [ds:0x10]"); + try lowerToMEnc(.seta, RegisterOrMemory.reg(.r11b), emit.code()); + try expectEqualHexStrings("\x41\x0F\x97\xC3", emit.lowered(), "seta r11b"); } test "lower O encoding" { - var isel = TestIsel.init(); - defer isel.deinit(); - try lowerToOEnc(.pop, .r12, isel.code()); - try expectEqualHexStrings("\x41\x5c", isel.lowered(), "pop r12"); - try lowerToOEnc(.push, .r12w, isel.code()); - try expectEqualHexStrings("\x66\x41\x54", isel.lowered(), "push r12w"); + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToOEnc(.pop, .r12, emit.code()); + try expectEqualHexStrings("\x41\x5c", emit.lowered(), "pop r12"); + try lowerToOEnc(.push, .r12w, emit.code()); + try expectEqualHexStrings("\x66\x41\x54", emit.lowered(), "push r12w"); } test "lower RMI encoding" { - var isel = TestIsel.init(); - defer isel.deinit(); + var emit = TestEmit.init(); + defer emit.deinit(); try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = -8, + .disp = @bitCast(u32, @as(i32, -8)), .base = .rbp, - }), 0x10, isel.code()); + }), 0x10, emit.code()); try expectEqualHexStrings( "\x48\x69\x45\xF8\x10\x00\x00\x00", - isel.lowered(), + emit.lowered(), "imul rax, qword ptr [rbp - 8], 0x10", ); try lowerToRmiEnc(.imul, .eax, RegisterOrMemory.mem(.dword_ptr, .{ - .disp = -4, + .disp = @bitCast(u32, @as(i32, -4)), .base = .rbp, - }), 0x10, isel.code()); - try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", isel.lowered(), "imul eax, dword ptr [rbp - 4], 0x10"); + }), 0x10, emit.code()); + try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", emit.lowered(), "imul eax, dword ptr [rbp - 4], 0x10"); try lowerToRmiEnc(.imul, .ax, RegisterOrMemory.mem(.word_ptr, .{ - .disp = -2, + .disp = @bitCast(u32, @as(i32, -2)), .base = .rbp, - }), 0x10, isel.code()); - try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", isel.lowered(), "imul ax, word ptr [rbp - 2], 0x10"); - try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, isel.code()); - try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", isel.lowered(), "imul r12, r12, 0x10"); - try lowerToRmiEnc(.imul, .r12w, RegisterOrMemory.reg(.r12w), 0x10, isel.code()); - try expectEqualHexStrings("\x66\x45\x69\xE4\x10\x00", isel.lowered(), "imul r12w, r12w, 0x10"); + }), 0x10, emit.code()); + try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", emit.lowered(), "imul ax, word ptr [rbp - 2], 0x10"); + try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, emit.code()); + try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", emit.lowered(), "imul r12, r12, 0x10"); + try lowerToRmiEnc(.imul, .r12w, RegisterOrMemory.reg(.r12w), 0x10, emit.code()); + try expectEqualHexStrings("\x66\x45\x69\xE4\x10\x00", emit.lowered(), "imul r12w, r12w, 0x10"); } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index c5f595a86e..5ea4646c64 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -302,7 +302,7 @@ pub const Inst = struct { /// Another instruction. inst: Index, /// A 32-bit immediate value. - imm: i32, + imm: u32, /// An extern function. /// Index into the linker's string table. extern_fn: u32, @@ -324,8 +324,8 @@ pub const Inst = struct { }; pub const ImmPair = struct { - dest_off: i32, - operand: i32, + dest_off: u32, + operand: u32, }; pub const Imm64 = struct { |
