aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2020-11-10 18:43:53 +0100
committerJakub Konka <kubkon@jakubkonka.com>2020-11-11 14:34:53 +0100
commitf1960302d115cf42554481871d22393998b87338 (patch)
tree26da75fea4be8e3b877d4e2145520e00c4ede207 /src/codegen
parentca0016a225e7ad96d286923594d4077dbd215f0d (diff)
downloadzig-f1960302d115cf42554481871d22393998b87338.tar.gz
zig-f1960302d115cf42554481871d22393998b87338.zip
stage2 aarch64: add ldr instruction + smoke tests
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/aarch64.zig228
1 files changed, 228 insertions, 0 deletions
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| {