github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/staticinit/sched.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 staticinit 6 7 import ( 8 "fmt" 9 "github.com/bir3/gocompiler/src/go/constant" 10 "github.com/bir3/gocompiler/src/go/token" 11 12 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 13 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 14 "github.com/bir3/gocompiler/src/cmd/compile/internal/reflectdata" 15 "github.com/bir3/gocompiler/src/cmd/compile/internal/staticdata" 16 "github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck" 17 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 18 "github.com/bir3/gocompiler/src/cmd/internal/obj" 19 "github.com/bir3/gocompiler/src/cmd/internal/src" 20 ) 21 22 type Entry struct { 23 Xoffset int64 // struct, array only 24 Expr ir.Node // bytes of run-time computed expressions 25 } 26 27 type Plan struct { 28 E []Entry 29 } 30 31 // An Schedule is used to decompose assignment statements into 32 // static and dynamic initialization parts. Static initializations are 33 // handled by populating variables' linker symbol data, while dynamic 34 // initializations are accumulated to be executed in order. 35 type Schedule struct { 36 // Out is the ordered list of dynamic initialization 37 // statements. 38 Out []ir.Node 39 40 Plans map[ir.Node]*Plan 41 Temps map[ir.Node]*ir.Name 42 } 43 44 func (s *Schedule) append(n ir.Node) { 45 s.Out = append(s.Out, n) 46 } 47 48 // StaticInit adds an initialization statement n to the schedule. 49 func (s *Schedule) StaticInit(n ir.Node) { 50 if !s.tryStaticInit(n) { 51 if base.Flag.Percent != 0 { 52 ir.Dump("StaticInit failed", n) 53 } 54 s.append(n) 55 } 56 } 57 58 // tryStaticInit attempts to statically execute an initialization 59 // statement and reports whether it succeeded. 60 func (s *Schedule) tryStaticInit(nn ir.Node) bool { 61 // Only worry about simple "l = r" assignments. Multiple 62 // variable/expression OAS2 assignments have already been 63 // replaced by multiple simple OAS assignments, and the other 64 // OAS2* assignments mostly necessitate dynamic execution 65 // anyway. 66 if nn.Op() != ir.OAS { 67 return false 68 } 69 n := nn.(*ir.AssignStmt) 70 if ir.IsBlank(n.X) && !AnySideEffects(n.Y) { 71 // Discard. 72 return true 73 } 74 lno := ir.SetPos(n) 75 defer func() { base.Pos = lno }() 76 nam := n.X.(*ir.Name) 77 return s.StaticAssign(nam, 0, n.Y, nam.Type()) 78 } 79 80 // like staticassign but we are copying an already 81 // initialized value r. 82 func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Type) bool { 83 if rn.Class == ir.PFUNC { 84 // TODO if roff != 0 { panic } 85 staticdata.InitAddr(l, loff, staticdata.FuncLinksym(rn)) 86 return true 87 } 88 if rn.Class != ir.PEXTERN || rn.Sym().Pkg != types.LocalPkg { 89 return false 90 } 91 if rn.Defn.Op() != ir.OAS { 92 return false 93 } 94 if rn.Type().IsString() { // perhaps overwritten by cmd/link -X (#34675) 95 return false 96 } 97 if rn.Embed != nil { 98 return false 99 } 100 orig := rn 101 r := rn.Defn.(*ir.AssignStmt).Y 102 if r == nil { 103 // No explicit initialization value. Probably zeroed but perhaps 104 // supplied externally and of unknown value. 105 return false 106 } 107 108 for r.Op() == ir.OCONVNOP && !types.Identical(r.Type(), typ) { 109 r = r.(*ir.ConvExpr).X 110 } 111 112 switch r.Op() { 113 case ir.OMETHEXPR: 114 r = r.(*ir.SelectorExpr).FuncName() 115 fallthrough 116 case ir.ONAME: 117 r := r.(*ir.Name) 118 if s.staticcopy(l, loff, r, typ) { 119 return true 120 } 121 // We may have skipped past one or more OCONVNOPs, so 122 // use conv to ensure r is assignable to l (#13263). 123 dst := ir.Node(l) 124 if loff != 0 || !types.Identical(typ, l.Type()) { 125 dst = ir.NewNameOffsetExpr(base.Pos, l, loff, typ) 126 } 127 s.append(ir.NewAssignStmt(base.Pos, dst, typecheck.Conv(r, typ))) 128 return true 129 130 case ir.ONIL: 131 return true 132 133 case ir.OLITERAL: 134 if ir.IsZero(r) { 135 return true 136 } 137 staticdata.InitConst(l, loff, r, int(typ.Size())) 138 return true 139 140 case ir.OADDR: 141 r := r.(*ir.AddrExpr) 142 if a, ok := r.X.(*ir.Name); ok && a.Op() == ir.ONAME { 143 staticdata.InitAddr(l, loff, staticdata.GlobalLinksym(a)) 144 return true 145 } 146 147 case ir.OPTRLIT: 148 r := r.(*ir.AddrExpr) 149 switch r.X.Op() { 150 case ir.OARRAYLIT, ir.OSLICELIT, ir.OSTRUCTLIT, ir.OMAPLIT: 151 // copy pointer 152 staticdata.InitAddr(l, loff, staticdata.GlobalLinksym(s.Temps[r])) 153 return true 154 } 155 156 case ir.OSLICELIT: 157 r := r.(*ir.CompLitExpr) 158 // copy slice 159 staticdata.InitSlice(l, loff, staticdata.GlobalLinksym(s.Temps[r]), r.Len) 160 return true 161 162 case ir.OARRAYLIT, ir.OSTRUCTLIT: 163 r := r.(*ir.CompLitExpr) 164 p := s.Plans[r] 165 for i := range p.E { 166 e := &p.E[i] 167 typ := e.Expr.Type() 168 if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL { 169 staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(typ.Size())) 170 continue 171 } 172 x := e.Expr 173 if x.Op() == ir.OMETHEXPR { 174 x = x.(*ir.SelectorExpr).FuncName() 175 } 176 if x.Op() == ir.ONAME && s.staticcopy(l, loff+e.Xoffset, x.(*ir.Name), typ) { 177 continue 178 } 179 // Requires computation, but we're 180 // copying someone else's computation. 181 ll := ir.NewNameOffsetExpr(base.Pos, l, loff+e.Xoffset, typ) 182 rr := ir.NewNameOffsetExpr(base.Pos, orig, e.Xoffset, typ) 183 ir.SetPos(rr) 184 s.append(ir.NewAssignStmt(base.Pos, ll, rr)) 185 } 186 187 return true 188 } 189 190 return false 191 } 192 193 func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Type) bool { 194 if r == nil { 195 // No explicit initialization value. Either zero or supplied 196 // externally. 197 return true 198 } 199 for r.Op() == ir.OCONVNOP { 200 r = r.(*ir.ConvExpr).X 201 } 202 203 assign := func(pos src.XPos, a *ir.Name, aoff int64, v ir.Node) { 204 if s.StaticAssign(a, aoff, v, v.Type()) { 205 return 206 } 207 var lhs ir.Node 208 if ir.IsBlank(a) { 209 // Don't use NameOffsetExpr with blank (#43677). 210 lhs = ir.BlankNode 211 } else { 212 lhs = ir.NewNameOffsetExpr(pos, a, aoff, v.Type()) 213 } 214 s.append(ir.NewAssignStmt(pos, lhs, v)) 215 } 216 217 switch r.Op() { 218 case ir.ONAME: 219 r := r.(*ir.Name) 220 return s.staticcopy(l, loff, r, typ) 221 222 case ir.OMETHEXPR: 223 r := r.(*ir.SelectorExpr) 224 return s.staticcopy(l, loff, r.FuncName(), typ) 225 226 case ir.ONIL: 227 return true 228 229 case ir.OLITERAL: 230 if ir.IsZero(r) { 231 return true 232 } 233 staticdata.InitConst(l, loff, r, int(typ.Size())) 234 return true 235 236 case ir.OADDR: 237 r := r.(*ir.AddrExpr) 238 if name, offset, ok := StaticLoc(r.X); ok && name.Class == ir.PEXTERN { 239 staticdata.InitAddrOffset(l, loff, name.Linksym(), offset) 240 return true 241 } 242 fallthrough 243 244 case ir.OPTRLIT: 245 r := r.(*ir.AddrExpr) 246 switch r.X.Op() { 247 case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT: 248 // Init pointer. 249 a := StaticName(r.X.Type()) 250 251 s.Temps[r] = a 252 staticdata.InitAddr(l, loff, a.Linksym()) 253 254 // Init underlying literal. 255 assign(base.Pos, a, 0, r.X) 256 return true 257 } 258 //dump("not static ptrlit", r); 259 260 case ir.OSTR2BYTES: 261 r := r.(*ir.ConvExpr) 262 if l.Class == ir.PEXTERN && r.X.Op() == ir.OLITERAL { 263 sval := ir.StringVal(r.X) 264 staticdata.InitSliceBytes(l, loff, sval) 265 return true 266 } 267 268 case ir.OSLICELIT: 269 r := r.(*ir.CompLitExpr) 270 s.initplan(r) 271 // Init slice. 272 ta := types.NewArray(r.Type().Elem(), r.Len) 273 ta.SetNoalg(true) 274 a := StaticName(ta) 275 s.Temps[r] = a 276 staticdata.InitSlice(l, loff, a.Linksym(), r.Len) 277 // Fall through to init underlying array. 278 l = a 279 loff = 0 280 fallthrough 281 282 case ir.OARRAYLIT, ir.OSTRUCTLIT: 283 r := r.(*ir.CompLitExpr) 284 s.initplan(r) 285 286 p := s.Plans[r] 287 for i := range p.E { 288 e := &p.E[i] 289 if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL { 290 staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Size())) 291 continue 292 } 293 ir.SetPos(e.Expr) 294 assign(base.Pos, l, loff+e.Xoffset, e.Expr) 295 } 296 297 return true 298 299 case ir.OMAPLIT: 300 break 301 302 case ir.OCLOSURE: 303 r := r.(*ir.ClosureExpr) 304 if ir.IsTrivialClosure(r) { 305 if base.Debug.Closure > 0 { 306 base.WarnfAt(r.Pos(), "closure converted to global") 307 } 308 // Closures with no captured variables are globals, 309 // so the assignment can be done at link time. 310 // TODO if roff != 0 { panic } 311 staticdata.InitAddr(l, loff, staticdata.FuncLinksym(r.Func.Nname)) 312 return true 313 } 314 ir.ClosureDebugRuntimeCheck(r) 315 316 case ir.OCONVIFACE: 317 // This logic is mirrored in isStaticCompositeLiteral. 318 // If you change something here, change it there, and vice versa. 319 320 // Determine the underlying concrete type and value we are converting from. 321 r := r.(*ir.ConvExpr) 322 val := ir.Node(r) 323 for val.Op() == ir.OCONVIFACE { 324 val = val.(*ir.ConvExpr).X 325 } 326 327 if val.Type().IsInterface() { 328 // val is an interface type. 329 // If val is nil, we can statically initialize l; 330 // both words are zero and so there no work to do, so report success. 331 // If val is non-nil, we have no concrete type to record, 332 // and we won't be able to statically initialize its value, so report failure. 333 return val.Op() == ir.ONIL 334 } 335 336 if base.Debug.Unified != 0 && val.Type().HasShape() { 337 // See comment in cmd/compile/internal/walk/convert.go:walkConvInterface 338 return false 339 } 340 341 reflectdata.MarkTypeUsedInInterface(val.Type(), l.Linksym()) 342 343 var itab *ir.AddrExpr 344 if typ.IsEmptyInterface() { 345 itab = reflectdata.TypePtr(val.Type()) 346 } else { 347 itab = reflectdata.ITabAddr(val.Type(), typ) 348 } 349 350 // Create a copy of l to modify while we emit data. 351 352 // Emit itab, advance offset. 353 staticdata.InitAddr(l, loff, itab.X.(*ir.LinksymOffsetExpr).Linksym) 354 355 // Emit data. 356 if types.IsDirectIface(val.Type()) { 357 if val.Op() == ir.ONIL { 358 // Nil is zero, nothing to do. 359 return true 360 } 361 // Copy val directly into n. 362 ir.SetPos(val) 363 assign(base.Pos, l, loff+int64(types.PtrSize), val) 364 } else { 365 // Construct temp to hold val, write pointer to temp into n. 366 a := StaticName(val.Type()) 367 s.Temps[val] = a 368 assign(base.Pos, a, 0, val) 369 staticdata.InitAddr(l, loff+int64(types.PtrSize), a.Linksym()) 370 } 371 372 return true 373 374 case ir.OINLCALL: 375 r := r.(*ir.InlinedCallExpr) 376 return s.staticAssignInlinedCall(l, loff, r, typ) 377 } 378 379 if base.Flag.Percent != 0 { 380 ir.Dump("not static", r) 381 } 382 return false 383 } 384 385 func (s *Schedule) initplan(n ir.Node) { 386 if s.Plans[n] != nil { 387 return 388 } 389 p := new(Plan) 390 s.Plans[n] = p 391 switch n.Op() { 392 default: 393 base.Fatalf("initplan") 394 395 case ir.OARRAYLIT, ir.OSLICELIT: 396 n := n.(*ir.CompLitExpr) 397 var k int64 398 for _, a := range n.List { 399 if a.Op() == ir.OKEY { 400 kv := a.(*ir.KeyExpr) 401 k = typecheck.IndexConst(kv.Key) 402 if k < 0 { 403 base.Fatalf("initplan arraylit: invalid index %v", kv.Key) 404 } 405 a = kv.Value 406 } 407 s.addvalue(p, k*n.Type().Elem().Size(), a) 408 k++ 409 } 410 411 case ir.OSTRUCTLIT: 412 n := n.(*ir.CompLitExpr) 413 for _, a := range n.List { 414 if a.Op() != ir.OSTRUCTKEY { 415 base.Fatalf("initplan structlit") 416 } 417 a := a.(*ir.StructKeyExpr) 418 if a.Sym().IsBlank() { 419 continue 420 } 421 s.addvalue(p, a.Field.Offset, a.Value) 422 } 423 424 case ir.OMAPLIT: 425 n := n.(*ir.CompLitExpr) 426 for _, a := range n.List { 427 if a.Op() != ir.OKEY { 428 base.Fatalf("initplan maplit") 429 } 430 a := a.(*ir.KeyExpr) 431 s.addvalue(p, -1, a.Value) 432 } 433 } 434 } 435 436 func (s *Schedule) addvalue(p *Plan, xoffset int64, n ir.Node) { 437 // special case: zero can be dropped entirely 438 if ir.IsZero(n) { 439 return 440 } 441 442 // special case: inline struct and array (not slice) literals 443 if isvaluelit(n) { 444 s.initplan(n) 445 q := s.Plans[n] 446 for _, qe := range q.E { 447 // qe is a copy; we are not modifying entries in q.E 448 qe.Xoffset += xoffset 449 p.E = append(p.E, qe) 450 } 451 return 452 } 453 454 // add to plan 455 p.E = append(p.E, Entry{Xoffset: xoffset, Expr: n}) 456 } 457 458 func (s *Schedule) staticAssignInlinedCall(l *ir.Name, loff int64, call *ir.InlinedCallExpr, typ *types.Type) bool { 459 if base.Debug.InlStaticInit == 0 { 460 return false 461 } 462 463 // Handle the special case of an inlined call of 464 // a function body with a single return statement, 465 // which turns into a single assignment plus a goto. 466 // 467 // For example code like this: 468 // 469 // type T struct{ x int } 470 // func F(x int) *T { return &T{x} } 471 // var Global = F(400) 472 // 473 // turns into IR like this: 474 // 475 // INLCALL-init 476 // . AS2-init 477 // . . DCL # x.go:18:13 478 // . . . NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13 479 // . AS2 Def tc(1) # x.go:18:13 480 // . AS2-Lhs 481 // . . NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13 482 // . AS2-Rhs 483 // . . LITERAL-400 int tc(1) # x.go:18:14 484 // . INLMARK Index:1 # +x.go:18:13 485 // INLCALL PTR-*T tc(1) # x.go:18:13 486 // INLCALL-Body 487 // . BLOCK tc(1) # x.go:18:13 488 // . BLOCK-List 489 // . . DCL tc(1) # x.go:18:13 490 // . . . NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13 491 // . . AS2 tc(1) # x.go:18:13 492 // . . AS2-Lhs 493 // . . . NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13 494 // . . AS2-Rhs 495 // . . . INLINED RETURN ARGUMENT HERE 496 // . . GOTO p..i1 tc(1) # x.go:18:13 497 // . LABEL p..i1 # x.go:18:13 498 // INLCALL-ReturnVars 499 // . NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13 500 // 501 // In non-unified IR, the tree is slightly different: 502 // - if there are no arguments to the inlined function, 503 // the INLCALL-init omits the AS2. 504 // - the DCL inside BLOCK is on the AS2's init list, 505 // not its own statement in the top level of the BLOCK. 506 // 507 // If the init values are side-effect-free and each either only 508 // appears once in the function body or is safely repeatable, 509 // then we inline the value expressions into the return argument 510 // and then call StaticAssign to handle that copy. 511 // 512 // This handles simple cases like 513 // 514 // var myError = errors.New("mine") 515 // 516 // where errors.New is 517 // 518 // func New(text string) error { 519 // return &errorString{text} 520 // } 521 // 522 // We could make things more sophisticated but this kind of initializer 523 // is the most important case for us to get right. 524 525 init := call.Init() 526 var as2init *ir.AssignListStmt 527 if len(init) == 2 && init[0].Op() == ir.OAS2 && init[1].Op() == ir.OINLMARK { 528 as2init = init[0].(*ir.AssignListStmt) 529 } else if len(init) == 1 && init[0].Op() == ir.OINLMARK { 530 as2init = new(ir.AssignListStmt) 531 } else { 532 return false 533 } 534 if len(call.Body) != 2 || call.Body[0].Op() != ir.OBLOCK || call.Body[1].Op() != ir.OLABEL { 535 return false 536 } 537 label := call.Body[1].(*ir.LabelStmt).Label 538 block := call.Body[0].(*ir.BlockStmt) 539 list := block.List 540 var dcl *ir.Decl 541 if len(list) == 3 && list[0].Op() == ir.ODCL { 542 dcl = list[0].(*ir.Decl) 543 list = list[1:] 544 } 545 if len(list) != 2 || 546 list[0].Op() != ir.OAS2 || 547 list[1].Op() != ir.OGOTO || 548 list[1].(*ir.BranchStmt).Label != label { 549 return false 550 } 551 as2body := list[0].(*ir.AssignListStmt) 552 if dcl == nil { 553 ainit := as2body.Init() 554 if len(ainit) != 1 || ainit[0].Op() != ir.ODCL { 555 return false 556 } 557 dcl = ainit[0].(*ir.Decl) 558 } 559 if len(as2body.Lhs) != 1 || as2body.Lhs[0] != dcl.X { 560 return false 561 } 562 563 // Can't remove the parameter variables if an address is taken. 564 for _, v := range as2init.Lhs { 565 if v.(*ir.Name).Addrtaken() { 566 return false 567 } 568 } 569 // Can't move the computation of the args if they have side effects. 570 for _, r := range as2init.Rhs { 571 if AnySideEffects(r) { 572 return false 573 } 574 } 575 576 // Can only substitute arg for param if param is used 577 // at most once or is repeatable. 578 count := make(map[*ir.Name]int) 579 for _, x := range as2init.Lhs { 580 count[x.(*ir.Name)] = 0 581 } 582 583 hasNonTrivialClosure := false 584 ir.Visit(as2body.Rhs[0], func(n ir.Node) { 585 if name, ok := n.(*ir.Name); ok { 586 if c, ok := count[name]; ok { 587 count[name] = c + 1 588 } 589 } 590 if clo, ok := n.(*ir.ClosureExpr); ok { 591 hasNonTrivialClosure = hasNonTrivialClosure || !ir.IsTrivialClosure(clo) 592 } 593 }) 594 595 // If there's a non-trivial closure, it has captured the param, 596 // so we can't substitute arg for param. 597 if hasNonTrivialClosure { 598 return false 599 } 600 601 for name, c := range count { 602 if c > 1 { 603 // Check whether corresponding initializer can be repeated. 604 // Something like 1 can be; make(chan int) or &T{} cannot, 605 // because they need to evaluate to the same result in each use. 606 for i, n := range as2init.Lhs { 607 if n == name && !canRepeat(as2init.Rhs[i]) { 608 return false 609 } 610 } 611 } 612 } 613 614 // Possible static init. 615 // Build tree with args substituted for params and try it. 616 args := make(map[*ir.Name]ir.Node) 617 for i, v := range as2init.Lhs { 618 if ir.IsBlank(v) { 619 continue 620 } 621 args[v.(*ir.Name)] = as2init.Rhs[i] 622 } 623 r, ok := subst(as2body.Rhs[0], args) 624 if !ok { 625 return false 626 } 627 ok = s.StaticAssign(l, loff, r, typ) 628 629 if ok && base.Flag.Percent != 0 { 630 ir.Dump("static inlined-LEFT", l) 631 ir.Dump("static inlined-ORIG", call) 632 ir.Dump("static inlined-RIGHT", r) 633 } 634 return ok 635 } 636 637 // from here down is the walk analysis 638 // of composite literals. 639 // most of the work is to generate 640 // data statements for the constant 641 // part of the composite literal. 642 643 var statuniqgen int // name generator for static temps 644 645 // StaticName returns a name backed by a (writable) static data symbol. 646 // Use readonlystaticname for read-only node. 647 func StaticName(t *types.Type) *ir.Name { 648 // Don't use LookupNum; it interns the resulting string, but these are all unique. 649 n := typecheck.NewName(typecheck.Lookup(fmt.Sprintf("%s%d", obj.StaticNamePref, statuniqgen))) 650 statuniqgen++ 651 typecheck.Declare(n, ir.PEXTERN) 652 n.SetType(t) 653 n.Linksym().Set(obj.AttrStatic, true) 654 return n 655 } 656 657 // StaticLoc returns the static address of n, if n has one, or else nil. 658 func StaticLoc(n ir.Node) (name *ir.Name, offset int64, ok bool) { 659 if n == nil { 660 return nil, 0, false 661 } 662 663 switch n.Op() { 664 case ir.ONAME: 665 n := n.(*ir.Name) 666 return n, 0, true 667 668 case ir.OMETHEXPR: 669 n := n.(*ir.SelectorExpr) 670 return StaticLoc(n.FuncName()) 671 672 case ir.ODOT: 673 n := n.(*ir.SelectorExpr) 674 if name, offset, ok = StaticLoc(n.X); !ok { 675 break 676 } 677 offset += n.Offset() 678 return name, offset, true 679 680 case ir.OINDEX: 681 n := n.(*ir.IndexExpr) 682 if n.X.Type().IsSlice() { 683 break 684 } 685 if name, offset, ok = StaticLoc(n.X); !ok { 686 break 687 } 688 l := getlit(n.Index) 689 if l < 0 { 690 break 691 } 692 693 // Check for overflow. 694 if n.Type().Size() != 0 && types.MaxWidth/n.Type().Size() <= int64(l) { 695 break 696 } 697 offset += int64(l) * n.Type().Size() 698 return name, offset, true 699 } 700 701 return nil, 0, false 702 } 703 704 func isSideEffect(n ir.Node) bool { 705 switch n.Op() { 706 // Assume side effects unless we know otherwise. 707 default: 708 return true 709 710 // No side effects here (arguments are checked separately). 711 case ir.ONAME, 712 ir.ONONAME, 713 ir.OTYPE, 714 ir.OLITERAL, 715 ir.ONIL, 716 ir.OADD, 717 ir.OSUB, 718 ir.OOR, 719 ir.OXOR, 720 ir.OADDSTR, 721 ir.OADDR, 722 ir.OANDAND, 723 ir.OBYTES2STR, 724 ir.ORUNES2STR, 725 ir.OSTR2BYTES, 726 ir.OSTR2RUNES, 727 ir.OCAP, 728 ir.OCOMPLIT, 729 ir.OMAPLIT, 730 ir.OSTRUCTLIT, 731 ir.OARRAYLIT, 732 ir.OSLICELIT, 733 ir.OPTRLIT, 734 ir.OCONV, 735 ir.OCONVIFACE, 736 ir.OCONVNOP, 737 ir.ODOT, 738 ir.OEQ, 739 ir.ONE, 740 ir.OLT, 741 ir.OLE, 742 ir.OGT, 743 ir.OGE, 744 ir.OKEY, 745 ir.OSTRUCTKEY, 746 ir.OLEN, 747 ir.OMUL, 748 ir.OLSH, 749 ir.ORSH, 750 ir.OAND, 751 ir.OANDNOT, 752 ir.ONEW, 753 ir.ONOT, 754 ir.OBITNOT, 755 ir.OPLUS, 756 ir.ONEG, 757 ir.OOROR, 758 ir.OPAREN, 759 ir.ORUNESTR, 760 ir.OREAL, 761 ir.OIMAG, 762 ir.OCOMPLEX: 763 return false 764 765 // Only possible side effect is division by zero. 766 case ir.ODIV, ir.OMOD: 767 n := n.(*ir.BinaryExpr) 768 if n.Y.Op() != ir.OLITERAL || constant.Sign(n.Y.Val()) == 0 { 769 return true 770 } 771 772 // Only possible side effect is panic on invalid size, 773 // but many makechan and makemap use size zero, which is definitely OK. 774 case ir.OMAKECHAN, ir.OMAKEMAP: 775 n := n.(*ir.MakeExpr) 776 if !ir.IsConst(n.Len, constant.Int) || constant.Sign(n.Len.Val()) != 0 { 777 return true 778 } 779 780 // Only possible side effect is panic on invalid size. 781 // TODO(rsc): Merge with previous case (probably breaks toolstash -cmp). 782 case ir.OMAKESLICE, ir.OMAKESLICECOPY: 783 return true 784 } 785 return false 786 } 787 788 // AnySideEffects reports whether n contains any operations that could have observable side effects. 789 func AnySideEffects(n ir.Node) bool { 790 return ir.Any(n, isSideEffect) 791 } 792 793 // canRepeat reports whether executing n multiple times has the same effect as 794 // assigning n to a single variable and using that variable multiple times. 795 func canRepeat(n ir.Node) bool { 796 bad := func(n ir.Node) bool { 797 if isSideEffect(n) { 798 return true 799 } 800 switch n.Op() { 801 case ir.OMAKECHAN, 802 ir.OMAKEMAP, 803 ir.OMAKESLICE, 804 ir.OMAKESLICECOPY, 805 ir.OMAPLIT, 806 ir.ONEW, 807 ir.OPTRLIT, 808 ir.OSLICELIT, 809 ir.OSTR2BYTES, 810 ir.OSTR2RUNES: 811 return true 812 } 813 return false 814 } 815 return !ir.Any(n, bad) 816 } 817 818 func getlit(lit ir.Node) int { 819 if ir.IsSmallIntConst(lit) { 820 return int(ir.Int64Val(lit)) 821 } 822 return -1 823 } 824 825 func isvaluelit(n ir.Node) bool { 826 return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT 827 } 828 829 func subst(n ir.Node, m map[*ir.Name]ir.Node) (ir.Node, bool) { 830 valid := true 831 var edit func(ir.Node) ir.Node 832 edit = func(x ir.Node) ir.Node { 833 switch x.Op() { 834 case ir.ONAME: 835 x := x.(*ir.Name) 836 if v, ok := m[x]; ok { 837 return ir.DeepCopy(v.Pos(), v) 838 } 839 return x 840 case ir.ONONAME, ir.OLITERAL, ir.ONIL, ir.OTYPE: 841 return x 842 } 843 x = ir.Copy(x) 844 ir.EditChildrenWithHidden(x, edit) 845 if x, ok := x.(*ir.ConvExpr); ok && x.X.Op() == ir.OLITERAL { 846 // A conversion of variable or expression involving variables 847 // may become a conversion of constant after inlining the parameters 848 // and doing constant evaluation. Truncations that were valid 849 // on variables are not valid on constants, so we might have 850 // generated invalid code that will trip up the rest of the compiler. 851 // Fix those by truncating the constants. 852 if x, ok := truncate(x.X.(*ir.ConstExpr), x.Type()); ok { 853 return x 854 } 855 valid = false 856 return x 857 } 858 return typecheck.EvalConst(x) 859 } 860 n = edit(n) 861 return n, valid 862 } 863 864 // truncate returns the result of force converting c to type t, 865 // truncating its value as needed, like a conversion of a variable. 866 // If the conversion is too difficult, truncate returns nil, false. 867 func truncate(c *ir.ConstExpr, t *types.Type) (*ir.ConstExpr, bool) { 868 ct := c.Type() 869 cv := c.Val() 870 if ct.Kind() != t.Kind() { 871 switch { 872 default: 873 // Note: float -> float/integer and complex -> complex are valid but subtle. 874 // For example a float32(float64 1e300) evaluates to +Inf at runtime 875 // and the compiler doesn't have any concept of +Inf, so that would 876 // have to be left for runtime code evaluation. 877 // For now 878 return nil, false 879 880 case ct.IsInteger() && t.IsInteger(): 881 // truncate or sign extend 882 bits := t.Size() * 8 883 cv = constant.BinaryOp(cv, token.AND, constant.MakeUint64(1<<bits-1)) 884 if t.IsSigned() && constant.Compare(cv, token.GEQ, constant.MakeUint64(1<<(bits-1))) { 885 cv = constant.BinaryOp(cv, token.OR, constant.MakeInt64(-1<<(bits-1))) 886 } 887 } 888 } 889 c = ir.NewConstExpr(cv, c).(*ir.ConstExpr) 890 c.SetType(t) 891 return c, true 892 }