diff options
Diffstat (limited to 'src/arch')
| -rw-r--r-- | src/arch/aarch64/CodeGen.zig | 122 | ||||
| -rw-r--r-- | src/arch/aarch64/Emit.zig | 40 | ||||
| -rw-r--r-- | src/arch/aarch64/Mir.zig | 38 | ||||
| -rw-r--r-- | src/arch/aarch64/bits.zig | 75 |
4 files changed, 224 insertions, 51 deletions
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 0a5c5065e8..3a73a54c54 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -939,14 +939,99 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); } +fn truncRegister( + self: *Self, + operand_reg: Register, + dest_reg: Register, + int_signedness: std.builtin.Signedness, + int_bits: u16, +) !void { + switch (int_bits) { + 1...31, 33...63 => { + _ = try self.addInst(.{ + .tag = switch (int_signedness) { + .signed => .sbfx, + .unsigned => .ubfx, + }, + .data = .{ .rr_lsb_width = .{ + .rd = dest_reg, + .rn = operand_reg, + .lsb = 0, + .width = @intCast(u6, int_bits), + } }, + }); + }, + 32, 64 => { + _ = try self.addInst(.{ + .tag = .mov_register, + .data = .{ .rr = .{ + .rd = dest_reg, + .rn = operand_reg, + } }, + }); + }, + else => unreachable, + } +} + +fn trunc( + self: *Self, + maybe_inst: ?Air.Inst.Index, + operand: MCValue, + operand_ty: Type, + dest_ty: Type, +) !MCValue { + const info_a = operand_ty.intInfo(self.target.*); + const info_b = dest_ty.intInfo(self.target.*); + + if (info_b.bits <= 64) { + const operand_reg = switch (operand) { + .register => |r| r, + else => operand_reg: { + if (info_a.bits <= 64) { + const raw_reg = try self.copyToTmpRegister(operand_ty, operand); + break :operand_reg registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); + } else { + return self.fail("TODO load least significant word into register", .{}); + } + }, + }; + self.register_manager.freezeRegs(&.{operand_reg}); + defer self.register_manager.unfreezeRegs(&.{operand_reg}); + + const dest_reg = if (maybe_inst) |inst| blk: { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + + if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { + break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*)); + } else { + const raw_reg = try self.register_manager.allocReg(inst); + break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); + } + } else blk: { + const raw_reg = try self.register_manager.allocReg(null); + break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); + }; + + try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); + + return MCValue{ .register = dest_reg }; + } else { + return self.fail("TODO: truncate to ints > 32 bits", .{}); + } +} + fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - if (self.liveness.isUnused(inst)) - return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); - const operand = try self.resolveInst(ty_op.operand); - _ = operand; - return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch}); + const operand_ty = self.air.typeOf(ty_op.operand); + const dest_ty = self.air.typeOfIndex(inst); + + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { + break :blk try self.trunc(inst, operand, operand_ty, dest_ty); + }; + + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { @@ -3483,23 +3568,26 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } }, }); - if (x > math.maxInt(u16)) { + if (x & 0x0000_0000_ffff_0000 != 0) { _ = try self.addInst(.{ .tag = .movk, .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } }, }); } - if (x > math.maxInt(u32)) { - _ = try self.addInst(.{ - .tag = .movk, - .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, - }); - } - if (x > math.maxInt(u48)) { - _ = try self.addInst(.{ - .tag = .movk, - .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, - }); + + if (reg.size() == 64) { + if (x & 0x0000_ffff_0000_0000 != 0) { + _ = try self.addInst(.{ + .tag = .movk, + .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, + }); + } + if (x & 0xffff_0000_0000_0000 != 0) { + _ = try self.addInst(.{ + .tag = .movk, + .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, + }); + } } }, .register => |src_reg| { diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 077330f0ca..ac731636bd 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -162,6 +162,17 @@ pub fn emitMir( .push_regs => try emit.mirPushPopRegs(inst), .pop_regs => try emit.mirPushPopRegs(inst), + + .sbfx, + .ubfx, + => try emit.mirBitfieldExtract(inst), + + .sxtb, + .sxth, + .sxtw, + .uxtb, + .uxth, + => try emit.mirExtend(inst), } } } @@ -1050,3 +1061,32 @@ fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void { else => unreachable, } } + +fn mirBitfieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width; + const rd = rr_lsb_width.rd; + const rn = rr_lsb_width.rn; + const lsb = rr_lsb_width.lsb; + const width = rr_lsb_width.width; + + switch (tag) { + .sbfx => try emit.writeInstruction(Instruction.sbfx(rd, rn, lsb, width)), + .ubfx => try emit.writeInstruction(Instruction.ubfx(rd, rn, lsb, width)), + else => unreachable, + } +} + +fn mirExtend(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const rr = emit.mir.instructions.items(.data)[inst].rr; + + switch (tag) { + .sxtb => try emit.writeInstruction(Instruction.sxtb(rr.rd, rr.rn)), + .sxth => try emit.writeInstruction(Instruction.sxth(rr.rd, rr.rn)), + .sxtw => try emit.writeInstruction(Instruction.sxtw(rr.rd, rr.rn)), + .uxtb => try emit.writeInstruction(Instruction.uxtb(rr.rd, rr.rn)), + .uxth => try emit.writeInstruction(Instruction.uxth(rr.rd, rr.rn)), + else => unreachable, + } +} diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 6515a8da2e..dcde591a07 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -130,6 +130,14 @@ pub const Inst = struct { push_regs, /// Return from subroutine ret, + /// Signed bitfield extract + sbfx, + /// Signed extend byte + sxtb, + /// Signed extend halfword + sxth, + /// Signed extend word + sxtw, /// Store Pair of Registers stp, /// Pseudo-instruction: Store to stack @@ -156,6 +164,12 @@ pub const Inst = struct { sub_shifted_register, /// Supervisor Call svc, + /// Unsigned bitfield extract + ubfx, + /// Unsigned extend byte + uxtb, + /// Unsigned extend halfword + uxth, }; /// The position of an MIR instruction within the `Mir` instructions array. @@ -225,13 +239,6 @@ pub const Inst = struct { rt: Register, inst: Index, }, - /// Two registers - /// - /// Used by e.g. mov_register - rr: struct { - rd: Register, - rn: Register, - }, /// A register, an unsigned 12-bit immediate, and an optional shift /// /// Used by e.g. cmp_immediate @@ -240,6 +247,13 @@ pub const Inst = struct { imm12: u12, sh: u1 = 0, }, + /// Two registers + /// + /// Used by e.g. mov_register + rr: struct { + rd: Register, + rn: Register, + }, /// Two registers, an unsigned 12-bit immediate, and an optional shift /// /// Used by e.g. sub_immediate @@ -268,6 +282,16 @@ pub const Inst = struct { imm6: u6, shift: bits.Instruction.LogicalShiftedRegisterShift, }, + /// Two registers and a lsb (range 0-63) and a width (range + /// 1-64) + /// + /// Used by e.g. ubfx + rr_lsb_width: struct { + rd: Register, + rn: Register, + lsb: u6, + width: u7, + }, /// Two registers and a bitmask immediate /// /// Used by e.g. eor_immediate diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index 49bc09d8f2..f275776ac6 100644 --- a/src/arch/aarch64/bits.zig +++ b/src/arch/aarch64/bits.zig @@ -510,33 +510,23 @@ pub const Instruction = union(enum) { imm16: u16, shift: u6, ) Instruction { - switch (rd.size()) { - 32 => { - assert(shift % 16 == 0 and shift <= 16); - return Instruction{ - .move_wide_immediate = .{ - .rd = rd.enc(), - .imm16 = imm16, - .hw = @intCast(u2, shift / 16), - .opc = opc, - .sf = 0, - }, - }; - }, - 64 => { - assert(shift % 16 == 0 and shift <= 48); - return Instruction{ - .move_wide_immediate = .{ - .rd = rd.enc(), - .imm16 = imm16, - .hw = @intCast(u2, shift / 16), - .opc = opc, - .sf = 1, - }, - }; + assert(shift % 16 == 0); + assert(!(rd.size() == 32 and shift > 16)); + assert(!(rd.size() == 64 and shift > 48)); + + return Instruction{ + .move_wide_immediate = .{ + .rd = rd.enc(), + .imm16 = imm16, + .hw = @intCast(u2, shift / 16), + .opc = opc, + .sf = switch (rd.size()) { + 32 => 0, + 64 => 1, + else => unreachable, // unexpected register size + }, }, - else => unreachable, // unexpected register size - } + }; } fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction { @@ -914,7 +904,7 @@ pub const Instruction = union(enum) { n: u1, ) Instruction { assert(rd.size() == rn.size()); - assert(!(rd.size() == 32 and n == 1)); + assert(!(rd.size() == 32 and n != 0)); return Instruction{ .logical_immediate = .{ @@ -942,6 +932,8 @@ pub const Instruction = union(enum) { imms: u6, ) Instruction { assert(rd.size() == rn.size()); + assert(!(rd.size() == 64 and n != 1)); + assert(!(rd.size() == 32 and (n != 0 or immr >> 5 != 0 or immr >> 5 != 0))); return Instruction{ .bitfield = .{ @@ -1417,6 +1409,23 @@ pub const Instruction = union(enum) { return sbfm(rd, rn, shift, imms); } + pub fn sbfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction { + return sbfm(rd, rn, lsb, @intCast(u6, lsb + width - 1)); + } + + pub fn sxtb(rd: Register, rn: Register) Instruction { + return sbfm(rd, rn, 0, 7); + } + + pub fn sxth(rd: Register, rn: Register) Instruction { + return sbfm(rd, rn, 0, 15); + } + + pub fn sxtw(rd: Register, rn: Register) Instruction { + assert(rd.size() == 64); + return sbfm(rd, rn, 0, 31); + } + pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction { const size = @intCast(u6, rd.size() - 1); return ubfm(rd, rn, size - shift + 1, size - shift); @@ -1427,6 +1436,18 @@ pub const Instruction = union(enum) { return ubfm(rd, rn, shift, imms); } + pub fn ubfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction { + return ubfm(rd, rn, lsb, @intCast(u6, lsb + width - 1)); + } + + pub fn uxtb(rd: Register, rn: Register) Instruction { + return ubfm(rd, rn, 0, 7); + } + + pub fn uxth(rd: Register, rn: Register) Instruction { + return ubfm(rd, rn, 0, 15); + } + // Add/subtract (shifted register) pub fn addShiftedRegister( |
