lib/choir/src/backends/aarch64/disassemble/format.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const registers = @import("../registers/root.zig");
3
4 const Encoded = registers.Encoded;
5
6 const GprSize = enum { w, x };
7 const GprRole = enum { base, other };
8
9 const ConditionName = [_][]const u8{
10 "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
11 "hi", "ls", "ge", "lt", "gt", "le", "al", "nv",
12 };
13
14 pub fn formatInstructionRaw(raw: u32, buf: []u8) []const u8 {
15 return formatInstructionRawWithDetail(raw, buf, false);
16 }
17
18 pub fn formatInstructionRawWithDetail(raw: u32, buf: []u8, show_offsets_in_bytes: bool) []const u8 {
19 var writer = std.Io.Writer.fixed(buf);
20
21 disassembleToWriter(raw, &writer, show_offsets_in_bytes) catch {
22 return fallbackUnknown(raw, buf);
23 };
24
25 return writer.buffered();
26 }
27
28 pub fn disassembleToWriter(raw: u32, writer: anytype, show_offsets_in_bytes: bool) !void {
29 if (try decodeBranchReg(raw, writer)) return;
30 if (try decodeBranchCond(raw, writer, show_offsets_in_bytes)) return;
31 if (try decodeBranchImm(raw, writer, show_offsets_in_bytes)) return;
32
33 if (try decodeLoadStorePair(raw, writer)) return;
34 if (try decodeLoadStoreRegImm(raw, writer)) return;
35
36 if (try decodeDataProcessingImmediate(raw, writer)) return;
37 if (try decodeConditionalSelect(raw, writer)) return;
38 if (try decodeDataProcessingRegister(raw, writer)) return;
39
40 if (try decodeFPImmediate(raw, writer)) return;
41 if (try decodeFP(raw, writer)) return;
42
43 try writer.print("unknown 0x{x:0>8}", .{raw});
44 }
45
46 fn fallbackUnknown(raw: u32, buf: []u8) []const u8 {
47 return std.fmt.bufPrint(buf, "unknown 0x{x:0>8}", .{raw}) catch "unknown";
48 }
49
50 fn bits(raw: u32, lo: u5, width: u5) u32 {
51 const mask: u32 = if (width == 32) std.math.maxInt(u32) else (@as(u32, 1) << width) - 1;
52 return (raw >> lo) & mask;
53 }
54
55 fn signExtend(value: u32, width: u6) i64 {
56 const shift: u7 = @as(u7, 64) - @as(u7, width);
57 return (@as(i64, @intCast(value)) << @intCast(shift)) >> @intCast(shift);
58 }
59
60 fn gprSizeFromBit(sf: u1) GprSize {
61 return if (sf == 0) .w else .x;
62 }
63
64 fn gprScaleBytes(size: GprSize) u32 {
65 return switch (size) {
66 .w => 4,
67 .x => 8,
68 };
69 }
70
71 fn condName(cond: u4) []const u8 {
72 return ConditionName[cond];
73 }
74
75 fn invertCond(cond: u4) u4 {
76 return cond ^ 1;
77 }
78
79 fn formatGpr(buf: []u8, reg: Encoded, size: GprSize, role: GprRole) []const u8 {
80 const n: u5 = reg.toInt();
81 if (n == 31) {
82 return switch (role) {
83 .base => "sp",
84 .other => switch (size) {
85 .w => "wzr",
86 .x => "xzr",
87 },
88 };
89 }
90
91 const prefix: u8 = switch (size) {
92 .w => 'w',
93 .x => 'x',
94 };
95 return std.fmt.bufPrint(buf, "{c}{d}", .{ prefix, n }) catch "x?";
96 }
97
98 fn formatFpr(buf: []u8, reg: Encoded, fp_prefix: u8) []const u8 {
99 return std.fmt.bufPrint(buf, "{c}{d}", .{ fp_prefix, reg.toInt() }) catch "v?";
100 }
101
102 fn formatUnsignedHex(buf: []u8, value: u64) []const u8 {
103 return std.fmt.bufPrint(buf, "0x{x}", .{value}) catch "0x?";
104 }
105
106 fn formatSignedHex(buf: []u8, value: i64) []const u8 {
107 if (value < 0) {
108 const mag: u64 = @intCast(-value);
109 return std.fmt.bufPrint(buf, "-0x{x}", .{mag}) catch "-0x?";
110 }
111 return std.fmt.bufPrint(buf, "0x{x}", .{@as(u64, @intCast(value))}) catch "0x?";
112 }
113
114 fn decodeDataProcessingImmediate(raw: u32, writer: anytype) !bool {
115 const fixed_23_28 = bits(raw, 23, 6);
116
117 if (fixed_23_28 == 0b100010) {
118 return decodeAddSubImmediate(raw, writer);
119 }
120 if (fixed_23_28 == 0b100101) {
121 return decodeMoveWide(raw, writer);
122 }
123
124 return false;
125 }
126
127 fn decodeAddSubImmediate(raw: u32, writer: anytype) !bool {
128 const sf: u1 = @intCast(bits(raw, 31, 1));
129 const op: u1 = @intCast(bits(raw, 30, 1));
130 const s: u1 = @intCast(bits(raw, 29, 1));
131 const sh: u1 = @intCast(bits(raw, 22, 1));
132 const imm12: u12 = @intCast(bits(raw, 10, 12));
133 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
134 const rd: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
135
136 const size = gprSizeFromBit(sf);
137 const imm_value: u32 = if (sh == 1) (@as(u32, imm12) << 12) else imm12;
138
139 var reg_buf0: [8]u8 = undefined;
140 var reg_buf1: [8]u8 = undefined;
141 var imm_buf: [32]u8 = undefined;
142
143 const rn_name = formatGpr(®_buf0, rn, size, .other);
144 const imm_name = formatUnsignedHex(&imm_buf, imm_value);
145
146 if (s == 1 and rd.toInt() == 31) {
147 try writer.print("cmp {s}, #{s}", .{ rn_name, imm_name });
148 return true;
149 }
150
151 const rd_name = formatGpr(®_buf1, rd, size, .other);
152
153 const base = if (op == 0) "add" else "sub";
154 const suffix = if (s == 1) "s" else "";
155 try writer.print("{s}{s} {s}, {s}, #{s}", .{ base, suffix, rd_name, rn_name, imm_name });
156 return true;
157 }
158
159 fn decodeMoveWide(raw: u32, writer: anytype) !bool {
160 const sf: u1 = @intCast(bits(raw, 31, 1));
161 const opc: u2 = @intCast(bits(raw, 29, 2));
162 const hw: u2 = @intCast(bits(raw, 21, 2));
163 const imm16: u16 = @intCast(bits(raw, 5, 16));
164 const rd: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
165
166 const mnemonic = switch (opc) {
167 0b00 => "movn",
168 0b10 => "movz",
169 0b11 => "movk",
170 else => return false,
171 };
172
173 const size = gprSizeFromBit(sf);
174 const shift_amount: u32 = @as(u32, hw) * 16;
175
176 var reg_buf: [8]u8 = undefined;
177 var imm_buf: [32]u8 = undefined;
178
179 const rd_name = formatGpr(®_buf, rd, size, .other);
180 const imm_name = formatUnsignedHex(&imm_buf, imm16);
181
182 if (shift_amount == 0) {
183 try writer.print("{s} {s}, #{s}", .{ mnemonic, rd_name, imm_name });
184 return true;
185 }
186
187 try writer.print("{s} {s}, #{s}, lsl #{d}", .{ mnemonic, rd_name, imm_name, shift_amount });
188 return true;
189 }
190
191 fn decodeDataProcessingRegister(raw: u32, writer: anytype) !bool {
192 const fixed_24_28 = bits(raw, 24, 5);
193
194 if (fixed_24_28 == 0b01011 and bits(raw, 21, 1) == 0) {
195 return decodeAddSubShiftedRegister(raw, writer);
196 }
197 if (fixed_24_28 == 0b01010) {
198 return decodeLogicalShiftedRegister(raw, writer);
199 }
200 if (bits(raw, 21, 8) == 0b11010110 and bits(raw, 30, 1) == 0) {
201 return decodeDataProcessing2Source(raw, writer);
202 }
203 if (fixed_24_28 == 0b11011) {
204 return decodeDataProcessing3Source(raw, writer);
205 }
206
207 return false;
208 }
209
210 fn decodeAddSubShiftedRegister(raw: u32, writer: anytype) !bool {
211 const sf: u1 = @intCast(bits(raw, 31, 1));
212 const op: u1 = @intCast(bits(raw, 30, 1));
213 const s: u1 = @intCast(bits(raw, 29, 1));
214 const shift: u2 = @intCast(bits(raw, 22, 2));
215 const imm6: u6 = @intCast(bits(raw, 10, 6));
216 const rm: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 16, 5)))));
217 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
218 const rd: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
219
220 const size = gprSizeFromBit(sf);
221
222 var reg_buf0: [8]u8 = undefined;
223 var reg_buf1: [8]u8 = undefined;
224 var reg_buf2: [8]u8 = undefined;
225
226 const rn_name = formatGpr(®_buf0, rn, size, .other);
227 const rm_name = formatGpr(®_buf1, rm, size, .other);
228
229 if (s == 1 and rd.toInt() == 31) {
230 try writer.print("cmp {s}, {s}", .{ rn_name, rm_name });
231 if (imm6 != 0 or shift != 0) {
232 try writeShiftSuffix(writer, shift, imm6);
233 }
234 return true;
235 }
236
237 const rd_name = formatGpr(®_buf2, rd, size, .other);
238
239 const base = if (op == 0) "add" else "sub";
240 const suffix = if (s == 1) "s" else "";
241
242 try writer.print("{s}{s} {s}, {s}, {s}", .{ base, suffix, rd_name, rn_name, rm_name });
243 if (imm6 != 0 or shift != 0) {
244 try writeShiftSuffix(writer, shift, imm6);
245 }
246 return true;
247 }
248
249 fn writeShiftSuffix(writer: anytype, shift: u2, amount: u6) !void {
250 const shift_name = switch (shift) {
251 0b00 => "lsl",
252 0b01 => "lsr",
253 0b10 => "asr",
254 else => "ror",
255 };
256 try writer.print(", {s} #{d}", .{ shift_name, amount });
257 }
258
259 fn decodeLogicalShiftedRegister(raw: u32, writer: anytype) !bool {
260 const sf: u1 = @intCast(bits(raw, 31, 1));
261 const opc: u2 = @intCast(bits(raw, 29, 2));
262 const shift: u2 = @intCast(bits(raw, 22, 2));
263 const imm6: u6 = @intCast(bits(raw, 10, 6));
264 const n: u1 = @intCast(bits(raw, 21, 1));
265 const rm: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 16, 5)))));
266 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
267 const rd: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
268
269 const size = gprSizeFromBit(sf);
270
271 var reg_buf0: [8]u8 = undefined;
272 var reg_buf1: [8]u8 = undefined;
273 var reg_buf2: [8]u8 = undefined;
274
275 const rn_name = formatGpr(®_buf0, rn, size, .other);
276 const rm_name = formatGpr(®_buf1, rm, size, .other);
277 const rd_name = formatGpr(®_buf2, rd, size, .other);
278
279 if (opc == 0b01 and n == 0 and rn.toInt() == 31 and shift == 0 and imm6 == 0) {
280 try writer.print("mov {s}, {s}", .{ rd_name, rm_name });
281 return true;
282 }
283
284 const mnemonic = switch (opc) {
285 0b00 => "and",
286 0b01 => "orr",
287 0b10 => "eor",
288 0b11 => "ands",
289 };
290
291 try writer.print("{s} {s}, {s}, {s}", .{ mnemonic, rd_name, rn_name, rm_name });
292 if (shift != 0 or imm6 != 0) {
293 try writeShiftSuffix(writer, shift, imm6);
294 }
295 return true;
296 }
297
298 fn decodeDataProcessing2Source(raw: u32, writer: anytype) !bool {
299 const sf: u1 = @intCast(bits(raw, 31, 1));
300 const opcode: u6 = @intCast(bits(raw, 10, 6));
301 const rm: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 16, 5)))));
302 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
303 const rd: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
304
305 const mnemonic = switch (opcode) {
306 0b000010 => "udiv",
307 0b000011 => "sdiv",
308 else => return false,
309 };
310
311 const size = gprSizeFromBit(sf);
312
313 var reg_buf0: [8]u8 = undefined;
314 var reg_buf1: [8]u8 = undefined;
315 var reg_buf2: [8]u8 = undefined;
316
317 const rd_name = formatGpr(®_buf0, rd, size, .other);
318 const rn_name = formatGpr(®_buf1, rn, size, .other);
319 const rm_name = formatGpr(®_buf2, rm, size, .other);
320
321 try writer.print("{s} {s}, {s}, {s}", .{ mnemonic, rd_name, rn_name, rm_name });
322 return true;
323 }
324
325 fn decodeDataProcessing3Source(raw: u32, writer: anytype) !bool {
326 const sf: u1 = @intCast(bits(raw, 31, 1));
327 const op54: u2 = @intCast(bits(raw, 29, 2));
328 const op31: u3 = @intCast(bits(raw, 21, 3));
329 const o0: u1 = @intCast(bits(raw, 15, 1));
330 const rm: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 16, 5)))));
331 const ra: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 10, 5)))));
332 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
333 const rd: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
334
335 if (op54 != 0 or op31 != 0) return false;
336
337 const size = gprSizeFromBit(sf);
338
339 var reg_buf0: [8]u8 = undefined;
340 var reg_buf1: [8]u8 = undefined;
341 var reg_buf2: [8]u8 = undefined;
342 var reg_buf3: [8]u8 = undefined;
343
344 const rd_name = formatGpr(®_buf0, rd, size, .other);
345 const rn_name = formatGpr(®_buf1, rn, size, .other);
346 const rm_name = formatGpr(®_buf2, rm, size, .other);
347 const ra_name = formatGpr(®_buf3, ra, size, .other);
348
349 if (o0 == 0 and ra.toInt() == 31) {
350 try writer.print("mul {s}, {s}, {s}", .{ rd_name, rn_name, rm_name });
351 return true;
352 }
353
354 const mnemonic = if (o0 == 0) "madd" else "msub";
355 try writer.print("{s} {s}, {s}, {s}, {s}", .{ mnemonic, rd_name, rn_name, rm_name, ra_name });
356 return true;
357 }
358
359 fn decodeConditionalSelect(raw: u32, writer: anytype) !bool {
360 if (bits(raw, 24, 5) != 0b11010) return false;
361 if (bits(raw, 22, 2) != 0b10) return false;
362 if (bits(raw, 21, 1) != 0) return false;
363
364 const sf: u1 = @intCast(bits(raw, 31, 1));
365 const op: u1 = @intCast(bits(raw, 30, 1));
366 const cond: u4 = @intCast(bits(raw, 12, 4));
367 const op2: u2 = @intCast(bits(raw, 10, 2));
368 const rm: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 16, 5)))));
369 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
370 const rd: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
371
372 if (op != 0) return false;
373
374 const size = gprSizeFromBit(sf);
375
376 var reg_buf0: [8]u8 = undefined;
377 var reg_buf1: [8]u8 = undefined;
378 var reg_buf2: [8]u8 = undefined;
379
380 const rd_name = formatGpr(®_buf0, rd, size, .other);
381
382 if (op2 == 0b01 and rn.toInt() == 31 and rm.toInt() == 31) {
383 const original_cond = invertCond(cond);
384 try writer.print("cset {s}, {s}", .{ rd_name, condName(original_cond) });
385 return true;
386 }
387
388 const rn_name = formatGpr(®_buf1, rn, size, .other);
389 const rm_name = formatGpr(®_buf2, rm, size, .other);
390
391 if (op2 != 0b00) return false;
392
393 try writer.print("csel {s}, {s}, {s}, {s}", .{ rd_name, rn_name, rm_name, condName(cond) });
394 return true;
395 }
396
397 fn decodeLoadStoreRegImm(raw: u32, writer: anytype) !bool {
398 if (bits(raw, 24, 2) != 0b01) return false;
399 if (bits(raw, 27, 3) != 0b111) return false;
400
401 const v: u1 = @intCast(bits(raw, 26, 1));
402 const size_bits: u2 = @intCast(bits(raw, 30, 2));
403 const opc: u2 = @intCast(bits(raw, 22, 2));
404 const imm12: u12 = @intCast(bits(raw, 10, 12));
405 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
406 const rt: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
407
408 const is_load = opc == 0b01;
409 if (!is_load and opc != 0b00) return false;
410
411 var reg_buf0: [8]u8 = undefined;
412 var reg_buf1: [8]u8 = undefined;
413 var off_buf: [32]u8 = undefined;
414
415 const mnemonic = if (is_load) "ldr" else "str";
416
417 if (v == 0) {
418 const size: GprSize = switch (size_bits) {
419 0b10 => .w,
420 0b11 => .x,
421 else => return false,
422 };
423 const scale = gprScaleBytes(size);
424 const byte_offset: u32 = @as(u32, imm12) * scale;
425
426 const rt_name = formatGpr(®_buf0, rt, size, .other);
427 const rn_name = formatGpr(®_buf1, rn, size, .base);
428
429 try writer.print("{s} {s}, [", .{ mnemonic, rt_name });
430 try writer.writeAll(rn_name);
431 if (byte_offset != 0) {
432 const off_name = formatUnsignedHex(&off_buf, byte_offset);
433 try writer.print(", #{s}", .{off_name});
434 }
435 try writer.writeAll("]");
436 return true;
437 }
438
439 const fp_prefix: u8 = switch (size_bits) {
440 0b10 => 's',
441 0b11 => 'd',
442 else => return false,
443 };
444 const scale: u32 = if (fp_prefix == 's') 4 else 8;
445 const byte_offset: u32 = @as(u32, imm12) * scale;
446
447 const rt_name = formatFpr(®_buf0, rt, fp_prefix);
448 const rn_name = formatGpr(®_buf1, rn, .x, .base);
449
450 try writer.print("{s} {s}, [", .{ mnemonic, rt_name });
451 try writer.writeAll(rn_name);
452 if (byte_offset != 0) {
453 const off_name = formatUnsignedHex(&off_buf, byte_offset);
454 try writer.print(", #{s}", .{off_name});
455 }
456 try writer.writeAll("]");
457 return true;
458 }
459
460 fn decodeLoadStorePair(raw: u32, writer: anytype) !bool {
461 if (bits(raw, 27, 3) != 0b101) return false;
462
463 const v: u1 = @intCast(bits(raw, 26, 1));
464 const op2: u3 = @intCast(bits(raw, 23, 3));
465 const l: u1 = @intCast(bits(raw, 22, 1));
466 const imm7_bits: u7 = @intCast(bits(raw, 15, 7));
467 const rt2: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 10, 5)))));
468 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
469 const rt: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
470 const opc: u2 = @intCast(bits(raw, 30, 2));
471
472 const valid_op2 = op2 == 0b001 or op2 == 0b010 or op2 == 0b011;
473 if (!valid_op2) return false;
474
475 const signed_imm7: i64 = signExtend(imm7_bits, 7);
476
477 const mnemonic = if (l == 1) "ldp" else "stp";
478
479 var reg_buf0: [8]u8 = undefined;
480 var reg_buf1: [8]u8 = undefined;
481 var reg_buf2: [8]u8 = undefined;
482 var off_buf: [32]u8 = undefined;
483
484 const base_name = formatGpr(®_buf2, rn, .x, .base);
485
486 if (v == 0) {
487 const size: GprSize = switch (opc) {
488 0b00 => .w,
489 0b10 => .x,
490 else => return false,
491 };
492 const scale = gprScaleBytes(size);
493 const byte_offset: i64 = signed_imm7 * scale;
494
495 const rt_name = formatGpr(®_buf0, rt, size, .other);
496 const rt2_name = formatGpr(®_buf1, rt2, size, .other);
497
498 try writer.print("{s} {s}, {s}, ", .{ mnemonic, rt_name, rt2_name });
499 try writeAddressingMode(writer, base_name, op2, byte_offset, &off_buf);
500 return true;
501 }
502
503 const fp_prefix: u8 = switch (opc) {
504 0b00 => 's',
505 0b01 => 'd',
506 0b10 => 'q',
507 else => return false,
508 };
509 const scale: i64 = switch (fp_prefix) {
510 's' => 4,
511 'd' => 8,
512 'q' => 16,
513 else => 8,
514 };
515 const byte_offset: i64 = signed_imm7 * scale;
516
517 const rt_name = formatFpr(®_buf0, rt, fp_prefix);
518 const rt2_name = formatFpr(®_buf1, rt2, fp_prefix);
519
520 try writer.print("{s} {s}, {s}, ", .{ mnemonic, rt_name, rt2_name });
521 try writeAddressingMode(writer, base_name, op2, byte_offset, &off_buf);
522 return true;
523 }
524
525 fn writeAddressingMode(writer: anytype, base_name: []const u8, op2: u3, byte_offset: i64, off_buf: []u8) !void {
526 const offset_text = if (byte_offset == 0) "" else formatSignedHex(off_buf, byte_offset);
527
528 switch (op2) {
529 0b010 => {
530 try writer.writeAll("[");
531 try writer.writeAll(base_name);
532 if (byte_offset != 0) {
533 try writer.print(", #{s}", .{offset_text});
534 }
535 try writer.writeAll("]");
536 },
537 0b011 => {
538 try writer.writeAll("[");
539 try writer.writeAll(base_name);
540 if (byte_offset != 0) {
541 try writer.print(", #{s}", .{offset_text});
542 }
543 try writer.writeAll("]!");
544 },
545 0b001 => {
546 try writer.writeAll("[");
547 try writer.writeAll(base_name);
548 try writer.writeAll("]");
549 if (byte_offset != 0) {
550 try writer.print(", #{s}", .{offset_text});
551 }
552 },
553 else => {
554 try writer.print("[{s}]", .{base_name});
555 },
556 }
557 }
558
559 fn decodeBranchImm(raw: u32, writer: anytype, show_offsets_in_bytes: bool) !bool {
560 if (bits(raw, 26, 5) != 0b00101) return false;
561
562 const op: u1 = @intCast(bits(raw, 31, 1));
563 const imm26_bits: u26 = @intCast(bits(raw, 0, 26));
564 const signed_imm26: i64 = signExtend(imm26_bits, 26);
565 const offset: i64 = if (show_offsets_in_bytes) signed_imm26 * 4 else signed_imm26;
566
567 var off_buf: [32]u8 = undefined;
568 const off_name = formatSignedHex(&off_buf, offset);
569
570 const mnemonic = if (op == 1) "bl" else "b";
571 try writer.print("{s} #{s}", .{ mnemonic, off_name });
572 return true;
573 }
574
575 fn decodeBranchCond(raw: u32, writer: anytype, show_offsets_in_bytes: bool) !bool {
576 if (bits(raw, 25, 7) != 0b0101010) return false;
577 if (bits(raw, 24, 1) != 0) return false;
578 if (bits(raw, 4, 1) != 0) return false;
579
580 const cond: u4 = @intCast(bits(raw, 0, 4));
581 const imm19_bits: u19 = @intCast(bits(raw, 5, 19));
582 const signed_imm19: i64 = signExtend(imm19_bits, 19);
583 const offset: i64 = if (show_offsets_in_bytes) signed_imm19 * 4 else signed_imm19;
584
585 var off_buf: [32]u8 = undefined;
586 const off_name = formatSignedHex(&off_buf, offset);
587
588 try writer.print("b.{s} #{s}", .{ condName(cond), off_name });
589 return true;
590 }
591
592 fn decodeBranchReg(raw: u32, writer: anytype) !bool {
593 if (bits(raw, 25, 7) != 0b1101011) return false;
594
595 const op2: u5 = @intCast(bits(raw, 16, 5));
596 if (op2 != 0b11111) return false;
597
598 const opc: u4 = @intCast(bits(raw, 21, 4));
599 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
600
601 var reg_buf: [8]u8 = undefined;
602 const rn_name = formatGpr(®_buf, rn, .x, .other);
603
604 switch (opc) {
605 0b0010 => {
606 if (rn.toInt() == 30) {
607 try writer.writeAll("ret");
608 } else {
609 try writer.print("ret {s}", .{rn_name});
610 }
611 return true;
612 },
613 0b0000 => {
614 try writer.print("br {s}", .{rn_name});
615 return true;
616 },
617 0b0001 => {
618 try writer.print("blr {s}", .{rn_name});
619 return true;
620 },
621 else => return false,
622 }
623 }
624
625 fn decodeFPImmediate(raw: u32, writer: anytype) !bool {
626 if (bits(raw, 23, 9) != 0b000111100) return false;
627 if (bits(raw, 10, 3) != 0b100) return false;
628 if (bits(raw, 5, 5) != 0) return false;
629
630 const rd: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
631 const imm8: u8 = @intCast(bits(raw, 13, 8));
632 const ptype: u2 = @intCast(bits(raw, 21, 2));
633
634 const fp_prefix: u8 = switch (ptype) {
635 0b00 => 'h',
636 0b01 => 's',
637 0b11 => 'd',
638 else => 'v',
639 };
640
641 var reg_buf: [8]u8 = undefined;
642 const rd_name = formatFpr(®_buf, rd, fp_prefix);
643
644 try writer.print("fmov {s}, #0x{x}", .{ rd_name, imm8 });
645 return true;
646 }
647
648 fn decodeFP(raw: u32, writer: anytype) !bool {
649 if (bits(raw, 24, 5) != 0b11110) return false;
650 if (bits(raw, 21, 1) != 1) return false;
651 if (bits(raw, 29, 3) != 0) return false;
652
653 if (bits(raw, 10, 5) == 0b10000) {
654 return decodeFP1Source(raw, writer);
655 }
656 if (bits(raw, 10, 2) == 0b10) {
657 return decodeFP2Source(raw, writer);
658 }
659 if (bits(raw, 10, 4) == 0b1000) {
660 return decodeFPCompare(raw, writer);
661 }
662 if (bits(raw, 10, 6) == 0 and (bits(raw, 16, 3) == 0b110 or bits(raw, 16, 3) == 0b111)) {
663 return decodeFMovGeneral(raw, writer);
664 }
665
666 return false;
667 }
668
669 fn fpPrefixFromType(ptype: u2) u8 {
670 return switch (ptype) {
671 0b00 => 's',
672 0b01 => 'd',
673 else => 'v',
674 };
675 }
676
677 fn decodeFP1Source(raw: u32, writer: anytype) !bool {
678 const opcode: u6 = @intCast(bits(raw, 15, 6));
679 const ptype: u2 = @intCast(bits(raw, 22, 2));
680 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
681 const rd: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
682
683 const mnemonic = switch (opcode) {
684 0b000000 => "fmov",
685 0b000001 => "fabs",
686 0b000010 => "fneg",
687 0b000011 => "fsqrt",
688 else => return false,
689 };
690
691 const fp_prefix = fpPrefixFromType(ptype);
692
693 var reg_buf0: [8]u8 = undefined;
694 var reg_buf1: [8]u8 = undefined;
695
696 const rd_name = formatFpr(®_buf0, rd, fp_prefix);
697 const rn_name = formatFpr(®_buf1, rn, fp_prefix);
698
699 try writer.print("{s} {s}, {s}", .{ mnemonic, rd_name, rn_name });
700 return true;
701 }
702
703 fn decodeFP2Source(raw: u32, writer: anytype) !bool {
704 const opcode: u4 = @intCast(bits(raw, 12, 4));
705 const ptype: u2 = @intCast(bits(raw, 22, 2));
706 const rm: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 16, 5)))));
707 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
708 const rd: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
709
710 const mnemonic = switch (opcode) {
711 0b0000 => "fmul",
712 0b0001 => "fdiv",
713 0b0010 => "fadd",
714 0b0011 => "fsub",
715 else => return false,
716 };
717
718 const fp_prefix = fpPrefixFromType(ptype);
719
720 var reg_buf0: [8]u8 = undefined;
721 var reg_buf1: [8]u8 = undefined;
722 var reg_buf2: [8]u8 = undefined;
723
724 const rd_name = formatFpr(®_buf0, rd, fp_prefix);
725 const rn_name = formatFpr(®_buf1, rn, fp_prefix);
726 const rm_name = formatFpr(®_buf2, rm, fp_prefix);
727
728 try writer.print("{s} {s}, {s}, {s}", .{ mnemonic, rd_name, rn_name, rm_name });
729 return true;
730 }
731
732 fn decodeFPCompare(raw: u32, writer: anytype) !bool {
733 const opcode2: u5 = @intCast(bits(raw, 0, 5));
734 const op: u2 = @intCast(bits(raw, 14, 2));
735 if (op != 0) return false;
736
737 const ptype: u2 = @intCast(bits(raw, 22, 2));
738 const rm: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 16, 5)))));
739 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
740
741 const fp_prefix = fpPrefixFromType(ptype);
742
743 var reg_buf0: [8]u8 = undefined;
744 var reg_buf1: [8]u8 = undefined;
745
746 const rn_name = formatFpr(®_buf0, rn, fp_prefix);
747
748 if (opcode2 == 0b01000 and rm.toInt() == 0) {
749 try writer.print("fcmp {s}, #0.0", .{rn_name});
750 return true;
751 }
752
753 const rm_name = formatFpr(®_buf1, rm, fp_prefix);
754 try writer.print("fcmp {s}, {s}", .{ rn_name, rm_name });
755 return true;
756 }
757
758 fn decodeFMovGeneral(raw: u32, writer: anytype) !bool {
759 const opcode: u3 = @intCast(bits(raw, 16, 3));
760 const ptype: u2 = @intCast(bits(raw, 22, 2));
761 const sf: u1 = @intCast(bits(raw, 31, 1));
762 const rn: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 5, 5)))));
763 const rd: Encoded = @fromBackingInt(@intCast(@as(u5, @intCast(bits(raw, 0, 5)))));
764
765 if (opcode != 0b110 and opcode != 0b111) return false;
766
767 const fp_prefix = fpPrefixFromType(ptype);
768 const gpr_size = gprSizeFromBit(sf);
769
770 var reg_buf0: [8]u8 = undefined;
771 var reg_buf1: [8]u8 = undefined;
772
773 if (opcode == 0b111) {
774 const rd_name = formatFpr(®_buf0, rd, fp_prefix);
775 const rn_name = formatGpr(®_buf1, rn, gpr_size, .other);
776 try writer.print("fmov {s}, {s}", .{ rd_name, rn_name });
777 return true;
778 }
779
780 const rd_name = formatGpr(®_buf0, rd, gpr_size, .other);
781 const rn_name = formatFpr(®_buf1, rn, fp_prefix);
782 try writer.print("fmov {s}, {s}", .{ rd_name, rn_name });
783 return true;
784 }