aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/arch/arm/CodeGen.zig69
-rw-r--r--src/arch/arm/Emit.zig19
-rw-r--r--src/arch/arm/Mir.zig14
-rw-r--r--src/arch/arm/bits.zig44
4 files changed, 142 insertions, 4 deletions
diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig
index 8ad9e980cf..467e560687 100644
--- a/src/arch/arm/CodeGen.zig
+++ b/src/arch/arm/CodeGen.zig
@@ -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 {
diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig
index 3ee70583d0..d4561553c4 100644
--- a/src/arch/arm/Emit.zig
+++ b/src/arch/arm/Emit.zig
@@ -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,
+ }
+}
diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig
index e6f1bcb0a2..c85acae085 100644
--- a/src/arch/arm/Mir.zig
+++ b/src/arch/arm/Mir.zig
@@ -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
diff --git a/src/arch/arm/bits.zig b/src/arch/arm/bits.zig
index 792bf0dc05..a8866ac5c9 100644
--- a/src/arch/arm/bits.zig
+++ b/src/arch/arm/bits.zig
@@ -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 {