1
2
3
4
5
6
7 package compare
8
9 import (
10 "cmd/compile/internal/base"
11 "cmd/compile/internal/ir"
12 "cmd/compile/internal/typecheck"
13 "cmd/compile/internal/types"
14 "fmt"
15 "math/bits"
16 "sort"
17 )
18
19
20 func IsRegularMemory(t *types.Type) bool {
21 return types.AlgType(t) == types.AMEM
22 }
23
24
25
26
27
28 func Memrun(t *types.Type, start int) (size int64, next int) {
29 next = start
30 for {
31 next++
32 if next == t.NumFields() {
33 break
34 }
35
36 if types.IsPaddedField(t, next-1) {
37 break
38 }
39
40 if f := t.Field(next); f.Sym.IsBlank() || !IsRegularMemory(f.Type) {
41 break
42 }
43
44
45 if base.Ctxt.Arch.Alignment > 1 {
46 align := t.Alignment()
47 if off := t.Field(start).Offset; off&(align-1) != 0 {
48
49
50 align = 1 << uint(bits.TrailingZeros64(uint64(off)))
51 }
52 size := t.Field(next).End() - t.Field(start).Offset
53 if size > align {
54 break
55 }
56 }
57 }
58 return t.Field(next-1).End() - t.Field(start).Offset, next
59 }
60
61
62
63 func EqCanPanic(t *types.Type) bool {
64 switch t.Kind() {
65 default:
66 return false
67 case types.TINTER:
68 return true
69 case types.TARRAY:
70 return EqCanPanic(t.Elem())
71 case types.TSTRUCT:
72 for _, f := range t.Fields() {
73 if !f.Sym.IsBlank() && EqCanPanic(f.Type) {
74 return true
75 }
76 }
77 return false
78 }
79 }
80
81
82
83
84
85
86 func EqStructCost(t *types.Type) int64 {
87 cost := int64(0)
88
89 for i, fields := 0, t.Fields(); i < len(fields); {
90 f := fields[i]
91
92
93 if f.Sym.IsBlank() {
94 i++
95 continue
96 }
97
98 n, _, next := eqStructFieldCost(t, i)
99
100 cost += n
101 i = next
102 }
103
104 return cost
105 }
106
107
108
109
110
111
112
113 func eqStructFieldCost(t *types.Type, i int) (int64, int64, int) {
114 var (
115 cost = int64(0)
116 regSize = int64(types.RegSize)
117
118 size int64
119 next int
120 )
121
122 if base.Ctxt.Arch.CanMergeLoads {
123
124
125 size, next = Memrun(t, i)
126 cost = size / regSize
127 if size%regSize != 0 {
128 cost++
129 }
130 return cost, size, next
131 }
132
133
134
135
136 ft := t.Field(i).Type
137 size = ft.Size()
138 next = i + 1
139
140 return calculateCostForType(ft), size, next
141 }
142
143 func calculateCostForType(t *types.Type) int64 {
144 var cost int64
145 switch t.Kind() {
146 case types.TSTRUCT:
147 return EqStructCost(t)
148 case types.TSLICE:
149
150 base.Fatalf("calculateCostForType: unexpected slice type")
151 case types.TARRAY:
152 elemCost := calculateCostForType(t.Elem())
153 cost = t.NumElem() * elemCost
154 case types.TSTRING, types.TINTER, types.TCOMPLEX64, types.TCOMPLEX128:
155 cost = 2
156 case types.TINT64, types.TUINT64:
157 cost = 8 / int64(types.RegSize)
158 default:
159 cost = 1
160 }
161 return cost
162 }
163
164
165
166
167
168
169
170
171 func EqStruct(t *types.Type, np, nq ir.Node) ([]ir.Node, bool) {
172
173
174 var conds [][]ir.Node
175 conds = append(conds, []ir.Node{})
176 and := func(n ir.Node) {
177 i := len(conds) - 1
178 conds[i] = append(conds[i], n)
179 }
180
181
182
183 for i, fields := 0, t.Fields(); i < len(fields); {
184 f := fields[i]
185
186
187 if f.Sym.IsBlank() {
188 i++
189 continue
190 }
191
192 typeCanPanic := EqCanPanic(f.Type)
193
194
195 if !IsRegularMemory(f.Type) {
196 if typeCanPanic {
197
198 conds = append(conds, []ir.Node{})
199 }
200 switch {
201 case f.Type.IsString():
202 p := typecheck.DotField(base.Pos, typecheck.Expr(np), i)
203 q := typecheck.DotField(base.Pos, typecheck.Expr(nq), i)
204 eqlen, eqmem := EqString(p, q)
205 and(eqlen)
206 and(eqmem)
207 default:
208 and(eqfield(np, nq, i))
209 }
210 if typeCanPanic {
211
212 conds = append(conds, []ir.Node{})
213 }
214 i++
215 continue
216 }
217
218 cost, size, next := eqStructFieldCost(t, i)
219 if cost <= 4 {
220
221 for j := i; j < next; j++ {
222 and(eqfield(np, nq, j))
223 }
224 } else {
225
226 cc := eqmem(np, nq, i, size)
227 and(cc)
228 }
229 i = next
230 }
231
232
233
234 var flatConds []ir.Node
235 for _, c := range conds {
236 isCall := func(n ir.Node) bool {
237 return n.Op() == ir.OCALL || n.Op() == ir.OCALLFUNC
238 }
239 sort.SliceStable(c, func(i, j int) bool {
240 return !isCall(c[i]) && isCall(c[j])
241 })
242 flatConds = append(flatConds, c...)
243 }
244 return flatConds, len(conds) > 1
245 }
246
247
248
249
250
251
252
253
254
255
256
257 func EqString(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) {
258 s = typecheck.Conv(s, types.Types[types.TSTRING])
259 t = typecheck.Conv(t, types.Types[types.TSTRING])
260 sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s)
261 tptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, t)
262 slen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, s), types.Types[types.TUINTPTR])
263 tlen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, t), types.Types[types.TUINTPTR])
264
265
266
267
268 probablyConstant := func(n ir.Node) bool {
269 if n.Op() == ir.OCONVNOP {
270 n = n.(*ir.ConvExpr).X
271 }
272 if n.Op() == ir.OLITERAL {
273 return true
274 }
275 if n.Op() != ir.ONAME {
276 return false
277 }
278 name := n.(*ir.Name)
279 if name.Class != ir.PAUTO {
280 return false
281 }
282 if def := name.Defn; def == nil {
283
284 return true
285 } else if def.Op() == ir.OAS && (def.(*ir.AssignStmt).Y == nil || def.(*ir.AssignStmt).Y.Op() == ir.OLITERAL) {
286
287 return true
288 }
289 return false
290 }
291 cmplen := slen
292 if probablyConstant(t) && !probablyConstant(s) {
293 cmplen = tlen
294 }
295
296 fn := typecheck.LookupRuntime("memequal", types.Types[types.TUINT8], types.Types[types.TUINT8])
297 call := typecheck.Call(base.Pos, fn, []ir.Node{sptr, tptr, ir.Copy(cmplen)}, false).(*ir.CallExpr)
298
299 cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, slen, tlen)
300 cmp = typecheck.Expr(cmp).(*ir.BinaryExpr)
301 cmp.SetType(types.Types[types.TBOOL])
302 return cmp, call
303 }
304
305
306
307
308
309
310
311
312
313
314
315 func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) {
316 if !types.Identical(s.Type(), t.Type()) {
317 base.Fatalf("EqInterface %v %v", s.Type(), t.Type())
318 }
319
320
321 var fn ir.Node
322 if s.Type().IsEmptyInterface() {
323 fn = typecheck.LookupRuntime("efaceeq")
324 } else {
325 fn = typecheck.LookupRuntime("ifaceeq")
326 }
327
328 stab := ir.NewUnaryExpr(base.Pos, ir.OITAB, s)
329 ttab := ir.NewUnaryExpr(base.Pos, ir.OITAB, t)
330 sdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, s)
331 tdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, t)
332 sdata.SetType(types.Types[types.TUNSAFEPTR])
333 tdata.SetType(types.Types[types.TUNSAFEPTR])
334 sdata.SetTypecheck(1)
335 tdata.SetTypecheck(1)
336
337 call := typecheck.Call(base.Pos, fn, []ir.Node{stab, sdata, tdata}, false).(*ir.CallExpr)
338
339 cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, stab, ttab)
340 cmp = typecheck.Expr(cmp).(*ir.BinaryExpr)
341 cmp.SetType(types.Types[types.TBOOL])
342 return cmp, call
343 }
344
345
346
347
348 func eqfield(p, q ir.Node, field int) ir.Node {
349 nx := typecheck.DotField(base.Pos, typecheck.Expr(p), field)
350 ny := typecheck.DotField(base.Pos, typecheck.Expr(q), field)
351 return typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.OEQ, nx, ny))
352 }
353
354
355
356
357 func eqmem(p, q ir.Node, field int, size int64) ir.Node {
358 nx := typecheck.Expr(typecheck.NodAddr(typecheck.DotField(base.Pos, p, field)))
359 ny := typecheck.Expr(typecheck.NodAddr(typecheck.DotField(base.Pos, q, field)))
360
361 fn, needsize := eqmemfunc(size, nx.Type().Elem())
362 call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
363 call.Args.Append(nx)
364 call.Args.Append(ny)
365 if needsize {
366 call.Args.Append(ir.NewInt(base.Pos, size))
367 }
368
369 return call
370 }
371
372 func eqmemfunc(size int64, t *types.Type) (fn *ir.Name, needsize bool) {
373 if !base.Ctxt.Arch.CanMergeLoads && t.Alignment() < int64(base.Ctxt.Arch.Alignment) && t.Alignment() < t.Size() {
374
375
376 size = 0
377 }
378 switch size {
379 case 1, 2, 4, 8, 16:
380 buf := fmt.Sprintf("memequal%d", int(size)*8)
381 return typecheck.LookupRuntime(buf, t, t), false
382 }
383
384 return typecheck.LookupRuntime("memequal", t, t), true
385 }
386
View as plain text