authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2020-10-28 14:39:55+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-11-11 14:34:53+01:00
log5ad501c00b59205154caeec95685351ee613ea5e
tree2e814909e0f39fae473fbebb580de46096092e52
parentc9dc30daf7b71c583963fd7564acf9ba299d9119

stage2 aarch64: add codegen/aarch64.zig


2 files changed, 244 insertions(+), 3 deletions(-)

src/codegen.zig+4-3
......@@ -83,9 +83,9 @@ pub fn generateSymbol(
8383 .wasm64 => unreachable, // has its own code path
8484 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, debug_output),
8585 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
86 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, debug_output),
87 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, debug_output),
88 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, debug_output),
86 .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, debug_output),
87 .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, debug_output),
88 .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, debug_output),
8989 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, debug_output),
9090 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, debug_output),
9191 //.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 {
30073007 .riscv64 => @import("codegen/riscv64.zig"),
30083008 .spu_2 => @import("codegen/spu-mk2.zig"),
30093009 .arm, .armeb => @import("codegen/arm.zig"),
3010 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig"),
30103011 else => struct {
30113012 pub const Register = enum {
30123013 dummy,
src/codegen/aarch64.zig created+240
......@@ -0,0 +1,240 @@
1const std = @import("std");
2const DW = std.dwarf;
3const testing = std.testing;
4
5// zig fmt: off
6
7/// General purpose registers in the AArch64 instruction set
8pub const Register = enum(u6) {
9 // 64-bit registers
10 x0, x1, x2, x3, x4, x5, x6, x7,
11 x8, x9, x10, x11, x12, x13, x14, x15,
12 x16, x17, x18, x19, x20, x21, x22, x23,
13 x24, x25, x26, x27, x28, x29, x30, xzr,
14
15 // 32-bit registers
16 w0, w1, w2, w3, w4, w5, w6, w7,
17 w8, w9, w10, w11, w12, w13, w14, w15,
18 w16, w17, w18, w19, w20, w21, w22, w23,
19 w24, w25, w26, w27, w28, w29, w30, wzr,
20
21 pub fn id(self: Register) u5 {
22 return @truncate(u5, @enumToInt(self));
23 }
24
25 /// Returns the bit-width of the register.
26 pub fn size(self: Register) u7 {
27 return switch (@enumToInt(self)) {
28 0...31 => 64,
29 32...63 => 32,
30 };
31 }
32
33 /// Convert from any register to its 64 bit alias.
34 pub fn to64(self: Register) Register {
35 return @intToEnum(Register, self.id());
36 }
37
38 /// Convert from any register to its 32 bit alias.
39 pub fn to32(self: Register) Register {
40 return @intToEnum(Register, @as(u6, self.id()) + 32);
41 }
42
43 /// Returns the index into `callee_preserved_regs`.
44 pub fn allocIndex(self: Register) ?u4 {
45 inline for (callee_preserved_regs) |cpreg, i| {
46 if (self.id() == cpreg.id()) return i;
47 }
48 return null;
49 }
50
51 pub fn dwarfLocOp(self: Register) u8 {
52 return @as(u8, self.id()) + DW.OP_reg0;
53 }
54};
55
56// zig fmt: on
57
58pub const callee_preserved_regs = [_]Register{
59 .x19, .x20, .x21, .x22, .x23,
60 .x24, .x25, .x26, .x27, .x28,
61};
62
63test "Register.id" {
64 testing.expectEqual(@as(u5, 0), Register.x0.id());
65 testing.expectEqual(@as(u5, 0), Register.w0.id());
66
67 testing.expectEqual(@as(u5, 31), Register.xzr.id());
68 testing.expectEqual(@as(u5, 31), Register.wzr.id());
69}
70
71test "Register.size" {
72 testing.expectEqual(@as(u7, 64), Register.x19.size());
73 testing.expectEqual(@as(u7, 32), Register.w3.size());
74}
75
76test "Register.to64/to32" {
77 testing.expectEqual(Register.x0, Register.w0.to64());
78 testing.expectEqual(Register.x0, Register.x0.to64());
79
80 testing.expectEqual(Register.w3, Register.w3.to32());
81 testing.expectEqual(Register.w3, Register.x3.to32());
82}
83
84// zig fmt: off
85
86/// Scalar floating point registers in the aarch64 instruction set
87pub const FloatingPointRegister = enum(u8) {
88 // 128-bit registers
89 q0, q1, q2, q3, q4, q5, q6, q7,
90 q8, q9, q10, q11, q12, q13, q14, q15,
91 q16, q17, q18, q19, q20, q21, q22, q23,
92 q24, q25, q26, q27, q28, q29, q30, q31,
93
94 // 64-bit registers
95 d0, d1, d2, d3, d4, d5, d6, d7,
96 d8, d9, d10, d11, d12, d13, d14, d15,
97 d16, d17, d18, d19, d20, d21, d22, d23,
98 d24, d25, d26, d27, d28, d29, d30, d31,
99
100 // 32-bit registers
101 s0, s1, s2, s3, s4, s5, s6, s7,
102 s8, s9, s10, s11, s12, s13, s14, s15,
103 s16, s17, s18, s19, s20, s21, s22, s23,
104 s24, s25, s26, s27, s28, s29, s30, s31,
105
106 // 16-bit registers
107 h0, h1, h2, h3, h4, h5, h6, h7,
108 h8, h9, h10, h11, h12, h13, h14, h15,
109 h16, h17, h18, h19, h20, h21, h22, h23,
110 h24, h25, h26, h27, h28, h29, h30, h31,
111
112 // 8-bit registers
113 b0, b1, b2, b3, b4, b5, b6, b7,
114 b8, b9, b10, b11, b12, b13, b14, b15,
115 b16, b17, b18, b19, b20, b21, b22, b23,
116 b24, b25, b26, b27, b28, b29, b30, b31,
117
118 pub fn id(self: FloatingPointRegister) u5 {
119 return @truncate(u5, @enumToInt(self));
120 }
121
122 /// Returns the bit-width of the register.
123 pub fn size(self: FloatingPointRegister) u8 {
124 return switch (@enumToInt(self)) {
125 0...31 => 128,
126 32...63 => 64,
127 64...95 => 32,
128 96...127 => 16,
129 128...159 => 8,
130 else => unreachable,
131 };
132 }
133
134 /// Convert from any register to its 128 bit alias.
135 pub fn to128(self: FloatingPointRegister) FloatingPointRegister {
136 return @intToEnum(FloatingPointRegister, self.id());
137 }
138
139 /// Convert from any register to its 64 bit alias.
140 pub fn to64(self: FloatingPointRegister) FloatingPointRegister {
141 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32);
142 }
143
144 /// Convert from any register to its 32 bit alias.
145 pub fn to32(self: FloatingPointRegister) FloatingPointRegister {
146 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64);
147 }
148
149 /// Convert from any register to its 16 bit alias.
150 pub fn to16(self: FloatingPointRegister) FloatingPointRegister {
151 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96);
152 }
153
154 /// Convert from any register to its 8 bit alias.
155 pub fn to8(self: FloatingPointRegister) FloatingPointRegister {
156 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128);
157 }
158};
159
160// zig fmt: on
161
162test "FloatingPointRegister.id" {
163 testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id());
164 testing.expectEqual(@as(u5, 0), FloatingPointRegister.h0.id());
165 testing.expectEqual(@as(u5, 0), FloatingPointRegister.s0.id());
166 testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.id());
167 testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.id());
168
169 testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id());
170 testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id());
171}
172
173test "FloatingPointRegister.size" {
174 testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size());
175 testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.size());
176 testing.expectEqual(@as(u8, 32), FloatingPointRegister.s3.size());
177 testing.expectEqual(@as(u8, 16), FloatingPointRegister.h4.size());
178 testing.expectEqual(@as(u8, 8), FloatingPointRegister.b5.size());
179}
180
181test "FloatingPointRegister.toX" {
182 testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128());
183 testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128());
184 testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128());
185
186 testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64());
187 testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32());
188 testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16());
189 testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8());
190}
191
192/// Represents an instruction in the AArch64 instruction set
193pub const Instruction = union(enum) {
194 SupervisorCall: packed struct {
195 fixed_1: u5 = 0b00001,
196 imm16: u16,
197 fixed_2: u11 = 0b11010100000,
198 },
199
200 pub fn toU32(self: Instruction) u32 {
201 return switch (self) {
202 .SupervisorCall => |v| @bitCast(u32, v),
203 };
204 }
205
206 // Helper functions for assembly syntax functions
207
208 fn supervisorCall(imm16: u16) Instruction {
209 return Instruction{
210 .SupervisorCall = .{
211 .imm16 = imm16,
212 },
213 };
214 }
215
216 // Supervisor Call
217
218 fn svc(imm16: u16) Instruction {
219 return supervisorCall(imm16);
220 }
221};
222
223test "serialize instructions" {
224 const Testcase = struct {
225 inst: Instruction,
226 expected: u32,
227 };
228
229 const testcases = [_]Testcase{
230 .{ // svc #0
231 .inst = Instruction.svc(0),
232 .expected = 0b1101_0100_000_0000000000000000_00001,
233 },
234 };
235
236 for (testcases) |case| {
237 const actual = case.inst.toU32();
238 testing.expectEqual(case.expected, actual);
239 }
240}