github.com/bir3/gocompiler@v0.9.2202/src/cmd/compile/internal/ir/func.go (about) 1 // Copyright 2020 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 ir 6 7 import ( 8 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 9 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 10 "github.com/bir3/gocompiler/src/cmd/internal/obj" 11 "github.com/bir3/gocompiler/src/cmd/internal/objabi" 12 "github.com/bir3/gocompiler/src/cmd/internal/src" 13 "fmt" 14 "strings" 15 "unicode/utf8" 16 ) 17 18 // A Func corresponds to a single function in a Go program 19 // (and vice versa: each function is denoted by exactly one *Func). 20 // 21 // There are multiple nodes that represent a Func in the IR. 22 // 23 // The ONAME node (Func.Nname) is used for plain references to it. 24 // The ODCLFUNC node (the Func itself) is used for its declaration code. 25 // The OCLOSURE node (Func.OClosure) is used for a reference to a 26 // function literal. 27 // 28 // An imported function will have an ONAME node which points to a Func 29 // with an empty body. 30 // A declared function or method has an ODCLFUNC (the Func itself) and an ONAME. 31 // A function literal is represented directly by an OCLOSURE, but it also 32 // has an ODCLFUNC (and a matching ONAME) representing the compiled 33 // underlying form of the closure, which accesses the captured variables 34 // using a special data structure passed in a register. 35 // 36 // A method declaration is represented like functions, except f.Sym 37 // will be the qualified method name (e.g., "T.m"). 38 // 39 // A method expression (T.M) is represented as an OMETHEXPR node, 40 // in which n.Left and n.Right point to the type and method, respectively. 41 // Each distinct mention of a method expression in the source code 42 // constructs a fresh node. 43 // 44 // A method value (t.M) is represented by ODOTMETH/ODOTINTER 45 // when it is called directly and by OMETHVALUE otherwise. 46 // These are like method expressions, except that for ODOTMETH/ODOTINTER, 47 // the method name is stored in Sym instead of Right. 48 // Each OMETHVALUE ends up being implemented as a new 49 // function, a bit like a closure, with its own ODCLFUNC. 50 // The OMETHVALUE uses n.Func to record the linkage to 51 // the generated ODCLFUNC, but there is no 52 // pointer from the Func back to the OMETHVALUE. 53 type Func struct { 54 miniNode 55 Body Nodes 56 57 Nname *Name // ONAME node 58 OClosure *ClosureExpr // OCLOSURE node 59 60 // ONAME nodes for all params/locals for this func/closure, does NOT 61 // include closurevars until transforming closures during walk. 62 // Names must be listed PPARAMs, PPARAMOUTs, then PAUTOs, 63 // with PPARAMs and PPARAMOUTs in order corresponding to the function signature. 64 // Anonymous and blank params are declared as ~pNN (for PPARAMs) and ~rNN (for PPARAMOUTs). 65 Dcl []*Name 66 67 // ClosureVars lists the free variables that are used within a 68 // function literal, but formally declared in an enclosing 69 // function. The variables in this slice are the closure function's 70 // own copy of the variables, which are used within its function 71 // body. They will also each have IsClosureVar set, and will have 72 // Byval set if they're captured by value. 73 ClosureVars []*Name 74 75 // Enclosed functions that need to be compiled. 76 // Populated during walk. 77 Closures []*Func 78 79 // Parents records the parent scope of each scope within a 80 // function. The root scope (0) has no parent, so the i'th 81 // scope's parent is stored at Parents[i-1]. 82 Parents []ScopeID 83 84 // Marks records scope boundary changes. 85 Marks []Mark 86 87 FieldTrack map[*obj.LSym]struct{} 88 DebugInfo interface{} 89 LSym *obj.LSym // Linker object in this function's native ABI (Func.ABI) 90 91 Inl *Inline 92 93 // funcLitGen and goDeferGen track how many closures have been 94 // created in this function for function literals and go/defer 95 // wrappers, respectively. Used by closureName for creating unique 96 // function names. 97 // 98 // Tracking goDeferGen separately avoids wrappers throwing off 99 // function literal numbering (e.g., runtime/trace_test.TestTraceSymbolize.func11). 100 funcLitGen int32 101 goDeferGen int32 102 103 Label int32 // largest auto-generated label in this function 104 105 Endlineno src.XPos 106 WBPos src.XPos // position of first write barrier; see SetWBPos 107 108 Pragma PragmaFlag // go:xxx function annotations 109 110 flags bitset16 111 112 // ABI is a function's "definition" ABI. This is the ABI that 113 // this function's generated code is expecting to be called by. 114 // 115 // For most functions, this will be obj.ABIInternal. It may be 116 // a different ABI for functions defined in assembly or ABI wrappers. 117 // 118 // This is included in the export data and tracked across packages. 119 ABI obj.ABI 120 // ABIRefs is the set of ABIs by which this function is referenced. 121 // For ABIs other than this function's definition ABI, the 122 // compiler generates ABI wrapper functions. This is only tracked 123 // within a package. 124 ABIRefs obj.ABISet 125 126 NumDefers int32 // number of defer calls in the function 127 NumReturns int32 // number of explicit returns in the function 128 129 // NWBRCalls records the LSyms of functions called by this 130 // function for go:nowritebarrierrec analysis. Only filled in 131 // if nowritebarrierrecCheck != nil. 132 NWBRCalls *[]SymAndPos 133 134 // For wrapper functions, WrappedFunc point to the original Func. 135 // Currently only used for go/defer wrappers. 136 WrappedFunc *Func 137 138 // WasmImport is used by the //go:wasmimport directive to store info about 139 // a WebAssembly function import. 140 WasmImport *WasmImport 141 } 142 143 // WasmImport stores metadata associated with the //go:wasmimport pragma. 144 type WasmImport struct { 145 Module string 146 Name string 147 } 148 149 // NewFunc returns a new Func with the given name and type. 150 // 151 // fpos is the position of the "func" token, and npos is the position 152 // of the name identifier. 153 // 154 // TODO(mdempsky): I suspect there's no need for separate fpos and 155 // npos. 156 func NewFunc(fpos, npos src.XPos, sym *types.Sym, typ *types.Type) *Func { 157 name := NewNameAt(npos, sym, typ) 158 name.Class = PFUNC 159 sym.SetFunc(true) 160 161 fn := &Func{Nname: name} 162 fn.pos = fpos 163 fn.op = ODCLFUNC 164 // Most functions are ABIInternal. The importer or symabis 165 // pass may override this. 166 fn.ABI = obj.ABIInternal 167 fn.SetTypecheck(1) 168 169 name.Func = fn 170 171 return fn 172 } 173 174 func (f *Func) isStmt() {} 175 176 func (n *Func) copy() Node { panic(n.no("copy")) } 177 func (n *Func) doChildren(do func(Node) bool) bool { return doNodes(n.Body, do) } 178 func (n *Func) editChildren(edit func(Node) Node) { editNodes(n.Body, edit) } 179 func (n *Func) editChildrenWithHidden(edit func(Node) Node) { editNodes(n.Body, edit) } 180 181 func (f *Func) Type() *types.Type { return f.Nname.Type() } 182 func (f *Func) Sym() *types.Sym { return f.Nname.Sym() } 183 func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() } 184 func (f *Func) LinksymABI(abi obj.ABI) *obj.LSym { return f.Nname.LinksymABI(abi) } 185 186 // An Inline holds fields used for function bodies that can be inlined. 187 type Inline struct { 188 Cost int32 // heuristic cost of inlining this function 189 190 // Copy of Func.Dcl for use during inlining. This copy is needed 191 // because the function's Dcl may change from later compiler 192 // transformations. This field is also populated when a function 193 // from another package is imported and inlined. 194 Dcl []*Name 195 HaveDcl bool // whether we've loaded Dcl 196 197 // Function properties, encoded as a string (these are used for 198 // making inlining decisions). See cmd/compile/internal/inline/inlheur. 199 Properties string 200 201 // CanDelayResults reports whether it's safe for the inliner to delay 202 // initializing the result parameters until immediately before the 203 // "return" statement. 204 CanDelayResults bool 205 } 206 207 // A Mark represents a scope boundary. 208 type Mark struct { 209 // Pos is the position of the token that marks the scope 210 // change. 211 Pos src.XPos 212 213 // Scope identifies the innermost scope to the right of Pos. 214 Scope ScopeID 215 } 216 217 // A ScopeID represents a lexical scope within a function. 218 type ScopeID int32 219 220 const ( 221 funcDupok = 1 << iota // duplicate definitions ok 222 funcWrapper // hide frame from users (elide in tracebacks, don't count as a frame for recover()) 223 funcABIWrapper // is an ABI wrapper (also set flagWrapper) 224 funcNeedctxt // function uses context register (has closure variables) 225 // true if closure inside a function; false if a simple function or a 226 // closure in a global variable initialization 227 funcIsHiddenClosure 228 funcIsDeadcodeClosure // true if closure is deadcode 229 funcHasDefer // contains a defer statement 230 funcNilCheckDisabled // disable nil checks when compiling this function 231 funcInlinabilityChecked // inliner has already determined whether the function is inlinable 232 funcNeverReturns // function never returns (in most cases calls panic(), os.Exit(), or equivalent) 233 funcOpenCodedDeferDisallowed // can't do open-coded defers 234 funcClosureResultsLost // closure is called indirectly and we lost track of its results; used by escape analysis 235 funcPackageInit // compiler emitted .init func for package 236 ) 237 238 type SymAndPos struct { 239 Sym *obj.LSym // LSym of callee 240 Pos src.XPos // line of call 241 } 242 243 func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 } 244 func (f *Func) Wrapper() bool { return f.flags&funcWrapper != 0 } 245 func (f *Func) ABIWrapper() bool { return f.flags&funcABIWrapper != 0 } 246 func (f *Func) Needctxt() bool { return f.flags&funcNeedctxt != 0 } 247 func (f *Func) IsHiddenClosure() bool { return f.flags&funcIsHiddenClosure != 0 } 248 func (f *Func) IsDeadcodeClosure() bool { return f.flags&funcIsDeadcodeClosure != 0 } 249 func (f *Func) HasDefer() bool { return f.flags&funcHasDefer != 0 } 250 func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 } 251 func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 } 252 func (f *Func) NeverReturns() bool { return f.flags&funcNeverReturns != 0 } 253 func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 } 254 func (f *Func) ClosureResultsLost() bool { return f.flags&funcClosureResultsLost != 0 } 255 func (f *Func) IsPackageInit() bool { return f.flags&funcPackageInit != 0 } 256 257 func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) } 258 func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) } 259 func (f *Func) SetABIWrapper(b bool) { f.flags.set(funcABIWrapper, b) } 260 func (f *Func) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) } 261 func (f *Func) SetIsHiddenClosure(b bool) { f.flags.set(funcIsHiddenClosure, b) } 262 func (f *Func) SetIsDeadcodeClosure(b bool) { f.flags.set(funcIsDeadcodeClosure, b) } 263 func (f *Func) SetHasDefer(b bool) { f.flags.set(funcHasDefer, b) } 264 func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) } 265 func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) } 266 func (f *Func) SetNeverReturns(b bool) { f.flags.set(funcNeverReturns, b) } 267 func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) } 268 func (f *Func) SetClosureResultsLost(b bool) { f.flags.set(funcClosureResultsLost, b) } 269 func (f *Func) SetIsPackageInit(b bool) { f.flags.set(funcPackageInit, b) } 270 271 func (f *Func) SetWBPos(pos src.XPos) { 272 if base.Debug.WB != 0 { 273 base.WarnfAt(pos, "write barrier") 274 } 275 if !f.WBPos.IsKnown() { 276 f.WBPos = pos 277 } 278 } 279 280 // FuncName returns the name (without the package) of the function f. 281 func FuncName(f *Func) string { 282 if f == nil || f.Nname == nil { 283 return "<nil>" 284 } 285 return f.Sym().Name 286 } 287 288 // PkgFuncName returns the name of the function referenced by f, with package 289 // prepended. 290 // 291 // This differs from the compiler's internal convention where local functions 292 // lack a package. This is primarily useful when the ultimate consumer of this 293 // is a human looking at message. 294 func PkgFuncName(f *Func) string { 295 if f == nil || f.Nname == nil { 296 return "<nil>" 297 } 298 s := f.Sym() 299 pkg := s.Pkg 300 301 return pkg.Path + "." + s.Name 302 } 303 304 // LinkFuncName returns the name of the function f, as it will appear in the 305 // symbol table of the final linked binary. 306 func LinkFuncName(f *Func) string { 307 if f == nil || f.Nname == nil { 308 return "<nil>" 309 } 310 s := f.Sym() 311 pkg := s.Pkg 312 313 return objabi.PathToPrefix(pkg.Path) + "." + s.Name 314 } 315 316 // ParseLinkFuncName parsers a symbol name (as returned from LinkFuncName) back 317 // to the package path and local symbol name. 318 func ParseLinkFuncName(name string) (pkg, sym string, err error) { 319 pkg, sym = splitPkg(name) 320 if pkg == "" { 321 return "", "", fmt.Errorf("no package path in name") 322 } 323 324 pkg, err = objabi.PrefixToPath(pkg) // unescape 325 if err != nil { 326 return "", "", fmt.Errorf("malformed package path: %v", err) 327 } 328 329 return pkg, sym, nil 330 } 331 332 // Borrowed from x/mod. 333 func modPathOK(r rune) bool { 334 if r < utf8.RuneSelf { 335 return r == '-' || r == '.' || r == '_' || r == '~' || 336 '0' <= r && r <= '9' || 337 'A' <= r && r <= 'Z' || 338 'a' <= r && r <= 'z' 339 } 340 return false 341 } 342 343 func escapedImportPathOK(r rune) bool { 344 return modPathOK(r) || r == '+' || r == '/' || r == '%' 345 } 346 347 // splitPkg splits the full linker symbol name into package and local symbol 348 // name. 349 func splitPkg(name string) (pkgpath, sym string) { 350 // package-sym split is at first dot after last the / that comes before 351 // any characters illegal in a package path. 352 353 lastSlashIdx := 0 354 for i, r := range name { 355 // Catches cases like: 356 // * example.foo[sync/atomic.Uint64]. 357 // * example%2ecom.foo[sync/atomic.Uint64]. 358 // 359 // Note that name is still escaped; unescape occurs after splitPkg. 360 if !escapedImportPathOK(r) { 361 break 362 } 363 if r == '/' { 364 lastSlashIdx = i 365 } 366 } 367 for i := lastSlashIdx; i < len(name); i++ { 368 r := name[i] 369 if r == '.' { 370 return name[:i], name[i+1:] 371 } 372 } 373 374 return "", name 375 } 376 377 var CurFunc *Func 378 379 // WithFunc invokes do with CurFunc and base.Pos set to curfn and 380 // curfn.Pos(), respectively, and then restores their previous values 381 // before returning. 382 func WithFunc(curfn *Func, do func()) { 383 oldfn, oldpos := CurFunc, base.Pos 384 defer func() { CurFunc, base.Pos = oldfn, oldpos }() 385 386 CurFunc, base.Pos = curfn, curfn.Pos() 387 do() 388 } 389 390 func FuncSymName(s *types.Sym) string { 391 return s.Name + "·f" 392 } 393 394 // ClosureDebugRuntimeCheck applies boilerplate checks for debug flags 395 // and compiling runtime. 396 func ClosureDebugRuntimeCheck(clo *ClosureExpr) { 397 if base.Debug.Closure > 0 { 398 if clo.Esc() == EscHeap { 399 base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func.ClosureVars) 400 } else { 401 base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars) 402 } 403 } 404 if base.Flag.CompilingRuntime && clo.Esc() == EscHeap && !clo.IsGoWrap { 405 base.ErrorfAt(clo.Pos(), 0, "heap-allocated closure %s, not allowed in runtime", FuncName(clo.Func)) 406 } 407 } 408 409 // IsTrivialClosure reports whether closure clo has an 410 // empty list of captured vars. 411 func IsTrivialClosure(clo *ClosureExpr) bool { 412 return len(clo.Func.ClosureVars) == 0 413 } 414 415 // globClosgen is like Func.Closgen, but for the global scope. 416 var globClosgen int32 417 418 // closureName generates a new unique name for a closure within outerfn at pos. 419 func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym { 420 pkg := types.LocalPkg 421 outer := "glob." 422 var prefix string 423 switch why { 424 default: 425 base.FatalfAt(pos, "closureName: bad Op: %v", why) 426 case OCLOSURE: 427 if outerfn == nil || outerfn.OClosure == nil { 428 prefix = "func" 429 } 430 case OGO: 431 prefix = "gowrap" 432 case ODEFER: 433 prefix = "deferwrap" 434 } 435 gen := &globClosgen 436 437 // There may be multiple functions named "_". In those 438 // cases, we can't use their individual Closgens as it 439 // would lead to name clashes. 440 if outerfn != nil && !IsBlank(outerfn.Nname) { 441 pkg = outerfn.Sym().Pkg 442 outer = FuncName(outerfn) 443 444 if why == OCLOSURE { 445 gen = &outerfn.funcLitGen 446 } else { 447 gen = &outerfn.goDeferGen 448 } 449 } 450 451 // If this closure was created due to inlining, then incorporate any 452 // inlined functions' names into the closure's linker symbol name 453 // too (#60324). 454 if inlIndex := base.Ctxt.InnermostPos(pos).Base().InliningIndex(); inlIndex >= 0 { 455 names := []string{outer} 456 base.Ctxt.InlTree.AllParents(inlIndex, func(call obj.InlinedCall) { 457 names = append(names, call.Name) 458 }) 459 outer = strings.Join(names, ".") 460 } 461 462 *gen++ 463 return pkg.Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen)) 464 } 465 466 // NewClosureFunc creates a new Func to represent a function literal 467 // with the given type. 468 // 469 // fpos the position used for the underlying ODCLFUNC and ONAME, 470 // whereas cpos is the position used for the OCLOSURE. They're 471 // separate because in the presence of inlining, the OCLOSURE node 472 // should have an inline-adjusted position, whereas the ODCLFUNC and 473 // ONAME must not. 474 // 475 // outerfn is the enclosing function, if any. The returned function is 476 // appending to pkg.Funcs. 477 // 478 // why is the reason we're generating this Func. It can be OCLOSURE 479 // (for a normal function literal) or OGO or ODEFER (for wrapping a 480 // call expression that has parameters or results). 481 func NewClosureFunc(fpos, cpos src.XPos, why Op, typ *types.Type, outerfn *Func, pkg *Package) *Func { 482 fn := NewFunc(fpos, fpos, closureName(outerfn, cpos, why), typ) 483 fn.SetIsHiddenClosure(outerfn != nil) 484 485 clo := &ClosureExpr{Func: fn} 486 clo.op = OCLOSURE 487 clo.pos = cpos 488 clo.SetType(typ) 489 clo.SetTypecheck(1) 490 fn.OClosure = clo 491 492 fn.Nname.Defn = fn 493 pkg.Funcs = append(pkg.Funcs, fn) 494 495 return fn 496 } 497 498 // IsFuncPCIntrinsic returns whether n is a direct call of internal/abi.FuncPCABIxxx functions. 499 func IsFuncPCIntrinsic(n *CallExpr) bool { 500 if n.Op() != OCALLFUNC || n.Fun.Op() != ONAME { 501 return false 502 } 503 fn := n.Fun.(*Name).Sym() 504 return (fn.Name == "FuncPCABI0" || fn.Name == "FuncPCABIInternal") && 505 fn.Pkg.Path == "internal/abi" 506 } 507 508 // IsIfaceOfFunc inspects whether n is an interface conversion from a direct 509 // reference of a func. If so, it returns referenced Func; otherwise nil. 510 // 511 // This is only usable before walk.walkConvertInterface, which converts to an 512 // OMAKEFACE. 513 func IsIfaceOfFunc(n Node) *Func { 514 if n, ok := n.(*ConvExpr); ok && n.Op() == OCONVIFACE { 515 if name, ok := n.X.(*Name); ok && name.Op() == ONAME && name.Class == PFUNC { 516 return name.Func 517 } 518 } 519 return nil 520 } 521 522 // FuncPC returns a uintptr-typed expression that evaluates to the PC of a 523 // function as uintptr, as returned by internal/abi.FuncPC{ABI0,ABIInternal}. 524 // 525 // n should be a Node of an interface type, as is passed to 526 // internal/abi.FuncPC{ABI0,ABIInternal}. 527 // 528 // TODO(prattmic): Since n is simply an interface{} there is no assertion that 529 // it is actually a function at all. Perhaps we should emit a runtime type 530 // assertion? 531 func FuncPC(pos src.XPos, n Node, wantABI obj.ABI) Node { 532 if !n.Type().IsInterface() { 533 base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s expects an interface value, got %v", wantABI, n.Type()) 534 } 535 536 if fn := IsIfaceOfFunc(n); fn != nil { 537 name := fn.Nname 538 abi := fn.ABI 539 if abi != wantABI { 540 base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s expects an %v function, %s is defined as %v", wantABI, wantABI, name.Sym().Name, abi) 541 } 542 var e Node = NewLinksymExpr(pos, name.Sym().LinksymABI(abi), types.Types[types.TUINTPTR]) 543 e = NewAddrExpr(pos, e) 544 e.SetType(types.Types[types.TUINTPTR].PtrTo()) 545 e = NewConvExpr(pos, OCONVNOP, types.Types[types.TUINTPTR], e) 546 e.SetTypecheck(1) 547 return e 548 } 549 // fn is not a defined function. It must be ABIInternal. 550 // Read the address from func value, i.e. *(*uintptr)(idata(fn)). 551 if wantABI != obj.ABIInternal { 552 base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s does not accept func expression, which is ABIInternal", wantABI) 553 } 554 var e Node = NewUnaryExpr(pos, OIDATA, n) 555 e.SetType(types.Types[types.TUINTPTR].PtrTo()) 556 e.SetTypecheck(1) 557 e = NewStarExpr(pos, e) 558 e.SetType(types.Types[types.TUINTPTR]) 559 e.SetTypecheck(1) 560 return e 561 } 562 563 // DeclareParams creates Names for all of the parameters in fn's 564 // signature and adds them to fn.Dcl. 565 // 566 // If setNname is true, then it also sets types.Field.Nname for each 567 // parameter. 568 func (fn *Func) DeclareParams(setNname bool) { 569 if fn.Dcl != nil { 570 base.FatalfAt(fn.Pos(), "%v already has Dcl", fn) 571 } 572 573 declareParams := func(params []*types.Field, ctxt Class, prefix string, offset int) { 574 for i, param := range params { 575 sym := param.Sym 576 if sym == nil || sym.IsBlank() { 577 sym = fn.Sym().Pkg.LookupNum(prefix, i) 578 } 579 580 name := NewNameAt(param.Pos, sym, param.Type) 581 name.Class = ctxt 582 name.Curfn = fn 583 fn.Dcl[offset+i] = name 584 585 if setNname { 586 param.Nname = name 587 } 588 } 589 } 590 591 sig := fn.Type() 592 params := sig.RecvParams() 593 results := sig.Results() 594 595 fn.Dcl = make([]*Name, len(params)+len(results)) 596 declareParams(params, PPARAM, "~p", 0) 597 declareParams(results, PPARAMOUT, "~r", len(params)) 598 }