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 5package staticinit 6 7import ( 8 "fmt" 9 "go/constant" 10 "go/token" 11 "os" 12 "strings" 13 14 "cmd/compile/internal/base" 15 "cmd/compile/internal/ir" 16 "cmd/compile/internal/reflectdata" 17 "cmd/compile/internal/staticdata" 18 "cmd/compile/internal/typecheck" 19 "cmd/compile/internal/types" 20 "cmd/internal/obj" 21 "cmd/internal/objabi" 22 "cmd/internal/src" 23) 24 25type Entry struct { 26 Xoffset int64 // struct, array only 27 Expr ir.Node // bytes of run-time computed expressions 28} 29 30type Plan struct { 31 E []Entry 32} 33 34// An Schedule is used to decompose assignment statements into 35// static and dynamic initialization parts. Static initializations are 36// handled by populating variables' linker symbol data, while dynamic 37// initializations are accumulated to be executed in order. 38type Schedule struct { 39 // Out is the ordered list of dynamic initialization 40 // statements. 41 Out []ir.Node 42 43 Plans map[ir.Node]*Plan 44 Temps map[ir.Node]*ir.Name 45 46 // seenMutation tracks whether we've seen an initialization 47 // expression that may have modified other package-scope variables 48 // within this package. 49 seenMutation bool 50} 51 52func (s *Schedule) append(n ir.Node) { 53 s.Out = append(s.Out, n) 54} 55 56// StaticInit adds an initialization statement n to the schedule. 57func (s *Schedule) StaticInit(n ir.Node) { 58 if !s.tryStaticInit(n) { 59 if base.Flag.Percent != 0 { 60 ir.Dump("StaticInit failed", n) 61 } 62 s.append(n) 63 } 64} 65 66// varToMapInit holds book-keeping state for global map initialization; 67// it records the init function created by the compiler to host the 68// initialization code for the map in question. 69var varToMapInit map[*ir.Name]*ir.Func 70 71// MapInitToVar is the inverse of VarToMapInit; it maintains a mapping 72// from a compiler-generated init function to the map the function is 73// initializing. 74var MapInitToVar map[*ir.Func]*ir.Name 75 76// recordFuncForVar establishes a mapping between global map var "v" and 77// outlined init function "fn" (and vice versa); so that we can use 78// the mappings later on to update relocations. 79func recordFuncForVar(v *ir.Name, fn *ir.Func) { 80 if varToMapInit == nil { 81 varToMapInit = make(map[*ir.Name]*ir.Func) 82 MapInitToVar = make(map[*ir.Func]*ir.Name) 83 } 84 varToMapInit[v] = fn 85 MapInitToVar[fn] = v 86} 87 88// allBlank reports whether every node in exprs is blank. 89func allBlank(exprs []ir.Node) bool { 90 for _, expr := range exprs { 91 if !ir.IsBlank(expr) { 92 return false 93 } 94 } 95 return true 96} 97 98// tryStaticInit attempts to statically execute an initialization 99// statement and reports whether it succeeded. 100func (s *Schedule) tryStaticInit(n ir.Node) bool { 101 var lhs []ir.Node 102 var rhs ir.Node 103 104 switch n.Op() { 105 default: 106 base.FatalfAt(n.Pos(), "unexpected initialization statement: %v", n) 107 case ir.OAS: 108 n := n.(*ir.AssignStmt) 109 lhs, rhs = []ir.Node{n.X}, n.Y 110 case ir.OAS2: 111 // Usually OAS2 has been rewritten to separate OASes by types2. 112 // What's left here is "var a, b = tmp1, tmp2" as a result from rewriting 113 // "var a, b = f()" that needs type conversion, which is not static. 114 n := n.(*ir.AssignListStmt) 115 for _, rhs := range n.Rhs { 116 for rhs.Op() == ir.OCONVNOP { 117 rhs = rhs.(*ir.ConvExpr).X 118 } 119 if name, ok := rhs.(*ir.Name); !ok || !name.AutoTemp() { 120 base.FatalfAt(n.Pos(), "unexpected rhs, not an autotmp: %+v", rhs) 121 } 122 } 123 return false 124 case ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV: 125 n := n.(*ir.AssignListStmt) 126 if len(n.Lhs) < 2 || len(n.Rhs) != 1 { 127 base.FatalfAt(n.Pos(), "unexpected shape for %v: %v", n.Op(), n) 128 } 129 lhs, rhs = n.Lhs, n.Rhs[0] 130 case ir.OCALLFUNC: 131 return false // outlined map init call; no mutations 132 } 133 134 if !s.seenMutation { 135 s.seenMutation = mayModifyPkgVar(rhs) 136 } 137 138 if allBlank(lhs) && !AnySideEffects(rhs) { 139 return true // discard 140 } 141 142 // Only worry about simple "l = r" assignments. The OAS2* 143 // assignments mostly necessitate dynamic execution anyway. 144 if len(lhs) > 1 { 145 return false 146 } 147 148 lno := ir.SetPos(n) 149 defer func() { base.Pos = lno }() 150 151 nam := lhs[0].(*ir.Name) 152 return s.StaticAssign(nam, 0, rhs, nam.Type()) 153} 154 155// like staticassign but we are copying an already 156// initialized value r. 157func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Type) bool { 158 if rn.Class == ir.PFUNC { 159 // TODO if roff != 0 { panic } 160 staticdata.InitAddr(l, loff, staticdata.FuncLinksym(rn)) 161 return true 162 } 163 if rn.Class != ir.PEXTERN || rn.Sym().Pkg != types.LocalPkg { 164 return false 165 } 166 if rn.Defn == nil { 167 // No explicit initialization value. Probably zeroed but perhaps 168 // supplied externally and of unknown value. 169 return false 170 } 171 if rn.Defn.Op() != ir.OAS { 172 return false 173 } 174 if rn.Type().IsString() { // perhaps overwritten by cmd/link -X (#34675) 175 return false 176 } 177 if rn.Embed != nil { 178 return false 179 } 180 orig := rn 181 r := rn.Defn.(*ir.AssignStmt).Y 182 if r == nil { 183 // types2.InitOrder doesn't include default initializers. 184 base.Fatalf("unexpected initializer: %v", rn.Defn) 185 } 186 187 // Variable may have been reassigned by a user-written function call 188 // that was invoked to initialize another global variable (#51913). 189 if s.seenMutation { 190 if base.Debug.StaticCopy != 0 { 191 base.WarnfAt(l.Pos(), "skipping static copy of %v+%v with %v", l, loff, r) 192 } 193 return false 194 } 195 196 for r.Op() == ir.OCONVNOP && !types.Identical(r.Type(), typ) { 197 r = r.(*ir.ConvExpr).X 198 } 199 200 switch r.Op() { 201 case ir.OMETHEXPR: 202 r = r.(*ir.SelectorExpr).FuncName() 203 fallthrough 204 case ir.ONAME: 205 r := r.(*ir.Name) 206 if s.staticcopy(l, loff, r, typ) { 207 return true 208 } 209 // We may have skipped past one or more OCONVNOPs, so 210 // use conv to ensure r is assignable to l (#13263). 211 dst := ir.Node(l) 212 if loff != 0 || !types.Identical(typ, l.Type()) { 213 dst = ir.NewNameOffsetExpr(base.Pos, l, loff, typ) 214 } 215 s.append(ir.NewAssignStmt(base.Pos, dst, typecheck.Conv(r, typ))) 216 return true 217 218 case ir.ONIL: 219 return true 220 221 case ir.OLITERAL: 222 if ir.IsZero(r) { 223 return true 224 } 225 staticdata.InitConst(l, loff, r, int(typ.Size())) 226 return true 227 228 case ir.OADDR: 229 r := r.(*ir.AddrExpr) 230 if a, ok := r.X.(*ir.Name); ok && a.Op() == ir.ONAME { 231 staticdata.InitAddr(l, loff, staticdata.GlobalLinksym(a)) 232 return true 233 } 234 235 case ir.OPTRLIT: 236 r := r.(*ir.AddrExpr) 237 switch r.X.Op() { 238 case ir.OARRAYLIT, ir.OSLICELIT, ir.OSTRUCTLIT, ir.OMAPLIT: 239 // copy pointer 240 staticdata.InitAddr(l, loff, staticdata.GlobalLinksym(s.Temps[r])) 241 return true 242 } 243 244 case ir.OSLICELIT: 245 r := r.(*ir.CompLitExpr) 246 // copy slice 247 staticdata.InitSlice(l, loff, staticdata.GlobalLinksym(s.Temps[r]), r.Len) 248 return true 249 250 case ir.OARRAYLIT, ir.OSTRUCTLIT: 251 r := r.(*ir.CompLitExpr) 252 p := s.Plans[r] 253 for i := range p.E { 254 e := &p.E[i] 255 typ := e.Expr.Type() 256 if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL { 257 staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(typ.Size())) 258 continue 259 } 260 x := e.Expr 261 if x.Op() == ir.OMETHEXPR { 262 x = x.(*ir.SelectorExpr).FuncName() 263 } 264 if x.Op() == ir.ONAME && s.staticcopy(l, loff+e.Xoffset, x.(*ir.Name), typ) { 265 continue 266 } 267 // Requires computation, but we're 268 // copying someone else's computation. 269 ll := ir.NewNameOffsetExpr(base.Pos, l, loff+e.Xoffset, typ) 270 rr := ir.NewNameOffsetExpr(base.Pos, orig, e.Xoffset, typ) 271 ir.SetPos(rr) 272 s.append(ir.NewAssignStmt(base.Pos, ll, rr)) 273 } 274 275 return true 276 } 277 278 return false 279} 280 281func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Type) bool { 282 if r == nil { 283 // No explicit initialization value. Either zero or supplied 284 // externally. 285 return true 286 } 287 for r.Op() == ir.OCONVNOP { 288 r = r.(*ir.ConvExpr).X 289 } 290 291 assign := func(pos src.XPos, a *ir.Name, aoff int64, v ir.Node) { 292 if s.StaticAssign(a, aoff, v, v.Type()) { 293 return 294 } 295 var lhs ir.Node 296 if ir.IsBlank(a) { 297 // Don't use NameOffsetExpr with blank (#43677). 298 lhs = ir.BlankNode 299 } else { 300 lhs = ir.NewNameOffsetExpr(pos, a, aoff, v.Type()) 301 } 302 s.append(ir.NewAssignStmt(pos, lhs, v)) 303 } 304 305 switch r.Op() { 306 case ir.ONAME: 307 r := r.(*ir.Name) 308 return s.staticcopy(l, loff, r, typ) 309 310 case ir.OMETHEXPR: 311 r := r.(*ir.SelectorExpr) 312 return s.staticcopy(l, loff, r.FuncName(), typ) 313 314 case ir.ONIL: 315 return true 316 317 case ir.OLITERAL: 318 if ir.IsZero(r) { 319 return true 320 } 321 staticdata.InitConst(l, loff, r, int(typ.Size())) 322 return true 323 324 case ir.OADDR: 325 r := r.(*ir.AddrExpr) 326 if name, offset, ok := StaticLoc(r.X); ok && name.Class == ir.PEXTERN { 327 staticdata.InitAddrOffset(l, loff, name.Linksym(), offset) 328 return true 329 } 330 fallthrough 331 332 case ir.OPTRLIT: 333 r := r.(*ir.AddrExpr) 334 switch r.X.Op() { 335 case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT: 336 // Init pointer. 337 a := StaticName(r.X.Type()) 338 339 s.Temps[r] = a 340 staticdata.InitAddr(l, loff, a.Linksym()) 341 342 // Init underlying literal. 343 assign(base.Pos, a, 0, r.X) 344 return true 345 } 346 //dump("not static ptrlit", r); 347 348 case ir.OSTR2BYTES: 349 r := r.(*ir.ConvExpr) 350 if l.Class == ir.PEXTERN && r.X.Op() == ir.OLITERAL { 351 sval := ir.StringVal(r.X) 352 staticdata.InitSliceBytes(l, loff, sval) 353 return true 354 } 355 356 case ir.OSLICELIT: 357 r := r.(*ir.CompLitExpr) 358 s.initplan(r) 359 // Init slice. 360 ta := types.NewArray(r.Type().Elem(), r.Len) 361 ta.SetNoalg(true) 362 a := StaticName(ta) 363 s.Temps[r] = a 364 staticdata.InitSlice(l, loff, a.Linksym(), r.Len) 365 // Fall through to init underlying array. 366 l = a 367 loff = 0 368 fallthrough 369 370 case ir.OARRAYLIT, ir.OSTRUCTLIT: 371 r := r.(*ir.CompLitExpr) 372 s.initplan(r) 373 374 p := s.Plans[r] 375 for i := range p.E { 376 e := &p.E[i] 377 if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL { 378 staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Size())) 379 continue 380 } 381 ir.SetPos(e.Expr) 382 assign(base.Pos, l, loff+e.Xoffset, e.Expr) 383 } 384 385 return true 386 387 case ir.OMAPLIT: 388 break 389 390 case ir.OCLOSURE: 391 r := r.(*ir.ClosureExpr) 392 if ir.IsTrivialClosure(r) { 393 if base.Debug.Closure > 0 { 394 base.WarnfAt(r.Pos(), "closure converted to global") 395 } 396 // Issue 59680: if the closure we're looking at was produced 397 // by inlining, it could be marked as hidden, which we don't 398 // want (moving the func to a static init will effectively 399 // hide it from escape analysis). Mark as non-hidden here. 400 // so that it will participated in escape analysis. 401 r.Func.SetIsHiddenClosure(false) 402 // Closures with no captured variables are globals, 403 // so the assignment can be done at link time. 404 // TODO if roff != 0 { panic } 405 staticdata.InitAddr(l, loff, staticdata.FuncLinksym(r.Func.Nname)) 406 return true 407 } 408 ir.ClosureDebugRuntimeCheck(r) 409 410 case ir.OCONVIFACE: 411 // This logic is mirrored in isStaticCompositeLiteral. 412 // If you change something here, change it there, and vice versa. 413 414 // Determine the underlying concrete type and value we are converting from. 415 r := r.(*ir.ConvExpr) 416 val := ir.Node(r) 417 for val.Op() == ir.OCONVIFACE { 418 val = val.(*ir.ConvExpr).X 419 } 420 421 if val.Type().IsInterface() { 422 // val is an interface type. 423 // If val is nil, we can statically initialize l; 424 // both words are zero and so there no work to do, so report success. 425 // If val is non-nil, we have no concrete type to record, 426 // and we won't be able to statically initialize its value, so report failure. 427 return val.Op() == ir.ONIL 428 } 429 430 if val.Type().HasShape() { 431 // See comment in cmd/compile/internal/walk/convert.go:walkConvInterface 432 return false 433 } 434 435 reflectdata.MarkTypeUsedInInterface(val.Type(), l.Linksym()) 436 437 var itab *ir.AddrExpr 438 if typ.IsEmptyInterface() { 439 itab = reflectdata.TypePtrAt(base.Pos, val.Type()) 440 } else { 441 itab = reflectdata.ITabAddrAt(base.Pos, val.Type(), typ) 442 } 443 444 // Create a copy of l to modify while we emit data. 445 446 // Emit itab, advance offset. 447 staticdata.InitAddr(l, loff, itab.X.(*ir.LinksymOffsetExpr).Linksym) 448 449 // Emit data. 450 if types.IsDirectIface(val.Type()) { 451 if val.Op() == ir.ONIL { 452 // Nil is zero, nothing to do. 453 return true 454 } 455 // Copy val directly into n. 456 ir.SetPos(val) 457 assign(base.Pos, l, loff+int64(types.PtrSize), val) 458 } else { 459 // Construct temp to hold val, write pointer to temp into n. 460 a := StaticName(val.Type()) 461 s.Temps[val] = a 462 assign(base.Pos, a, 0, val) 463 staticdata.InitAddr(l, loff+int64(types.PtrSize), a.Linksym()) 464 } 465 466 return true 467 468 case ir.OINLCALL: 469 r := r.(*ir.InlinedCallExpr) 470 return s.staticAssignInlinedCall(l, loff, r, typ) 471 } 472 473 if base.Flag.Percent != 0 { 474 ir.Dump("not static", r) 475 } 476 return false 477} 478 479func (s *Schedule) initplan(n ir.Node) { 480 if s.Plans[n] != nil { 481 return 482 } 483 p := new(Plan) 484 s.Plans[n] = p 485 switch n.Op() { 486 default: 487 base.Fatalf("initplan") 488 489 case ir.OARRAYLIT, ir.OSLICELIT: 490 n := n.(*ir.CompLitExpr) 491 var k int64 492 for _, a := range n.List { 493 if a.Op() == ir.OKEY { 494 kv := a.(*ir.KeyExpr) 495 k = typecheck.IndexConst(kv.Key) 496 if k < 0 { 497 base.Fatalf("initplan arraylit: invalid index %v", kv.Key) 498 } 499 a = kv.Value 500 } 501 s.addvalue(p, k*n.Type().Elem().Size(), a) 502 k++ 503 } 504 505 case ir.OSTRUCTLIT: 506 n := n.(*ir.CompLitExpr) 507 for _, a := range n.List { 508 if a.Op() != ir.OSTRUCTKEY { 509 base.Fatalf("initplan structlit") 510 } 511 a := a.(*ir.StructKeyExpr) 512 if a.Sym().IsBlank() { 513 continue 514 } 515 s.addvalue(p, a.Field.Offset, a.Value) 516 } 517 518 case ir.OMAPLIT: 519 n := n.(*ir.CompLitExpr) 520 for _, a := range n.List { 521 if a.Op() != ir.OKEY { 522 base.Fatalf("initplan maplit") 523 } 524 a := a.(*ir.KeyExpr) 525 s.addvalue(p, -1, a.Value) 526 } 527 } 528} 529 530func (s *Schedule) addvalue(p *Plan, xoffset int64, n ir.Node) { 531 // special case: zero can be dropped entirely 532 if ir.IsZero(n) { 533 return 534 } 535 536 // special case: inline struct and array (not slice) literals 537 if isvaluelit(n) { 538 s.initplan(n) 539 q := s.Plans[n] 540 for _, qe := range q.E { 541 // qe is a copy; we are not modifying entries in q.E 542 qe.Xoffset += xoffset 543 p.E = append(p.E, qe) 544 } 545 return 546 } 547 548 // add to plan 549 p.E = append(p.E, Entry{Xoffset: xoffset, Expr: n}) 550} 551 552func (s *Schedule) staticAssignInlinedCall(l *ir.Name, loff int64, call *ir.InlinedCallExpr, typ *types.Type) bool { 553 if base.Debug.InlStaticInit == 0 { 554 return false 555 } 556 557 // Handle the special case of an inlined call of 558 // a function body with a single return statement, 559 // which turns into a single assignment plus a goto. 560 // 561 // For example code like this: 562 // 563 // type T struct{ x int } 564 // func F(x int) *T { return &T{x} } 565 // var Global = F(400) 566 // 567 // turns into IR like this: 568 // 569 // INLCALL-init 570 // . AS2-init 571 // . . DCL # x.go:18:13 572 // . . . NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13 573 // . AS2 Def tc(1) # x.go:18:13 574 // . AS2-Lhs 575 // . . NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13 576 // . AS2-Rhs 577 // . . LITERAL-400 int tc(1) # x.go:18:14 578 // . INLMARK Index:1 # +x.go:18:13 579 // INLCALL PTR-*T tc(1) # x.go:18:13 580 // INLCALL-Body 581 // . BLOCK tc(1) # x.go:18:13 582 // . BLOCK-List 583 // . . DCL tc(1) # x.go:18:13 584 // . . . NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13 585 // . . AS2 tc(1) # x.go:18:13 586 // . . AS2-Lhs 587 // . . . NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13 588 // . . AS2-Rhs 589 // . . . INLINED RETURN ARGUMENT HERE 590 // . . GOTO p..i1 tc(1) # x.go:18:13 591 // . LABEL p..i1 # x.go:18:13 592 // INLCALL-ReturnVars 593 // . NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13 594 // 595 // In non-unified IR, the tree is slightly different: 596 // - if there are no arguments to the inlined function, 597 // the INLCALL-init omits the AS2. 598 // - the DCL inside BLOCK is on the AS2's init list, 599 // not its own statement in the top level of the BLOCK. 600 // 601 // If the init values are side-effect-free and each either only 602 // appears once in the function body or is safely repeatable, 603 // then we inline the value expressions into the return argument 604 // and then call StaticAssign to handle that copy. 605 // 606 // This handles simple cases like 607 // 608 // var myError = errors.New("mine") 609 // 610 // where errors.New is 611 // 612 // func New(text string) error { 613 // return &errorString{text} 614 // } 615 // 616 // We could make things more sophisticated but this kind of initializer 617 // is the most important case for us to get right. 618 619 init := call.Init() 620 var as2init *ir.AssignListStmt 621 if len(init) == 2 && init[0].Op() == ir.OAS2 && init[1].Op() == ir.OINLMARK { 622 as2init = init[0].(*ir.AssignListStmt) 623 } else if len(init) == 1 && init[0].Op() == ir.OINLMARK { 624 as2init = new(ir.AssignListStmt) 625 } else { 626 return false 627 } 628 if len(call.Body) != 2 || call.Body[0].Op() != ir.OBLOCK || call.Body[1].Op() != ir.OLABEL { 629 return false 630 } 631 label := call.Body[1].(*ir.LabelStmt).Label 632 block := call.Body[0].(*ir.BlockStmt) 633 list := block.List 634 var dcl *ir.Decl 635 if len(list) == 3 && list[0].Op() == ir.ODCL { 636 dcl = list[0].(*ir.Decl) 637 list = list[1:] 638 } 639 if len(list) != 2 || 640 list[0].Op() != ir.OAS2 || 641 list[1].Op() != ir.OGOTO || 642 list[1].(*ir.BranchStmt).Label != label { 643 return false 644 } 645 as2body := list[0].(*ir.AssignListStmt) 646 if dcl == nil { 647 ainit := as2body.Init() 648 if len(ainit) != 1 || ainit[0].Op() != ir.ODCL { 649 return false 650 } 651 dcl = ainit[0].(*ir.Decl) 652 } 653 if len(as2body.Lhs) != 1 || as2body.Lhs[0] != dcl.X { 654 return false 655 } 656 657 // Can't remove the parameter variables if an address is taken. 658 for _, v := range as2init.Lhs { 659 if v.(*ir.Name).Addrtaken() { 660 return false 661 } 662 } 663 // Can't move the computation of the args if they have side effects. 664 for _, r := range as2init.Rhs { 665 if AnySideEffects(r) { 666 return false 667 } 668 } 669 670 // Can only substitute arg for param if param is used 671 // at most once or is repeatable. 672 count := make(map[*ir.Name]int) 673 for _, x := range as2init.Lhs { 674 count[x.(*ir.Name)] = 0 675 } 676 677 hasNonTrivialClosure := false 678 ir.Visit(as2body.Rhs[0], func(n ir.Node) { 679 if name, ok := n.(*ir.Name); ok { 680 if c, ok := count[name]; ok { 681 count[name] = c + 1 682 } 683 } 684 if clo, ok := n.(*ir.ClosureExpr); ok { 685 hasNonTrivialClosure = hasNonTrivialClosure || !ir.IsTrivialClosure(clo) 686 } 687 }) 688 689 // If there's a non-trivial closure, it has captured the param, 690 // so we can't substitute arg for param. 691 if hasNonTrivialClosure { 692 return false 693 } 694 695 for name, c := range count { 696 if c > 1 { 697 // Check whether corresponding initializer can be repeated. 698 // Something like 1 can be; make(chan int) or &T{} cannot, 699 // because they need to evaluate to the same result in each use. 700 for i, n := range as2init.Lhs { 701 if n == name && !canRepeat(as2init.Rhs[i]) { 702 return false 703 } 704 } 705 } 706 } 707 708 // Possible static init. 709 // Build tree with args substituted for params and try it. 710 args := make(map[*ir.Name]ir.Node) 711 for i, v := range as2init.Lhs { 712 if ir.IsBlank(v) { 713 continue 714 } 715 args[v.(*ir.Name)] = as2init.Rhs[i] 716 } 717 r, ok := subst(as2body.Rhs[0], args) 718 if !ok { 719 return false 720 } 721 ok = s.StaticAssign(l, loff, r, typ) 722 723 if ok && base.Flag.Percent != 0 { 724 ir.Dump("static inlined-LEFT", l) 725 ir.Dump("static inlined-ORIG", call) 726 ir.Dump("static inlined-RIGHT", r) 727 } 728 return ok 729} 730 731// from here down is the walk analysis 732// of composite literals. 733// most of the work is to generate 734// data statements for the constant 735// part of the composite literal. 736 737var statuniqgen int // name generator for static temps 738 739// StaticName returns a name backed by a (writable) static data symbol. 740// Use readonlystaticname for read-only node. 741func StaticName(t *types.Type) *ir.Name { 742 // Don't use LookupNum; it interns the resulting string, but these are all unique. 743 sym := typecheck.Lookup(fmt.Sprintf("%s%d", obj.StaticNamePref, statuniqgen)) 744 statuniqgen++ 745 746 n := ir.NewNameAt(base.Pos, sym, t) 747 sym.Def = n 748 749 n.Class = ir.PEXTERN 750 typecheck.Target.Externs = append(typecheck.Target.Externs, n) 751 752 n.Linksym().Set(obj.AttrStatic, true) 753 return n 754} 755 756// StaticLoc returns the static address of n, if n has one, or else nil. 757func StaticLoc(n ir.Node) (name *ir.Name, offset int64, ok bool) { 758 if n == nil { 759 return nil, 0, false 760 } 761 762 switch n.Op() { 763 case ir.ONAME: 764 n := n.(*ir.Name) 765 return n, 0, true 766 767 case ir.OMETHEXPR: 768 n := n.(*ir.SelectorExpr) 769 return StaticLoc(n.FuncName()) 770 771 case ir.ODOT: 772 n := n.(*ir.SelectorExpr) 773 if name, offset, ok = StaticLoc(n.X); !ok { 774 break 775 } 776 offset += n.Offset() 777 return name, offset, true 778 779 case ir.OINDEX: 780 n := n.(*ir.IndexExpr) 781 if n.X.Type().IsSlice() { 782 break 783 } 784 if name, offset, ok = StaticLoc(n.X); !ok { 785 break 786 } 787 l := getlit(n.Index) 788 if l < 0 { 789 break 790 } 791 792 // Check for overflow. 793 if n.Type().Size() != 0 && types.MaxWidth/n.Type().Size() <= int64(l) { 794 break 795 } 796 offset += int64(l) * n.Type().Size() 797 return name, offset, true 798 } 799 800 return nil, 0, false 801} 802 803func isSideEffect(n ir.Node) bool { 804 switch n.Op() { 805 // Assume side effects unless we know otherwise. 806 default: 807 return true 808 809 // No side effects here (arguments are checked separately). 810 case ir.ONAME, 811 ir.ONONAME, 812 ir.OTYPE, 813 ir.OLITERAL, 814 ir.ONIL, 815 ir.OADD, 816 ir.OSUB, 817 ir.OOR, 818 ir.OXOR, 819 ir.OADDSTR, 820 ir.OADDR, 821 ir.OANDAND, 822 ir.OBYTES2STR, 823 ir.ORUNES2STR, 824 ir.OSTR2BYTES, 825 ir.OSTR2RUNES, 826 ir.OCAP, 827 ir.OCOMPLIT, 828 ir.OMAPLIT, 829 ir.OSTRUCTLIT, 830 ir.OARRAYLIT, 831 ir.OSLICELIT, 832 ir.OPTRLIT, 833 ir.OCONV, 834 ir.OCONVIFACE, 835 ir.OCONVNOP, 836 ir.ODOT, 837 ir.OEQ, 838 ir.ONE, 839 ir.OLT, 840 ir.OLE, 841 ir.OGT, 842 ir.OGE, 843 ir.OKEY, 844 ir.OSTRUCTKEY, 845 ir.OLEN, 846 ir.OMUL, 847 ir.OLSH, 848 ir.ORSH, 849 ir.OAND, 850 ir.OANDNOT, 851 ir.ONEW, 852 ir.ONOT, 853 ir.OBITNOT, 854 ir.OPLUS, 855 ir.ONEG, 856 ir.OOROR, 857 ir.OPAREN, 858 ir.ORUNESTR, 859 ir.OREAL, 860 ir.OIMAG, 861 ir.OCOMPLEX: 862 return false 863 864 // Only possible side effect is division by zero. 865 case ir.ODIV, ir.OMOD: 866 n := n.(*ir.BinaryExpr) 867 if n.Y.Op() != ir.OLITERAL || constant.Sign(n.Y.Val()) == 0 { 868 return true 869 } 870 871 // Only possible side effect is panic on invalid size, 872 // but many makechan and makemap use size zero, which is definitely OK. 873 case ir.OMAKECHAN, ir.OMAKEMAP: 874 n := n.(*ir.MakeExpr) 875 if !ir.IsConst(n.Len, constant.Int) || constant.Sign(n.Len.Val()) != 0 { 876 return true 877 } 878 879 // Only possible side effect is panic on invalid size. 880 // TODO(rsc): Merge with previous case (probably breaks toolstash -cmp). 881 case ir.OMAKESLICE, ir.OMAKESLICECOPY: 882 return true 883 } 884 return false 885} 886 887// AnySideEffects reports whether n contains any operations that could have observable side effects. 888func AnySideEffects(n ir.Node) bool { 889 return ir.Any(n, isSideEffect) 890} 891 892// mayModifyPkgVar reports whether expression n may modify any 893// package-scope variables declared within the current package. 894func mayModifyPkgVar(n ir.Node) bool { 895 // safeLHS reports whether the assigned-to variable lhs is either a 896 // local variable or a global from another package. 897 safeLHS := func(lhs ir.Node) bool { 898 outer := ir.OuterValue(lhs) 899 // "*p = ..." should be safe if p is a local variable. 900 // TODO: Should ir.OuterValue handle this? 901 for outer.Op() == ir.ODEREF { 902 outer = outer.(*ir.StarExpr).X 903 } 904 v, ok := outer.(*ir.Name) 905 return ok && v.Op() == ir.ONAME && !(v.Class == ir.PEXTERN && v.Sym().Pkg == types.LocalPkg) 906 } 907 908 return ir.Any(n, func(n ir.Node) bool { 909 switch n.Op() { 910 case ir.OCALLFUNC, ir.OCALLINTER: 911 return !ir.IsFuncPCIntrinsic(n.(*ir.CallExpr)) 912 913 case ir.OAPPEND, ir.OCLEAR, ir.OCOPY: 914 return true // could mutate a global array 915 916 case ir.OASOP: 917 n := n.(*ir.AssignOpStmt) 918 if !safeLHS(n.X) { 919 return true 920 } 921 922 case ir.OAS: 923 n := n.(*ir.AssignStmt) 924 if !safeLHS(n.X) { 925 return true 926 } 927 928 case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV: 929 n := n.(*ir.AssignListStmt) 930 for _, lhs := range n.Lhs { 931 if !safeLHS(lhs) { 932 return true 933 } 934 } 935 } 936 937 return false 938 }) 939} 940 941// canRepeat reports whether executing n multiple times has the same effect as 942// assigning n to a single variable and using that variable multiple times. 943func canRepeat(n ir.Node) bool { 944 bad := func(n ir.Node) bool { 945 if isSideEffect(n) { 946 return true 947 } 948 switch n.Op() { 949 case ir.OMAKECHAN, 950 ir.OMAKEMAP, 951 ir.OMAKESLICE, 952 ir.OMAKESLICECOPY, 953 ir.OMAPLIT, 954 ir.ONEW, 955 ir.OPTRLIT, 956 ir.OSLICELIT, 957 ir.OSTR2BYTES, 958 ir.OSTR2RUNES: 959 return true 960 } 961 return false 962 } 963 return !ir.Any(n, bad) 964} 965 966func getlit(lit ir.Node) int { 967 if ir.IsSmallIntConst(lit) { 968 return int(ir.Int64Val(lit)) 969 } 970 return -1 971} 972 973func isvaluelit(n ir.Node) bool { 974 return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT 975} 976 977func subst(n ir.Node, m map[*ir.Name]ir.Node) (ir.Node, bool) { 978 valid := true 979 var edit func(ir.Node) ir.Node 980 edit = func(x ir.Node) ir.Node { 981 switch x.Op() { 982 case ir.ONAME: 983 x := x.(*ir.Name) 984 if v, ok := m[x]; ok { 985 return ir.DeepCopy(v.Pos(), v) 986 } 987 return x 988 case ir.ONONAME, ir.OLITERAL, ir.ONIL, ir.OTYPE: 989 return x 990 } 991 x = ir.Copy(x) 992 ir.EditChildrenWithHidden(x, edit) 993 994 // TODO: handle more operations, see details discussion in go.dev/cl/466277. 995 switch x.Op() { 996 case ir.OCONV: 997 x := x.(*ir.ConvExpr) 998 if x.X.Op() == ir.OLITERAL { 999 if x, ok := truncate(x.X, x.Type()); ok { 1000 return x 1001 } 1002 valid = false 1003 return x 1004 } 1005 case ir.OADDSTR: 1006 return addStr(x.(*ir.AddStringExpr)) 1007 } 1008 return x 1009 } 1010 n = edit(n) 1011 return n, valid 1012} 1013 1014// truncate returns the result of force converting c to type t, 1015// truncating its value as needed, like a conversion of a variable. 1016// If the conversion is too difficult, truncate returns nil, false. 1017func truncate(c ir.Node, t *types.Type) (ir.Node, bool) { 1018 ct := c.Type() 1019 cv := c.Val() 1020 if ct.Kind() != t.Kind() { 1021 switch { 1022 default: 1023 // Note: float -> float/integer and complex -> complex are valid but subtle. 1024 // For example a float32(float64 1e300) evaluates to +Inf at runtime 1025 // and the compiler doesn't have any concept of +Inf, so that would 1026 // have to be left for runtime code evaluation. 1027 // For now 1028 return nil, false 1029 1030 case ct.IsInteger() && t.IsInteger(): 1031 // truncate or sign extend 1032 bits := t.Size() * 8 1033 cv = constant.BinaryOp(cv, token.AND, constant.MakeUint64(1<<bits-1)) 1034 if t.IsSigned() && constant.Compare(cv, token.GEQ, constant.MakeUint64(1<<(bits-1))) { 1035 cv = constant.BinaryOp(cv, token.OR, constant.MakeInt64(-1<<(bits-1))) 1036 } 1037 } 1038 } 1039 c = ir.NewConstExpr(cv, c) 1040 c.SetType(t) 1041 return c, true 1042} 1043 1044func addStr(n *ir.AddStringExpr) ir.Node { 1045 // Merge adjacent constants in the argument list. 1046 s := n.List 1047 need := 0 1048 for i := 0; i < len(s); i++ { 1049 if i == 0 || !ir.IsConst(s[i-1], constant.String) || !ir.IsConst(s[i], constant.String) { 1050 // Can't merge s[i] into s[i-1]; need a slot in the list. 1051 need++ 1052 } 1053 } 1054 if need == len(s) { 1055 return n 1056 } 1057 if need == 1 { 1058 var strs []string 1059 for _, c := range s { 1060 strs = append(strs, ir.StringVal(c)) 1061 } 1062 return ir.NewConstExpr(constant.MakeString(strings.Join(strs, "")), n) 1063 } 1064 newList := make([]ir.Node, 0, need) 1065 for i := 0; i < len(s); i++ { 1066 if ir.IsConst(s[i], constant.String) && i+1 < len(s) && ir.IsConst(s[i+1], constant.String) { 1067 // merge from i up to but not including i2 1068 var strs []string 1069 i2 := i 1070 for i2 < len(s) && ir.IsConst(s[i2], constant.String) { 1071 strs = append(strs, ir.StringVal(s[i2])) 1072 i2++ 1073 } 1074 1075 newList = append(newList, ir.NewConstExpr(constant.MakeString(strings.Join(strs, "")), s[i])) 1076 i = i2 - 1 1077 } else { 1078 newList = append(newList, s[i]) 1079 } 1080 } 1081 1082 nn := ir.Copy(n).(*ir.AddStringExpr) 1083 nn.List = newList 1084 return nn 1085} 1086 1087const wrapGlobalMapInitSizeThreshold = 20 1088 1089// tryWrapGlobalInit returns a new outlined function to contain global 1090// initializer statement n, if possible and worthwhile. Otherwise, it 1091// returns nil. 1092// 1093// Currently, it outlines map assignment statements with large, 1094// side-effect-free RHS expressions. 1095func tryWrapGlobalInit(n ir.Node) *ir.Func { 1096 // Look for "X = ..." where X has map type. 1097 // FIXME: might also be worth trying to look for cases where 1098 // the LHS is of interface type but RHS is map type. 1099 if n.Op() != ir.OAS { 1100 return nil 1101 } 1102 as := n.(*ir.AssignStmt) 1103 if ir.IsBlank(as.X) || as.X.Op() != ir.ONAME { 1104 return nil 1105 } 1106 nm := as.X.(*ir.Name) 1107 if !nm.Type().IsMap() { 1108 return nil 1109 } 1110 1111 // Determine size of RHS. 1112 rsiz := 0 1113 ir.Any(as.Y, func(n ir.Node) bool { 1114 rsiz++ 1115 return false 1116 }) 1117 if base.Debug.WrapGlobalMapDbg > 0 { 1118 fmt.Fprintf(os.Stderr, "=-= mapassign %s %v rhs size %d\n", 1119 base.Ctxt.Pkgpath, n, rsiz) 1120 } 1121 1122 // Reject smaller candidates if not in stress mode. 1123 if rsiz < wrapGlobalMapInitSizeThreshold && base.Debug.WrapGlobalMapCtl != 2 { 1124 if base.Debug.WrapGlobalMapDbg > 1 { 1125 fmt.Fprintf(os.Stderr, "=-= skipping %v size too small at %d\n", 1126 nm, rsiz) 1127 } 1128 return nil 1129 } 1130 1131 // Reject right hand sides with side effects. 1132 if AnySideEffects(as.Y) { 1133 if base.Debug.WrapGlobalMapDbg > 0 { 1134 fmt.Fprintf(os.Stderr, "=-= rejected %v due to side effects\n", nm) 1135 } 1136 return nil 1137 } 1138 1139 if base.Debug.WrapGlobalMapDbg > 1 { 1140 fmt.Fprintf(os.Stderr, "=-= committed for: %+v\n", n) 1141 } 1142 1143 // Create a new function that will (eventually) have this form: 1144 // 1145 // func map.init.%d() { 1146 // globmapvar = <map initialization> 1147 // } 1148 // 1149 // Note: cmd/link expects the function name to contain "map.init". 1150 minitsym := typecheck.LookupNum("map.init.", mapinitgen) 1151 mapinitgen++ 1152 1153 fn := ir.NewFunc(n.Pos(), n.Pos(), minitsym, types.NewSignature(nil, nil, nil)) 1154 fn.SetInlinabilityChecked(true) // suppress inlining (which would defeat the point) 1155 typecheck.DeclFunc(fn) 1156 if base.Debug.WrapGlobalMapDbg > 0 { 1157 fmt.Fprintf(os.Stderr, "=-= generated func is %v\n", fn) 1158 } 1159 1160 // NB: we're relying on this phase being run before inlining; 1161 // if for some reason we need to move it after inlining, we'll 1162 // need code here that relocates or duplicates inline temps. 1163 1164 // Insert assignment into function body; mark body finished. 1165 fn.Body = []ir.Node{as} 1166 typecheck.FinishFuncBody() 1167 1168 if base.Debug.WrapGlobalMapDbg > 1 { 1169 fmt.Fprintf(os.Stderr, "=-= mapvar is %v\n", nm) 1170 fmt.Fprintf(os.Stderr, "=-= newfunc is %+v\n", fn) 1171 } 1172 1173 recordFuncForVar(nm, fn) 1174 1175 return fn 1176} 1177 1178// mapinitgen is a counter used to uniquify compiler-generated 1179// map init functions. 1180var mapinitgen int 1181 1182// AddKeepRelocations adds a dummy "R_KEEP" relocation from each 1183// global map variable V to its associated outlined init function. 1184// These relocation ensure that if the map var itself is determined to 1185// be reachable at link time, we also mark the init function as 1186// reachable. 1187func AddKeepRelocations() { 1188 if varToMapInit == nil { 1189 return 1190 } 1191 for k, v := range varToMapInit { 1192 // Add R_KEEP relocation from map to init function. 1193 fs := v.Linksym() 1194 if fs == nil { 1195 base.Fatalf("bad: func %v has no linksym", v) 1196 } 1197 vs := k.Linksym() 1198 if vs == nil { 1199 base.Fatalf("bad: mapvar %v has no linksym", k) 1200 } 1201 r := obj.Addrel(vs) 1202 r.Sym = fs 1203 r.Type = objabi.R_KEEP 1204 if base.Debug.WrapGlobalMapDbg > 1 { 1205 fmt.Fprintf(os.Stderr, "=-= add R_KEEP relo from %s to %s\n", 1206 vs.Name, fs.Name) 1207 } 1208 } 1209 varToMapInit = nil 1210} 1211 1212// OutlineMapInits replaces global map initializers with outlined 1213// calls to separate "map init" functions (where possible and 1214// profitable), to facilitate better dead-code elimination by the 1215// linker. 1216func OutlineMapInits(fn *ir.Func) { 1217 if base.Debug.WrapGlobalMapCtl == 1 { 1218 return 1219 } 1220 1221 outlined := 0 1222 for i, stmt := range fn.Body { 1223 // Attempt to outline stmt. If successful, replace it with a call 1224 // to the returned wrapper function. 1225 if wrapperFn := tryWrapGlobalInit(stmt); wrapperFn != nil { 1226 ir.WithFunc(fn, func() { 1227 fn.Body[i] = typecheck.Call(stmt.Pos(), wrapperFn.Nname, nil, false) 1228 }) 1229 outlined++ 1230 } 1231 } 1232 1233 if base.Debug.WrapGlobalMapDbg > 1 { 1234 fmt.Fprintf(os.Stderr, "=-= outlined %v map initializations\n", outlined) 1235 } 1236} 1237