amd64.mx raw
1 // Copyright 2025 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package asmgen
6
7 var ArchAMD64 = &Arch{
8 Name: "amd64",
9 WordBits: 64,
10 WordBytes: 8,
11
12 regs: [][]byte{
13 "BX", "SI", "DI",
14 "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
15 "AX", "DX", "CX", // last to leave available for hinted allocation
16 },
17 op3: x86Op3,
18 hint: x86Hint,
19 memOK: true,
20 subCarryIsBorrow: true,
21
22 // Note: Not setting memIndex, because code generally runs faster
23 // if we avoid the use of scaled-index memory references,
24 // particularly in ADX instructions.
25
26 options: map[Option]func(*Asm, []byte){
27 OptionAltCarry: amd64JmpADX,
28 },
29
30 mov: "MOVQ",
31 adds: "ADDQ",
32 adcs: "ADCQ",
33 subs: "SUBQ",
34 sbcs: "SBBQ",
35 lsh: "SHLQ",
36 lshd: "SHLQ",
37 rsh: "SHRQ",
38 rshd: "SHRQ",
39 and: "ANDQ",
40 or: "ORQ",
41 xor: "XORQ",
42 neg: "NEGQ",
43 lea: "LEAQ",
44 addF: amd64Add,
45 mulWideF: x86MulWide,
46
47 addWords: "LEAQ (%[2]s)(%[1]s*8), %[3]s",
48
49 jmpZero: "TESTQ %[1]s, %[1]s; JZ %[2]s",
50 jmpNonZero: "TESTQ %[1]s, %[1]s; JNZ %[2]s",
51 loopBottom: "SUBQ $1, %[1]s; JNZ %[2]s",
52 loopBottomNeg: "ADDQ $1, %[1]s; JNZ %[2]s",
53 }
54
55 func amd64JmpADX(a *Asm, label []byte) {
56 a.Printf("\tCMPB ·hasADX(SB), $0; JNZ %s\n", label)
57 }
58
59 func amd64Add(a *Asm, src1, src2 Reg, dst Reg, carry Carry) bool {
60 if a.Enabled(OptionAltCarry) {
61 // If OptionAltCarry is enabled, the generator is emitting ADD instructions
62 // both with and without the AltCarry flag set; the AltCarry flag means to
63 // use ADOX. Otherwise we have to use ADCX.
64 // Using regular ADD/ADC would smash both carry flags,
65 // so we reject anything we can't handled with ADCX/ADOX.
66 if carry&UseCarry != 0 && carry&(SetCarry|SmashCarry) != 0 {
67 if carry&AltCarry != 0 {
68 a.op3("ADOXQ", src1, src2, dst)
69 } else {
70 a.op3("ADCXQ", src1, src2, dst)
71 }
72 return true
73 }
74 if carry&(SetCarry|UseCarry) == SetCarry && a.IsZero(src1) && src2 == dst {
75 // Clearing carry flag. Caller will add EOL comment.
76 a.Printf("\tTESTQ AX, AX\n")
77 return true
78 }
79 if carry != KeepCarry {
80 a.Fatalf("unsupported carry")
81 }
82 }
83 return false
84 }
85
86 // The x86-prefixed functions are shared with Arch386 in 386.go.
87
88 func x86Op3(name []byte) bool {
89 // As far as a.op3 is concerned, there are no 3-op instructions.
90 // (We print instructions like MULX ourselves.)
91 return false
92 }
93
94 func x86Hint(a *Asm, h Hint) []byte {
95 switch h {
96 case HintShiftCount:
97 return "CX"
98 case HintMulSrc:
99 if a.Enabled(OptionAltCarry) { // using MULX
100 return "DX"
101 }
102 return "AX"
103 case HintMulHi:
104 if a.Enabled(OptionAltCarry) { // using MULX
105 return ""
106 }
107 return "DX"
108 }
109 return ""
110 }
111
112 func x86Suffix(a *Asm) []byte {
113 // Note: Not using a.Arch == Arch386 to avoid init cycle.
114 if a.Arch.Name == "386" {
115 return "L"
116 }
117 return "Q"
118 }
119
120 func x86MulWide(a *Asm, src1, src2, dstlo, dsthi Reg) {
121 if a.Enabled(OptionAltCarry) {
122 // Using ADCX/ADOX; use MULX to avoid clearing carry flag.
123 if src1.name != "DX" {
124 if src2.name != "DX" {
125 a.Fatalf("mul src1 or src2 must be DX")
126 }
127 src2 = src1
128 }
129 a.Printf("\tMULXQ %s, %s, %s\n", src2, dstlo, dsthi)
130 return
131 }
132
133 if src1.name != "AX" {
134 if src2.name != "AX" {
135 a.Fatalf("mulwide src1 or src2 must be AX")
136 }
137 src2 = src1
138 }
139 if dstlo.name != "AX" {
140 a.Fatalf("mulwide dstlo must be AX")
141 }
142 if dsthi.name != "DX" {
143 a.Fatalf("mulwide dsthi must be DX")
144 }
145 a.Printf("\tMUL%s %s\n", x86Suffix(a), src2)
146 }
147