authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-09-23 22:47:12+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-24 13:47:59-04:00
log8f58e2d77951cdb046e365394c5f02c9b3a93a4f
tree3530477de6cda55258f5967d3fba0b352938942f
parent664941bf14cad3e62b453f83153ca4b65606707b

stage2 codegen: move bit definitions to src/arch


14 files changed, 3978 insertions(+), 3978 deletions(-)

CMakeLists.txt+4-4
......@@ -551,18 +551,18 @@ set(ZIG_STAGE2_SOURCES
551551 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"
552552 "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig"
553553 "${CMAKE_SOURCE_DIR}/src/Zir.zig"
554 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/bits.zig"
555 "${CMAKE_SOURCE_DIR}/src/arch/arm/bits.zig"
556 "${CMAKE_SOURCE_DIR}/src/arch/riscv64/bits.zig"
557 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig"
554558 "${CMAKE_SOURCE_DIR}/src/clang.zig"
555559 "${CMAKE_SOURCE_DIR}/src/clang_options.zig"
556560 "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"
557561 "${CMAKE_SOURCE_DIR}/src/codegen.zig"
558 "${CMAKE_SOURCE_DIR}/src/codegen/aarch64.zig"
559 "${CMAKE_SOURCE_DIR}/src/codegen/arm.zig"
560562 "${CMAKE_SOURCE_DIR}/src/codegen/c.zig"
561563 "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig"
562564 "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig"
563 "${CMAKE_SOURCE_DIR}/src/codegen/riscv64.zig"
564565 "${CMAKE_SOURCE_DIR}/src/codegen/wasm.zig"
565 "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig"
566566 "${CMAKE_SOURCE_DIR}/src/glibc.zig"
567567 "${CMAKE_SOURCE_DIR}/src/introspect.zig"
568568 "${CMAKE_SOURCE_DIR}/src/libc_installation.zig"
src/arch/aarch64/bits.zig created+1230
......@@ -0,0 +1,1230 @@
1const std = @import("std");
2const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6// zig fmt: off
7
8/// General purpose registers in the AArch64 instruction set
9pub const Register = enum(u6) {
10 // 64-bit registers
11 x0, x1, x2, x3, x4, x5, x6, x7,
12 x8, x9, x10, x11, x12, x13, x14, x15,
13 x16, x17, x18, x19, x20, x21, x22, x23,
14 x24, x25, x26, x27, x28, x29, x30, xzr,
15
16 // 32-bit registers
17 w0, w1, w2, w3, w4, w5, w6, w7,
18 w8, w9, w10, w11, w12, w13, w14, w15,
19 w16, w17, w18, w19, w20, w21, w22, w23,
20 w24, w25, w26, w27, w28, w29, w30, wzr,
21
22 pub const sp = Register.xzr;
23
24 pub fn id(self: Register) u5 {
25 return @truncate(u5, @enumToInt(self));
26 }
27
28 /// Returns the bit-width of the register.
29 pub fn size(self: Register) u7 {
30 return switch (@enumToInt(self)) {
31 0...31 => 64,
32 32...63 => 32,
33 };
34 }
35
36 /// Convert from any register to its 64 bit alias.
37 pub fn to64(self: Register) Register {
38 return @intToEnum(Register, self.id());
39 }
40
41 /// Convert from any register to its 32 bit alias.
42 pub fn to32(self: Register) Register {
43 return @intToEnum(Register, @as(u6, self.id()) + 32);
44 }
45
46 /// Returns the index into `callee_preserved_regs`.
47 pub fn allocIndex(self: Register) ?u4 {
48 inline for (callee_preserved_regs) |cpreg, i| {
49 if (self.id() == cpreg.id()) return i;
50 }
51 return null;
52 }
53
54 pub fn dwarfLocOp(self: Register) u8 {
55 return @as(u8, self.id()) + DW.OP.reg0;
56 }
57};
58
59// zig fmt: on
60
61pub const callee_preserved_regs = [_]Register{
62 .x19, .x20, .x21, .x22, .x23,
63 .x24, .x25, .x26, .x27, .x28,
64};
65
66pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
67pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
68
69test "Register.id" {
70 try testing.expectEqual(@as(u5, 0), Register.x0.id());
71 try testing.expectEqual(@as(u5, 0), Register.w0.id());
72
73 try testing.expectEqual(@as(u5, 31), Register.xzr.id());
74 try testing.expectEqual(@as(u5, 31), Register.wzr.id());
75
76 try testing.expectEqual(@as(u5, 31), Register.sp.id());
77 try testing.expectEqual(@as(u5, 31), Register.sp.id());
78}
79
80test "Register.size" {
81 try testing.expectEqual(@as(u7, 64), Register.x19.size());
82 try testing.expectEqual(@as(u7, 32), Register.w3.size());
83}
84
85test "Register.to64/to32" {
86 try testing.expectEqual(Register.x0, Register.w0.to64());
87 try testing.expectEqual(Register.x0, Register.x0.to64());
88
89 try testing.expectEqual(Register.w3, Register.w3.to32());
90 try testing.expectEqual(Register.w3, Register.x3.to32());
91}
92
93// zig fmt: off
94
95/// Scalar floating point registers in the aarch64 instruction set
96pub const FloatingPointRegister = enum(u8) {
97 // 128-bit registers
98 q0, q1, q2, q3, q4, q5, q6, q7,
99 q8, q9, q10, q11, q12, q13, q14, q15,
100 q16, q17, q18, q19, q20, q21, q22, q23,
101 q24, q25, q26, q27, q28, q29, q30, q31,
102
103 // 64-bit registers
104 d0, d1, d2, d3, d4, d5, d6, d7,
105 d8, d9, d10, d11, d12, d13, d14, d15,
106 d16, d17, d18, d19, d20, d21, d22, d23,
107 d24, d25, d26, d27, d28, d29, d30, d31,
108
109 // 32-bit registers
110 s0, s1, s2, s3, s4, s5, s6, s7,
111 s8, s9, s10, s11, s12, s13, s14, s15,
112 s16, s17, s18, s19, s20, s21, s22, s23,
113 s24, s25, s26, s27, s28, s29, s30, s31,
114
115 // 16-bit registers
116 h0, h1, h2, h3, h4, h5, h6, h7,
117 h8, h9, h10, h11, h12, h13, h14, h15,
118 h16, h17, h18, h19, h20, h21, h22, h23,
119 h24, h25, h26, h27, h28, h29, h30, h31,
120
121 // 8-bit registers
122 b0, b1, b2, b3, b4, b5, b6, b7,
123 b8, b9, b10, b11, b12, b13, b14, b15,
124 b16, b17, b18, b19, b20, b21, b22, b23,
125 b24, b25, b26, b27, b28, b29, b30, b31,
126
127 pub fn id(self: FloatingPointRegister) u5 {
128 return @truncate(u5, @enumToInt(self));
129 }
130
131 /// Returns the bit-width of the register.
132 pub fn size(self: FloatingPointRegister) u8 {
133 return switch (@enumToInt(self)) {
134 0...31 => 128,
135 32...63 => 64,
136 64...95 => 32,
137 96...127 => 16,
138 128...159 => 8,
139 else => unreachable,
140 };
141 }
142
143 /// Convert from any register to its 128 bit alias.
144 pub fn to128(self: FloatingPointRegister) FloatingPointRegister {
145 return @intToEnum(FloatingPointRegister, self.id());
146 }
147
148 /// Convert from any register to its 64 bit alias.
149 pub fn to64(self: FloatingPointRegister) FloatingPointRegister {
150 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32);
151 }
152
153 /// Convert from any register to its 32 bit alias.
154 pub fn to32(self: FloatingPointRegister) FloatingPointRegister {
155 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64);
156 }
157
158 /// Convert from any register to its 16 bit alias.
159 pub fn to16(self: FloatingPointRegister) FloatingPointRegister {
160 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96);
161 }
162
163 /// Convert from any register to its 8 bit alias.
164 pub fn to8(self: FloatingPointRegister) FloatingPointRegister {
165 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128);
166 }
167};
168
169// zig fmt: on
170
171test "FloatingPointRegister.id" {
172 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id());
173 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.h0.id());
174 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.s0.id());
175 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.id());
176 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.id());
177
178 try testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id());
179 try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id());
180}
181
182test "FloatingPointRegister.size" {
183 try testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size());
184 try testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.size());
185 try testing.expectEqual(@as(u8, 32), FloatingPointRegister.s3.size());
186 try testing.expectEqual(@as(u8, 16), FloatingPointRegister.h4.size());
187 try testing.expectEqual(@as(u8, 8), FloatingPointRegister.b5.size());
188}
189
190test "FloatingPointRegister.toX" {
191 try testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128());
192 try testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128());
193 try testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128());
194
195 try testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64());
196 try testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32());
197 try testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16());
198 try testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8());
199}
200
201/// Represents an instruction in the AArch64 instruction set
202pub const Instruction = union(enum) {
203 move_wide_immediate: packed struct {
204 rd: u5,
205 imm16: u16,
206 hw: u2,
207 fixed: u6 = 0b100101,
208 opc: u2,
209 sf: u1,
210 },
211 pc_relative_address: packed struct {
212 rd: u5,
213 immhi: u19,
214 fixed: u5 = 0b10000,
215 immlo: u2,
216 op: u1,
217 },
218 load_store_register: packed struct {
219 rt: u5,
220 rn: u5,
221 offset: u12,
222 opc: u2,
223 op1: u2,
224 v: u1,
225 fixed: u3 = 0b111,
226 size: u2,
227 },
228 load_store_register_pair: packed struct {
229 rt1: u5,
230 rn: u5,
231 rt2: u5,
232 imm7: u7,
233 load: u1,
234 encoding: u2,
235 fixed: u5 = 0b101_0_0,
236 opc: u2,
237 },
238 load_literal: packed struct {
239 rt: u5,
240 imm19: u19,
241 fixed: u6 = 0b011_0_00,
242 opc: u2,
243 },
244 exception_generation: packed struct {
245 ll: u2,
246 op2: u3,
247 imm16: u16,
248 opc: u3,
249 fixed: u8 = 0b1101_0100,
250 },
251 unconditional_branch_register: packed struct {
252 op4: u5,
253 rn: u5,
254 op3: u6,
255 op2: u5,
256 opc: u4,
257 fixed: u7 = 0b1101_011,
258 },
259 unconditional_branch_immediate: packed struct {
260 imm26: u26,
261 fixed: u5 = 0b00101,
262 op: u1,
263 },
264 no_operation: packed struct {
265 fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111,
266 },
267 logical_shifted_register: packed struct {
268 rd: u5,
269 rn: u5,
270 imm6: u6,
271 rm: u5,
272 n: u1,
273 shift: u2,
274 fixed: u5 = 0b01010,
275 opc: u2,
276 sf: u1,
277 },
278 add_subtract_immediate: packed struct {
279 rd: u5,
280 rn: u5,
281 imm12: u12,
282 sh: u1,
283 fixed: u6 = 0b100010,
284 s: u1,
285 op: u1,
286 sf: u1,
287 },
288 conditional_branch: struct {
289 cond: u4,
290 o0: u1,
291 imm19: u19,
292 o1: u1,
293 fixed: u7 = 0b0101010,
294 },
295 compare_and_branch: struct {
296 rt: u5,
297 imm19: u19,
298 op: u1,
299 fixed: u6 = 0b011010,
300 sf: u1,
301 },
302
303 pub const Shift = struct {
304 shift: Type = .lsl,
305 amount: u6 = 0,
306
307 pub const Type = enum(u2) {
308 lsl,
309 lsr,
310 asr,
311 ror,
312 };
313
314 pub const none = Shift{
315 .shift = .lsl,
316 .amount = 0,
317 };
318 };
319
320 pub const Condition = enum(u4) {
321 /// Integer: Equal
322 /// Floating point: Equal
323 eq,
324 /// Integer: Not equal
325 /// Floating point: Not equal or unordered
326 ne,
327 /// Integer: Carry set
328 /// Floating point: Greater than, equal, or unordered
329 cs,
330 /// Integer: Carry clear
331 /// Floating point: Less than
332 cc,
333 /// Integer: Minus, negative
334 /// Floating point: Less than
335 mi,
336 /// Integer: Plus, positive or zero
337 /// Floating point: Greater than, equal, or unordered
338 pl,
339 /// Integer: Overflow
340 /// Floating point: Unordered
341 vs,
342 /// Integer: No overflow
343 /// Floating point: Ordered
344 vc,
345 /// Integer: Unsigned higher
346 /// Floating point: Greater than, or unordered
347 hi,
348 /// Integer: Unsigned lower or same
349 /// Floating point: Less than or equal
350 ls,
351 /// Integer: Signed greater than or equal
352 /// Floating point: Greater than or equal
353 ge,
354 /// Integer: Signed less than
355 /// Floating point: Less than, or unordered
356 lt,
357 /// Integer: Signed greater than
358 /// Floating point: Greater than
359 gt,
360 /// Integer: Signed less than or equal
361 /// Floating point: Less than, equal, or unordered
362 le,
363 /// Integer: Always
364 /// Floating point: Always
365 al,
366 /// Integer: Always
367 /// Floating point: Always
368 nv,
369 };
370
371 pub fn toU32(self: Instruction) u32 {
372 return switch (self) {
373 .move_wide_immediate => |v| @bitCast(u32, v),
374 .pc_relative_address => |v| @bitCast(u32, v),
375 .load_store_register => |v| @bitCast(u32, v),
376 .load_store_register_pair => |v| @bitCast(u32, v),
377 .load_literal => |v| @bitCast(u32, v),
378 .exception_generation => |v| @bitCast(u32, v),
379 .unconditional_branch_register => |v| @bitCast(u32, v),
380 .unconditional_branch_immediate => |v| @bitCast(u32, v),
381 .no_operation => |v| @bitCast(u32, v),
382 .logical_shifted_register => |v| @bitCast(u32, v),
383 .add_subtract_immediate => |v| @bitCast(u32, v),
384 // TODO once packed structs work, this can be refactored
385 .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25),
386 .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31),
387 };
388 }
389
390 fn moveWideImmediate(
391 opc: u2,
392 rd: Register,
393 imm16: u16,
394 shift: u6,
395 ) Instruction {
396 switch (rd.size()) {
397 32 => {
398 assert(shift % 16 == 0 and shift <= 16);
399 return Instruction{
400 .move_wide_immediate = .{
401 .rd = rd.id(),
402 .imm16 = imm16,
403 .hw = @intCast(u2, shift / 16),
404 .opc = opc,
405 .sf = 0,
406 },
407 };
408 },
409 64 => {
410 assert(shift % 16 == 0 and shift <= 48);
411 return Instruction{
412 .move_wide_immediate = .{
413 .rd = rd.id(),
414 .imm16 = imm16,
415 .hw = @intCast(u2, shift / 16),
416 .opc = opc,
417 .sf = 1,
418 },
419 };
420 },
421 else => unreachable, // unexpected register size
422 }
423 }
424
425 fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction {
426 assert(rd.size() == 64);
427 const imm21_u = @bitCast(u21, imm21);
428 return Instruction{
429 .pc_relative_address = .{
430 .rd = rd.id(),
431 .immlo = @truncate(u2, imm21_u),
432 .immhi = @truncate(u19, imm21_u >> 2),
433 .op = op,
434 },
435 };
436 }
437
438 /// Represents the offset operand of a load or store instruction.
439 /// Data can be loaded from memory with either an immediate offset
440 /// or an offset that is stored in some register.
441 pub const LoadStoreOffset = union(enum) {
442 Immediate: union(enum) {
443 PostIndex: i9,
444 PreIndex: i9,
445 Unsigned: u12,
446 },
447 Register: struct {
448 rm: u5,
449 shift: union(enum) {
450 Uxtw: u2,
451 Lsl: u2,
452 Sxtw: u2,
453 Sxtx: u2,
454 },
455 },
456
457 pub const none = LoadStoreOffset{
458 .Immediate = .{ .Unsigned = 0 },
459 };
460
461 pub fn toU12(self: LoadStoreOffset) u12 {
462 return switch (self) {
463 .Immediate => |imm_type| switch (imm_type) {
464 .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1,
465 .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3,
466 .Unsigned => |v| v,
467 },
468 .Register => |r| switch (r.shift) {
469 .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050,
470 .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050,
471 .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050,
472 .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050,
473 },
474 };
475 }
476
477 pub fn imm(offset: u12) LoadStoreOffset {
478 return .{
479 .Immediate = .{ .Unsigned = offset },
480 };
481 }
482
483 pub fn imm_post_index(offset: i9) LoadStoreOffset {
484 return .{
485 .Immediate = .{ .PostIndex = offset },
486 };
487 }
488
489 pub fn imm_pre_index(offset: i9) LoadStoreOffset {
490 return .{
491 .Immediate = .{ .PreIndex = offset },
492 };
493 }
494
495 pub fn reg(rm: Register) LoadStoreOffset {
496 return .{
497 .Register = .{
498 .rm = rm.id(),
499 .shift = .{
500 .Lsl = 0,
501 },
502 },
503 };
504 }
505
506 pub fn reg_uxtw(rm: Register, shift: u2) LoadStoreOffset {
507 assert(rm.size() == 32 and (shift == 0 or shift == 2));
508 return .{
509 .Register = .{
510 .rm = rm.id(),
511 .shift = .{
512 .Uxtw = shift,
513 },
514 },
515 };
516 }
517
518 pub fn reg_lsl(rm: Register, shift: u2) LoadStoreOffset {
519 assert(rm.size() == 64 and (shift == 0 or shift == 3));
520 return .{
521 .Register = .{
522 .rm = rm.id(),
523 .shift = .{
524 .Lsl = shift,
525 },
526 },
527 };
528 }
529
530 pub fn reg_sxtw(rm: Register, shift: u2) LoadStoreOffset {
531 assert(rm.size() == 32 and (shift == 0 or shift == 2));
532 return .{
533 .Register = .{
534 .rm = rm.id(),
535 .shift = .{
536 .Sxtw = shift,
537 },
538 },
539 };
540 }
541
542 pub fn reg_sxtx(rm: Register, shift: u2) LoadStoreOffset {
543 assert(rm.size() == 64 and (shift == 0 or shift == 3));
544 return .{
545 .Register = .{
546 .rm = rm.id(),
547 .shift = .{
548 .Sxtx = shift,
549 },
550 },
551 };
552 }
553 };
554
555 /// Which kind of load/store to perform
556 const LoadStoreVariant = enum {
557 /// 32-bit or 64-bit
558 str,
559 /// 16-bit, zero-extended
560 strh,
561 /// 8-bit, zero-extended
562 strb,
563 /// 32-bit or 64-bit
564 ldr,
565 /// 16-bit, zero-extended
566 ldrh,
567 /// 8-bit, zero-extended
568 ldrb,
569 };
570
571 fn loadStoreRegister(
572 rt: Register,
573 rn: Register,
574 offset: LoadStoreOffset,
575 variant: LoadStoreVariant,
576 ) Instruction {
577 const off = offset.toU12();
578 const op1: u2 = blk: {
579 switch (offset) {
580 .Immediate => |imm| switch (imm) {
581 .Unsigned => break :blk 0b01,
582 else => {},
583 },
584 else => {},
585 }
586 break :blk 0b00;
587 };
588 const opc: u2 = switch (variant) {
589 .ldr, .ldrh, .ldrb => 0b01,
590 .str, .strh, .strb => 0b00,
591 };
592 return Instruction{
593 .load_store_register = .{
594 .rt = rt.id(),
595 .rn = rn.id(),
596 .offset = off,
597 .opc = opc,
598 .op1 = op1,
599 .v = 0,
600 .size = blk: {
601 switch (variant) {
602 .ldr, .str => switch (rt.size()) {
603 32 => break :blk 0b10,
604 64 => break :blk 0b11,
605 else => unreachable, // unexpected register size
606 },
607 .ldrh, .strh => break :blk 0b01,
608 .ldrb, .strb => break :blk 0b00,
609 }
610 },
611 },
612 };
613 }
614
615 fn loadStoreRegisterPair(
616 rt1: Register,
617 rt2: Register,
618 rn: Register,
619 offset: i9,
620 encoding: u2,
621 load: bool,
622 ) Instruction {
623 switch (rt1.size()) {
624 32 => {
625 assert(-256 <= offset and offset <= 252);
626 const imm7 = @truncate(u7, @bitCast(u9, offset >> 2));
627 return Instruction{
628 .load_store_register_pair = .{
629 .rt1 = rt1.id(),
630 .rn = rn.id(),
631 .rt2 = rt2.id(),
632 .imm7 = imm7,
633 .load = @boolToInt(load),
634 .encoding = encoding,
635 .opc = 0b00,
636 },
637 };
638 },
639 64 => {
640 assert(-512 <= offset and offset <= 504);
641 const imm7 = @truncate(u7, @bitCast(u9, offset >> 3));
642 return Instruction{
643 .load_store_register_pair = .{
644 .rt1 = rt1.id(),
645 .rn = rn.id(),
646 .rt2 = rt2.id(),
647 .imm7 = imm7,
648 .load = @boolToInt(load),
649 .encoding = encoding,
650 .opc = 0b10,
651 },
652 };
653 },
654 else => unreachable, // unexpected register size
655 }
656 }
657
658 fn loadLiteral(rt: Register, imm19: u19) Instruction {
659 switch (rt.size()) {
660 32 => {
661 return Instruction{
662 .load_literal = .{
663 .rt = rt.id(),
664 .imm19 = imm19,
665 .opc = 0b00,
666 },
667 };
668 },
669 64 => {
670 return Instruction{
671 .load_literal = .{
672 .rt = rt.id(),
673 .imm19 = imm19,
674 .opc = 0b01,
675 },
676 };
677 },
678 else => unreachable, // unexpected register size
679 }
680 }
681
682 fn exceptionGeneration(
683 opc: u3,
684 op2: u3,
685 ll: u2,
686 imm16: u16,
687 ) Instruction {
688 return Instruction{
689 .exception_generation = .{
690 .ll = ll,
691 .op2 = op2,
692 .imm16 = imm16,
693 .opc = opc,
694 },
695 };
696 }
697
698 fn unconditionalBranchRegister(
699 opc: u4,
700 op2: u5,
701 op3: u6,
702 rn: Register,
703 op4: u5,
704 ) Instruction {
705 assert(rn.size() == 64);
706
707 return Instruction{
708 .unconditional_branch_register = .{
709 .op4 = op4,
710 .rn = rn.id(),
711 .op3 = op3,
712 .op2 = op2,
713 .opc = opc,
714 },
715 };
716 }
717
718 fn unconditionalBranchImmediate(
719 op: u1,
720 offset: i28,
721 ) Instruction {
722 return Instruction{
723 .unconditional_branch_immediate = .{
724 .imm26 = @bitCast(u26, @intCast(i26, offset >> 2)),
725 .op = op,
726 },
727 };
728 }
729
730 fn logicalShiftedRegister(
731 opc: u2,
732 n: u1,
733 shift: Shift,
734 rd: Register,
735 rn: Register,
736 rm: Register,
737 ) Instruction {
738 switch (rd.size()) {
739 32 => {
740 assert(shift.amount < 32);
741 return Instruction{
742 .logical_shifted_register = .{
743 .rd = rd.id(),
744 .rn = rn.id(),
745 .imm6 = shift.amount,
746 .rm = rm.id(),
747 .n = n,
748 .shift = @enumToInt(shift.shift),
749 .opc = opc,
750 .sf = 0b0,
751 },
752 };
753 },
754 64 => {
755 return Instruction{
756 .logical_shifted_register = .{
757 .rd = rd.id(),
758 .rn = rn.id(),
759 .imm6 = shift.amount,
760 .rm = rm.id(),
761 .n = n,
762 .shift = @enumToInt(shift.shift),
763 .opc = opc,
764 .sf = 0b1,
765 },
766 };
767 },
768 else => unreachable, // unexpected register size
769 }
770 }
771
772 fn addSubtractImmediate(
773 op: u1,
774 s: u1,
775 rd: Register,
776 rn: Register,
777 imm12: u12,
778 shift: bool,
779 ) Instruction {
780 return Instruction{
781 .add_subtract_immediate = .{
782 .rd = rd.id(),
783 .rn = rn.id(),
784 .imm12 = imm12,
785 .sh = @boolToInt(shift),
786 .s = s,
787 .op = op,
788 .sf = switch (rd.size()) {
789 32 => 0b0,
790 64 => 0b1,
791 else => unreachable, // unexpected register size
792 },
793 },
794 };
795 }
796
797 fn conditionalBranch(
798 o0: u1,
799 o1: u1,
800 cond: Condition,
801 offset: i21,
802 ) Instruction {
803 assert(offset & 0b11 == 0b00);
804 return Instruction{
805 .conditional_branch = .{
806 .cond = @enumToInt(cond),
807 .o0 = o0,
808 .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)),
809 .o1 = o1,
810 },
811 };
812 }
813
814 fn compareAndBranch(
815 op: u1,
816 rt: Register,
817 offset: i21,
818 ) Instruction {
819 assert(offset & 0b11 == 0b00);
820 return Instruction{
821 .compare_and_branch = .{
822 .rt = rt.id(),
823 .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)),
824 .op = op,
825 .sf = switch (rt.size()) {
826 32 => 0b0,
827 64 => 0b1,
828 else => unreachable, // unexpected register size
829 },
830 },
831 };
832 }
833
834 // Helper functions for assembly syntax functions
835
836 // Move wide (immediate)
837
838 pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction {
839 return moveWideImmediate(0b00, rd, imm16, shift);
840 }
841
842 pub fn movz(rd: Register, imm16: u16, shift: u6) Instruction {
843 return moveWideImmediate(0b10, rd, imm16, shift);
844 }
845
846 pub fn movk(rd: Register, imm16: u16, shift: u6) Instruction {
847 return moveWideImmediate(0b11, rd, imm16, shift);
848 }
849
850 // PC relative address
851
852 pub fn adr(rd: Register, imm21: i21) Instruction {
853 return pcRelativeAddress(rd, imm21, 0b0);
854 }
855
856 pub fn adrp(rd: Register, imm21: i21) Instruction {
857 return pcRelativeAddress(rd, imm21, 0b1);
858 }
859
860 // Load or store register
861
862 pub const LdrArgs = union(enum) {
863 register: struct {
864 rn: Register,
865 offset: LoadStoreOffset = LoadStoreOffset.none,
866 },
867 literal: u19,
868 };
869
870 pub fn ldr(rt: Register, args: LdrArgs) Instruction {
871 switch (args) {
872 .register => |info| return loadStoreRegister(rt, info.rn, info.offset, .ldr),
873 .literal => |literal| return loadLiteral(rt, literal),
874 }
875 }
876
877 pub fn ldrh(rt: Register, rn: Register, args: StrArgs) Instruction {
878 return loadStoreRegister(rt, rn, args.offset, .ldrh);
879 }
880
881 pub fn ldrb(rt: Register, rn: Register, args: StrArgs) Instruction {
882 return loadStoreRegister(rt, rn, args.offset, .ldrb);
883 }
884
885 pub const StrArgs = struct {
886 offset: LoadStoreOffset = LoadStoreOffset.none,
887 };
888
889 pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction {
890 return loadStoreRegister(rt, rn, args.offset, .str);
891 }
892
893 pub fn strh(rt: Register, rn: Register, args: StrArgs) Instruction {
894 return loadStoreRegister(rt, rn, args.offset, .strh);
895 }
896
897 pub fn strb(rt: Register, rn: Register, args: StrArgs) Instruction {
898 return loadStoreRegister(rt, rn, args.offset, .strb);
899 }
900
901 // Load or store pair of registers
902
903 pub const LoadStorePairOffset = struct {
904 encoding: enum(u2) {
905 PostIndex = 0b01,
906 Signed = 0b10,
907 PreIndex = 0b11,
908 },
909 offset: i9,
910
911 pub fn none() LoadStorePairOffset {
912 return .{ .encoding = .Signed, .offset = 0 };
913 }
914
915 pub fn post_index(imm: i9) LoadStorePairOffset {
916 return .{ .encoding = .PostIndex, .offset = imm };
917 }
918
919 pub fn pre_index(imm: i9) LoadStorePairOffset {
920 return .{ .encoding = .PreIndex, .offset = imm };
921 }
922
923 pub fn signed(imm: i9) LoadStorePairOffset {
924 return .{ .encoding = .Signed, .offset = imm };
925 }
926 };
927
928 pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction {
929 return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true);
930 }
931
932 pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction {
933 return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true);
934 }
935
936 pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction {
937 return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false);
938 }
939
940 pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction {
941 return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false);
942 }
943
944 // Exception generation
945
946 pub fn svc(imm16: u16) Instruction {
947 return exceptionGeneration(0b000, 0b000, 0b01, imm16);
948 }
949
950 pub fn hvc(imm16: u16) Instruction {
951 return exceptionGeneration(0b000, 0b000, 0b10, imm16);
952 }
953
954 pub fn smc(imm16: u16) Instruction {
955 return exceptionGeneration(0b000, 0b000, 0b11, imm16);
956 }
957
958 pub fn brk(imm16: u16) Instruction {
959 return exceptionGeneration(0b001, 0b000, 0b00, imm16);
960 }
961
962 pub fn hlt(imm16: u16) Instruction {
963 return exceptionGeneration(0b010, 0b000, 0b00, imm16);
964 }
965
966 // Unconditional branch (register)
967
968 pub fn br(rn: Register) Instruction {
969 return unconditionalBranchRegister(0b0000, 0b11111, 0b000000, rn, 0b00000);
970 }
971
972 pub fn blr(rn: Register) Instruction {
973 return unconditionalBranchRegister(0b0001, 0b11111, 0b000000, rn, 0b00000);
974 }
975
976 pub fn ret(rn: ?Register) Instruction {
977 return unconditionalBranchRegister(0b0010, 0b11111, 0b000000, rn orelse .x30, 0b00000);
978 }
979
980 // Unconditional branch (immediate)
981
982 pub fn b(offset: i28) Instruction {
983 return unconditionalBranchImmediate(0, offset);
984 }
985
986 pub fn bl(offset: i28) Instruction {
987 return unconditionalBranchImmediate(1, offset);
988 }
989
990 // Nop
991
992 pub fn nop() Instruction {
993 return Instruction{ .no_operation = .{} };
994 }
995
996 // Logical (shifted register)
997
998 pub fn @"and"(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
999 return logicalShiftedRegister(0b00, 0b0, shift, rd, rn, rm);
1000 }
1001
1002 pub fn bic(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1003 return logicalShiftedRegister(0b00, 0b1, shift, rd, rn, rm);
1004 }
1005
1006 pub fn orr(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1007 return logicalShiftedRegister(0b01, 0b0, shift, rd, rn, rm);
1008 }
1009
1010 pub fn orn(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1011 return logicalShiftedRegister(0b01, 0b1, shift, rd, rn, rm);
1012 }
1013
1014 pub fn eor(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1015 return logicalShiftedRegister(0b10, 0b0, shift, rd, rn, rm);
1016 }
1017
1018 pub fn eon(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1019 return logicalShiftedRegister(0b10, 0b1, shift, rd, rn, rm);
1020 }
1021
1022 pub fn ands(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1023 return logicalShiftedRegister(0b11, 0b0, shift, rd, rn, rm);
1024 }
1025
1026 pub fn bics(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1027 return logicalShiftedRegister(0b11, 0b1, shift, rd, rn, rm);
1028 }
1029
1030 // Add/subtract (immediate)
1031
1032 pub fn add(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
1033 return addSubtractImmediate(0b0, 0b0, rd, rn, imm, shift);
1034 }
1035
1036 pub fn adds(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
1037 return addSubtractImmediate(0b0, 0b1, rd, rn, imm, shift);
1038 }
1039
1040 pub fn sub(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
1041 return addSubtractImmediate(0b1, 0b0, rd, rn, imm, shift);
1042 }
1043
1044 pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
1045 return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift);
1046 }
1047
1048 // Conditional branch
1049
1050 pub fn bCond(cond: Condition, offset: i21) Instruction {
1051 return conditionalBranch(0b0, 0b0, cond, offset);
1052 }
1053
1054 // Compare and branch
1055
1056 pub fn cbz(rt: Register, offset: i21) Instruction {
1057 return compareAndBranch(0b0, rt, offset);
1058 }
1059
1060 pub fn cbnz(rt: Register, offset: i21) Instruction {
1061 return compareAndBranch(0b1, rt, offset);
1062 }
1063};
1064
1065test {
1066 testing.refAllDecls(@This());
1067}
1068
1069test "serialize instructions" {
1070 const Testcase = struct {
1071 inst: Instruction,
1072 expected: u32,
1073 };
1074
1075 const testcases = [_]Testcase{
1076 .{ // orr x0, xzr, x1
1077 .inst = Instruction.orr(.x0, .xzr, .x1, Instruction.Shift.none),
1078 .expected = 0b1_01_01010_00_0_00001_000000_11111_00000,
1079 },
1080 .{ // orn x0, xzr, x1
1081 .inst = Instruction.orn(.x0, .xzr, .x1, Instruction.Shift.none),
1082 .expected = 0b1_01_01010_00_1_00001_000000_11111_00000,
1083 },
1084 .{ // movz x1, #4
1085 .inst = Instruction.movz(.x1, 4, 0),
1086 .expected = 0b1_10_100101_00_0000000000000100_00001,
1087 },
1088 .{ // movz x1, #4, lsl 16
1089 .inst = Instruction.movz(.x1, 4, 16),
1090 .expected = 0b1_10_100101_01_0000000000000100_00001,
1091 },
1092 .{ // movz x1, #4, lsl 32
1093 .inst = Instruction.movz(.x1, 4, 32),
1094 .expected = 0b1_10_100101_10_0000000000000100_00001,
1095 },
1096 .{ // movz x1, #4, lsl 48
1097 .inst = Instruction.movz(.x1, 4, 48),
1098 .expected = 0b1_10_100101_11_0000000000000100_00001,
1099 },
1100 .{ // movz w1, #4
1101 .inst = Instruction.movz(.w1, 4, 0),
1102 .expected = 0b0_10_100101_00_0000000000000100_00001,
1103 },
1104 .{ // movz w1, #4, lsl 16
1105 .inst = Instruction.movz(.w1, 4, 16),
1106 .expected = 0b0_10_100101_01_0000000000000100_00001,
1107 },
1108 .{ // svc #0
1109 .inst = Instruction.svc(0),
1110 .expected = 0b1101_0100_000_0000000000000000_00001,
1111 },
1112 .{ // svc #0x80 ; typical on Darwin
1113 .inst = Instruction.svc(0x80),
1114 .expected = 0b1101_0100_000_0000000010000000_00001,
1115 },
1116 .{ // ret
1117 .inst = Instruction.ret(null),
1118 .expected = 0b1101_011_00_10_11111_0000_00_11110_00000,
1119 },
1120 .{ // bl #0x10
1121 .inst = Instruction.bl(0x10),
1122 .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100,
1123 },
1124 .{ // ldr x2, [x1]
1125 .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1 } }),
1126 .expected = 0b11_111_0_01_01_000000000000_00001_00010,
1127 },
1128 .{ // ldr x2, [x1, #1]!
1129 .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_pre_index(1) } }),
1130 .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010,
1131 },
1132 .{ // ldr x2, [x1], #-1
1133 .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_post_index(-1) } }),
1134 .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010,
1135 },
1136 .{ // ldr x2, [x1], (x3)
1137 .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.reg(.x3) } }),
1138 .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010,
1139 },
1140 .{ // ldr x2, label
1141 .inst = Instruction.ldr(.x2, .{ .literal = 0x1 }),
1142 .expected = 0b01_011_0_00_0000000000000000001_00010,
1143 },
1144 .{ // ldrh x7, [x4], #0xaa
1145 .inst = Instruction.ldrh(.x7, .x4, .{ .offset = Instruction.LoadStoreOffset.imm_post_index(0xaa) }),
1146 .expected = 0b01_111_0_00_01_0_010101010_01_00100_00111,
1147 },
1148 .{ // ldrb x9, [x15, #0xff]!
1149 .inst = Instruction.ldrb(.x9, .x15, .{ .offset = Instruction.LoadStoreOffset.imm_pre_index(0xff) }),
1150 .expected = 0b00_111_0_00_01_0_011111111_11_01111_01001,
1151 },
1152 .{ // str x2, [x1]
1153 .inst = Instruction.str(.x2, .x1, .{}),
1154 .expected = 0b11_111_0_01_00_000000000000_00001_00010,
1155 },
1156 .{ // str x2, [x1], (x3)
1157 .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }),
1158 .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010,
1159 },
1160 .{ // strh w0, [x1]
1161 .inst = Instruction.strh(.w0, .x1, .{}),
1162 .expected = 0b01_111_0_01_00_000000000000_00001_00000,
1163 },
1164 .{ // strb w8, [x9]
1165 .inst = Instruction.strb(.w8, .x9, .{}),
1166 .expected = 0b00_111_0_01_00_000000000000_01001_01000,
1167 },
1168 .{ // adr x2, #0x8
1169 .inst = Instruction.adr(.x2, 0x8),
1170 .expected = 0b0_00_10000_0000000000000000010_00010,
1171 },
1172 .{ // adr x2, -#0x8
1173 .inst = Instruction.adr(.x2, -0x8),
1174 .expected = 0b0_00_10000_1111111111111111110_00010,
1175 },
1176 .{ // adrp x2, #0x8
1177 .inst = Instruction.adrp(.x2, 0x8),
1178 .expected = 0b1_00_10000_0000000000000000010_00010,
1179 },
1180 .{ // adrp x2, -#0x8
1181 .inst = Instruction.adrp(.x2, -0x8),
1182 .expected = 0b1_00_10000_1111111111111111110_00010,
1183 },
1184 .{ // stp x1, x2, [sp, #8]
1185 .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)),
1186 .expected = 0b10_101_0_010_0_0000001_00010_11111_00001,
1187 },
1188 .{ // ldp x1, x2, [sp, #8]
1189 .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)),
1190 .expected = 0b10_101_0_010_1_0000001_00010_11111_00001,
1191 },
1192 .{ // stp x1, x2, [sp, #-16]!
1193 .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.pre_index(-16)),
1194 .expected = 0b10_101_0_011_0_1111110_00010_11111_00001,
1195 },
1196 .{ // ldp x1, x2, [sp], #16
1197 .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)),
1198 .expected = 0b10_101_0_001_1_0000010_00010_11111_00001,
1199 },
1200 .{ // and x0, x4, x2
1201 .inst = Instruction.@"and"(.x0, .x4, .x2, .{}),
1202 .expected = 0b1_00_01010_00_0_00010_000000_00100_00000,
1203 },
1204 .{ // and x0, x4, x2, lsl #0x8
1205 .inst = Instruction.@"and"(.x0, .x4, .x2, .{ .shift = .lsl, .amount = 0x8 }),
1206 .expected = 0b1_00_01010_00_0_00010_001000_00100_00000,
1207 },
1208 .{ // add x0, x10, #10
1209 .inst = Instruction.add(.x0, .x10, 10, false),
1210 .expected = 0b1_0_0_100010_0_0000_0000_1010_01010_00000,
1211 },
1212 .{ // subs x0, x5, #11, lsl #12
1213 .inst = Instruction.subs(.x0, .x5, 11, true),
1214 .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000,
1215 },
1216 .{ // b.hi #-4
1217 .inst = Instruction.bCond(.hi, -4),
1218 .expected = 0b0101010_0_1111111111111111111_0_1000,
1219 },
1220 .{ // cbz x10, #40
1221 .inst = Instruction.cbz(.x10, 40),
1222 .expected = 0b1_011010_0_0000000000000001010_01010,
1223 },
1224 };
1225
1226 for (testcases) |case| {
1227 const actual = case.inst.toU32();
1228 try testing.expectEqual(case.expected, actual);
1229 }
1230}
src/arch/arm/bits.zig created+1408
......@@ -0,0 +1,1408 @@
1const std = @import("std");
2const DW = std.dwarf;
3const testing = std.testing;
4
5/// The condition field specifies the flags necessary for an
6/// Instruction to be executed
7pub const Condition = enum(u4) {
8 /// equal
9 eq,
10 /// not equal
11 ne,
12 /// unsigned higher or same
13 cs,
14 /// unsigned lower
15 cc,
16 /// negative
17 mi,
18 /// positive or zero
19 pl,
20 /// overflow
21 vs,
22 /// no overflow
23 vc,
24 /// unsigned higer
25 hi,
26 /// unsigned lower or same
27 ls,
28 /// greater or equal
29 ge,
30 /// less than
31 lt,
32 /// greater than
33 gt,
34 /// less than or equal
35 le,
36 /// always
37 al,
38
39 /// Converts a std.math.CompareOperator into a condition flag,
40 /// i.e. returns the condition that is true iff the result of the
41 /// comparison is true. Assumes signed comparison
42 pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition {
43 return switch (op) {
44 .gte => .ge,
45 .gt => .gt,
46 .neq => .ne,
47 .lt => .lt,
48 .lte => .le,
49 .eq => .eq,
50 };
51 }
52
53 /// Converts a std.math.CompareOperator into a condition flag,
54 /// i.e. returns the condition that is true iff the result of the
55 /// comparison is true. Assumes unsigned comparison
56 pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition {
57 return switch (op) {
58 .gte => .cs,
59 .gt => .hi,
60 .neq => .ne,
61 .lt => .cc,
62 .lte => .ls,
63 .eq => .eq,
64 };
65 }
66
67 /// Returns the condition which is true iff the given condition is
68 /// false (if such a condition exists)
69 pub fn negate(cond: Condition) Condition {
70 return switch (cond) {
71 .eq => .ne,
72 .ne => .eq,
73 .cs => .cc,
74 .cc => .cs,
75 .mi => .pl,
76 .pl => .mi,
77 .vs => .vc,
78 .vc => .vs,
79 .hi => .ls,
80 .ls => .hi,
81 .ge => .lt,
82 .lt => .ge,
83 .gt => .le,
84 .le => .gt,
85 .al => unreachable,
86 };
87 }
88};
89
90test "condition from CompareOperator" {
91 try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq));
92 try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq));
93
94 try testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt));
95 try testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt));
96
97 try testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte));
98 try testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte));
99}
100
101test "negate condition" {
102 try testing.expectEqual(@as(Condition, .eq), Condition.ne.negate());
103 try testing.expectEqual(@as(Condition, .ne), Condition.eq.negate());
104}
105
106/// Represents a register in the ARM instruction set architecture
107pub const Register = enum(u5) {
108 r0,
109 r1,
110 r2,
111 r3,
112 r4,
113 r5,
114 r6,
115 r7,
116 r8,
117 r9,
118 r10,
119 r11,
120 r12,
121 r13,
122 r14,
123 r15,
124
125 /// Argument / result / scratch register 1
126 a1,
127 /// Argument / result / scratch register 2
128 a2,
129 /// Argument / scratch register 3
130 a3,
131 /// Argument / scratch register 4
132 a4,
133 /// Variable-register 1
134 v1,
135 /// Variable-register 2
136 v2,
137 /// Variable-register 3
138 v3,
139 /// Variable-register 4
140 v4,
141 /// Variable-register 5
142 v5,
143 /// Platform register
144 v6,
145 /// Variable-register 7
146 v7,
147 /// Frame pointer or Variable-register 8
148 fp,
149 /// Intra-Procedure-call scratch register
150 ip,
151 /// Stack pointer
152 sp,
153 /// Link register
154 lr,
155 /// Program counter
156 pc,
157
158 /// Returns the unique 4-bit ID of this register which is used in
159 /// the machine code
160 pub fn id(self: Register) u4 {
161 return @truncate(u4, @enumToInt(self));
162 }
163
164 /// Returns the index into `callee_preserved_regs`.
165 pub fn allocIndex(self: Register) ?u4 {
166 inline for (callee_preserved_regs) |cpreg, i| {
167 if (self.id() == cpreg.id()) return i;
168 }
169 return null;
170 }
171
172 pub fn dwarfLocOp(self: Register) u8 {
173 return @as(u8, self.id()) + DW.OP.reg0;
174 }
175};
176
177test "Register.id" {
178 try testing.expectEqual(@as(u4, 15), Register.r15.id());
179 try testing.expectEqual(@as(u4, 15), Register.pc.id());
180}
181
182/// Program status registers containing flags, mode bits and other
183/// vital information
184pub const Psr = enum {
185 cpsr,
186 spsr,
187};
188
189pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 };
190pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
191pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
192
193/// Represents an instruction in the ARM instruction set architecture
194pub const Instruction = union(enum) {
195 data_processing: packed struct {
196 // Note to self: The order of the fields top-to-bottom is
197 // right-to-left in the actual 32-bit int representation
198 op2: u12,
199 rd: u4,
200 rn: u4,
201 s: u1,
202 opcode: u4,
203 i: u1,
204 fixed: u2 = 0b00,
205 cond: u4,
206 },
207 multiply: packed struct {
208 rn: u4,
209 fixed_1: u4 = 0b1001,
210 rm: u4,
211 ra: u4,
212 rd: u4,
213 set_cond: u1,
214 accumulate: u1,
215 fixed_2: u6 = 0b000000,
216 cond: u4,
217 },
218 multiply_long: packed struct {
219 rn: u4,
220 fixed_1: u4 = 0b1001,
221 rm: u4,
222 rdlo: u4,
223 rdhi: u4,
224 set_cond: u1,
225 accumulate: u1,
226 unsigned: u1,
227 fixed_2: u5 = 0b00001,
228 cond: u4,
229 },
230 integer_saturating_arithmetic: packed struct {
231 rm: u4,
232 fixed_1: u8 = 0b0000_0101,
233 rd: u4,
234 rn: u4,
235 fixed_2: u1 = 0b0,
236 opc: u2,
237 fixed_3: u5 = 0b00010,
238 cond: u4,
239 },
240 single_data_transfer: packed struct {
241 offset: u12,
242 rd: u4,
243 rn: u4,
244 load_store: u1,
245 write_back: u1,
246 byte_word: u1,
247 up_down: u1,
248 pre_post: u1,
249 imm: u1,
250 fixed: u2 = 0b01,
251 cond: u4,
252 },
253 extra_load_store: packed struct {
254 imm4l: u4,
255 fixed_1: u1 = 0b1,
256 op2: u2,
257 fixed_2: u1 = 0b1,
258 imm4h: u4,
259 rt: u4,
260 rn: u4,
261 o1: u1,
262 write_back: u1,
263 imm: u1,
264 up_down: u1,
265 pre_index: u1,
266 fixed_3: u3 = 0b000,
267 cond: u4,
268 },
269 block_data_transfer: packed struct {
270 register_list: u16,
271 rn: u4,
272 load_store: u1,
273 write_back: u1,
274 psr_or_user: u1,
275 up_down: u1,
276 pre_post: u1,
277 fixed: u3 = 0b100,
278 cond: u4,
279 },
280 branch: packed struct {
281 offset: u24,
282 link: u1,
283 fixed: u3 = 0b101,
284 cond: u4,
285 },
286 branch_exchange: packed struct {
287 rn: u4,
288 fixed_1: u1 = 0b1,
289 link: u1,
290 fixed_2: u22 = 0b0001_0010_1111_1111_1111_00,
291 cond: u4,
292 },
293 supervisor_call: packed struct {
294 comment: u24,
295 fixed: u4 = 0b1111,
296 cond: u4,
297 },
298 breakpoint: packed struct {
299 imm4: u4,
300 fixed_1: u4 = 0b0111,
301 imm12: u12,
302 fixed_2_and_cond: u12 = 0b1110_0001_0010,
303 },
304
305 /// Represents the possible operations which can be performed by a
306 /// Data Processing instruction
307 const Opcode = enum(u4) {
308 // Rd := Op1 AND Op2
309 @"and",
310 // Rd := Op1 EOR Op2
311 eor,
312 // Rd := Op1 - Op2
313 sub,
314 // Rd := Op2 - Op1
315 rsb,
316 // Rd := Op1 + Op2
317 add,
318 // Rd := Op1 + Op2 + C
319 adc,
320 // Rd := Op1 - Op2 + C - 1
321 sbc,
322 // Rd := Op2 - Op1 + C - 1
323 rsc,
324 // set condition codes on Op1 AND Op2
325 tst,
326 // set condition codes on Op1 EOR Op2
327 teq,
328 // set condition codes on Op1 - Op2
329 cmp,
330 // set condition codes on Op1 + Op2
331 cmn,
332 // Rd := Op1 OR Op2
333 orr,
334 // Rd := Op2
335 mov,
336 // Rd := Op1 AND NOT Op2
337 bic,
338 // Rd := NOT Op2
339 mvn,
340 };
341
342 /// Represents the second operand to a data processing instruction
343 /// which can either be content from a register or an immediate
344 /// value
345 pub const Operand = union(enum) {
346 Register: packed struct {
347 rm: u4,
348 shift: u8,
349 },
350 Immediate: packed struct {
351 imm: u8,
352 rotate: u4,
353 },
354
355 /// Represents multiple ways a register can be shifted. A
356 /// register can be shifted by a specific immediate value or
357 /// by the contents of another register
358 pub const Shift = union(enum) {
359 Immediate: packed struct {
360 fixed: u1 = 0b0,
361 typ: u2,
362 amount: u5,
363 },
364 Register: packed struct {
365 fixed_1: u1 = 0b1,
366 typ: u2,
367 fixed_2: u1 = 0b0,
368 rs: u4,
369 },
370
371 pub const Type = enum(u2) {
372 logical_left,
373 logical_right,
374 arithmetic_right,
375 rotate_right,
376 };
377
378 pub const none = Shift{
379 .Immediate = .{
380 .amount = 0,
381 .typ = 0,
382 },
383 };
384
385 pub fn toU8(self: Shift) u8 {
386 return switch (self) {
387 .Register => |v| @bitCast(u8, v),
388 .Immediate => |v| @bitCast(u8, v),
389 };
390 }
391
392 pub fn reg(rs: Register, typ: Type) Shift {
393 return Shift{
394 .Register = .{
395 .rs = rs.id(),
396 .typ = @enumToInt(typ),
397 },
398 };
399 }
400
401 pub fn imm(amount: u5, typ: Type) Shift {
402 return Shift{
403 .Immediate = .{
404 .amount = amount,
405 .typ = @enumToInt(typ),
406 },
407 };
408 }
409 };
410
411 pub fn toU12(self: Operand) u12 {
412 return switch (self) {
413 .Register => |v| @bitCast(u12, v),
414 .Immediate => |v| @bitCast(u12, v),
415 };
416 }
417
418 pub fn reg(rm: Register, shift: Shift) Operand {
419 return Operand{
420 .Register = .{
421 .rm = rm.id(),
422 .shift = shift.toU8(),
423 },
424 };
425 }
426
427 pub fn imm(immediate: u8, rotate: u4) Operand {
428 return Operand{
429 .Immediate = .{
430 .imm = immediate,
431 .rotate = rotate,
432 },
433 };
434 }
435
436 /// Tries to convert an unsigned 32 bit integer into an
437 /// immediate operand using rotation. Returns null when there
438 /// is no conversion
439 pub fn fromU32(x: u32) ?Operand {
440 const masks = comptime blk: {
441 const base_mask: u32 = std.math.maxInt(u8);
442 var result = [_]u32{0} ** 16;
443 for (result) |*mask, i| mask.* = std.math.rotr(u32, base_mask, 2 * i);
444 break :blk result;
445 };
446
447 return for (masks) |mask, i| {
448 if (x & mask == x) {
449 break Operand{
450 .Immediate = .{
451 .imm = @intCast(u8, std.math.rotl(u32, x, 2 * i)),
452 .rotate = @intCast(u4, i),
453 },
454 };
455 }
456 } else null;
457 }
458 };
459
460 /// Represents the offset operand of a load or store
461 /// instruction. Data can be loaded from memory with either an
462 /// immediate offset or an offset that is stored in some register.
463 pub const Offset = union(enum) {
464 Immediate: u12,
465 Register: packed struct {
466 rm: u4,
467 shift: u8,
468 },
469
470 pub const none = Offset{
471 .Immediate = 0,
472 };
473
474 pub fn toU12(self: Offset) u12 {
475 return switch (self) {
476 .Register => |v| @bitCast(u12, v),
477 .Immediate => |v| v,
478 };
479 }
480
481 pub fn reg(rm: Register, shift: u8) Offset {
482 return Offset{
483 .Register = .{
484 .rm = rm.id(),
485 .shift = shift,
486 },
487 };
488 }
489
490 pub fn imm(immediate: u12) Offset {
491 return Offset{
492 .Immediate = immediate,
493 };
494 }
495 };
496
497 /// Represents the offset operand of an extra load or store
498 /// instruction.
499 pub const ExtraLoadStoreOffset = union(enum) {
500 immediate: u8,
501 register: u4,
502
503 pub const none = ExtraLoadStoreOffset{
504 .immediate = 0,
505 };
506
507 pub fn reg(register: Register) ExtraLoadStoreOffset {
508 return ExtraLoadStoreOffset{
509 .register = register.id(),
510 };
511 }
512
513 pub fn imm(immediate: u8) ExtraLoadStoreOffset {
514 return ExtraLoadStoreOffset{
515 .immediate = immediate,
516 };
517 }
518 };
519
520 /// Represents the register list operand to a block data transfer
521 /// instruction
522 pub const RegisterList = packed struct {
523 r0: bool = false,
524 r1: bool = false,
525 r2: bool = false,
526 r3: bool = false,
527 r4: bool = false,
528 r5: bool = false,
529 r6: bool = false,
530 r7: bool = false,
531 r8: bool = false,
532 r9: bool = false,
533 r10: bool = false,
534 r11: bool = false,
535 r12: bool = false,
536 r13: bool = false,
537 r14: bool = false,
538 r15: bool = false,
539 };
540
541 pub fn toU32(self: Instruction) u32 {
542 return switch (self) {
543 .data_processing => |v| @bitCast(u32, v),
544 .multiply => |v| @bitCast(u32, v),
545 .multiply_long => |v| @bitCast(u32, v),
546 .integer_saturating_arithmetic => |v| @bitCast(u32, v),
547 .single_data_transfer => |v| @bitCast(u32, v),
548 .extra_load_store => |v| @bitCast(u32, v),
549 .block_data_transfer => |v| @bitCast(u32, v),
550 .branch => |v| @bitCast(u32, v),
551 .branch_exchange => |v| @bitCast(u32, v),
552 .supervisor_call => |v| @bitCast(u32, v),
553 .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20),
554 };
555 }
556
557 // Helper functions for the "real" functions below
558
559 fn dataProcessing(
560 cond: Condition,
561 opcode: Opcode,
562 s: u1,
563 rd: Register,
564 rn: Register,
565 op2: Operand,
566 ) Instruction {
567 return Instruction{
568 .data_processing = .{
569 .cond = @enumToInt(cond),
570 .i = @boolToInt(op2 == .Immediate),
571 .opcode = @enumToInt(opcode),
572 .s = s,
573 .rn = rn.id(),
574 .rd = rd.id(),
575 .op2 = op2.toU12(),
576 },
577 };
578 }
579
580 fn specialMov(
581 cond: Condition,
582 rd: Register,
583 imm: u16,
584 top: bool,
585 ) Instruction {
586 return Instruction{
587 .data_processing = .{
588 .cond = @enumToInt(cond),
589 .i = 1,
590 .opcode = if (top) 0b1010 else 0b1000,
591 .s = 0,
592 .rn = @truncate(u4, imm >> 12),
593 .rd = rd.id(),
594 .op2 = @truncate(u12, imm),
595 },
596 };
597 }
598
599 fn multiply(
600 cond: Condition,
601 set_cond: u1,
602 rd: Register,
603 rn: Register,
604 rm: Register,
605 ra: ?Register,
606 ) Instruction {
607 return Instruction{
608 .multiply = .{
609 .cond = @enumToInt(cond),
610 .accumulate = @boolToInt(ra != null),
611 .set_cond = set_cond,
612 .rd = rd.id(),
613 .rn = rn.id(),
614 .ra = if (ra) |reg| reg.id() else 0b0000,
615 .rm = rm.id(),
616 },
617 };
618 }
619
620 fn multiplyLong(
621 cond: Condition,
622 signed: u1,
623 accumulate: u1,
624 set_cond: u1,
625 rdhi: Register,
626 rdlo: Register,
627 rm: Register,
628 rn: Register,
629 ) Instruction {
630 return Instruction{
631 .multiply_long = .{
632 .cond = @enumToInt(cond),
633 .unsigned = signed,
634 .accumulate = accumulate,
635 .set_cond = set_cond,
636 .rdlo = rdlo.id(),
637 .rdhi = rdhi.id(),
638 .rn = rn.id(),
639 .rm = rm.id(),
640 },
641 };
642 }
643
644 fn integerSaturationArithmetic(
645 cond: Condition,
646 rd: Register,
647 rm: Register,
648 rn: Register,
649 opc: u2,
650 ) Instruction {
651 return Instruction{
652 .integer_saturating_arithmetic = .{
653 .rm = rm.id(),
654 .rd = rd.id(),
655 .rn = rn.id(),
656 .opc = opc,
657 .cond = @enumToInt(cond),
658 },
659 };
660 }
661
662 fn singleDataTransfer(
663 cond: Condition,
664 rd: Register,
665 rn: Register,
666 offset: Offset,
667 pre_index: bool,
668 positive: bool,
669 byte_word: u1,
670 write_back: bool,
671 load_store: u1,
672 ) Instruction {
673 return Instruction{
674 .single_data_transfer = .{
675 .cond = @enumToInt(cond),
676 .rn = rn.id(),
677 .rd = rd.id(),
678 .offset = offset.toU12(),
679 .load_store = load_store,
680 .write_back = @boolToInt(write_back),
681 .byte_word = byte_word,
682 .up_down = @boolToInt(positive),
683 .pre_post = @boolToInt(pre_index),
684 .imm = @boolToInt(offset != .Immediate),
685 },
686 };
687 }
688
689 fn extraLoadStore(
690 cond: Condition,
691 pre_index: bool,
692 positive: bool,
693 write_back: bool,
694 o1: u1,
695 op2: u2,
696 rn: Register,
697 rt: Register,
698 offset: ExtraLoadStoreOffset,
699 ) Instruction {
700 const imm4l: u4 = switch (offset) {
701 .immediate => |imm| @truncate(u4, imm),
702 .register => |reg| reg,
703 };
704 const imm4h: u4 = switch (offset) {
705 .immediate => |imm| @truncate(u4, imm >> 4),
706 .register => 0b0000,
707 };
708
709 return Instruction{
710 .extra_load_store = .{
711 .imm4l = imm4l,
712 .op2 = op2,
713 .imm4h = imm4h,
714 .rt = rt.id(),
715 .rn = rn.id(),
716 .o1 = o1,
717 .write_back = @boolToInt(write_back),
718 .imm = @boolToInt(offset == .immediate),
719 .up_down = @boolToInt(positive),
720 .pre_index = @boolToInt(pre_index),
721 .cond = @enumToInt(cond),
722 },
723 };
724 }
725
726 fn blockDataTransfer(
727 cond: Condition,
728 rn: Register,
729 reg_list: RegisterList,
730 pre_post: u1,
731 up_down: u1,
732 psr_or_user: u1,
733 write_back: bool,
734 load_store: u1,
735 ) Instruction {
736 return Instruction{
737 .block_data_transfer = .{
738 .register_list = @bitCast(u16, reg_list),
739 .rn = rn.id(),
740 .load_store = load_store,
741 .write_back = @boolToInt(write_back),
742 .psr_or_user = psr_or_user,
743 .up_down = up_down,
744 .pre_post = pre_post,
745 .cond = @enumToInt(cond),
746 },
747 };
748 }
749
750 fn branch(cond: Condition, offset: i26, link: u1) Instruction {
751 return Instruction{
752 .branch = .{
753 .cond = @enumToInt(cond),
754 .link = link,
755 .offset = @bitCast(u24, @intCast(i24, offset >> 2)),
756 },
757 };
758 }
759
760 fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction {
761 return Instruction{
762 .branch_exchange = .{
763 .cond = @enumToInt(cond),
764 .link = link,
765 .rn = rn.id(),
766 },
767 };
768 }
769
770 fn supervisorCall(cond: Condition, comment: u24) Instruction {
771 return Instruction{
772 .supervisor_call = .{
773 .cond = @enumToInt(cond),
774 .comment = comment,
775 },
776 };
777 }
778
779 fn breakpoint(imm: u16) Instruction {
780 return Instruction{
781 .breakpoint = .{
782 .imm12 = @truncate(u12, imm >> 4),
783 .imm4 = @truncate(u4, imm),
784 },
785 };
786 }
787
788 // Public functions replicating assembler syntax as closely as
789 // possible
790
791 // Data processing
792
793 pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
794 return dataProcessing(cond, .@"and", 0, rd, rn, op2);
795 }
796
797 pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
798 return dataProcessing(cond, .@"and", 1, rd, rn, op2);
799 }
800
801 pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
802 return dataProcessing(cond, .eor, 0, rd, rn, op2);
803 }
804
805 pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
806 return dataProcessing(cond, .eor, 1, rd, rn, op2);
807 }
808
809 pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
810 return dataProcessing(cond, .sub, 0, rd, rn, op2);
811 }
812
813 pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
814 return dataProcessing(cond, .sub, 1, rd, rn, op2);
815 }
816
817 pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
818 return dataProcessing(cond, .rsb, 0, rd, rn, op2);
819 }
820
821 pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
822 return dataProcessing(cond, .rsb, 1, rd, rn, op2);
823 }
824
825 pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
826 return dataProcessing(cond, .add, 0, rd, rn, op2);
827 }
828
829 pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
830 return dataProcessing(cond, .add, 1, rd, rn, op2);
831 }
832
833 pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
834 return dataProcessing(cond, .adc, 0, rd, rn, op2);
835 }
836
837 pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
838 return dataProcessing(cond, .adc, 1, rd, rn, op2);
839 }
840
841 pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
842 return dataProcessing(cond, .sbc, 0, rd, rn, op2);
843 }
844
845 pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
846 return dataProcessing(cond, .sbc, 1, rd, rn, op2);
847 }
848
849 pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
850 return dataProcessing(cond, .rsc, 0, rd, rn, op2);
851 }
852
853 pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
854 return dataProcessing(cond, .rsc, 1, rd, rn, op2);
855 }
856
857 pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
858 return dataProcessing(cond, .tst, 1, .r0, rn, op2);
859 }
860
861 pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction {
862 return dataProcessing(cond, .teq, 1, .r0, rn, op2);
863 }
864
865 pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction {
866 return dataProcessing(cond, .cmp, 1, .r0, rn, op2);
867 }
868
869 pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction {
870 return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
871 }
872
873 pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
874 return dataProcessing(cond, .orr, 0, rd, rn, op2);
875 }
876
877 pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
878 return dataProcessing(cond, .orr, 1, rd, rn, op2);
879 }
880
881 pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction {
882 return dataProcessing(cond, .mov, 0, rd, .r0, op2);
883 }
884
885 pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction {
886 return dataProcessing(cond, .mov, 1, rd, .r0, op2);
887 }
888
889 pub fn bic(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
890 return dataProcessing(cond, .bic, 0, rd, rn, op2);
891 }
892
893 pub fn bics(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
894 return dataProcessing(cond, .bic, 1, rd, rn, op2);
895 }
896
897 pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction {
898 return dataProcessing(cond, .mvn, 0, rd, .r0, op2);
899 }
900
901 pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction {
902 return dataProcessing(cond, .mvn, 1, rd, .r0, op2);
903 }
904
905 // Integer Saturating Arithmetic
906
907 pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
908 return integerSaturationArithmetic(cond, rd, rm, rn, 0b00);
909 }
910
911 pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
912 return integerSaturationArithmetic(cond, rd, rm, rn, 0b01);
913 }
914
915 pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
916 return integerSaturationArithmetic(cond, rd, rm, rn, 0b10);
917 }
918
919 pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
920 return integerSaturationArithmetic(cond, rd, rm, rn, 0b11);
921 }
922
923 // movw and movt
924
925 pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction {
926 return specialMov(cond, rd, imm, false);
927 }
928
929 pub fn movt(cond: Condition, rd: Register, imm: u16) Instruction {
930 return specialMov(cond, rd, imm, true);
931 }
932
933 // PSR transfer
934
935 pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction {
936 return Instruction{
937 .data_processing = .{
938 .cond = @enumToInt(cond),
939 .i = 0,
940 .opcode = if (psr == .spsr) 0b1010 else 0b1000,
941 .s = 0,
942 .rn = 0b1111,
943 .rd = rd.id(),
944 .op2 = 0b0000_0000_0000,
945 },
946 };
947 }
948
949 pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction {
950 return Instruction{
951 .data_processing = .{
952 .cond = @enumToInt(cond),
953 .i = 0,
954 .opcode = if (psr == .spsr) 0b1011 else 0b1001,
955 .s = 0,
956 .rn = 0b1111,
957 .rd = 0b1111,
958 .op2 = op.toU12(),
959 },
960 };
961 }
962
963 // Multiply
964
965 pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
966 return multiply(cond, 0, rd, rn, rm, null);
967 }
968
969 pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
970 return multiply(cond, 1, rd, rn, rm, null);
971 }
972
973 pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
974 return multiply(cond, 0, rd, rn, rm, ra);
975 }
976
977 pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
978 return multiply(cond, 1, rd, rn, rm, ra);
979 }
980
981 // Multiply long
982
983 pub fn umull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
984 return multiplyLong(cond, 0, 0, 0, rdhi, rdlo, rm, rn);
985 }
986
987 pub fn umulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
988 return multiplyLong(cond, 0, 0, 1, rdhi, rdlo, rm, rn);
989 }
990
991 pub fn umlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
992 return multiplyLong(cond, 0, 1, 0, rdhi, rdlo, rm, rn);
993 }
994
995 pub fn umlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
996 return multiplyLong(cond, 0, 1, 1, rdhi, rdlo, rm, rn);
997 }
998
999 pub fn smull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1000 return multiplyLong(cond, 1, 0, 0, rdhi, rdlo, rm, rn);
1001 }
1002
1003 pub fn smulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1004 return multiplyLong(cond, 1, 0, 1, rdhi, rdlo, rm, rn);
1005 }
1006
1007 pub fn smlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1008 return multiplyLong(cond, 1, 1, 0, rdhi, rdlo, rm, rn);
1009 }
1010
1011 pub fn smlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1012 return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn);
1013 }
1014
1015 // Single data transfer
1016
1017 pub const OffsetArgs = struct {
1018 pre_index: bool = true,
1019 positive: bool = true,
1020 offset: Offset,
1021 write_back: bool = false,
1022 };
1023
1024 pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1025 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1);
1026 }
1027
1028 pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1029 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1);
1030 }
1031
1032 pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1033 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0);
1034 }
1035
1036 pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1037 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0);
1038 }
1039
1040 // Extra load/store
1041
1042 pub const ExtraLoadStoreOffsetArgs = struct {
1043 pre_index: bool = true,
1044 positive: bool = true,
1045 offset: ExtraLoadStoreOffset,
1046 write_back: bool = false,
1047 };
1048
1049 pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
1050 return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0, 0b01, rn, rt, args.offset);
1051 }
1052
1053 pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
1054 return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 1, 0b01, rn, rt, args.offset);
1055 }
1056
1057 // Block data transfer
1058
1059 pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1060 return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1);
1061 }
1062
1063 pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1064 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1);
1065 }
1066
1067 pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1068 return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1);
1069 }
1070
1071 pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1072 return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1);
1073 }
1074
1075 pub const ldmfa = ldmda;
1076 pub const ldmea = ldmdb;
1077 pub const ldmed = ldmib;
1078 pub const ldmfd = ldmia;
1079 pub const ldm = ldmia;
1080
1081 pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1082 return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0);
1083 }
1084
1085 pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1086 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0);
1087 }
1088
1089 pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1090 return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0);
1091 }
1092
1093 pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1094 return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0);
1095 }
1096
1097 pub const stmed = stmda;
1098 pub const stmfd = stmdb;
1099 pub const stmfa = stmib;
1100 pub const stmea = stmia;
1101 pub const stm = stmia;
1102
1103 // Branch
1104
1105 pub fn b(cond: Condition, offset: i26) Instruction {
1106 return branch(cond, offset, 0);
1107 }
1108
1109 pub fn bl(cond: Condition, offset: i26) Instruction {
1110 return branch(cond, offset, 1);
1111 }
1112
1113 // Branch and exchange
1114
1115 pub fn bx(cond: Condition, rn: Register) Instruction {
1116 return branchExchange(cond, rn, 0);
1117 }
1118
1119 pub fn blx(cond: Condition, rn: Register) Instruction {
1120 return branchExchange(cond, rn, 1);
1121 }
1122
1123 // Supervisor Call
1124
1125 pub const swi = svc;
1126
1127 pub fn svc(cond: Condition, comment: u24) Instruction {
1128 return supervisorCall(cond, comment);
1129 }
1130
1131 // Breakpoint
1132
1133 pub fn bkpt(imm: u16) Instruction {
1134 return breakpoint(imm);
1135 }
1136
1137 // Aliases
1138
1139 pub fn nop() Instruction {
1140 return mov(.al, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none));
1141 }
1142
1143 pub fn pop(cond: Condition, args: anytype) Instruction {
1144 if (@typeInfo(@TypeOf(args)) != .Struct) {
1145 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
1146 }
1147
1148 if (args.len < 1) {
1149 @compileError("Expected at least one register");
1150 } else if (args.len == 1) {
1151 const reg = args[0];
1152 return ldr(cond, reg, .sp, .{
1153 .pre_index = false,
1154 .positive = true,
1155 .offset = Offset.imm(4),
1156 .write_back = false,
1157 });
1158 } else {
1159 var register_list: u16 = 0;
1160 inline for (args) |arg| {
1161 const reg = @as(Register, arg);
1162 register_list |= @as(u16, 1) << reg.id();
1163 }
1164 return ldm(cond, .sp, true, @bitCast(RegisterList, register_list));
1165 }
1166 }
1167
1168 pub fn push(cond: Condition, args: anytype) Instruction {
1169 if (@typeInfo(@TypeOf(args)) != .Struct) {
1170 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
1171 }
1172
1173 if (args.len < 1) {
1174 @compileError("Expected at least one register");
1175 } else if (args.len == 1) {
1176 const reg = args[0];
1177 return str(cond, reg, .sp, .{
1178 .pre_index = true,
1179 .positive = false,
1180 .offset = Offset.imm(4),
1181 .write_back = true,
1182 });
1183 } else {
1184 var register_list: u16 = 0;
1185 inline for (args) |arg| {
1186 const reg = @as(Register, arg);
1187 register_list |= @as(u16, 1) << reg.id();
1188 }
1189 return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list));
1190 }
1191 }
1192
1193 pub const ShiftAmount = union(enum) {
1194 immediate: u5,
1195 register: Register,
1196
1197 pub fn imm(immediate: u5) ShiftAmount {
1198 return .{
1199 .immediate = immediate,
1200 };
1201 }
1202
1203 pub fn reg(register: Register) ShiftAmount {
1204 return .{
1205 .register = register,
1206 };
1207 }
1208 };
1209
1210 pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1211 return switch (shift) {
1212 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
1213 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
1214 };
1215 }
1216
1217 pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1218 return switch (shift) {
1219 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
1220 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
1221 };
1222 }
1223
1224 pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1225 return switch (shift) {
1226 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
1227 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
1228 };
1229 }
1230
1231 pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1232 return switch (shift) {
1233 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
1234 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
1235 };
1236 }
1237
1238 pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1239 return switch (shift) {
1240 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
1241 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
1242 };
1243 }
1244
1245 pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1246 return switch (shift) {
1247 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
1248 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
1249 };
1250 }
1251
1252 pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1253 return switch (shift) {
1254 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
1255 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
1256 };
1257 }
1258
1259 pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1260 return switch (shift) {
1261 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
1262 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
1263 };
1264 }
1265};
1266
1267test "serialize instructions" {
1268 const Testcase = struct {
1269 inst: Instruction,
1270 expected: u32,
1271 };
1272
1273 const testcases = [_]Testcase{
1274 .{ // add r0, r0, r0
1275 .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
1276 .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
1277 },
1278 .{ // mov r4, r2
1279 .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
1280 .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
1281 },
1282 .{ // mov r0, #42
1283 .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)),
1284 .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
1285 },
1286 .{ // mrs r5, cpsr
1287 .inst = Instruction.mrs(.al, .r5, .cpsr),
1288 .expected = 0b1110_00010_0_001111_0101_000000000000,
1289 },
1290 .{ // mul r0, r1, r2
1291 .inst = Instruction.mul(.al, .r0, .r1, .r2),
1292 .expected = 0b1110_000000_0_0_0000_0000_0010_1001_0001,
1293 },
1294 .{ // umlal r0, r1, r5, r6
1295 .inst = Instruction.umlal(.al, .r0, .r1, .r5, .r6),
1296 .expected = 0b1110_00001_0_1_0_0001_0000_0110_1001_0101,
1297 },
1298 .{ // ldr r0, [r2, #42]
1299 .inst = Instruction.ldr(.al, .r0, .r2, .{
1300 .offset = Instruction.Offset.imm(42),
1301 }),
1302 .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
1303 },
1304 .{ // str r0, [r3]
1305 .inst = Instruction.str(.al, .r0, .r3, .{
1306 .offset = Instruction.Offset.none,
1307 }),
1308 .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
1309 },
1310 .{ // strh r1, [r5]
1311 .inst = Instruction.strh(.al, .r1, .r5, .{
1312 .offset = Instruction.ExtraLoadStoreOffset.none,
1313 }),
1314 .expected = 0b1110_000_1_1_1_0_0_0101_0001_0000_1011_0000,
1315 },
1316 .{ // b #12
1317 .inst = Instruction.b(.al, 12),
1318 .expected = 0b1110_101_0_0000_0000_0000_0000_0000_0011,
1319 },
1320 .{ // bl #-4
1321 .inst = Instruction.bl(.al, -4),
1322 .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1111,
1323 },
1324 .{ // bx lr
1325 .inst = Instruction.bx(.al, .lr),
1326 .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110,
1327 },
1328 .{ // svc #0
1329 .inst = Instruction.svc(.al, 0),
1330 .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000,
1331 },
1332 .{ // bkpt #42
1333 .inst = Instruction.bkpt(42),
1334 .expected = 0b1110_0001_0010_000000000010_0111_1010,
1335 },
1336 .{ // stmdb r9, {r0}
1337 .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }),
1338 .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001,
1339 },
1340 .{ // ldmea r4!, {r2, r5}
1341 .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }),
1342 .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100,
1343 },
1344 .{ // qadd r0, r7, r8
1345 .inst = Instruction.qadd(.al, .r0, .r7, .r8),
1346 .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111,
1347 },
1348 };
1349
1350 for (testcases) |case| {
1351 const actual = case.inst.toU32();
1352 try testing.expectEqual(case.expected, actual);
1353 }
1354}
1355
1356test "aliases" {
1357 const Testcase = struct {
1358 expected: Instruction,
1359 actual: Instruction,
1360 };
1361
1362 const testcases = [_]Testcase{
1363 .{ // pop { r6 }
1364 .actual = Instruction.pop(.al, .{.r6}),
1365 .expected = Instruction.ldr(.al, .r6, .sp, .{
1366 .pre_index = false,
1367 .positive = true,
1368 .offset = Instruction.Offset.imm(4),
1369 .write_back = false,
1370 }),
1371 },
1372 .{ // pop { r1, r5 }
1373 .actual = Instruction.pop(.al, .{ .r1, .r5 }),
1374 .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }),
1375 },
1376 .{ // push { r3 }
1377 .actual = Instruction.push(.al, .{.r3}),
1378 .expected = Instruction.str(.al, .r3, .sp, .{
1379 .pre_index = true,
1380 .positive = false,
1381 .offset = Instruction.Offset.imm(4),
1382 .write_back = true,
1383 }),
1384 },
1385 .{ // push { r0, r2 }
1386 .actual = Instruction.push(.al, .{ .r0, .r2 }),
1387 .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }),
1388 },
1389 .{ // lsl r4, r5, #5
1390 .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)),
1391 .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg(
1392 .r5,
1393 Instruction.Operand.Shift.imm(5, .logical_left),
1394 )),
1395 },
1396 .{ // asrs r1, r1, r3
1397 .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)),
1398 .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg(
1399 .r1,
1400 Instruction.Operand.Shift.reg(.r3, .arithmetic_right),
1401 )),
1402 },
1403 };
1404
1405 for (testcases) |case| {
1406 try testing.expectEqual(case.expected.toU32(), case.actual.toU32());
1407 }
1408}
src/arch/riscv64/bits.zig created+470
......@@ -0,0 +1,470 @@
1const std = @import("std");
2const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6// TODO: this is only tagged to facilitate the monstrosity.
7// Once packed structs work make it packed.
8pub const Instruction = union(enum) {
9 R: packed struct {
10 opcode: u7,
11 rd: u5,
12 funct3: u3,
13 rs1: u5,
14 rs2: u5,
15 funct7: u7,
16 },
17 I: packed struct {
18 opcode: u7,
19 rd: u5,
20 funct3: u3,
21 rs1: u5,
22 imm0_11: u12,
23 },
24 S: packed struct {
25 opcode: u7,
26 imm0_4: u5,
27 funct3: u3,
28 rs1: u5,
29 rs2: u5,
30 imm5_11: u7,
31 },
32 B: packed struct {
33 opcode: u7,
34 imm11: u1,
35 imm1_4: u4,
36 funct3: u3,
37 rs1: u5,
38 rs2: u5,
39 imm5_10: u6,
40 imm12: u1,
41 },
42 U: packed struct {
43 opcode: u7,
44 rd: u5,
45 imm12_31: u20,
46 },
47 J: packed struct {
48 opcode: u7,
49 rd: u5,
50 imm12_19: u8,
51 imm11: u1,
52 imm1_10: u10,
53 imm20: u1,
54 },
55
56 // TODO: once packed structs work we can remove this monstrosity.
57 pub fn toU32(self: Instruction) u32 {
58 return switch (self) {
59 .R => |v| @bitCast(u32, v),
60 .I => |v| @bitCast(u32, v),
61 .S => |v| @bitCast(u32, v),
62 .B => |v| @intCast(u32, v.opcode) + (@intCast(u32, v.imm11) << 7) + (@intCast(u32, v.imm1_4) << 8) + (@intCast(u32, v.funct3) << 12) + (@intCast(u32, v.rs1) << 15) + (@intCast(u32, v.rs2) << 20) + (@intCast(u32, v.imm5_10) << 25) + (@intCast(u32, v.imm12) << 31),
63 .U => |v| @bitCast(u32, v),
64 .J => |v| @bitCast(u32, v),
65 };
66 }
67
68 fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction {
69 return Instruction{
70 .R = .{
71 .opcode = op,
72 .funct3 = fn3,
73 .funct7 = fn7,
74 .rd = @enumToInt(rd),
75 .rs1 = @enumToInt(r1),
76 .rs2 = @enumToInt(r2),
77 },
78 };
79 }
80
81 // RISC-V is all signed all the time -- convert immediates to unsigned for processing
82 fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction {
83 const umm = @bitCast(u12, imm);
84
85 return Instruction{
86 .I = .{
87 .opcode = op,
88 .funct3 = fn3,
89 .rd = @enumToInt(rd),
90 .rs1 = @enumToInt(r1),
91 .imm0_11 = umm,
92 },
93 };
94 }
95
96 fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction {
97 const umm = @bitCast(u12, imm);
98
99 return Instruction{
100 .S = .{
101 .opcode = op,
102 .funct3 = fn3,
103 .rs1 = @enumToInt(r1),
104 .rs2 = @enumToInt(r2),
105 .imm0_4 = @truncate(u5, umm),
106 .imm5_11 = @truncate(u7, umm >> 5),
107 },
108 };
109 }
110
111 // Use significance value rather than bit value, same for J-type
112 // -- less burden on callsite, bonus semantic checking
113 fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
114 const umm = @bitCast(u13, imm);
115 assert(umm % 2 == 0); // misaligned branch target
116
117 return Instruction{
118 .B = .{
119 .opcode = op,
120 .funct3 = fn3,
121 .rs1 = @enumToInt(r1),
122 .rs2 = @enumToInt(r2),
123 .imm1_4 = @truncate(u4, umm >> 1),
124 .imm5_10 = @truncate(u6, umm >> 5),
125 .imm11 = @truncate(u1, umm >> 11),
126 .imm12 = @truncate(u1, umm >> 12),
127 },
128 };
129 }
130
131 // We have to extract the 20 bits anyway -- let's not make it more painful
132 fn uType(op: u7, rd: Register, imm: i20) Instruction {
133 const umm = @bitCast(u20, imm);
134
135 return Instruction{
136 .U = .{
137 .opcode = op,
138 .rd = @enumToInt(rd),
139 .imm12_31 = umm,
140 },
141 };
142 }
143
144 fn jType(op: u7, rd: Register, imm: i21) Instruction {
145 const umm = @bitCast(u21, imm);
146 assert(umm % 2 == 0); // misaligned jump target
147
148 return Instruction{
149 .J = .{
150 .opcode = op,
151 .rd = @enumToInt(rd),
152 .imm1_10 = @truncate(u10, umm >> 1),
153 .imm11 = @truncate(u1, umm >> 11),
154 .imm12_19 = @truncate(u8, umm >> 12),
155 .imm20 = @truncate(u1, umm >> 20),
156 },
157 };
158 }
159
160 // The meat and potatoes. Arguments are in the order in which they would appear in assembly code.
161
162 // Arithmetic/Logical, Register-Register
163
164 pub fn add(rd: Register, r1: Register, r2: Register) Instruction {
165 return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2);
166 }
167
168 pub fn sub(rd: Register, r1: Register, r2: Register) Instruction {
169 return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2);
170 }
171
172 pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction {
173 return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2);
174 }
175
176 pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction {
177 return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2);
178 }
179
180 pub fn xor(rd: Register, r1: Register, r2: Register) Instruction {
181 return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2);
182 }
183
184 pub fn sll(rd: Register, r1: Register, r2: Register) Instruction {
185 return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2);
186 }
187
188 pub fn srl(rd: Register, r1: Register, r2: Register) Instruction {
189 return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2);
190 }
191
192 pub fn sra(rd: Register, r1: Register, r2: Register) Instruction {
193 return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2);
194 }
195
196 pub fn slt(rd: Register, r1: Register, r2: Register) Instruction {
197 return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2);
198 }
199
200 pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction {
201 return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2);
202 }
203
204 // Arithmetic/Logical, Register-Register (32-bit)
205
206 pub fn addw(rd: Register, r1: Register, r2: Register) Instruction {
207 return rType(0b0111011, 0b000, rd, r1, r2);
208 }
209
210 pub fn subw(rd: Register, r1: Register, r2: Register) Instruction {
211 return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2);
212 }
213
214 pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction {
215 return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2);
216 }
217
218 pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction {
219 return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2);
220 }
221
222 pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction {
223 return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2);
224 }
225
226 // Arithmetic/Logical, Register-Immediate
227
228 pub fn addi(rd: Register, r1: Register, imm: i12) Instruction {
229 return iType(0b0010011, 0b000, rd, r1, imm);
230 }
231
232 pub fn andi(rd: Register, r1: Register, imm: i12) Instruction {
233 return iType(0b0010011, 0b111, rd, r1, imm);
234 }
235
236 pub fn ori(rd: Register, r1: Register, imm: i12) Instruction {
237 return iType(0b0010011, 0b110, rd, r1, imm);
238 }
239
240 pub fn xori(rd: Register, r1: Register, imm: i12) Instruction {
241 return iType(0b0010011, 0b100, rd, r1, imm);
242 }
243
244 pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction {
245 return iType(0b0010011, 0b001, rd, r1, shamt);
246 }
247
248 pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction {
249 return iType(0b0010011, 0b101, rd, r1, shamt);
250 }
251
252 pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction {
253 return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt);
254 }
255
256 pub fn slti(rd: Register, r1: Register, imm: i12) Instruction {
257 return iType(0b0010011, 0b010, rd, r1, imm);
258 }
259
260 pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction {
261 return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm));
262 }
263
264 // Arithmetic/Logical, Register-Immediate (32-bit)
265
266 pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction {
267 return iType(0b0011011, 0b000, rd, r1, imm);
268 }
269
270 pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction {
271 return iType(0b0011011, 0b001, rd, r1, shamt);
272 }
273
274 pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction {
275 return iType(0b0011011, 0b101, rd, r1, shamt);
276 }
277
278 pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction {
279 return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt);
280 }
281
282 // Upper Immediate
283
284 pub fn lui(rd: Register, imm: i20) Instruction {
285 return uType(0b0110111, rd, imm);
286 }
287
288 pub fn auipc(rd: Register, imm: i20) Instruction {
289 return uType(0b0010111, rd, imm);
290 }
291
292 // Load
293
294 pub fn ld(rd: Register, offset: i12, base: Register) Instruction {
295 return iType(0b0000011, 0b011, rd, base, offset);
296 }
297
298 pub fn lw(rd: Register, offset: i12, base: Register) Instruction {
299 return iType(0b0000011, 0b010, rd, base, offset);
300 }
301
302 pub fn lwu(rd: Register, offset: i12, base: Register) Instruction {
303 return iType(0b0000011, 0b110, rd, base, offset);
304 }
305
306 pub fn lh(rd: Register, offset: i12, base: Register) Instruction {
307 return iType(0b0000011, 0b001, rd, base, offset);
308 }
309
310 pub fn lhu(rd: Register, offset: i12, base: Register) Instruction {
311 return iType(0b0000011, 0b101, rd, base, offset);
312 }
313
314 pub fn lb(rd: Register, offset: i12, base: Register) Instruction {
315 return iType(0b0000011, 0b000, rd, base, offset);
316 }
317
318 pub fn lbu(rd: Register, offset: i12, base: Register) Instruction {
319 return iType(0b0000011, 0b100, rd, base, offset);
320 }
321
322 // Store
323
324 pub fn sd(rs: Register, offset: i12, base: Register) Instruction {
325 return sType(0b0100011, 0b011, base, rs, offset);
326 }
327
328 pub fn sw(rs: Register, offset: i12, base: Register) Instruction {
329 return sType(0b0100011, 0b010, base, rs, offset);
330 }
331
332 pub fn sh(rs: Register, offset: i12, base: Register) Instruction {
333 return sType(0b0100011, 0b001, base, rs, offset);
334 }
335
336 pub fn sb(rs: Register, offset: i12, base: Register) Instruction {
337 return sType(0b0100011, 0b000, base, rs, offset);
338 }
339
340 // Fence
341 // TODO: implement fence
342
343 // Branch
344
345 pub fn beq(r1: Register, r2: Register, offset: i13) Instruction {
346 return bType(0b1100011, 0b000, r1, r2, offset);
347 }
348
349 pub fn bne(r1: Register, r2: Register, offset: i13) Instruction {
350 return bType(0b1100011, 0b001, r1, r2, offset);
351 }
352
353 pub fn blt(r1: Register, r2: Register, offset: i13) Instruction {
354 return bType(0b1100011, 0b100, r1, r2, offset);
355 }
356
357 pub fn bge(r1: Register, r2: Register, offset: i13) Instruction {
358 return bType(0b1100011, 0b101, r1, r2, offset);
359 }
360
361 pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction {
362 return bType(0b1100011, 0b110, r1, r2, offset);
363 }
364
365 pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction {
366 return bType(0b1100011, 0b111, r1, r2, offset);
367 }
368
369 // Jump
370
371 pub fn jal(link: Register, offset: i21) Instruction {
372 return jType(0b1101111, link, offset);
373 }
374
375 pub fn jalr(link: Register, offset: i12, base: Register) Instruction {
376 return iType(0b1100111, 0b000, link, base, offset);
377 }
378
379 // System
380
381 pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
382 pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
383};
384
385// zig fmt: off
386pub const RawRegister = enum(u5) {
387 x0, x1, x2, x3, x4, x5, x6, x7,
388 x8, x9, x10, x11, x12, x13, x14, x15,
389 x16, x17, x18, x19, x20, x21, x22, x23,
390 x24, x25, x26, x27, x28, x29, x30, x31,
391
392 pub fn dwarfLocOp(reg: RawRegister) u8 {
393 return @enumToInt(reg) + DW.OP.reg0;
394 }
395};
396
397pub const Register = enum(u5) {
398 // 64 bit registers
399 zero, // zero
400 ra, // return address. caller saved
401 sp, // stack pointer. callee saved.
402 gp, // global pointer
403 tp, // thread pointer
404 t0, t1, t2, // temporaries. caller saved.
405 s0, // s0/fp, callee saved.
406 s1, // callee saved.
407 a0, a1, // fn args/return values. caller saved.
408 a2, a3, a4, a5, a6, a7, // fn args. caller saved.
409 s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
410 t3, t4, t5, t6, // caller saved
411
412 pub fn parseRegName(name: []const u8) ?Register {
413 if(std.meta.stringToEnum(Register, name)) |reg| return reg;
414 if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg));
415 return null;
416 }
417
418 /// Returns the index into `callee_preserved_regs`.
419 pub fn allocIndex(self: Register) ?u4 {
420 inline for(callee_preserved_regs) |cpreg, i| {
421 if(self == cpreg) return i;
422 }
423 return null;
424 }
425
426 pub fn dwarfLocOp(reg: Register) u8 {
427 return @as(u8, @enumToInt(reg)) + DW.OP.reg0;
428 }
429};
430
431// zig fmt: on
432
433pub const callee_preserved_regs = [_]Register{
434 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
435};
436
437test "serialize instructions" {
438 const Testcase = struct {
439 inst: Instruction,
440 expected: u32,
441 };
442
443 const testcases = [_]Testcase{
444 .{ // add t6, zero, zero
445 .inst = Instruction.add(.t6, .zero, .zero),
446 .expected = 0b0000000_00000_00000_000_11111_0110011,
447 },
448 .{ // sd s0, 0x7f(s0)
449 .inst = Instruction.sd(.s0, 0x7f, .s0),
450 .expected = 0b0000011_01000_01000_011_11111_0100011,
451 },
452 .{ // bne s0, s1, 0x42
453 .inst = Instruction.bne(.s0, .s1, 0x42),
454 .expected = 0b0_000010_01001_01000_001_0001_0_1100011,
455 },
456 .{ // j 0x1a
457 .inst = Instruction.jal(.zero, 0x1a),
458 .expected = 0b0_0000001101_0_00000000_00000_1101111,
459 },
460 .{ // ebreak
461 .inst = Instruction.ebreak,
462 .expected = 0b000000000001_00000_000_00000_1110011,
463 },
464 };
465
466 for (testcases) |case| {
467 const actual = case.inst.toU32();
468 try testing.expectEqual(case.expected, actual);
469 }
470}
src/arch/x86/bits.zig created+123
......@@ -0,0 +1,123 @@
1const std = @import("std");
2const DW = std.dwarf;
3
4// zig fmt: off
5pub const Register = enum(u8) {
6 // 0 through 7, 32-bit registers. id is int value
7 eax, ecx, edx, ebx, esp, ebp, esi, edi,
8
9 // 8-15, 16-bit registers. id is int value - 8.
10 ax, cx, dx, bx, sp, bp, si, di,
11
12 // 16-23, 8-bit registers. id is int value - 16.
13 al, cl, dl, bl, ah, ch, dh, bh,
14
15 /// Returns the bit-width of the register.
16 pub fn size(self: @This()) u7 {
17 return switch (@enumToInt(self)) {
18 0...7 => 32,
19 8...15 => 16,
20 16...23 => 8,
21 else => unreachable,
22 };
23 }
24
25 /// Returns the register's id. This is used in practically every opcode the
26 /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move
27 /// instruction, and is used in the R/M byte.
28 pub fn id(self: @This()) u3 {
29 return @truncate(u3, @enumToInt(self));
30 }
31
32 /// Returns the index into `callee_preserved_regs`.
33 pub fn allocIndex(self: Register) ?u4 {
34 return switch (self) {
35 .eax, .ax, .al => 0,
36 .ecx, .cx, .cl => 1,
37 .edx, .dx, .dl => 2,
38 .esi, .si => 3,
39 .edi, .di => 4,
40 else => null,
41 };
42 }
43
44 /// Convert from any register to its 32 bit alias.
45 pub fn to32(self: Register) Register {
46 return @intToEnum(Register, @as(u8, self.id()));
47 }
48
49 /// Convert from any register to its 16 bit alias.
50 pub fn to16(self: Register) Register {
51 return @intToEnum(Register, @as(u8, self.id()) + 8);
52 }
53
54 /// Convert from any register to its 8 bit alias.
55 pub fn to8(self: Register) Register {
56 return @intToEnum(Register, @as(u8, self.id()) + 16);
57 }
58
59
60 pub fn dwarfLocOp(reg: Register) u8 {
61 return switch (reg.to32()) {
62 .eax => DW.OP.reg0,
63 .ecx => DW.OP.reg1,
64 .edx => DW.OP.reg2,
65 .ebx => DW.OP.reg3,
66 .esp => DW.OP.reg4,
67 .ebp => DW.OP.reg5,
68 .esi => DW.OP.reg6,
69 .edi => DW.OP.reg7,
70 else => unreachable,
71 };
72 }
73};
74
75// zig fmt: on
76
77pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi };
78
79// TODO add these to Register enum and corresponding dwarfLocOp
80// // Return Address register. This is stored in `0(%esp, "")` and is not a physical register.
81// RA = (8, "RA"),
82//
83// ST0 = (11, "st0"),
84// ST1 = (12, "st1"),
85// ST2 = (13, "st2"),
86// ST3 = (14, "st3"),
87// ST4 = (15, "st4"),
88// ST5 = (16, "st5"),
89// ST6 = (17, "st6"),
90// ST7 = (18, "st7"),
91//
92// XMM0 = (21, "xmm0"),
93// XMM1 = (22, "xmm1"),
94// XMM2 = (23, "xmm2"),
95// XMM3 = (24, "xmm3"),
96// XMM4 = (25, "xmm4"),
97// XMM5 = (26, "xmm5"),
98// XMM6 = (27, "xmm6"),
99// XMM7 = (28, "xmm7"),
100//
101// MM0 = (29, "mm0"),
102// MM1 = (30, "mm1"),
103// MM2 = (31, "mm2"),
104// MM3 = (32, "mm3"),
105// MM4 = (33, "mm4"),
106// MM5 = (34, "mm5"),
107// MM6 = (35, "mm6"),
108// MM7 = (36, "mm7"),
109//
110// MXCSR = (39, "mxcsr"),
111//
112// ES = (40, "es"),
113// CS = (41, "cs"),
114// SS = (42, "ss"),
115// DS = (43, "ds"),
116// FS = (44, "fs"),
117// GS = (45, "gs"),
118//
119// TR = (48, "tr"),
120// LDTR = (49, "ldtr"),
121//
122// FS_BASE = (93, "fs.base"),
123// GS_BASE = (94, "gs.base"),
src/arch/x86_64/bits.zig created+716
......@@ -0,0 +1,716 @@
1const std = @import("std");
2const testing = std.testing;
3const mem = std.mem;
4const assert = std.debug.assert;
5const ArrayList = std.ArrayList;
6const Allocator = std.mem.Allocator;
7const DW = std.dwarf;
8
9// zig fmt: off
10
11/// Definitions of all of the x64 registers. The order is semantically meaningful.
12/// The registers are defined such that IDs go in descending order of 64-bit,
13/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
14/// registers. This results in some useful properties:
15///
16/// Any 64-bit register can be turned into its 32-bit form by adding 16, and
17/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it
18/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit
19/// form.
20///
21/// If (register & 8) is set, the register is extended.
22///
23/// The ID can be easily determined by figuring out what range the register is
24/// in, and then subtracting the base.
25pub const Register = enum(u8) {
26 // 0 through 15, 64-bit registers. 8-15 are extended.
27 // id is just the int value.
28 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
29 r8, r9, r10, r11, r12, r13, r14, r15,
30
31 // 16 through 31, 32-bit registers. 24-31 are extended.
32 // id is int value - 16.
33 eax, ecx, edx, ebx, esp, ebp, esi, edi,
34 r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d,
35
36 // 32-47, 16-bit registers. 40-47 are extended.
37 // id is int value - 32.
38 ax, cx, dx, bx, sp, bp, si, di,
39 r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w,
40
41 // 48-63, 8-bit registers. 56-63 are extended.
42 // id is int value - 48.
43 al, cl, dl, bl, ah, ch, dh, bh,
44 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
45
46 /// Returns the bit-width of the register.
47 pub fn size(self: Register) u7 {
48 return switch (@enumToInt(self)) {
49 0...15 => 64,
50 16...31 => 32,
51 32...47 => 16,
52 48...64 => 8,
53 else => unreachable,
54 };
55 }
56
57 /// Returns whether the register is *extended*. Extended registers are the
58 /// new registers added with amd64, r8 through r15. This also includes any
59 /// other variant of access to those registers, such as r8b, r15d, and so
60 /// on. This is needed because access to these registers requires special
61 /// handling via the REX prefix, via the B or R bits, depending on context.
62 pub fn isExtended(self: Register) bool {
63 return @enumToInt(self) & 0x08 != 0;
64 }
65
66 /// This returns the 4-bit register ID, which is used in practically every
67 /// opcode. Note that bit 3 (the highest bit) is *never* used directly in
68 /// an instruction (@see isExtended), and requires special handling. The
69 /// lower three bits are often embedded directly in instructions (such as
70 /// the B8 variant of moves), or used in R/M bytes.
71 pub fn id(self: Register) u4 {
72 return @truncate(u4, @enumToInt(self));
73 }
74
75 /// Like id, but only returns the lower 3 bits.
76 pub fn low_id(self: Register) u3 {
77 return @truncate(u3, @enumToInt(self));
78 }
79
80 /// Returns the index into `callee_preserved_regs`.
81 pub fn allocIndex(self: Register) ?u4 {
82 return switch (self) {
83 .rax, .eax, .ax, .al => 0,
84 .rcx, .ecx, .cx, .cl => 1,
85 .rdx, .edx, .dx, .dl => 2,
86 .rsi, .esi, .si => 3,
87 .rdi, .edi, .di => 4,
88 .r8, .r8d, .r8w, .r8b => 5,
89 .r9, .r9d, .r9w, .r9b => 6,
90 .r10, .r10d, .r10w, .r10b => 7,
91 .r11, .r11d, .r11w, .r11b => 8,
92 else => null,
93 };
94 }
95
96 /// Convert from any register to its 64 bit alias.
97 pub fn to64(self: Register) Register {
98 return @intToEnum(Register, self.id());
99 }
100
101 /// Convert from any register to its 32 bit alias.
102 pub fn to32(self: Register) Register {
103 return @intToEnum(Register, @as(u8, self.id()) + 16);
104 }
105
106 /// Convert from any register to its 16 bit alias.
107 pub fn to16(self: Register) Register {
108 return @intToEnum(Register, @as(u8, self.id()) + 32);
109 }
110
111 /// Convert from any register to its 8 bit alias.
112 pub fn to8(self: Register) Register {
113 return @intToEnum(Register, @as(u8, self.id()) + 48);
114 }
115
116 pub fn dwarfLocOp(self: Register) u8 {
117 return switch (self.to64()) {
118 .rax => DW.OP.reg0,
119 .rdx => DW.OP.reg1,
120 .rcx => DW.OP.reg2,
121 .rbx => DW.OP.reg3,
122 .rsi => DW.OP.reg4,
123 .rdi => DW.OP.reg5,
124 .rbp => DW.OP.reg6,
125 .rsp => DW.OP.reg7,
126
127 .r8 => DW.OP.reg8,
128 .r9 => DW.OP.reg9,
129 .r10 => DW.OP.reg10,
130 .r11 => DW.OP.reg11,
131 .r12 => DW.OP.reg12,
132 .r13 => DW.OP.reg13,
133 .r14 => DW.OP.reg14,
134 .r15 => DW.OP.reg15,
135
136 else => unreachable,
137 };
138 }
139};
140
141// zig fmt: on
142
143/// These registers belong to the called function.
144pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
145pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
146pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
147
148/// Encoding helper functions for x86_64 instructions
149///
150/// Many of these helpers do very little, but they can help make things
151/// slightly more readable with more descriptive field names / function names.
152///
153/// Some of them also have asserts to ensure that we aren't doing dumb things.
154/// For example, trying to use register 4 (esp) in an indirect modr/m byte is illegal,
155/// you need to encode it with an SIB byte.
156///
157/// Note that ALL of these helper functions will assume capacity,
158/// so ensure that the `code` has sufficient capacity before using them.
159/// The `init` method is the recommended way to ensure capacity.
160pub const Encoder = struct {
161 /// Non-owning reference to the code array
162 code: *ArrayList(u8),
163
164 const Self = @This();
165
166 /// Wrap `code` in Encoder to make it easier to call these helper functions
167 ///
168 /// maximum_inst_size should contain the maximum number of bytes
169 /// that the encoded instruction will take.
170 /// This is because the helper functions will assume capacity
171 /// in order to avoid bounds checking.
172 pub fn init(code: *ArrayList(u8), maximum_inst_size: u8) !Self {
173 try code.ensureUnusedCapacity(maximum_inst_size);
174 return Self{ .code = code };
175 }
176
177 /// Directly write a number to the code array with big endianness
178 pub fn writeIntBig(self: Self, comptime T: type, value: T) void {
179 mem.writeIntBig(
180 T,
181 self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)),
182 value,
183 );
184 }
185
186 /// Directly write a number to the code array with little endianness
187 pub fn writeIntLittle(self: Self, comptime T: type, value: T) void {
188 mem.writeIntLittle(
189 T,
190 self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)),
191 value,
192 );
193 }
194
195 // --------
196 // Prefixes
197 // --------
198
199 pub const LegacyPrefixes = packed struct {
200 /// LOCK
201 prefix_f0: bool = false,
202 /// REPNZ, REPNE, REP, Scalar Double-precision
203 prefix_f2: bool = false,
204 /// REPZ, REPE, REP, Scalar Single-precision
205 prefix_f3: bool = false,
206
207 /// CS segment override or Branch not taken
208 prefix_2e: bool = false,
209 /// DS segment override
210 prefix_36: bool = false,
211 /// ES segment override
212 prefix_26: bool = false,
213 /// FS segment override
214 prefix_64: bool = false,
215 /// GS segment override
216 prefix_65: bool = false,
217
218 /// Branch taken
219 prefix_3e: bool = false,
220
221 /// Operand size override (enables 16 bit operation)
222 prefix_66: bool = false,
223
224 /// Address size override (enables 16 bit address size)
225 prefix_67: bool = false,
226
227 padding: u5 = 0,
228 };
229
230 /// Encodes legacy prefixes
231 pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) void {
232 if (@bitCast(u16, prefixes) != 0) {
233 // Hopefully this path isn't taken very often, so we'll do it the slow way for now
234
235 // LOCK
236 if (prefixes.prefix_f0) self.code.appendAssumeCapacity(0xf0);
237 // REPNZ, REPNE, REP, Scalar Double-precision
238 if (prefixes.prefix_f2) self.code.appendAssumeCapacity(0xf2);
239 // REPZ, REPE, REP, Scalar Single-precision
240 if (prefixes.prefix_f3) self.code.appendAssumeCapacity(0xf3);
241
242 // CS segment override or Branch not taken
243 if (prefixes.prefix_2e) self.code.appendAssumeCapacity(0x2e);
244 // DS segment override
245 if (prefixes.prefix_36) self.code.appendAssumeCapacity(0x36);
246 // ES segment override
247 if (prefixes.prefix_26) self.code.appendAssumeCapacity(0x26);
248 // FS segment override
249 if (prefixes.prefix_64) self.code.appendAssumeCapacity(0x64);
250 // GS segment override
251 if (prefixes.prefix_65) self.code.appendAssumeCapacity(0x65);
252
253 // Branch taken
254 if (prefixes.prefix_3e) self.code.appendAssumeCapacity(0x3e);
255
256 // Operand size override
257 if (prefixes.prefix_66) self.code.appendAssumeCapacity(0x66);
258
259 // Address size override
260 if (prefixes.prefix_67) self.code.appendAssumeCapacity(0x67);
261 }
262 }
263
264 /// Use 16 bit operand size
265 ///
266 /// Note that this flag is overridden by REX.W, if both are present.
267 pub fn prefix16BitMode(self: Self) void {
268 self.code.appendAssumeCapacity(0x66);
269 }
270
271 /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB
272 pub const Rex = struct {
273 /// Wide, enables 64-bit operation
274 w: bool = false,
275 /// Extends the reg field in the ModR/M byte
276 r: bool = false,
277 /// Extends the index field in the SIB byte
278 x: bool = false,
279 /// Extends the r/m field in the ModR/M byte,
280 /// or the base field in the SIB byte,
281 /// or the reg field in the Opcode byte
282 b: bool = false,
283 };
284
285 /// Encodes a REX prefix byte given all the fields
286 ///
287 /// Use this byte whenever you need 64 bit operation,
288 /// or one of reg, index, r/m, base, or opcode-reg might be extended.
289 ///
290 /// See struct `Rex` for a description of each field.
291 ///
292 /// Does not add a prefix byte if none of the fields are set!
293 pub fn rex(self: Self, byte: Rex) void {
294 var value: u8 = 0b0100_0000;
295
296 if (byte.w) value |= 0b1000;
297 if (byte.r) value |= 0b0100;
298 if (byte.x) value |= 0b0010;
299 if (byte.b) value |= 0b0001;
300
301 if (value != 0b0100_0000) {
302 self.code.appendAssumeCapacity(value);
303 }
304 }
305
306 // ------
307 // Opcode
308 // ------
309
310 /// Encodes a 1 byte opcode
311 pub fn opcode_1byte(self: Self, opcode: u8) void {
312 self.code.appendAssumeCapacity(opcode);
313 }
314
315 /// Encodes a 2 byte opcode
316 ///
317 /// e.g. IMUL has the opcode 0x0f 0xaf, so you use
318 ///
319 /// encoder.opcode_2byte(0x0f, 0xaf);
320 pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) void {
321 self.code.appendAssumeCapacity(prefix);
322 self.code.appendAssumeCapacity(opcode);
323 }
324
325 /// Encodes a 1 byte opcode with a reg field
326 ///
327 /// Remember to add a REX prefix byte if reg is extended!
328 pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) void {
329 assert(opcode & 0b111 == 0);
330 self.code.appendAssumeCapacity(opcode | reg);
331 }
332
333 // ------
334 // ModR/M
335 // ------
336
337 /// Construct a ModR/M byte given all the fields
338 ///
339 /// Remember to add a REX prefix byte if reg or rm are extended!
340 pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) void {
341 self.code.appendAssumeCapacity(
342 @as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm,
343 );
344 }
345
346 /// Construct a ModR/M byte using direct r/m addressing
347 /// r/m effective address: r/m
348 ///
349 /// Note reg's effective address is always just reg for the ModR/M byte.
350 /// Remember to add a REX prefix byte if reg or rm are extended!
351 pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) void {
352 self.modRm(0b11, reg_or_opx, rm);
353 }
354
355 /// Construct a ModR/M byte using indirect r/m addressing
356 /// r/m effective address: [r/m]
357 ///
358 /// Note reg's effective address is always just reg for the ModR/M byte.
359 /// Remember to add a REX prefix byte if reg or rm are extended!
360 pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) void {
361 assert(rm != 4 and rm != 5);
362 self.modRm(0b00, reg_or_opx, rm);
363 }
364
365 /// Construct a ModR/M byte using indirect SIB addressing
366 /// r/m effective address: [SIB]
367 ///
368 /// Note reg's effective address is always just reg for the ModR/M byte.
369 /// Remember to add a REX prefix byte if reg or rm are extended!
370 pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) void {
371 self.modRm(0b00, reg_or_opx, 0b100);
372 }
373
374 /// Construct a ModR/M byte using RIP-relative addressing
375 /// r/m effective address: [RIP + disp32]
376 ///
377 /// Note reg's effective address is always just reg for the ModR/M byte.
378 /// Remember to add a REX prefix byte if reg or rm are extended!
379 pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) void {
380 self.modRm(0b00, reg_or_opx, 0b101);
381 }
382
383 /// Construct a ModR/M byte using indirect r/m with a 8bit displacement
384 /// r/m effective address: [r/m + disp8]
385 ///
386 /// Note reg's effective address is always just reg for the ModR/M byte.
387 /// Remember to add a REX prefix byte if reg or rm are extended!
388 pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) void {
389 assert(rm != 4);
390 self.modRm(0b01, reg_or_opx, rm);
391 }
392
393 /// Construct a ModR/M byte using indirect SIB with a 8bit displacement
394 /// r/m effective address: [SIB + disp8]
395 ///
396 /// Note reg's effective address is always just reg for the ModR/M byte.
397 /// Remember to add a REX prefix byte if reg or rm are extended!
398 pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) void {
399 self.modRm(0b01, reg_or_opx, 0b100);
400 }
401
402 /// Construct a ModR/M byte using indirect r/m with a 32bit displacement
403 /// r/m effective address: [r/m + disp32]
404 ///
405 /// Note reg's effective address is always just reg for the ModR/M byte.
406 /// Remember to add a REX prefix byte if reg or rm are extended!
407 pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) void {
408 assert(rm != 4);
409 self.modRm(0b10, reg_or_opx, rm);
410 }
411
412 /// Construct a ModR/M byte using indirect SIB with a 32bit displacement
413 /// r/m effective address: [SIB + disp32]
414 ///
415 /// Note reg's effective address is always just reg for the ModR/M byte.
416 /// Remember to add a REX prefix byte if reg or rm are extended!
417 pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) void {
418 self.modRm(0b10, reg_or_opx, 0b100);
419 }
420
421 // ---
422 // SIB
423 // ---
424
425 /// Construct a SIB byte given all the fields
426 ///
427 /// Remember to add a REX prefix byte if index or base are extended!
428 pub fn sib(self: Self, scale: u2, index: u3, base: u3) void {
429 self.code.appendAssumeCapacity(
430 @as(u8, scale) << 6 | @as(u8, index) << 3 | base,
431 );
432 }
433
434 /// Construct a SIB byte with scale * index + base, no frills.
435 /// r/m effective address: [base + scale * index]
436 ///
437 /// Remember to add a REX prefix byte if index or base are extended!
438 pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) void {
439 assert(base != 5);
440
441 self.sib(scale, index, base);
442 }
443
444 /// Construct a SIB byte with scale * index + disp32
445 /// r/m effective address: [scale * index + disp32]
446 ///
447 /// Remember to add a REX prefix byte if index or base are extended!
448 pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) void {
449 assert(index != 4);
450
451 // scale is actually ignored
452 // index = 4 means no index
453 // base = 5 means no base, if mod == 0.
454 self.sib(scale, index, 5);
455 }
456
457 /// Construct a SIB byte with just base
458 /// r/m effective address: [base]
459 ///
460 /// Remember to add a REX prefix byte if index or base are extended!
461 pub fn sib_base(self: Self, base: u3) void {
462 assert(base != 5);
463
464 // scale is actually ignored
465 // index = 4 means no index
466 self.sib(0, 4, base);
467 }
468
469 /// Construct a SIB byte with just disp32
470 /// r/m effective address: [disp32]
471 ///
472 /// Remember to add a REX prefix byte if index or base are extended!
473 pub fn sib_disp32(self: Self) void {
474 // scale is actually ignored
475 // index = 4 means no index
476 // base = 5 means no base, if mod == 0.
477 self.sib(0, 4, 5);
478 }
479
480 /// Construct a SIB byte with scale * index + base + disp8
481 /// r/m effective address: [base + scale * index + disp8]
482 ///
483 /// Remember to add a REX prefix byte if index or base are extended!
484 pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) void {
485 self.sib(scale, index, base);
486 }
487
488 /// Construct a SIB byte with base + disp8, no index
489 /// r/m effective address: [base + disp8]
490 ///
491 /// Remember to add a REX prefix byte if index or base are extended!
492 pub fn sib_baseDisp8(self: Self, base: u3) void {
493 // scale is ignored
494 // index = 4 means no index
495 self.sib(0, 4, base);
496 }
497
498 /// Construct a SIB byte with scale * index + base + disp32
499 /// r/m effective address: [base + scale * index + disp32]
500 ///
501 /// Remember to add a REX prefix byte if index or base are extended!
502 pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) void {
503 self.sib(scale, index, base);
504 }
505
506 /// Construct a SIB byte with base + disp32, no index
507 /// r/m effective address: [base + disp32]
508 ///
509 /// Remember to add a REX prefix byte if index or base are extended!
510 pub fn sib_baseDisp32(self: Self, base: u3) void {
511 // scale is ignored
512 // index = 4 means no index
513 self.sib(0, 4, base);
514 }
515
516 // -------------------------
517 // Trivial (no bit fiddling)
518 // -------------------------
519
520 /// Encode an 8 bit immediate
521 ///
522 /// It is sign-extended to 64 bits by the cpu.
523 pub fn imm8(self: Self, imm: i8) void {
524 self.code.appendAssumeCapacity(@bitCast(u8, imm));
525 }
526
527 /// Encode an 8 bit displacement
528 ///
529 /// It is sign-extended to 64 bits by the cpu.
530 pub fn disp8(self: Self, disp: i8) void {
531 self.code.appendAssumeCapacity(@bitCast(u8, disp));
532 }
533
534 /// Encode an 16 bit immediate
535 ///
536 /// It is sign-extended to 64 bits by the cpu.
537 pub fn imm16(self: Self, imm: i16) void {
538 self.writeIntLittle(i16, imm);
539 }
540
541 /// Encode an 32 bit immediate
542 ///
543 /// It is sign-extended to 64 bits by the cpu.
544 pub fn imm32(self: Self, imm: i32) void {
545 self.writeIntLittle(i32, imm);
546 }
547
548 /// Encode an 32 bit displacement
549 ///
550 /// It is sign-extended to 64 bits by the cpu.
551 pub fn disp32(self: Self, disp: i32) void {
552 self.writeIntLittle(i32, disp);
553 }
554
555 /// Encode an 64 bit immediate
556 ///
557 /// It is sign-extended to 64 bits by the cpu.
558 pub fn imm64(self: Self, imm: u64) void {
559 self.writeIntLittle(u64, imm);
560 }
561};
562
563test "x86_64 Encoder helpers" {
564 var code = ArrayList(u8).init(testing.allocator);
565 defer code.deinit();
566
567 // simple integer multiplication
568
569 // imul eax,edi
570 // 0faf c7
571 {
572 try code.resize(0);
573 const encoder = try Encoder.init(&code, 4);
574 encoder.rex(.{
575 .r = Register.eax.isExtended(),
576 .b = Register.edi.isExtended(),
577 });
578 encoder.opcode_2byte(0x0f, 0xaf);
579 encoder.modRm_direct(
580 Register.eax.low_id(),
581 Register.edi.low_id(),
582 );
583
584 try testing.expectEqualSlices(u8, &[_]u8{ 0x0f, 0xaf, 0xc7 }, code.items);
585 }
586
587 // simple mov
588
589 // mov eax,edi
590 // 89 f8
591 {
592 try code.resize(0);
593 const encoder = try Encoder.init(&code, 3);
594 encoder.rex(.{
595 .r = Register.edi.isExtended(),
596 .b = Register.eax.isExtended(),
597 });
598 encoder.opcode_1byte(0x89);
599 encoder.modRm_direct(
600 Register.edi.low_id(),
601 Register.eax.low_id(),
602 );
603
604 try testing.expectEqualSlices(u8, &[_]u8{ 0x89, 0xf8 }, code.items);
605 }
606
607 // signed integer addition of 32-bit sign extended immediate to 64 bit register
608
609 // add rcx, 2147483647
610 //
611 // Using the following opcode: REX.W + 81 /0 id, we expect the following encoding
612 //
613 // 48 : REX.W set for 64 bit operand (*r*cx)
614 // 81 : opcode for "<arithmetic> with immediate"
615 // c1 : id = rcx,
616 // : c1 = 11 <-- mod = 11 indicates r/m is register (rcx)
617 // : 000 <-- opcode_extension = 0 because opcode extension is /0. /0 specifies ADD
618 // : 001 <-- 001 is rcx
619 // ffffff7f : 2147483647
620 {
621 try code.resize(0);
622 const encoder = try Encoder.init(&code, 7);
623 encoder.rex(.{ .w = true }); // use 64 bit operation
624 encoder.opcode_1byte(0x81);
625 encoder.modRm_direct(
626 0,
627 Register.rcx.low_id(),
628 );
629 encoder.imm32(2147483647);
630
631 try testing.expectEqualSlices(u8, &[_]u8{ 0x48, 0x81, 0xc1, 0xff, 0xff, 0xff, 0x7f }, code.items);
632 }
633}
634
635// TODO add these registers to the enum and populate dwarfLocOp
636// // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register.
637// RA = (16, "RA"),
638//
639// XMM0 = (17, "xmm0"),
640// XMM1 = (18, "xmm1"),
641// XMM2 = (19, "xmm2"),
642// XMM3 = (20, "xmm3"),
643// XMM4 = (21, "xmm4"),
644// XMM5 = (22, "xmm5"),
645// XMM6 = (23, "xmm6"),
646// XMM7 = (24, "xmm7"),
647//
648// XMM8 = (25, "xmm8"),
649// XMM9 = (26, "xmm9"),
650// XMM10 = (27, "xmm10"),
651// XMM11 = (28, "xmm11"),
652// XMM12 = (29, "xmm12"),
653// XMM13 = (30, "xmm13"),
654// XMM14 = (31, "xmm14"),
655// XMM15 = (32, "xmm15"),
656//
657// ST0 = (33, "st0"),
658// ST1 = (34, "st1"),
659// ST2 = (35, "st2"),
660// ST3 = (36, "st3"),
661// ST4 = (37, "st4"),
662// ST5 = (38, "st5"),
663// ST6 = (39, "st6"),
664// ST7 = (40, "st7"),
665//
666// MM0 = (41, "mm0"),
667// MM1 = (42, "mm1"),
668// MM2 = (43, "mm2"),
669// MM3 = (44, "mm3"),
670// MM4 = (45, "mm4"),
671// MM5 = (46, "mm5"),
672// MM6 = (47, "mm6"),
673// MM7 = (48, "mm7"),
674//
675// RFLAGS = (49, "rFLAGS"),
676// ES = (50, "es"),
677// CS = (51, "cs"),
678// SS = (52, "ss"),
679// DS = (53, "ds"),
680// FS = (54, "fs"),
681// GS = (55, "gs"),
682//
683// FS_BASE = (58, "fs.base"),
684// GS_BASE = (59, "gs.base"),
685//
686// TR = (62, "tr"),
687// LDTR = (63, "ldtr"),
688// MXCSR = (64, "mxcsr"),
689// FCW = (65, "fcw"),
690// FSW = (66, "fsw"),
691//
692// XMM16 = (67, "xmm16"),
693// XMM17 = (68, "xmm17"),
694// XMM18 = (69, "xmm18"),
695// XMM19 = (70, "xmm19"),
696// XMM20 = (71, "xmm20"),
697// XMM21 = (72, "xmm21"),
698// XMM22 = (73, "xmm22"),
699// XMM23 = (74, "xmm23"),
700// XMM24 = (75, "xmm24"),
701// XMM25 = (76, "xmm25"),
702// XMM26 = (77, "xmm26"),
703// XMM27 = (78, "xmm27"),
704// XMM28 = (79, "xmm28"),
705// XMM29 = (80, "xmm29"),
706// XMM30 = (81, "xmm30"),
707// XMM31 = (82, "xmm31"),
708//
709// K0 = (118, "k0"),
710// K1 = (119, "k1"),
711// K2 = (120, "k2"),
712// K3 = (121, "k3"),
713// K4 = (122, "k4"),
714// K5 = (123, "k5"),
715// K6 = (124, "k6"),
716// K7 = (125, "k7"),
src/codegen.zig+24-24
......@@ -21,7 +21,7 @@ const log = std.log.scoped(.codegen);
2121const build_options = @import("build_options");
2222const RegisterManager = @import("register_manager.zig").RegisterManager;
2323
24const X8664Encoder = @import("codegen/x86_64.zig").Encoder;
24const X8664Encoder = @import("arch/x86_64/bits.zig").Encoder;
2525
2626pub const FnResult = union(enum) {
2727 /// The `code` parameter passed to `generateSymbol` has the value appended.
......@@ -470,7 +470,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
470470 /// A branch in the ARM instruction set
471471 arm_branch: struct {
472472 pos: usize,
473 cond: @import("codegen/arm.zig").Condition,
473 cond: @import("arch/arm/bits.zig").Condition,
474474 },
475475 };
476476
......@@ -5336,11 +5336,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
53365336 }
53375337
53385338 const Register = switch (arch) {
5339 .i386 => @import("codegen/x86.zig").Register,
5340 .x86_64 => @import("codegen/x86_64.zig").Register,
5341 .riscv64 => @import("codegen/riscv64.zig").Register,
5342 .arm, .armeb => @import("codegen/arm.zig").Register,
5343 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Register,
5339 .i386 => @import("arch/x86/bits.zig").Register,
5340 .x86_64 => @import("arch/x86_64/bits.zig").Register,
5341 .riscv64 => @import("arch/riscv64/bits.zig").Register,
5342 .arm, .armeb => @import("arch/arm/bits.zig").Register,
5343 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Register,
53445344 else => enum {
53455345 dummy,
53465346
......@@ -5352,39 +5352,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
53525352 };
53535353
53545354 const Instruction = switch (arch) {
5355 .riscv64 => @import("codegen/riscv64.zig").Instruction,
5356 .arm, .armeb => @import("codegen/arm.zig").Instruction,
5357 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Instruction,
5355 .riscv64 => @import("arch/riscv64/bits.zig").Instruction,
5356 .arm, .armeb => @import("arch/arm/bits.zig").Instruction,
5357 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Instruction,
53585358 else => void,
53595359 };
53605360
53615361 const Condition = switch (arch) {
5362 .arm, .armeb => @import("codegen/arm.zig").Condition,
5362 .arm, .armeb => @import("arch/arm/bits.zig").Condition,
53635363 else => void,
53645364 };
53655365
53665366 const callee_preserved_regs = switch (arch) {
5367 .i386 => @import("codegen/x86.zig").callee_preserved_regs,
5368 .x86_64 => @import("codegen/x86_64.zig").callee_preserved_regs,
5369 .riscv64 => @import("codegen/riscv64.zig").callee_preserved_regs,
5370 .arm, .armeb => @import("codegen/arm.zig").callee_preserved_regs,
5371 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").callee_preserved_regs,
5367 .i386 => @import("arch/x86/bits.zig").callee_preserved_regs,
5368 .x86_64 => @import("arch/x86_64/bits.zig").callee_preserved_regs,
5369 .riscv64 => @import("arch/riscv64/bits.zig").callee_preserved_regs,
5370 .arm, .armeb => @import("arch/arm/bits.zig").callee_preserved_regs,
5371 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").callee_preserved_regs,
53725372 else => [_]Register{},
53735373 };
53745374
53755375 const c_abi_int_param_regs = switch (arch) {
5376 .i386 => @import("codegen/x86.zig").c_abi_int_param_regs,
5377 .x86_64 => @import("codegen/x86_64.zig").c_abi_int_param_regs,
5378 .arm, .armeb => @import("codegen/arm.zig").c_abi_int_param_regs,
5379 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_param_regs,
5376 .i386 => @import("arch/x86/bits.zig").c_abi_int_param_regs,
5377 .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_param_regs,
5378 .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_param_regs,
5379 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_param_regs,
53805380 else => [_]Register{},
53815381 };
53825382
53835383 const c_abi_int_return_regs = switch (arch) {
5384 .i386 => @import("codegen/x86.zig").c_abi_int_return_regs,
5385 .x86_64 => @import("codegen/x86_64.zig").c_abi_int_return_regs,
5386 .arm, .armeb => @import("codegen/arm.zig").c_abi_int_return_regs,
5387 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_return_regs,
5384 .i386 => @import("arch/x86/bits.zig").c_abi_int_return_regs,
5385 .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_return_regs,
5386 .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_return_regs,
5387 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_return_regs,
53885388 else => [_]Register{},
53895389 };
53905390
src/codegen/aarch64.zig deleted-1230
......@@ -1,1230 +0,0 @@
1const std = @import("std");
2const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6// zig fmt: off
7
8/// General purpose registers in the AArch64 instruction set
9pub const Register = enum(u6) {
10 // 64-bit registers
11 x0, x1, x2, x3, x4, x5, x6, x7,
12 x8, x9, x10, x11, x12, x13, x14, x15,
13 x16, x17, x18, x19, x20, x21, x22, x23,
14 x24, x25, x26, x27, x28, x29, x30, xzr,
15
16 // 32-bit registers
17 w0, w1, w2, w3, w4, w5, w6, w7,
18 w8, w9, w10, w11, w12, w13, w14, w15,
19 w16, w17, w18, w19, w20, w21, w22, w23,
20 w24, w25, w26, w27, w28, w29, w30, wzr,
21
22 pub const sp = Register.xzr;
23
24 pub fn id(self: Register) u5 {
25 return @truncate(u5, @enumToInt(self));
26 }
27
28 /// Returns the bit-width of the register.
29 pub fn size(self: Register) u7 {
30 return switch (@enumToInt(self)) {
31 0...31 => 64,
32 32...63 => 32,
33 };
34 }
35
36 /// Convert from any register to its 64 bit alias.
37 pub fn to64(self: Register) Register {
38 return @intToEnum(Register, self.id());
39 }
40
41 /// Convert from any register to its 32 bit alias.
42 pub fn to32(self: Register) Register {
43 return @intToEnum(Register, @as(u6, self.id()) + 32);
44 }
45
46 /// Returns the index into `callee_preserved_regs`.
47 pub fn allocIndex(self: Register) ?u4 {
48 inline for (callee_preserved_regs) |cpreg, i| {
49 if (self.id() == cpreg.id()) return i;
50 }
51 return null;
52 }
53
54 pub fn dwarfLocOp(self: Register) u8 {
55 return @as(u8, self.id()) + DW.OP.reg0;
56 }
57};
58
59// zig fmt: on
60
61pub const callee_preserved_regs = [_]Register{
62 .x19, .x20, .x21, .x22, .x23,
63 .x24, .x25, .x26, .x27, .x28,
64};
65
66pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
67pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
68
69test "Register.id" {
70 try testing.expectEqual(@as(u5, 0), Register.x0.id());
71 try testing.expectEqual(@as(u5, 0), Register.w0.id());
72
73 try testing.expectEqual(@as(u5, 31), Register.xzr.id());
74 try testing.expectEqual(@as(u5, 31), Register.wzr.id());
75
76 try testing.expectEqual(@as(u5, 31), Register.sp.id());
77 try testing.expectEqual(@as(u5, 31), Register.sp.id());
78}
79
80test "Register.size" {
81 try testing.expectEqual(@as(u7, 64), Register.x19.size());
82 try testing.expectEqual(@as(u7, 32), Register.w3.size());
83}
84
85test "Register.to64/to32" {
86 try testing.expectEqual(Register.x0, Register.w0.to64());
87 try testing.expectEqual(Register.x0, Register.x0.to64());
88
89 try testing.expectEqual(Register.w3, Register.w3.to32());
90 try testing.expectEqual(Register.w3, Register.x3.to32());
91}
92
93// zig fmt: off
94
95/// Scalar floating point registers in the aarch64 instruction set
96pub const FloatingPointRegister = enum(u8) {
97 // 128-bit registers
98 q0, q1, q2, q3, q4, q5, q6, q7,
99 q8, q9, q10, q11, q12, q13, q14, q15,
100 q16, q17, q18, q19, q20, q21, q22, q23,
101 q24, q25, q26, q27, q28, q29, q30, q31,
102
103 // 64-bit registers
104 d0, d1, d2, d3, d4, d5, d6, d7,
105 d8, d9, d10, d11, d12, d13, d14, d15,
106 d16, d17, d18, d19, d20, d21, d22, d23,
107 d24, d25, d26, d27, d28, d29, d30, d31,
108
109 // 32-bit registers
110 s0, s1, s2, s3, s4, s5, s6, s7,
111 s8, s9, s10, s11, s12, s13, s14, s15,
112 s16, s17, s18, s19, s20, s21, s22, s23,
113 s24, s25, s26, s27, s28, s29, s30, s31,
114
115 // 16-bit registers
116 h0, h1, h2, h3, h4, h5, h6, h7,
117 h8, h9, h10, h11, h12, h13, h14, h15,
118 h16, h17, h18, h19, h20, h21, h22, h23,
119 h24, h25, h26, h27, h28, h29, h30, h31,
120
121 // 8-bit registers
122 b0, b1, b2, b3, b4, b5, b6, b7,
123 b8, b9, b10, b11, b12, b13, b14, b15,
124 b16, b17, b18, b19, b20, b21, b22, b23,
125 b24, b25, b26, b27, b28, b29, b30, b31,
126
127 pub fn id(self: FloatingPointRegister) u5 {
128 return @truncate(u5, @enumToInt(self));
129 }
130
131 /// Returns the bit-width of the register.
132 pub fn size(self: FloatingPointRegister) u8 {
133 return switch (@enumToInt(self)) {
134 0...31 => 128,
135 32...63 => 64,
136 64...95 => 32,
137 96...127 => 16,
138 128...159 => 8,
139 else => unreachable,
140 };
141 }
142
143 /// Convert from any register to its 128 bit alias.
144 pub fn to128(self: FloatingPointRegister) FloatingPointRegister {
145 return @intToEnum(FloatingPointRegister, self.id());
146 }
147
148 /// Convert from any register to its 64 bit alias.
149 pub fn to64(self: FloatingPointRegister) FloatingPointRegister {
150 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32);
151 }
152
153 /// Convert from any register to its 32 bit alias.
154 pub fn to32(self: FloatingPointRegister) FloatingPointRegister {
155 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64);
156 }
157
158 /// Convert from any register to its 16 bit alias.
159 pub fn to16(self: FloatingPointRegister) FloatingPointRegister {
160 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96);
161 }
162
163 /// Convert from any register to its 8 bit alias.
164 pub fn to8(self: FloatingPointRegister) FloatingPointRegister {
165 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128);
166 }
167};
168
169// zig fmt: on
170
171test "FloatingPointRegister.id" {
172 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id());
173 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.h0.id());
174 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.s0.id());
175 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.id());
176 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.id());
177
178 try testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id());
179 try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id());
180}
181
182test "FloatingPointRegister.size" {
183 try testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size());
184 try testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.size());
185 try testing.expectEqual(@as(u8, 32), FloatingPointRegister.s3.size());
186 try testing.expectEqual(@as(u8, 16), FloatingPointRegister.h4.size());
187 try testing.expectEqual(@as(u8, 8), FloatingPointRegister.b5.size());
188}
189
190test "FloatingPointRegister.toX" {
191 try testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128());
192 try testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128());
193 try testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128());
194
195 try testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64());
196 try testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32());
197 try testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16());
198 try testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8());
199}
200
201/// Represents an instruction in the AArch64 instruction set
202pub const Instruction = union(enum) {
203 move_wide_immediate: packed struct {
204 rd: u5,
205 imm16: u16,
206 hw: u2,
207 fixed: u6 = 0b100101,
208 opc: u2,
209 sf: u1,
210 },
211 pc_relative_address: packed struct {
212 rd: u5,
213 immhi: u19,
214 fixed: u5 = 0b10000,
215 immlo: u2,
216 op: u1,
217 },
218 load_store_register: packed struct {
219 rt: u5,
220 rn: u5,
221 offset: u12,
222 opc: u2,
223 op1: u2,
224 v: u1,
225 fixed: u3 = 0b111,
226 size: u2,
227 },
228 load_store_register_pair: packed struct {
229 rt1: u5,
230 rn: u5,
231 rt2: u5,
232 imm7: u7,
233 load: u1,
234 encoding: u2,
235 fixed: u5 = 0b101_0_0,
236 opc: u2,
237 },
238 load_literal: packed struct {
239 rt: u5,
240 imm19: u19,
241 fixed: u6 = 0b011_0_00,
242 opc: u2,
243 },
244 exception_generation: packed struct {
245 ll: u2,
246 op2: u3,
247 imm16: u16,
248 opc: u3,
249 fixed: u8 = 0b1101_0100,
250 },
251 unconditional_branch_register: packed struct {
252 op4: u5,
253 rn: u5,
254 op3: u6,
255 op2: u5,
256 opc: u4,
257 fixed: u7 = 0b1101_011,
258 },
259 unconditional_branch_immediate: packed struct {
260 imm26: u26,
261 fixed: u5 = 0b00101,
262 op: u1,
263 },
264 no_operation: packed struct {
265 fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111,
266 },
267 logical_shifted_register: packed struct {
268 rd: u5,
269 rn: u5,
270 imm6: u6,
271 rm: u5,
272 n: u1,
273 shift: u2,
274 fixed: u5 = 0b01010,
275 opc: u2,
276 sf: u1,
277 },
278 add_subtract_immediate: packed struct {
279 rd: u5,
280 rn: u5,
281 imm12: u12,
282 sh: u1,
283 fixed: u6 = 0b100010,
284 s: u1,
285 op: u1,
286 sf: u1,
287 },
288 conditional_branch: struct {
289 cond: u4,
290 o0: u1,
291 imm19: u19,
292 o1: u1,
293 fixed: u7 = 0b0101010,
294 },
295 compare_and_branch: struct {
296 rt: u5,
297 imm19: u19,
298 op: u1,
299 fixed: u6 = 0b011010,
300 sf: u1,
301 },
302
303 pub const Shift = struct {
304 shift: Type = .lsl,
305 amount: u6 = 0,
306
307 pub const Type = enum(u2) {
308 lsl,
309 lsr,
310 asr,
311 ror,
312 };
313
314 pub const none = Shift{
315 .shift = .lsl,
316 .amount = 0,
317 };
318 };
319
320 pub const Condition = enum(u4) {
321 /// Integer: Equal
322 /// Floating point: Equal
323 eq,
324 /// Integer: Not equal
325 /// Floating point: Not equal or unordered
326 ne,
327 /// Integer: Carry set
328 /// Floating point: Greater than, equal, or unordered
329 cs,
330 /// Integer: Carry clear
331 /// Floating point: Less than
332 cc,
333 /// Integer: Minus, negative
334 /// Floating point: Less than
335 mi,
336 /// Integer: Plus, positive or zero
337 /// Floating point: Greater than, equal, or unordered
338 pl,
339 /// Integer: Overflow
340 /// Floating point: Unordered
341 vs,
342 /// Integer: No overflow
343 /// Floating point: Ordered
344 vc,
345 /// Integer: Unsigned higher
346 /// Floating point: Greater than, or unordered
347 hi,
348 /// Integer: Unsigned lower or same
349 /// Floating point: Less than or equal
350 ls,
351 /// Integer: Signed greater than or equal
352 /// Floating point: Greater than or equal
353 ge,
354 /// Integer: Signed less than
355 /// Floating point: Less than, or unordered
356 lt,
357 /// Integer: Signed greater than
358 /// Floating point: Greater than
359 gt,
360 /// Integer: Signed less than or equal
361 /// Floating point: Less than, equal, or unordered
362 le,
363 /// Integer: Always
364 /// Floating point: Always
365 al,
366 /// Integer: Always
367 /// Floating point: Always
368 nv,
369 };
370
371 pub fn toU32(self: Instruction) u32 {
372 return switch (self) {
373 .move_wide_immediate => |v| @bitCast(u32, v),
374 .pc_relative_address => |v| @bitCast(u32, v),
375 .load_store_register => |v| @bitCast(u32, v),
376 .load_store_register_pair => |v| @bitCast(u32, v),
377 .load_literal => |v| @bitCast(u32, v),
378 .exception_generation => |v| @bitCast(u32, v),
379 .unconditional_branch_register => |v| @bitCast(u32, v),
380 .unconditional_branch_immediate => |v| @bitCast(u32, v),
381 .no_operation => |v| @bitCast(u32, v),
382 .logical_shifted_register => |v| @bitCast(u32, v),
383 .add_subtract_immediate => |v| @bitCast(u32, v),
384 // TODO once packed structs work, this can be refactored
385 .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25),
386 .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31),
387 };
388 }
389
390 fn moveWideImmediate(
391 opc: u2,
392 rd: Register,
393 imm16: u16,
394 shift: u6,
395 ) Instruction {
396 switch (rd.size()) {
397 32 => {
398 assert(shift % 16 == 0 and shift <= 16);
399 return Instruction{
400 .move_wide_immediate = .{
401 .rd = rd.id(),
402 .imm16 = imm16,
403 .hw = @intCast(u2, shift / 16),
404 .opc = opc,
405 .sf = 0,
406 },
407 };
408 },
409 64 => {
410 assert(shift % 16 == 0 and shift <= 48);
411 return Instruction{
412 .move_wide_immediate = .{
413 .rd = rd.id(),
414 .imm16 = imm16,
415 .hw = @intCast(u2, shift / 16),
416 .opc = opc,
417 .sf = 1,
418 },
419 };
420 },
421 else => unreachable, // unexpected register size
422 }
423 }
424
425 fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction {
426 assert(rd.size() == 64);
427 const imm21_u = @bitCast(u21, imm21);
428 return Instruction{
429 .pc_relative_address = .{
430 .rd = rd.id(),
431 .immlo = @truncate(u2, imm21_u),
432 .immhi = @truncate(u19, imm21_u >> 2),
433 .op = op,
434 },
435 };
436 }
437
438 /// Represents the offset operand of a load or store instruction.
439 /// Data can be loaded from memory with either an immediate offset
440 /// or an offset that is stored in some register.
441 pub const LoadStoreOffset = union(enum) {
442 Immediate: union(enum) {
443 PostIndex: i9,
444 PreIndex: i9,
445 Unsigned: u12,
446 },
447 Register: struct {
448 rm: u5,
449 shift: union(enum) {
450 Uxtw: u2,
451 Lsl: u2,
452 Sxtw: u2,
453 Sxtx: u2,
454 },
455 },
456
457 pub const none = LoadStoreOffset{
458 .Immediate = .{ .Unsigned = 0 },
459 };
460
461 pub fn toU12(self: LoadStoreOffset) u12 {
462 return switch (self) {
463 .Immediate => |imm_type| switch (imm_type) {
464 .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1,
465 .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3,
466 .Unsigned => |v| v,
467 },
468 .Register => |r| switch (r.shift) {
469 .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050,
470 .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050,
471 .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050,
472 .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050,
473 },
474 };
475 }
476
477 pub fn imm(offset: u12) LoadStoreOffset {
478 return .{
479 .Immediate = .{ .Unsigned = offset },
480 };
481 }
482
483 pub fn imm_post_index(offset: i9) LoadStoreOffset {
484 return .{
485 .Immediate = .{ .PostIndex = offset },
486 };
487 }
488
489 pub fn imm_pre_index(offset: i9) LoadStoreOffset {
490 return .{
491 .Immediate = .{ .PreIndex = offset },
492 };
493 }
494
495 pub fn reg(rm: Register) LoadStoreOffset {
496 return .{
497 .Register = .{
498 .rm = rm.id(),
499 .shift = .{
500 .Lsl = 0,
501 },
502 },
503 };
504 }
505
506 pub fn reg_uxtw(rm: Register, shift: u2) LoadStoreOffset {
507 assert(rm.size() == 32 and (shift == 0 or shift == 2));
508 return .{
509 .Register = .{
510 .rm = rm.id(),
511 .shift = .{
512 .Uxtw = shift,
513 },
514 },
515 };
516 }
517
518 pub fn reg_lsl(rm: Register, shift: u2) LoadStoreOffset {
519 assert(rm.size() == 64 and (shift == 0 or shift == 3));
520 return .{
521 .Register = .{
522 .rm = rm.id(),
523 .shift = .{
524 .Lsl = shift,
525 },
526 },
527 };
528 }
529
530 pub fn reg_sxtw(rm: Register, shift: u2) LoadStoreOffset {
531 assert(rm.size() == 32 and (shift == 0 or shift == 2));
532 return .{
533 .Register = .{
534 .rm = rm.id(),
535 .shift = .{
536 .Sxtw = shift,
537 },
538 },
539 };
540 }
541
542 pub fn reg_sxtx(rm: Register, shift: u2) LoadStoreOffset {
543 assert(rm.size() == 64 and (shift == 0 or shift == 3));
544 return .{
545 .Register = .{
546 .rm = rm.id(),
547 .shift = .{
548 .Sxtx = shift,
549 },
550 },
551 };
552 }
553 };
554
555 /// Which kind of load/store to perform
556 const LoadStoreVariant = enum {
557 /// 32-bit or 64-bit
558 str,
559 /// 16-bit, zero-extended
560 strh,
561 /// 8-bit, zero-extended
562 strb,
563 /// 32-bit or 64-bit
564 ldr,
565 /// 16-bit, zero-extended
566 ldrh,
567 /// 8-bit, zero-extended
568 ldrb,
569 };
570
571 fn loadStoreRegister(
572 rt: Register,
573 rn: Register,
574 offset: LoadStoreOffset,
575 variant: LoadStoreVariant,
576 ) Instruction {
577 const off = offset.toU12();
578 const op1: u2 = blk: {
579 switch (offset) {
580 .Immediate => |imm| switch (imm) {
581 .Unsigned => break :blk 0b01,
582 else => {},
583 },
584 else => {},
585 }
586 break :blk 0b00;
587 };
588 const opc: u2 = switch (variant) {
589 .ldr, .ldrh, .ldrb => 0b01,
590 .str, .strh, .strb => 0b00,
591 };
592 return Instruction{
593 .load_store_register = .{
594 .rt = rt.id(),
595 .rn = rn.id(),
596 .offset = off,
597 .opc = opc,
598 .op1 = op1,
599 .v = 0,
600 .size = blk: {
601 switch (variant) {
602 .ldr, .str => switch (rt.size()) {
603 32 => break :blk 0b10,
604 64 => break :blk 0b11,
605 else => unreachable, // unexpected register size
606 },
607 .ldrh, .strh => break :blk 0b01,
608 .ldrb, .strb => break :blk 0b00,
609 }
610 },
611 },
612 };
613 }
614
615 fn loadStoreRegisterPair(
616 rt1: Register,
617 rt2: Register,
618 rn: Register,
619 offset: i9,
620 encoding: u2,
621 load: bool,
622 ) Instruction {
623 switch (rt1.size()) {
624 32 => {
625 assert(-256 <= offset and offset <= 252);
626 const imm7 = @truncate(u7, @bitCast(u9, offset >> 2));
627 return Instruction{
628 .load_store_register_pair = .{
629 .rt1 = rt1.id(),
630 .rn = rn.id(),
631 .rt2 = rt2.id(),
632 .imm7 = imm7,
633 .load = @boolToInt(load),
634 .encoding = encoding,
635 .opc = 0b00,
636 },
637 };
638 },
639 64 => {
640 assert(-512 <= offset and offset <= 504);
641 const imm7 = @truncate(u7, @bitCast(u9, offset >> 3));
642 return Instruction{
643 .load_store_register_pair = .{
644 .rt1 = rt1.id(),
645 .rn = rn.id(),
646 .rt2 = rt2.id(),
647 .imm7 = imm7,
648 .load = @boolToInt(load),
649 .encoding = encoding,
650 .opc = 0b10,
651 },
652 };
653 },
654 else => unreachable, // unexpected register size
655 }
656 }
657
658 fn loadLiteral(rt: Register, imm19: u19) Instruction {
659 switch (rt.size()) {
660 32 => {
661 return Instruction{
662 .load_literal = .{
663 .rt = rt.id(),
664 .imm19 = imm19,
665 .opc = 0b00,
666 },
667 };
668 },
669 64 => {
670 return Instruction{
671 .load_literal = .{
672 .rt = rt.id(),
673 .imm19 = imm19,
674 .opc = 0b01,
675 },
676 };
677 },
678 else => unreachable, // unexpected register size
679 }
680 }
681
682 fn exceptionGeneration(
683 opc: u3,
684 op2: u3,
685 ll: u2,
686 imm16: u16,
687 ) Instruction {
688 return Instruction{
689 .exception_generation = .{
690 .ll = ll,
691 .op2 = op2,
692 .imm16 = imm16,
693 .opc = opc,
694 },
695 };
696 }
697
698 fn unconditionalBranchRegister(
699 opc: u4,
700 op2: u5,
701 op3: u6,
702 rn: Register,
703 op4: u5,
704 ) Instruction {
705 assert(rn.size() == 64);
706
707 return Instruction{
708 .unconditional_branch_register = .{
709 .op4 = op4,
710 .rn = rn.id(),
711 .op3 = op3,
712 .op2 = op2,
713 .opc = opc,
714 },
715 };
716 }
717
718 fn unconditionalBranchImmediate(
719 op: u1,
720 offset: i28,
721 ) Instruction {
722 return Instruction{
723 .unconditional_branch_immediate = .{
724 .imm26 = @bitCast(u26, @intCast(i26, offset >> 2)),
725 .op = op,
726 },
727 };
728 }
729
730 fn logicalShiftedRegister(
731 opc: u2,
732 n: u1,
733 shift: Shift,
734 rd: Register,
735 rn: Register,
736 rm: Register,
737 ) Instruction {
738 switch (rd.size()) {
739 32 => {
740 assert(shift.amount < 32);
741 return Instruction{
742 .logical_shifted_register = .{
743 .rd = rd.id(),
744 .rn = rn.id(),
745 .imm6 = shift.amount,
746 .rm = rm.id(),
747 .n = n,
748 .shift = @enumToInt(shift.shift),
749 .opc = opc,
750 .sf = 0b0,
751 },
752 };
753 },
754 64 => {
755 return Instruction{
756 .logical_shifted_register = .{
757 .rd = rd.id(),
758 .rn = rn.id(),
759 .imm6 = shift.amount,
760 .rm = rm.id(),
761 .n = n,
762 .shift = @enumToInt(shift.shift),
763 .opc = opc,
764 .sf = 0b1,
765 },
766 };
767 },
768 else => unreachable, // unexpected register size
769 }
770 }
771
772 fn addSubtractImmediate(
773 op: u1,
774 s: u1,
775 rd: Register,
776 rn: Register,
777 imm12: u12,
778 shift: bool,
779 ) Instruction {
780 return Instruction{
781 .add_subtract_immediate = .{
782 .rd = rd.id(),
783 .rn = rn.id(),
784 .imm12 = imm12,
785 .sh = @boolToInt(shift),
786 .s = s,
787 .op = op,
788 .sf = switch (rd.size()) {
789 32 => 0b0,
790 64 => 0b1,
791 else => unreachable, // unexpected register size
792 },
793 },
794 };
795 }
796
797 fn conditionalBranch(
798 o0: u1,
799 o1: u1,
800 cond: Condition,
801 offset: i21,
802 ) Instruction {
803 assert(offset & 0b11 == 0b00);
804 return Instruction{
805 .conditional_branch = .{
806 .cond = @enumToInt(cond),
807 .o0 = o0,
808 .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)),
809 .o1 = o1,
810 },
811 };
812 }
813
814 fn compareAndBranch(
815 op: u1,
816 rt: Register,
817 offset: i21,
818 ) Instruction {
819 assert(offset & 0b11 == 0b00);
820 return Instruction{
821 .compare_and_branch = .{
822 .rt = rt.id(),
823 .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)),
824 .op = op,
825 .sf = switch (rt.size()) {
826 32 => 0b0,
827 64 => 0b1,
828 else => unreachable, // unexpected register size
829 },
830 },
831 };
832 }
833
834 // Helper functions for assembly syntax functions
835
836 // Move wide (immediate)
837
838 pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction {
839 return moveWideImmediate(0b00, rd, imm16, shift);
840 }
841
842 pub fn movz(rd: Register, imm16: u16, shift: u6) Instruction {
843 return moveWideImmediate(0b10, rd, imm16, shift);
844 }
845
846 pub fn movk(rd: Register, imm16: u16, shift: u6) Instruction {
847 return moveWideImmediate(0b11, rd, imm16, shift);
848 }
849
850 // PC relative address
851
852 pub fn adr(rd: Register, imm21: i21) Instruction {
853 return pcRelativeAddress(rd, imm21, 0b0);
854 }
855
856 pub fn adrp(rd: Register, imm21: i21) Instruction {
857 return pcRelativeAddress(rd, imm21, 0b1);
858 }
859
860 // Load or store register
861
862 pub const LdrArgs = union(enum) {
863 register: struct {
864 rn: Register,
865 offset: LoadStoreOffset = LoadStoreOffset.none,
866 },
867 literal: u19,
868 };
869
870 pub fn ldr(rt: Register, args: LdrArgs) Instruction {
871 switch (args) {
872 .register => |info| return loadStoreRegister(rt, info.rn, info.offset, .ldr),
873 .literal => |literal| return loadLiteral(rt, literal),
874 }
875 }
876
877 pub fn ldrh(rt: Register, rn: Register, args: StrArgs) Instruction {
878 return loadStoreRegister(rt, rn, args.offset, .ldrh);
879 }
880
881 pub fn ldrb(rt: Register, rn: Register, args: StrArgs) Instruction {
882 return loadStoreRegister(rt, rn, args.offset, .ldrb);
883 }
884
885 pub const StrArgs = struct {
886 offset: LoadStoreOffset = LoadStoreOffset.none,
887 };
888
889 pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction {
890 return loadStoreRegister(rt, rn, args.offset, .str);
891 }
892
893 pub fn strh(rt: Register, rn: Register, args: StrArgs) Instruction {
894 return loadStoreRegister(rt, rn, args.offset, .strh);
895 }
896
897 pub fn strb(rt: Register, rn: Register, args: StrArgs) Instruction {
898 return loadStoreRegister(rt, rn, args.offset, .strb);
899 }
900
901 // Load or store pair of registers
902
903 pub const LoadStorePairOffset = struct {
904 encoding: enum(u2) {
905 PostIndex = 0b01,
906 Signed = 0b10,
907 PreIndex = 0b11,
908 },
909 offset: i9,
910
911 pub fn none() LoadStorePairOffset {
912 return .{ .encoding = .Signed, .offset = 0 };
913 }
914
915 pub fn post_index(imm: i9) LoadStorePairOffset {
916 return .{ .encoding = .PostIndex, .offset = imm };
917 }
918
919 pub fn pre_index(imm: i9) LoadStorePairOffset {
920 return .{ .encoding = .PreIndex, .offset = imm };
921 }
922
923 pub fn signed(imm: i9) LoadStorePairOffset {
924 return .{ .encoding = .Signed, .offset = imm };
925 }
926 };
927
928 pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction {
929 return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true);
930 }
931
932 pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction {
933 return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true);
934 }
935
936 pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction {
937 return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false);
938 }
939
940 pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction {
941 return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false);
942 }
943
944 // Exception generation
945
946 pub fn svc(imm16: u16) Instruction {
947 return exceptionGeneration(0b000, 0b000, 0b01, imm16);
948 }
949
950 pub fn hvc(imm16: u16) Instruction {
951 return exceptionGeneration(0b000, 0b000, 0b10, imm16);
952 }
953
954 pub fn smc(imm16: u16) Instruction {
955 return exceptionGeneration(0b000, 0b000, 0b11, imm16);
956 }
957
958 pub fn brk(imm16: u16) Instruction {
959 return exceptionGeneration(0b001, 0b000, 0b00, imm16);
960 }
961
962 pub fn hlt(imm16: u16) Instruction {
963 return exceptionGeneration(0b010, 0b000, 0b00, imm16);
964 }
965
966 // Unconditional branch (register)
967
968 pub fn br(rn: Register) Instruction {
969 return unconditionalBranchRegister(0b0000, 0b11111, 0b000000, rn, 0b00000);
970 }
971
972 pub fn blr(rn: Register) Instruction {
973 return unconditionalBranchRegister(0b0001, 0b11111, 0b000000, rn, 0b00000);
974 }
975
976 pub fn ret(rn: ?Register) Instruction {
977 return unconditionalBranchRegister(0b0010, 0b11111, 0b000000, rn orelse .x30, 0b00000);
978 }
979
980 // Unconditional branch (immediate)
981
982 pub fn b(offset: i28) Instruction {
983 return unconditionalBranchImmediate(0, offset);
984 }
985
986 pub fn bl(offset: i28) Instruction {
987 return unconditionalBranchImmediate(1, offset);
988 }
989
990 // Nop
991
992 pub fn nop() Instruction {
993 return Instruction{ .no_operation = .{} };
994 }
995
996 // Logical (shifted register)
997
998 pub fn @"and"(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
999 return logicalShiftedRegister(0b00, 0b0, shift, rd, rn, rm);
1000 }
1001
1002 pub fn bic(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1003 return logicalShiftedRegister(0b00, 0b1, shift, rd, rn, rm);
1004 }
1005
1006 pub fn orr(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1007 return logicalShiftedRegister(0b01, 0b0, shift, rd, rn, rm);
1008 }
1009
1010 pub fn orn(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1011 return logicalShiftedRegister(0b01, 0b1, shift, rd, rn, rm);
1012 }
1013
1014 pub fn eor(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1015 return logicalShiftedRegister(0b10, 0b0, shift, rd, rn, rm);
1016 }
1017
1018 pub fn eon(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1019 return logicalShiftedRegister(0b10, 0b1, shift, rd, rn, rm);
1020 }
1021
1022 pub fn ands(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1023 return logicalShiftedRegister(0b11, 0b0, shift, rd, rn, rm);
1024 }
1025
1026 pub fn bics(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction {
1027 return logicalShiftedRegister(0b11, 0b1, shift, rd, rn, rm);
1028 }
1029
1030 // Add/subtract (immediate)
1031
1032 pub fn add(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
1033 return addSubtractImmediate(0b0, 0b0, rd, rn, imm, shift);
1034 }
1035
1036 pub fn adds(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
1037 return addSubtractImmediate(0b0, 0b1, rd, rn, imm, shift);
1038 }
1039
1040 pub fn sub(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
1041 return addSubtractImmediate(0b1, 0b0, rd, rn, imm, shift);
1042 }
1043
1044 pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
1045 return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift);
1046 }
1047
1048 // Conditional branch
1049
1050 pub fn bCond(cond: Condition, offset: i21) Instruction {
1051 return conditionalBranch(0b0, 0b0, cond, offset);
1052 }
1053
1054 // Compare and branch
1055
1056 pub fn cbz(rt: Register, offset: i21) Instruction {
1057 return compareAndBranch(0b0, rt, offset);
1058 }
1059
1060 pub fn cbnz(rt: Register, offset: i21) Instruction {
1061 return compareAndBranch(0b1, rt, offset);
1062 }
1063};
1064
1065test {
1066 testing.refAllDecls(@This());
1067}
1068
1069test "serialize instructions" {
1070 const Testcase = struct {
1071 inst: Instruction,
1072 expected: u32,
1073 };
1074
1075 const testcases = [_]Testcase{
1076 .{ // orr x0, xzr, x1
1077 .inst = Instruction.orr(.x0, .xzr, .x1, Instruction.Shift.none),
1078 .expected = 0b1_01_01010_00_0_00001_000000_11111_00000,
1079 },
1080 .{ // orn x0, xzr, x1
1081 .inst = Instruction.orn(.x0, .xzr, .x1, Instruction.Shift.none),
1082 .expected = 0b1_01_01010_00_1_00001_000000_11111_00000,
1083 },
1084 .{ // movz x1, #4
1085 .inst = Instruction.movz(.x1, 4, 0),
1086 .expected = 0b1_10_100101_00_0000000000000100_00001,
1087 },
1088 .{ // movz x1, #4, lsl 16
1089 .inst = Instruction.movz(.x1, 4, 16),
1090 .expected = 0b1_10_100101_01_0000000000000100_00001,
1091 },
1092 .{ // movz x1, #4, lsl 32
1093 .inst = Instruction.movz(.x1, 4, 32),
1094 .expected = 0b1_10_100101_10_0000000000000100_00001,
1095 },
1096 .{ // movz x1, #4, lsl 48
1097 .inst = Instruction.movz(.x1, 4, 48),
1098 .expected = 0b1_10_100101_11_0000000000000100_00001,
1099 },
1100 .{ // movz w1, #4
1101 .inst = Instruction.movz(.w1, 4, 0),
1102 .expected = 0b0_10_100101_00_0000000000000100_00001,
1103 },
1104 .{ // movz w1, #4, lsl 16
1105 .inst = Instruction.movz(.w1, 4, 16),
1106 .expected = 0b0_10_100101_01_0000000000000100_00001,
1107 },
1108 .{ // svc #0
1109 .inst = Instruction.svc(0),
1110 .expected = 0b1101_0100_000_0000000000000000_00001,
1111 },
1112 .{ // svc #0x80 ; typical on Darwin
1113 .inst = Instruction.svc(0x80),
1114 .expected = 0b1101_0100_000_0000000010000000_00001,
1115 },
1116 .{ // ret
1117 .inst = Instruction.ret(null),
1118 .expected = 0b1101_011_00_10_11111_0000_00_11110_00000,
1119 },
1120 .{ // bl #0x10
1121 .inst = Instruction.bl(0x10),
1122 .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100,
1123 },
1124 .{ // ldr x2, [x1]
1125 .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1 } }),
1126 .expected = 0b11_111_0_01_01_000000000000_00001_00010,
1127 },
1128 .{ // ldr x2, [x1, #1]!
1129 .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_pre_index(1) } }),
1130 .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010,
1131 },
1132 .{ // ldr x2, [x1], #-1
1133 .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_post_index(-1) } }),
1134 .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010,
1135 },
1136 .{ // ldr x2, [x1], (x3)
1137 .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.reg(.x3) } }),
1138 .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010,
1139 },
1140 .{ // ldr x2, label
1141 .inst = Instruction.ldr(.x2, .{ .literal = 0x1 }),
1142 .expected = 0b01_011_0_00_0000000000000000001_00010,
1143 },
1144 .{ // ldrh x7, [x4], #0xaa
1145 .inst = Instruction.ldrh(.x7, .x4, .{ .offset = Instruction.LoadStoreOffset.imm_post_index(0xaa) }),
1146 .expected = 0b01_111_0_00_01_0_010101010_01_00100_00111,
1147 },
1148 .{ // ldrb x9, [x15, #0xff]!
1149 .inst = Instruction.ldrb(.x9, .x15, .{ .offset = Instruction.LoadStoreOffset.imm_pre_index(0xff) }),
1150 .expected = 0b00_111_0_00_01_0_011111111_11_01111_01001,
1151 },
1152 .{ // str x2, [x1]
1153 .inst = Instruction.str(.x2, .x1, .{}),
1154 .expected = 0b11_111_0_01_00_000000000000_00001_00010,
1155 },
1156 .{ // str x2, [x1], (x3)
1157 .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }),
1158 .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010,
1159 },
1160 .{ // strh w0, [x1]
1161 .inst = Instruction.strh(.w0, .x1, .{}),
1162 .expected = 0b01_111_0_01_00_000000000000_00001_00000,
1163 },
1164 .{ // strb w8, [x9]
1165 .inst = Instruction.strb(.w8, .x9, .{}),
1166 .expected = 0b00_111_0_01_00_000000000000_01001_01000,
1167 },
1168 .{ // adr x2, #0x8
1169 .inst = Instruction.adr(.x2, 0x8),
1170 .expected = 0b0_00_10000_0000000000000000010_00010,
1171 },
1172 .{ // adr x2, -#0x8
1173 .inst = Instruction.adr(.x2, -0x8),
1174 .expected = 0b0_00_10000_1111111111111111110_00010,
1175 },
1176 .{ // adrp x2, #0x8
1177 .inst = Instruction.adrp(.x2, 0x8),
1178 .expected = 0b1_00_10000_0000000000000000010_00010,
1179 },
1180 .{ // adrp x2, -#0x8
1181 .inst = Instruction.adrp(.x2, -0x8),
1182 .expected = 0b1_00_10000_1111111111111111110_00010,
1183 },
1184 .{ // stp x1, x2, [sp, #8]
1185 .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)),
1186 .expected = 0b10_101_0_010_0_0000001_00010_11111_00001,
1187 },
1188 .{ // ldp x1, x2, [sp, #8]
1189 .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)),
1190 .expected = 0b10_101_0_010_1_0000001_00010_11111_00001,
1191 },
1192 .{ // stp x1, x2, [sp, #-16]!
1193 .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.pre_index(-16)),
1194 .expected = 0b10_101_0_011_0_1111110_00010_11111_00001,
1195 },
1196 .{ // ldp x1, x2, [sp], #16
1197 .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)),
1198 .expected = 0b10_101_0_001_1_0000010_00010_11111_00001,
1199 },
1200 .{ // and x0, x4, x2
1201 .inst = Instruction.@"and"(.x0, .x4, .x2, .{}),
1202 .expected = 0b1_00_01010_00_0_00010_000000_00100_00000,
1203 },
1204 .{ // and x0, x4, x2, lsl #0x8
1205 .inst = Instruction.@"and"(.x0, .x4, .x2, .{ .shift = .lsl, .amount = 0x8 }),
1206 .expected = 0b1_00_01010_00_0_00010_001000_00100_00000,
1207 },
1208 .{ // add x0, x10, #10
1209 .inst = Instruction.add(.x0, .x10, 10, false),
1210 .expected = 0b1_0_0_100010_0_0000_0000_1010_01010_00000,
1211 },
1212 .{ // subs x0, x5, #11, lsl #12
1213 .inst = Instruction.subs(.x0, .x5, 11, true),
1214 .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000,
1215 },
1216 .{ // b.hi #-4
1217 .inst = Instruction.bCond(.hi, -4),
1218 .expected = 0b0101010_0_1111111111111111111_0_1000,
1219 },
1220 .{ // cbz x10, #40
1221 .inst = Instruction.cbz(.x10, 40),
1222 .expected = 0b1_011010_0_0000000000000001010_01010,
1223 },
1224 };
1225
1226 for (testcases) |case| {
1227 const actual = case.inst.toU32();
1228 try testing.expectEqual(case.expected, actual);
1229 }
1230}
src/codegen/arm.zig deleted-1408
......@@ -1,1408 +0,0 @@
1const std = @import("std");
2const DW = std.dwarf;
3const testing = std.testing;
4
5/// The condition field specifies the flags necessary for an
6/// Instruction to be executed
7pub const Condition = enum(u4) {
8 /// equal
9 eq,
10 /// not equal
11 ne,
12 /// unsigned higher or same
13 cs,
14 /// unsigned lower
15 cc,
16 /// negative
17 mi,
18 /// positive or zero
19 pl,
20 /// overflow
21 vs,
22 /// no overflow
23 vc,
24 /// unsigned higer
25 hi,
26 /// unsigned lower or same
27 ls,
28 /// greater or equal
29 ge,
30 /// less than
31 lt,
32 /// greater than
33 gt,
34 /// less than or equal
35 le,
36 /// always
37 al,
38
39 /// Converts a std.math.CompareOperator into a condition flag,
40 /// i.e. returns the condition that is true iff the result of the
41 /// comparison is true. Assumes signed comparison
42 pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition {
43 return switch (op) {
44 .gte => .ge,
45 .gt => .gt,
46 .neq => .ne,
47 .lt => .lt,
48 .lte => .le,
49 .eq => .eq,
50 };
51 }
52
53 /// Converts a std.math.CompareOperator into a condition flag,
54 /// i.e. returns the condition that is true iff the result of the
55 /// comparison is true. Assumes unsigned comparison
56 pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition {
57 return switch (op) {
58 .gte => .cs,
59 .gt => .hi,
60 .neq => .ne,
61 .lt => .cc,
62 .lte => .ls,
63 .eq => .eq,
64 };
65 }
66
67 /// Returns the condition which is true iff the given condition is
68 /// false (if such a condition exists)
69 pub fn negate(cond: Condition) Condition {
70 return switch (cond) {
71 .eq => .ne,
72 .ne => .eq,
73 .cs => .cc,
74 .cc => .cs,
75 .mi => .pl,
76 .pl => .mi,
77 .vs => .vc,
78 .vc => .vs,
79 .hi => .ls,
80 .ls => .hi,
81 .ge => .lt,
82 .lt => .ge,
83 .gt => .le,
84 .le => .gt,
85 .al => unreachable,
86 };
87 }
88};
89
90test "condition from CompareOperator" {
91 try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq));
92 try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq));
93
94 try testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt));
95 try testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt));
96
97 try testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte));
98 try testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte));
99}
100
101test "negate condition" {
102 try testing.expectEqual(@as(Condition, .eq), Condition.ne.negate());
103 try testing.expectEqual(@as(Condition, .ne), Condition.eq.negate());
104}
105
106/// Represents a register in the ARM instruction set architecture
107pub const Register = enum(u5) {
108 r0,
109 r1,
110 r2,
111 r3,
112 r4,
113 r5,
114 r6,
115 r7,
116 r8,
117 r9,
118 r10,
119 r11,
120 r12,
121 r13,
122 r14,
123 r15,
124
125 /// Argument / result / scratch register 1
126 a1,
127 /// Argument / result / scratch register 2
128 a2,
129 /// Argument / scratch register 3
130 a3,
131 /// Argument / scratch register 4
132 a4,
133 /// Variable-register 1
134 v1,
135 /// Variable-register 2
136 v2,
137 /// Variable-register 3
138 v3,
139 /// Variable-register 4
140 v4,
141 /// Variable-register 5
142 v5,
143 /// Platform register
144 v6,
145 /// Variable-register 7
146 v7,
147 /// Frame pointer or Variable-register 8
148 fp,
149 /// Intra-Procedure-call scratch register
150 ip,
151 /// Stack pointer
152 sp,
153 /// Link register
154 lr,
155 /// Program counter
156 pc,
157
158 /// Returns the unique 4-bit ID of this register which is used in
159 /// the machine code
160 pub fn id(self: Register) u4 {
161 return @truncate(u4, @enumToInt(self));
162 }
163
164 /// Returns the index into `callee_preserved_regs`.
165 pub fn allocIndex(self: Register) ?u4 {
166 inline for (callee_preserved_regs) |cpreg, i| {
167 if (self.id() == cpreg.id()) return i;
168 }
169 return null;
170 }
171
172 pub fn dwarfLocOp(self: Register) u8 {
173 return @as(u8, self.id()) + DW.OP.reg0;
174 }
175};
176
177test "Register.id" {
178 try testing.expectEqual(@as(u4, 15), Register.r15.id());
179 try testing.expectEqual(@as(u4, 15), Register.pc.id());
180}
181
182/// Program status registers containing flags, mode bits and other
183/// vital information
184pub const Psr = enum {
185 cpsr,
186 spsr,
187};
188
189pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 };
190pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
191pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
192
193/// Represents an instruction in the ARM instruction set architecture
194pub const Instruction = union(enum) {
195 data_processing: packed struct {
196 // Note to self: The order of the fields top-to-bottom is
197 // right-to-left in the actual 32-bit int representation
198 op2: u12,
199 rd: u4,
200 rn: u4,
201 s: u1,
202 opcode: u4,
203 i: u1,
204 fixed: u2 = 0b00,
205 cond: u4,
206 },
207 multiply: packed struct {
208 rn: u4,
209 fixed_1: u4 = 0b1001,
210 rm: u4,
211 ra: u4,
212 rd: u4,
213 set_cond: u1,
214 accumulate: u1,
215 fixed_2: u6 = 0b000000,
216 cond: u4,
217 },
218 multiply_long: packed struct {
219 rn: u4,
220 fixed_1: u4 = 0b1001,
221 rm: u4,
222 rdlo: u4,
223 rdhi: u4,
224 set_cond: u1,
225 accumulate: u1,
226 unsigned: u1,
227 fixed_2: u5 = 0b00001,
228 cond: u4,
229 },
230 integer_saturating_arithmetic: packed struct {
231 rm: u4,
232 fixed_1: u8 = 0b0000_0101,
233 rd: u4,
234 rn: u4,
235 fixed_2: u1 = 0b0,
236 opc: u2,
237 fixed_3: u5 = 0b00010,
238 cond: u4,
239 },
240 single_data_transfer: packed struct {
241 offset: u12,
242 rd: u4,
243 rn: u4,
244 load_store: u1,
245 write_back: u1,
246 byte_word: u1,
247 up_down: u1,
248 pre_post: u1,
249 imm: u1,
250 fixed: u2 = 0b01,
251 cond: u4,
252 },
253 extra_load_store: packed struct {
254 imm4l: u4,
255 fixed_1: u1 = 0b1,
256 op2: u2,
257 fixed_2: u1 = 0b1,
258 imm4h: u4,
259 rt: u4,
260 rn: u4,
261 o1: u1,
262 write_back: u1,
263 imm: u1,
264 up_down: u1,
265 pre_index: u1,
266 fixed_3: u3 = 0b000,
267 cond: u4,
268 },
269 block_data_transfer: packed struct {
270 register_list: u16,
271 rn: u4,
272 load_store: u1,
273 write_back: u1,
274 psr_or_user: u1,
275 up_down: u1,
276 pre_post: u1,
277 fixed: u3 = 0b100,
278 cond: u4,
279 },
280 branch: packed struct {
281 offset: u24,
282 link: u1,
283 fixed: u3 = 0b101,
284 cond: u4,
285 },
286 branch_exchange: packed struct {
287 rn: u4,
288 fixed_1: u1 = 0b1,
289 link: u1,
290 fixed_2: u22 = 0b0001_0010_1111_1111_1111_00,
291 cond: u4,
292 },
293 supervisor_call: packed struct {
294 comment: u24,
295 fixed: u4 = 0b1111,
296 cond: u4,
297 },
298 breakpoint: packed struct {
299 imm4: u4,
300 fixed_1: u4 = 0b0111,
301 imm12: u12,
302 fixed_2_and_cond: u12 = 0b1110_0001_0010,
303 },
304
305 /// Represents the possible operations which can be performed by a
306 /// Data Processing instruction
307 const Opcode = enum(u4) {
308 // Rd := Op1 AND Op2
309 @"and",
310 // Rd := Op1 EOR Op2
311 eor,
312 // Rd := Op1 - Op2
313 sub,
314 // Rd := Op2 - Op1
315 rsb,
316 // Rd := Op1 + Op2
317 add,
318 // Rd := Op1 + Op2 + C
319 adc,
320 // Rd := Op1 - Op2 + C - 1
321 sbc,
322 // Rd := Op2 - Op1 + C - 1
323 rsc,
324 // set condition codes on Op1 AND Op2
325 tst,
326 // set condition codes on Op1 EOR Op2
327 teq,
328 // set condition codes on Op1 - Op2
329 cmp,
330 // set condition codes on Op1 + Op2
331 cmn,
332 // Rd := Op1 OR Op2
333 orr,
334 // Rd := Op2
335 mov,
336 // Rd := Op1 AND NOT Op2
337 bic,
338 // Rd := NOT Op2
339 mvn,
340 };
341
342 /// Represents the second operand to a data processing instruction
343 /// which can either be content from a register or an immediate
344 /// value
345 pub const Operand = union(enum) {
346 Register: packed struct {
347 rm: u4,
348 shift: u8,
349 },
350 Immediate: packed struct {
351 imm: u8,
352 rotate: u4,
353 },
354
355 /// Represents multiple ways a register can be shifted. A
356 /// register can be shifted by a specific immediate value or
357 /// by the contents of another register
358 pub const Shift = union(enum) {
359 Immediate: packed struct {
360 fixed: u1 = 0b0,
361 typ: u2,
362 amount: u5,
363 },
364 Register: packed struct {
365 fixed_1: u1 = 0b1,
366 typ: u2,
367 fixed_2: u1 = 0b0,
368 rs: u4,
369 },
370
371 pub const Type = enum(u2) {
372 logical_left,
373 logical_right,
374 arithmetic_right,
375 rotate_right,
376 };
377
378 pub const none = Shift{
379 .Immediate = .{
380 .amount = 0,
381 .typ = 0,
382 },
383 };
384
385 pub fn toU8(self: Shift) u8 {
386 return switch (self) {
387 .Register => |v| @bitCast(u8, v),
388 .Immediate => |v| @bitCast(u8, v),
389 };
390 }
391
392 pub fn reg(rs: Register, typ: Type) Shift {
393 return Shift{
394 .Register = .{
395 .rs = rs.id(),
396 .typ = @enumToInt(typ),
397 },
398 };
399 }
400
401 pub fn imm(amount: u5, typ: Type) Shift {
402 return Shift{
403 .Immediate = .{
404 .amount = amount,
405 .typ = @enumToInt(typ),
406 },
407 };
408 }
409 };
410
411 pub fn toU12(self: Operand) u12 {
412 return switch (self) {
413 .Register => |v| @bitCast(u12, v),
414 .Immediate => |v| @bitCast(u12, v),
415 };
416 }
417
418 pub fn reg(rm: Register, shift: Shift) Operand {
419 return Operand{
420 .Register = .{
421 .rm = rm.id(),
422 .shift = shift.toU8(),
423 },
424 };
425 }
426
427 pub fn imm(immediate: u8, rotate: u4) Operand {
428 return Operand{
429 .Immediate = .{
430 .imm = immediate,
431 .rotate = rotate,
432 },
433 };
434 }
435
436 /// Tries to convert an unsigned 32 bit integer into an
437 /// immediate operand using rotation. Returns null when there
438 /// is no conversion
439 pub fn fromU32(x: u32) ?Operand {
440 const masks = comptime blk: {
441 const base_mask: u32 = std.math.maxInt(u8);
442 var result = [_]u32{0} ** 16;
443 for (result) |*mask, i| mask.* = std.math.rotr(u32, base_mask, 2 * i);
444 break :blk result;
445 };
446
447 return for (masks) |mask, i| {
448 if (x & mask == x) {
449 break Operand{
450 .Immediate = .{
451 .imm = @intCast(u8, std.math.rotl(u32, x, 2 * i)),
452 .rotate = @intCast(u4, i),
453 },
454 };
455 }
456 } else null;
457 }
458 };
459
460 /// Represents the offset operand of a load or store
461 /// instruction. Data can be loaded from memory with either an
462 /// immediate offset or an offset that is stored in some register.
463 pub const Offset = union(enum) {
464 Immediate: u12,
465 Register: packed struct {
466 rm: u4,
467 shift: u8,
468 },
469
470 pub const none = Offset{
471 .Immediate = 0,
472 };
473
474 pub fn toU12(self: Offset) u12 {
475 return switch (self) {
476 .Register => |v| @bitCast(u12, v),
477 .Immediate => |v| v,
478 };
479 }
480
481 pub fn reg(rm: Register, shift: u8) Offset {
482 return Offset{
483 .Register = .{
484 .rm = rm.id(),
485 .shift = shift,
486 },
487 };
488 }
489
490 pub fn imm(immediate: u12) Offset {
491 return Offset{
492 .Immediate = immediate,
493 };
494 }
495 };
496
497 /// Represents the offset operand of an extra load or store
498 /// instruction.
499 pub const ExtraLoadStoreOffset = union(enum) {
500 immediate: u8,
501 register: u4,
502
503 pub const none = ExtraLoadStoreOffset{
504 .immediate = 0,
505 };
506
507 pub fn reg(register: Register) ExtraLoadStoreOffset {
508 return ExtraLoadStoreOffset{
509 .register = register.id(),
510 };
511 }
512
513 pub fn imm(immediate: u8) ExtraLoadStoreOffset {
514 return ExtraLoadStoreOffset{
515 .immediate = immediate,
516 };
517 }
518 };
519
520 /// Represents the register list operand to a block data transfer
521 /// instruction
522 pub const RegisterList = packed struct {
523 r0: bool = false,
524 r1: bool = false,
525 r2: bool = false,
526 r3: bool = false,
527 r4: bool = false,
528 r5: bool = false,
529 r6: bool = false,
530 r7: bool = false,
531 r8: bool = false,
532 r9: bool = false,
533 r10: bool = false,
534 r11: bool = false,
535 r12: bool = false,
536 r13: bool = false,
537 r14: bool = false,
538 r15: bool = false,
539 };
540
541 pub fn toU32(self: Instruction) u32 {
542 return switch (self) {
543 .data_processing => |v| @bitCast(u32, v),
544 .multiply => |v| @bitCast(u32, v),
545 .multiply_long => |v| @bitCast(u32, v),
546 .integer_saturating_arithmetic => |v| @bitCast(u32, v),
547 .single_data_transfer => |v| @bitCast(u32, v),
548 .extra_load_store => |v| @bitCast(u32, v),
549 .block_data_transfer => |v| @bitCast(u32, v),
550 .branch => |v| @bitCast(u32, v),
551 .branch_exchange => |v| @bitCast(u32, v),
552 .supervisor_call => |v| @bitCast(u32, v),
553 .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20),
554 };
555 }
556
557 // Helper functions for the "real" functions below
558
559 fn dataProcessing(
560 cond: Condition,
561 opcode: Opcode,
562 s: u1,
563 rd: Register,
564 rn: Register,
565 op2: Operand,
566 ) Instruction {
567 return Instruction{
568 .data_processing = .{
569 .cond = @enumToInt(cond),
570 .i = @boolToInt(op2 == .Immediate),
571 .opcode = @enumToInt(opcode),
572 .s = s,
573 .rn = rn.id(),
574 .rd = rd.id(),
575 .op2 = op2.toU12(),
576 },
577 };
578 }
579
580 fn specialMov(
581 cond: Condition,
582 rd: Register,
583 imm: u16,
584 top: bool,
585 ) Instruction {
586 return Instruction{
587 .data_processing = .{
588 .cond = @enumToInt(cond),
589 .i = 1,
590 .opcode = if (top) 0b1010 else 0b1000,
591 .s = 0,
592 .rn = @truncate(u4, imm >> 12),
593 .rd = rd.id(),
594 .op2 = @truncate(u12, imm),
595 },
596 };
597 }
598
599 fn multiply(
600 cond: Condition,
601 set_cond: u1,
602 rd: Register,
603 rn: Register,
604 rm: Register,
605 ra: ?Register,
606 ) Instruction {
607 return Instruction{
608 .multiply = .{
609 .cond = @enumToInt(cond),
610 .accumulate = @boolToInt(ra != null),
611 .set_cond = set_cond,
612 .rd = rd.id(),
613 .rn = rn.id(),
614 .ra = if (ra) |reg| reg.id() else 0b0000,
615 .rm = rm.id(),
616 },
617 };
618 }
619
620 fn multiplyLong(
621 cond: Condition,
622 signed: u1,
623 accumulate: u1,
624 set_cond: u1,
625 rdhi: Register,
626 rdlo: Register,
627 rm: Register,
628 rn: Register,
629 ) Instruction {
630 return Instruction{
631 .multiply_long = .{
632 .cond = @enumToInt(cond),
633 .unsigned = signed,
634 .accumulate = accumulate,
635 .set_cond = set_cond,
636 .rdlo = rdlo.id(),
637 .rdhi = rdhi.id(),
638 .rn = rn.id(),
639 .rm = rm.id(),
640 },
641 };
642 }
643
644 fn integerSaturationArithmetic(
645 cond: Condition,
646 rd: Register,
647 rm: Register,
648 rn: Register,
649 opc: u2,
650 ) Instruction {
651 return Instruction{
652 .integer_saturating_arithmetic = .{
653 .rm = rm.id(),
654 .rd = rd.id(),
655 .rn = rn.id(),
656 .opc = opc,
657 .cond = @enumToInt(cond),
658 },
659 };
660 }
661
662 fn singleDataTransfer(
663 cond: Condition,
664 rd: Register,
665 rn: Register,
666 offset: Offset,
667 pre_index: bool,
668 positive: bool,
669 byte_word: u1,
670 write_back: bool,
671 load_store: u1,
672 ) Instruction {
673 return Instruction{
674 .single_data_transfer = .{
675 .cond = @enumToInt(cond),
676 .rn = rn.id(),
677 .rd = rd.id(),
678 .offset = offset.toU12(),
679 .load_store = load_store,
680 .write_back = @boolToInt(write_back),
681 .byte_word = byte_word,
682 .up_down = @boolToInt(positive),
683 .pre_post = @boolToInt(pre_index),
684 .imm = @boolToInt(offset != .Immediate),
685 },
686 };
687 }
688
689 fn extraLoadStore(
690 cond: Condition,
691 pre_index: bool,
692 positive: bool,
693 write_back: bool,
694 o1: u1,
695 op2: u2,
696 rn: Register,
697 rt: Register,
698 offset: ExtraLoadStoreOffset,
699 ) Instruction {
700 const imm4l: u4 = switch (offset) {
701 .immediate => |imm| @truncate(u4, imm),
702 .register => |reg| reg,
703 };
704 const imm4h: u4 = switch (offset) {
705 .immediate => |imm| @truncate(u4, imm >> 4),
706 .register => 0b0000,
707 };
708
709 return Instruction{
710 .extra_load_store = .{
711 .imm4l = imm4l,
712 .op2 = op2,
713 .imm4h = imm4h,
714 .rt = rt.id(),
715 .rn = rn.id(),
716 .o1 = o1,
717 .write_back = @boolToInt(write_back),
718 .imm = @boolToInt(offset == .immediate),
719 .up_down = @boolToInt(positive),
720 .pre_index = @boolToInt(pre_index),
721 .cond = @enumToInt(cond),
722 },
723 };
724 }
725
726 fn blockDataTransfer(
727 cond: Condition,
728 rn: Register,
729 reg_list: RegisterList,
730 pre_post: u1,
731 up_down: u1,
732 psr_or_user: u1,
733 write_back: bool,
734 load_store: u1,
735 ) Instruction {
736 return Instruction{
737 .block_data_transfer = .{
738 .register_list = @bitCast(u16, reg_list),
739 .rn = rn.id(),
740 .load_store = load_store,
741 .write_back = @boolToInt(write_back),
742 .psr_or_user = psr_or_user,
743 .up_down = up_down,
744 .pre_post = pre_post,
745 .cond = @enumToInt(cond),
746 },
747 };
748 }
749
750 fn branch(cond: Condition, offset: i26, link: u1) Instruction {
751 return Instruction{
752 .branch = .{
753 .cond = @enumToInt(cond),
754 .link = link,
755 .offset = @bitCast(u24, @intCast(i24, offset >> 2)),
756 },
757 };
758 }
759
760 fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction {
761 return Instruction{
762 .branch_exchange = .{
763 .cond = @enumToInt(cond),
764 .link = link,
765 .rn = rn.id(),
766 },
767 };
768 }
769
770 fn supervisorCall(cond: Condition, comment: u24) Instruction {
771 return Instruction{
772 .supervisor_call = .{
773 .cond = @enumToInt(cond),
774 .comment = comment,
775 },
776 };
777 }
778
779 fn breakpoint(imm: u16) Instruction {
780 return Instruction{
781 .breakpoint = .{
782 .imm12 = @truncate(u12, imm >> 4),
783 .imm4 = @truncate(u4, imm),
784 },
785 };
786 }
787
788 // Public functions replicating assembler syntax as closely as
789 // possible
790
791 // Data processing
792
793 pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
794 return dataProcessing(cond, .@"and", 0, rd, rn, op2);
795 }
796
797 pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
798 return dataProcessing(cond, .@"and", 1, rd, rn, op2);
799 }
800
801 pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
802 return dataProcessing(cond, .eor, 0, rd, rn, op2);
803 }
804
805 pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
806 return dataProcessing(cond, .eor, 1, rd, rn, op2);
807 }
808
809 pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
810 return dataProcessing(cond, .sub, 0, rd, rn, op2);
811 }
812
813 pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
814 return dataProcessing(cond, .sub, 1, rd, rn, op2);
815 }
816
817 pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
818 return dataProcessing(cond, .rsb, 0, rd, rn, op2);
819 }
820
821 pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
822 return dataProcessing(cond, .rsb, 1, rd, rn, op2);
823 }
824
825 pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
826 return dataProcessing(cond, .add, 0, rd, rn, op2);
827 }
828
829 pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
830 return dataProcessing(cond, .add, 1, rd, rn, op2);
831 }
832
833 pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
834 return dataProcessing(cond, .adc, 0, rd, rn, op2);
835 }
836
837 pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
838 return dataProcessing(cond, .adc, 1, rd, rn, op2);
839 }
840
841 pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
842 return dataProcessing(cond, .sbc, 0, rd, rn, op2);
843 }
844
845 pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
846 return dataProcessing(cond, .sbc, 1, rd, rn, op2);
847 }
848
849 pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
850 return dataProcessing(cond, .rsc, 0, rd, rn, op2);
851 }
852
853 pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
854 return dataProcessing(cond, .rsc, 1, rd, rn, op2);
855 }
856
857 pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
858 return dataProcessing(cond, .tst, 1, .r0, rn, op2);
859 }
860
861 pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction {
862 return dataProcessing(cond, .teq, 1, .r0, rn, op2);
863 }
864
865 pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction {
866 return dataProcessing(cond, .cmp, 1, .r0, rn, op2);
867 }
868
869 pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction {
870 return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
871 }
872
873 pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
874 return dataProcessing(cond, .orr, 0, rd, rn, op2);
875 }
876
877 pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
878 return dataProcessing(cond, .orr, 1, rd, rn, op2);
879 }
880
881 pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction {
882 return dataProcessing(cond, .mov, 0, rd, .r0, op2);
883 }
884
885 pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction {
886 return dataProcessing(cond, .mov, 1, rd, .r0, op2);
887 }
888
889 pub fn bic(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
890 return dataProcessing(cond, .bic, 0, rd, rn, op2);
891 }
892
893 pub fn bics(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
894 return dataProcessing(cond, .bic, 1, rd, rn, op2);
895 }
896
897 pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction {
898 return dataProcessing(cond, .mvn, 0, rd, .r0, op2);
899 }
900
901 pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction {
902 return dataProcessing(cond, .mvn, 1, rd, .r0, op2);
903 }
904
905 // Integer Saturating Arithmetic
906
907 pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
908 return integerSaturationArithmetic(cond, rd, rm, rn, 0b00);
909 }
910
911 pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
912 return integerSaturationArithmetic(cond, rd, rm, rn, 0b01);
913 }
914
915 pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
916 return integerSaturationArithmetic(cond, rd, rm, rn, 0b10);
917 }
918
919 pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
920 return integerSaturationArithmetic(cond, rd, rm, rn, 0b11);
921 }
922
923 // movw and movt
924
925 pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction {
926 return specialMov(cond, rd, imm, false);
927 }
928
929 pub fn movt(cond: Condition, rd: Register, imm: u16) Instruction {
930 return specialMov(cond, rd, imm, true);
931 }
932
933 // PSR transfer
934
935 pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction {
936 return Instruction{
937 .data_processing = .{
938 .cond = @enumToInt(cond),
939 .i = 0,
940 .opcode = if (psr == .spsr) 0b1010 else 0b1000,
941 .s = 0,
942 .rn = 0b1111,
943 .rd = rd.id(),
944 .op2 = 0b0000_0000_0000,
945 },
946 };
947 }
948
949 pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction {
950 return Instruction{
951 .data_processing = .{
952 .cond = @enumToInt(cond),
953 .i = 0,
954 .opcode = if (psr == .spsr) 0b1011 else 0b1001,
955 .s = 0,
956 .rn = 0b1111,
957 .rd = 0b1111,
958 .op2 = op.toU12(),
959 },
960 };
961 }
962
963 // Multiply
964
965 pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
966 return multiply(cond, 0, rd, rn, rm, null);
967 }
968
969 pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
970 return multiply(cond, 1, rd, rn, rm, null);
971 }
972
973 pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
974 return multiply(cond, 0, rd, rn, rm, ra);
975 }
976
977 pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
978 return multiply(cond, 1, rd, rn, rm, ra);
979 }
980
981 // Multiply long
982
983 pub fn umull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
984 return multiplyLong(cond, 0, 0, 0, rdhi, rdlo, rm, rn);
985 }
986
987 pub fn umulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
988 return multiplyLong(cond, 0, 0, 1, rdhi, rdlo, rm, rn);
989 }
990
991 pub fn umlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
992 return multiplyLong(cond, 0, 1, 0, rdhi, rdlo, rm, rn);
993 }
994
995 pub fn umlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
996 return multiplyLong(cond, 0, 1, 1, rdhi, rdlo, rm, rn);
997 }
998
999 pub fn smull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1000 return multiplyLong(cond, 1, 0, 0, rdhi, rdlo, rm, rn);
1001 }
1002
1003 pub fn smulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1004 return multiplyLong(cond, 1, 0, 1, rdhi, rdlo, rm, rn);
1005 }
1006
1007 pub fn smlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1008 return multiplyLong(cond, 1, 1, 0, rdhi, rdlo, rm, rn);
1009 }
1010
1011 pub fn smlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction {
1012 return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn);
1013 }
1014
1015 // Single data transfer
1016
1017 pub const OffsetArgs = struct {
1018 pre_index: bool = true,
1019 positive: bool = true,
1020 offset: Offset,
1021 write_back: bool = false,
1022 };
1023
1024 pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1025 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1);
1026 }
1027
1028 pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1029 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1);
1030 }
1031
1032 pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1033 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0);
1034 }
1035
1036 pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
1037 return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0);
1038 }
1039
1040 // Extra load/store
1041
1042 pub const ExtraLoadStoreOffsetArgs = struct {
1043 pre_index: bool = true,
1044 positive: bool = true,
1045 offset: ExtraLoadStoreOffset,
1046 write_back: bool = false,
1047 };
1048
1049 pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
1050 return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0, 0b01, rn, rt, args.offset);
1051 }
1052
1053 pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
1054 return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 1, 0b01, rn, rt, args.offset);
1055 }
1056
1057 // Block data transfer
1058
1059 pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1060 return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1);
1061 }
1062
1063 pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1064 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1);
1065 }
1066
1067 pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1068 return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1);
1069 }
1070
1071 pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1072 return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1);
1073 }
1074
1075 pub const ldmfa = ldmda;
1076 pub const ldmea = ldmdb;
1077 pub const ldmed = ldmib;
1078 pub const ldmfd = ldmia;
1079 pub const ldm = ldmia;
1080
1081 pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1082 return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0);
1083 }
1084
1085 pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1086 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0);
1087 }
1088
1089 pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1090 return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0);
1091 }
1092
1093 pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
1094 return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0);
1095 }
1096
1097 pub const stmed = stmda;
1098 pub const stmfd = stmdb;
1099 pub const stmfa = stmib;
1100 pub const stmea = stmia;
1101 pub const stm = stmia;
1102
1103 // Branch
1104
1105 pub fn b(cond: Condition, offset: i26) Instruction {
1106 return branch(cond, offset, 0);
1107 }
1108
1109 pub fn bl(cond: Condition, offset: i26) Instruction {
1110 return branch(cond, offset, 1);
1111 }
1112
1113 // Branch and exchange
1114
1115 pub fn bx(cond: Condition, rn: Register) Instruction {
1116 return branchExchange(cond, rn, 0);
1117 }
1118
1119 pub fn blx(cond: Condition, rn: Register) Instruction {
1120 return branchExchange(cond, rn, 1);
1121 }
1122
1123 // Supervisor Call
1124
1125 pub const swi = svc;
1126
1127 pub fn svc(cond: Condition, comment: u24) Instruction {
1128 return supervisorCall(cond, comment);
1129 }
1130
1131 // Breakpoint
1132
1133 pub fn bkpt(imm: u16) Instruction {
1134 return breakpoint(imm);
1135 }
1136
1137 // Aliases
1138
1139 pub fn nop() Instruction {
1140 return mov(.al, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none));
1141 }
1142
1143 pub fn pop(cond: Condition, args: anytype) Instruction {
1144 if (@typeInfo(@TypeOf(args)) != .Struct) {
1145 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
1146 }
1147
1148 if (args.len < 1) {
1149 @compileError("Expected at least one register");
1150 } else if (args.len == 1) {
1151 const reg = args[0];
1152 return ldr(cond, reg, .sp, .{
1153 .pre_index = false,
1154 .positive = true,
1155 .offset = Offset.imm(4),
1156 .write_back = false,
1157 });
1158 } else {
1159 var register_list: u16 = 0;
1160 inline for (args) |arg| {
1161 const reg = @as(Register, arg);
1162 register_list |= @as(u16, 1) << reg.id();
1163 }
1164 return ldm(cond, .sp, true, @bitCast(RegisterList, register_list));
1165 }
1166 }
1167
1168 pub fn push(cond: Condition, args: anytype) Instruction {
1169 if (@typeInfo(@TypeOf(args)) != .Struct) {
1170 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
1171 }
1172
1173 if (args.len < 1) {
1174 @compileError("Expected at least one register");
1175 } else if (args.len == 1) {
1176 const reg = args[0];
1177 return str(cond, reg, .sp, .{
1178 .pre_index = true,
1179 .positive = false,
1180 .offset = Offset.imm(4),
1181 .write_back = true,
1182 });
1183 } else {
1184 var register_list: u16 = 0;
1185 inline for (args) |arg| {
1186 const reg = @as(Register, arg);
1187 register_list |= @as(u16, 1) << reg.id();
1188 }
1189 return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list));
1190 }
1191 }
1192
1193 pub const ShiftAmount = union(enum) {
1194 immediate: u5,
1195 register: Register,
1196
1197 pub fn imm(immediate: u5) ShiftAmount {
1198 return .{
1199 .immediate = immediate,
1200 };
1201 }
1202
1203 pub fn reg(register: Register) ShiftAmount {
1204 return .{
1205 .register = register,
1206 };
1207 }
1208 };
1209
1210 pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1211 return switch (shift) {
1212 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
1213 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
1214 };
1215 }
1216
1217 pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1218 return switch (shift) {
1219 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
1220 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
1221 };
1222 }
1223
1224 pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1225 return switch (shift) {
1226 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
1227 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
1228 };
1229 }
1230
1231 pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1232 return switch (shift) {
1233 .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
1234 .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
1235 };
1236 }
1237
1238 pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1239 return switch (shift) {
1240 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
1241 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
1242 };
1243 }
1244
1245 pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1246 return switch (shift) {
1247 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
1248 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
1249 };
1250 }
1251
1252 pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1253 return switch (shift) {
1254 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
1255 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
1256 };
1257 }
1258
1259 pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
1260 return switch (shift) {
1261 .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
1262 .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
1263 };
1264 }
1265};
1266
1267test "serialize instructions" {
1268 const Testcase = struct {
1269 inst: Instruction,
1270 expected: u32,
1271 };
1272
1273 const testcases = [_]Testcase{
1274 .{ // add r0, r0, r0
1275 .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
1276 .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
1277 },
1278 .{ // mov r4, r2
1279 .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
1280 .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
1281 },
1282 .{ // mov r0, #42
1283 .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)),
1284 .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
1285 },
1286 .{ // mrs r5, cpsr
1287 .inst = Instruction.mrs(.al, .r5, .cpsr),
1288 .expected = 0b1110_00010_0_001111_0101_000000000000,
1289 },
1290 .{ // mul r0, r1, r2
1291 .inst = Instruction.mul(.al, .r0, .r1, .r2),
1292 .expected = 0b1110_000000_0_0_0000_0000_0010_1001_0001,
1293 },
1294 .{ // umlal r0, r1, r5, r6
1295 .inst = Instruction.umlal(.al, .r0, .r1, .r5, .r6),
1296 .expected = 0b1110_00001_0_1_0_0001_0000_0110_1001_0101,
1297 },
1298 .{ // ldr r0, [r2, #42]
1299 .inst = Instruction.ldr(.al, .r0, .r2, .{
1300 .offset = Instruction.Offset.imm(42),
1301 }),
1302 .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
1303 },
1304 .{ // str r0, [r3]
1305 .inst = Instruction.str(.al, .r0, .r3, .{
1306 .offset = Instruction.Offset.none,
1307 }),
1308 .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
1309 },
1310 .{ // strh r1, [r5]
1311 .inst = Instruction.strh(.al, .r1, .r5, .{
1312 .offset = Instruction.ExtraLoadStoreOffset.none,
1313 }),
1314 .expected = 0b1110_000_1_1_1_0_0_0101_0001_0000_1011_0000,
1315 },
1316 .{ // b #12
1317 .inst = Instruction.b(.al, 12),
1318 .expected = 0b1110_101_0_0000_0000_0000_0000_0000_0011,
1319 },
1320 .{ // bl #-4
1321 .inst = Instruction.bl(.al, -4),
1322 .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1111,
1323 },
1324 .{ // bx lr
1325 .inst = Instruction.bx(.al, .lr),
1326 .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110,
1327 },
1328 .{ // svc #0
1329 .inst = Instruction.svc(.al, 0),
1330 .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000,
1331 },
1332 .{ // bkpt #42
1333 .inst = Instruction.bkpt(42),
1334 .expected = 0b1110_0001_0010_000000000010_0111_1010,
1335 },
1336 .{ // stmdb r9, {r0}
1337 .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }),
1338 .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001,
1339 },
1340 .{ // ldmea r4!, {r2, r5}
1341 .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }),
1342 .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100,
1343 },
1344 .{ // qadd r0, r7, r8
1345 .inst = Instruction.qadd(.al, .r0, .r7, .r8),
1346 .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111,
1347 },
1348 };
1349
1350 for (testcases) |case| {
1351 const actual = case.inst.toU32();
1352 try testing.expectEqual(case.expected, actual);
1353 }
1354}
1355
1356test "aliases" {
1357 const Testcase = struct {
1358 expected: Instruction,
1359 actual: Instruction,
1360 };
1361
1362 const testcases = [_]Testcase{
1363 .{ // pop { r6 }
1364 .actual = Instruction.pop(.al, .{.r6}),
1365 .expected = Instruction.ldr(.al, .r6, .sp, .{
1366 .pre_index = false,
1367 .positive = true,
1368 .offset = Instruction.Offset.imm(4),
1369 .write_back = false,
1370 }),
1371 },
1372 .{ // pop { r1, r5 }
1373 .actual = Instruction.pop(.al, .{ .r1, .r5 }),
1374 .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }),
1375 },
1376 .{ // push { r3 }
1377 .actual = Instruction.push(.al, .{.r3}),
1378 .expected = Instruction.str(.al, .r3, .sp, .{
1379 .pre_index = true,
1380 .positive = false,
1381 .offset = Instruction.Offset.imm(4),
1382 .write_back = true,
1383 }),
1384 },
1385 .{ // push { r0, r2 }
1386 .actual = Instruction.push(.al, .{ .r0, .r2 }),
1387 .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }),
1388 },
1389 .{ // lsl r4, r5, #5
1390 .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)),
1391 .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg(
1392 .r5,
1393 Instruction.Operand.Shift.imm(5, .logical_left),
1394 )),
1395 },
1396 .{ // asrs r1, r1, r3
1397 .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)),
1398 .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg(
1399 .r1,
1400 Instruction.Operand.Shift.reg(.r3, .arithmetic_right),
1401 )),
1402 },
1403 };
1404
1405 for (testcases) |case| {
1406 try testing.expectEqual(case.expected.toU32(), case.actual.toU32());
1407 }
1408}
src/codegen/riscv64.zig deleted-470
......@@ -1,470 +0,0 @@
1const std = @import("std");
2const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6// TODO: this is only tagged to facilitate the monstrosity.
7// Once packed structs work make it packed.
8pub const Instruction = union(enum) {
9 R: packed struct {
10 opcode: u7,
11 rd: u5,
12 funct3: u3,
13 rs1: u5,
14 rs2: u5,
15 funct7: u7,
16 },
17 I: packed struct {
18 opcode: u7,
19 rd: u5,
20 funct3: u3,
21 rs1: u5,
22 imm0_11: u12,
23 },
24 S: packed struct {
25 opcode: u7,
26 imm0_4: u5,
27 funct3: u3,
28 rs1: u5,
29 rs2: u5,
30 imm5_11: u7,
31 },
32 B: packed struct {
33 opcode: u7,
34 imm11: u1,
35 imm1_4: u4,
36 funct3: u3,
37 rs1: u5,
38 rs2: u5,
39 imm5_10: u6,
40 imm12: u1,
41 },
42 U: packed struct {
43 opcode: u7,
44 rd: u5,
45 imm12_31: u20,
46 },
47 J: packed struct {
48 opcode: u7,
49 rd: u5,
50 imm12_19: u8,
51 imm11: u1,
52 imm1_10: u10,
53 imm20: u1,
54 },
55
56 // TODO: once packed structs work we can remove this monstrosity.
57 pub fn toU32(self: Instruction) u32 {
58 return switch (self) {
59 .R => |v| @bitCast(u32, v),
60 .I => |v| @bitCast(u32, v),
61 .S => |v| @bitCast(u32, v),
62 .B => |v| @intCast(u32, v.opcode) + (@intCast(u32, v.imm11) << 7) + (@intCast(u32, v.imm1_4) << 8) + (@intCast(u32, v.funct3) << 12) + (@intCast(u32, v.rs1) << 15) + (@intCast(u32, v.rs2) << 20) + (@intCast(u32, v.imm5_10) << 25) + (@intCast(u32, v.imm12) << 31),
63 .U => |v| @bitCast(u32, v),
64 .J => |v| @bitCast(u32, v),
65 };
66 }
67
68 fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction {
69 return Instruction{
70 .R = .{
71 .opcode = op,
72 .funct3 = fn3,
73 .funct7 = fn7,
74 .rd = @enumToInt(rd),
75 .rs1 = @enumToInt(r1),
76 .rs2 = @enumToInt(r2),
77 },
78 };
79 }
80
81 // RISC-V is all signed all the time -- convert immediates to unsigned for processing
82 fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction {
83 const umm = @bitCast(u12, imm);
84
85 return Instruction{
86 .I = .{
87 .opcode = op,
88 .funct3 = fn3,
89 .rd = @enumToInt(rd),
90 .rs1 = @enumToInt(r1),
91 .imm0_11 = umm,
92 },
93 };
94 }
95
96 fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction {
97 const umm = @bitCast(u12, imm);
98
99 return Instruction{
100 .S = .{
101 .opcode = op,
102 .funct3 = fn3,
103 .rs1 = @enumToInt(r1),
104 .rs2 = @enumToInt(r2),
105 .imm0_4 = @truncate(u5, umm),
106 .imm5_11 = @truncate(u7, umm >> 5),
107 },
108 };
109 }
110
111 // Use significance value rather than bit value, same for J-type
112 // -- less burden on callsite, bonus semantic checking
113 fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
114 const umm = @bitCast(u13, imm);
115 assert(umm % 2 == 0); // misaligned branch target
116
117 return Instruction{
118 .B = .{
119 .opcode = op,
120 .funct3 = fn3,
121 .rs1 = @enumToInt(r1),
122 .rs2 = @enumToInt(r2),
123 .imm1_4 = @truncate(u4, umm >> 1),
124 .imm5_10 = @truncate(u6, umm >> 5),
125 .imm11 = @truncate(u1, umm >> 11),
126 .imm12 = @truncate(u1, umm >> 12),
127 },
128 };
129 }
130
131 // We have to extract the 20 bits anyway -- let's not make it more painful
132 fn uType(op: u7, rd: Register, imm: i20) Instruction {
133 const umm = @bitCast(u20, imm);
134
135 return Instruction{
136 .U = .{
137 .opcode = op,
138 .rd = @enumToInt(rd),
139 .imm12_31 = umm,
140 },
141 };
142 }
143
144 fn jType(op: u7, rd: Register, imm: i21) Instruction {
145 const umm = @bitCast(u21, imm);
146 assert(umm % 2 == 0); // misaligned jump target
147
148 return Instruction{
149 .J = .{
150 .opcode = op,
151 .rd = @enumToInt(rd),
152 .imm1_10 = @truncate(u10, umm >> 1),
153 .imm11 = @truncate(u1, umm >> 11),
154 .imm12_19 = @truncate(u8, umm >> 12),
155 .imm20 = @truncate(u1, umm >> 20),
156 },
157 };
158 }
159
160 // The meat and potatoes. Arguments are in the order in which they would appear in assembly code.
161
162 // Arithmetic/Logical, Register-Register
163
164 pub fn add(rd: Register, r1: Register, r2: Register) Instruction {
165 return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2);
166 }
167
168 pub fn sub(rd: Register, r1: Register, r2: Register) Instruction {
169 return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2);
170 }
171
172 pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction {
173 return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2);
174 }
175
176 pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction {
177 return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2);
178 }
179
180 pub fn xor(rd: Register, r1: Register, r2: Register) Instruction {
181 return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2);
182 }
183
184 pub fn sll(rd: Register, r1: Register, r2: Register) Instruction {
185 return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2);
186 }
187
188 pub fn srl(rd: Register, r1: Register, r2: Register) Instruction {
189 return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2);
190 }
191
192 pub fn sra(rd: Register, r1: Register, r2: Register) Instruction {
193 return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2);
194 }
195
196 pub fn slt(rd: Register, r1: Register, r2: Register) Instruction {
197 return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2);
198 }
199
200 pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction {
201 return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2);
202 }
203
204 // Arithmetic/Logical, Register-Register (32-bit)
205
206 pub fn addw(rd: Register, r1: Register, r2: Register) Instruction {
207 return rType(0b0111011, 0b000, rd, r1, r2);
208 }
209
210 pub fn subw(rd: Register, r1: Register, r2: Register) Instruction {
211 return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2);
212 }
213
214 pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction {
215 return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2);
216 }
217
218 pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction {
219 return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2);
220 }
221
222 pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction {
223 return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2);
224 }
225
226 // Arithmetic/Logical, Register-Immediate
227
228 pub fn addi(rd: Register, r1: Register, imm: i12) Instruction {
229 return iType(0b0010011, 0b000, rd, r1, imm);
230 }
231
232 pub fn andi(rd: Register, r1: Register, imm: i12) Instruction {
233 return iType(0b0010011, 0b111, rd, r1, imm);
234 }
235
236 pub fn ori(rd: Register, r1: Register, imm: i12) Instruction {
237 return iType(0b0010011, 0b110, rd, r1, imm);
238 }
239
240 pub fn xori(rd: Register, r1: Register, imm: i12) Instruction {
241 return iType(0b0010011, 0b100, rd, r1, imm);
242 }
243
244 pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction {
245 return iType(0b0010011, 0b001, rd, r1, shamt);
246 }
247
248 pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction {
249 return iType(0b0010011, 0b101, rd, r1, shamt);
250 }
251
252 pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction {
253 return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt);
254 }
255
256 pub fn slti(rd: Register, r1: Register, imm: i12) Instruction {
257 return iType(0b0010011, 0b010, rd, r1, imm);
258 }
259
260 pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction {
261 return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm));
262 }
263
264 // Arithmetic/Logical, Register-Immediate (32-bit)
265
266 pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction {
267 return iType(0b0011011, 0b000, rd, r1, imm);
268 }
269
270 pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction {
271 return iType(0b0011011, 0b001, rd, r1, shamt);
272 }
273
274 pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction {
275 return iType(0b0011011, 0b101, rd, r1, shamt);
276 }
277
278 pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction {
279 return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt);
280 }
281
282 // Upper Immediate
283
284 pub fn lui(rd: Register, imm: i20) Instruction {
285 return uType(0b0110111, rd, imm);
286 }
287
288 pub fn auipc(rd: Register, imm: i20) Instruction {
289 return uType(0b0010111, rd, imm);
290 }
291
292 // Load
293
294 pub fn ld(rd: Register, offset: i12, base: Register) Instruction {
295 return iType(0b0000011, 0b011, rd, base, offset);
296 }
297
298 pub fn lw(rd: Register, offset: i12, base: Register) Instruction {
299 return iType(0b0000011, 0b010, rd, base, offset);
300 }
301
302 pub fn lwu(rd: Register, offset: i12, base: Register) Instruction {
303 return iType(0b0000011, 0b110, rd, base, offset);
304 }
305
306 pub fn lh(rd: Register, offset: i12, base: Register) Instruction {
307 return iType(0b0000011, 0b001, rd, base, offset);
308 }
309
310 pub fn lhu(rd: Register, offset: i12, base: Register) Instruction {
311 return iType(0b0000011, 0b101, rd, base, offset);
312 }
313
314 pub fn lb(rd: Register, offset: i12, base: Register) Instruction {
315 return iType(0b0000011, 0b000, rd, base, offset);
316 }
317
318 pub fn lbu(rd: Register, offset: i12, base: Register) Instruction {
319 return iType(0b0000011, 0b100, rd, base, offset);
320 }
321
322 // Store
323
324 pub fn sd(rs: Register, offset: i12, base: Register) Instruction {
325 return sType(0b0100011, 0b011, base, rs, offset);
326 }
327
328 pub fn sw(rs: Register, offset: i12, base: Register) Instruction {
329 return sType(0b0100011, 0b010, base, rs, offset);
330 }
331
332 pub fn sh(rs: Register, offset: i12, base: Register) Instruction {
333 return sType(0b0100011, 0b001, base, rs, offset);
334 }
335
336 pub fn sb(rs: Register, offset: i12, base: Register) Instruction {
337 return sType(0b0100011, 0b000, base, rs, offset);
338 }
339
340 // Fence
341 // TODO: implement fence
342
343 // Branch
344
345 pub fn beq(r1: Register, r2: Register, offset: i13) Instruction {
346 return bType(0b1100011, 0b000, r1, r2, offset);
347 }
348
349 pub fn bne(r1: Register, r2: Register, offset: i13) Instruction {
350 return bType(0b1100011, 0b001, r1, r2, offset);
351 }
352
353 pub fn blt(r1: Register, r2: Register, offset: i13) Instruction {
354 return bType(0b1100011, 0b100, r1, r2, offset);
355 }
356
357 pub fn bge(r1: Register, r2: Register, offset: i13) Instruction {
358 return bType(0b1100011, 0b101, r1, r2, offset);
359 }
360
361 pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction {
362 return bType(0b1100011, 0b110, r1, r2, offset);
363 }
364
365 pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction {
366 return bType(0b1100011, 0b111, r1, r2, offset);
367 }
368
369 // Jump
370
371 pub fn jal(link: Register, offset: i21) Instruction {
372 return jType(0b1101111, link, offset);
373 }
374
375 pub fn jalr(link: Register, offset: i12, base: Register) Instruction {
376 return iType(0b1100111, 0b000, link, base, offset);
377 }
378
379 // System
380
381 pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
382 pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
383};
384
385// zig fmt: off
386pub const RawRegister = enum(u5) {
387 x0, x1, x2, x3, x4, x5, x6, x7,
388 x8, x9, x10, x11, x12, x13, x14, x15,
389 x16, x17, x18, x19, x20, x21, x22, x23,
390 x24, x25, x26, x27, x28, x29, x30, x31,
391
392 pub fn dwarfLocOp(reg: RawRegister) u8 {
393 return @enumToInt(reg) + DW.OP.reg0;
394 }
395};
396
397pub const Register = enum(u5) {
398 // 64 bit registers
399 zero, // zero
400 ra, // return address. caller saved
401 sp, // stack pointer. callee saved.
402 gp, // global pointer
403 tp, // thread pointer
404 t0, t1, t2, // temporaries. caller saved.
405 s0, // s0/fp, callee saved.
406 s1, // callee saved.
407 a0, a1, // fn args/return values. caller saved.
408 a2, a3, a4, a5, a6, a7, // fn args. caller saved.
409 s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
410 t3, t4, t5, t6, // caller saved
411
412 pub fn parseRegName(name: []const u8) ?Register {
413 if(std.meta.stringToEnum(Register, name)) |reg| return reg;
414 if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg));
415 return null;
416 }
417
418 /// Returns the index into `callee_preserved_regs`.
419 pub fn allocIndex(self: Register) ?u4 {
420 inline for(callee_preserved_regs) |cpreg, i| {
421 if(self == cpreg) return i;
422 }
423 return null;
424 }
425
426 pub fn dwarfLocOp(reg: Register) u8 {
427 return @as(u8, @enumToInt(reg)) + DW.OP.reg0;
428 }
429};
430
431// zig fmt: on
432
433pub const callee_preserved_regs = [_]Register{
434 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
435};
436
437test "serialize instructions" {
438 const Testcase = struct {
439 inst: Instruction,
440 expected: u32,
441 };
442
443 const testcases = [_]Testcase{
444 .{ // add t6, zero, zero
445 .inst = Instruction.add(.t6, .zero, .zero),
446 .expected = 0b0000000_00000_00000_000_11111_0110011,
447 },
448 .{ // sd s0, 0x7f(s0)
449 .inst = Instruction.sd(.s0, 0x7f, .s0),
450 .expected = 0b0000011_01000_01000_011_11111_0100011,
451 },
452 .{ // bne s0, s1, 0x42
453 .inst = Instruction.bne(.s0, .s1, 0x42),
454 .expected = 0b0_000010_01001_01000_001_0001_0_1100011,
455 },
456 .{ // j 0x1a
457 .inst = Instruction.jal(.zero, 0x1a),
458 .expected = 0b0_0000001101_0_00000000_00000_1101111,
459 },
460 .{ // ebreak
461 .inst = Instruction.ebreak,
462 .expected = 0b000000000001_00000_000_00000_1110011,
463 },
464 };
465
466 for (testcases) |case| {
467 const actual = case.inst.toU32();
468 try testing.expectEqual(case.expected, actual);
469 }
470}
src/codegen/x86.zig deleted-123
......@@ -1,123 +0,0 @@
1const std = @import("std");
2const DW = std.dwarf;
3
4// zig fmt: off
5pub const Register = enum(u8) {
6 // 0 through 7, 32-bit registers. id is int value
7 eax, ecx, edx, ebx, esp, ebp, esi, edi,
8
9 // 8-15, 16-bit registers. id is int value - 8.
10 ax, cx, dx, bx, sp, bp, si, di,
11
12 // 16-23, 8-bit registers. id is int value - 16.
13 al, cl, dl, bl, ah, ch, dh, bh,
14
15 /// Returns the bit-width of the register.
16 pub fn size(self: @This()) u7 {
17 return switch (@enumToInt(self)) {
18 0...7 => 32,
19 8...15 => 16,
20 16...23 => 8,
21 else => unreachable,
22 };
23 }
24
25 /// Returns the register's id. This is used in practically every opcode the
26 /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move
27 /// instruction, and is used in the R/M byte.
28 pub fn id(self: @This()) u3 {
29 return @truncate(u3, @enumToInt(self));
30 }
31
32 /// Returns the index into `callee_preserved_regs`.
33 pub fn allocIndex(self: Register) ?u4 {
34 return switch (self) {
35 .eax, .ax, .al => 0,
36 .ecx, .cx, .cl => 1,
37 .edx, .dx, .dl => 2,
38 .esi, .si => 3,
39 .edi, .di => 4,
40 else => null,
41 };
42 }
43
44 /// Convert from any register to its 32 bit alias.
45 pub fn to32(self: Register) Register {
46 return @intToEnum(Register, @as(u8, self.id()));
47 }
48
49 /// Convert from any register to its 16 bit alias.
50 pub fn to16(self: Register) Register {
51 return @intToEnum(Register, @as(u8, self.id()) + 8);
52 }
53
54 /// Convert from any register to its 8 bit alias.
55 pub fn to8(self: Register) Register {
56 return @intToEnum(Register, @as(u8, self.id()) + 16);
57 }
58
59
60 pub fn dwarfLocOp(reg: Register) u8 {
61 return switch (reg.to32()) {
62 .eax => DW.OP.reg0,
63 .ecx => DW.OP.reg1,
64 .edx => DW.OP.reg2,
65 .ebx => DW.OP.reg3,
66 .esp => DW.OP.reg4,
67 .ebp => DW.OP.reg5,
68 .esi => DW.OP.reg6,
69 .edi => DW.OP.reg7,
70 else => unreachable,
71 };
72 }
73};
74
75// zig fmt: on
76
77pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi };
78
79// TODO add these to Register enum and corresponding dwarfLocOp
80// // Return Address register. This is stored in `0(%esp, "")` and is not a physical register.
81// RA = (8, "RA"),
82//
83// ST0 = (11, "st0"),
84// ST1 = (12, "st1"),
85// ST2 = (13, "st2"),
86// ST3 = (14, "st3"),
87// ST4 = (15, "st4"),
88// ST5 = (16, "st5"),
89// ST6 = (17, "st6"),
90// ST7 = (18, "st7"),
91//
92// XMM0 = (21, "xmm0"),
93// XMM1 = (22, "xmm1"),
94// XMM2 = (23, "xmm2"),
95// XMM3 = (24, "xmm3"),
96// XMM4 = (25, "xmm4"),
97// XMM5 = (26, "xmm5"),
98// XMM6 = (27, "xmm6"),
99// XMM7 = (28, "xmm7"),
100//
101// MM0 = (29, "mm0"),
102// MM1 = (30, "mm1"),
103// MM2 = (31, "mm2"),
104// MM3 = (32, "mm3"),
105// MM4 = (33, "mm4"),
106// MM5 = (34, "mm5"),
107// MM6 = (35, "mm6"),
108// MM7 = (36, "mm7"),
109//
110// MXCSR = (39, "mxcsr"),
111//
112// ES = (40, "es"),
113// CS = (41, "cs"),
114// SS = (42, "ss"),
115// DS = (43, "ds"),
116// FS = (44, "fs"),
117// GS = (45, "gs"),
118//
119// TR = (48, "tr"),
120// LDTR = (49, "ldtr"),
121//
122// FS_BASE = (93, "fs.base"),
123// GS_BASE = (94, "gs.base"),
src/codegen/x86_64.zig deleted-716
......@@ -1,716 +0,0 @@
1const std = @import("std");
2const testing = std.testing;
3const mem = std.mem;
4const assert = std.debug.assert;
5const ArrayList = std.ArrayList;
6const Allocator = std.mem.Allocator;
7const DW = std.dwarf;
8
9// zig fmt: off
10
11/// Definitions of all of the x64 registers. The order is semantically meaningful.
12/// The registers are defined such that IDs go in descending order of 64-bit,
13/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
14/// registers. This results in some useful properties:
15///
16/// Any 64-bit register can be turned into its 32-bit form by adding 16, and
17/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it
18/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit
19/// form.
20///
21/// If (register & 8) is set, the register is extended.
22///
23/// The ID can be easily determined by figuring out what range the register is
24/// in, and then subtracting the base.
25pub const Register = enum(u8) {
26 // 0 through 15, 64-bit registers. 8-15 are extended.
27 // id is just the int value.
28 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
29 r8, r9, r10, r11, r12, r13, r14, r15,
30
31 // 16 through 31, 32-bit registers. 24-31 are extended.
32 // id is int value - 16.
33 eax, ecx, edx, ebx, esp, ebp, esi, edi,
34 r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d,
35
36 // 32-47, 16-bit registers. 40-47 are extended.
37 // id is int value - 32.
38 ax, cx, dx, bx, sp, bp, si, di,
39 r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w,
40
41 // 48-63, 8-bit registers. 56-63 are extended.
42 // id is int value - 48.
43 al, cl, dl, bl, ah, ch, dh, bh,
44 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
45
46 /// Returns the bit-width of the register.
47 pub fn size(self: Register) u7 {
48 return switch (@enumToInt(self)) {
49 0...15 => 64,
50 16...31 => 32,
51 32...47 => 16,
52 48...64 => 8,
53 else => unreachable,
54 };
55 }
56
57 /// Returns whether the register is *extended*. Extended registers are the
58 /// new registers added with amd64, r8 through r15. This also includes any
59 /// other variant of access to those registers, such as r8b, r15d, and so
60 /// on. This is needed because access to these registers requires special
61 /// handling via the REX prefix, via the B or R bits, depending on context.
62 pub fn isExtended(self: Register) bool {
63 return @enumToInt(self) & 0x08 != 0;
64 }
65
66 /// This returns the 4-bit register ID, which is used in practically every
67 /// opcode. Note that bit 3 (the highest bit) is *never* used directly in
68 /// an instruction (@see isExtended), and requires special handling. The
69 /// lower three bits are often embedded directly in instructions (such as
70 /// the B8 variant of moves), or used in R/M bytes.
71 pub fn id(self: Register) u4 {
72 return @truncate(u4, @enumToInt(self));
73 }
74
75 /// Like id, but only returns the lower 3 bits.
76 pub fn low_id(self: Register) u3 {
77 return @truncate(u3, @enumToInt(self));
78 }
79
80 /// Returns the index into `callee_preserved_regs`.
81 pub fn allocIndex(self: Register) ?u4 {
82 return switch (self) {
83 .rax, .eax, .ax, .al => 0,
84 .rcx, .ecx, .cx, .cl => 1,
85 .rdx, .edx, .dx, .dl => 2,
86 .rsi, .esi, .si => 3,
87 .rdi, .edi, .di => 4,
88 .r8, .r8d, .r8w, .r8b => 5,
89 .r9, .r9d, .r9w, .r9b => 6,
90 .r10, .r10d, .r10w, .r10b => 7,
91 .r11, .r11d, .r11w, .r11b => 8,
92 else => null,
93 };
94 }
95
96 /// Convert from any register to its 64 bit alias.
97 pub fn to64(self: Register) Register {
98 return @intToEnum(Register, self.id());
99 }
100
101 /// Convert from any register to its 32 bit alias.
102 pub fn to32(self: Register) Register {
103 return @intToEnum(Register, @as(u8, self.id()) + 16);
104 }
105
106 /// Convert from any register to its 16 bit alias.
107 pub fn to16(self: Register) Register {
108 return @intToEnum(Register, @as(u8, self.id()) + 32);
109 }
110
111 /// Convert from any register to its 8 bit alias.
112 pub fn to8(self: Register) Register {
113 return @intToEnum(Register, @as(u8, self.id()) + 48);
114 }
115
116 pub fn dwarfLocOp(self: Register) u8 {
117 return switch (self.to64()) {
118 .rax => DW.OP.reg0,
119 .rdx => DW.OP.reg1,
120 .rcx => DW.OP.reg2,
121 .rbx => DW.OP.reg3,
122 .rsi => DW.OP.reg4,
123 .rdi => DW.OP.reg5,
124 .rbp => DW.OP.reg6,
125 .rsp => DW.OP.reg7,
126
127 .r8 => DW.OP.reg8,
128 .r9 => DW.OP.reg9,
129 .r10 => DW.OP.reg10,
130 .r11 => DW.OP.reg11,
131 .r12 => DW.OP.reg12,
132 .r13 => DW.OP.reg13,
133 .r14 => DW.OP.reg14,
134 .r15 => DW.OP.reg15,
135
136 else => unreachable,
137 };
138 }
139};
140
141// zig fmt: on
142
143/// These registers belong to the called function.
144pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
145pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
146pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
147
148/// Encoding helper functions for x86_64 instructions
149///
150/// Many of these helpers do very little, but they can help make things
151/// slightly more readable with more descriptive field names / function names.
152///
153/// Some of them also have asserts to ensure that we aren't doing dumb things.
154/// For example, trying to use register 4 (esp) in an indirect modr/m byte is illegal,
155/// you need to encode it with an SIB byte.
156///
157/// Note that ALL of these helper functions will assume capacity,
158/// so ensure that the `code` has sufficient capacity before using them.
159/// The `init` method is the recommended way to ensure capacity.
160pub const Encoder = struct {
161 /// Non-owning reference to the code array
162 code: *ArrayList(u8),
163
164 const Self = @This();
165
166 /// Wrap `code` in Encoder to make it easier to call these helper functions
167 ///
168 /// maximum_inst_size should contain the maximum number of bytes
169 /// that the encoded instruction will take.
170 /// This is because the helper functions will assume capacity
171 /// in order to avoid bounds checking.
172 pub fn init(code: *ArrayList(u8), maximum_inst_size: u8) !Self {
173 try code.ensureUnusedCapacity(maximum_inst_size);
174 return Self{ .code = code };
175 }
176
177 /// Directly write a number to the code array with big endianness
178 pub fn writeIntBig(self: Self, comptime T: type, value: T) void {
179 mem.writeIntBig(
180 T,
181 self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)),
182 value,
183 );
184 }
185
186 /// Directly write a number to the code array with little endianness
187 pub fn writeIntLittle(self: Self, comptime T: type, value: T) void {
188 mem.writeIntLittle(
189 T,
190 self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)),
191 value,
192 );
193 }
194
195 // --------
196 // Prefixes
197 // --------
198
199 pub const LegacyPrefixes = packed struct {
200 /// LOCK
201 prefix_f0: bool = false,
202 /// REPNZ, REPNE, REP, Scalar Double-precision
203 prefix_f2: bool = false,
204 /// REPZ, REPE, REP, Scalar Single-precision
205 prefix_f3: bool = false,
206
207 /// CS segment override or Branch not taken
208 prefix_2e: bool = false,
209 /// DS segment override
210 prefix_36: bool = false,
211 /// ES segment override
212 prefix_26: bool = false,
213 /// FS segment override
214 prefix_64: bool = false,
215 /// GS segment override
216 prefix_65: bool = false,
217
218 /// Branch taken
219 prefix_3e: bool = false,
220
221 /// Operand size override (enables 16 bit operation)
222 prefix_66: bool = false,
223
224 /// Address size override (enables 16 bit address size)
225 prefix_67: bool = false,
226
227 padding: u5 = 0,
228 };
229
230 /// Encodes legacy prefixes
231 pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) void {
232 if (@bitCast(u16, prefixes) != 0) {
233 // Hopefully this path isn't taken very often, so we'll do it the slow way for now
234
235 // LOCK
236 if (prefixes.prefix_f0) self.code.appendAssumeCapacity(0xf0);
237 // REPNZ, REPNE, REP, Scalar Double-precision
238 if (prefixes.prefix_f2) self.code.appendAssumeCapacity(0xf2);
239 // REPZ, REPE, REP, Scalar Single-precision
240 if (prefixes.prefix_f3) self.code.appendAssumeCapacity(0xf3);
241
242 // CS segment override or Branch not taken
243 if (prefixes.prefix_2e) self.code.appendAssumeCapacity(0x2e);
244 // DS segment override
245 if (prefixes.prefix_36) self.code.appendAssumeCapacity(0x36);
246 // ES segment override
247 if (prefixes.prefix_26) self.code.appendAssumeCapacity(0x26);
248 // FS segment override
249 if (prefixes.prefix_64) self.code.appendAssumeCapacity(0x64);
250 // GS segment override
251 if (prefixes.prefix_65) self.code.appendAssumeCapacity(0x65);
252
253 // Branch taken
254 if (prefixes.prefix_3e) self.code.appendAssumeCapacity(0x3e);
255
256 // Operand size override
257 if (prefixes.prefix_66) self.code.appendAssumeCapacity(0x66);
258
259 // Address size override
260 if (prefixes.prefix_67) self.code.appendAssumeCapacity(0x67);
261 }
262 }
263
264 /// Use 16 bit operand size
265 ///
266 /// Note that this flag is overridden by REX.W, if both are present.
267 pub fn prefix16BitMode(self: Self) void {
268 self.code.appendAssumeCapacity(0x66);
269 }
270
271 /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB
272 pub const Rex = struct {
273 /// Wide, enables 64-bit operation
274 w: bool = false,
275 /// Extends the reg field in the ModR/M byte
276 r: bool = false,
277 /// Extends the index field in the SIB byte
278 x: bool = false,
279 /// Extends the r/m field in the ModR/M byte,
280 /// or the base field in the SIB byte,
281 /// or the reg field in the Opcode byte
282 b: bool = false,
283 };
284
285 /// Encodes a REX prefix byte given all the fields
286 ///
287 /// Use this byte whenever you need 64 bit operation,
288 /// or one of reg, index, r/m, base, or opcode-reg might be extended.
289 ///
290 /// See struct `Rex` for a description of each field.
291 ///
292 /// Does not add a prefix byte if none of the fields are set!
293 pub fn rex(self: Self, byte: Rex) void {
294 var value: u8 = 0b0100_0000;
295
296 if (byte.w) value |= 0b1000;
297 if (byte.r) value |= 0b0100;
298 if (byte.x) value |= 0b0010;
299 if (byte.b) value |= 0b0001;
300
301 if (value != 0b0100_0000) {
302 self.code.appendAssumeCapacity(value);
303 }
304 }
305
306 // ------
307 // Opcode
308 // ------
309
310 /// Encodes a 1 byte opcode
311 pub fn opcode_1byte(self: Self, opcode: u8) void {
312 self.code.appendAssumeCapacity(opcode);
313 }
314
315 /// Encodes a 2 byte opcode
316 ///
317 /// e.g. IMUL has the opcode 0x0f 0xaf, so you use
318 ///
319 /// encoder.opcode_2byte(0x0f, 0xaf);
320 pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) void {
321 self.code.appendAssumeCapacity(prefix);
322 self.code.appendAssumeCapacity(opcode);
323 }
324
325 /// Encodes a 1 byte opcode with a reg field
326 ///
327 /// Remember to add a REX prefix byte if reg is extended!
328 pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) void {
329 assert(opcode & 0b111 == 0);
330 self.code.appendAssumeCapacity(opcode | reg);
331 }
332
333 // ------
334 // ModR/M
335 // ------
336
337 /// Construct a ModR/M byte given all the fields
338 ///
339 /// Remember to add a REX prefix byte if reg or rm are extended!
340 pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) void {
341 self.code.appendAssumeCapacity(
342 @as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm,
343 );
344 }
345
346 /// Construct a ModR/M byte using direct r/m addressing
347 /// r/m effective address: r/m
348 ///
349 /// Note reg's effective address is always just reg for the ModR/M byte.
350 /// Remember to add a REX prefix byte if reg or rm are extended!
351 pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) void {
352 self.modRm(0b11, reg_or_opx, rm);
353 }
354
355 /// Construct a ModR/M byte using indirect r/m addressing
356 /// r/m effective address: [r/m]
357 ///
358 /// Note reg's effective address is always just reg for the ModR/M byte.
359 /// Remember to add a REX prefix byte if reg or rm are extended!
360 pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) void {
361 assert(rm != 4 and rm != 5);
362 self.modRm(0b00, reg_or_opx, rm);
363 }
364
365 /// Construct a ModR/M byte using indirect SIB addressing
366 /// r/m effective address: [SIB]
367 ///
368 /// Note reg's effective address is always just reg for the ModR/M byte.
369 /// Remember to add a REX prefix byte if reg or rm are extended!
370 pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) void {
371 self.modRm(0b00, reg_or_opx, 0b100);
372 }
373
374 /// Construct a ModR/M byte using RIP-relative addressing
375 /// r/m effective address: [RIP + disp32]
376 ///
377 /// Note reg's effective address is always just reg for the ModR/M byte.
378 /// Remember to add a REX prefix byte if reg or rm are extended!
379 pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) void {
380 self.modRm(0b00, reg_or_opx, 0b101);
381 }
382
383 /// Construct a ModR/M byte using indirect r/m with a 8bit displacement
384 /// r/m effective address: [r/m + disp8]
385 ///
386 /// Note reg's effective address is always just reg for the ModR/M byte.
387 /// Remember to add a REX prefix byte if reg or rm are extended!
388 pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) void {
389 assert(rm != 4);
390 self.modRm(0b01, reg_or_opx, rm);
391 }
392
393 /// Construct a ModR/M byte using indirect SIB with a 8bit displacement
394 /// r/m effective address: [SIB + disp8]
395 ///
396 /// Note reg's effective address is always just reg for the ModR/M byte.
397 /// Remember to add a REX prefix byte if reg or rm are extended!
398 pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) void {
399 self.modRm(0b01, reg_or_opx, 0b100);
400 }
401
402 /// Construct a ModR/M byte using indirect r/m with a 32bit displacement
403 /// r/m effective address: [r/m + disp32]
404 ///
405 /// Note reg's effective address is always just reg for the ModR/M byte.
406 /// Remember to add a REX prefix byte if reg or rm are extended!
407 pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) void {
408 assert(rm != 4);
409 self.modRm(0b10, reg_or_opx, rm);
410 }
411
412 /// Construct a ModR/M byte using indirect SIB with a 32bit displacement
413 /// r/m effective address: [SIB + disp32]
414 ///
415 /// Note reg's effective address is always just reg for the ModR/M byte.
416 /// Remember to add a REX prefix byte if reg or rm are extended!
417 pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) void {
418 self.modRm(0b10, reg_or_opx, 0b100);
419 }
420
421 // ---
422 // SIB
423 // ---
424
425 /// Construct a SIB byte given all the fields
426 ///
427 /// Remember to add a REX prefix byte if index or base are extended!
428 pub fn sib(self: Self, scale: u2, index: u3, base: u3) void {
429 self.code.appendAssumeCapacity(
430 @as(u8, scale) << 6 | @as(u8, index) << 3 | base,
431 );
432 }
433
434 /// Construct a SIB byte with scale * index + base, no frills.
435 /// r/m effective address: [base + scale * index]
436 ///
437 /// Remember to add a REX prefix byte if index or base are extended!
438 pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) void {
439 assert(base != 5);
440
441 self.sib(scale, index, base);
442 }
443
444 /// Construct a SIB byte with scale * index + disp32
445 /// r/m effective address: [scale * index + disp32]
446 ///
447 /// Remember to add a REX prefix byte if index or base are extended!
448 pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) void {
449 assert(index != 4);
450
451 // scale is actually ignored
452 // index = 4 means no index
453 // base = 5 means no base, if mod == 0.
454 self.sib(scale, index, 5);
455 }
456
457 /// Construct a SIB byte with just base
458 /// r/m effective address: [base]
459 ///
460 /// Remember to add a REX prefix byte if index or base are extended!
461 pub fn sib_base(self: Self, base: u3) void {
462 assert(base != 5);
463
464 // scale is actually ignored
465 // index = 4 means no index
466 self.sib(0, 4, base);
467 }
468
469 /// Construct a SIB byte with just disp32
470 /// r/m effective address: [disp32]
471 ///
472 /// Remember to add a REX prefix byte if index or base are extended!
473 pub fn sib_disp32(self: Self) void {
474 // scale is actually ignored
475 // index = 4 means no index
476 // base = 5 means no base, if mod == 0.
477 self.sib(0, 4, 5);
478 }
479
480 /// Construct a SIB byte with scale * index + base + disp8
481 /// r/m effective address: [base + scale * index + disp8]
482 ///
483 /// Remember to add a REX prefix byte if index or base are extended!
484 pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) void {
485 self.sib(scale, index, base);
486 }
487
488 /// Construct a SIB byte with base + disp8, no index
489 /// r/m effective address: [base + disp8]
490 ///
491 /// Remember to add a REX prefix byte if index or base are extended!
492 pub fn sib_baseDisp8(self: Self, base: u3) void {
493 // scale is ignored
494 // index = 4 means no index
495 self.sib(0, 4, base);
496 }
497
498 /// Construct a SIB byte with scale * index + base + disp32
499 /// r/m effective address: [base + scale * index + disp32]
500 ///
501 /// Remember to add a REX prefix byte if index or base are extended!
502 pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) void {
503 self.sib(scale, index, base);
504 }
505
506 /// Construct a SIB byte with base + disp32, no index
507 /// r/m effective address: [base + disp32]
508 ///
509 /// Remember to add a REX prefix byte if index or base are extended!
510 pub fn sib_baseDisp32(self: Self, base: u3) void {
511 // scale is ignored
512 // index = 4 means no index
513 self.sib(0, 4, base);
514 }
515
516 // -------------------------
517 // Trivial (no bit fiddling)
518 // -------------------------
519
520 /// Encode an 8 bit immediate
521 ///
522 /// It is sign-extended to 64 bits by the cpu.
523 pub fn imm8(self: Self, imm: i8) void {
524 self.code.appendAssumeCapacity(@bitCast(u8, imm));
525 }
526
527 /// Encode an 8 bit displacement
528 ///
529 /// It is sign-extended to 64 bits by the cpu.
530 pub fn disp8(self: Self, disp: i8) void {
531 self.code.appendAssumeCapacity(@bitCast(u8, disp));
532 }
533
534 /// Encode an 16 bit immediate
535 ///
536 /// It is sign-extended to 64 bits by the cpu.
537 pub fn imm16(self: Self, imm: i16) void {
538 self.writeIntLittle(i16, imm);
539 }
540
541 /// Encode an 32 bit immediate
542 ///
543 /// It is sign-extended to 64 bits by the cpu.
544 pub fn imm32(self: Self, imm: i32) void {
545 self.writeIntLittle(i32, imm);
546 }
547
548 /// Encode an 32 bit displacement
549 ///
550 /// It is sign-extended to 64 bits by the cpu.
551 pub fn disp32(self: Self, disp: i32) void {
552 self.writeIntLittle(i32, disp);
553 }
554
555 /// Encode an 64 bit immediate
556 ///
557 /// It is sign-extended to 64 bits by the cpu.
558 pub fn imm64(self: Self, imm: u64) void {
559 self.writeIntLittle(u64, imm);
560 }
561};
562
563test "x86_64 Encoder helpers" {
564 var code = ArrayList(u8).init(testing.allocator);
565 defer code.deinit();
566
567 // simple integer multiplication
568
569 // imul eax,edi
570 // 0faf c7
571 {
572 try code.resize(0);
573 const encoder = try Encoder.init(&code, 4);
574 encoder.rex(.{
575 .r = Register.eax.isExtended(),
576 .b = Register.edi.isExtended(),
577 });
578 encoder.opcode_2byte(0x0f, 0xaf);
579 encoder.modRm_direct(
580 Register.eax.low_id(),
581 Register.edi.low_id(),
582 );
583
584 try testing.expectEqualSlices(u8, &[_]u8{ 0x0f, 0xaf, 0xc7 }, code.items);
585 }
586
587 // simple mov
588
589 // mov eax,edi
590 // 89 f8
591 {
592 try code.resize(0);
593 const encoder = try Encoder.init(&code, 3);
594 encoder.rex(.{
595 .r = Register.edi.isExtended(),
596 .b = Register.eax.isExtended(),
597 });
598 encoder.opcode_1byte(0x89);
599 encoder.modRm_direct(
600 Register.edi.low_id(),
601 Register.eax.low_id(),
602 );
603
604 try testing.expectEqualSlices(u8, &[_]u8{ 0x89, 0xf8 }, code.items);
605 }
606
607 // signed integer addition of 32-bit sign extended immediate to 64 bit register
608
609 // add rcx, 2147483647
610 //
611 // Using the following opcode: REX.W + 81 /0 id, we expect the following encoding
612 //
613 // 48 : REX.W set for 64 bit operand (*r*cx)
614 // 81 : opcode for "<arithmetic> with immediate"
615 // c1 : id = rcx,
616 // : c1 = 11 <-- mod = 11 indicates r/m is register (rcx)
617 // : 000 <-- opcode_extension = 0 because opcode extension is /0. /0 specifies ADD
618 // : 001 <-- 001 is rcx
619 // ffffff7f : 2147483647
620 {
621 try code.resize(0);
622 const encoder = try Encoder.init(&code, 7);
623 encoder.rex(.{ .w = true }); // use 64 bit operation
624 encoder.opcode_1byte(0x81);
625 encoder.modRm_direct(
626 0,
627 Register.rcx.low_id(),
628 );
629 encoder.imm32(2147483647);
630
631 try testing.expectEqualSlices(u8, &[_]u8{ 0x48, 0x81, 0xc1, 0xff, 0xff, 0xff, 0x7f }, code.items);
632 }
633}
634
635// TODO add these registers to the enum and populate dwarfLocOp
636// // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register.
637// RA = (16, "RA"),
638//
639// XMM0 = (17, "xmm0"),
640// XMM1 = (18, "xmm1"),
641// XMM2 = (19, "xmm2"),
642// XMM3 = (20, "xmm3"),
643// XMM4 = (21, "xmm4"),
644// XMM5 = (22, "xmm5"),
645// XMM6 = (23, "xmm6"),
646// XMM7 = (24, "xmm7"),
647//
648// XMM8 = (25, "xmm8"),
649// XMM9 = (26, "xmm9"),
650// XMM10 = (27, "xmm10"),
651// XMM11 = (28, "xmm11"),
652// XMM12 = (29, "xmm12"),
653// XMM13 = (30, "xmm13"),
654// XMM14 = (31, "xmm14"),
655// XMM15 = (32, "xmm15"),
656//
657// ST0 = (33, "st0"),
658// ST1 = (34, "st1"),
659// ST2 = (35, "st2"),
660// ST3 = (36, "st3"),
661// ST4 = (37, "st4"),
662// ST5 = (38, "st5"),
663// ST6 = (39, "st6"),
664// ST7 = (40, "st7"),
665//
666// MM0 = (41, "mm0"),
667// MM1 = (42, "mm1"),
668// MM2 = (43, "mm2"),
669// MM3 = (44, "mm3"),
670// MM4 = (45, "mm4"),
671// MM5 = (46, "mm5"),
672// MM6 = (47, "mm6"),
673// MM7 = (48, "mm7"),
674//
675// RFLAGS = (49, "rFLAGS"),
676// ES = (50, "es"),
677// CS = (51, "cs"),
678// SS = (52, "ss"),
679// DS = (53, "ds"),
680// FS = (54, "fs"),
681// GS = (55, "gs"),
682//
683// FS_BASE = (58, "fs.base"),
684// GS_BASE = (59, "gs.base"),
685//
686// TR = (62, "tr"),
687// LDTR = (63, "ldtr"),
688// MXCSR = (64, "mxcsr"),
689// FCW = (65, "fcw"),
690// FSW = (66, "fsw"),
691//
692// XMM16 = (67, "xmm16"),
693// XMM17 = (68, "xmm17"),
694// XMM18 = (69, "xmm18"),
695// XMM19 = (70, "xmm19"),
696// XMM20 = (71, "xmm20"),
697// XMM21 = (72, "xmm21"),
698// XMM22 = (73, "xmm22"),
699// XMM23 = (74, "xmm23"),
700// XMM24 = (75, "xmm24"),
701// XMM25 = (76, "xmm25"),
702// XMM26 = (77, "xmm26"),
703// XMM27 = (78, "xmm27"),
704// XMM28 = (79, "xmm28"),
705// XMM29 = (80, "xmm29"),
706// XMM30 = (81, "xmm30"),
707// XMM31 = (82, "xmm31"),
708//
709// K0 = (118, "k0"),
710// K1 = (119, "k1"),
711// K2 = (120, "k2"),
712// K3 = (121, "k3"),
713// K4 = (122, "k4"),
714// K5 = (123, "k5"),
715// K6 = (124, "k6"),
716// K7 = (125, "k7"),
src/link/MachO.zig+2-2
......@@ -12,7 +12,7 @@ const math = std.math;
1212const mem = std.mem;
1313const meta = std.meta;
1414
15const aarch64 = @import("../codegen/aarch64.zig");
15const aarch64 = @import("../arch/aarch64/bits.zig");
1616const bind = @import("MachO/bind.zig");
1717const codegen = @import("../codegen.zig");
1818const commands = @import("MachO/commands.zig");
......@@ -200,7 +200,7 @@ atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},
200200
201201/// List of atoms that are owned directly by the linker.
202202/// Currently these are only atoms that are the result of linking
203/// object files. Atoms which take part in incremental linking are
203/// object files. Atoms which take part in incremental linking are
204204/// at present owned by Module.Decl.
205205/// TODO consolidate this.
206206managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
src/link/MachO/Atom.zig+1-1
......@@ -2,7 +2,7 @@ const Atom = @This();
22
33const std = @import("std");
44const build_options = @import("build_options");
5const aarch64 = @import("../../codegen/aarch64.zig");
5const aarch64 = @import("../../arch/aarch64/bits.zig");
66const assert = std.debug.assert;
77const commands = @import("commands.zig");
88const log = std.log.scoped(.text_block);