authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2020-08-19 20:51:56+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2020-08-23 22:27:46+02:00
logf31cee5393bbfd29883a7b253cd518db145b8b19
tree34fcc9fe63e6204977d200c3da0bded10615de98
parent4e63cae369ec6e333e4aecb3d749bedff6484ee3

Start working on stage2 ARM backend

- add codegen/arm.zig with some basic functionality (load/store, data processing, branching, software interrupts)

2 files changed, 567 insertions(+), 0 deletions(-)

src-self-hosted/codegen.zig+1
...@@ -2372,6 +2372,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2372,6 +2372,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2372 .x86_64 => @import("codegen/x86_64.zig"),2372 .x86_64 => @import("codegen/x86_64.zig"),
2373 .riscv64 => @import("codegen/riscv64.zig"),2373 .riscv64 => @import("codegen/riscv64.zig"),
2374 .spu_2 => @import("codegen/spu-mk2.zig"),2374 .spu_2 => @import("codegen/spu-mk2.zig"),
2375 .arm => @import("codegen/arm.zig"),
2375 else => struct {2376 else => struct {
2376 pub const Register = enum {2377 pub const Register = enum {
2377 dummy,2378 dummy,
src-self-hosted/codegen/arm.zig created+566
...@@ -0,0 +1,566 @@
1const std = @import("std");
2const testing = std.testing;
3
4/// The condition field specifies the flags neccessary for an
5/// Instruction to be executed
6pub const Condition = enum(u4) {
7 /// equal
8 eq,
9 /// not equal
10 ne,
11 /// unsigned higher or same
12 cs,
13 /// unsigned lower
14 cc,
15 /// negative
16 mi,
17 /// positive or zero
18 pl,
19 /// overflow
20 vs,
21 /// no overflow
22 vc,
23 /// unsigned higer
24 hi,
25 /// unsigned lower or same
26 ls,
27 /// greater or equal
28 ge,
29 /// less than
30 lt,
31 /// greater than
32 gt,
33 /// less than or equal
34 le,
35 /// always
36 al,
37};
38
39/// Represents a register in the ARM instruction set architecture
40pub const Register = enum(u5) {
41 r0,
42 r1,
43 r2,
44 r3,
45 r4,
46 r5,
47 r6,
48 r7,
49 r8,
50 r9,
51 r10,
52 r11,
53 r12,
54 r13,
55 r14,
56 r15,
57
58 /// Argument / result / scratch register 1
59 a1,
60 /// Argument / result / scratch register 2
61 a2,
62 /// Argument / scratch register 3
63 a3,
64 /// Argument / scratch register 4
65 a4,
66 /// Variable-register 1
67 v1,
68 /// Variable-register 2
69 v2,
70 /// Variable-register 3
71 v3,
72 /// Variable-register 4
73 v4,
74 /// Variable-register 5
75 v5,
76 /// Platform register
77 v6,
78 /// Variable-register 7
79 v7,
80 /// Frame pointer or Variable-register 8
81 fp,
82 /// Intra-Procedure-call scratch register
83 ip,
84 /// Stack pointer
85 sp,
86 /// Link register
87 lr,
88 /// Program counter
89 pc,
90
91 /// Returns the unique 4-bit ID of this register which is used in
92 /// the machine code
93 pub fn id(self: Register) u4 {
94 return @truncate(u4, @enumToInt(self));
95 }
96};
97
98test "Register.id" {
99 testing.expectEqual(@as(u4, 15), Register.r15.id());
100 testing.expectEqual(@as(u4, 15), Register.pc.id());
101}
102
103pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
104pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
105pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
106
107/// Represents an instruction in the ARM instruction set architecture
108pub const Instruction = union(enum) {
109 DataProcessing: packed struct {
110 // Note to self: The order of the fields top-to-bottom is
111 // right-to-left in the actual 32-bit int representation
112 op2: u12,
113 rd: u4,
114 rn: u4,
115 s: u1,
116 opcode: u4,
117 i: u1,
118 fixed: u2 = 0b00,
119 cond: u4,
120 },
121 SingleDataTransfer: packed struct {
122 offset: u12,
123 rd: u4,
124 rn: u4,
125 l: u1,
126 w: u1,
127 b: u1,
128 u: u1,
129 p: u1,
130 i: u1,
131 fixed: u2 = 0b01,
132 cond: u4,
133 },
134 Branch: packed struct {
135 offset: u24,
136 link: u1,
137 fixed: u3 = 0b101,
138 cond: u4,
139 },
140 BranchExchange: packed struct {
141 rn: u4,
142 fixed_1: u1 = 0b1,
143 link: u1,
144 fixed_2: u22 = 0b0001_0010_1111_1111_1111_00,
145 cond: u4,
146 },
147 SoftwareInterrupt: packed struct {
148 comment: u24,
149 fixed: u4 = 0b1111,
150 cond: u4,
151 },
152
153 /// Represents the possible operations which can be performed by a
154 /// DataProcessing instruction
155 const Opcode = enum(u4) {
156 // Rd := Op1 AND Op2
157 @"and",
158 // Rd := Op1 EOR Op2
159 eor,
160 // Rd := Op1 - Op2
161 sub,
162 // Rd := Op2 - Op1
163 rsb,
164 // Rd := Op1 + Op2
165 add,
166 // Rd := Op1 + Op2 + C
167 adc,
168 // Rd := Op1 - Op2 + C - 1
169 sbc,
170 // Rd := Op2 - Op1 + C - 1
171 rsc,
172 // set condition codes on Op1 AND Op2
173 tst,
174 // set condition codes on Op1 EOR Op2
175 teq,
176 // set condition codes on Op1 - Op2
177 cmp,
178 // set condition codes on Op1 + Op2
179 cmn,
180 // Rd := Op1 OR Op2
181 orr,
182 // Rd := Op2
183 mov,
184 // Rd := Op1 AND NOT Op2
185 bic,
186 // Rd := NOT Op2
187 mvn,
188 };
189
190 /// Represents the second operand to a data processing instruction
191 /// which can either be content from a register or an immediate
192 /// value
193 pub const Operand = union(enum) {
194 Register: packed struct {
195 rm: u4,
196 shift: u8,
197 },
198 Immediate: packed struct {
199 imm: u8,
200 rotate: u4,
201 },
202
203 /// Represents multiple ways a register can be shifted. A
204 /// register can be shifted by a specific immediate value or
205 /// by the contents of another register
206 pub const Shift = union(enum) {
207 Immediate: packed struct {
208 fixed: u1 = 0b0,
209 typ: u2,
210 amount: u5,
211 },
212 Register: packed struct {
213 fixed_1: u1 = 0b1,
214 typ: u2,
215 fixed_2: u1 = 0b0,
216 rs: u4,
217 },
218
219 const Type = enum(u2) {
220 LogicalLeft,
221 LogicalRight,
222 ArithmeticRight,
223 RotateRight,
224 };
225
226 const none = Shift{
227 .Immediate = .{
228 .amount = 0,
229 .typ = 0,
230 },
231 };
232
233 pub fn toU8(self: Shift) u8 {
234 return switch (self) {
235 .Register => |v| @bitCast(u8, v),
236 .Immediate => |v| @bitCast(u8, v),
237 };
238 }
239
240 pub fn reg(rs: Register, typ: Type) Shift {
241 return Shift{
242 .Register = .{
243 .rs = rs.id(),
244 .typ = @enumToInt(typ),
245 },
246 };
247 }
248
249 pub fn imm(amount: u5, typ: Type) Shift {
250 return Shift{
251 .Immediate = .{
252 .amount = amount,
253 .typ = @enumToInt(typ),
254 },
255 };
256 }
257 };
258
259 pub fn toU12(self: Operand) u12 {
260 return switch (self) {
261 .Register => |v| @bitCast(u12, v),
262 .Immediate => |v| @bitCast(u12, v),
263 };
264 }
265
266 pub fn reg(rm: Register, shift: Shift) Operand {
267 return Operand{
268 .Register = .{
269 .rm = rm.id(),
270 .shift = shift.toU8(),
271 },
272 };
273 }
274
275 pub fn imm(immediate: u8, rotate: u4) Operand {
276 return Operand{
277 .Immediate = .{
278 .imm = immediate,
279 .rotate = rotate,
280 },
281 };
282 }
283 };
284
285 /// Represents the offset operand of a load or store
286 /// instruction. Data can be loaded from memory with either an
287 /// immediate offset or an offset that is stored in some register.
288 pub const Offset = union(enum) {
289 Immediate: u12,
290 Register: packed struct {
291 rm: u4,
292 shift: u8,
293 },
294
295 pub const none = Offset{
296 .Immediate = 0,
297 };
298
299 pub fn toU12(self: Offset) u12 {
300 return switch (self) {
301 .Register => |v| @bitCast(u12, v),
302 .Immediate => |v| v,
303 };
304 }
305
306 pub fn reg(rm: Register, shift: u8) Offset {
307 return Offset{
308 .Register = .{
309 .rm = rm.id(),
310 .shift = shift,
311 },
312 };
313 }
314
315 pub fn imm(immediate: u8) Offset {
316 return Offset{
317 .Immediate = immediate,
318 };
319 }
320 };
321
322 pub fn toU32(self: Instruction) u32 {
323 return switch (self) {
324 .DataProcessing => |v| @bitCast(u32, v),
325 .SingleDataTransfer => |v| @bitCast(u32, v),
326 .Branch => |v| @bitCast(u32, v),
327 .BranchExchange => |v| @bitCast(u32, v),
328 .SoftwareInterrupt => |v| @bitCast(u32, v),
329 };
330 }
331
332 // Helper functions for the "real" functions below
333
334 fn dataProcessing(
335 cond: Condition,
336 opcode: Opcode,
337 s: u1,
338 rn: Register,
339 rd: Register,
340 op2: Operand,
341 ) Instruction {
342 return Instruction{
343 .DataProcessing = .{
344 .cond = @enumToInt(cond),
345 .i = if (op2 == .Immediate) 1 else 0,
346 .opcode = @enumToInt(opcode),
347 .s = s,
348 .rn = rn.id(),
349 .rd = rd.id(),
350 .op2 = op2.toU12(),
351 },
352 };
353 }
354
355 fn singleDataTransfer(
356 cond: Condition,
357 rd: Register,
358 rn: Register,
359 offset: Offset,
360 pre_post: u1,
361 up_down: u1,
362 byte_word: u1,
363 writeback: u1,
364 load_store: u1,
365 ) Instruction {
366 return Instruction{
367 .SingleDataTransfer = .{
368 .cond = @enumToInt(cond),
369 .rn = rn.id(),
370 .rd = rd.id(),
371 .offset = offset.toU12(),
372 .l = load_store,
373 .w = writeback,
374 .b = byte_word,
375 .u = up_down,
376 .p = pre_post,
377 .i = if (offset == .Immediate) 1 else 0,
378 },
379 };
380 }
381
382 fn branch(cond: Condition, offset: i24, link: u1) Instruction {
383 return Instruction{
384 .Branch = .{
385 .cond = @enumToInt(cond),
386 .link = link,
387 .offset = @bitCast(u24, offset),
388 },
389 };
390 }
391
392 fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction {
393 return Instruction{
394 .BranchExchange = .{
395 .cond = @enumToInt(cond),
396 .link = link,
397 .rn = rn.id(),
398 },
399 };
400 }
401
402 fn softwareInterrupt(cond: Condition, comment: u24) Instruction {
403 return Instruction{
404 .SoftwareInterrupt = .{
405 .cond = @enumToInt(cond),
406 .comment = comment,
407 },
408 };
409 }
410
411 // Public functions replicating assembler syntax as closely as
412 // possible
413
414 // Data processing
415
416 pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
417 return dataProcessing(cond, .@"and", s, rd, rn, op2);
418 }
419
420 pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
421 return dataProcessing(cond, .eor, s, rd, rn, op2);
422 }
423
424 pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
425 return dataProcessing(cond, .sub, s, rd, rn, op2);
426 }
427
428 pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
429 return dataProcessing(cond, .rsb, s, rd, rn, op2);
430 }
431
432 pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
433 return dataProcessing(cond, .add, s, rd, rn, op2);
434 }
435
436 pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
437 return dataProcessing(cond, .adc, s, rd, rn, op2);
438 }
439
440 pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
441 return dataProcessing(cond, .sbc, s, rd, rn, op2);
442 }
443
444 pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
445 return dataProcessing(cond, .rsc, s, rd, rn, op2);
446 }
447
448 pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
449 return dataProcessing(cond, .tst, 1, .r0, rn, op2);
450 }
451
452 pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction {
453 return dataProcessing(cond, .teq, 1, .r0, rn, op2);
454 }
455
456 pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction {
457 return dataProcessing(cond, .cmp, 1, .r0, rn, op2);
458 }
459
460 pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction {
461 return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
462 }
463
464 pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
465 return dataProcessing(cond, .orr, s, rd, rn, op2);
466 }
467
468 pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
469 return dataProcessing(cond, .mov, s, rd, .r0, op2);
470 }
471
472 pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
473 return dataProcessing(cond, .bic, s, rd, rn, op2);
474 }
475
476 pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
477 return dataProcessing(cond, .mvn, s, rd, .r0, op2);
478 }
479
480 // Single data transfer
481
482 pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
483 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1);
484 }
485
486 pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
487 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0);
488 }
489
490 // Branch
491
492 pub fn b(cond: Condition, offset: i24) Instruction {
493 return branch(cond, offset, 0);
494 }
495
496 pub fn bl(cond: Condition, offset: i24) Instruction {
497 return branch(cond, offset, 1);
498 }
499
500 // Branch and exchange
501
502 pub fn bx(cond: Condition, rn: Register) Instruction {
503 return branchExchange(cond, rn, 0);
504 }
505
506 pub fn blx(cond: Condition, rn: Register) Instruction {
507 return branchExchange(cond, rn, 1);
508 }
509
510 // Software interrupt
511
512 pub fn swi(cond: Condition, comment: u24) Instruction {
513 return softwareInterrupt(cond, comment);
514 }
515};
516
517test "serialize instructions" {
518 const Testcase = struct {
519 inst: Instruction,
520 expected: u32,
521 };
522
523 const testcases = [_]Testcase{
524 .{ // add r0, r0, r0
525 .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
526 .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
527 },
528 .{ // mov r4, r2
529 .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
530 .expected = 0b1110_00_0_1101_0_0100_0000_00000000_0010,
531 },
532 .{ // mov r0, #42
533 .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)),
534 .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
535 },
536 .{ // ldr r0, [r2, #42]
537 .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),
538 .expected = 0b1110_01_1_1_1_0_0_1_0010_0000_000000101010,
539 },
540 .{ // str r0, [r3]
541 .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none),
542 .expected = 0b1110_01_1_1_1_0_0_0_0011_0000_000000000000,
543 },
544 .{ // b #12
545 .inst = Instruction.b(.al, 12),
546 .expected = 0b1110_101_0_0000_0000_0000_0000_0000_1100,
547 },
548 .{ // bl #-4
549 .inst = Instruction.bl(.al, -4),
550 .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1100,
551 },
552 .{ // bx lr
553 .inst = Instruction.bx(.al, .lr),
554 .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110,
555 },
556 .{ // swi #0
557 .inst = Instruction.swi(.al, 0),
558 .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000,
559 },
560 };
561
562 for (testcases) |case| {
563 const actual = case.inst.toU32();
564 testing.expectEqual(case.expected, actual);
565 }
566}