1
2
3
4
5 package asmgen
6
7 import (
8 "bytes"
9 "cmp"
10 "fmt"
11 "math/bits"
12 "slices"
13 "strings"
14 )
15
16
17
18
19
20
21 type Asm struct {
22 Arch *Arch
23 out bytes.Buffer
24 regavail uint64
25 enabled map[Option]bool
26 }
27
28
29
30 func NewAsm(arch *Arch) *Asm {
31 a := &Asm{Arch: arch, enabled: make(map[Option]bool)}
32 buildTag := ""
33 if arch.Build != "" {
34 buildTag = " && (" + arch.Build + ")"
35 }
36 a.Printf(asmHeader, buildTag)
37 return a
38 }
39
40
41
42
43
44
45
46 var asmHeader = `// Copyright 2025 The Go Authors. All rights reserved.
47 // Use of this source code is governed by a BSD-style
48 // license that can be found in the LICENSE file.
49
50 // Code generated by 'go generate' (with ./internal/asmgen). DO NOT EDIT.
51
52 //go:build !math_big_pure_go%s
53
54 #include "textflag.h"
55 `
56
57
58
59
60 func (a *Asm) Fatalf(format string, args ...any) {
61 text := a.out.String()
62 i := strings.LastIndex(text, "\nTEXT")
63 text = text[i+1:]
64 panic("[" + a.Arch.Name + "] asmgen internal error: " + fmt.Sprintf(format, args...) + "\n" + text)
65 }
66
67
68 func (a *Asm) hint(h Hint) string {
69 if h == HintCarry && a.Arch.regCarry != "" {
70 return a.Arch.regCarry
71 }
72 if h == HintAltCarry && a.Arch.regAltCarry != "" {
73 return a.Arch.regAltCarry
74 }
75 if h == HintNone || a.Arch.hint == nil {
76 return ""
77 }
78 return a.Arch.hint(a, h)
79 }
80
81
82
83 func (a *Asm) ZR() Reg {
84 return Reg{a.Arch.reg0}
85 }
86
87
88
89
90
91
92
93
94
95
96 func (a *Asm) tmp() Reg {
97 return Reg{a.Arch.regTmp}
98 }
99
100
101 func (a *Asm) Carry() Reg {
102 return Reg{a.Arch.regCarry}
103 }
104
105
106 func (a *Asm) AltCarry() Reg {
107 return Reg{a.Arch.regAltCarry}
108 }
109
110
111 func (a *Asm) Imm(x int) Reg {
112 if x == 0 && a.Arch.reg0 != "" {
113 return Reg{a.Arch.reg0}
114 }
115 return Reg{fmt.Sprintf("$%d", x)}
116 }
117
118
119 func (a *Asm) IsZero(r Reg) bool {
120 return r.name == "$0" || a.Arch.reg0 != "" && r.name == a.Arch.reg0
121 }
122
123
124 func (a *Asm) Reg() Reg {
125 i := bits.TrailingZeros64(a.regavail)
126 if i == 64 {
127 a.Fatalf("out of registers")
128 }
129 a.regavail ^= 1 << i
130 return Reg{a.Arch.regs[i]}
131 }
132
133
134 func (a *Asm) RegHint(hint Hint) Reg {
135 if name := a.hint(hint); name != "" {
136 i := slices.Index(a.Arch.regs, name)
137 if i < 0 {
138 return Reg{name}
139 }
140 if a.regavail&(1<<i) == 0 {
141 a.Fatalf("hint for already allocated register %s", name)
142 }
143 a.regavail &^= 1 << i
144 return Reg{name}
145 }
146 return a.Reg()
147 }
148
149
150
151 func (a *Asm) Free(r Reg) {
152 i := slices.Index(a.Arch.regs, r.name)
153 if i < 0 {
154 return
155 }
156 if a.regavail&(1<<i) != 0 {
157 a.Fatalf("register %s already freed", r.name)
158 }
159 a.regavail |= 1 << i
160 }
161
162
163
164
165
166
167 func (a *Asm) Unfree(r Reg) {
168 i := slices.Index(a.Arch.regs, r.name)
169 if i < 0 {
170 return
171 }
172 if a.regavail&(1<<i) == 0 {
173 a.Fatalf("register %s not free", r.name)
174 }
175 a.regavail &^= 1 << i
176 }
177
178
179 type RegsUsed struct {
180 avail uint64
181 }
182
183
184
185 func (a *Asm) RegsUsed() RegsUsed {
186 return RegsUsed{a.regavail}
187 }
188
189
190
191
192 func (a *Asm) SetRegsUsed(used RegsUsed) {
193 a.regavail = used.avail
194 }
195
196
197 func (a *Asm) FreeAll() {
198 a.regavail = 1<<len(a.Arch.regs) - 1
199 }
200
201
202 func (a *Asm) Printf(format string, args ...any) {
203 text := fmt.Sprintf(format, args...)
204 if strings.Contains(text, "%!") {
205 a.Fatalf("printf error: %s", text)
206 }
207 a.out.WriteString(text)
208 }
209
210
211 func (a *Asm) Comment(format string, args ...any) {
212 fmt.Fprintf(&a.out, "\t// %s\n", fmt.Sprintf(format, args...))
213 }
214
215
216 func (a *Asm) EOL(format string, args ...any) {
217 bytes := a.out.Bytes()
218 if len(bytes) > 0 && bytes[len(bytes)-1] == '\n' {
219 a.out.Truncate(a.out.Len() - 1)
220 }
221 a.Comment(format, args...)
222 }
223
224
225
226 func (a *Asm) JmpEnable(option Option, label string) bool {
227 jmpEnable := a.Arch.options[option]
228 if jmpEnable == nil {
229 return false
230 }
231 jmpEnable(a, label)
232 return true
233 }
234
235
236
237 func (a *Asm) Enabled(option Option) bool {
238 return a.enabled[option]
239 }
240
241
242
243 func (a *Asm) SetOption(option Option, on bool) {
244 a.enabled[option] = on
245 }
246
247
248
249
250 func (a *Asm) op3(op string, src1, src2, dst Reg) {
251 if op == "" {
252 a.Fatalf("missing instruction")
253 }
254 if src2 == dst {
255
256 a.Printf("\t%s %s, %s\n", op, src1, dst)
257 } else if a.Arch.op3 != nil && !a.Arch.op3(op) {
258
259 if src1 == dst {
260 a.Fatalf("implicit mov %s, %s would smash src1", src2, dst)
261 }
262 a.Mov(src2, dst)
263 a.Printf("\t%s %s, %s\n", op, src1, dst)
264 } else {
265
266 a.Printf("\t%s %s, %s, %s\n", op, src1, src2, dst)
267 }
268 }
269
270
271 func (a *Asm) Mov(src, dst Reg) {
272 if src != dst {
273 a.Printf("\t%s %s, %s\n", a.Arch.mov, src, dst)
274 }
275 }
276
277
278
279 func (a *Asm) AddWords(src1 Reg, src2, dst RegPtr) {
280 if a.Arch.addWords == "" {
281
282
283 t := a.Reg()
284 a.Lsh(a.Imm(bits.TrailingZeros(uint(a.Arch.WordBytes))), src1, t)
285 a.Add(t, Reg(src2), Reg(dst), KeepCarry)
286 a.Free(t)
287 return
288 }
289 a.Printf("\t"+a.Arch.addWords+"\n", src1, src2, dst)
290 }
291
292
293
294 func (a *Asm) And(src1, src2, dst Reg) {
295 a.op3(a.Arch.and, src1, src2, dst)
296 }
297
298
299
300 func (a *Asm) Or(src1, src2, dst Reg) {
301 a.op3(a.Arch.or, src1, src2, dst)
302 }
303
304
305
306 func (a *Asm) Xor(src1, src2, dst Reg) {
307 a.op3(a.Arch.xor, src1, src2, dst)
308 }
309
310
311
312 func (a *Asm) Neg(src, dst Reg) {
313 if a.Arch.neg == "" {
314 if a.Arch.rsb != "" {
315 a.Printf("\t%s $0, %s, %s\n", a.Arch.rsb, src, dst)
316 return
317 }
318 if a.Arch.sub != "" && a.Arch.reg0 != "" {
319 a.Printf("\t%s %s, %s, %s\n", a.Arch.sub, src, a.Arch.reg0, dst)
320 return
321 }
322 a.Fatalf("missing neg")
323 }
324 if src == dst {
325 a.Printf("\t%s %s\n", a.Arch.neg, dst)
326 } else {
327 a.Printf("\t%s %s, %s\n", a.Arch.neg, src, dst)
328 }
329 }
330
331
332 func (a *Asm) HasRegShift() bool {
333 return a.Arch.regShift
334 }
335
336
337
338 func (a *Asm) LshReg(shift, src Reg) Reg {
339 if !a.HasRegShift() {
340 a.Fatalf("no reg shift")
341 }
342 return Reg{fmt.Sprintf("%s<<%s", src, strings.TrimPrefix(shift.name, "$"))}
343 }
344
345
346
347 func (a *Asm) Lsh(shift, src, dst Reg) {
348 if need := a.hint(HintShiftCount); need != "" && shift.name != need && !shift.IsImm() {
349 a.Fatalf("shift count not in %s", need)
350 }
351 if a.HasRegShift() {
352 a.Mov(a.LshReg(shift, src), dst)
353 return
354 }
355 a.op3(a.Arch.lsh, shift, src, dst)
356 }
357
358
359
360 func (a *Asm) LshWide(shift, adj, src, dst Reg) {
361 if a.Arch.lshd == "" {
362 a.Fatalf("no lshwide on %s", a.Arch.Name)
363 }
364 if need := a.hint(HintShiftCount); need != "" && shift.name != need && !shift.IsImm() {
365 a.Fatalf("shift count not in %s", need)
366 }
367 a.op3(fmt.Sprintf("%s %s,", a.Arch.lshd, shift), adj, src, dst)
368 }
369
370
371
372 func (a *Asm) RshReg(shift, src Reg) Reg {
373 if !a.HasRegShift() {
374 a.Fatalf("no reg shift")
375 }
376 return Reg{fmt.Sprintf("%s>>%s", src, strings.TrimPrefix(shift.name, "$"))}
377 }
378
379
380
381 func (a *Asm) Rsh(shift, src, dst Reg) {
382 if need := a.hint(HintShiftCount); need != "" && shift.name != need && !shift.IsImm() {
383 a.Fatalf("shift count not in %s", need)
384 }
385 if a.HasRegShift() {
386 a.Mov(a.RshReg(shift, src), dst)
387 return
388 }
389 a.op3(a.Arch.rsh, shift, src, dst)
390 }
391
392
393
394 func (a *Asm) RshWide(shift, adj, src, dst Reg) {
395 if a.Arch.lshd == "" {
396 a.Fatalf("no rshwide on %s", a.Arch.Name)
397 }
398 if need := a.hint(HintShiftCount); need != "" && shift.name != need && !shift.IsImm() {
399 a.Fatalf("shift count not in %s", need)
400 }
401 a.op3(fmt.Sprintf("%s %s,", a.Arch.rshd, shift), adj, src, dst)
402 }
403
404
405 func (a *Asm) SLTU(src1, src2, dst Reg) {
406 switch {
407 default:
408 a.Fatalf("arch has no sltu/sgtu")
409 case a.Arch.sltu != "":
410 a.Printf("\t%s %s, %s, %s\n", a.Arch.sltu, src1, src2, dst)
411 case a.Arch.sgtu != "":
412 a.Printf("\t%s %s, %s, %s\n", a.Arch.sgtu, src2, src1, dst)
413 }
414 }
415
416
417 func (a *Asm) Add(src1, src2, dst Reg, carry Carry) {
418 switch {
419 default:
420 a.Fatalf("unsupported carry behavior")
421 case a.Arch.addF != nil && a.Arch.addF(a, src1, src2, dst, carry):
422
423 case a.Arch.add != "" && (carry == KeepCarry || carry == SmashCarry):
424 a.op3(a.Arch.add, src1, src2, dst)
425 case a.Arch.adds != "" && (carry == SetCarry || carry == SmashCarry):
426 a.op3(a.Arch.adds, src1, src2, dst)
427 case a.Arch.adc != "" && (carry == UseCarry || carry == UseCarry|SmashCarry):
428 a.op3(a.Arch.adc, src1, src2, dst)
429 case a.Arch.adcs != "" && (carry == UseCarry|SetCarry || carry == UseCarry|SmashCarry):
430 a.op3(a.Arch.adcs, src1, src2, dst)
431 case a.Arch.lea != "" && (carry == KeepCarry || carry == SmashCarry):
432 if src1.IsImm() {
433 a.Printf("\t%s %s(%s), %s\n", a.Arch.lea, src1.name[1:], src2, dst)
434 } else {
435 a.Printf("\t%s (%s)(%s), %s\n", a.Arch.lea, src1, src2, dst)
436 }
437 if src2 == dst {
438 a.EOL("ADD %s, %s", src1, dst)
439 } else {
440 a.EOL("ADD %s, %s, %s", src1, src2, dst)
441 }
442
443 case a.Arch.add != "" && a.Arch.regCarry != "":
444
445
446
447
448
449
450
451
452
453 cr := a.Carry()
454 if carry&AltCarry != 0 {
455 cr = a.AltCarry()
456 if !cr.Valid() {
457 a.Fatalf("alt carry not supported")
458 }
459 carry &^= AltCarry
460 }
461 tmp := a.tmp()
462 if !tmp.Valid() {
463 a.Fatalf("cannot simulate sub carry without regTmp")
464 }
465 switch carry {
466 default:
467 a.Fatalf("unsupported carry behavior")
468 case UseCarry, UseCarry | SmashCarry:
469
470 if a.IsZero(src1) {
471
472 a.Add(cr, src2, dst, KeepCarry)
473 a.EOL("ADC $0, %s, %s", src2, dst)
474 break
475 }
476 a.Add(src1, src2, dst, KeepCarry)
477 a.EOL("ADC %s, %s, %s (cr=%s)", src1, src2, dst, cr)
478 a.Add(cr, dst, dst, KeepCarry)
479 a.EOL("...")
480
481 case SetCarry:
482 if a.IsZero(src1) && src2 == dst {
483
484 a.Xor(cr, cr, cr)
485 break
486 }
487 var old Reg
488 switch {
489 case dst != src1:
490 old = src1
491 case dst != src2:
492 old = src2
493 default:
494
495
496 a.Rsh(a.Imm(a.Arch.WordBits-1), src1, cr)
497 a.EOL("ADDS %s, %s, %s (cr=%s)", src1, src2, dst, cr)
498 a.Add(src1, src2, dst, KeepCarry)
499 a.EOL("...")
500 return
501 }
502 a.Add(src1, src2, dst, KeepCarry)
503 a.EOL("ADDS %s, %s, %s (cr=%s)", src1, src2, dst, cr)
504 a.SLTU(old, dst, cr)
505 a.EOL("...")
506
507 case UseCarry | SetCarry:
508 if a.IsZero(src1) {
509
510
511 a.Add(cr, src2, dst, KeepCarry)
512 a.EOL("ADCS $0, %s, %s (cr=%s)", src2, dst, cr)
513 a.SLTU(cr, dst, cr)
514 a.EOL("...")
515 break
516 }
517
518
519
520 var old Reg
521 switch {
522 case dst != src1:
523 old = src1
524 case dst != src2:
525 old = src2
526 }
527 if old.Valid() {
528 a.Add(src1, src2, dst, KeepCarry)
529 a.EOL("ADCS %s, %s, %s (cr=%s)", src1, src2, dst, cr)
530 a.SLTU(old, dst, tmp)
531 a.EOL("...")
532 } else {
533
534
535 a.Rsh(a.Imm(a.Arch.WordBits-1), src1, tmp)
536 a.EOL("ADCS %s, %s, %s (cr=%s)", src1, src2, dst, cr)
537 a.Add(src1, src2, dst, KeepCarry)
538 a.EOL("...")
539 }
540
541 a.Add(cr, dst, dst, KeepCarry)
542 a.EOL("...")
543 a.SLTU(cr, dst, cr)
544 a.EOL("...")
545
546 a.Add(tmp, cr, cr, KeepCarry)
547 a.EOL("...")
548 }
549 }
550 }
551
552
553 func (a *Asm) Sub(src1, src2, dst Reg, carry Carry) {
554 switch {
555 default:
556 a.Fatalf("unsupported carry behavior")
557 case a.Arch.subF != nil && a.Arch.subF(a, src1, src2, dst, carry):
558
559 case a.Arch.sub != "" && (carry == KeepCarry || carry == SmashCarry):
560 a.op3(a.Arch.sub, src1, src2, dst)
561 case a.Arch.subs != "" && (carry == SetCarry || carry == SmashCarry):
562 a.op3(a.Arch.subs, src1, src2, dst)
563 case a.Arch.sbc != "" && (carry == UseCarry || carry == UseCarry|SmashCarry):
564 a.op3(a.Arch.sbc, src1, src2, dst)
565 case a.Arch.sbcs != "" && (carry == UseCarry|SetCarry || carry == UseCarry|SmashCarry):
566 a.op3(a.Arch.sbcs, src1, src2, dst)
567 case strings.HasPrefix(src1.name, "$") && (carry == KeepCarry || carry == SmashCarry):
568
569
570
571 if strings.HasPrefix(src1.name, "$-") {
572 src1.name = "$" + src1.name[2:]
573 } else {
574 src1.name = "$-" + src1.name[1:]
575 }
576 a.Add(src1, src2, dst, carry)
577
578 case a.Arch.sub != "" && a.Arch.regCarry != "":
579
580
581
582
583
584
585
586
587
588 cr := a.Carry()
589 if carry&AltCarry != 0 {
590 a.Fatalf("alt carry not supported")
591 }
592 tmp := a.tmp()
593 if !tmp.Valid() {
594 a.Fatalf("cannot simulate carry without regTmp")
595 }
596 switch carry {
597 default:
598 a.Fatalf("unsupported carry behavior")
599 case UseCarry, UseCarry | SmashCarry:
600
601 if a.IsZero(src1) {
602
603 a.Sub(cr, src2, dst, KeepCarry)
604 a.EOL("SBC $0, %s, %s", src2, dst)
605 break
606 }
607 a.Sub(src1, src2, dst, KeepCarry)
608 a.EOL("SBC %s, %s, %s", src1, src2, dst)
609 a.Sub(cr, dst, dst, KeepCarry)
610 a.EOL("...")
611
612 case SetCarry:
613 if a.IsZero(src1) && src2 == dst {
614
615 a.Xor(cr, cr, cr)
616 break
617 }
618
619 a.SLTU(src1, src2, cr)
620 a.EOL("SUBS %s, %s, %s", src1, src2, dst)
621 a.Sub(src1, src2, dst, KeepCarry)
622 a.EOL("...")
623
624 case UseCarry | SetCarry:
625 if a.IsZero(src1) {
626
627 if src2 == dst {
628
629
630
631
632
633
634 a.SLTU(cr, src2, tmp)
635 a.EOL("SBCS $0, %s, %s", src2, dst)
636 a.Sub(cr, src2, dst, KeepCarry)
637 a.EOL("...")
638 a.Mov(tmp, cr)
639 a.EOL("...")
640 break
641 }
642 a.Sub(cr, src2, dst, KeepCarry)
643 a.SLTU(cr, src2, cr)
644 break
645 }
646
647
648
649 a.SLTU(cr, src2, tmp)
650 a.EOL("SBCS %s, %s, %s", src1, src2, dst)
651 a.Sub(cr, src2, dst, KeepCarry)
652 a.EOL("...")
653 a.SLTU(src1, dst, cr)
654 a.EOL("...")
655 a.Sub(src1, dst, dst, KeepCarry)
656 a.EOL("...")
657 a.Add(tmp, cr, cr, KeepCarry)
658 a.EOL("...")
659 }
660 }
661 }
662
663
664
665
666 func (a *Asm) ClearCarry(which Carry) {
667 dst := Reg{a.Arch.regs[0]}
668 switch which & (AddCarry | SubCarry) {
669 default:
670 a.Fatalf("bad carry")
671 case AddCarry:
672 a.Add(a.Imm(0), dst, dst, SetCarry|which&AltCarry)
673 case SubCarry:
674 a.Sub(a.Imm(0), dst, dst, SetCarry|which&AltCarry)
675 }
676 a.EOL("clear carry")
677 }
678
679
680
681
682 func (a *Asm) SaveCarry(dst Reg) {
683
684
685
686 if cr := a.Carry(); cr.Valid() {
687 if cr == dst {
688 return
689 }
690 a.Mov(cr, dst)
691 } else {
692 a.Sub(dst, dst, dst, UseCarry|SmashCarry)
693 }
694 a.EOL("save carry")
695 }
696
697
698
699 func (a *Asm) RestoreCarry(src Reg) {
700 if cr := a.Carry(); cr.Valid() {
701 if cr == src {
702 return
703 }
704 a.Mov(src, cr)
705 } else if a.Arch.subCarryIsBorrow {
706 a.Add(src, src, src, SetCarry)
707 } else {
708
709
710
711
712 a.Sub(src, cmp.Or(a.ZR(), Reg{"SP"}), src, SetCarry)
713 }
714 a.EOL("restore carry")
715 }
716
717
718
719 func (a *Asm) ConvertCarry(which Carry, dst Reg) {
720 if a.Carry().Valid() {
721 return
722 }
723 switch which {
724 case AddCarry:
725 if a.Arch.subCarryIsBorrow {
726 a.Neg(dst, dst)
727 } else {
728 a.Add(a.Imm(1), dst, dst, SmashCarry)
729 }
730 a.EOL("convert add carry")
731 case SubCarry:
732 a.Neg(dst, dst)
733 a.EOL("convert sub carry")
734 }
735 }
736
737
738
739 func (a *Asm) SaveConvertCarry(which Carry, dst Reg) {
740 switch which {
741 default:
742 a.Fatalf("bad carry")
743 case AddCarry:
744 if (a.Arch.adc != "" || a.Arch.adcs != "") && a.ZR().Valid() {
745 a.Add(a.ZR(), a.ZR(), dst, UseCarry|SmashCarry)
746 a.EOL("save & convert add carry")
747 return
748 }
749 case SubCarry:
750
751 }
752 a.SaveCarry(dst)
753 a.ConvertCarry(which, dst)
754 }
755
756
757
758
759 func (a *Asm) MulWide(src1, src2, dstlo, dsthi Reg) {
760 switch {
761 default:
762 a.Fatalf("mulwide not available")
763 case a.Arch.mulWideF != nil:
764 a.Arch.mulWideF(a, src1, src2, dstlo, dsthi)
765 case a.Arch.mul != "" && !dsthi.Valid():
766 a.op3(a.Arch.mul, src1, src2, dstlo)
767 case a.Arch.mulhi != "" && !dstlo.Valid():
768 a.op3(a.Arch.mulhi, src1, src2, dsthi)
769 case a.Arch.mul != "" && a.Arch.mulhi != "" && dstlo != src1 && dstlo != src2:
770 a.op3(a.Arch.mul, src1, src2, dstlo)
771 a.op3(a.Arch.mulhi, src1, src2, dsthi)
772 case a.Arch.mul != "" && a.Arch.mulhi != "" && dsthi != src1 && dsthi != src2:
773 a.op3(a.Arch.mulhi, src1, src2, dsthi)
774 a.op3(a.Arch.mul, src1, src2, dstlo)
775 }
776 }
777
778
779 func (a *Asm) Jmp(label string) {
780
781 a.Printf("\tJMP %s\n", label)
782 }
783
784
785
786 func (a *Asm) JmpZero(src Reg, label string) {
787 a.Printf("\t"+a.Arch.jmpZero+"\n", src, label)
788 }
789
790
791
792 func (a *Asm) JmpNonZero(src Reg, label string) {
793 a.Printf("\t"+a.Arch.jmpNonZero+"\n", src, label)
794 }
795
796
797 func (a *Asm) Label(name string) {
798 a.Printf("%s:\n", name)
799 }
800
801
802 func (a *Asm) Ret() {
803 a.Printf("\tRET\n")
804 }
805
View as plain text