From f1960302d115cf42554481871d22393998b87338 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 Nov 2020 18:43:53 +0100 Subject: stage2 aarch64: add ldr instruction + smoke tests --- src/codegen/aarch64.zig | 228 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) (limited to 'src/codegen') diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig index 6ceeaea47e..e18cc830b4 100644 --- a/src/codegen/aarch64.zig +++ b/src/codegen/aarch64.zig @@ -203,6 +203,21 @@ pub const Instruction = union(enum) { opc: u2, sf: u1, }, + LoadRegister: packed struct { + rt: u5, + rn: u5, + offset: u12, + opc: u2 = 0b01, + op1: u2, + fixed: u4 = 0b111_0, + size: u2, + }, + LoadLiteral: packed struct { + rt: u5, + imm19: u19, + fixed: u6 = 0b011_0_00, + opc: u2, + }, ExceptionGeneration: packed struct { ll: u2, op2: u3, @@ -227,12 +242,131 @@ pub const Instruction = union(enum) { pub fn toU32(self: Instruction) u32 { return switch (self) { .MoveWideImmediate => |v| @bitCast(u32, v), + .LoadRegister => |v| @bitCast(u32, v), + .LoadLiteral => |v| @bitCast(u32, v), .ExceptionGeneration => |v| @bitCast(u32, v), .UnconditionalBranchRegister => |v| @bitCast(u32, v), .UnconditionalBranchImmediate => |v| @bitCast(u32, v), }; } + /// Represents the offset operand of a load or store instruction. + /// Data can be loaded from memory with either an immediate offset + /// or an offset that is stored in some register. + pub const Offset = union(enum) { + Immediate: union(enum) { + PostIndex: i9, + PreIndex: i9, + Unsigned: u12, + }, + Register: struct { + rm: u5, + shift: union(enum) { + Uxtw: u2, + Lsl: u2, + Sxtw: u2, + Sxtx: u2, + }, + }, + + pub const none = Offset{ + .Immediate = .{ .Unsigned = 0 }, + }; + + pub fn toU12(self: Offset) u12 { + return switch (self) { + .Immediate => |imm_type| switch (imm_type) { + .PostIndex => |v| @intCast(u12, @bitCast(u9, v)) << 2 + 1, + .PreIndex => |v| @intCast(u12, @bitCast(u9, v)) << 2 + 3, + .Unsigned => |v| v, + }, + .Register => |r| switch (r.shift) { + .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, + .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, + .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, + .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, + }, + }; + } + + pub fn imm(offset: u12) Offset { + return Offset{ + .Immediate = .{ .Unsigned = offset }, + }; + } + + pub fn imm_post_index(offset: i9) Offset { + return Offset{ + .Immediate = .{ .PostIndex = offset }, + }; + } + + pub fn imm_pre_index(offset: i9) Offset { + return Offset{ + .Immediate = .{ .PreIndex = offset }, + }; + } + + pub fn reg(rm: Register) Offset { + return Offset{ + .Register = .{ + .rm = rm.id(), + .shift = .{ + .Lsl = 0, + }, + }, + }; + } + + pub fn reg_uxtw(rm: Register, shift: u2) Offset { + assert(rm.size() == 32 and (shift == 0 or shift == 2)); + return Offset{ + .Register = .{ + .rm = rm.id(), + .shift = .{ + .Uxtw = shift, + }, + }, + }; + } + + pub fn reg_lsl(rm: Register, shift: u2) Offset { + assert(rm.size() == 64 and (shift == 0 or shift == 3)); + return Offset{ + .Register = .{ + .rm = rm.id(), + .shift = .{ + .Lsl = shift, + }, + }, + }; + } + + pub fn reg_sxtw(rm: Register, shift: u2) Offset { + assert(rm.size() == 32 and (shift == 0 or shift == 2)); + return Offset{ + .Register = .{ + .rm = rm.id(), + .shift = .{ + .Sxtw = shift, + }, + }, + }; + } + + pub fn reg_sxtx(rm: Register, shift: u2) Offset { + assert(rm.size() == 64 and (shift == 0 or shift == 3)); + return Offset{ + .Register = .{ + .rm = rm.id(), + .shift = .{ + .Sxtx = shift, + }, + }, + }; + } + }; + // Helper functions for assembly syntax functions fn moveWideImmediate( @@ -270,6 +404,69 @@ pub const Instruction = union(enum) { } } + fn loadRegister(rt: Register, rn: Register, offset: Offset) Instruction { + const off = offset.toU12(); + const op1: u2 = blk: { + switch (offset) { + .Immediate => |imm| switch (imm) { + .Unsigned => break :blk 0b01, + else => {}, + }, + else => {}, + } + break :blk 0b00; + }; + switch (rt.size()) { + 32 => { + return Instruction{ + .LoadRegister = .{ + .rt = rt.id(), + .rn = rn.id(), + .offset = offset.toU12(), + .op1 = op1, + .size = 0b10, + }, + }; + }, + 64 => { + return Instruction{ + .LoadRegister = .{ + .rt = rt.id(), + .rn = rn.id(), + .offset = offset.toU12(), + .op1 = op1, + .size = 0b11, + }, + }; + }, + else => unreachable, // unexpected register size + } + } + + fn loadLiteral(rt: Register, imm19: u19) Instruction { + switch (rt.size()) { + 32 => { + return Instruction{ + .LoadLiteral = .{ + .rt = rt.id(), + .imm19 = imm19, + .opc = 0b00, + }, + }; + }, + 64 => { + return Instruction{ + .LoadLiteral = .{ + .rt = rt.id(), + .imm19 = imm19, + .opc = 0b01, + }, + }; + }, + else => unreachable, // unexpected register size + } + } + fn exceptionGeneration( opc: u3, op2: u3, @@ -332,6 +529,21 @@ pub const Instruction = union(enum) { return moveWideImmediate(0b11, rd, imm16, shift); } + // Load register + + pub const LdrArgs = struct { + rn: ?Register = null, + offset: Offset = Offset.none, + literal: ?u19 = null, + }; + pub fn ldr(rt: Register, args: LdrArgs) Instruction { + if (args.rn) |rn| { + return loadRegister(rt, rn, args.offset); + } else { + return loadLiteral(rt, args.literal.?); + } + } + // Exception generation pub fn svc(imm16: u16) Instruction { @@ -379,6 +591,10 @@ pub const Instruction = union(enum) { } }; +test "" { + testing.refAllDecls(@This()); +} + test "serialize instructions" { const Testcase = struct { inst: Instruction, @@ -426,6 +642,18 @@ test "serialize instructions" { .inst = Instruction.bl(0x10), .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100, }, + .{ // ldr x2, [x1] + .inst = Instruction.ldr(.x2, .{ .rn = .x1 }), + .expected = 0b11_111_0_01_01_000000000000_00001_00010, + }, + .{ // ldr x2, [x1], (x3) + .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.Offset.reg(.x3) }), + .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, + }, + .{ // ldr x2, label + .inst = Instruction.ldr(.x2, .{ .literal = 0x1 }), + .expected = 0b01_011_0_00_0000000000000000001_00010, + }, }; for (testcases) |case| { -- cgit v1.2.3