1
2
3
4
5 package walk
6
7 import (
8 "encoding/binary"
9 "go/constant"
10
11 "cmd/compile/internal/base"
12 "cmd/compile/internal/ir"
13 "cmd/compile/internal/reflectdata"
14 "cmd/compile/internal/ssagen"
15 "cmd/compile/internal/typecheck"
16 "cmd/compile/internal/types"
17 "cmd/internal/src"
18 "cmd/internal/sys"
19 )
20
21
22 func walkConv(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
23 n.X = walkExpr(n.X, init)
24 if n.Op() == ir.OCONVNOP && n.Type() == n.X.Type() {
25 return n.X
26 }
27 if n.Op() == ir.OCONVNOP && ir.ShouldCheckPtr(ir.CurFunc, 1) {
28 if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() {
29 return walkCheckPtrArithmetic(n, init)
30 }
31 }
32 param, result := rtconvfn(n.X.Type(), n.Type())
33 if param == types.Txxx {
34 return n
35 }
36 fn := types.BasicTypeNames[param] + "to" + types.BasicTypeNames[result]
37 return typecheck.Conv(mkcall(fn, types.Types[result], init, typecheck.Conv(n.X, types.Types[param])), n.Type())
38 }
39
40
41 func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
42
43 n.X = walkExpr(n.X, init)
44
45 fromType := n.X.Type()
46 toType := n.Type()
47 if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) {
48
49 reflectdata.MarkTypeUsedInInterface(fromType, ir.CurFunc.LSym)
50 }
51
52 if !fromType.IsInterface() {
53 var typeWord ir.Node
54 if toType.IsEmptyInterface() {
55 typeWord = reflectdata.TypePtr(fromType)
56 } else {
57 typeWord = reflectdata.ITabAddr(fromType, toType)
58 }
59 l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone))
60 l.SetType(toType)
61 l.SetTypecheck(n.Typecheck())
62 return l
63 }
64 if fromType.IsEmptyInterface() {
65 base.Fatalf("OCONVIFACE can't operate on an empty interface")
66 }
67
68
69 c := typecheck.Temp(fromType)
70 init.Append(ir.NewAssignStmt(base.Pos, c, n.X))
71
72
73 itab := ir.NewUnaryExpr(base.Pos, ir.OITAB, c)
74 itab.SetType(types.Types[types.TUINTPTR].PtrTo())
75 itab.SetTypecheck(1)
76 data := ir.NewUnaryExpr(n.Pos(), ir.OIDATA, c)
77 data.SetType(types.Types[types.TUINT8].PtrTo())
78 data.SetTypecheck(1)
79
80 var typeWord ir.Node
81 if toType.IsEmptyInterface() {
82
83
84
85
86
87 typeWord = typecheck.Temp(types.NewPtr(types.Types[types.TUINT8]))
88 init.Append(ir.NewAssignStmt(base.Pos, typeWord, itab))
89 nif := ir.NewIfStmt(base.Pos, typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.ONE, typeWord, typecheck.NodNil())), nil, nil)
90 nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, typeWord, itabType(typeWord))}
91 init.Append(nif)
92 } else {
93
94
95 fn := typecheck.LookupRuntime("convI2I")
96 types.CalcSize(fn.Type())
97 call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
98 call.Args = []ir.Node{reflectdata.TypePtr(toType), itab}
99 typeWord = walkExpr(typecheck.Expr(call), init)
100 }
101
102
103
104 e := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, data)
105 e.SetType(toType)
106 e.SetTypecheck(1)
107 return e
108 }
109
110
111
112
113 func dataWord(pos src.XPos, n ir.Node, init *ir.Nodes, escapes bool) ir.Node {
114 fromType := n.Type()
115
116
117 if types.IsDirectIface(fromType) {
118 return n
119 }
120
121
122 var value ir.Node
123 switch {
124 case fromType.Size() == 0:
125
126 cheapExpr(n, init)
127 value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR])
128 case fromType.IsBoolean() || (fromType.Size() == 1 && fromType.IsInteger()):
129
130
131 n = cheapExpr(n, init)
132
133 index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n), ir.NewInt(3))
134 if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian {
135 index = ir.NewBinaryExpr(base.Pos, ir.OADD, index, ir.NewInt(7))
136 }
137
138
139 staticuint64s := ir.NewLinksymExpr(base.Pos, ir.Syms.Staticuint64s, types.NewArray(types.Types[types.TUINT8], 256*8))
140 xe := ir.NewIndexExpr(base.Pos, staticuint64s, index)
141 xe.SetBounded(true)
142 value = xe
143 case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly():
144
145 value = n
146 case !escapes && fromType.Size() <= 1024:
147
148 value = typecheck.Temp(fromType)
149 init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n)))
150 }
151 if value != nil {
152
153 return typecheck.Expr(typecheck.NodAddr(value))
154 }
155
156
157 fnname, argType, needsaddr := dataWordFuncName(fromType)
158 fn := typecheck.LookupRuntime(fnname)
159
160 var args []ir.Node
161 if needsaddr {
162
163
164
165
166
167
168 if !ir.IsAddressable(n) {
169 n = copyExpr(n, fromType, init)
170 }
171 fn = typecheck.SubstArgTypes(fn, fromType)
172 args = []ir.Node{reflectdata.TypePtr(fromType), typecheck.NodAddr(n)}
173 } else {
174
175
176 var arg ir.Node
177 switch {
178 case fromType == argType:
179
180 arg = n
181 case fromType.Kind() == argType.Kind(),
182 fromType.IsPtrShaped() && argType.IsPtrShaped():
183
184
185 arg = ir.NewConvExpr(pos, ir.OCONVNOP, argType, n)
186 case fromType.IsInteger() && argType.IsInteger():
187
188 arg = ir.NewConvExpr(pos, ir.OCONV, argType, n)
189 default:
190
191 arg = copyExpr(n, fromType, init)
192 var addr ir.Node = typecheck.NodAddr(arg)
193 addr = ir.NewConvExpr(pos, ir.OCONVNOP, argType.PtrTo(), addr)
194 arg = ir.NewStarExpr(pos, addr)
195 arg.SetType(argType)
196 }
197 args = []ir.Node{arg}
198 }
199 call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
200 call.Args = args
201 return safeExpr(walkExpr(typecheck.Expr(call), init), init)
202 }
203
204
205 func walkConvIData(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
206 n.X = walkExpr(n.X, init)
207 return dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone)
208 }
209
210
211 func walkBytesRunesToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
212 a := typecheck.NodNil()
213 if n.Esc() == ir.EscNone {
214
215 a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
216 }
217 if n.Op() == ir.ORUNES2STR {
218
219 return mkcall("slicerunetostring", n.Type(), init, a, n.X)
220 }
221
222 n.X = cheapExpr(n.X, init)
223 ptr, len := backingArrayPtrLen(n.X)
224 return mkcall("slicebytetostring", n.Type(), init, a, ptr, len)
225 }
226
227
228 func walkBytesToStringTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
229 n.X = walkExpr(n.X, init)
230 if !base.Flag.Cfg.Instrumenting {
231
232
233 return n
234 }
235
236 n.X = cheapExpr(n.X, init)
237 ptr, len := backingArrayPtrLen(n.X)
238 return mkcall("slicebytetostringtmp", n.Type(), init, ptr, len)
239 }
240
241
242 func walkRuneToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
243 a := typecheck.NodNil()
244 if n.Esc() == ir.EscNone {
245 a = stackBufAddr(4, types.Types[types.TUINT8])
246 }
247
248 return mkcall("intstring", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TINT64]))
249 }
250
251
252 func walkStringToBytes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
253 s := n.X
254 if ir.IsConst(s, constant.String) {
255 sc := ir.StringVal(s)
256
257
258 t := types.NewArray(types.Types[types.TUINT8], int64(len(sc)))
259 var a ir.Node
260 if n.Esc() == ir.EscNone && len(sc) <= int(ir.MaxImplicitStackVarSize) {
261 a = stackBufAddr(t.NumElem(), t.Elem())
262 } else {
263 types.CalcSize(t)
264 a = ir.NewUnaryExpr(base.Pos, ir.ONEW, nil)
265 a.SetType(types.NewPtr(t))
266 a.SetTypecheck(1)
267 a.MarkNonNil()
268 }
269 p := typecheck.Temp(t.PtrTo())
270 init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, p, a)))
271
272
273 if len(sc) > 0 {
274 as := ir.NewAssignStmt(base.Pos, ir.NewStarExpr(base.Pos, p), ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), t.PtrTo())))
275 appendWalkStmt(init, as)
276 }
277
278
279 slice := ir.NewSliceExpr(n.Pos(), ir.OSLICEARR, p, nil, nil, nil)
280 slice.SetType(n.Type())
281 slice.SetTypecheck(1)
282 return walkExpr(slice, init)
283 }
284
285 a := typecheck.NodNil()
286 if n.Esc() == ir.EscNone {
287
288 a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
289 }
290
291 return mkcall("stringtoslicebyte", n.Type(), init, a, typecheck.Conv(s, types.Types[types.TSTRING]))
292 }
293
294
295 func walkStringToBytesTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
296
297
298
299
300
301
302
303 n.X = walkExpr(n.X, init)
304 return n
305 }
306
307
308 func walkStringToRunes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
309 a := typecheck.NodNil()
310 if n.Esc() == ir.EscNone {
311
312 a = stackBufAddr(tmpstringbufsize, types.Types[types.TINT32])
313 }
314
315 return mkcall("stringtoslicerune", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TSTRING]))
316 }
317
318
319
320
321
322 func dataWordFuncName(from *types.Type) (fnname string, argType *types.Type, needsaddr bool) {
323 if from.IsInterface() {
324 base.Fatalf("can only handle non-interfaces")
325 }
326 switch {
327 case from.Size() == 2 && uint8(from.Alignment()) == 2:
328 return "convT16", types.Types[types.TUINT16], false
329 case from.Size() == 4 && uint8(from.Alignment()) == 4 && !from.HasPointers():
330 return "convT32", types.Types[types.TUINT32], false
331 case from.Size() == 8 && uint8(from.Alignment()) == uint8(types.Types[types.TUINT64].Alignment()) && !from.HasPointers():
332 return "convT64", types.Types[types.TUINT64], false
333 }
334 if sc := from.SoleComponent(); sc != nil {
335 switch {
336 case sc.IsString():
337 return "convTstring", types.Types[types.TSTRING], false
338 case sc.IsSlice():
339 return "convTslice", types.NewSlice(types.Types[types.TUINT8]), false
340 }
341 }
342
343 if from.HasPointers() {
344 return "convT", types.Types[types.TUNSAFEPTR], true
345 }
346 return "convTnoptr", types.Types[types.TUNSAFEPTR], true
347 }
348
349
350
351
352
353
354 func rtconvfn(src, dst *types.Type) (param, result types.Kind) {
355 if ssagen.Arch.SoftFloat {
356 return types.Txxx, types.Txxx
357 }
358
359 switch ssagen.Arch.LinkArch.Family {
360 case sys.ARM, sys.MIPS:
361 if src.IsFloat() {
362 switch dst.Kind() {
363 case types.TINT64, types.TUINT64:
364 return types.TFLOAT64, dst.Kind()
365 }
366 }
367 if dst.IsFloat() {
368 switch src.Kind() {
369 case types.TINT64, types.TUINT64:
370 return src.Kind(), dst.Kind()
371 }
372 }
373
374 case sys.I386:
375 if src.IsFloat() {
376 switch dst.Kind() {
377 case types.TINT64, types.TUINT64:
378 return types.TFLOAT64, dst.Kind()
379 case types.TUINT32, types.TUINT, types.TUINTPTR:
380 return types.TFLOAT64, types.TUINT32
381 }
382 }
383 if dst.IsFloat() {
384 switch src.Kind() {
385 case types.TINT64, types.TUINT64:
386 return src.Kind(), dst.Kind()
387 case types.TUINT32, types.TUINT, types.TUINTPTR:
388 return types.TUINT32, types.TFLOAT64
389 }
390 }
391 }
392 return types.Txxx, types.Txxx
393 }
394
395
396
397
398 func byteindex(n ir.Node) ir.Node {
399
400
401
402
403 if !types.Identical(n.Type(), types.Types[types.TUINT8]) {
404 n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
405 n.SetType(types.Types[types.TUINT8])
406 n.SetTypecheck(1)
407 }
408 n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
409 n.SetType(types.Types[types.TINT])
410 n.SetTypecheck(1)
411 return n
412 }
413
414 func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
415
416
417
418 if n.CheckPtr() {
419 return n
420 }
421 n.SetCheckPtr(true)
422 defer n.SetCheckPtr(false)
423
424
425
426 switch n.X.Op() {
427 case ir.OCALLMETH:
428 base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
429 case ir.OCALLFUNC, ir.OCALLINTER:
430 return n
431 }
432
433 if n.X.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(n.X) {
434 return n
435 }
436
437
438
439
440
441
442
443 var originals []ir.Node
444 var walk func(n ir.Node)
445 walk = func(n ir.Node) {
446 switch n.Op() {
447 case ir.OADD:
448 n := n.(*ir.BinaryExpr)
449 walk(n.X)
450 walk(n.Y)
451 case ir.OSUB, ir.OANDNOT:
452 n := n.(*ir.BinaryExpr)
453 walk(n.X)
454 case ir.OCONVNOP:
455 n := n.(*ir.ConvExpr)
456 if n.X.Type().IsUnsafePtr() {
457 n.X = cheapExpr(n.X, init)
458 originals = append(originals, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR]))
459 }
460 }
461 }
462 walk(n.X)
463
464 cheap := cheapExpr(n, init)
465
466 slice := typecheck.MakeDotArgs(base.Pos, types.NewSlice(types.Types[types.TUNSAFEPTR]), originals)
467 slice.SetEsc(ir.EscNone)
468
469 init.Append(mkcall("checkptrArithmetic", nil, init, typecheck.ConvNop(cheap, types.Types[types.TUNSAFEPTR]), slice))
470
471
472
473 return cheap
474 }
475
View as plain text