From d2a297c2b3eb293b393f41640892ff7a5a71027f Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 10 Jan 2021 20:50:38 +0100 Subject: [PATCH] stage2 ARM: add extra load/store instructions --- src/codegen.zig | 76 ++++++++++++++++++++++----------- src/codegen/arm.zig | 100 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 25 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 7c67a9191b..709c91a635 100644 --- a/src/codegen.zig +++ b/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}), } }, diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig index 978c653cb0..94f1ae951d 100644 --- a/src/codegen/arm.zig +++ b/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,