1
2
3
4
5 package ssacompile
6
7
8
9
10
11
12
13
14
15
16
17
18 import (
19 "cmd/compile/internal/base"
20 "cmd/compile/internal/ir"
21 "cmd/compile/internal/ssa"
22 "cmd/compile/internal/ssa/ssaop"
23 "cmd/compile/internal/types"
24 "internal/buildcfg"
25 "slices"
26 )
27
28 type useType int
29
30 const (
31 useLoad useType = 1 << iota
32 useStore
33 useOffset
34 useCopy
35 useZero
36 useMove
37 useOther
38 )
39
40
41
42
43
44 type useTable struct {
45 useInfo
46 }
47
48
49 func (u *useTable) of(v *ssa.Value) []*ssa.Value {
50 if int(v.ID) >= len(u.starts) {
51 return nil
52 }
53 return u.get(v)
54 }
55
56
57 func classifyUses(v *ssa.Value, uses *useTable) useType {
58 q := []*ssa.Value{v}
59 var u useType
60 for len(q) > 0 {
61 curr := q[len(q)-1]
62 q = q[:len(q)-1]
63 for _, use := range uses.of(curr) {
64 switch use.Op {
65 case ssaop.OpCopy:
66 u |= useCopy
67 q = append(q, use)
68 case ssaop.OpOffPtr:
69 u |= useOffset
70 q = append(q, use)
71 case ssaop.OpLoad:
72 u |= useLoad
73 case ssaop.OpStore:
74 if curr == use.Args[1] {
75
76 u |= useOther
77 } else {
78 u |= useStore
79 }
80 case ssaop.OpZero:
81 u |= useZero
82 case ssaop.OpMove:
83 u |= useMove
84 default:
85
86
87 u |= useOther
88 }
89 }
90 }
91 return u
92 }
93
94
95
96 type variableDemographic struct {
97
98
99 ls bool
100
101
102
103 lszmco bool
104 varDefs []*ssa.Value
105 }
106
107
108
109
110
111
112
113
114
115 func variableDemographics(f *ssa.Func) (
116 demographics map[ssa.Aux]*variableDemographic,
117 localAddrs []*ssa.Value,
118 u *useTable) {
119
120
121
122 found := false
123 for _, b := range f.Blocks {
124 for _, c := range b.ControlValues() {
125 if c.Op == ssaop.OpOffPtr || c.Op == ssaop.OpLocalAddr {
126 f.Fatalf("unexpected pointer value in block control")
127 }
128 }
129 for _, v := range b.Values {
130 if v.Op == ssaop.OpLocalAddr {
131 if n := v.Aux.(*ir.Name); n.Class == ir.PAUTO || isABIInternalParam(f, n) {
132 found = true
133 break
134 }
135 }
136 }
137 if found {
138 break
139 }
140 }
141 if !found {
142 return nil, nil, nil
143 }
144
145
146
147 u = &useTable{uses(f)}
148 varLives := map[ssa.Aux]bool{}
149 demographics = make(map[ssa.Aux]*variableDemographic)
150 for _, b := range f.Blocks {
151 for _, v := range b.Values {
152 switch v.Op {
153 case ssaop.OpVarLive:
154 varLives[v.Aux] = true
155 case ssaop.OpVarDef:
156 d := demographics[v.Aux]
157 if d == nil {
158 d = &variableDemographic{ls: true, lszmco: true}
159 demographics[v.Aux] = d
160 }
161 d.varDefs = append(d.varDefs, v)
162 }
163 }
164 }
165
166 for _, b := range f.Blocks {
167 for _, v := range b.Values {
168 if v.Op == ssaop.OpLocalAddr {
169 if n := v.Aux.(*ir.Name); n.Class == ir.PAUTO || isABIInternalParam(f, n) {
170 ut := classifyUses(v, u)
171 d := demographics[n]
172 if d == nil {
173 d = &variableDemographic{ls: true, lszmco: true}
174 demographics[n] = d
175 }
176 if ut&^(useLoad|useStore) != 0 {
177 d.ls = false
178 }
179 if ut&useOther != 0 {
180 d.lszmco = false
181 }
182 localAddrs = append(localAddrs, v)
183 }
184 }
185 }
186 }
187
188
189
190 for n := range varLives {
191 if d := demographics[n]; d != nil {
192 d.ls = false
193 d.lszmco = false
194 }
195 }
196 return
197 }
198
199 var reinterpretOpMap = map[[2]types.Kind]ssaop.Op{
200 {types.TUINT32, types.TFLOAT32}: ssaop.OpI32AsF32,
201 {types.TINT32, types.TFLOAT32}: ssaop.OpI32AsF32,
202 {types.TFLOAT32, types.TUINT32}: ssaop.OpF32AsI32,
203 {types.TFLOAT32, types.TINT32}: ssaop.OpF32AsI32,
204 {types.TUINT64, types.TFLOAT64}: ssaop.OpI64AsF64,
205 {types.TINT64, types.TFLOAT64}: ssaop.OpI64AsF64,
206 {types.TFLOAT64, types.TUINT64}: ssaop.OpF64AsI64,
207 {types.TFLOAT64, types.TINT64}: ssaop.OpF64AsI64,
208 }
209
210
211
212 func reinterpretOp(t1, t2 *types.Type) ssaop.Op {
213 if buildcfg.GOARCH != "amd64" {
214
215 return ssaop.OpInvalid
216 }
217 if op, ok := reinterpretOpMap[[2]types.Kind{t1.Kind(), t2.Kind()}]; ok {
218 return op
219 }
220
221 return ssaop.OpInvalid
222 }
223
224
225
226 func copyCompatibleType(t1, t2 *types.Type) bool {
227 if t1.Size() != t2.Size() {
228 return false
229 }
230 if t1.IsInteger() {
231 return t2.IsInteger()
232 }
233 if ssa.IsPtr(t1) {
234 return ssa.IsPtr(t2)
235 }
236 return t1.Compare(t2) == types.CMPeq
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254 func sortLoadStores(bb *ssa.Block, loadStores []*ssa.Value, memoryOrders map[ssa.ID]map[ssa.ID]int) []*ssa.Value {
255 memOrder, ok := memoryOrders[bb.ID]
256 if !ok {
257 memOrder = make(map[ssa.ID]int)
258 var computeDepth func(v *ssa.Value) int
259 computeDepth = func(v *ssa.Value) int {
260 if d, ok := memOrder[v.ID]; ok {
261 return d
262 }
263
264 if v.Block != bb || v.Op == ssaop.OpInitMem || v.Op == ssaop.OpPhi {
265 memOrder[v.ID] = 0
266 return 0
267 }
268
269 d := computeDepth(v.MemoryArg()) + 2
270 memOrder[v.ID] = d
271 return d
272 }
273 for _, v := range bb.Values {
274 if v.Type.IsMemory() {
275 computeDepth(v)
276 }
277 }
278 memoryOrders[bb.ID] = memOrder
279 }
280 key := func(v *ssa.Value) int {
281 if v.Op == ssaop.OpLoad {
282 return memOrder[v.MemoryArg().ID] + 1
283 }
284 return memOrder[v.ID]
285 }
286 slices.SortFunc(loadStores, func(a, b *ssa.Value) int {
287 return key(a) - key(b)
288 })
289 return loadStores
290 }
291
292 func mem2reg(f *ssa.Func) {
293 changed := false
294 if base.Flag.N != 0 {
295 return
296 }
297 st := f.NewStats("mem2reg")
298
299
300 demographics, localAddrs, uses := variableDemographics(f)
301 if uses == nil {
302
303 return
304 }
305 defer uses.free(f)
306 memoryOrders := make(map[ssa.ID]map[ssa.ID]int)
307
308
309
310 varGrouped := make(map[*ir.Name][]*ssa.Value)
311 for _, v := range localAddrs {
312
313
314
315
316 if d, ok := demographics[v.Aux]; !ok || !d.ls {
317 continue
318 }
319 n := v.Aux.(*ir.Name)
320 if n.Class != ir.PAUTO {
321
322 continue
323 }
324 varGrouped[n] = append(varGrouped[n], v)
325 }
326 namesOrdered := []*ir.Name{}
327 for n, vag := range varGrouped {
328 namesOrdered = append(namesOrdered, n)
329 slices.SortFunc(vag, func(a, b *ssa.Value) int { return int(a.ID - b.ID) })
330 }
331 slices.SortFunc(namesOrdered, func(a, b *ir.Name) int {
332 return int(varGrouped[a][0].ID - varGrouped[b][0].ID)
333 })
334
335 removeStore := func(v *ssa.Value) {
336 changed = true
337 v.SetArgs1(v.MemoryArg())
338 v.Aux = nil
339 v.AuxInt = 0
340 v.Op = ssaop.OpCopy
341 }
342 type loadCandidate struct {
343 l *ssa.Value
344 v *ssa.Value
345
346
347 reinterpret ssaop.Op
348 }
349 replaceLoad := func(lc loadCandidate) {
350 changed = true
351 if lc.reinterpret == ssaop.OpInvalid {
352 if !copyCompatibleType(lc.l.Type, lc.v.Type) {
353 f.Fatalf("mem2reg: load is being replaced by a value of an incompatible type")
354 }
355 lc.l.SetArgs1(lc.v)
356 } else {
357
358
359 lc.l.SetArgs1(lc.l.Block.NewValue1(lc.l.Pos, lc.reinterpret, lc.l.Type, lc.v))
360 }
361 lc.l.Aux = nil
362 lc.l.AuxInt = 0
363 lc.l.Op = ssaop.OpCopy
364 }
365
366
367 storeCands := []*ssa.Value{}
368 loadCands := []loadCandidate{}
369 vaUses := []*ssa.Value{}
370 NextVar:
371 for _, n := range namesOrdered {
372 vag := varGrouped[n]
373 var block *ssa.Block
374 for _, va := range vag {
375
376 for _, use := range uses.of(va) {
377 if block == nil {
378 block = use.Block
379 }
380 if use.Block != block {
381
382 continue NextVar
383 }
384 }
385 }
386
387 storeCands = storeCands[:0]
388 loadCands = loadCands[:0]
389 vaUses = vaUses[:0]
390 for _, va := range vag {
391 vaUses = append(vaUses, uses.of(va)...)
392 }
393 vaUses = sortLoadStores(block, vaUses, memoryOrders)
394 var curV *ssa.Value
395 for _, v := range vaUses {
396 switch v.Op {
397 case ssaop.OpLoad:
398 if curV == nil {
399 f.Fatalf("mem2reg sees a load from an auto variable before any store")
400 }
401 reinter := ssaop.OpInvalid
402 if !copyCompatibleType(v.Type, curV.Type) {
403 reinter = reinterpretOp(curV.Type, v.Type)
404 if reinter == ssaop.OpInvalid {
405
406
407 st.Record("incompatible types in single block case", 1)
408 delete(varGrouped, n)
409 continue NextVar
410 }
411 }
412 loadCands = append(loadCands, loadCandidate{
413 l: v,
414 v: curV,
415 reinterpret: reinter,
416 })
417 case ssaop.OpStore:
418 curV = v.Args[1]
419 storeCands = append(storeCands, v)
420 default:
421 f.Fatalf("should only has load or store uses")
422 }
423 }
424
425 for _, v := range storeCands {
426
427 removeStore(v)
428 }
429 for _, v := range demographics[n].varDefs {
430
431 removeStore(v)
432 }
433 for _, lc := range loadCands {
434
435 replaceLoad(lc)
436 }
437
438 if f.Pass.Debug > 1 {
439 f.Warnl(n.Pos(), "promoted %v in single block case", n)
440 }
441 delete(varGrouped, n)
442 st.Record("promoted variable in single block case", 1)
443 }
444
445
446 if changed {
447 deadcode(f)
448 }
449 }
450
View as plain text