stage2 AArch64: Implement basic truncate functionality

This commit is contained in:
joachimschmidt557 2022-04-16 18:58:48 +02:00
parent c78daeb642
commit f95a8ddafa
No known key found for this signature in database
GPG Key ID: E0B575BE2884ACC5
5 changed files with 224 additions and 53 deletions

View File

@ -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| {

View File

@ -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,
}
}

View File

@ -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

View File

@ -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,
},
};
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
},
},
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,
},
};
},
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(

View File

@ -15,8 +15,6 @@ test "empty function with comments" {
}
test "truncate" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
try expect(testTruncate(0x10fd) == 0xfd);
comptime try expect(testTruncate(0x10fd) == 0xfd);
}