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