Commit d2a297c2b3
Changed files (2)
src
codegen
src/codegen/arm.zig
@@ -240,6 +240,22 @@ pub const Instruction = union(enum) {
fixed: u2 = 0b01,
cond: u4,
},
+ ExtraLoadStore: packed struct {
+ imm4l: u4,
+ fixed_1: u1 = 0b1,
+ op2: u2,
+ fixed_2: u1 = 0b1,
+ imm4h: u4,
+ rt: u4,
+ rn: u4,
+ o1: u1,
+ write_back: u1,
+ imm: u1,
+ up_down: u1,
+ pre_index: u1,
+ fixed_3: u3 = 0b000,
+ cond: u4,
+ },
BlockDataTransfer: packed struct {
register_list: u16,
rn: u4,
@@ -468,6 +484,29 @@ pub const Instruction = union(enum) {
}
};
+ /// Represents the offset operand of an extra load or store
+ /// instruction.
+ pub const ExtraLoadStoreOffset = union(enum) {
+ immediate: u8,
+ register: u4,
+
+ pub const none = ExtraLoadStoreOffset{
+ .immediate = 0,
+ };
+
+ pub fn reg(register: Register) ExtraLoadStoreOffset {
+ return ExtraLoadStoreOffset{
+ .register = register.id(),
+ };
+ }
+
+ pub fn imm(immediate: u8) ExtraLoadStoreOffset {
+ return ExtraLoadStoreOffset{
+ .immediate = immediate,
+ };
+ }
+ };
+
/// Represents the register list operand to a block data transfer
/// instruction
pub const RegisterList = packed struct {
@@ -495,6 +534,7 @@ pub const Instruction = union(enum) {
.Multiply => |v| @bitCast(u32, v),
.MultiplyLong => |v| @bitCast(u32, v),
.SingleDataTransfer => |v| @bitCast(u32, v),
+ .ExtraLoadStore => |v| @bitCast(u32, v),
.BlockDataTransfer => |v| @bitCast(u32, v),
.Branch => |v| @bitCast(u32, v),
.BranchExchange => |v| @bitCast(u32, v),
@@ -617,6 +657,43 @@ pub const Instruction = union(enum) {
};
}
+ fn extraLoadStore(
+ cond: Condition,
+ pre_index: bool,
+ positive: bool,
+ write_back: bool,
+ o1: u1,
+ op2: u2,
+ rn: Register,
+ rt: Register,
+ offset: ExtraLoadStoreOffset,
+ ) Instruction {
+ const imm4l: u4 = switch (offset) {
+ .immediate => |imm| @truncate(u4, imm),
+ .register => |reg| reg,
+ };
+ const imm4h: u4 = switch (offset) {
+ .immediate => |imm| @truncate(u4, imm >> 4),
+ .register => |reg| 0b0000,
+ };
+
+ return Instruction{
+ .ExtraLoadStore = .{
+ .imm4l = imm4l,
+ .op2 = op2,
+ .imm4h = imm4h,
+ .rt = rt.id(),
+ .rn = rn.id(),
+ .o1 = o1,
+ .write_back = @boolToInt(write_back),
+ .imm = @boolToInt(offset == .immediate),
+ .up_down = @boolToInt(positive),
+ .pre_index = @boolToInt(pre_index),
+ .cond = @enumToInt(cond),
+ },
+ };
+ }
+
fn blockDataTransfer(
cond: Condition,
rn: Register,
@@ -913,6 +990,23 @@ pub const Instruction = union(enum) {
return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0);
}
+ // Extra load/store
+
+ pub const ExtraLoadStoreOffsetArgs = struct {
+ pre_index: bool = true,
+ positive: bool = true,
+ offset: ExtraLoadStoreOffset,
+ write_back: bool = false,
+ };
+
+ pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
+ return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0, 0b01, rn, rt, args.offset);
+ }
+
+ pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
+ return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 1, 0b01, rn, rt, args.offset);
+ }
+
// Block data transfer
pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
@@ -1093,6 +1187,12 @@ test "serialize instructions" {
}),
.expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
},
+ .{ // strh r1, [r5]
+ .inst = Instruction.strh(.al, .r1, .r5, .{
+ .offset = Instruction.ExtraLoadStoreOffset.none,
+ }),
+ .expected = 0b1110_000_1_1_1_0_0_0101_0001_0000_1011_0000,
+ },
.{ // b #12
.inst = Instruction.b(.al, 12),
.expected = 0b1110_101_0_0000_0000_0000_0000_0000_0011,
src/codegen.zig
@@ -2630,21 +2630,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.register => |reg| {
const abi_size = ty.abiSize(self.target.*);
const adj_off = stack_offset + abi_size;
- const offset = if (adj_off <= math.maxInt(u12)) blk: {
- break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
- } else Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0);
switch (abi_size) {
- 1 => writeInt(u32, try self.code.addManyAsArray(4), Instruction.strb(.al, reg, .fp, .{
- .offset = offset,
- .positive = false,
- }).toU32()),
- 2 => return self.fail(src, "TODO implement strh", .{}),
- 4 => writeInt(u32, try self.code.addManyAsArray(4), Instruction.str(.al, reg, .fp, .{
- .offset = offset,
- .positive = false,
- }).toU32()),
- else => return self.fail(src, "TODO a type of size {} is not allowed in a register", .{abi_size}),
+ 1, 4 => {
+ const offset = if (adj_off <= math.maxInt(u12)) blk: {
+ break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
+ } else Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0);
+ const str = switch (abi_size) {
+ 1 => Instruction.strb,
+ 4 => Instruction.str,
+ else => unreachable,
+ };
+
+ writeInt(u32, try self.code.addManyAsArray(4), str(.al, reg, .fp, .{
+ .offset = offset,
+ .positive = false,
+ }).toU32());
+ },
+ 2 => {
+ const offset = if (adj_off <= math.maxInt(u8)) blk: {
+ break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
+ } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }));
+
+ writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{
+ .offset = offset,
+ .positive = false,
+ }).toU32());
+ },
+ else => return self.fail(src, "TODO implement storing other types abi_size={}", .{abi_size}),
}
},
.memory => |vaddr| {
@@ -2836,20 +2849,33 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
// const abi_size = ty.abiSize(self.target.*);
const abi_size = 4;
const adj_off = unadjusted_off + abi_size;
- const offset = if (adj_off <= math.maxInt(u12)) blk: {
- break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
- } else Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0);
switch (abi_size) {
- 1 => writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrb(.al, reg, .fp, .{
- .offset = offset,
- .positive = false,
- }).toU32()),
- 2 => return self.fail(src, "TODO implement ldrh", .{}),
- 4 => writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, .fp, .{
- .offset = offset,
- .positive = false,
- }).toU32()),
+ 1, 4 => {
+ const offset = if (adj_off <= math.maxInt(u12)) blk: {
+ break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
+ } else Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0);
+ const ldr = switch (abi_size) {
+ 1 => Instruction.ldrb,
+ 4 => Instruction.ldr,
+ else => unreachable,
+ };
+
+ writeInt(u32, try self.code.addManyAsArray(4), ldr(.al, reg, .fp, .{
+ .offset = offset,
+ .positive = false,
+ }).toU32());
+ },
+ 2 => {
+ const offset = if (adj_off <= math.maxInt(u8)) blk: {
+ break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
+ } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }));
+
+ writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{
+ .offset = offset,
+ .positive = false,
+ }).toU32());
+ },
else => return self.fail(src, "TODO a type of size {} is not allowed in a register", .{abi_size}),
}
},