mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 22:35:24 +00:00
stage2 AArch64: Implement basic truncate functionality
This commit is contained in:
parent
c78daeb642
commit
f95a8ddafa
@ -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| {
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user