stage2 ARM: implement truncate to ints with bits <= 32

This commit is contained in:
joachimschmidt557 2022-02-24 22:18:21 +01:00
parent db82c1b982
commit 8ef80cfaab
No known key found for this signature in database
GPG Key ID: E0B575BE2884ACC5
6 changed files with 142 additions and 13 deletions

View File

@ -439,7 +439,7 @@ fn gen(self: *Self) !void {
// the code. Therefore, we can just delete
// the space initially reserved for the
// jump
self.mir_instructions.len -= 1;
self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.items[0]);
} else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
self.mir_instructions.set(jmp_reloc, .{
.tag = .b,
@ -749,6 +749,17 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u
/// Use a pointer instruction as the basis for allocating stack memory.
fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
const elem_ty = self.air.typeOfIndex(inst).elemType();
if (!elem_ty.hasRuntimeBits()) {
// As this stack item will never be dereferenced at runtime,
// return the current stack offset
try self.stack.putNoClobber(self.gpa, self.next_stack_offset, .{
.inst = inst,
.size = 0,
});
return self.next_stack_offset;
}
const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
};
@ -872,11 +883,61 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
if (self.liveness.isUnused(inst))
return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
const operand_ty = self.air.typeOf(ty_op.operand);
const operand = try self.resolveInst(ty_op.operand);
_ = operand;
const info_a = operand_ty.intInfo(self.target.*);
const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*);
return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch});
// return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
const result: MCValue = blk: {
if (info_b.bits <= 32) {
const operand_reg = switch (operand) {
.register => |r| r,
else => operand_reg: {
if (info_a.bits <= 32) {
break :operand_reg try self.copyToTmpRegister(operand_ty, operand);
} 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 = dest_reg: {
if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
break :dest_reg operand_reg;
}
break :dest_reg try self.register_manager.allocReg(null);
};
switch (info_b.bits) {
32 => {
try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg });
break :blk MCValue{ .register = dest_reg };
},
else => {
_ = try self.addInst(.{
.tag = switch (info_b.signedness) {
.signed => .sbfx,
.unsigned => .ubfx,
},
.data = .{ .rr_lsb_width = .{
.rd = dest_reg,
.rn = operand_reg,
.lsb = 0,
.width = @intCast(u6, info_b.bits),
} },
});
break :blk MCValue{ .register = dest_reg };
},
}
} else {
return self.fail("TODO: truncate to ints > 32 bits", .{});
}
};
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {

View File

@ -130,6 +130,9 @@ pub fn emitMir(
.push => try emit.mirBlockDataTransfer(inst),
.svc => try emit.mirSupervisorCall(inst),
.sbfx => try emit.mirBitFieldExtract(inst),
.ubfx => try emit.mirBitFieldExtract(inst),
}
}
}
@ -691,3 +694,19 @@ fn mirSupervisorCall(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 cond = emit.mir.instructions.items(.cond)[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(cond, rd, rn, lsb, width)),
.ubfx => try emit.writeInstruction(Instruction.ubfx(cond, rd, rn, lsb, width)),
else => unreachable,
}
}

View File

@ -88,6 +88,8 @@ pub const Inst = struct {
push,
/// Reverse Subtract
rsb,
/// Signed Bit Field Extract
sbfx,
/// Store Register
str,
/// Store Register Byte
@ -98,6 +100,8 @@ pub const Inst = struct {
sub,
/// Supervisor Call
svc,
/// Unsigned Bit Field Extract
ubfx,
};
/// The position of an MIR instruction within the `Mir` instructions array.
@ -179,6 +183,16 @@ pub const Inst = struct {
rn: Register,
offset: bits.Instruction.ExtraLoadStoreOffsetArgs,
},
/// Two registers and a lsb (range 0-31) and a width (range
/// 1-32)
///
/// Used by e.g. sbfx
rr_lsb_width: struct {
rd: Register,
rn: Register,
lsb: u5,
width: u6,
},
/// Three registers
///
/// Used by e.g. mul

View File

@ -1,5 +1,6 @@
const std = @import("std");
const DW = std.dwarf;
const assert = std.debug.assert;
const testing = std.testing;
/// The condition field specifies the flags necessary for an
@ -237,6 +238,17 @@ pub const Instruction = union(enum) {
fixed_3: u5 = 0b00010,
cond: u4,
},
bit_field_extract: packed struct {
rn: u4,
fixed_1: u3 = 0b101,
lsb: u5,
rd: u4,
widthm1: u5,
fixed_2: u1 = 0b1,
unsigned: u1,
fixed_3: u5 = 0b01111,
cond: u4,
},
single_data_transfer: packed struct {
offset: u12,
rd: u4,
@ -576,6 +588,7 @@ pub const Instruction = union(enum) {
.multiply => |v| @bitCast(u32, v),
.multiply_long => |v| @bitCast(u32, v),
.integer_saturating_arithmetic => |v| @bitCast(u32, v),
.bit_field_extract => |v| @bitCast(u32, v),
.single_data_transfer => |v| @bitCast(u32, v),
.extra_load_store => |v| @bitCast(u32, v),
.block_data_transfer => |v| @bitCast(u32, v),
@ -691,6 +704,27 @@ pub const Instruction = union(enum) {
};
}
fn bitFieldExtract(
unsigned: u1,
cond: Condition,
rd: Register,
rn: Register,
lsb: u5,
width: u6,
) Instruction {
assert(width > 0 and width <= 32);
return Instruction{
.bit_field_extract = .{
.rn = rn.id(),
.lsb = lsb,
.rd = rd.id(),
.widthm1 = @intCast(u5, width - 1),
.unsigned = unsigned,
.cond = @enumToInt(cond),
},
};
}
fn singleDataTransfer(
cond: Condition,
rd: Register,
@ -1044,6 +1078,16 @@ pub const Instruction = union(enum) {
return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn);
}
// Bit field extract
pub fn ubfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction {
return bitFieldExtract(0b1, cond, rd, rn, lsb, width);
}
pub fn sbfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction {
return bitFieldExtract(0b0, cond, rd, rn, lsb, width);
}
// Single data transfer
pub const OffsetArgs = struct {

View File

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

View File

@ -4,7 +4,6 @@ const expect = std.testing.expect;
test "truncate u0 to larger integer allowed and has comptime known result" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
var x: u0 = 0;
const y = @truncate(u8, x);
@ -13,7 +12,6 @@ test "truncate u0 to larger integer allowed and has comptime known result" {
test "truncate.u0.literal" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
var z = @truncate(u0, 0);
try expect(z == 0);
@ -21,7 +19,6 @@ test "truncate.u0.literal" {
test "truncate.u0.const" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
const c0: usize = 0;
var z = @truncate(u0, c0);
@ -30,7 +27,6 @@ test "truncate.u0.const" {
test "truncate.u0.var" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
var d: u8 = 2;
var z = @truncate(u0, d);
@ -39,7 +35,6 @@ test "truncate.u0.var" {
test "truncate i0 to larger integer allowed and has comptime known result" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
var x: i0 = 0;
const y = @truncate(i8, x);
@ -48,7 +43,6 @@ test "truncate i0 to larger integer allowed and has comptime known result" {
test "truncate.i0.literal" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
var z = @truncate(i0, 0);
try expect(z == 0);
@ -56,7 +50,6 @@ test "truncate.i0.literal" {
test "truncate.i0.const" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
const c0: isize = 0;
var z = @truncate(i0, c0);
@ -65,7 +58,6 @@ test "truncate.i0.const" {
test "truncate.i0.var" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
var d: i8 = 2;
var z = @truncate(i0, d);