github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/walk/walk.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package walk 6 7 import ( 8 "fmt" 9 10 "github.com/go-asm/go/cmd/compile/base" 11 "github.com/go-asm/go/cmd/compile/ir" 12 "github.com/go-asm/go/cmd/compile/reflectdata" 13 "github.com/go-asm/go/cmd/compile/ssagen" 14 "github.com/go-asm/go/cmd/compile/typecheck" 15 "github.com/go-asm/go/cmd/compile/types" 16 "github.com/go-asm/go/cmd/src" 17 ) 18 19 // The constant is known to runtime. 20 const tmpstringbufsize = 32 21 22 func Walk(fn *ir.Func) { 23 ir.CurFunc = fn 24 errorsBefore := base.Errors() 25 order(fn) 26 if base.Errors() > errorsBefore { 27 return 28 } 29 30 if base.Flag.W != 0 { 31 s := fmt.Sprintf("\nbefore walk %v", ir.CurFunc.Sym()) 32 ir.DumpList(s, ir.CurFunc.Body) 33 } 34 35 lno := base.Pos 36 37 base.Pos = lno 38 if base.Errors() > errorsBefore { 39 return 40 } 41 walkStmtList(ir.CurFunc.Body) 42 if base.Flag.W != 0 { 43 s := fmt.Sprintf("after walk %v", ir.CurFunc.Sym()) 44 ir.DumpList(s, ir.CurFunc.Body) 45 } 46 47 // Eagerly compute sizes of all variables for SSA. 48 for _, n := range fn.Dcl { 49 types.CalcSize(n.Type()) 50 } 51 } 52 53 // walkRecv walks an ORECV node. 54 func walkRecv(n *ir.UnaryExpr) ir.Node { 55 if n.Typecheck() == 0 { 56 base.Fatalf("missing typecheck: %+v", n) 57 } 58 init := ir.TakeInit(n) 59 60 n.X = walkExpr(n.X, &init) 61 call := walkExpr(mkcall1(chanfn("chanrecv1", 2, n.X.Type()), nil, &init, n.X, typecheck.NodNil()), &init) 62 return ir.InitExpr(init, call) 63 } 64 65 func convas(n *ir.AssignStmt, init *ir.Nodes) *ir.AssignStmt { 66 if n.Op() != ir.OAS { 67 base.Fatalf("convas: not OAS %v", n.Op()) 68 } 69 n.SetTypecheck(1) 70 71 if n.X == nil || n.Y == nil { 72 return n 73 } 74 75 lt := n.X.Type() 76 rt := n.Y.Type() 77 if lt == nil || rt == nil { 78 return n 79 } 80 81 if ir.IsBlank(n.X) { 82 n.Y = typecheck.DefaultLit(n.Y, nil) 83 return n 84 } 85 86 if !types.Identical(lt, rt) { 87 n.Y = typecheck.AssignConv(n.Y, lt, "assignment") 88 n.Y = walkExpr(n.Y, init) 89 } 90 types.CalcSize(n.Y.Type()) 91 92 return n 93 } 94 95 func vmkcall(fn ir.Node, t *types.Type, init *ir.Nodes, va []ir.Node) *ir.CallExpr { 96 if init == nil { 97 base.Fatalf("mkcall with nil init: %v", fn) 98 } 99 if fn.Type() == nil || fn.Type().Kind() != types.TFUNC { 100 base.Fatalf("mkcall %v %v", fn, fn.Type()) 101 } 102 103 n := fn.Type().NumParams() 104 if n != len(va) { 105 base.Fatalf("vmkcall %v needs %v args got %v", fn, n, len(va)) 106 } 107 108 call := typecheck.Call(base.Pos, fn, va, false).(*ir.CallExpr) 109 call.SetType(t) 110 return walkExpr(call, init).(*ir.CallExpr) 111 } 112 113 func mkcall(name string, t *types.Type, init *ir.Nodes, args ...ir.Node) *ir.CallExpr { 114 return vmkcall(typecheck.LookupRuntime(name), t, init, args) 115 } 116 117 func mkcallstmt(name string, args ...ir.Node) ir.Node { 118 return mkcallstmt1(typecheck.LookupRuntime(name), args...) 119 } 120 121 func mkcall1(fn ir.Node, t *types.Type, init *ir.Nodes, args ...ir.Node) *ir.CallExpr { 122 return vmkcall(fn, t, init, args) 123 } 124 125 func mkcallstmt1(fn ir.Node, args ...ir.Node) ir.Node { 126 var init ir.Nodes 127 n := vmkcall(fn, nil, &init, args) 128 if len(init) == 0 { 129 return n 130 } 131 init.Append(n) 132 return ir.NewBlockStmt(n.Pos(), init) 133 } 134 135 func chanfn(name string, n int, t *types.Type) ir.Node { 136 if !t.IsChan() { 137 base.Fatalf("chanfn %v", t) 138 } 139 switch n { 140 case 1: 141 return typecheck.LookupRuntime(name, t.Elem()) 142 case 2: 143 return typecheck.LookupRuntime(name, t.Elem(), t.Elem()) 144 } 145 base.Fatalf("chanfn %d", n) 146 return nil 147 } 148 149 func mapfn(name string, t *types.Type, isfat bool) ir.Node { 150 if !t.IsMap() { 151 base.Fatalf("mapfn %v", t) 152 } 153 if mapfast(t) == mapslow || isfat { 154 return typecheck.LookupRuntime(name, t.Key(), t.Elem(), t.Key(), t.Elem()) 155 } 156 return typecheck.LookupRuntime(name, t.Key(), t.Elem(), t.Elem()) 157 } 158 159 func mapfndel(name string, t *types.Type) ir.Node { 160 if !t.IsMap() { 161 base.Fatalf("mapfn %v", t) 162 } 163 if mapfast(t) == mapslow { 164 return typecheck.LookupRuntime(name, t.Key(), t.Elem(), t.Key()) 165 } 166 return typecheck.LookupRuntime(name, t.Key(), t.Elem()) 167 } 168 169 const ( 170 mapslow = iota 171 mapfast32 172 mapfast32ptr 173 mapfast64 174 mapfast64ptr 175 mapfaststr 176 nmapfast 177 ) 178 179 type mapnames [nmapfast]string 180 181 func mkmapnames(base string, ptr string) mapnames { 182 return mapnames{base, base + "_fast32", base + "_fast32" + ptr, base + "_fast64", base + "_fast64" + ptr, base + "_faststr"} 183 } 184 185 var mapaccess1 = mkmapnames("mapaccess1", "") 186 var mapaccess2 = mkmapnames("mapaccess2", "") 187 var mapassign = mkmapnames("mapassign", "ptr") 188 var mapdelete = mkmapnames("mapdelete", "") 189 190 func mapfast(t *types.Type) int { 191 // Check runtime/map.go:maxElemSize before changing. 192 if t.Elem().Size() > 128 { 193 return mapslow 194 } 195 switch reflectdata.AlgType(t.Key()) { 196 case types.AMEM32: 197 if !t.Key().HasPointers() { 198 return mapfast32 199 } 200 if types.PtrSize == 4 { 201 return mapfast32ptr 202 } 203 base.Fatalf("small pointer %v", t.Key()) 204 case types.AMEM64: 205 if !t.Key().HasPointers() { 206 return mapfast64 207 } 208 if types.PtrSize == 8 { 209 return mapfast64ptr 210 } 211 // Two-word object, at least one of which is a pointer. 212 // Use the slow path. 213 case types.ASTRING: 214 return mapfaststr 215 } 216 return mapslow 217 } 218 219 func walkAppendArgs(n *ir.CallExpr, init *ir.Nodes) { 220 walkExprListSafe(n.Args, init) 221 222 // walkExprListSafe will leave OINDEX (s[n]) alone if both s 223 // and n are name or literal, but those may index the slice we're 224 // modifying here. Fix explicitly. 225 ls := n.Args 226 for i1, n1 := range ls { 227 ls[i1] = cheapExpr(n1, init) 228 } 229 } 230 231 // appendWalkStmt typechecks and walks stmt and then appends it to init. 232 func appendWalkStmt(init *ir.Nodes, stmt ir.Node) { 233 op := stmt.Op() 234 n := typecheck.Stmt(stmt) 235 if op == ir.OAS || op == ir.OAS2 { 236 // If the assignment has side effects, walkExpr will append them 237 // directly to init for us, while walkStmt will wrap it in an OBLOCK. 238 // We need to append them directly. 239 // TODO(rsc): Clean this up. 240 n = walkExpr(n, init) 241 } else { 242 n = walkStmt(n) 243 } 244 init.Append(n) 245 } 246 247 // The max number of defers in a function using open-coded defers. We enforce this 248 // limit because the deferBits bitmask is currently a single byte (to minimize code size) 249 const maxOpenDefers = 8 250 251 // backingArrayPtrLen extracts the pointer and length from a slice or string. 252 // This constructs two nodes referring to n, so n must be a cheapExpr. 253 func backingArrayPtrLen(n ir.Node) (ptr, length ir.Node) { 254 var init ir.Nodes 255 c := cheapExpr(n, &init) 256 if c != n || len(init) != 0 { 257 base.Fatalf("backingArrayPtrLen not cheap: %v", n) 258 } 259 ptr = ir.NewUnaryExpr(base.Pos, ir.OSPTR, n) 260 if n.Type().IsString() { 261 ptr.SetType(types.Types[types.TUINT8].PtrTo()) 262 } else { 263 ptr.SetType(n.Type().Elem().PtrTo()) 264 } 265 ptr.SetTypecheck(1) 266 length = ir.NewUnaryExpr(base.Pos, ir.OLEN, n) 267 length.SetType(types.Types[types.TINT]) 268 length.SetTypecheck(1) 269 return ptr, length 270 } 271 272 // mayCall reports whether evaluating expression n may require 273 // function calls, which could clobber function call arguments/results 274 // currently on the stack. 275 func mayCall(n ir.Node) bool { 276 // When instrumenting, any expression might require function calls. 277 if base.Flag.Cfg.Instrumenting { 278 return true 279 } 280 281 isSoftFloat := func(typ *types.Type) bool { 282 return types.IsFloat[typ.Kind()] || types.IsComplex[typ.Kind()] 283 } 284 285 return ir.Any(n, func(n ir.Node) bool { 286 // walk should have already moved any Init blocks off of 287 // expressions. 288 if len(n.Init()) != 0 { 289 base.FatalfAt(n.Pos(), "mayCall %+v", n) 290 } 291 292 switch n.Op() { 293 default: 294 base.FatalfAt(n.Pos(), "mayCall %+v", n) 295 296 case ir.OCALLFUNC, ir.OCALLINTER, 297 ir.OUNSAFEADD, ir.OUNSAFESLICE: 298 return true 299 300 case ir.OINDEX, ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR, 301 ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODYNAMICDOTTYPE, ir.ODIV, ir.OMOD, 302 ir.OSLICE2ARR, ir.OSLICE2ARRPTR: 303 // These ops might panic, make sure they are done 304 // before we start marshaling args for a call. See issue 16760. 305 return true 306 307 case ir.OANDAND, ir.OOROR: 308 n := n.(*ir.LogicalExpr) 309 // The RHS expression may have init statements that 310 // should only execute conditionally, and so cannot be 311 // pulled out to the top-level init list. We could try 312 // to be more precise here. 313 return len(n.Y.Init()) != 0 314 315 // When using soft-float, these ops might be rewritten to function calls 316 // so we ensure they are evaluated first. 317 case ir.OADD, ir.OSUB, ir.OMUL, ir.ONEG: 318 return ssagen.Arch.SoftFloat && isSoftFloat(n.Type()) 319 case ir.OLT, ir.OEQ, ir.ONE, ir.OLE, ir.OGE, ir.OGT: 320 n := n.(*ir.BinaryExpr) 321 return ssagen.Arch.SoftFloat && isSoftFloat(n.X.Type()) 322 case ir.OCONV: 323 n := n.(*ir.ConvExpr) 324 return ssagen.Arch.SoftFloat && (isSoftFloat(n.Type()) || isSoftFloat(n.X.Type())) 325 326 case ir.OMIN, ir.OMAX: 327 // string or float requires runtime call, see (*ssagen.state).minmax method. 328 return n.Type().IsString() || n.Type().IsFloat() 329 330 case ir.OLITERAL, ir.ONIL, ir.ONAME, ir.OLINKSYMOFFSET, ir.OMETHEXPR, 331 ir.OAND, ir.OANDNOT, ir.OLSH, ir.OOR, ir.ORSH, ir.OXOR, ir.OCOMPLEX, ir.OMAKEFACE, 332 ir.OADDR, ir.OBITNOT, ir.ONOT, ir.OPLUS, 333 ir.OCAP, ir.OIMAG, ir.OLEN, ir.OREAL, 334 ir.OCONVNOP, ir.ODOT, 335 ir.OCFUNC, ir.OIDATA, ir.OITAB, ir.OSPTR, 336 ir.OBYTES2STRTMP, ir.OGETG, ir.OGETCALLERPC, ir.OGETCALLERSP, ir.OSLICEHEADER, ir.OSTRINGHEADER: 337 // ok: operations that don't require function calls. 338 // Expand as needed. 339 } 340 341 return false 342 }) 343 } 344 345 // itabType loads the _type field from a runtime.itab struct. 346 func itabType(itab ir.Node) ir.Node { 347 if itabTypeField == nil { 348 // runtime.itab's _type field 349 itabTypeField = runtimeField("_type", int64(types.PtrSize), types.NewPtr(types.Types[types.TUINT8])) 350 } 351 return boundedDotPtr(base.Pos, itab, itabTypeField) 352 } 353 354 var itabTypeField *types.Field 355 356 // boundedDotPtr returns a selector expression representing ptr.field 357 // and omits nil-pointer checks for ptr. 358 func boundedDotPtr(pos src.XPos, ptr ir.Node, field *types.Field) *ir.SelectorExpr { 359 sel := ir.NewSelectorExpr(pos, ir.ODOTPTR, ptr, field.Sym) 360 sel.Selection = field 361 sel.SetType(field.Type) 362 sel.SetTypecheck(1) 363 sel.SetBounded(true) // guaranteed not to fault 364 return sel 365 } 366 367 func runtimeField(name string, offset int64, typ *types.Type) *types.Field { 368 f := types.NewField(src.NoXPos, ir.Pkgs.Runtime.Lookup(name), typ) 369 f.Offset = offset 370 return f 371 } 372 373 // ifaceData loads the data field from an interface. 374 // The concrete type must be known to have type t. 375 // It follows the pointer if !IsDirectIface(t). 376 func ifaceData(pos src.XPos, n ir.Node, t *types.Type) ir.Node { 377 if t.IsInterface() { 378 base.Fatalf("ifaceData interface: %v", t) 379 } 380 ptr := ir.NewUnaryExpr(pos, ir.OIDATA, n) 381 if types.IsDirectIface(t) { 382 ptr.SetType(t) 383 ptr.SetTypecheck(1) 384 return ptr 385 } 386 ptr.SetType(types.NewPtr(t)) 387 ptr.SetTypecheck(1) 388 ind := ir.NewStarExpr(pos, ptr) 389 ind.SetType(t) 390 ind.SetTypecheck(1) 391 ind.SetBounded(true) 392 return ind 393 }