lib/choir/src/dialects/aarch64.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const alloc_arena = @import("alloc_arena");
3 const ir = @import("../core/root.zig");
4
5 pub const registers = @import("../backends/aarch64/registers/root.zig");
6 pub const encoding = @import("../backends/aarch64/encoding/root.zig");
7
8 pub const GPR = registers.GPR;
9 pub const FPR = registers.FPR;
10 pub const GPRSize = registers.GPRSize;
11 pub const Condition = encoding.Condition;
12
13 pub const type_names = struct {
14 pub const gpr = "aarch64.gpr";
15 pub const fpr = "aarch64.fpr";
16 };
17
18 pub const attr_names = struct {
19 pub const gpr_reg = "aarch64.gpr_reg";
20 pub const fpr_reg = "aarch64.fpr_reg";
21 pub const imm12 = "aarch64.imm12";
22 pub const imm16 = "aarch64.imm16";
23 pub const imm8 = "aarch64.imm8";
24 pub const condition = "aarch64.condition";
25 pub const gpr_size = "aarch64.gpr_size";
26 pub const fp_type = "aarch64.fp_type";
27 pub const frame_offset = "aarch64.frame_offset";
28 };
29
30 pub const AArch64Dialect = struct {
31 pub const name = "aarch64";
32 const op_specs = ir.dialects.opSpec.dialect(@This());
33 pub const spec = ir.dialects.dialectSpec(@This(), .{
34 .types = ir.dialects.typeNames(type_names),
35 });
36
37 pub const GetRegOp = struct {
38 op: *ir.Operation,
39
40 pub const operation_spec = op_specs.define(.{
41 .mnemonic = "get_reg",
42 .required_attrs = &.{"reg"},
43 });
44 pub const operation_name = operation_spec.name;
45
46 pub fn createGPR(ctx: *ir.Context, loc: ir.Location, reg: GPR) !GetRegOp {
47 var builder = ir.OperationBuilder.init(ctx);
48 const gpr_type = try getGPRType(ctx);
49 var state = op_specs.state(@This(), loc);
50 state.addTypes(&.{gpr_type});
51
52 const op = try builder.create(state);
53 try op.setAttr("reg", try getGPRAttr(ctx, reg));
54 return .{ .op = op };
55 }
56
57 pub fn createFPR(ctx: *ir.Context, loc: ir.Location, reg: FPR) !GetRegOp {
58 var builder = ir.OperationBuilder.init(ctx);
59 const fpr_type = try getFPRType(ctx);
60 var state = op_specs.state(@This(), loc);
61 state.addTypes(&.{fpr_type});
62
63 const op = try builder.create(state);
64 try op.setAttr("reg", try getFPRAttr(ctx, reg));
65 return .{ .op = op };
66 }
67
68 pub fn getResult(self: *const GetRegOp) *ir.Value {
69 return self.op.getResult(0).?;
70 }
71
72 pub fn getGPR(self: GetRegOp) ?GPR {
73 if (self.op.getAttr("reg")) |attr| {
74 return getGPRValue(attr);
75 }
76 return null;
77 }
78
79 pub fn getFPR(self: GetRegOp) ?FPR {
80 if (self.op.getAttr("reg")) |attr| {
81 return getFPRValue(attr);
82 }
83 return null;
84 }
85 };
86
87 pub const AddOp = struct {
88 op: *ir.Operation,
89
90 pub const operation_spec = op_specs.define(.{
91 .mnemonic = "add",
92 .required_attrs = &.{"size"},
93 });
94 pub const operation_name = operation_spec.name;
95
96 pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value, size: GPRSize) !AddOp {
97 var builder = ir.OperationBuilder.init(ctx);
98 const gpr_type = try getGPRType(ctx);
99 var state = op_specs.state(@This(), loc);
100 state.addOperands(&.{ lhs, rhs });
101 state.addTypes(&.{gpr_type});
102
103 const op = try builder.create(state);
104 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
105 return .{ .op = op };
106 }
107
108 pub fn getResult(self: *const AddOp) *ir.Value {
109 return self.op.getResult(0).?;
110 }
111
112 pub fn getLhs(self: AddOp) *ir.Value {
113 return self.op.operands.items[0].value;
114 }
115
116 pub fn getRhs(self: AddOp) *ir.Value {
117 return self.op.operands.items[1].value;
118 }
119
120 pub fn getSize(self: AddOp) GPRSize {
121 if (self.op.getAttr("size")) |attr| {
122 return getGPRSizeValue(attr) orelse .x;
123 }
124 return .x;
125 }
126 };
127
128 pub const AddImmOp = struct {
129 op: *ir.Operation,
130
131 pub const operation_spec = op_specs.define(.{
132 .mnemonic = "add_imm",
133 .required_attrs = &.{ "imm", "size" },
134 });
135 pub const operation_name = operation_spec.name;
136
137 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, imm: u12, size: GPRSize) !AddImmOp {
138 var builder = ir.OperationBuilder.init(ctx);
139 const gpr_type = try getGPRType(ctx);
140 var state = op_specs.state(@This(), loc);
141 state.addOperands(&.{src});
142 state.addTypes(&.{gpr_type});
143
144 const op = try builder.create(state);
145 try op.setAttr("imm", try getImm12Attr(ctx, imm));
146 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
147 return .{ .op = op };
148 }
149
150 pub fn getResult(self: *const AddImmOp) *ir.Value {
151 return self.op.getResult(0).?;
152 }
153
154 pub fn getSrc(self: AddImmOp) *ir.Value {
155 return self.op.operands.items[0].value;
156 }
157
158 pub fn getImm(self: AddImmOp) u12 {
159 if (self.op.getAttr("imm")) |attr| {
160 return getImm12Value(attr) orelse 0;
161 }
162 return 0;
163 }
164
165 pub fn getSize(self: AddImmOp) GPRSize {
166 if (self.op.getAttr("size")) |attr| {
167 return getGPRSizeValue(attr) orelse .x;
168 }
169 return .x;
170 }
171 };
172
173 pub const SubOp = struct {
174 op: *ir.Operation,
175
176 pub const operation_spec = op_specs.define(.{
177 .mnemonic = "sub",
178 .required_attrs = &.{"size"},
179 });
180 pub const operation_name = operation_spec.name;
181
182 pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value, size: GPRSize) !SubOp {
183 var builder = ir.OperationBuilder.init(ctx);
184 const gpr_type = try getGPRType(ctx);
185 var state = op_specs.state(@This(), loc);
186 state.addOperands(&.{ lhs, rhs });
187 state.addTypes(&.{gpr_type});
188
189 const op = try builder.create(state);
190 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
191 return .{ .op = op };
192 }
193
194 pub fn getResult(self: *const SubOp) *ir.Value {
195 return self.op.getResult(0).?;
196 }
197
198 pub fn getLhs(self: SubOp) *ir.Value {
199 return self.op.operands.items[0].value;
200 }
201
202 pub fn getRhs(self: SubOp) *ir.Value {
203 return self.op.operands.items[1].value;
204 }
205
206 pub fn getSize(self: SubOp) GPRSize {
207 if (self.op.getAttr("size")) |attr| {
208 return getGPRSizeValue(attr) orelse .x;
209 }
210 return .x;
211 }
212 };
213
214 pub const MulOp = struct {
215 op: *ir.Operation,
216
217 pub const operation_spec = op_specs.define(.{
218 .mnemonic = "mul",
219 .required_attrs = &.{"size"},
220 });
221 pub const operation_name = operation_spec.name;
222
223 pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value, size: GPRSize) !MulOp {
224 var builder = ir.OperationBuilder.init(ctx);
225 const gpr_type = try getGPRType(ctx);
226 var state = op_specs.state(@This(), loc);
227 state.addOperands(&.{ lhs, rhs });
228 state.addTypes(&.{gpr_type});
229
230 const op = try builder.create(state);
231 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
232 return .{ .op = op };
233 }
234
235 pub fn getResult(self: *const MulOp) *ir.Value {
236 return self.op.getResult(0).?;
237 }
238 };
239
240 pub const SDivOp = struct {
241 op: *ir.Operation,
242
243 pub const operation_spec = op_specs.define(.{
244 .mnemonic = "sdiv",
245 .required_attrs = &.{"size"},
246 });
247 pub const operation_name = operation_spec.name;
248
249 pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value, size: GPRSize) !SDivOp {
250 var builder = ir.OperationBuilder.init(ctx);
251 const gpr_type = try getGPRType(ctx);
252 var state = op_specs.state(@This(), loc);
253 state.addOperands(&.{ lhs, rhs });
254 state.addTypes(&.{gpr_type});
255
256 const op = try builder.create(state);
257 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
258 return .{ .op = op };
259 }
260
261 pub fn getResult(self: *const SDivOp) *ir.Value {
262 return self.op.getResult(0).?;
263 }
264 };
265
266 pub const UDivOp = struct {
267 op: *ir.Operation,
268
269 pub const operation_spec = op_specs.define(.{
270 .mnemonic = "udiv",
271 .required_attrs = &.{"size"},
272 });
273 pub const operation_name = operation_spec.name;
274
275 pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value, size: GPRSize) !UDivOp {
276 var builder = ir.OperationBuilder.init(ctx);
277 const gpr_type = try getGPRType(ctx);
278 var state = op_specs.state(@This(), loc);
279 state.addOperands(&.{ lhs, rhs });
280 state.addTypes(&.{gpr_type});
281
282 const op = try builder.create(state);
283 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
284 return .{ .op = op };
285 }
286
287 pub fn getResult(self: *const UDivOp) *ir.Value {
288 return self.op.getResult(0).?;
289 }
290 };
291
292 pub const CmpOp = struct {
293 op: *ir.Operation,
294
295 pub const operation_spec = op_specs.define(.{
296 .mnemonic = "cmp",
297 .required_attrs = &.{"size"},
298 });
299 pub const operation_name = operation_spec.name;
300
301 pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value, size: GPRSize) !CmpOp {
302 var builder = ir.OperationBuilder.init(ctx);
303 var state = op_specs.state(@This(), loc);
304 state.addOperands(&.{ lhs, rhs });
305
306 const op = try builder.create(state);
307 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
308 return .{ .op = op };
309 }
310 };
311
312 pub const CSetOp = struct {
313 op: *ir.Operation,
314
315 pub const operation_spec = op_specs.define(.{
316 .mnemonic = "cset",
317 .required_attrs = &.{ "cond", "size" },
318 });
319 pub const operation_name = operation_spec.name;
320
321 pub fn create(ctx: *ir.Context, loc: ir.Location, cond: Condition, size: GPRSize) !CSetOp {
322 var builder = ir.OperationBuilder.init(ctx);
323 const gpr_type = try getGPRType(ctx);
324 var state = op_specs.state(@This(), loc);
325 state.addTypes(&.{gpr_type});
326
327 const op = try builder.create(state);
328 try op.setAttr("cond", try getConditionAttr(ctx, cond));
329 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
330 return .{ .op = op };
331 }
332
333 pub fn getResult(self: *const CSetOp) *ir.Value {
334 return self.op.getResult(0).?;
335 }
336
337 pub fn getCondition(self: CSetOp) Condition {
338 if (self.op.getAttr("cond")) |attr| {
339 return getConditionValue(attr) orelse .al;
340 }
341 return .al;
342 }
343 };
344
345 pub const CSelOp = struct {
346 op: *ir.Operation,
347
348 pub const operation_spec = op_specs.define(.{
349 .mnemonic = "csel",
350 .required_attrs = &.{ "cond", "size" },
351 });
352 pub const operation_name = operation_spec.name;
353
354 pub fn create(ctx: *ir.Context, loc: ir.Location, rn: *ir.Value, rm: *ir.Value, cond: Condition, size: GPRSize) !CSelOp {
355 var builder = ir.OperationBuilder.init(ctx);
356 const gpr_type = try getGPRType(ctx);
357 var state = op_specs.state(@This(), loc);
358 state.addOperands(&.{ rn, rm });
359 state.addTypes(&.{gpr_type});
360
361 const op = try builder.create(state);
362 try op.setAttr("cond", try getConditionAttr(ctx, cond));
363 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
364 return .{ .op = op };
365 }
366
367 pub fn getResult(self: *const CSelOp) *ir.Value {
368 return self.op.getResult(0).?;
369 }
370
371 pub fn getRn(self: CSelOp) *ir.Value {
372 return self.op.operands.items[0].value;
373 }
374
375 pub fn getRm(self: CSelOp) *ir.Value {
376 return self.op.operands.items[1].value;
377 }
378
379 pub fn getCondition(self: CSelOp) Condition {
380 if (self.op.getAttr("cond")) |attr| {
381 return getConditionValue(attr) orelse .al;
382 }
383 return .al;
384 }
385 };
386
387 pub const FAddOp = struct {
388 op: *ir.Operation,
389
390 pub const operation_spec = op_specs.define(.{
391 .mnemonic = "fadd",
392 .required_attrs = &.{"ftype"},
393 });
394 pub const operation_name = operation_spec.name;
395
396 pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value, ftype: encoding.FP2Source.FPType) !FAddOp {
397 var builder = ir.OperationBuilder.init(ctx);
398 const fpr_type = try getFPRType(ctx);
399 var state = op_specs.state(@This(), loc);
400 state.addOperands(&.{ lhs, rhs });
401 state.addTypes(&.{fpr_type});
402
403 const op = try builder.create(state);
404 const type_payload = [_]u8{@backingInt(ftype)};
405 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
406 return .{ .op = op };
407 }
408
409 pub fn getResult(self: *const FAddOp) *ir.Value {
410 return self.op.getResult(0).?;
411 }
412 };
413
414 pub const FSubOp = struct {
415 op: *ir.Operation,
416
417 pub const operation_spec = op_specs.define(.{
418 .mnemonic = "fsub",
419 .required_attrs = &.{"ftype"},
420 });
421 pub const operation_name = operation_spec.name;
422
423 pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value, ftype: encoding.FP2Source.FPType) !FSubOp {
424 var builder = ir.OperationBuilder.init(ctx);
425 const fpr_type = try getFPRType(ctx);
426 var state = op_specs.state(@This(), loc);
427 state.addOperands(&.{ lhs, rhs });
428 state.addTypes(&.{fpr_type});
429
430 const op = try builder.create(state);
431 const type_payload = [_]u8{@backingInt(ftype)};
432 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
433 return .{ .op = op };
434 }
435
436 pub fn getResult(self: *const FSubOp) *ir.Value {
437 return self.op.getResult(0).?;
438 }
439 };
440
441 pub const FMulOp = struct {
442 op: *ir.Operation,
443
444 pub const operation_spec = op_specs.define(.{
445 .mnemonic = "fmul",
446 .required_attrs = &.{"ftype"},
447 });
448 pub const operation_name = operation_spec.name;
449
450 pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value, ftype: encoding.FP2Source.FPType) !FMulOp {
451 var builder = ir.OperationBuilder.init(ctx);
452 const fpr_type = try getFPRType(ctx);
453 var state = op_specs.state(@This(), loc);
454 state.addOperands(&.{ lhs, rhs });
455 state.addTypes(&.{fpr_type});
456
457 const op = try builder.create(state);
458 const type_payload = [_]u8{@backingInt(ftype)};
459 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
460 return .{ .op = op };
461 }
462
463 pub fn getResult(self: *const FMulOp) *ir.Value {
464 return self.op.getResult(0).?;
465 }
466 };
467
468 pub const FDivOp = struct {
469 op: *ir.Operation,
470
471 pub const operation_spec = op_specs.define(.{
472 .mnemonic = "fdiv",
473 .required_attrs = &.{"ftype"},
474 });
475 pub const operation_name = operation_spec.name;
476
477 pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value, ftype: encoding.FP2Source.FPType) !FDivOp {
478 var builder = ir.OperationBuilder.init(ctx);
479 const fpr_type = try getFPRType(ctx);
480 var state = op_specs.state(@This(), loc);
481 state.addOperands(&.{ lhs, rhs });
482 state.addTypes(&.{fpr_type});
483
484 const op = try builder.create(state);
485 const type_payload = [_]u8{@backingInt(ftype)};
486 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
487 return .{ .op = op };
488 }
489
490 pub fn getResult(self: *const FDivOp) *ir.Value {
491 return self.op.getResult(0).?;
492 }
493 };
494
495 pub const FCmpOp = struct {
496 op: *ir.Operation,
497
498 pub const operation_spec = op_specs.define(.{
499 .mnemonic = "fcmp",
500 .required_attrs = &.{"ftype"},
501 });
502 pub const operation_name = operation_spec.name;
503
504 pub fn create(ctx: *ir.Context, loc: ir.Location, lhs: *ir.Value, rhs: *ir.Value, ftype: encoding.FP2Source.FPType) !FCmpOp {
505 var builder = ir.OperationBuilder.init(ctx);
506 var state = op_specs.state(@This(), loc);
507 state.addOperands(&.{ lhs, rhs });
508
509 const op = try builder.create(state);
510 const type_payload = [_]u8{@backingInt(ftype)};
511 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
512 return .{ .op = op };
513 }
514 };
515
516 pub const FMovImmOp = struct {
517 op: *ir.Operation,
518
519 pub const operation_spec = op_specs.define(.{
520 .mnemonic = "fmov_imm",
521 .required_attrs = &.{ "ftype", "imm8" },
522 });
523 pub const operation_name = operation_spec.name;
524
525 pub fn create(ctx: *ir.Context, loc: ir.Location, imm8: u8, ftype: encoding.FP2Source.FPType) !FMovImmOp {
526 var builder = ir.OperationBuilder.init(ctx);
527 const fpr_type = try getFPRType(ctx);
528 var state = op_specs.state(@This(), loc);
529 state.addTypes(&.{fpr_type});
530
531 const op = try builder.create(state);
532 const imm_payload = [_]u8{imm8};
533 try op.setAttr("imm8", try ctx.getDialectAttr(attr_names.imm8, &imm_payload));
534 const type_payload = [_]u8{@backingInt(ftype)};
535 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
536 return .{ .op = op };
537 }
538
539 pub fn getResult(self: *const FMovImmOp) *ir.Value {
540 return self.op.getResult(0).?;
541 }
542
543 pub fn getImm8(self: *const FMovImmOp) u8 {
544 if (self.op.getAttrAs(ir.Attribute.DialectAttr, "imm8")) |dialect_attr| {
545 if (dialect_attr.payload.len > 0) {
546 return dialect_attr.payload[0];
547 }
548 }
549 return 0;
550 }
551 };
552
553 pub const FMovFromGPROp = struct {
554 op: *ir.Operation,
555
556 pub const operation_spec = op_specs.define(.{
557 .mnemonic = "fmov_gpr",
558 .required_attrs = &.{"ftype"},
559 });
560 pub const operation_name = operation_spec.name;
561
562 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, ftype: encoding.FP2Source.FPType) !FMovFromGPROp {
563 var builder = ir.OperationBuilder.init(ctx);
564 const fpr_type = try getFPRType(ctx);
565 var state = op_specs.state(@This(), loc);
566 state.addOperands(&.{src});
567 state.addTypes(&.{fpr_type});
568
569 const op = try builder.create(state);
570 const type_payload = [_]u8{@backingInt(ftype)};
571 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
572 return .{ .op = op };
573 }
574
575 pub fn getResult(self: *const FMovFromGPROp) *ir.Value {
576 return self.op.getResult(0).?;
577 }
578
579 pub fn getSrc(self: *const FMovFromGPROp) *ir.Value {
580 return self.op.getOperand(0).?;
581 }
582 };
583
584 pub const FMovFromFPROp = struct {
585 op: *ir.Operation,
586
587 pub const operation_spec = op_specs.define(.{
588 .mnemonic = "fmov_fpr",
589 .required_attrs = &.{"ftype"},
590 });
591 pub const operation_name = operation_spec.name;
592
593 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, ftype: encoding.FP2Source.FPType) !FMovFromFPROp {
594 var builder = ir.OperationBuilder.init(ctx);
595 const gpr_type = try getGPRType(ctx);
596 var state = op_specs.state(@This(), loc);
597 state.addOperands(&.{src});
598 state.addTypes(&.{gpr_type});
599
600 const op = try builder.create(state);
601 const type_payload = [_]u8{@backingInt(ftype)};
602 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
603 return .{ .op = op };
604 }
605
606 pub fn getResult(self: *const FMovFromFPROp) *ir.Value {
607 return self.op.getResult(0).?;
608 }
609
610 pub fn getSrc(self: *const FMovFromFPROp) *ir.Value {
611 return self.op.getOperand(0).?;
612 }
613 };
614
615 pub const FMovOp = struct {
616 op: *ir.Operation,
617
618 pub const operation_spec = op_specs.define(.{
619 .mnemonic = "fmov",
620 .required_attrs = &.{"ftype"},
621 });
622 pub const operation_name = operation_spec.name;
623
624 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, ftype: encoding.FP2Source.FPType) !FMovOp {
625 var builder = ir.OperationBuilder.init(ctx);
626 const fpr_type = try getFPRType(ctx);
627 var state = op_specs.state(@This(), loc);
628 state.addOperands(&.{src});
629 state.addTypes(&.{fpr_type});
630
631 const op = try builder.create(state);
632 const type_payload = [_]u8{@backingInt(ftype)};
633 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
634 return .{ .op = op };
635 }
636
637 pub fn getResult(self: *const FMovOp) *ir.Value {
638 return self.op.getResult(0).?;
639 }
640
641 pub fn getSrc(self: *const FMovOp) *ir.Value {
642 return self.op.getOperand(0).?;
643 }
644
645 pub fn getFType(self: FMovOp) encoding.FP2Source.FPType {
646 if (self.op.getAttrAs(ir.Attribute.DialectAttr, "ftype")) |dialect_attr| {
647 if (dialect_attr.payload.len > 0) {
648 return @fromBackingInt(@intCast(dialect_attr.payload[0]));
649 }
650 }
651 return .single;
652 }
653 };
654
655 pub const FSqrtOp = struct {
656 op: *ir.Operation,
657
658 pub const operation_spec = op_specs.define(.{
659 .mnemonic = "fsqrt",
660 .required_attrs = &.{"ftype"},
661 });
662 pub const operation_name = operation_spec.name;
663
664 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, ftype: encoding.FP2Source.FPType) !FSqrtOp {
665 var builder = ir.OperationBuilder.init(ctx);
666 const fpr_type = try getFPRType(ctx);
667 var state = op_specs.state(@This(), loc);
668 state.addOperands(&.{src});
669 state.addTypes(&.{fpr_type});
670
671 const op = try builder.create(state);
672 const type_payload = [_]u8{@backingInt(ftype)};
673 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
674 return .{ .op = op };
675 }
676
677 pub fn getResult(self: *const FSqrtOp) *ir.Value {
678 return self.op.getResult(0).?;
679 }
680
681 pub fn getSrc(self: *const FSqrtOp) *ir.Value {
682 return self.op.getOperand(0).?;
683 }
684 };
685
686 pub const FNegOp = struct {
687 op: *ir.Operation,
688
689 pub const operation_spec = op_specs.define(.{
690 .mnemonic = "fneg",
691 .required_attrs = &.{"ftype"},
692 });
693 pub const operation_name = operation_spec.name;
694
695 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, ftype: encoding.FP2Source.FPType) !FNegOp {
696 var builder = ir.OperationBuilder.init(ctx);
697 const fpr_type = try getFPRType(ctx);
698 var state = op_specs.state(@This(), loc);
699 state.addOperands(&.{src});
700 state.addTypes(&.{fpr_type});
701
702 const op = try builder.create(state);
703 const type_payload = [_]u8{@backingInt(ftype)};
704 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
705 return .{ .op = op };
706 }
707
708 pub fn getResult(self: *const FNegOp) *ir.Value {
709 return self.op.getResult(0).?;
710 }
711
712 pub fn getSrc(self: *const FNegOp) *ir.Value {
713 return self.op.getOperand(0).?;
714 }
715 };
716
717 pub const FAbsOp = struct {
718 op: *ir.Operation,
719
720 pub const operation_spec = op_specs.define(.{
721 .mnemonic = "fabs",
722 .required_attrs = &.{"ftype"},
723 });
724 pub const operation_name = operation_spec.name;
725
726 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, ftype: encoding.FP2Source.FPType) !FAbsOp {
727 var builder = ir.OperationBuilder.init(ctx);
728 const fpr_type = try getFPRType(ctx);
729 var state = op_specs.state(@This(), loc);
730 state.addOperands(&.{src});
731 state.addTypes(&.{fpr_type});
732
733 const op = try builder.create(state);
734 const type_payload = [_]u8{@backingInt(ftype)};
735 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
736 return .{ .op = op };
737 }
738
739 pub fn getResult(self: *const FAbsOp) *ir.Value {
740 return self.op.getResult(0).?;
741 }
742
743 pub fn getSrc(self: *const FAbsOp) *ir.Value {
744 return self.op.getOperand(0).?;
745 }
746 };
747
748 pub const LdrOp = struct {
749 op: *ir.Operation,
750
751 pub const operation_spec = op_specs.define(.{
752 .mnemonic = "ldr",
753 .required_attrs = &.{ "offset", "size" },
754 });
755 pub const operation_name = operation_spec.name;
756
757 pub fn create(ctx: *ir.Context, loc: ir.Location, base: *ir.Value, offset: u12, size: GPRSize) !LdrOp {
758 var builder = ir.OperationBuilder.init(ctx);
759 const gpr_type = try getGPRType(ctx);
760 var state = op_specs.state(@This(), loc);
761 state.addOperands(&.{base});
762 state.addTypes(&.{gpr_type});
763
764 const op = try builder.create(state);
765 try op.setAttr("offset", try getImm12Attr(ctx, offset));
766 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
767 return .{ .op = op };
768 }
769
770 pub fn getResult(self: *const LdrOp) *ir.Value {
771 return self.op.getResult(0).?;
772 }
773
774 pub fn getBase(self: LdrOp) *ir.Value {
775 return self.op.operands.items[0].value;
776 }
777
778 pub fn getOffset(self: LdrOp) u12 {
779 if (self.op.getAttr("offset")) |attr| {
780 return getImm12Value(attr) orelse 0;
781 }
782 return 0;
783 }
784 };
785
786 pub const StrOp = struct {
787 op: *ir.Operation,
788
789 pub const operation_spec = op_specs.define(.{
790 .mnemonic = "str",
791 .required_attrs = &.{ "offset", "size" },
792 });
793 pub const operation_name = operation_spec.name;
794
795 pub fn create(ctx: *ir.Context, loc: ir.Location, value: *ir.Value, base: *ir.Value, offset: u12, size: GPRSize) !StrOp {
796 var builder = ir.OperationBuilder.init(ctx);
797 var state = op_specs.state(@This(), loc);
798 state.addOperands(&.{ value, base });
799
800 const op = try builder.create(state);
801 try op.setAttr("offset", try getImm12Attr(ctx, offset));
802 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
803 return .{ .op = op };
804 }
805
806 pub fn getValue(self: StrOp) *ir.Value {
807 return self.op.operands.items[0].value;
808 }
809
810 pub fn getBase(self: StrOp) *ir.Value {
811 return self.op.operands.items[1].value;
812 }
813
814 pub fn getOffset(self: StrOp) u12 {
815 if (self.op.getAttr("offset")) |attr| {
816 return getImm12Value(attr) orelse 0;
817 }
818 return 0;
819 }
820 };
821
822 pub const BOp = struct {
823 op: *ir.Operation,
824
825 pub const operation_spec = op_specs.define(.{ .mnemonic = "b" });
826 pub const operation_name = operation_spec.name;
827
828 pub fn create(ctx: *ir.Context, loc: ir.Location, target: *ir.Block) !BOp {
829 var builder = ir.OperationBuilder.init(ctx);
830 var state = op_specs.state(@This(), loc);
831 state.addSuccessors(&.{target});
832
833 const op = try builder.create(state);
834 return .{ .op = op };
835 }
836
837 pub fn getTarget(self: BOp) *ir.Block {
838 return self.op.getSuccessor(0).?;
839 }
840 };
841
842 pub const BCondOp = struct {
843 op: *ir.Operation,
844
845 pub const operation_spec = op_specs.define(.{
846 .mnemonic = "b_cond",
847 .required_attrs = &.{"cond"},
848 });
849 pub const operation_name = operation_spec.name;
850
851 pub fn create(ctx: *ir.Context, loc: ir.Location, cond: Condition, true_target: *ir.Block, false_target: *ir.Block) !BCondOp {
852 var builder = ir.OperationBuilder.init(ctx);
853 var state = op_specs.state(@This(), loc);
854 state.addSuccessors(&.{ true_target, false_target });
855
856 const op = try builder.create(state);
857 try op.setAttr("cond", try getConditionAttr(ctx, cond));
858 return .{ .op = op };
859 }
860
861 pub fn getCondition(self: BCondOp) Condition {
862 if (self.op.getAttr("cond")) |attr| {
863 return getConditionValue(attr) orelse .al;
864 }
865 return .al;
866 }
867
868 pub fn getTrueTarget(self: BCondOp) *ir.Block {
869 return self.op.getSuccessor(0).?;
870 }
871
872 pub fn getFalseTarget(self: BCondOp) *ir.Block {
873 return self.op.getSuccessor(1).?;
874 }
875 };
876
877 pub const RetOp = struct {
878 op: *ir.Operation,
879
880 pub const operation_spec = op_specs.define(.{ .mnemonic = "ret" });
881 pub const operation_name = operation_spec.name;
882
883 pub fn create(ctx: *ir.Context, loc: ir.Location) !RetOp {
884 var builder = ir.OperationBuilder.init(ctx);
885 const state = op_specs.state(@This(), loc);
886
887 const op = try builder.create(state);
888 return .{ .op = op };
889 }
890 };
891
892 pub const BlOp = struct {
893 op: *ir.Operation,
894
895 pub const operation_spec = op_specs.define(.{
896 .mnemonic = "bl",
897 .required_attrs = &.{"callee"},
898 });
899 pub const operation_name = operation_spec.name;
900
901 pub fn create(ctx: *ir.Context, loc: ir.Location, callee: []const u8, args: []const *ir.Value, result_types: []const ir.Type) !BlOp {
902 var builder = ir.OperationBuilder.init(ctx);
903 var state = op_specs.state(@This(), loc);
904 state.addOperands(args);
905 state.addTypes(result_types);
906
907 const op = try builder.create(state);
908 const name_attr = try ctx.getDialectAttr("choir.string", callee);
909 try op.setAttr("callee", name_attr);
910 return .{ .op = op };
911 }
912 };
913
914 pub const PrologueOp = struct {
915 op: *ir.Operation,
916
917 pub const operation_spec = op_specs.define(.{
918 .mnemonic = "prologue",
919 .required_attrs = &.{"frame_size"},
920 });
921 pub const operation_name = operation_spec.name;
922
923 pub fn create(ctx: *ir.Context, loc: ir.Location, frame_size: i32) !PrologueOp {
924 try loadSpec(ctx);
925 var builder = ir.OperationBuilder.init(ctx);
926 const state = op_specs.state(@This(), loc);
927
928 const op = try builder.create(state);
929 try op.setAttr("frame_size", try getFrameOffsetAttr(ctx, frame_size));
930 return .{ .op = op };
931 }
932
933 pub fn getFrameSize(self: PrologueOp) i32 {
934 if (self.op.getAttr("frame_size")) |attr| {
935 return getFrameOffsetValue(attr) orelse 0;
936 }
937 return 0;
938 }
939 };
940
941 pub const EpilogueOp = struct {
942 op: *ir.Operation,
943
944 pub const operation_spec = op_specs.define(.{
945 .mnemonic = "epilogue",
946 .required_attrs = &.{"frame_size"},
947 });
948 pub const operation_name = operation_spec.name;
949
950 pub fn create(ctx: *ir.Context, loc: ir.Location, frame_size: i32) !EpilogueOp {
951 var builder = ir.OperationBuilder.init(ctx);
952 const state = op_specs.state(@This(), loc);
953
954 const op = try builder.create(state);
955 try op.setAttr("frame_size", try getFrameOffsetAttr(ctx, frame_size));
956 return .{ .op = op };
957 }
958
959 pub fn getFrameSize(self: EpilogueOp) i32 {
960 if (self.op.getAttr("frame_size")) |attr| {
961 return getFrameOffsetValue(attr) orelse 0;
962 }
963 return 0;
964 }
965 };
966
967 pub const MovOp = struct {
968 op: *ir.Operation,
969
970 pub const operation_spec = op_specs.define(.{
971 .mnemonic = "mov",
972 .required_attrs = &.{"size"},
973 });
974 pub const operation_name = operation_spec.name;
975
976 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, size: GPRSize) !MovOp {
977 var builder = ir.OperationBuilder.init(ctx);
978 const gpr_type = try getGPRType(ctx);
979 var state = op_specs.state(@This(), loc);
980 state.addOperands(&.{src});
981 state.addTypes(&.{gpr_type});
982
983 const op = try builder.create(state);
984 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
985 return .{ .op = op };
986 }
987
988 pub fn getResult(self: *const MovOp) *ir.Value {
989 return self.op.getResult(0).?;
990 }
991
992 pub fn getSrc(self: MovOp) *ir.Value {
993 return self.op.operands.items[0].value;
994 }
995 };
996
997 pub const MovzOp = struct {
998 op: *ir.Operation,
999
1000 pub const operation_spec = op_specs.define(.{
1001 .mnemonic = "movz",
1002 .required_attrs = &.{ "imm", "shift", "size" },
1003 });
1004 pub const operation_name = operation_spec.name;
1005
1006 pub fn create(ctx: *ir.Context, loc: ir.Location, imm: u16, shift: u2, size: GPRSize) !MovzOp {
1007 var builder = ir.OperationBuilder.init(ctx);
1008 const gpr_type = try getGPRType(ctx);
1009 var state = op_specs.state(@This(), loc);
1010 state.addTypes(&.{gpr_type});
1011
1012 const op = try builder.create(state);
1013 var buf: [8]u8 = undefined;
1014 const imm_payload = try ir.format.intPayload(buf[0..], imm);
1015 try op.setAttr("imm", try ctx.getDialectAttr(attr_names.imm16, imm_payload));
1016 const shift_payload = [_]u8{shift};
1017 try op.setAttr("shift", try ctx.getDialectAttr("aarch64.shift", &shift_payload));
1018 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
1019 return .{ .op = op };
1020 }
1021
1022 pub fn getResult(self: *const MovzOp) *ir.Value {
1023 return self.op.getResult(0).?;
1024 }
1025 };
1026
1027 pub const MovnOp = struct {
1028 op: *ir.Operation,
1029
1030 pub const operation_spec = op_specs.define(.{
1031 .mnemonic = "movn",
1032 .required_attrs = &.{ "imm", "shift", "size" },
1033 });
1034 pub const operation_name = operation_spec.name;
1035
1036 pub fn create(ctx: *ir.Context, loc: ir.Location, imm: u16, shift: u2, size: GPRSize) !MovnOp {
1037 var builder = ir.OperationBuilder.init(ctx);
1038 const gpr_type = try getGPRType(ctx);
1039 var state = op_specs.state(@This(), loc);
1040 state.addTypes(&.{gpr_type});
1041
1042 const op = try builder.create(state);
1043 var buf: [8]u8 = undefined;
1044 const imm_payload = try ir.format.intPayload(buf[0..], imm);
1045 try op.setAttr("imm", try ctx.getDialectAttr(attr_names.imm16, imm_payload));
1046 const shift_payload = [_]u8{shift};
1047 try op.setAttr("shift", try ctx.getDialectAttr("aarch64.shift", &shift_payload));
1048 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
1049 return .{ .op = op };
1050 }
1051
1052 pub fn getResult(self: *const MovnOp) *ir.Value {
1053 return self.op.getResult(0).?;
1054 }
1055 };
1056
1057 pub const MovkOp = struct {
1058 op: *ir.Operation,
1059
1060 pub const operation_spec = op_specs.define(.{
1061 .mnemonic = "movk",
1062 .required_attrs = &.{ "imm", "shift", "size" },
1063 });
1064 pub const operation_name = operation_spec.name;
1065
1066 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, imm: u16, shift: u2, size: GPRSize) !MovkOp {
1067 var builder = ir.OperationBuilder.init(ctx);
1068 const gpr_type = try getGPRType(ctx);
1069 var state = op_specs.state(@This(), loc);
1070 state.addOperands(&.{src});
1071 state.addTypes(&.{gpr_type});
1072
1073 const op = try builder.create(state);
1074 var buf: [8]u8 = undefined;
1075 const imm_payload = try ir.format.intPayload(buf[0..], imm);
1076 try op.setAttr("imm", try ctx.getDialectAttr(attr_names.imm16, imm_payload));
1077 const shift_payload = [_]u8{shift};
1078 try op.setAttr("shift", try ctx.getDialectAttr("aarch64.shift", &shift_payload));
1079 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
1080 return .{ .op = op };
1081 }
1082
1083 pub fn getResult(self: *const MovkOp) *ir.Value {
1084 return self.op.getResult(0).?;
1085 }
1086
1087 pub fn getSrc(self: *const MovkOp) *ir.Value {
1088 return self.op.getOperand(0).?;
1089 }
1090 };
1091
1092 pub const CopyToRegOp = struct {
1093 op: *ir.Operation,
1094
1095 pub const operation_spec = op_specs.define(.{
1096 .mnemonic = "copy_to_reg",
1097 .required_attrs = &.{ "dest_reg", "size" },
1098 });
1099 pub const operation_name = operation_spec.name;
1100
1101 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, dest_reg: u8, size: GPRSize) !CopyToRegOp {
1102 var builder = ir.OperationBuilder.init(ctx);
1103 const gpr_type = try getGPRType(ctx);
1104 var state = op_specs.state(@This(), loc);
1105 state.addOperands(&.{src});
1106 state.addTypes(&.{gpr_type});
1107
1108 const op = try builder.create(state);
1109 const reg_payload = [_]u8{dest_reg};
1110 try op.setAttr("dest_reg", try ctx.getDialectAttr("aarch64.reg_num", ®_payload));
1111 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
1112 return .{ .op = op };
1113 }
1114
1115 pub fn getResult(self: *const CopyToRegOp) *ir.Value {
1116 return self.op.getResult(0).?;
1117 }
1118
1119 pub fn getSrc(self: CopyToRegOp) *ir.Value {
1120 return self.op.operands.items[0].value;
1121 }
1122
1123 pub fn getDestReg(self: CopyToRegOp) u8 {
1124 if (self.op.getAttrAs(ir.Attribute.DialectAttr, "dest_reg")) |dialect_attr| {
1125 if (dialect_attr.payload.len > 0) {
1126 return dialect_attr.payload[0];
1127 }
1128 }
1129 return 0;
1130 }
1131
1132 pub fn getSize(self: CopyToRegOp) GPRSize {
1133 if (self.op.getAttr("size")) |attr| {
1134 return getGPRSizeValue(attr) orelse .x;
1135 }
1136 return .x;
1137 }
1138 };
1139
1140 pub const CopyToSameRegAsOp = struct {
1141 op: *ir.Operation,
1142
1143 pub const operation_spec = op_specs.define(.{
1144 .mnemonic = "copy_to_same_reg",
1145 .required_attrs = &.{"size"},
1146 });
1147 pub const operation_name = operation_spec.name;
1148
1149 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, target_ref: *ir.Value, size: GPRSize) !CopyToSameRegAsOp {
1150 var builder = ir.OperationBuilder.init(ctx);
1151 const gpr_type = try getGPRType(ctx);
1152 var state = op_specs.state(@This(), loc);
1153 state.addOperands(&.{ src, target_ref });
1154 state.addTypes(&.{gpr_type});
1155
1156 const op = try builder.create(state);
1157 try op.setAttr("size", try getGPRSizeAttr(ctx, size));
1158 return .{ .op = op };
1159 }
1160
1161 pub fn getResult(self: *const CopyToSameRegAsOp) *ir.Value {
1162 return self.op.getResult(0).?;
1163 }
1164
1165 pub fn getSrc(self: CopyToSameRegAsOp) *ir.Value {
1166 return self.op.operands.items[0].value;
1167 }
1168
1169 pub fn getTargetRef(self: CopyToSameRegAsOp) *ir.Value {
1170 return self.op.operands.items[1].value;
1171 }
1172
1173 pub fn getSize(self: CopyToSameRegAsOp) GPRSize {
1174 if (self.op.getAttr("size")) |attr| {
1175 return getGPRSizeValue(attr) orelse .x;
1176 }
1177 return .x;
1178 }
1179 };
1180
1181 pub const CopyToSameFPRegAsOp = struct {
1182 op: *ir.Operation,
1183
1184 pub const operation_spec = op_specs.define(.{
1185 .mnemonic = "copy_to_same_fpreg",
1186 .required_attrs = &.{"ftype"},
1187 });
1188 pub const operation_name = operation_spec.name;
1189
1190 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, target_ref: *ir.Value, ftype: encoding.FP2Source.FPType) !CopyToSameFPRegAsOp {
1191 var builder = ir.OperationBuilder.init(ctx);
1192 const fpr_type = try getFPRType(ctx);
1193 var state = op_specs.state(@This(), loc);
1194 state.addOperands(&.{ src, target_ref });
1195 state.addTypes(&.{fpr_type});
1196
1197 const op = try builder.create(state);
1198 const type_payload = [_]u8{@backingInt(ftype)};
1199 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
1200 return .{ .op = op };
1201 }
1202
1203 pub fn getResult(self: *const CopyToSameFPRegAsOp) *ir.Value {
1204 return self.op.getResult(0).?;
1205 }
1206
1207 pub fn getSrc(self: CopyToSameFPRegAsOp) *ir.Value {
1208 return self.op.operands.items[0].value;
1209 }
1210
1211 pub fn getTargetRef(self: CopyToSameFPRegAsOp) *ir.Value {
1212 return self.op.operands.items[1].value;
1213 }
1214
1215 pub fn getFType(self: CopyToSameFPRegAsOp) encoding.FP2Source.FPType {
1216 if (self.op.getAttrAs(ir.Attribute.DialectAttr, "ftype")) |dialect_attr| {
1217 if (dialect_attr.payload.len > 0) {
1218 return @fromBackingInt(@intCast(dialect_attr.payload[0]));
1219 }
1220 }
1221 return .single;
1222 }
1223 };
1224
1225 pub const CopyToFPRegOp = struct {
1226 op: *ir.Operation,
1227
1228 pub const operation_spec = op_specs.define(.{
1229 .mnemonic = "copy_to_fpreg",
1230 .required_attrs = &.{ "dest_reg", "ftype" },
1231 });
1232 pub const operation_name = operation_spec.name;
1233
1234 pub fn create(ctx: *ir.Context, loc: ir.Location, src: *ir.Value, dest_reg: u8, ftype: encoding.FP2Source.FPType) !CopyToFPRegOp {
1235 var builder = ir.OperationBuilder.init(ctx);
1236 const fpr_type = try getFPRType(ctx);
1237 var state = op_specs.state(@This(), loc);
1238 state.addOperands(&.{src});
1239 state.addTypes(&.{fpr_type});
1240
1241 const op = try builder.create(state);
1242 const reg_payload = [_]u8{dest_reg};
1243 try op.setAttr("dest_reg", try ctx.getDialectAttr("aarch64.fpreg_num", ®_payload));
1244 const type_payload = [_]u8{@backingInt(ftype)};
1245 try op.setAttr("ftype", try ctx.getDialectAttr(attr_names.fp_type, &type_payload));
1246 return .{ .op = op };
1247 }
1248
1249 pub fn getResult(self: *const CopyToFPRegOp) *ir.Value {
1250 return self.op.getResult(0).?;
1251 }
1252
1253 pub fn getSrc(self: CopyToFPRegOp) *ir.Value {
1254 return self.op.operands.items[0].value;
1255 }
1256
1257 pub fn getDestReg(self: CopyToFPRegOp) u8 {
1258 if (self.op.getAttrAs(ir.Attribute.DialectAttr, "dest_reg")) |dialect_attr| {
1259 if (dialect_attr.payload.len > 0) {
1260 return dialect_attr.payload[0];
1261 }
1262 }
1263 return 0;
1264 }
1265
1266 pub fn getFType(self: CopyToFPRegOp) encoding.FP2Source.FPType {
1267 if (self.op.getAttrAs(ir.Attribute.DialectAttr, "ftype")) |dialect_attr| {
1268 if (dialect_attr.payload.len > 0) {
1269 return @fromBackingInt(@intCast(dialect_attr.payload[0]));
1270 }
1271 }
1272 return .single;
1273 }
1274 };
1275
1276 fn loadSpec(ctx: *ir.Context) !void {
1277 try ir.dialects.loadDialectSpec(ctx, spec);
1278 }
1279
1280 pub fn getGPRType(ctx: *ir.Context) !ir.Type {
1281 try loadSpec(ctx);
1282 return ctx.getDialectTypeFromName(type_names.gpr);
1283 }
1284
1285 pub fn getFPRType(ctx: *ir.Context) !ir.Type {
1286 try loadSpec(ctx);
1287 return ctx.getDialectTypeFromName(type_names.fpr);
1288 }
1289
1290 pub fn getGPRAttr(ctx: *ir.Context, reg: GPR) !ir.Attribute {
1291 var buf: [4]u8 = undefined;
1292 const payload = try ir.format.intPayload(buf[0..], @backingInt(reg));
1293 return ctx.getDialectAttr(attr_names.gpr_reg, payload);
1294 }
1295
1296 pub fn getGPRValue(attr: ir.Attribute) ?GPR {
1297 if (!std.mem.eql(u8, attr.abstract.name, attr_names.gpr_reg)) return null;
1298 const dialect_attr = attr.cast(ir.Attribute.DialectAttr) orelse return null;
1299 const val = std.fmt.parseInt(u5, dialect_attr.payload, 10) catch return null;
1300 return @fromBackingInt(@intCast(val));
1301 }
1302
1303 pub fn getFPRAttr(ctx: *ir.Context, reg: FPR) !ir.Attribute {
1304 var buf: [4]u8 = undefined;
1305 const payload = try ir.format.intPayload(buf[0..], @backingInt(reg));
1306 return ctx.getDialectAttr(attr_names.fpr_reg, payload);
1307 }
1308
1309 pub fn getFPRValue(attr: ir.Attribute) ?FPR {
1310 if (!std.mem.eql(u8, attr.abstract.name, attr_names.fpr_reg)) return null;
1311 const dialect_attr = attr.cast(ir.Attribute.DialectAttr) orelse return null;
1312 const val = std.fmt.parseInt(u5, dialect_attr.payload, 10) catch return null;
1313 return @fromBackingInt(@intCast(val));
1314 }
1315
1316 pub fn getImm12Attr(ctx: *ir.Context, value: u12) !ir.Attribute {
1317 var buf: [8]u8 = undefined;
1318 const payload = try ir.format.intPayload(buf[0..], value);
1319 return ctx.getDialectAttr(attr_names.imm12, payload);
1320 }
1321
1322 pub fn getImm12Value(attr: ir.Attribute) ?u12 {
1323 if (!std.mem.eql(u8, attr.abstract.name, attr_names.imm12)) return null;
1324 const dialect_attr = attr.cast(ir.Attribute.DialectAttr) orelse return null;
1325 return std.fmt.parseInt(u12, dialect_attr.payload, 10) catch null;
1326 }
1327
1328 pub fn getConditionAttr(ctx: *ir.Context, cond: Condition) !ir.Attribute {
1329 var buf: [4]u8 = undefined;
1330 const payload = try ir.format.intPayload(buf[0..], @backingInt(cond));
1331 return ctx.getDialectAttr(attr_names.condition, payload);
1332 }
1333
1334 pub fn getConditionValue(attr: ir.Attribute) ?Condition {
1335 if (!std.mem.eql(u8, attr.abstract.name, attr_names.condition)) return null;
1336 const dialect_attr = attr.cast(ir.Attribute.DialectAttr) orelse return null;
1337 const val = std.fmt.parseInt(u4, dialect_attr.payload, 10) catch return null;
1338 return @fromBackingInt(@intCast(val));
1339 }
1340
1341 pub fn getGPRSizeAttr(ctx: *ir.Context, size: GPRSize) !ir.Attribute {
1342 const payload = [_]u8{@backingInt(size)};
1343 return ctx.getDialectAttr(attr_names.gpr_size, &payload);
1344 }
1345
1346 pub fn getGPRSizeValue(attr: ir.Attribute) ?GPRSize {
1347 if (!std.mem.eql(u8, attr.abstract.name, attr_names.gpr_size)) return null;
1348 const dialect_attr = attr.cast(ir.Attribute.DialectAttr) orelse return null;
1349 if (dialect_attr.payload.len != 1) return null;
1350 return @fromBackingInt(@intCast(dialect_attr.payload[0]));
1351 }
1352
1353 pub fn getFrameOffsetAttr(ctx: *ir.Context, offset: i32) !ir.Attribute {
1354 var buf: [16]u8 = undefined;
1355 const payload = try ir.format.intPayload(buf[0..], offset);
1356 return ctx.getDialectAttr(attr_names.frame_offset, payload);
1357 }
1358
1359 pub fn getFrameOffsetValue(attr: ir.Attribute) ?i32 {
1360 if (!std.mem.eql(u8, attr.abstract.name, attr_names.frame_offset)) return null;
1361 const dialect_attr = attr.cast(ir.Attribute.DialectAttr) orelse return null;
1362 return std.fmt.parseInt(i32, dialect_attr.payload, 10) catch null;
1363 }
1364 };
1365
1366 test "AArch64Dialect.AddOp creates addition" {
1367 const testing = std.testing;
1368 var arena = alloc_arena.Arena.init(std.testing.allocator);
1369 defer arena.deinit();
1370 const allocator = arena.allocator();
1371
1372 var ctx = try ir.Context.init(allocator, ir.Context.Limits.testing);
1373 defer ctx.deinit(allocator);
1374
1375 const loc = ir.Location.getUnknown();
1376
1377 var r1 = try AArch64Dialect.GetRegOp.createGPR(&ctx, loc, .x1);
1378 var r2 = try AArch64Dialect.GetRegOp.createGPR(&ctx, loc, .x2);
1379
1380 var add = try AArch64Dialect.AddOp.create(&ctx, loc, r1.getResult(), r2.getResult(), .x);
1381
1382 try testing.expectEqualStrings("aarch64.add", add.op.name.name);
1383 try testing.expectEqual(GPRSize.x, add.getSize());
1384 }
1385
1386 test "AArch64Dialect.GetRegOp materializes register" {
1387 const testing = std.testing;
1388 var arena = alloc_arena.Arena.init(std.testing.allocator);
1389 defer arena.deinit();
1390 const allocator = arena.allocator();
1391
1392 var ctx = try ir.Context.init(allocator, ir.Context.Limits.testing);
1393 defer ctx.deinit(allocator);
1394
1395 const loc = ir.Location.getUnknown();
1396
1397 var get_reg = try AArch64Dialect.GetRegOp.createGPR(&ctx, loc, .x19);
1398
1399 try testing.expectEqualStrings("aarch64.get_reg", get_reg.op.name.name);
1400 try testing.expectEqual(GPR.x19, get_reg.getGPR().?);
1401 }
1402
1403 test "AArch64Dialect registers inherent attribute names" {
1404 const testing = std.testing;
1405 var arena = alloc_arena.Arena.init(std.testing.allocator);
1406 defer arena.deinit();
1407 const allocator = arena.allocator();
1408
1409 var ctx = try ir.Context.init(allocator, ir.Context.Limits.testing);
1410 defer ctx.deinit(allocator);
1411 try ir.dialects.loadDialectSpec(&ctx, AArch64Dialect.spec);
1412
1413 const add_info = ctx.lookupOperation(AArch64Dialect.AddOp.operation_name) orelse
1414 return error.TestExpectedOperationInfo;
1415 try testing.expect(add_info.hasInherentAttributeName("size"));
1416
1417 const movk_info = ctx.lookupOperation(AArch64Dialect.MovkOp.operation_name) orelse
1418 return error.TestExpectedOperationInfo;
1419 try testing.expect(movk_info.hasInherentAttributeName("imm"));
1420 try testing.expect(movk_info.hasInherentAttributeName("shift"));
1421 try testing.expect(movk_info.hasInherentAttributeName("size"));
1422
1423 const branch_info = ctx.lookupOperation(AArch64Dialect.BlOp.operation_name) orelse
1424 return error.TestExpectedOperationInfo;
1425 try testing.expect(branch_info.hasInherentAttributeName("callee"));
1426
1427 const loc = ir.Location.getUnknown();
1428 var r1 = try AArch64Dialect.GetRegOp.createGPR(&ctx, loc, .x1);
1429 var r2 = try AArch64Dialect.GetRegOp.createGPR(&ctx, loc, .x2);
1430 const add = try AArch64Dialect.AddOp.create(&ctx, loc, r1.getResult(), r2.getResult(), .x);
1431
1432 try testing.expect(!add.op.isDiscardableAttrName("size"));
1433 try testing.expectEqual(@as(usize, 0), add.op.countDiscardableAttrs());
1434 }
1435
1436 test "AArch64Dialect.PrologueOp stores frame size" {
1437 const testing = std.testing;
1438 var arena = alloc_arena.Arena.init(std.testing.allocator);
1439 defer arena.deinit();
1440 const allocator = arena.allocator();
1441
1442 var ctx = try ir.Context.init(allocator, ir.Context.Limits.testing);
1443 defer ctx.deinit(allocator);
1444
1445 const loc = ir.Location.getUnknown();
1446
1447 var prologue = try AArch64Dialect.PrologueOp.create(&ctx, loc, 32);
1448
1449 try testing.expectEqual(@as(i32, 32), prologue.getFrameSize());
1450 }
1451
1452 test "AArch64Dialect attribute helpers round trip" {
1453 const testing = std.testing;
1454 var arena = alloc_arena.Arena.init(std.testing.allocator);
1455 defer arena.deinit();
1456 const allocator = arena.allocator();
1457
1458 var ctx = try ir.Context.init(allocator, ir.Context.Limits.testing);
1459 defer ctx.deinit(allocator);
1460
1461 const gpr_attr = try AArch64Dialect.getGPRAttr(&ctx, .x7);
1462 try testing.expectEqual(GPR.x7, AArch64Dialect.getGPRValue(gpr_attr).?);
1463
1464 const fpr_attr = try AArch64Dialect.getFPRAttr(&ctx, .v3);
1465 try testing.expectEqual(FPR.v3, AArch64Dialect.getFPRValue(fpr_attr).?);
1466
1467 const imm_attr = try AArch64Dialect.getImm12Attr(&ctx, 4095);
1468 try testing.expectEqual(@as(u12, 4095), AArch64Dialect.getImm12Value(imm_attr).?);
1469
1470 const cond_attr = try AArch64Dialect.getConditionAttr(&ctx, .ne);
1471 try testing.expectEqual(Condition.ne, AArch64Dialect.getConditionValue(cond_attr).?);
1472 }