aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/arch/aarch64/CodeGen.zig122
-rw-r--r--src/arch/aarch64/Emit.zig40
-rw-r--r--src/arch/aarch64/Mir.zig38
-rw-r--r--src/arch/aarch64/bits.zig75
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(