/// \brief Check if this instruction corresponds to user-written code. static bool isUserCode(const SILInstruction *I) { SILLocation Loc = I->getLoc(); if (Loc.isAutoGenerated()) return false; // Branch instructions are not user code. These could belong to the control // flow statement we are folding (ex: while loop). // Also, unreachable instructions are not user code, they are "expected" in // unreachable blocks. if ((isa<BranchInst>(I) || isa<UnreachableInst>(I)) && Loc.is<RegularLocation>()) return false; // If the instruction corresponds to user-written return or some other // statement, we know it corresponds to user code. if (Loc.is<RegularLocation>() || Loc.is<ReturnLocation>()) { if (auto *E = Loc.getAsASTNode<Expr>()) return !E->isImplicit(); if (auto *D = Loc.getAsASTNode<Decl>()) return !D->isImplicit(); if (auto *S = Loc.getAsASTNode<Decl>()) return !S->isImplicit(); if (auto *P = Loc.getAsASTNode<Decl>()) return !P->isImplicit(); return true; } return false; }
InlinedLocation InlinedLocation::getInlinedLocation(SILLocation L) { if (Expr *E = L.getAsASTNode<Expr>()) return InlinedLocation(E, L.getSpecialFlags()); if (Stmt *S = L.getAsASTNode<Stmt>()) return InlinedLocation(S, L.getSpecialFlags()); if (Pattern *P = L.getAsASTNode<Pattern>()) return InlinedLocation(P, L.getSpecialFlags()); if (Decl *D = L.getAsASTNode<Decl>()) return InlinedLocation(D, L.getSpecialFlags()); if (L.isSILFile()) return InlinedLocation(L.Loc.SILFileLoc, L.getSpecialFlags()); if (L.isInTopLevel()) return InlinedLocation::getModuleLocation(L.getSpecialFlags()); if (L.isAutoGenerated()) { InlinedLocation IL; IL.markAutoGenerated(); return IL; } llvm_unreachable("Cannot construct Inlined loc from the given location."); }
/// tryToRemoveDeadAllocation - If the allocation is an autogenerated allocation /// that is only stored to (after load promotion) then remove it completely. bool AllocOptimize::tryToRemoveDeadAllocation() { assert((isa<AllocBoxInst>(TheMemory) || isa<AllocStackInst>(TheMemory)) && "Unhandled allocation case"); // We don't want to remove allocations that are required for useful debug // information at -O0. As such, we only remove allocations if: // // 1. They are in a transparent function. // 2. They are in a normal function, but didn't come from a VarDecl, or came // from one that was autogenerated or inlined from a transparent function. SILLocation Loc = TheMemory->getLoc(); if (!TheMemory->getFunction()->isTransparent() && Loc.getAsASTNode<VarDecl>() && !Loc.isAutoGenerated() && !Loc.is<MandatoryInlinedLocation>()) return false; // Check the uses list to see if there are any non-store uses left over after // load promotion and other things DI does. for (auto &U : Uses) { // Ignore removed instructions. if (U.Inst == nullptr) continue; switch (U.Kind) { case DIUseKind::SelfInit: case DIUseKind::SuperInit: llvm_unreachable("Can't happen on allocations"); case DIUseKind::Assign: case DIUseKind::PartialStore: case DIUseKind::InitOrAssign: break; // These don't prevent removal. case DIUseKind::Initialization: if (!isa<ApplyInst>(U.Inst) && // A copy_addr that is not a take affects the retain count // of the source. (!isa<CopyAddrInst>(U.Inst) || cast<CopyAddrInst>(U.Inst)->isTakeOfSrc())) break; // FALL THROUGH. SWIFT_FALLTHROUGH; case DIUseKind::Load: case DIUseKind::IndirectIn: case DIUseKind::InOutUse: case DIUseKind::Escape: DEBUG(llvm::dbgs() << "*** Failed to remove autogenerated alloc: " "kept alive by: " << *U.Inst); return false; // These do prevent removal. } } // If the memory object has non-trivial type, then removing the deallocation // will drop any releases. Check that there is nothing preventing removal. if (!MemoryType.isTrivial(Module)) { for (auto *R : Releases) { if (R == nullptr || isa<DeallocStackInst>(R) || isa<DeallocBoxInst>(R)) continue; DEBUG(llvm::dbgs() << "*** Failed to remove autogenerated alloc: " "kept alive by release: " << *R); return false; } } DEBUG(llvm::dbgs() << "*** Removing autogenerated alloc_stack: "<<*TheMemory); // If it is safe to remove, do it. Recursively remove all instructions // hanging off the allocation instruction, then return success. Let the // caller remove the allocation itself to avoid iterator invalidation. eraseUsesOfInstruction(TheMemory); return true; }
/// \brief Issue an "unreachable code" diagnostic if the blocks contains or /// leads to another block that contains user code. /// /// Note, we rely on SILLocation information to determine if SILInstructions /// correspond to user code. static bool diagnoseUnreachableBlock(const SILBasicBlock &B, SILModule &M, const SILBasicBlockSet &Reachable, UnreachableUserCodeReportingState *State, const SILBasicBlock *TopLevelB, llvm::SmallPtrSetImpl<const SILBasicBlock*> &Visited){ if (Visited.count(&B)) return false; Visited.insert(&B); assert(State); for (auto I = B.begin(), E = B.end(); I != E; ++I) { SILLocation Loc = I->getLoc(); // If we've reached an implicit return, we have not found any user code and // can stop searching for it. if (Loc.is<ImplicitReturnLocation>() || Loc.isAutoGenerated()) return false; // Check if the instruction corresponds to user-written code, also make // sure we don't report an error twice for the same instruction. if (isUserCode(&*I) && !State->BlocksWithErrors.count(&B)) { // Emit the diagnostic. auto BrInfoIter = State->MetaMap.find(TopLevelB); assert(BrInfoIter != State->MetaMap.end()); auto BrInfo = BrInfoIter->second; switch (BrInfo.Kind) { case (UnreachableKind::FoldedBranch): // Emit the diagnostic on the unreachable block and emit the // note on the branch responsible for the unreachable code. diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code); diagnose(M.getASTContext(), BrInfo.Loc.getSourceLoc(), diag::unreachable_code_branch, BrInfo.CondIsAlwaysTrue); break; case (UnreachableKind::FoldedSwitchEnum): { // If we are warning about a switch condition being a constant, the main // emphasis should be on the condition (to ensure we have a single // message per switch). const SwitchStmt *SS = BrInfo.Loc.getAsASTNode<SwitchStmt>(); if (!SS) break; assert(SS); const Expr *SE = SS->getSubjectExpr(); diagnose(M.getASTContext(), SE->getLoc(), diag::switch_on_a_constant); diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code_note); break; } case (UnreachableKind::NoreturnCall): { // Specialcase when we are warning about unreachable code after a call // to a noreturn function. if (!BrInfo.Loc.isASTNode<ExplicitCastExpr>()) { assert(BrInfo.Loc.isASTNode<ApplyExpr>()); diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code); diagnose(M.getASTContext(), BrInfo.Loc.getSourceLoc(), diag::call_to_noreturn_note); } break; } } // Record that we've reported this unreachable block to avoid duplicates // in the future. State->BlocksWithErrors.insert(&B); return true; } } // This block could be empty if it's terminator has been folded. if (B.empty()) return false; // If we have not found user code in this block, inspect it's successors. // Check if at least one of the successors contains user code. for (auto I = B.succ_begin(), E = B.succ_end(); I != E; ++I) { SILBasicBlock *SB = *I; bool HasReachablePred = false; for (auto PI = SB->pred_begin(), PE = SB->pred_end(); PI != PE; ++PI) { if (Reachable.count(*PI)) HasReachablePred = true; } // If all of the predecessors of this successor are unreachable, check if // it contains user code. if (!HasReachablePred && diagnoseUnreachableBlock(*SB, M, Reachable, State, TopLevelB, Visited)) return true; } return false; }