1
2
3
4
5 package ssacompile
6
7 import (
8 "fmt"
9 "internal/goarch"
10
11 "cmd/compile/internal/ssa"
12 "cmd/compile/internal/ssa/block"
13 "cmd/compile/internal/ssa/ssaop"
14 "cmd/compile/internal/types"
15 "cmd/internal/obj"
16 )
17
18 type localEffect struct {
19 start ssa.CPUfeatures
20 internal ssa.CPUfeatures
21 end [2]ssa.CPUfeatures
22 visited bool
23 }
24
25 func (e localEffect) String() string {
26 return fmt.Sprintf("visited=%v, start=%v, internal=%v, end[0]=%v, end[1]=%v", e.visited, e.start, e.internal, e.end[0], e.end[1])
27 }
28
29
30
31
32 func ifEffect(b *ssa.Block) (features ssa.CPUfeatures, taken int) {
33
34 if b.Kind != block.BlockIf {
35 return
36 }
37 c := b.Controls[0]
38
39 if c.Op == ssaop.OpNot {
40 taken = 1
41 c = c.Args[0]
42 }
43 if c.Op != ssaop.OpLoad {
44 return
45 }
46 offPtr := c.Args[0]
47 if offPtr.Op != ssaop.OpOffPtr {
48 return
49 }
50 addr := offPtr.Args[0]
51 if addr.Op != ssaop.OpAddr || addr.Args[0].Op != ssaop.OpSB {
52 return
53 }
54 sym := addr.Aux.(*obj.LSym)
55 if sym.Name != "internal/cpu.X86" {
56 return
57 }
58 o := offPtr.AuxInt
59 t := addr.Type
60 if !t.IsPtr() {
61 b.Func.Fatalf("The symbol %s is not a pointer, found %v instead", sym.Name, t)
62 }
63 t = t.Elem()
64 if !t.IsStruct() {
65 b.Func.Fatalf("The referent of symbol %s is not a struct, found %v instead", sym.Name, t)
66 }
67 match := ""
68 for _, f := range t.Fields() {
69 if o == f.Offset && f.Sym != nil {
70 match = f.Sym.Name
71 break
72 }
73 }
74
75 switch match {
76
77 case "HasAVX":
78 features = ssa.CPUavx
79 case "HasAVXVNNI":
80 features = ssa.CPUavx | ssa.CPUavxvnni
81 case "HasAVX2":
82 features = ssa.CPUavx2 | ssa.CPUavx
83
84
85 case "HasAVX512", "HasAVX512F", "HasAVX512CD", "HasAVX512BW",
86 "HasAVX512DQ", "HasAVX512VL", "HasAVX512VPCLMULQDQ":
87 features = ssa.CPUavx512 | ssa.CPUavx2 | ssa.CPUavx
88
89 case "HasAVX512GFNI":
90 features = ssa.CPUavx512 | ssa.CPUgfni | ssa.CPUavx2 | ssa.CPUavx
91 case "HasAVX512VNNI":
92 features = ssa.CPUavx512 | ssa.CPUavx512vnni | ssa.CPUavx2 | ssa.CPUavx
93 case "HasAVX512VBMI":
94 features = ssa.CPUavx512 | ssa.CPUvbmi | ssa.CPUavx2 | ssa.CPUavx
95 case "HasAVX512VBMI2":
96 features = ssa.CPUavx512 | ssa.CPUvbmi2 | ssa.CPUavx2 | ssa.CPUavx
97 case "HasAVX512BITALG":
98 features = ssa.CPUavx512 | ssa.CPUbitalg | ssa.CPUavx2 | ssa.CPUavx
99 case "HasAVX512VPOPCNTDQ":
100 features = ssa.CPUavx512 | ssa.CPUvpopcntdq | ssa.CPUavx2 | ssa.CPUavx
101
102 case "HasBMI1":
103 features = ssa.CPUvbmi
104 case "HasBMI2":
105 features = ssa.CPUvbmi2
106
107
108 case "HasAES", "HasADX", "HasERMS", "HasFSRM", "HasFMA", "HasGFNI", "HasOSXSAVE",
109 "HasPCLMULQDQ", "HasPOPCNT", "HasRDTSCP", "HasSHA",
110 "HasSSE3", "HasSSSE3", "HasSSE41", "HasSSE42":
111
112 }
113 if b.Func.Pass.Debug > 2 {
114 b.Func.Warnl(b.Pos, "%s, block b%v has features offset %d, match is %s, features is %v", b.Func.Name, b.ID, o, match, features)
115 }
116 return
117 }
118
119 func cpufeatures(f *ssa.Func) {
120 arch := f.Config.Ctxt.Arch.Family
121
122 if arch != goarch.AMD64 {
123 return
124 }
125
126 po := f.Postorder()
127
128 effects := make([]localEffect, 1+f.NumBlocks(), 1+f.NumBlocks())
129
130 features := func(t *types.Type) ssa.CPUfeatures {
131 if t.IsSIMD() {
132 switch t.Size() {
133 case 16, 32:
134 return ssa.CPUavx
135 case 64:
136 return ssa.CPUavx512 | ssa.CPUavx2 | ssa.CPUavx
137 }
138 }
139 return ssa.CPUNone
140 }
141
142
143
144
145 for i := len(po) - 1; i >= 0; i-- {
146 b := po[i]
147
148 var feat ssa.CPUfeatures
149
150 if b == f.Entry {
151
152
153 if f.Type != nil {
154 for _, field := range f.Type.RecvParamsResults() {
155 feat |= features(field.Type)
156 }
157 }
158
159 } else {
160
161 feat = ssa.CPUAll
162 for _, p := range b.Preds {
163 pb := p.Block()
164 if !effects[pb.ID].visited {
165
166 continue
167 }
168 pi := p.Index()
169 if pb.Kind != block.BlockIf {
170 pi = 0
171 }
172
173 feat &= effects[pb.ID].end[pi]
174 }
175 }
176
177 e := localEffect{start: feat, visited: true}
178
179
180 var internal ssa.CPUfeatures
181 for _, v := range b.Values {
182
183
184
185
186 t := v.Type
187 if t.IsResults() {
188 for i := 0; i < t.NumFields(); i++ {
189 feat |= features(t.FieldType(i))
190 }
191 } else {
192 internal |= features(v.Type)
193 }
194 }
195 e.internal = internal
196 feat |= internal
197
198 branchEffect, taken := ifEffect(b)
199 e.end = [2]ssa.CPUfeatures{feat, feat}
200 e.end[taken] |= branchEffect
201
202 effects[b.ID] = e
203 if f.Pass.Debug > 1 && feat != ssa.CPUNone {
204 f.Warnl(b.Pos, "%s, block b%v has features %v", b.Func.Name, b.ID, feat)
205 }
206
207 b.CPUfeatures = feat
208 f.MaxCPUFeatures |= feat
209 }
210
211
212 change := true
213 for change {
214 change = false
215 for i := len(po) - 1; i >= 0; i-- {
216 b := po[i]
217
218 if b == f.Entry {
219 continue
220 }
221 feat := ssa.CPUAll
222 for _, p := range b.Preds {
223 pb := p.Block()
224 pi := p.Index()
225 if pb.Kind != block.BlockIf {
226 pi = 0
227 }
228 feat &= effects[pb.ID].end[pi]
229 }
230 e := effects[b.ID]
231 if feat == e.start {
232 continue
233 }
234 e.start = feat
235 effects[b.ID] = e
236
237 if f.Pass.Debug > 1 {
238 f.Warnl(b.Pos, "%s, block b%v saw predecessor feature change", b.Func.Name, b.ID)
239 }
240
241 feat |= e.internal
242 if feat == e.end[0]&e.end[1] {
243 continue
244 }
245
246 branchEffect, taken := ifEffect(b)
247 e.end = [2]ssa.CPUfeatures{feat, feat}
248 e.end[taken] |= branchEffect
249
250 effects[b.ID] = e
251 b.CPUfeatures = feat
252 if f.Pass.Debug > 1 {
253 f.Warnl(b.Pos, "%s, block b%v has new features %v", b.Func.Name, b.ID, feat)
254 }
255 change = true
256 }
257 }
258 if f.Pass.Debug > 0 {
259 for _, b := range f.Blocks {
260 if b.CPUfeatures != ssa.CPUNone {
261 f.Warnl(b.Pos, "%s, block b%v has features %v", b.Func.Name, b.ID, b.CPUfeatures)
262 }
263
264 }
265 }
266 }
267
View as plain text