diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2022-02-18 17:36:03 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2022-02-19 14:24:03 +0100 |
| commit | bd396d7e076c09d162f679c7d26ba883dbe5d7cf (patch) | |
| tree | 824be689e26571cc078bf7d1d6833fd44208b42f /src | |
| parent | 2c13a4b87e1d9ae5c5c1117bc03b5fb67825ca34 (diff) | |
| download | zig-bd396d7e076c09d162f679c7d26ba883dbe5d7cf.tar.gz zig-bd396d7e076c09d162f679c7d26ba883dbe5d7cf.zip | |
x64: add unsigned div and move logic into a helper fn
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 185 | ||||
| -rw-r--r-- | src/arch/x86_64/Emit.zig | 11 | ||||
| -rw-r--r-- | src/arch/x86_64/Mir.zig | 1 |
3 files changed, 119 insertions, 78 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0846d0b45e..db36d9b264 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1252,37 +1252,54 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); } -fn airDiv(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const dst_ty = self.air.typeOfIndex(inst); - const tag = self.air.instructions.items(.tag)[inst]; - switch (tag) { - .div_exact => {}, - .div_trunc, .div_floor, .div_float => return self.fail("TODO implement {}", .{tag}), - else => unreachable, - } +/// Perform signed and unsigned integer division. +/// TODO it might be wise to split some functionality into integer and floating-point +/// specialised functions. +/// Supports AIR tag: +/// .div_exact, .div_trunc, .div_floor, .mod, .rem +fn genDivOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { + const dst_ty = self.air.typeOfIndex(inst); + const tag = self.air.instructions.items(.tag)[inst]; - if (dst_ty.zigTypeTag() != .Int) { - return self.fail("TODO implement {} for operands of type {}", .{ tag, dst_ty.zigTypeTag() }); - } + switch (tag) { + .div_exact, .div_trunc, .div_floor, .mod, .rem => {}, + .div_float => return self.fail("TODO implement genDivOp for {}", .{tag}), + else => unreachable, + } + + if (dst_ty.zigTypeTag() != .Int) { + return self.fail("TODO implement {} for operands of type {}", .{ tag, dst_ty.zigTypeTag() }); + } + if (dst_ty.abiSize(self.target.*) > 8) { + return self.fail("TODO implement {} for ABI size larger than 8", .{tag}); + } - const signedness = dst_ty.intInfo(self.target.*).signedness; - const ty = if (signedness == .signed) Type.isize else dst_ty; - const abi_size = @intCast(u32, ty.abiSize(self.target.*)); + const signedness = dst_ty.intInfo(self.target.*).signedness; + const tmp_ty = switch (signedness) { + .signed => Type.isize, + .unsigned => dst_ty, + }; + const abi_size = @intCast(u32, tmp_ty.abiSize(self.target.*)); - const lhs = try self.resolveInst(bin_op.lhs); - blk: { - switch (lhs) { - .register => |reg| { - if (reg.to64() == .rax) break :blk; - }, - else => {}, - } - try self.register_manager.getReg(.rax, inst); // track inst -> rax in register manager - try self.genSetReg(ty, .rax, lhs); + const lhs = try self.resolveInst(op_lhs); + blk: { + switch (lhs) { + .register => |reg| { + if (reg.to64() == .rax) break :blk; + }, + else => {}, } - if (signedness == .signed) { + try self.register_manager.getReg(.rax, inst); // track inst -> rax in register manager + try self.genSetReg(tmp_ty, .rax, lhs); + } + + try self.register_manager.getReg(.rdx, null); + self.register_manager.freezeRegs(&.{ .rax, .rdx }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); + + // Prep rdx for the op + switch (signedness) { + .signed => { _ = try self.addInst(.{ .tag = .cwd, .ops = (Mir.Ops{ @@ -1290,56 +1307,76 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { }).encode(), .data = undefined, }); - } - const dst_mcv = MCValue{ .register = registerAlias(.rax, abi_size) }; - - try self.register_manager.getReg(.rdx, null); - self.register_manager.freezeRegs(&.{ .rax, .rdx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); - - const rhs = try self.resolveInst(bin_op.rhs); - const divisor = blk: { - switch (rhs) { - .register, .stack_offset => break :blk rhs, - else => { - const reg = try self.copyToTmpRegister(ty, rhs); - break :blk MCValue{ .register = reg }; - }, - } - }; + }, + .unsigned => { + _ = try self.addInst(.{ + .tag = .xor, + .ops = (Mir.Ops{ + .reg1 = .rdx, + .reg2 = .rdx, + }).encode(), + .data = undefined, + }); + }, + } - switch (divisor) { - .register => |reg| { - _ = try self.addInst(.{ - .tag = .idiv, - .ops = (Mir.Ops{ - .reg1 = registerAlias(reg, abi_size), - }).encode(), - .data = undefined, - }); - }, - .stack_offset => |off| { - const flags: u2 = switch (abi_size) { - 1 => 0b00, - 2 => 0b01, - 4 => 0b10, - 8 => 0b11, - else => unreachable, - }; - _ = try self.addInst(.{ - .tag = .idiv, - .ops = (Mir.Ops{ - .reg2 = .rbp, - .flags = flags, - }).encode(), - .data = .{ .imm = @bitCast(u32, -off) }, - }); + const rhs = try self.resolveInst(op_rhs); + const divisor = blk: { + switch (rhs) { + .register, .stack_offset => break :blk rhs, + else => { + const reg = try self.copyToTmpRegister(tmp_ty, rhs); + break :blk MCValue{ .register = reg }; }, - else => unreachable, } + }; + const op_tag: Mir.Inst.Tag = switch (signedness) { + .signed => .idiv, + .unsigned => .div, + }; - break :result dst_mcv; + switch (divisor) { + .register => |reg| { + _ = try self.addInst(.{ + .tag = op_tag, + .ops = (Mir.Ops{ + .reg1 = registerAlias(reg, abi_size), + }).encode(), + .data = undefined, + }); + }, + .stack_offset => |off| { + _ = try self.addInst(.{ + .tag = op_tag, + .ops = (Mir.Ops{ + .reg2 = .rbp, + .flags = switch (abi_size) { + 1 => 0b00, + 2 => 0b01, + 4 => 0b10, + 8 => 0b11, + else => unreachable, + }, + }).encode(), + .data = .{ .imm = @bitCast(u32, -off) }, + }); + }, + else => unreachable, + } + + return switch (tag) { + .mod, .div_exact, .div_trunc, .div_floor => MCValue{ .register = .rax }, + .rem => MCValue{ .register = .rdx }, + else => unreachable, }; +} + +fn airDiv(self: *Self, inst: Air.Inst.Index) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + try self.genDivOp(inst, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1348,7 +1385,7 @@ fn airRem(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else - return self.fail("TODO implement rem for {}", .{self.target.cpu.arch}); + try self.genDivOp(inst, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1357,7 +1394,7 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else - return self.fail("TODO implement mod for {}", .{self.target.cpu.arch}); + try self.genDivOp(inst, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index e2de913c18..bfef415ad2 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -138,8 +138,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .shr => try emit.mirShift(.shr, inst), .sar => try emit.mirShift(.sar, inst), - .imul => try emit.mirIMulIDiv(.imul, inst), - .idiv => try emit.mirIMulIDiv(.idiv, inst), + .imul => try emit.mirMulDiv(.imul, inst), + .idiv => try emit.mirMulDiv(.idiv, inst), + .div => try emit.mirMulDiv(.div, inst), .imul_complex => try emit.mirIMulComplex(inst), .cwd => try emit.mirCwd(inst), @@ -687,7 +688,7 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } } -fn mirIMulIDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); if (ops.reg1 != .none) { assert(ops.reg2 == .none); @@ -1085,6 +1086,7 @@ const Tag = enum { nop, imul, idiv, + div, syscall, ret_near, ret_far, @@ -1319,7 +1321,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .setnl, .setge => OpCode.twoByte(0x0f, 0x9d), .setle, .setng => OpCode.twoByte(0x0f, 0x9e), .setnle, .setg => OpCode.twoByte(0x0f, 0x9f), - .idiv, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), + .idiv, .div, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), else => null, }, .o => return switch (tag) { @@ -1455,6 +1457,7 @@ inline fn getModRmExt(tag: Tag) ?u3 { .sar => 0x7, .imul => 0x5, .idiv => 0x7, + .div => 0x6, else => null, }; } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 17d0503bda..490c69a38c 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -227,6 +227,7 @@ pub const Inst = struct { /// 0b11 qword ptr [reg2 + imm32] imul, idiv, + div, /// ops flags: form: /// 0b00 AX <- AL |
