github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/gc/esc.go (about) 1 // Do not edit. Bootstrap copy of /Users/rsc/g/go/src/cmd/internal/gc/esc.go 2 3 // Copyright 2011 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 package gc 8 9 import ( 10 "rsc.io/tmp/bootstrap/internal/obj" 11 "fmt" 12 "strings" 13 ) 14 15 // Run analysis on minimal sets of mutually recursive functions 16 // or single non-recursive functions, bottom up. 17 // 18 // Finding these sets is finding strongly connected components 19 // in the static call graph. The algorithm for doing that is taken 20 // from Sedgewick, Algorithms, Second Edition, p. 482, with two 21 // adaptations. 22 // 23 // First, a hidden closure function (n->curfn != N) cannot be the 24 // root of a connected component. Refusing to use it as a root 25 // forces it into the component of the function in which it appears. 26 // This is more convenient for escape analysis. 27 // 28 // Second, each function becomes two virtual nodes in the graph, 29 // with numbers n and n+1. We record the function's node number as n 30 // but search from node n+1. If the search tells us that the component 31 // number (min) is n+1, we know that this is a trivial component: one function 32 // plus its closures. If the search tells us that the component number is 33 // n, then there was a path from node n+1 back to node n, meaning that 34 // the function set is mutually recursive. The escape analysis can be 35 // more precise when analyzing a single non-recursive function than 36 // when analyzing a set of mutually recursive functions. 37 38 // TODO(rsc): Look into using a map[*Node]bool instead of walkgen, 39 // to allow analysis passes to use walkgen themselves. 40 41 type bottomUpVisitor struct { 42 analyze func(*NodeList, bool) 43 visitgen uint32 44 stack *NodeList 45 } 46 47 // visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list. 48 // It calls analyze with successive groups of functions, working from 49 // the bottom of the call graph upward. Each time analyze is called with 50 // a list of functions, every function on that list only calls other functions 51 // on the list or functions that have been passed in previous invocations of 52 // analyze. Closures appear in the same list as their outer functions. 53 // The lists are as short as possible while preserving those requirements. 54 // (In a typical program, many invocations of analyze will be passed just 55 // a single function.) The boolean argument 'recursive' passed to analyze 56 // specifies whether the functions on the list are mutually recursive. 57 // If recursive is false, the list consists of only a single function and its closures. 58 // If recursive is true, the list may still contain only a single function, 59 // if that function is itself recursive. 60 func visitBottomUp(list *NodeList, analyze func(list *NodeList, recursive bool)) { 61 for l := list; l != nil; l = l.Next { 62 l.N.Walkgen = 0 63 } 64 65 var v bottomUpVisitor 66 v.analyze = analyze 67 for l := list; l != nil; l = l.Next { 68 if l.N.Op == ODCLFUNC && l.N.Curfn == nil { 69 v.visit(l.N) 70 } 71 } 72 73 for l := list; l != nil; l = l.Next { 74 l.N.Walkgen = 0 75 } 76 } 77 78 func (v *bottomUpVisitor) visit(n *Node) uint32 { 79 if n.Walkgen > 0 { 80 // already visited 81 return n.Walkgen 82 } 83 84 v.visitgen++ 85 n.Walkgen = v.visitgen 86 v.visitgen++ 87 min := v.visitgen 88 89 l := new(NodeList) 90 l.Next = v.stack 91 l.N = n 92 v.stack = l 93 min = v.visitcodelist(n.Nbody, min) 94 if (min == n.Walkgen || min == n.Walkgen+1) && n.Curfn == nil { 95 // This node is the root of a strongly connected component. 96 97 // The original min passed to visitcodelist was n->walkgen+1. 98 // If visitcodelist found its way back to n->walkgen, then this 99 // block is a set of mutually recursive functions. 100 // Otherwise it's just a lone function that does not recurse. 101 recursive := min == n.Walkgen 102 103 // Remove connected component from stack. 104 // Mark walkgen so that future visits return a large number 105 // so as not to affect the caller's min. 106 block := v.stack 107 108 var l *NodeList 109 for l = v.stack; l.N != n; l = l.Next { 110 l.N.Walkgen = ^uint32(0) 111 } 112 n.Walkgen = ^uint32(0) 113 v.stack = l.Next 114 l.Next = nil 115 116 // Run escape analysis on this set of functions. 117 v.analyze(block, recursive) 118 } 119 120 return min 121 } 122 123 func (v *bottomUpVisitor) visitcodelist(l *NodeList, min uint32) uint32 { 124 for ; l != nil; l = l.Next { 125 min = v.visitcode(l.N, min) 126 } 127 return min 128 } 129 130 func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 { 131 if n == nil { 132 return min 133 } 134 135 min = v.visitcodelist(n.Ninit, min) 136 min = v.visitcode(n.Left, min) 137 min = v.visitcode(n.Right, min) 138 min = v.visitcodelist(n.List, min) 139 min = v.visitcode(n.Ntest, min) 140 min = v.visitcode(n.Nincr, min) 141 min = v.visitcodelist(n.Nbody, min) 142 min = v.visitcodelist(n.Nelse, min) 143 min = v.visitcodelist(n.Rlist, min) 144 145 if n.Op == OCALLFUNC || n.Op == OCALLMETH { 146 fn := n.Left 147 if n.Op == OCALLMETH { 148 fn = n.Left.Right.Sym.Def 149 } 150 if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Defn != nil { 151 m := v.visit(fn.Defn) 152 if m < min { 153 min = m 154 } 155 } 156 } 157 158 if n.Op == OCLOSURE { 159 m := v.visit(n.Closure) 160 if m < min { 161 min = m 162 } 163 } 164 165 return min 166 } 167 168 // Escape analysis. 169 170 // An escape analysis pass for a set of functions. 171 // The analysis assumes that closures and the functions in which they 172 // appear are analyzed together, so that the aliasing between their 173 // variables can be modeled more precisely. 174 // 175 // First escfunc, esc and escassign recurse over the ast of each 176 // function to dig out flow(dst,src) edges between any 177 // pointer-containing nodes and store them in dst->escflowsrc. For 178 // variables assigned to a variable in an outer scope or used as a 179 // return value, they store a flow(theSink, src) edge to a fake node 180 // 'the Sink'. For variables referenced in closures, an edge 181 // flow(closure, &var) is recorded and the flow of a closure itself to 182 // an outer scope is tracked the same way as other variables. 183 // 184 // Then escflood walks the graph starting at theSink and tags all 185 // variables of it can reach an & node as escaping and all function 186 // parameters it can reach as leaking. 187 // 188 // If a value's address is taken but the address does not escape, 189 // then the value can stay on the stack. If the value new(T) does 190 // not escape, then new(T) can be rewritten into a stack allocation. 191 // The same is true of slice literals. 192 // 193 // If optimizations are disabled (-N), this code is not used. 194 // Instead, the compiler assumes that any value whose address 195 // is taken without being immediately dereferenced 196 // needs to be moved to the heap, and new(T) and slice 197 // literals are always real allocations. 198 199 func escapes(all *NodeList) { 200 visitBottomUp(all, escAnalyze) 201 } 202 203 const ( 204 EscFuncUnknown = 0 + iota 205 EscFuncPlanned 206 EscFuncStarted 207 EscFuncTagged 208 ) 209 210 type EscState struct { 211 // Fake node that all 212 // - return values and output variables 213 // - parameters on imported functions not marked 'safe' 214 // - assignments to global variables 215 // flow to. 216 theSink Node 217 218 // If an analyzed function is recorded to return 219 // pieces obtained via indirection from a parameter, 220 // and later there is a call f(x) to that function, 221 // we create a link funcParam <- x to record that fact. 222 // The funcParam node is handled specially in escflood. 223 funcParam Node 224 225 dsts *NodeList // all dst nodes 226 loopdepth int // for detecting nested loop scopes 227 pdepth int // for debug printing in recursions. 228 dstcount int // diagnostic 229 edgecount int // diagnostic 230 noesc *NodeList // list of possible non-escaping nodes, for printing 231 recursive bool // recursive function or group of mutually recursive functions. 232 } 233 234 var tags [16]*string 235 236 // mktag returns the string representation for an escape analysis tag. 237 func mktag(mask int) *string { 238 switch mask & EscMask { 239 case EscNone, EscReturn: 240 break 241 242 default: 243 Fatal("escape mktag") 244 } 245 246 mask >>= EscBits 247 248 if mask < len(tags) && tags[mask] != nil { 249 return tags[mask] 250 } 251 252 s := fmt.Sprintf("esc:0x%x", mask) 253 if mask < len(tags) { 254 tags[mask] = &s 255 } 256 return &s 257 } 258 259 func parsetag(note *string) int { 260 if note == nil || !strings.HasPrefix(*note, "esc:") { 261 return EscUnknown 262 } 263 em := atoi((*note)[4:]) 264 if em == 0 { 265 return EscNone 266 } 267 return EscReturn | em<<EscBits 268 } 269 270 func escAnalyze(all *NodeList, recursive bool) { 271 var es EscState 272 e := &es 273 e.theSink.Op = ONAME 274 e.theSink.Orig = &e.theSink 275 e.theSink.Class = PEXTERN 276 e.theSink.Sym = Lookup(".sink") 277 e.theSink.Escloopdepth = -1 278 e.recursive = recursive 279 280 e.funcParam.Op = ONAME 281 e.funcParam.Orig = &e.funcParam 282 e.funcParam.Class = PAUTO 283 e.funcParam.Sym = Lookup(".param") 284 e.funcParam.Escloopdepth = 10000000 285 286 for l := all; l != nil; l = l.Next { 287 if l.N.Op == ODCLFUNC { 288 l.N.Esc = EscFuncPlanned 289 } 290 } 291 292 // flow-analyze functions 293 for l := all; l != nil; l = l.Next { 294 if l.N.Op == ODCLFUNC { 295 escfunc(e, l.N) 296 } 297 } 298 299 // print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount); 300 301 // visit the upstream of each dst, mark address nodes with 302 // addrescapes, mark parameters unsafe 303 for l := e.dsts; l != nil; l = l.Next { 304 escflood(e, l.N) 305 } 306 307 // for all top level functions, tag the typenodes corresponding to the param nodes 308 for l := all; l != nil; l = l.Next { 309 if l.N.Op == ODCLFUNC { 310 esctag(e, l.N) 311 } 312 } 313 314 if Debug['m'] != 0 { 315 for l := e.noesc; l != nil; l = l.Next { 316 if l.N.Esc == EscNone { 317 var tmp *Sym 318 if l.N.Curfn != nil && l.N.Curfn.Nname != nil { 319 tmp = l.N.Curfn.Nname.Sym 320 } else { 321 tmp = nil 322 } 323 Warnl(int(l.N.Lineno), "%v %v does not escape", tmp, Nconv(l.N, obj.FmtShort)) 324 } 325 } 326 } 327 } 328 329 func escfunc(e *EscState, func_ *Node) { 330 // print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":""); 331 332 if func_.Esc != 1 { 333 Fatal("repeat escfunc %v", func_.Nname) 334 } 335 func_.Esc = EscFuncStarted 336 337 saveld := e.loopdepth 338 e.loopdepth = 1 339 savefn := Curfn 340 Curfn = func_ 341 342 for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { 343 if ll.N.Op != ONAME { 344 continue 345 } 346 switch ll.N.Class { 347 // out params are in a loopdepth between the sink and all local variables 348 case PPARAMOUT: 349 ll.N.Escloopdepth = 0 350 351 case PPARAM: 352 ll.N.Escloopdepth = 1 353 if ll.N.Type != nil && !haspointers(ll.N.Type) { 354 break 355 } 356 if Curfn.Nbody == nil && !Curfn.Noescape { 357 ll.N.Esc = EscHeap 358 } else { 359 ll.N.Esc = EscNone // prime for escflood later 360 } 361 e.noesc = list(e.noesc, ll.N) 362 } 363 } 364 365 // in a mutually recursive group we lose track of the return values 366 if e.recursive { 367 for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { 368 if ll.N.Op == ONAME && ll.N.Class == PPARAMOUT { 369 escflows(e, &e.theSink, ll.N) 370 } 371 } 372 } 373 374 escloopdepthlist(e, Curfn.Nbody) 375 esclist(e, Curfn.Nbody, Curfn) 376 Curfn = savefn 377 e.loopdepth = saveld 378 } 379 380 // Mark labels that have no backjumps to them as not increasing e->loopdepth. 381 // Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat 382 // and set it to one of the following two. Then in esc we'll clear it again. 383 var looping Label 384 385 var nonlooping Label 386 387 func escloopdepthlist(e *EscState, l *NodeList) { 388 for ; l != nil; l = l.Next { 389 escloopdepth(e, l.N) 390 } 391 } 392 393 func escloopdepth(e *EscState, n *Node) { 394 if n == nil { 395 return 396 } 397 398 escloopdepthlist(e, n.Ninit) 399 400 switch n.Op { 401 case OLABEL: 402 if n.Left == nil || n.Left.Sym == nil { 403 Fatal("esc:label without label: %v", Nconv(n, obj.FmtSign)) 404 } 405 406 // Walk will complain about this label being already defined, but that's not until 407 // after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc 408 // if(n->left->sym->label != nil) 409 // fatal("escape analysis messed up analyzing label: %+N", n); 410 n.Left.Sym.Label = &nonlooping 411 412 case OGOTO: 413 if n.Left == nil || n.Left.Sym == nil { 414 Fatal("esc:goto without label: %v", Nconv(n, obj.FmtSign)) 415 } 416 417 // If we come past one that's uninitialized, this must be a (harmless) forward jump 418 // but if it's set to nonlooping the label must have preceded this goto. 419 if n.Left.Sym.Label == &nonlooping { 420 n.Left.Sym.Label = &looping 421 } 422 } 423 424 escloopdepth(e, n.Left) 425 escloopdepth(e, n.Right) 426 escloopdepthlist(e, n.List) 427 escloopdepth(e, n.Ntest) 428 escloopdepth(e, n.Nincr) 429 escloopdepthlist(e, n.Nbody) 430 escloopdepthlist(e, n.Nelse) 431 escloopdepthlist(e, n.Rlist) 432 } 433 434 func esclist(e *EscState, l *NodeList, up *Node) { 435 for ; l != nil; l = l.Next { 436 esc(e, l.N, up) 437 } 438 } 439 440 func esc(e *EscState, n *Node, up *Node) { 441 if n == nil { 442 return 443 } 444 445 lno := int(setlineno(n)) 446 447 // ninit logically runs at a different loopdepth than the rest of the for loop. 448 esclist(e, n.Ninit, n) 449 450 if n.Op == OFOR || n.Op == ORANGE { 451 e.loopdepth++ 452 } 453 454 // type switch variables have no ODCL. 455 // process type switch as declaration. 456 // must happen before processing of switch body, 457 // so before recursion. 458 if n.Op == OSWITCH && n.Ntest != nil && n.Ntest.Op == OTYPESW { 459 for ll := n.List; ll != nil; ll = ll.Next { // cases 460 461 // ll->n->nname is the variable per case 462 if ll.N.Nname != nil { 463 ll.N.Nname.Escloopdepth = e.loopdepth 464 } 465 } 466 } 467 468 esc(e, n.Left, n) 469 esc(e, n.Right, n) 470 esc(e, n.Ntest, n) 471 esc(e, n.Nincr, n) 472 esclist(e, n.Nbody, n) 473 esclist(e, n.Nelse, n) 474 esclist(e, n.List, n) 475 esclist(e, n.Rlist, n) 476 477 if n.Op == OFOR || n.Op == ORANGE { 478 e.loopdepth-- 479 } 480 481 if Debug['m'] > 1 { 482 var tmp *Sym 483 if Curfn != nil && Curfn.Nname != nil { 484 tmp = Curfn.Nname.Sym 485 } else { 486 tmp = nil 487 } 488 fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, tmp, n) 489 } 490 491 switch n.Op { 492 // Record loop depth at declaration. 493 case ODCL: 494 if n.Left != nil { 495 n.Left.Escloopdepth = e.loopdepth 496 } 497 498 case OLABEL: 499 if n.Left.Sym.Label == &nonlooping { 500 if Debug['m'] > 1 { 501 fmt.Printf("%v:%v non-looping label\n", Ctxt.Line(int(lineno)), n) 502 } 503 } else if n.Left.Sym.Label == &looping { 504 if Debug['m'] > 1 { 505 fmt.Printf("%v: %v looping label\n", Ctxt.Line(int(lineno)), n) 506 } 507 e.loopdepth++ 508 } 509 510 // See case OLABEL in escloopdepth above 511 // else if(n->left->sym->label == nil) 512 // fatal("escape analysis missed or messed up a label: %+N", n); 513 514 n.Left.Sym.Label = nil 515 516 // Everything but fixed array is a dereference. 517 case ORANGE: 518 if Isfixedarray(n.Type) && n.List != nil && n.List.Next != nil { 519 escassign(e, n.List.Next.N, n.Right) 520 } 521 522 case OSWITCH: 523 if n.Ntest != nil && n.Ntest.Op == OTYPESW { 524 for ll := n.List; ll != nil; ll = ll.Next { // cases 525 526 // ntest->right is the argument of the .(type), 527 // ll->n->nname is the variable per case 528 escassign(e, ll.N.Nname, n.Ntest.Right) 529 } 530 } 531 532 // Filter out the following special case. 533 // 534 // func (b *Buffer) Foo() { 535 // n, m := ... 536 // b.buf = b.buf[n:m] 537 // } 538 // 539 // This assignment is a no-op for escape analysis, 540 // it does not store any new pointers into b that were not already there. 541 // However, without this special case b will escape, because we assign to OIND/ODOTPTR. 542 case OAS, OASOP, OASWB: 543 if (n.Left.Op == OIND || n.Left.Op == ODOTPTR) && n.Left.Left.Op == ONAME && // dst is ONAME dereference 544 (n.Right.Op == OSLICE || n.Right.Op == OSLICE3 || n.Right.Op == OSLICESTR) && // src is slice operation 545 (n.Right.Left.Op == OIND || n.Right.Left.Op == ODOTPTR) && n.Right.Left.Left.Op == ONAME && // slice is applied to ONAME dereference 546 n.Left.Left == n.Right.Left.Left { // dst and src reference the same base ONAME 547 548 // Here we also assume that the statement will not contain calls, 549 // that is, that order will move any calls to init. 550 // Otherwise base ONAME value could change between the moments 551 // when we evaluate it for dst and for src. 552 // 553 // Note, this optimization does not apply to OSLICEARR, 554 // because it does introduce a new pointer into b that was not already there 555 // (pointer to b itself). After such assignment, if b contents escape, 556 // b escapes as well. If we ignore such OSLICEARR, we will conclude 557 // that b does not escape when b contents do. 558 if Debug['m'] != 0 { 559 var tmp *Sym 560 if n.Curfn != nil && n.Curfn.Nname != nil { 561 tmp = n.Curfn.Nname.Sym 562 } else { 563 tmp = nil 564 } 565 Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", tmp, Nconv(n.Left, obj.FmtShort)) 566 } 567 568 break 569 } 570 571 escassign(e, n.Left, n.Right) 572 573 case OAS2: // x,y = a,b 574 if count(n.List) == count(n.Rlist) { 575 ll := n.List 576 lr := n.Rlist 577 for ; ll != nil; ll, lr = ll.Next, lr.Next { 578 escassign(e, ll.N, lr.N) 579 } 580 } 581 582 case OAS2RECV, // v, ok = <-ch 583 OAS2MAPR, // v, ok = m[k] 584 OAS2DOTTYPE: // v, ok = x.(type) 585 escassign(e, n.List.N, n.Rlist.N) 586 587 case OSEND: // ch <- x 588 escassign(e, &e.theSink, n.Right) 589 590 case ODEFER: 591 if e.loopdepth == 1 { // top level 592 break 593 } 594 // arguments leak out of scope 595 // TODO: leak to a dummy node instead 596 fallthrough 597 598 case OPROC: 599 // go f(x) - f and x escape 600 escassign(e, &e.theSink, n.Left.Left) 601 602 escassign(e, &e.theSink, n.Left.Right) // ODDDARG for call 603 for ll := n.Left.List; ll != nil; ll = ll.Next { 604 escassign(e, &e.theSink, ll.N) 605 } 606 607 case OCALLMETH, OCALLFUNC, OCALLINTER: 608 esccall(e, n, up) 609 610 // esccall already done on n->rlist->n. tie it's escretval to n->list 611 case OAS2FUNC: // x,y = f() 612 lr := n.Rlist.N.Escretval 613 614 var ll *NodeList 615 for ll = n.List; lr != nil && ll != nil; lr, ll = lr.Next, ll.Next { 616 escassign(e, ll.N, lr.N) 617 } 618 if lr != nil || ll != nil { 619 Fatal("esc oas2func") 620 } 621 622 case ORETURN: 623 ll := n.List 624 if count(n.List) == 1 && Curfn.Type.Outtuple > 1 { 625 // OAS2FUNC in disguise 626 // esccall already done on n->list->n 627 // tie n->list->n->escretval to curfn->dcl PPARAMOUT's 628 ll = n.List.N.Escretval 629 } 630 631 for lr := Curfn.Func.Dcl; lr != nil && ll != nil; lr = lr.Next { 632 if lr.N.Op != ONAME || lr.N.Class != PPARAMOUT { 633 continue 634 } 635 escassign(e, lr.N, ll.N) 636 ll = ll.Next 637 } 638 639 if ll != nil { 640 Fatal("esc return list") 641 } 642 643 // Argument could leak through recover. 644 case OPANIC: 645 escassign(e, &e.theSink, n.Left) 646 647 case OAPPEND: 648 if !n.Isddd { 649 for ll := n.List.Next; ll != nil; ll = ll.Next { 650 escassign(e, &e.theSink, ll.N) // lose track of assign to dereference 651 } 652 } 653 654 case OCONV, OCONVNOP: 655 escassign(e, n, n.Left) 656 657 case OCONVIFACE: 658 n.Esc = EscNone // until proven otherwise 659 e.noesc = list(e.noesc, n) 660 n.Escloopdepth = e.loopdepth 661 escassign(e, n, n.Left) 662 663 case OARRAYLIT: 664 if Isslice(n.Type) { 665 n.Esc = EscNone // until proven otherwise 666 e.noesc = list(e.noesc, n) 667 n.Escloopdepth = e.loopdepth 668 669 // Values make it to memory, lose track. 670 for ll := n.List; ll != nil; ll = ll.Next { 671 escassign(e, &e.theSink, ll.N.Right) 672 } 673 } else { 674 // Link values to array. 675 for ll := n.List; ll != nil; ll = ll.Next { 676 escassign(e, n, ll.N.Right) 677 } 678 } 679 680 // Link values to struct. 681 case OSTRUCTLIT: 682 for ll := n.List; ll != nil; ll = ll.Next { 683 escassign(e, n, ll.N.Right) 684 } 685 686 case OPTRLIT: 687 n.Esc = EscNone // until proven otherwise 688 e.noesc = list(e.noesc, n) 689 n.Escloopdepth = e.loopdepth 690 691 // Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too. 692 escassign(e, n, n.Left) 693 694 case OCALLPART: 695 n.Esc = EscNone // until proven otherwise 696 e.noesc = list(e.noesc, n) 697 n.Escloopdepth = e.loopdepth 698 699 // Contents make it to memory, lose track. 700 escassign(e, &e.theSink, n.Left) 701 702 case OMAPLIT: 703 n.Esc = EscNone // until proven otherwise 704 e.noesc = list(e.noesc, n) 705 n.Escloopdepth = e.loopdepth 706 707 // Keys and values make it to memory, lose track. 708 for ll := n.List; ll != nil; ll = ll.Next { 709 escassign(e, &e.theSink, ll.N.Left) 710 escassign(e, &e.theSink, ll.N.Right) 711 } 712 713 // Link addresses of captured variables to closure. 714 case OCLOSURE: 715 var a *Node 716 var v *Node 717 for ll := n.Func.Cvars; ll != nil; ll = ll.Next { 718 v = ll.N 719 if v.Op == OXXX { // unnamed out argument; see dcl.c:/^funcargs 720 continue 721 } 722 a = v.Closure 723 if !v.Byval { 724 a = Nod(OADDR, a, nil) 725 a.Lineno = v.Lineno 726 a.Escloopdepth = e.loopdepth 727 typecheck(&a, Erv) 728 } 729 730 escassign(e, n, a) 731 } 732 fallthrough 733 734 case OMAKECHAN, 735 OMAKEMAP, 736 OMAKESLICE, 737 ONEW, 738 OARRAYRUNESTR, 739 OARRAYBYTESTR, 740 OSTRARRAYRUNE, 741 OSTRARRAYBYTE, 742 ORUNESTR: 743 n.Escloopdepth = e.loopdepth 744 745 n.Esc = EscNone // until proven otherwise 746 e.noesc = list(e.noesc, n) 747 748 case OADDSTR: 749 n.Escloopdepth = e.loopdepth 750 n.Esc = EscNone // until proven otherwise 751 e.noesc = list(e.noesc, n) 752 753 // Arguments of OADDSTR do not escape. 754 755 case OADDR: 756 n.Esc = EscNone // until proven otherwise 757 e.noesc = list(e.noesc, n) 758 759 // current loop depth is an upper bound on actual loop depth 760 // of addressed value. 761 n.Escloopdepth = e.loopdepth 762 763 // for &x, use loop depth of x if known. 764 // it should always be known, but if not, be conservative 765 // and keep the current loop depth. 766 if n.Left.Op == ONAME { 767 switch n.Left.Class { 768 case PAUTO: 769 if n.Left.Escloopdepth != 0 { 770 n.Escloopdepth = n.Left.Escloopdepth 771 } 772 773 // PPARAM is loop depth 1 always. 774 // PPARAMOUT is loop depth 0 for writes 775 // but considered loop depth 1 for address-of, 776 // so that writing the address of one result 777 // to another (or the same) result makes the 778 // first result move to the heap. 779 case PPARAM, PPARAMOUT: 780 n.Escloopdepth = 1 781 } 782 } 783 } 784 785 lineno = int32(lno) 786 } 787 788 // Assert that expr somehow gets assigned to dst, if non nil. for 789 // dst==nil, any name node expr still must be marked as being 790 // evaluated in curfn. For expr==nil, dst must still be examined for 791 // evaluations inside it (e.g *f(x) = y) 792 func escassign(e *EscState, dst *Node, src *Node) { 793 if isblank(dst) || dst == nil || src == nil || src.Op == ONONAME || src.Op == OXXX { 794 return 795 } 796 797 if Debug['m'] > 1 { 798 var tmp *Sym 799 if Curfn != nil && Curfn.Nname != nil { 800 tmp = Curfn.Nname.Sym 801 } else { 802 tmp = nil 803 } 804 fmt.Printf("%v:[%d] %v escassign: %v(%v) = %v(%v)\n", Ctxt.Line(int(lineno)), e.loopdepth, tmp, Nconv(dst, obj.FmtShort), Jconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort)) 805 } 806 807 setlineno(dst) 808 809 // Analyze lhs of assignment. 810 // Replace dst with e->theSink if we can't track it. 811 switch dst.Op { 812 default: 813 Dump("dst", dst) 814 Fatal("escassign: unexpected dst") 815 816 case OARRAYLIT, 817 OCLOSURE, 818 OCONV, 819 OCONVIFACE, 820 OCONVNOP, 821 OMAPLIT, 822 OSTRUCTLIT, 823 OPTRLIT, 824 OCALLPART: 825 break 826 827 case ONAME: 828 if dst.Class == PEXTERN { 829 dst = &e.theSink 830 } 831 832 case ODOT: // treat "dst.x = src" as "dst = src" 833 escassign(e, dst.Left, src) 834 835 return 836 837 case OINDEX: 838 if Isfixedarray(dst.Left.Type) { 839 escassign(e, dst.Left, src) 840 return 841 } 842 843 dst = &e.theSink // lose track of dereference 844 845 case OIND, ODOTPTR: 846 dst = &e.theSink // lose track of dereference 847 848 // lose track of key and value 849 case OINDEXMAP: 850 escassign(e, &e.theSink, dst.Right) 851 852 dst = &e.theSink 853 } 854 855 lno := int(setlineno(src)) 856 e.pdepth++ 857 858 switch src.Op { 859 case OADDR, // dst = &x 860 OIND, // dst = *x 861 ODOTPTR, // dst = (*x).f 862 ONAME, 863 OPARAM, 864 ODDDARG, 865 OPTRLIT, 866 OARRAYLIT, 867 OMAPLIT, 868 OSTRUCTLIT, 869 OMAKECHAN, 870 OMAKEMAP, 871 OMAKESLICE, 872 OARRAYRUNESTR, 873 OARRAYBYTESTR, 874 OSTRARRAYRUNE, 875 OSTRARRAYBYTE, 876 OADDSTR, 877 ONEW, 878 OCALLPART, 879 ORUNESTR, 880 OCONVIFACE: 881 escflows(e, dst, src) 882 883 case OCLOSURE: 884 // OCLOSURE is lowered to OPTRLIT, 885 // insert OADDR to account for the additional indirection. 886 a := Nod(OADDR, src, nil) 887 a.Lineno = src.Lineno 888 a.Escloopdepth = src.Escloopdepth 889 a.Type = Ptrto(src.Type) 890 escflows(e, dst, a) 891 892 // Flowing multiple returns to a single dst happens when 893 // analyzing "go f(g())": here g() flows to sink (issue 4529). 894 case OCALLMETH, OCALLFUNC, OCALLINTER: 895 for ll := src.Escretval; ll != nil; ll = ll.Next { 896 escflows(e, dst, ll.N) 897 } 898 899 // A non-pointer escaping from a struct does not concern us. 900 case ODOT: 901 if src.Type != nil && !haspointers(src.Type) { 902 break 903 } 904 fallthrough 905 906 // Conversions, field access, slice all preserve the input value. 907 case OCONV, 908 OCONVNOP, 909 ODOTMETH, 910 // treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC 911 // iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here 912 ODOTTYPE, 913 ODOTTYPE2, 914 OSLICE, 915 OSLICE3, 916 OSLICEARR, 917 OSLICE3ARR, 918 OSLICESTR: 919 // Conversions, field access, slice all preserve the input value. 920 escassign(e, dst, src.Left) 921 922 case OAPPEND: 923 // Append returns first argument. 924 escassign(e, dst, src.List.N) 925 926 case OINDEX: 927 // Index of array preserves input value. 928 if Isfixedarray(src.Left.Type) { 929 escassign(e, dst, src.Left) 930 } 931 932 // Might be pointer arithmetic, in which case 933 // the operands flow into the result. 934 // TODO(rsc): Decide what the story is here. This is unsettling. 935 case OADD, 936 OSUB, 937 OOR, 938 OXOR, 939 OMUL, 940 ODIV, 941 OMOD, 942 OLSH, 943 ORSH, 944 OAND, 945 OANDNOT, 946 OPLUS, 947 OMINUS, 948 OCOM: 949 escassign(e, dst, src.Left) 950 951 escassign(e, dst, src.Right) 952 } 953 954 e.pdepth-- 955 lineno = int32(lno) 956 } 957 958 func escassignfromtag(e *EscState, note *string, dsts *NodeList, src *Node) int { 959 em := parsetag(note) 960 961 if em == EscUnknown { 962 escassign(e, &e.theSink, src) 963 return em 964 } 965 966 if em == EscNone { 967 return em 968 } 969 970 // If content inside parameter (reached via indirection) 971 // escapes back to results, mark as such. 972 if em&EscContentEscapes != 0 { 973 escassign(e, &e.funcParam, src) 974 } 975 976 em0 := em 977 for em >>= EscReturnBits; em != 0 && dsts != nil; em, dsts = em>>1, dsts.Next { 978 if em&1 != 0 { 979 escassign(e, dsts.N, src) 980 } 981 } 982 983 if em != 0 && dsts == nil { 984 Fatal("corrupt esc tag %q or messed up escretval list\n", note) 985 } 986 return em0 987 } 988 989 // This is a bit messier than fortunate, pulled out of esc's big 990 // switch for clarity. We either have the paramnodes, which may be 991 // connected to other things through flows or we have the parameter type 992 // nodes, which may be marked "noescape". Navigating the ast is slightly 993 // different for methods vs plain functions and for imported vs 994 // this-package 995 func esccall(e *EscState, n *Node, up *Node) { 996 var fntype *Type 997 998 var fn *Node 999 switch n.Op { 1000 default: 1001 Fatal("esccall") 1002 1003 case OCALLFUNC: 1004 fn = n.Left 1005 fntype = fn.Type 1006 1007 case OCALLMETH: 1008 fn = n.Left.Right.Sym.Def 1009 if fn != nil { 1010 fntype = fn.Type 1011 } else { 1012 fntype = n.Left.Type 1013 } 1014 1015 case OCALLINTER: 1016 fntype = n.Left.Type 1017 } 1018 1019 ll := n.List 1020 if n.List != nil && n.List.Next == nil { 1021 a := n.List.N 1022 if a.Type.Etype == TSTRUCT && a.Type.Funarg != 0 { // f(g()). 1023 ll = a.Escretval 1024 } 1025 } 1026 1027 if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Defn != nil && fn.Defn.Nbody != nil && fn.Ntype != nil && fn.Defn.Esc < EscFuncTagged { 1028 // function in same mutually recursive group. Incorporate into flow graph. 1029 // print("esc local fn: %N\n", fn->ntype); 1030 if fn.Defn.Esc == EscFuncUnknown || n.Escretval != nil { 1031 Fatal("graph inconsistency") 1032 } 1033 1034 // set up out list on this call node 1035 for lr := fn.Ntype.Rlist; lr != nil; lr = lr.Next { 1036 n.Escretval = list(n.Escretval, lr.N.Left) // type.rlist -> dclfield -> ONAME (PPARAMOUT) 1037 } 1038 1039 // Receiver. 1040 if n.Op != OCALLFUNC { 1041 escassign(e, fn.Ntype.Left.Left, n.Left.Left) 1042 } 1043 1044 var src *Node 1045 for lr := fn.Ntype.List; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next { 1046 src = ll.N 1047 if lr.N.Isddd && !n.Isddd { 1048 // Introduce ODDDARG node to represent ... allocation. 1049 src = Nod(ODDDARG, nil, nil) 1050 1051 src.Type = typ(TARRAY) 1052 src.Type.Type = lr.N.Type.Type 1053 src.Type.Bound = int64(count(ll)) 1054 src.Type = Ptrto(src.Type) // make pointer so it will be tracked 1055 src.Escloopdepth = e.loopdepth 1056 src.Lineno = n.Lineno 1057 src.Esc = EscNone // until we find otherwise 1058 e.noesc = list(e.noesc, src) 1059 n.Right = src 1060 } 1061 1062 if lr.N.Left != nil { 1063 escassign(e, lr.N.Left, src) 1064 } 1065 if src != ll.N { 1066 break 1067 } 1068 } 1069 1070 // "..." arguments are untracked 1071 for ; ll != nil; ll = ll.Next { 1072 escassign(e, &e.theSink, ll.N) 1073 } 1074 1075 return 1076 } 1077 1078 // Imported or completely analyzed function. Use the escape tags. 1079 if n.Escretval != nil { 1080 Fatal("esc already decorated call %v\n", Nconv(n, obj.FmtSign)) 1081 } 1082 1083 // set up out list on this call node with dummy auto ONAMES in the current (calling) function. 1084 i := 0 1085 1086 var src *Node 1087 var buf string 1088 for t := getoutargx(fntype).Type; t != nil; t = t.Down { 1089 src = Nod(ONAME, nil, nil) 1090 buf = fmt.Sprintf(".dum%d", i) 1091 i++ 1092 src.Sym = Lookup(buf) 1093 src.Type = t.Type 1094 src.Class = PAUTO 1095 src.Curfn = Curfn 1096 src.Escloopdepth = e.loopdepth 1097 src.Used = true 1098 src.Lineno = n.Lineno 1099 n.Escretval = list(n.Escretval, src) 1100 } 1101 1102 // print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval); 1103 1104 // Receiver. 1105 if n.Op != OCALLFUNC { 1106 t := getthisx(fntype).Type 1107 src := n.Left.Left 1108 if haspointers(t.Type) { 1109 escassignfromtag(e, t.Note, n.Escretval, src) 1110 } 1111 } 1112 1113 var a *Node 1114 for t := getinargx(fntype).Type; ll != nil; ll = ll.Next { 1115 src = ll.N 1116 if t.Isddd && !n.Isddd { 1117 // Introduce ODDDARG node to represent ... allocation. 1118 src = Nod(ODDDARG, nil, nil) 1119 1120 src.Escloopdepth = e.loopdepth 1121 src.Lineno = n.Lineno 1122 src.Type = typ(TARRAY) 1123 src.Type.Type = t.Type.Type 1124 src.Type.Bound = int64(count(ll)) 1125 src.Type = Ptrto(src.Type) // make pointer so it will be tracked 1126 src.Esc = EscNone // until we find otherwise 1127 e.noesc = list(e.noesc, src) 1128 n.Right = src 1129 } 1130 1131 if haspointers(t.Type) { 1132 if escassignfromtag(e, t.Note, n.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC { 1133 a = src 1134 for a.Op == OCONVNOP { 1135 a = a.Left 1136 } 1137 switch a.Op { 1138 // The callee has already been analyzed, so its arguments have esc tags. 1139 // The argument is marked as not escaping at all. 1140 // Record that fact so that any temporary used for 1141 // synthesizing this expression can be reclaimed when 1142 // the function returns. 1143 // This 'noescape' is even stronger than the usual esc == EscNone. 1144 // src->esc == EscNone means that src does not escape the current function. 1145 // src->noescape = 1 here means that src does not escape this statement 1146 // in the current function. 1147 case OCALLPART, 1148 OCLOSURE, 1149 ODDDARG, 1150 OARRAYLIT, 1151 OPTRLIT, 1152 OSTRUCTLIT: 1153 a.Noescape = true 1154 } 1155 } 1156 } 1157 1158 if src != ll.N { 1159 break 1160 } 1161 t = t.Down 1162 } 1163 1164 // "..." arguments are untracked 1165 for ; ll != nil; ll = ll.Next { 1166 escassign(e, &e.theSink, ll.N) 1167 } 1168 } 1169 1170 // Store the link src->dst in dst, throwing out some quick wins. 1171 func escflows(e *EscState, dst *Node, src *Node) { 1172 if dst == nil || src == nil || dst == src { 1173 return 1174 } 1175 1176 // Don't bother building a graph for scalars. 1177 if src.Type != nil && !haspointers(src.Type) { 1178 return 1179 } 1180 1181 if Debug['m'] > 2 { 1182 fmt.Printf("%v::flows:: %v <- %v\n", Ctxt.Line(int(lineno)), Nconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort)) 1183 } 1184 1185 if dst.Escflowsrc == nil { 1186 e.dsts = list(e.dsts, dst) 1187 e.dstcount++ 1188 } 1189 1190 e.edgecount++ 1191 1192 dst.Escflowsrc = list(dst.Escflowsrc, src) 1193 } 1194 1195 // Whenever we hit a reference node, the level goes up by one, and whenever 1196 // we hit an OADDR, the level goes down by one. as long as we're on a level > 0 1197 // finding an OADDR just means we're following the upstream of a dereference, 1198 // so this address doesn't leak (yet). 1199 // If level == 0, it means the /value/ of this node can reach the root of this flood. 1200 // so if this node is an OADDR, it's argument should be marked as escaping iff 1201 // it's currfn/e->loopdepth are different from the flood's root. 1202 // Once an object has been moved to the heap, all of it's upstream should be considered 1203 // escaping to the global scope. 1204 func escflood(e *EscState, dst *Node) { 1205 switch dst.Op { 1206 case ONAME, OCLOSURE: 1207 break 1208 1209 default: 1210 return 1211 } 1212 1213 if Debug['m'] > 1 { 1214 var tmp *Sym 1215 if dst.Curfn != nil && dst.Curfn.Nname != nil { 1216 tmp = dst.Curfn.Nname.Sym 1217 } else { 1218 tmp = nil 1219 } 1220 fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", walkgen, Nconv(dst, obj.FmtShort), tmp, dst.Escloopdepth) 1221 } 1222 1223 for l := dst.Escflowsrc; l != nil; l = l.Next { 1224 walkgen++ 1225 escwalk(e, 0, dst, l.N) 1226 } 1227 } 1228 1229 // There appear to be some loops in the escape graph, causing 1230 // arbitrary recursion into deeper and deeper levels. 1231 // Cut this off safely by making minLevel sticky: once you 1232 // get that deep, you cannot go down any further but you also 1233 // cannot go up any further. This is a conservative fix. 1234 // Making minLevel smaller (more negative) would handle more 1235 // complex chains of indirections followed by address-of operations, 1236 // at the cost of repeating the traversal once for each additional 1237 // allowed level when a loop is encountered. Using -2 suffices to 1238 // pass all the tests we have written so far, which we assume matches 1239 // the level of complexity we want the escape analysis code to handle. 1240 const ( 1241 MinLevel = -2 1242 ) 1243 1244 func escwalk(e *EscState, level int, dst *Node, src *Node) { 1245 if src.Walkgen == walkgen && src.Esclevel <= int32(level) { 1246 return 1247 } 1248 src.Walkgen = walkgen 1249 src.Esclevel = int32(level) 1250 1251 if Debug['m'] > 1 { 1252 var tmp *Sym 1253 if src.Curfn != nil && src.Curfn.Nname != nil { 1254 tmp = src.Curfn.Nname.Sym 1255 } else { 1256 tmp = nil 1257 } 1258 fmt.Printf("escwalk: level:%d depth:%d %.*s %v(%v) scope:%v[%d]\n", level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), tmp, src.Escloopdepth) 1259 } 1260 1261 e.pdepth++ 1262 1263 // Input parameter flowing to output parameter? 1264 var leaks bool 1265 if dst.Op == ONAME && dst.Class == PPARAMOUT && dst.Vargen <= 20 { 1266 if src.Op == ONAME && src.Class == PPARAM && src.Curfn == dst.Curfn && src.Esc != EscScope && src.Esc != EscHeap { 1267 if level == 0 { 1268 if Debug['m'] != 0 { 1269 Warnl(int(src.Lineno), "leaking param: %v to result %v", Nconv(src, obj.FmtShort), dst.Sym) 1270 } 1271 if src.Esc&EscMask != EscReturn { 1272 src.Esc = EscReturn 1273 } 1274 src.Esc |= 1 << uint((dst.Vargen-1)+EscReturnBits) 1275 goto recurse 1276 } else if level > 0 { 1277 if Debug['m'] != 0 { 1278 Warnl(int(src.Lineno), "%v leaking param %v content to result %v", src.Curfn.Nname, Nconv(src, obj.FmtShort), dst.Sym) 1279 } 1280 if src.Esc&EscMask != EscReturn { 1281 src.Esc = EscReturn 1282 } 1283 src.Esc |= EscContentEscapes 1284 goto recurse 1285 } 1286 } 1287 } 1288 1289 // The second clause is for values pointed at by an object passed to a call 1290 // that returns something reached via indirect from the object. 1291 // We don't know which result it is or how many indirects, so we treat it as leaking. 1292 leaks = level <= 0 && dst.Escloopdepth < src.Escloopdepth || level < 0 && dst == &e.funcParam && haspointers(src.Type) 1293 1294 switch src.Op { 1295 case ONAME: 1296 if src.Class == PPARAM && (leaks || dst.Escloopdepth < 0) && src.Esc != EscHeap { 1297 src.Esc = EscScope 1298 if Debug['m'] != 0 { 1299 Warnl(int(src.Lineno), "leaking param: %v", Nconv(src, obj.FmtShort)) 1300 } 1301 } 1302 1303 // Treat a PPARAMREF closure variable as equivalent to the 1304 // original variable. 1305 if src.Class == PPARAMREF { 1306 if leaks && Debug['m'] != 0 { 1307 Warnl(int(src.Lineno), "leaking closure reference %v", Nconv(src, obj.FmtShort)) 1308 } 1309 escwalk(e, level, dst, src.Closure) 1310 } 1311 1312 case OPTRLIT, OADDR: 1313 if leaks { 1314 src.Esc = EscHeap 1315 addrescapes(src.Left) 1316 if Debug['m'] != 0 { 1317 p := src 1318 if p.Left.Op == OCLOSURE { 1319 p = p.Left // merely to satisfy error messages in tests 1320 } 1321 Warnl(int(src.Lineno), "%v escapes to heap", Nconv(p, obj.FmtShort)) 1322 } 1323 } 1324 1325 newlevel := level 1326 if level > MinLevel { 1327 newlevel-- 1328 } 1329 escwalk(e, newlevel, dst, src.Left) 1330 1331 case OARRAYLIT: 1332 if Isfixedarray(src.Type) { 1333 break 1334 } 1335 fallthrough 1336 1337 // fall through 1338 case ODDDARG, 1339 OMAKECHAN, 1340 OMAKEMAP, 1341 OMAKESLICE, 1342 OARRAYRUNESTR, 1343 OARRAYBYTESTR, 1344 OSTRARRAYRUNE, 1345 OSTRARRAYBYTE, 1346 OADDSTR, 1347 OMAPLIT, 1348 ONEW, 1349 OCLOSURE, 1350 OCALLPART, 1351 ORUNESTR, 1352 OCONVIFACE: 1353 if leaks { 1354 src.Esc = EscHeap 1355 if Debug['m'] != 0 { 1356 Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort)) 1357 } 1358 } 1359 1360 case ODOT, 1361 OSLICE, 1362 OSLICEARR, 1363 OSLICE3, 1364 OSLICE3ARR, 1365 OSLICESTR: 1366 escwalk(e, level, dst, src.Left) 1367 1368 case OINDEX: 1369 if Isfixedarray(src.Left.Type) { 1370 escwalk(e, level, dst, src.Left) 1371 break 1372 } 1373 fallthrough 1374 1375 // fall through 1376 case ODOTPTR, OINDEXMAP, OIND: 1377 newlevel := level 1378 1379 if level > MinLevel { 1380 newlevel++ 1381 } 1382 escwalk(e, newlevel, dst, src.Left) 1383 } 1384 1385 recurse: 1386 for ll := src.Escflowsrc; ll != nil; ll = ll.Next { 1387 escwalk(e, level, dst, ll.N) 1388 } 1389 1390 e.pdepth-- 1391 } 1392 1393 func esctag(e *EscState, func_ *Node) { 1394 func_.Esc = EscFuncTagged 1395 1396 // External functions are assumed unsafe, 1397 // unless //go:noescape is given before the declaration. 1398 if func_.Nbody == nil { 1399 if func_.Noescape { 1400 for t := getinargx(func_.Type).Type; t != nil; t = t.Down { 1401 if haspointers(t.Type) { 1402 t.Note = mktag(EscNone) 1403 } 1404 } 1405 } 1406 1407 return 1408 } 1409 1410 savefn := Curfn 1411 Curfn = func_ 1412 1413 for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { 1414 if ll.N.Op != ONAME || ll.N.Class != PPARAM { 1415 continue 1416 } 1417 1418 switch ll.N.Esc & EscMask { 1419 case EscNone, // not touched by escflood 1420 EscReturn: 1421 if haspointers(ll.N.Type) { // don't bother tagging for scalars 1422 ll.N.Paramfld.Note = mktag(int(ll.N.Esc)) 1423 } 1424 1425 case EscHeap, // touched by escflood, moved to heap 1426 EscScope: // touched by escflood, value leaves scope 1427 break 1428 } 1429 } 1430 1431 Curfn = savefn 1432 }