1 //===-- LibCallsShrinkWrap.cpp ----------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This pass shrink-wraps a call to function if the result is not used. 11 // The call can set errno but is otherwise side effect free. For example: 12 // sqrt(val); 13 // is transformed to 14 // if (val < 0) 15 // sqrt(val); 16 // Even if the result of library call is not being used, the compiler cannot 17 // safely delete the call because the function can set errno on error 18 // conditions. 19 // Note in many functions, the error condition solely depends on the incoming 20 // parameter. In this optimization, we can generate the condition can lead to 21 // the errno to shrink-wrap the call. Since the chances of hitting the error 22 // condition is low, the runtime call is effectively eliminated. 23 // 24 // These partially dead calls are usually results of C++ abstraction penalty 25 // exposed by inlining. 26 // 27 //===----------------------------------------------------------------------===// 28 29 #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h" 30 #include "llvm/ADT/SmallVector.h" 31 #include "llvm/ADT/Statistic.h" 32 #include "llvm/Analysis/GlobalsModRef.h" 33 #include "llvm/Analysis/TargetLibraryInfo.h" 34 #include "llvm/IR/CFG.h" 35 #include "llvm/IR/Constants.h" 36 #include "llvm/IR/Function.h" 37 #include "llvm/IR/IRBuilder.h" 38 #include "llvm/IR/InstVisitor.h" 39 #include "llvm/IR/Instructions.h" 40 #include "llvm/IR/LLVMContext.h" 41 #include "llvm/IR/MDBuilder.h" 42 #include "llvm/Pass.h" 43 #include "llvm/Transforms/Utils/BasicBlockUtils.h" 44 using namespace llvm; 45 46 #define DEBUG_TYPE "libcalls-shrinkwrap" 47 48 STATISTIC(NumWrappedOneCond, "Number of One-Condition Wrappers Inserted"); 49 STATISTIC(NumWrappedTwoCond, "Number of Two-Condition Wrappers Inserted"); 50 51 static cl::opt<bool> LibCallsShrinkWrapDoDomainError( 52 "libcalls-shrinkwrap-domain-error", cl::init(true), cl::Hidden, 53 cl::desc("Perform shrink-wrap on lib calls with domain errors")); 54 static cl::opt<bool> LibCallsShrinkWrapDoRangeError( 55 "libcalls-shrinkwrap-range-error", cl::init(true), cl::Hidden, 56 cl::desc("Perform shrink-wrap on lib calls with range errors")); 57 static cl::opt<bool> LibCallsShrinkWrapDoPoleError( 58 "libcalls-shrinkwrap-pole-error", cl::init(true), cl::Hidden, 59 cl::desc("Perform shrink-wrap on lib calls with pole errors")); 60 61 namespace { 62 class LibCallsShrinkWrapLegacyPass : public FunctionPass { 63 public: 64 static char ID; // Pass identification, replacement for typeid 65 explicit LibCallsShrinkWrapLegacyPass() : FunctionPass(ID) { 66 initializeLibCallsShrinkWrapLegacyPassPass( 67 *PassRegistry::getPassRegistry()); 68 } 69 void getAnalysisUsage(AnalysisUsage &AU) const override; 70 bool runOnFunction(Function &F) override; 71 }; 72 } 73 74 char LibCallsShrinkWrapLegacyPass::ID = 0; 75 INITIALIZE_PASS_BEGIN(LibCallsShrinkWrapLegacyPass, "libcalls-shrinkwrap", 76 "Conditionally eliminate dead library calls", false, 77 false) 78 INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) 79 INITIALIZE_PASS_END(LibCallsShrinkWrapLegacyPass, "libcalls-shrinkwrap", 80 "Conditionally eliminate dead library calls", false, false) 81 82 class LibCallsShrinkWrap : public InstVisitor<LibCallsShrinkWrap> { 83 public: 84 LibCallsShrinkWrap(const TargetLibraryInfo &TLI) : TLI(TLI), Changed(false){}; 85 bool isChanged() const { return Changed; } 86 void visitCallInst(CallInst &CI) { checkCandidate(CI); } 87 void perform() { 88 for (auto &CI : WorkList) { 89 DEBUG(dbgs() << "CDCE calls: " << CI->getCalledFunction()->getName() 90 << "\n"); 91 if (perform(CI)) { 92 Changed = true; 93 DEBUG(dbgs() << "Transformed\n"); 94 } 95 } 96 } 97 98 private: 99 bool perform(CallInst *CI); 100 void checkCandidate(CallInst &CI); 101 void shrinkWrapCI(CallInst *CI, Value *Cond); 102 bool performCallDomainErrorOnly(CallInst *CI, const LibFunc::Func &Func); 103 bool performCallErrors(CallInst *CI, const LibFunc::Func &Func); 104 bool performCallRangeErrorOnly(CallInst *CI, const LibFunc::Func &Func); 105 Value *generateOneRangeCond(CallInst *CI, const LibFunc::Func &Func); 106 Value *generateTwoRangeCond(CallInst *CI, const LibFunc::Func &Func); 107 Value *generateCondForPow(CallInst *CI, const LibFunc::Func &Func); 108 109 // Create an OR of two conditions. 110 Value *createOrCond(CallInst *CI, CmpInst::Predicate Cmp, float Val, 111 CmpInst::Predicate Cmp2, float Val2) { 112 IRBuilder<> BBBuilder(CI); 113 Value *Arg = CI->getArgOperand(0); 114 auto Cond2 = createCond(BBBuilder, Arg, Cmp2, Val2); 115 auto Cond1 = createCond(BBBuilder, Arg, Cmp, Val); 116 return BBBuilder.CreateOr(Cond1, Cond2); 117 } 118 119 // Create a single condition using IRBuilder. 120 Value *createCond(IRBuilder<> &BBBuilder, Value *Arg, CmpInst::Predicate Cmp, 121 float Val) { 122 Constant *V = ConstantFP::get(BBBuilder.getContext(), APFloat(Val)); 123 if (!Arg->getType()->isFloatTy()) 124 V = ConstantExpr::getFPExtend(V, Arg->getType()); 125 return BBBuilder.CreateFCmp(Cmp, Arg, V); 126 } 127 128 // Create a single condition. 129 Value *createCond(CallInst *CI, CmpInst::Predicate Cmp, float Val) { 130 IRBuilder<> BBBuilder(CI); 131 Value *Arg = CI->getArgOperand(0); 132 return createCond(BBBuilder, Arg, Cmp, Val); 133 } 134 135 const TargetLibraryInfo &TLI; 136 SmallVector<CallInst *, 16> WorkList; 137 bool Changed; 138 }; 139 140 // Perform the transformation to calls with errno set by domain error. 141 bool LibCallsShrinkWrap::performCallDomainErrorOnly(CallInst *CI, 142 const LibFunc::Func &Func) { 143 Value *Cond = nullptr; 144 145 switch (Func) { 146 case LibFunc::acos: // DomainError: (x < -1 || x > 1) 147 case LibFunc::acosf: // Same as acos 148 case LibFunc::acosl: // Same as acos 149 case LibFunc::asin: // DomainError: (x < -1 || x > 1) 150 case LibFunc::asinf: // Same as asin 151 case LibFunc::asinl: // Same as asin 152 { 153 ++NumWrappedTwoCond; 154 Cond = createOrCond(CI, CmpInst::FCMP_OLT, -1.0f, CmpInst::FCMP_OGT, 1.0f); 155 break; 156 } 157 case LibFunc::cos: // DomainError: (x == +inf || x == -inf) 158 case LibFunc::cosf: // Same as cos 159 case LibFunc::cosl: // Same as cos 160 case LibFunc::sin: // DomainError: (x == +inf || x == -inf) 161 case LibFunc::sinf: // Same as sin 162 case LibFunc::sinl: // Same as sin 163 { 164 ++NumWrappedTwoCond; 165 Cond = createOrCond(CI, CmpInst::FCMP_OEQ, INFINITY, CmpInst::FCMP_OEQ, 166 -INFINITY); 167 break; 168 } 169 case LibFunc::acosh: // DomainError: (x < 1) 170 case LibFunc::acoshf: // Same as acosh 171 case LibFunc::acoshl: // Same as acosh 172 { 173 ++NumWrappedOneCond; 174 Cond = createCond(CI, CmpInst::FCMP_OLT, 1.0f); 175 break; 176 } 177 case LibFunc::sqrt: // DomainError: (x < 0) 178 case LibFunc::sqrtf: // Same as sqrt 179 case LibFunc::sqrtl: // Same as sqrt 180 { 181 ++NumWrappedOneCond; 182 Cond = createCond(CI, CmpInst::FCMP_OLT, 0.0f); 183 break; 184 } 185 default: 186 return false; 187 } 188 shrinkWrapCI(CI, Cond); 189 return true; 190 } 191 192 // Perform the transformation to calls with errno set by range error. 193 bool LibCallsShrinkWrap::performCallRangeErrorOnly(CallInst *CI, 194 const LibFunc::Func &Func) { 195 Value *Cond = nullptr; 196 197 switch (Func) { 198 case LibFunc::cosh: 199 case LibFunc::coshf: 200 case LibFunc::coshl: 201 case LibFunc::exp: 202 case LibFunc::expf: 203 case LibFunc::expl: 204 case LibFunc::exp10: 205 case LibFunc::exp10f: 206 case LibFunc::exp10l: 207 case LibFunc::exp2: 208 case LibFunc::exp2f: 209 case LibFunc::exp2l: 210 case LibFunc::sinh: 211 case LibFunc::sinhf: 212 case LibFunc::sinhl: { 213 Cond = generateTwoRangeCond(CI, Func); 214 break; 215 } 216 case LibFunc::expm1: // RangeError: (709, inf) 217 case LibFunc::expm1f: // RangeError: (88, inf) 218 case LibFunc::expm1l: // RangeError: (11356, inf) 219 { 220 Cond = generateOneRangeCond(CI, Func); 221 break; 222 } 223 default: 224 return false; 225 } 226 shrinkWrapCI(CI, Cond); 227 return true; 228 } 229 230 // Perform the transformation to calls with errno set by combination of errors. 231 bool LibCallsShrinkWrap::performCallErrors(CallInst *CI, 232 const LibFunc::Func &Func) { 233 Value *Cond = nullptr; 234 235 switch (Func) { 236 case LibFunc::atanh: // DomainError: (x < -1 || x > 1) 237 // PoleError: (x == -1 || x == 1) 238 // Overall Cond: (x <= -1 || x >= 1) 239 case LibFunc::atanhf: // Same as atanh 240 case LibFunc::atanhl: // Same as atanh 241 { 242 if (!LibCallsShrinkWrapDoDomainError || !LibCallsShrinkWrapDoPoleError) 243 return false; 244 ++NumWrappedTwoCond; 245 Cond = createOrCond(CI, CmpInst::FCMP_OLE, -1.0f, CmpInst::FCMP_OGE, 1.0f); 246 break; 247 } 248 case LibFunc::log: // DomainError: (x < 0) 249 // PoleError: (x == 0) 250 // Overall Cond: (x <= 0) 251 case LibFunc::logf: // Same as log 252 case LibFunc::logl: // Same as log 253 case LibFunc::log10: // Same as log 254 case LibFunc::log10f: // Same as log 255 case LibFunc::log10l: // Same as log 256 case LibFunc::log2: // Same as log 257 case LibFunc::log2f: // Same as log 258 case LibFunc::log2l: // Same as log 259 case LibFunc::logb: // Same as log 260 case LibFunc::logbf: // Same as log 261 case LibFunc::logbl: // Same as log 262 { 263 if (!LibCallsShrinkWrapDoDomainError || !LibCallsShrinkWrapDoPoleError) 264 return false; 265 ++NumWrappedOneCond; 266 Cond = createCond(CI, CmpInst::FCMP_OLE, 0.0f); 267 break; 268 } 269 case LibFunc::log1p: // DomainError: (x < -1) 270 // PoleError: (x == -1) 271 // Overall Cond: (x <= -1) 272 case LibFunc::log1pf: // Same as log1p 273 case LibFunc::log1pl: // Same as log1p 274 { 275 if (!LibCallsShrinkWrapDoDomainError || !LibCallsShrinkWrapDoPoleError) 276 return false; 277 ++NumWrappedOneCond; 278 Cond = createCond(CI, CmpInst::FCMP_OLE, -1.0f); 279 break; 280 } 281 case LibFunc::pow: // DomainError: x < 0 and y is noninteger 282 // PoleError: x == 0 and y < 0 283 // RangeError: overflow or underflow 284 case LibFunc::powf: 285 case LibFunc::powl: { 286 if (!LibCallsShrinkWrapDoDomainError || !LibCallsShrinkWrapDoPoleError || 287 !LibCallsShrinkWrapDoRangeError) 288 return false; 289 Cond = generateCondForPow(CI, Func); 290 if (Cond == nullptr) 291 return false; 292 break; 293 } 294 default: 295 return false; 296 } 297 assert(Cond && "performCallErrors should not see an empty condition"); 298 shrinkWrapCI(CI, Cond); 299 return true; 300 } 301 302 // Checks if CI is a candidate for shrinkwrapping and put it into work list if 303 // true. 304 void LibCallsShrinkWrap::checkCandidate(CallInst &CI) { 305 if (CI.isNoBuiltin()) 306 return; 307 // A possible improvement is to handle the calls with the return value being 308 // used. If there is API for fast libcall implementation without setting 309 // errno, we can use the same framework to direct/wrap the call to the fast 310 // API in the error free path, and leave the original call in the slow path. 311 if (!CI.use_empty()) 312 return; 313 314 LibFunc::Func Func; 315 Function *Callee = CI.getCalledFunction(); 316 if (!Callee) 317 return; 318 if (!TLI.getLibFunc(*Callee, Func) || !TLI.has(Func)) 319 return; 320 321 if (CI.getNumArgOperands() == 0) 322 return; 323 // TODO: Handle long double in other formats. 324 Type *ArgType = CI.getArgOperand(0)->getType(); 325 if (!(ArgType->isFloatTy() || ArgType->isDoubleTy() || 326 ArgType->isX86_FP80Ty())) 327 return; 328 329 WorkList.push_back(&CI); 330 } 331 332 // Generate the upper bound condition for RangeError. 333 Value *LibCallsShrinkWrap::generateOneRangeCond(CallInst *CI, 334 const LibFunc::Func &Func) { 335 float UpperBound; 336 switch (Func) { 337 case LibFunc::expm1: // RangeError: (709, inf) 338 UpperBound = 709.0f; 339 break; 340 case LibFunc::expm1f: // RangeError: (88, inf) 341 UpperBound = 88.0f; 342 break; 343 case LibFunc::expm1l: // RangeError: (11356, inf) 344 UpperBound = 11356.0f; 345 break; 346 default: 347 llvm_unreachable("Should be reach here"); 348 } 349 350 ++NumWrappedOneCond; 351 return createCond(CI, CmpInst::FCMP_OGT, UpperBound); 352 } 353 354 // Generate the lower and upper bound condition for RangeError. 355 Value *LibCallsShrinkWrap::generateTwoRangeCond(CallInst *CI, 356 const LibFunc::Func &Func) { 357 float UpperBound, LowerBound; 358 switch (Func) { 359 case LibFunc::cosh: // RangeError: (x < -710 || x > 710) 360 case LibFunc::sinh: // Same as cosh 361 LowerBound = -710.0f; 362 UpperBound = 710.0f; 363 break; 364 case LibFunc::coshf: // RangeError: (x < -89 || x > 89) 365 case LibFunc::sinhf: // Same as coshf 366 LowerBound = -89.0f; 367 UpperBound = 89.0f; 368 break; 369 case LibFunc::coshl: // RangeError: (x < -11357 || x > 11357) 370 case LibFunc::sinhl: // Same as coshl 371 LowerBound = -11357.0f; 372 UpperBound = 11357.0f; 373 break; 374 case LibFunc::exp: // RangeError: (x < -745 || x > 709) 375 LowerBound = -745.0f; 376 UpperBound = 709.0f; 377 break; 378 case LibFunc::expf: // RangeError: (x < -103 || x > 88) 379 LowerBound = -103.0f; 380 UpperBound = 88.0f; 381 break; 382 case LibFunc::expl: // RangeError: (x < -11399 || x > 11356) 383 LowerBound = -11399.0f; 384 UpperBound = 11356.0f; 385 break; 386 case LibFunc::exp10: // RangeError: (x < -323 || x > 308) 387 LowerBound = -323.0f; 388 UpperBound = 308.0f; 389 break; 390 case LibFunc::exp10f: // RangeError: (x < -45 || x > 38) 391 LowerBound = -45.0f; 392 UpperBound = 38.0f; 393 break; 394 case LibFunc::exp10l: // RangeError: (x < -4950 || x > 4932) 395 LowerBound = -4950.0f; 396 UpperBound = 4932.0f; 397 break; 398 case LibFunc::exp2: // RangeError: (x < -1074 || x > 1023) 399 LowerBound = -1074.0f; 400 UpperBound = 1023.0f; 401 break; 402 case LibFunc::exp2f: // RangeError: (x < -149 || x > 127) 403 LowerBound = -149.0f; 404 UpperBound = 127.0f; 405 break; 406 case LibFunc::exp2l: // RangeError: (x < -16445 || x > 11383) 407 LowerBound = -16445.0f; 408 UpperBound = 11383.0f; 409 break; 410 default: 411 llvm_unreachable("Should be reach here"); 412 } 413 414 ++NumWrappedTwoCond; 415 return createOrCond(CI, CmpInst::FCMP_OGT, UpperBound, CmpInst::FCMP_OLT, 416 LowerBound); 417 } 418 419 // For pow(x,y), We only handle the following cases: 420 // (1) x is a constant && (x >= 1) && (x < MaxUInt8) 421 // Cond is: (y > 127) 422 // (2) x is a value coming from an integer type. 423 // (2.1) if x's bit_size == 8 424 // Cond: (x <= 0 || y > 128) 425 // (2.2) if x's bit_size is 16 426 // Cond: (x <= 0 || y > 64) 427 // (2.3) if x's bit_size is 32 428 // Cond: (x <= 0 || y > 32) 429 // Support for powl(x,y) and powf(x,y) are TBD. 430 // 431 // Note that condition can be more conservative than the actual condition 432 // (i.e. we might invoke the calls that will not set the errno.). 433 // 434 Value *LibCallsShrinkWrap::generateCondForPow(CallInst *CI, 435 const LibFunc::Func &Func) { 436 // FIXME: LibFunc::powf and powl TBD. 437 if (Func != LibFunc::pow) { 438 DEBUG(dbgs() << "Not handled powf() and powl()\n"); 439 return nullptr; 440 } 441 442 Value *Base = CI->getArgOperand(0); 443 Value *Exp = CI->getArgOperand(1); 444 IRBuilder<> BBBuilder(CI); 445 446 // Constant Base case. 447 if (ConstantFP *CF = dyn_cast<ConstantFP>(Base)) { 448 double D = CF->getValueAPF().convertToDouble(); 449 if (D < 1.0f || D > APInt::getMaxValue(8).getZExtValue()) { 450 DEBUG(dbgs() << "Not handled pow(): constant base out of range\n"); 451 return nullptr; 452 } 453 454 ++NumWrappedOneCond; 455 Constant *V = ConstantFP::get(CI->getContext(), APFloat(127.0f)); 456 if (!Exp->getType()->isFloatTy()) 457 V = ConstantExpr::getFPExtend(V, Exp->getType()); 458 return BBBuilder.CreateFCmp(CmpInst::FCMP_OGT, Exp, V); 459 } 460 461 // If the Base value coming from an integer type. 462 Instruction *I = dyn_cast<Instruction>(Base); 463 if (!I) { 464 DEBUG(dbgs() << "Not handled pow(): FP type base\n"); 465 return nullptr; 466 } 467 unsigned Opcode = I->getOpcode(); 468 if (Opcode == Instruction::UIToFP || Opcode == Instruction::SIToFP) { 469 unsigned BW = I->getOperand(0)->getType()->getPrimitiveSizeInBits(); 470 float UpperV = 0.0f; 471 if (BW == 8) 472 UpperV = 128.0f; 473 else if (BW == 16) 474 UpperV = 64.0f; 475 else if (BW == 32) 476 UpperV = 32.0f; 477 else { 478 DEBUG(dbgs() << "Not handled pow(): type too wide\n"); 479 return nullptr; 480 } 481 482 ++NumWrappedTwoCond; 483 Constant *V = ConstantFP::get(CI->getContext(), APFloat(UpperV)); 484 Constant *V0 = ConstantFP::get(CI->getContext(), APFloat(0.0f)); 485 if (!Exp->getType()->isFloatTy()) 486 V = ConstantExpr::getFPExtend(V, Exp->getType()); 487 if (!Base->getType()->isFloatTy()) 488 V0 = ConstantExpr::getFPExtend(V0, Exp->getType()); 489 490 Value *Cond = BBBuilder.CreateFCmp(CmpInst::FCMP_OGT, Exp, V); 491 Value *Cond0 = BBBuilder.CreateFCmp(CmpInst::FCMP_OLE, Base, V0); 492 return BBBuilder.CreateOr(Cond0, Cond); 493 } 494 DEBUG(dbgs() << "Not handled pow(): base not from integer convert\n"); 495 return nullptr; 496 } 497 498 // Wrap conditions that can potentially generate errno to the library call. 499 void LibCallsShrinkWrap::shrinkWrapCI(CallInst *CI, Value *Cond) { 500 assert(Cond != nullptr && "hrinkWrapCI is not expecting an empty call inst"); 501 MDNode *BranchWeights = 502 MDBuilder(CI->getContext()).createBranchWeights(1, 2000); 503 TerminatorInst *NewInst = 504 SplitBlockAndInsertIfThen(Cond, CI, false, BranchWeights); 505 BasicBlock *CallBB = NewInst->getParent(); 506 CallBB->setName("cdce.call"); 507 CallBB->getSingleSuccessor()->setName("cdce.end"); 508 CI->removeFromParent(); 509 CallBB->getInstList().insert(CallBB->getFirstInsertionPt(), CI); 510 DEBUG(dbgs() << "== Basic Block After =="); 511 DEBUG(dbgs() << *CallBB->getSinglePredecessor() << *CallBB 512 << *CallBB->getSingleSuccessor() << "\n"); 513 } 514 515 // Perform the transformation to a single candidate. 516 bool LibCallsShrinkWrap::perform(CallInst *CI) { 517 LibFunc::Func Func; 518 Function *Callee = CI->getCalledFunction(); 519 assert(Callee && "perform() should apply to a non-empty callee"); 520 TLI.getLibFunc(*Callee, Func); 521 assert(Func && "perform() is not expecting an empty function"); 522 523 if (LibCallsShrinkWrapDoDomainError && performCallDomainErrorOnly(CI, Func)) 524 return true; 525 526 if (LibCallsShrinkWrapDoRangeError && performCallRangeErrorOnly(CI, Func)) 527 return true; 528 529 return performCallErrors(CI, Func); 530 } 531 532 void LibCallsShrinkWrapLegacyPass::getAnalysisUsage(AnalysisUsage &AU) const { 533 AU.addPreserved<GlobalsAAWrapperPass>(); 534 AU.addRequired<TargetLibraryInfoWrapperPass>(); 535 } 536 537 bool runImpl(Function &F, const TargetLibraryInfo &TLI) { 538 if (F.hasFnAttribute(Attribute::OptimizeForSize)) 539 return false; 540 LibCallsShrinkWrap CCDCE(TLI); 541 CCDCE.visit(F); 542 CCDCE.perform(); 543 return CCDCE.isChanged(); 544 } 545 546 bool LibCallsShrinkWrapLegacyPass::runOnFunction(Function &F) { 547 auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(); 548 return runImpl(F, TLI); 549 } 550 551 namespace llvm { 552 char &LibCallsShrinkWrapPassID = LibCallsShrinkWrapLegacyPass::ID; 553 554 // Public interface to LibCallsShrinkWrap pass. 555 FunctionPass *createLibCallsShrinkWrapPass() { 556 return new LibCallsShrinkWrapLegacyPass(); 557 } 558 559 PreservedAnalyses LibCallsShrinkWrapPass::run(Function &F, 560 FunctionAnalysisManager &FAM) { 561 auto &TLI = FAM.getResult<TargetLibraryAnalysis>(F); 562 bool Changed = runImpl(F, TLI); 563 if (!Changed) 564 return PreservedAnalyses::all(); 565 auto PA = PreservedAnalyses(); 566 PA.preserve<GlobalsAA>(); 567 return PA; 568 } 569 } 570