golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/ssa/interp/interp.go (about) 1 // Copyright 2013 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 ssa/interp defines an interpreter for the SSA 6 // representation of Go programs. 7 // 8 // This interpreter is provided as an adjunct for testing the SSA 9 // construction algorithm. Its purpose is to provide a minimal 10 // metacircular implementation of the dynamic semantics of each SSA 11 // instruction. It is not, and will never be, a production-quality Go 12 // interpreter. 13 // 14 // The following is a partial list of Go features that are currently 15 // unsupported or incomplete in the interpreter. 16 // 17 // * Unsafe operations, including all uses of unsafe.Pointer, are 18 // impossible to support given the "boxed" value representation we 19 // have chosen. 20 // 21 // * The reflect package is only partially implemented. 22 // 23 // * The "testing" package is no longer supported because it 24 // depends on low-level details that change too often. 25 // 26 // * "sync/atomic" operations are not atomic due to the "boxed" value 27 // representation: it is not possible to read, modify and write an 28 // interface value atomically. As a consequence, Mutexes are currently 29 // broken. 30 // 31 // * recover is only partially implemented. Also, the interpreter 32 // makes no attempt to distinguish target panics from interpreter 33 // crashes. 34 // 35 // * the sizes of the int, uint and uintptr types in the target 36 // program are assumed to be the same as those of the interpreter 37 // itself. 38 // 39 // * all values occupy space, even those of types defined by the spec 40 // to have zero size, e.g. struct{}. This can cause asymptotic 41 // performance degradation. 42 // 43 // * os.Exit is implemented using panic, causing deferred functions to 44 // run. 45 package interp // import "golang.org/x/tools/go/ssa/interp" 46 47 import ( 48 "fmt" 49 "go/token" 50 "go/types" 51 "os" 52 "reflect" 53 "runtime" 54 "sync/atomic" 55 _ "unsafe" 56 57 "golang.org/x/tools/go/ssa" 58 "golang.org/x/tools/internal/typeparams" 59 ) 60 61 type continuation int 62 63 const ( 64 kNext continuation = iota 65 kReturn 66 kJump 67 ) 68 69 // Mode is a bitmask of options affecting the interpreter. 70 type Mode uint 71 72 const ( 73 DisableRecover Mode = 1 << iota // Disable recover() in target programs; show interpreter crash instead. 74 EnableTracing // Print a trace of all instructions as they are interpreted. 75 ) 76 77 type methodSet map[string]*ssa.Function 78 79 // State shared between all interpreted goroutines. 80 type interpreter struct { 81 osArgs []value // the value of os.Args 82 prog *ssa.Program // the SSA program 83 globals map[*ssa.Global]*value // addresses of global variables (immutable) 84 mode Mode // interpreter options 85 reflectPackage *ssa.Package // the fake reflect package 86 errorMethods methodSet // the method set of reflect.error, which implements the error interface. 87 rtypeMethods methodSet // the method set of rtype, which implements the reflect.Type interface. 88 runtimeErrorString types.Type // the runtime.errorString type 89 sizes types.Sizes // the effective type-sizing function 90 goroutines int32 // atomically updated 91 } 92 93 type deferred struct { 94 fn value 95 args []value 96 instr *ssa.Defer 97 tail *deferred 98 } 99 100 type frame struct { 101 i *interpreter 102 caller *frame 103 fn *ssa.Function 104 block, prevBlock *ssa.BasicBlock 105 env map[ssa.Value]value // dynamic values of SSA variables 106 locals []value 107 defers *deferred 108 result value 109 panicking bool 110 panic interface{} 111 } 112 113 func (fr *frame) get(key ssa.Value) value { 114 switch key := key.(type) { 115 case nil: 116 // Hack; simplifies handling of optional attributes 117 // such as ssa.Slice.{Low,High}. 118 return nil 119 case *ssa.Function, *ssa.Builtin: 120 return key 121 case *ssa.Const: 122 return constValue(key) 123 case *ssa.Global: 124 if r, ok := fr.i.globals[key]; ok { 125 return r 126 } 127 } 128 if r, ok := fr.env[key]; ok { 129 return r 130 } 131 panic(fmt.Sprintf("get: no value for %T: %v", key, key.Name())) 132 } 133 134 // runDefer runs a deferred call d. 135 // It always returns normally, but may set or clear fr.panic. 136 func (fr *frame) runDefer(d *deferred) { 137 if fr.i.mode&EnableTracing != 0 { 138 fmt.Fprintf(os.Stderr, "%s: invoking deferred function call\n", 139 fr.i.prog.Fset.Position(d.instr.Pos())) 140 } 141 var ok bool 142 defer func() { 143 if !ok { 144 // Deferred call created a new state of panic. 145 fr.panicking = true 146 fr.panic = recover() 147 } 148 }() 149 call(fr.i, fr, d.instr.Pos(), d.fn, d.args) 150 ok = true 151 } 152 153 // runDefers executes fr's deferred function calls in LIFO order. 154 // 155 // On entry, fr.panicking indicates a state of panic; if 156 // true, fr.panic contains the panic value. 157 // 158 // On completion, if a deferred call started a panic, or if no 159 // deferred call recovered from a previous state of panic, then 160 // runDefers itself panics after the last deferred call has run. 161 // 162 // If there was no initial state of panic, or it was recovered from, 163 // runDefers returns normally. 164 func (fr *frame) runDefers() { 165 for d := fr.defers; d != nil; d = d.tail { 166 fr.runDefer(d) 167 } 168 fr.defers = nil 169 if fr.panicking { 170 panic(fr.panic) // new panic, or still panicking 171 } 172 } 173 174 // lookupMethod returns the method set for type typ, which may be one 175 // of the interpreter's fake types. 176 func lookupMethod(i *interpreter, typ types.Type, meth *types.Func) *ssa.Function { 177 switch typ { 178 case rtypeType: 179 return i.rtypeMethods[meth.Id()] 180 case errorType: 181 return i.errorMethods[meth.Id()] 182 } 183 return i.prog.LookupMethod(typ, meth.Pkg(), meth.Name()) 184 } 185 186 // visitInstr interprets a single ssa.Instruction within the activation 187 // record frame. It returns a continuation value indicating where to 188 // read the next instruction from. 189 func visitInstr(fr *frame, instr ssa.Instruction) continuation { 190 switch instr := instr.(type) { 191 case *ssa.DebugRef: 192 // no-op 193 194 case *ssa.UnOp: 195 fr.env[instr] = unop(instr, fr.get(instr.X)) 196 197 case *ssa.BinOp: 198 fr.env[instr] = binop(instr.Op, instr.X.Type(), fr.get(instr.X), fr.get(instr.Y)) 199 200 case *ssa.Call: 201 fn, args := prepareCall(fr, &instr.Call) 202 fr.env[instr] = call(fr.i, fr, instr.Pos(), fn, args) 203 204 case *ssa.ChangeInterface: 205 fr.env[instr] = fr.get(instr.X) 206 207 case *ssa.ChangeType: 208 fr.env[instr] = fr.get(instr.X) // (can't fail) 209 210 case *ssa.Convert: 211 fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X)) 212 213 case *ssa.SliceToArrayPointer: 214 fr.env[instr] = sliceToArrayPointer(instr.Type(), instr.X.Type(), fr.get(instr.X)) 215 216 case *ssa.MakeInterface: 217 fr.env[instr] = iface{t: instr.X.Type(), v: fr.get(instr.X)} 218 219 case *ssa.Extract: 220 fr.env[instr] = fr.get(instr.Tuple).(tuple)[instr.Index] 221 222 case *ssa.Slice: 223 fr.env[instr] = slice(fr.get(instr.X), fr.get(instr.Low), fr.get(instr.High), fr.get(instr.Max)) 224 225 case *ssa.Return: 226 switch len(instr.Results) { 227 case 0: 228 case 1: 229 fr.result = fr.get(instr.Results[0]) 230 default: 231 var res []value 232 for _, r := range instr.Results { 233 res = append(res, fr.get(r)) 234 } 235 fr.result = tuple(res) 236 } 237 fr.block = nil 238 return kReturn 239 240 case *ssa.RunDefers: 241 fr.runDefers() 242 243 case *ssa.Panic: 244 panic(targetPanic{fr.get(instr.X)}) 245 246 case *ssa.Send: 247 fr.get(instr.Chan).(chan value) <- fr.get(instr.X) 248 249 case *ssa.Store: 250 store(typeparams.MustDeref(instr.Addr.Type()), fr.get(instr.Addr).(*value), fr.get(instr.Val)) 251 252 case *ssa.If: 253 succ := 1 254 if fr.get(instr.Cond).(bool) { 255 succ = 0 256 } 257 fr.prevBlock, fr.block = fr.block, fr.block.Succs[succ] 258 return kJump 259 260 case *ssa.Jump: 261 fr.prevBlock, fr.block = fr.block, fr.block.Succs[0] 262 return kJump 263 264 case *ssa.Defer: 265 fn, args := prepareCall(fr, &instr.Call) 266 defers := &fr.defers 267 if into := fr.get(deferStack(instr)); into != nil { 268 defers = into.(**deferred) 269 } 270 *defers = &deferred{ 271 fn: fn, 272 args: args, 273 instr: instr, 274 tail: *defers, 275 } 276 277 case *ssa.Go: 278 fn, args := prepareCall(fr, &instr.Call) 279 atomic.AddInt32(&fr.i.goroutines, 1) 280 go func() { 281 call(fr.i, nil, instr.Pos(), fn, args) 282 atomic.AddInt32(&fr.i.goroutines, -1) 283 }() 284 285 case *ssa.MakeChan: 286 fr.env[instr] = make(chan value, asInt64(fr.get(instr.Size))) 287 288 case *ssa.Alloc: 289 var addr *value 290 if instr.Heap { 291 // new 292 addr = new(value) 293 fr.env[instr] = addr 294 } else { 295 // local 296 addr = fr.env[instr].(*value) 297 } 298 *addr = zero(typeparams.MustDeref(instr.Type())) 299 300 case *ssa.MakeSlice: 301 slice := make([]value, asInt64(fr.get(instr.Cap))) 302 tElt := instr.Type().Underlying().(*types.Slice).Elem() 303 for i := range slice { 304 slice[i] = zero(tElt) 305 } 306 fr.env[instr] = slice[:asInt64(fr.get(instr.Len))] 307 308 case *ssa.MakeMap: 309 var reserve int64 310 if instr.Reserve != nil { 311 reserve = asInt64(fr.get(instr.Reserve)) 312 } 313 if !fitsInt(reserve, fr.i.sizes) { 314 panic(fmt.Sprintf("ssa.MakeMap.Reserve value %d does not fit in int", reserve)) 315 } 316 fr.env[instr] = makeMap(instr.Type().Underlying().(*types.Map).Key(), reserve) 317 318 case *ssa.Range: 319 fr.env[instr] = rangeIter(fr.get(instr.X), instr.X.Type()) 320 321 case *ssa.Next: 322 fr.env[instr] = fr.get(instr.Iter).(iter).next() 323 324 case *ssa.FieldAddr: 325 fr.env[instr] = &(*fr.get(instr.X).(*value)).(structure)[instr.Field] 326 327 case *ssa.Field: 328 fr.env[instr] = fr.get(instr.X).(structure)[instr.Field] 329 330 case *ssa.IndexAddr: 331 x := fr.get(instr.X) 332 idx := fr.get(instr.Index) 333 switch x := x.(type) { 334 case []value: 335 fr.env[instr] = &x[asInt64(idx)] 336 case *value: // *array 337 fr.env[instr] = &(*x).(array)[asInt64(idx)] 338 default: 339 panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x)) 340 } 341 342 case *ssa.Index: 343 x := fr.get(instr.X) 344 idx := fr.get(instr.Index) 345 346 switch x := x.(type) { 347 case array: 348 fr.env[instr] = x[asInt64(idx)] 349 case string: 350 fr.env[instr] = x[asInt64(idx)] 351 default: 352 panic(fmt.Sprintf("unexpected x type in Index: %T", x)) 353 } 354 355 case *ssa.Lookup: 356 fr.env[instr] = lookup(instr, fr.get(instr.X), fr.get(instr.Index)) 357 358 case *ssa.MapUpdate: 359 m := fr.get(instr.Map) 360 key := fr.get(instr.Key) 361 v := fr.get(instr.Value) 362 switch m := m.(type) { 363 case map[value]value: 364 m[key] = v 365 case *hashmap: 366 m.insert(key.(hashable), v) 367 default: 368 panic(fmt.Sprintf("illegal map type: %T", m)) 369 } 370 371 case *ssa.TypeAssert: 372 fr.env[instr] = typeAssert(fr.i, instr, fr.get(instr.X).(iface)) 373 374 case *ssa.MakeClosure: 375 var bindings []value 376 for _, binding := range instr.Bindings { 377 bindings = append(bindings, fr.get(binding)) 378 } 379 fr.env[instr] = &closure{instr.Fn.(*ssa.Function), bindings} 380 381 case *ssa.Phi: 382 for i, pred := range instr.Block().Preds { 383 if fr.prevBlock == pred { 384 fr.env[instr] = fr.get(instr.Edges[i]) 385 break 386 } 387 } 388 389 case *ssa.Select: 390 var cases []reflect.SelectCase 391 if !instr.Blocking { 392 cases = append(cases, reflect.SelectCase{ 393 Dir: reflect.SelectDefault, 394 }) 395 } 396 for _, state := range instr.States { 397 var dir reflect.SelectDir 398 if state.Dir == types.RecvOnly { 399 dir = reflect.SelectRecv 400 } else { 401 dir = reflect.SelectSend 402 } 403 var send reflect.Value 404 if state.Send != nil { 405 send = reflect.ValueOf(fr.get(state.Send)) 406 } 407 cases = append(cases, reflect.SelectCase{ 408 Dir: dir, 409 Chan: reflect.ValueOf(fr.get(state.Chan)), 410 Send: send, 411 }) 412 } 413 chosen, recv, recvOk := reflect.Select(cases) 414 if !instr.Blocking { 415 chosen-- // default case should have index -1. 416 } 417 r := tuple{chosen, recvOk} 418 for i, st := range instr.States { 419 if st.Dir == types.RecvOnly { 420 var v value 421 if i == chosen && recvOk { 422 // No need to copy since send makes an unaliased copy. 423 v = recv.Interface().(value) 424 } else { 425 v = zero(st.Chan.Type().Underlying().(*types.Chan).Elem()) 426 } 427 r = append(r, v) 428 } 429 } 430 fr.env[instr] = r 431 432 default: 433 panic(fmt.Sprintf("unexpected instruction: %T", instr)) 434 } 435 436 // if val, ok := instr.(ssa.Value); ok { 437 // fmt.Println(toString(fr.env[val])) // debugging 438 // } 439 440 return kNext 441 } 442 443 // prepareCall determines the function value and argument values for a 444 // function call in a Call, Go or Defer instruction, performing 445 // interface method lookup if needed. 446 func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) { 447 v := fr.get(call.Value) 448 if call.Method == nil { 449 // Function call. 450 fn = v 451 } else { 452 // Interface method invocation. 453 recv := v.(iface) 454 if recv.t == nil { 455 panic("method invoked on nil interface") 456 } 457 if f := lookupMethod(fr.i, recv.t, call.Method); f == nil { 458 // Unreachable in well-typed programs. 459 panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, call.Method)) 460 } else { 461 fn = f 462 } 463 args = append(args, recv.v) 464 } 465 for _, arg := range call.Args { 466 args = append(args, fr.get(arg)) 467 } 468 return 469 } 470 471 // call interprets a call to a function (function, builtin or closure) 472 // fn with arguments args, returning its result. 473 // callpos is the position of the callsite. 474 func call(i *interpreter, caller *frame, callpos token.Pos, fn value, args []value) value { 475 switch fn := fn.(type) { 476 case *ssa.Function: 477 if fn == nil { 478 panic("call of nil function") // nil of func type 479 } 480 return callSSA(i, caller, callpos, fn, args, nil) 481 case *closure: 482 return callSSA(i, caller, callpos, fn.Fn, args, fn.Env) 483 case *ssa.Builtin: 484 return callBuiltin(caller, callpos, fn, args) 485 } 486 panic(fmt.Sprintf("cannot call %T", fn)) 487 } 488 489 func loc(fset *token.FileSet, pos token.Pos) string { 490 if pos == token.NoPos { 491 return "" 492 } 493 return " at " + fset.Position(pos).String() 494 } 495 496 // callSSA interprets a call to function fn with arguments args, 497 // and lexical environment env, returning its result. 498 // callpos is the position of the callsite. 499 func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, args []value, env []value) value { 500 if i.mode&EnableTracing != 0 { 501 fset := fn.Prog.Fset 502 // TODO(adonovan): fix: loc() lies for external functions. 503 fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn, loc(fset, fn.Pos())) 504 suffix := "" 505 if caller != nil { 506 suffix = ", resuming " + caller.fn.String() + loc(fset, callpos) 507 } 508 defer fmt.Fprintf(os.Stderr, "Leaving %s%s.\n", fn, suffix) 509 } 510 fr := &frame{ 511 i: i, 512 caller: caller, // for panic/recover 513 fn: fn, 514 } 515 if fn.Parent() == nil { 516 name := fn.String() 517 if ext := externals[name]; ext != nil { 518 if i.mode&EnableTracing != 0 { 519 fmt.Fprintln(os.Stderr, "\t(external)") 520 } 521 return ext(fr, args) 522 } 523 if fn.Blocks == nil { 524 panic("no code for function: " + name) 525 } 526 } 527 528 // generic function body? 529 if fn.TypeParams().Len() > 0 && len(fn.TypeArgs()) == 0 { 530 panic("interp requires ssa.BuilderMode to include InstantiateGenerics to execute generics") 531 } 532 533 fr.env = make(map[ssa.Value]value) 534 fr.block = fn.Blocks[0] 535 fr.locals = make([]value, len(fn.Locals)) 536 for i, l := range fn.Locals { 537 fr.locals[i] = zero(typeparams.MustDeref(l.Type())) 538 fr.env[l] = &fr.locals[i] 539 } 540 for i, p := range fn.Params { 541 fr.env[p] = args[i] 542 } 543 for i, fv := range fn.FreeVars { 544 fr.env[fv] = env[i] 545 } 546 for fr.block != nil { 547 runFrame(fr) 548 } 549 // Destroy the locals to avoid accidental use after return. 550 for i := range fn.Locals { 551 fr.locals[i] = bad{} 552 } 553 return fr.result 554 } 555 556 // runFrame executes SSA instructions starting at fr.block and 557 // continuing until a return, a panic, or a recovered panic. 558 // 559 // After a panic, runFrame panics. 560 // 561 // After a normal return, fr.result contains the result of the call 562 // and fr.block is nil. 563 // 564 // A recovered panic in a function without named return parameters 565 // (NRPs) becomes a normal return of the zero value of the function's 566 // result type. 567 // 568 // After a recovered panic in a function with NRPs, fr.result is 569 // undefined and fr.block contains the block at which to resume 570 // control. 571 func runFrame(fr *frame) { 572 defer func() { 573 if fr.block == nil { 574 return // normal return 575 } 576 if fr.i.mode&DisableRecover != 0 { 577 return // let interpreter crash 578 } 579 fr.panicking = true 580 fr.panic = recover() 581 if fr.i.mode&EnableTracing != 0 { 582 fmt.Fprintf(os.Stderr, "Panicking: %T %v.\n", fr.panic, fr.panic) 583 } 584 fr.runDefers() 585 fr.block = fr.fn.Recover 586 }() 587 588 for { 589 if fr.i.mode&EnableTracing != 0 { 590 fmt.Fprintf(os.Stderr, ".%s:\n", fr.block) 591 } 592 block: 593 for _, instr := range fr.block.Instrs { 594 if fr.i.mode&EnableTracing != 0 { 595 if v, ok := instr.(ssa.Value); ok { 596 fmt.Fprintln(os.Stderr, "\t", v.Name(), "=", instr) 597 } else { 598 fmt.Fprintln(os.Stderr, "\t", instr) 599 } 600 } 601 switch visitInstr(fr, instr) { 602 case kReturn: 603 return 604 case kNext: 605 // no-op 606 case kJump: 607 break block 608 } 609 } 610 } 611 } 612 613 // doRecover implements the recover() built-in. 614 func doRecover(caller *frame) value { 615 // recover() must be exactly one level beneath the deferred 616 // function (two levels beneath the panicking function) to 617 // have any effect. Thus we ignore both "defer recover()" and 618 // "defer f() -> g() -> recover()". 619 if caller.i.mode&DisableRecover == 0 && 620 caller != nil && !caller.panicking && 621 caller.caller != nil && caller.caller.panicking { 622 caller.caller.panicking = false 623 p := caller.caller.panic 624 caller.caller.panic = nil 625 626 // TODO(adonovan): support runtime.Goexit. 627 switch p := p.(type) { 628 case targetPanic: 629 // The target program explicitly called panic(). 630 return p.v 631 case runtime.Error: 632 // The interpreter encountered a runtime error. 633 return iface{caller.i.runtimeErrorString, p.Error()} 634 case string: 635 // The interpreter explicitly called panic(). 636 return iface{caller.i.runtimeErrorString, p} 637 default: 638 panic(fmt.Sprintf("unexpected panic type %T in target call to recover()", p)) 639 } 640 } 641 return iface{} 642 } 643 644 // Interpret interprets the Go program whose main package is mainpkg. 645 // mode specifies various interpreter options. filename and args are 646 // the initial values of os.Args for the target program. sizes is the 647 // effective type-sizing function for this program. 648 // 649 // Interpret returns the exit code of the program: 2 for panic (like 650 // gc does), or the argument to os.Exit for normal termination. 651 // 652 // The SSA program must include the "runtime" package. 653 // 654 // Type parameterized functions must have been built with 655 // InstantiateGenerics in the ssa.BuilderMode to be interpreted. 656 func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename string, args []string) (exitCode int) { 657 i := &interpreter{ 658 prog: mainpkg.Prog, 659 globals: make(map[*ssa.Global]*value), 660 mode: mode, 661 sizes: sizes, 662 goroutines: 1, 663 } 664 runtimePkg := i.prog.ImportedPackage("runtime") 665 if runtimePkg == nil { 666 panic("ssa.Program doesn't include runtime package") 667 } 668 i.runtimeErrorString = runtimePkg.Type("errorString").Object().Type() 669 670 initReflect(i) 671 672 i.osArgs = append(i.osArgs, filename) 673 for _, arg := range args { 674 i.osArgs = append(i.osArgs, arg) 675 } 676 677 for _, pkg := range i.prog.AllPackages() { 678 // Initialize global storage. 679 for _, m := range pkg.Members { 680 switch v := m.(type) { 681 case *ssa.Global: 682 cell := zero(typeparams.MustDeref(v.Type())) 683 i.globals[v] = &cell 684 } 685 } 686 } 687 688 // Top-level error handler. 689 exitCode = 2 690 defer func() { 691 if exitCode != 2 || i.mode&DisableRecover != 0 { 692 return 693 } 694 switch p := recover().(type) { 695 case exitPanic: 696 exitCode = int(p) 697 return 698 case targetPanic: 699 fmt.Fprintln(os.Stderr, "panic:", toString(p.v)) 700 case runtime.Error: 701 fmt.Fprintln(os.Stderr, "panic:", p.Error()) 702 case string: 703 fmt.Fprintln(os.Stderr, "panic:", p) 704 default: 705 fmt.Fprintf(os.Stderr, "panic: unexpected type: %T: %v\n", p, p) 706 } 707 708 // TODO(adonovan): dump panicking interpreter goroutine? 709 // buf := make([]byte, 0x10000) 710 // runtime.Stack(buf, false) 711 // fmt.Fprintln(os.Stderr, string(buf)) 712 // (Or dump panicking target goroutine?) 713 }() 714 715 // Run! 716 call(i, nil, token.NoPos, mainpkg.Func("init"), nil) 717 if mainFn := mainpkg.Func("main"); mainFn != nil { 718 call(i, nil, token.NoPos, mainFn, nil) 719 exitCode = 0 720 } else { 721 fmt.Fprintln(os.Stderr, "No main function.") 722 exitCode = 1 723 } 724 return 725 } 726 727 // TODO(taking): Hack while proposal #66601 is being finalized. 728 // 729 //go:linkname deferStack golang.org/x/tools/go/ssa.deferStack 730 func deferStack(i *ssa.Defer) ssa.Value