From 5ad501c00b59205154caeec95685351ee613ea5e Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Wed, 28 Oct 2020 14:39:55 +0100 Subject: [PATCH] stage2 aarch64: add codegen/aarch64.zig --- src/codegen.zig | 7 +- src/codegen/aarch64.zig | 240 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+), 3 deletions(-) create mode 100644 src/codegen/aarch64.zig diff --git a/src/codegen.zig b/src/codegen.zig index ce509eb952..6962dcf8e4 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -83,9 +83,9 @@ pub fn generateSymbol( .wasm64 => unreachable, // has its own code path .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, debug_output), .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, debug_output), + .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, debug_output), + .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, debug_output), + .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, debug_output), //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, debug_output), //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, debug_output), //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, debug_output), @@ -3007,6 +3007,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .riscv64 => @import("codegen/riscv64.zig"), .spu_2 => @import("codegen/spu-mk2.zig"), .arm, .armeb => @import("codegen/arm.zig"), + .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig"), else => struct { pub const Register = enum { dummy, diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig new file mode 100644 index 0000000000..2ca9a71292 --- /dev/null +++ b/src/codegen/aarch64.zig @@ -0,0 +1,240 @@ +const std = @import("std"); +const DW = std.dwarf; +const testing = std.testing; + +// zig fmt: off + +/// General purpose registers in the AArch64 instruction set +pub const Register = enum(u6) { + // 64-bit registers + x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x13, x14, x15, + x16, x17, x18, x19, x20, x21, x22, x23, + x24, x25, x26, x27, x28, x29, x30, xzr, + + // 32-bit registers + w0, w1, w2, w3, w4, w5, w6, w7, + w8, w9, w10, w11, w12, w13, w14, w15, + w16, w17, w18, w19, w20, w21, w22, w23, + w24, w25, w26, w27, w28, w29, w30, wzr, + + pub fn id(self: Register) u5 { + return @truncate(u5, @enumToInt(self)); + } + + /// Returns the bit-width of the register. + pub fn size(self: Register) u7 { + return switch (@enumToInt(self)) { + 0...31 => 64, + 32...63 => 32, + }; + } + + /// Convert from any register to its 64 bit alias. + pub fn to64(self: Register) Register { + return @intToEnum(Register, self.id()); + } + + /// Convert from any register to its 32 bit alias. + pub fn to32(self: Register) Register { + return @intToEnum(Register, @as(u6, self.id()) + 32); + } + + /// Returns the index into `callee_preserved_regs`. + pub fn allocIndex(self: Register) ?u4 { + inline for (callee_preserved_regs) |cpreg, i| { + if (self.id() == cpreg.id()) return i; + } + return null; + } + + pub fn dwarfLocOp(self: Register) u8 { + return @as(u8, self.id()) + DW.OP_reg0; + } +}; + +// zig fmt: on + +pub const callee_preserved_regs = [_]Register{ + .x19, .x20, .x21, .x22, .x23, + .x24, .x25, .x26, .x27, .x28, +}; + +test "Register.id" { + testing.expectEqual(@as(u5, 0), Register.x0.id()); + testing.expectEqual(@as(u5, 0), Register.w0.id()); + + testing.expectEqual(@as(u5, 31), Register.xzr.id()); + testing.expectEqual(@as(u5, 31), Register.wzr.id()); +} + +test "Register.size" { + testing.expectEqual(@as(u7, 64), Register.x19.size()); + testing.expectEqual(@as(u7, 32), Register.w3.size()); +} + +test "Register.to64/to32" { + testing.expectEqual(Register.x0, Register.w0.to64()); + testing.expectEqual(Register.x0, Register.x0.to64()); + + testing.expectEqual(Register.w3, Register.w3.to32()); + testing.expectEqual(Register.w3, Register.x3.to32()); +} + +// zig fmt: off + +/// Scalar floating point registers in the aarch64 instruction set +pub const FloatingPointRegister = enum(u8) { + // 128-bit registers + q0, q1, q2, q3, q4, q5, q6, q7, + q8, q9, q10, q11, q12, q13, q14, q15, + q16, q17, q18, q19, q20, q21, q22, q23, + q24, q25, q26, q27, q28, q29, q30, q31, + + // 64-bit registers + d0, d1, d2, d3, d4, d5, d6, d7, + d8, d9, d10, d11, d12, d13, d14, d15, + d16, d17, d18, d19, d20, d21, d22, d23, + d24, d25, d26, d27, d28, d29, d30, d31, + + // 32-bit registers + s0, s1, s2, s3, s4, s5, s6, s7, + s8, s9, s10, s11, s12, s13, s14, s15, + s16, s17, s18, s19, s20, s21, s22, s23, + s24, s25, s26, s27, s28, s29, s30, s31, + + // 16-bit registers + h0, h1, h2, h3, h4, h5, h6, h7, + h8, h9, h10, h11, h12, h13, h14, h15, + h16, h17, h18, h19, h20, h21, h22, h23, + h24, h25, h26, h27, h28, h29, h30, h31, + + // 8-bit registers + b0, b1, b2, b3, b4, b5, b6, b7, + b8, b9, b10, b11, b12, b13, b14, b15, + b16, b17, b18, b19, b20, b21, b22, b23, + b24, b25, b26, b27, b28, b29, b30, b31, + + pub fn id(self: FloatingPointRegister) u5 { + return @truncate(u5, @enumToInt(self)); + } + + /// Returns the bit-width of the register. + pub fn size(self: FloatingPointRegister) u8 { + return switch (@enumToInt(self)) { + 0...31 => 128, + 32...63 => 64, + 64...95 => 32, + 96...127 => 16, + 128...159 => 8, + else => unreachable, + }; + } + + /// Convert from any register to its 128 bit alias. + pub fn to128(self: FloatingPointRegister) FloatingPointRegister { + return @intToEnum(FloatingPointRegister, self.id()); + } + + /// Convert from any register to its 64 bit alias. + pub fn to64(self: FloatingPointRegister) FloatingPointRegister { + return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32); + } + + /// Convert from any register to its 32 bit alias. + pub fn to32(self: FloatingPointRegister) FloatingPointRegister { + return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64); + } + + /// Convert from any register to its 16 bit alias. + pub fn to16(self: FloatingPointRegister) FloatingPointRegister { + return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96); + } + + /// Convert from any register to its 8 bit alias. + pub fn to8(self: FloatingPointRegister) FloatingPointRegister { + return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128); + } +}; + +// zig fmt: on + +test "FloatingPointRegister.id" { + testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id()); + testing.expectEqual(@as(u5, 0), FloatingPointRegister.h0.id()); + testing.expectEqual(@as(u5, 0), FloatingPointRegister.s0.id()); + testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.id()); + testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.id()); + + testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id()); + testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id()); +} + +test "FloatingPointRegister.size" { + testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size()); + testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.size()); + testing.expectEqual(@as(u8, 32), FloatingPointRegister.s3.size()); + testing.expectEqual(@as(u8, 16), FloatingPointRegister.h4.size()); + testing.expectEqual(@as(u8, 8), FloatingPointRegister.b5.size()); +} + +test "FloatingPointRegister.toX" { + testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128()); + testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128()); + testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128()); + + testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64()); + testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32()); + testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16()); + testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8()); +} + +/// Represents an instruction in the AArch64 instruction set +pub const Instruction = union(enum) { + SupervisorCall: packed struct { + fixed_1: u5 = 0b00001, + imm16: u16, + fixed_2: u11 = 0b11010100000, + }, + + pub fn toU32(self: Instruction) u32 { + return switch (self) { + .SupervisorCall => |v| @bitCast(u32, v), + }; + } + + // Helper functions for assembly syntax functions + + fn supervisorCall(imm16: u16) Instruction { + return Instruction{ + .SupervisorCall = .{ + .imm16 = imm16, + }, + }; + } + + // Supervisor Call + + fn svc(imm16: u16) Instruction { + return supervisorCall(imm16); + } +}; + +test "serialize instructions" { + const Testcase = struct { + inst: Instruction, + expected: u32, + }; + + const testcases = [_]Testcase{ + .{ // svc #0 + .inst = Instruction.svc(0), + .expected = 0b1101_0100_000_0000000000000000_00001, + }, + }; + + for (testcases) |case| { + const actual = case.inst.toU32(); + testing.expectEqual(case.expected, actual); + } +}