static bool constantFoldTerminator(SILBasicBlock &BB, UnreachableUserCodeReportingState *State) { TermInst *TI = BB.getTerminator(); // Process conditional branches with constant conditions. if (CondBranchInst *CBI = dyn_cast<CondBranchInst>(TI)) { SILValue V = CBI->getCondition(); SILInstruction *CondI = dyn_cast<SILInstruction>(V); SILLocation Loc = CBI->getLoc(); if (IntegerLiteralInst *ConstCond = dyn_cast_or_null<IntegerLiteralInst>(CondI)) { SILBuilderWithScope B(&BB, CBI); // Determine which of the successors is unreachable and create a new // terminator that only branches to the reachable successor. SILBasicBlock *UnreachableBlock = nullptr; bool CondIsTrue = false; if (ConstCond->getValue() == APInt(1, /*value*/ 0, false)) { B.createBranch(Loc, CBI->getFalseBB(), CBI->getFalseArgs()); UnreachableBlock = CBI->getTrueBB(); } else { assert(ConstCond->getValue() == APInt(1, /*value*/ 1, false) && "Our representation of true/false does not match."); B.createBranch(Loc, CBI->getTrueBB(), CBI->getTrueArgs()); UnreachableBlock = CBI->getFalseBB(); CondIsTrue = true; } recursivelyDeleteTriviallyDeadInstructions(TI, true); NumInstructionsRemoved++; // Produce an unreachable code warning for this basic block if it // contains user code (only if we are not within an inlined function or a // template instantiation). // FIXME: Do not report if we are within a template instantiation. if (Loc.is<RegularLocation>() && State && !State->PossiblyUnreachableBlocks.count(UnreachableBlock)) { // If this is the first time we see this unreachable block, store it // along with the folded branch info. State->PossiblyUnreachableBlocks.insert(UnreachableBlock); State->MetaMap.insert( std::pair<const SILBasicBlock*, UnreachableInfo>( UnreachableBlock, UnreachableInfo{UnreachableKind::FoldedBranch, Loc, CondIsTrue})); } NumTerminatorsFolded++; return true; } } // Constant fold switch enum. // %1 = enum $Bool, #Bool.false!unionelt // switch_enum %1 : $Bool, case #Bool.true!unionelt: bb1, // case #Bool.false!unionelt: bb2 // => // br bb2 if (SwitchEnumInst *SUI = dyn_cast<SwitchEnumInst>(TI)) { if (EnumInst *TheEnum = dyn_cast<EnumInst>(SUI->getOperand())) { const EnumElementDecl *TheEnumElem = TheEnum->getElement(); SILBasicBlock *TheSuccessorBlock = nullptr; int ReachableBlockIdx = -1; for (unsigned Idx = 0; Idx < SUI->getNumCases(); ++Idx) { const EnumElementDecl *EI; SILBasicBlock *BI; std::tie(EI, BI) = SUI->getCase(Idx); if (EI == TheEnumElem) { TheSuccessorBlock = BI; ReachableBlockIdx = Idx; break; } } if (!TheSuccessorBlock) if (SUI->hasDefault()) { SILBasicBlock *DB= SUI->getDefaultBB(); if (!isa<UnreachableInst>(DB->getTerminator())) { TheSuccessorBlock = DB; ReachableBlockIdx = SUI->getNumCases(); } } // Not fully covered switches will be diagnosed later. SILGen represents // them with a Default basic block with an unrechable instruction. // We are going to produce an error on all unreachable instructions not // eliminated by DCE. if (!TheSuccessorBlock) return false; // Replace the switch with a branch to the TheSuccessorBlock. SILBuilderWithScope B(&BB, TI); SILLocation Loc = TI->getLoc(); if (!TheSuccessorBlock->bbarg_empty()) { assert(TheEnum->hasOperand()); B.createBranch(Loc, TheSuccessorBlock, TheEnum->getOperand()); } else B.createBranch(Loc, TheSuccessorBlock); // Produce diagnostic info if we are not within an inlined function or // template instantiation. // FIXME: Do not report if we are within a template instantiation. assert(ReachableBlockIdx >= 0); if (Loc.is<RegularLocation>() && State) { // Find the first unreachable block in the switch so that we could use // it for better diagnostics. SILBasicBlock *UnreachableBlock = nullptr; if (SUI->getNumCases() > 1) { // More than one case. UnreachableBlock = (ReachableBlockIdx == 0) ? SUI->getCase(1).second: SUI->getCase(0).second; } else { if (SUI->getNumCases() == 1 && SUI->hasDefault()) { // One case and a default. UnreachableBlock = (ReachableBlockIdx == 0) ? SUI->getDefaultBB(): SUI->getCase(0).second; } } // Generate diagnostic info. if (UnreachableBlock && !State->PossiblyUnreachableBlocks.count(UnreachableBlock)) { State->PossiblyUnreachableBlocks.insert(UnreachableBlock); State->MetaMap.insert( std::pair<const SILBasicBlock*, UnreachableInfo>( UnreachableBlock, UnreachableInfo{UnreachableKind::FoldedSwitchEnum, Loc, true})); } } recursivelyDeleteTriviallyDeadInstructions(TI, true); NumTerminatorsFolded++; return true; } } // Constant fold switch int. // %1 = integer_literal $Builtin.Int64, 2 // switch_value %1 : $Builtin.Int64, case 1: bb1, case 2: bb2 // => // br bb2 if (SwitchValueInst *SUI = dyn_cast<SwitchValueInst>(TI)) { if (IntegerLiteralInst *SwitchVal = dyn_cast<IntegerLiteralInst>(SUI->getOperand())) { SILBasicBlock *TheSuccessorBlock = 0; for (unsigned Idx = 0; Idx < SUI->getNumCases(); ++Idx) { APInt AI; SILValue EI; SILBasicBlock *BI; std::tie(EI, BI) = SUI->getCase(Idx); // TODO: Check that EI is really an IntegerLiteralInst AI = dyn_cast<IntegerLiteralInst>(EI)->getValue(); if (AI == SwitchVal->getValue()) TheSuccessorBlock = BI; } if (!TheSuccessorBlock) if (SUI->hasDefault()) TheSuccessorBlock = SUI->getDefaultBB(); // Add the branch instruction with the block. if (TheSuccessorBlock) { SILBuilderWithScope B(&BB, TI); B.createBranch(TI->getLoc(), TheSuccessorBlock); recursivelyDeleteTriviallyDeadInstructions(TI, true); NumTerminatorsFolded++; return true; } // TODO: Warn on unreachable user code here as well. } } return false; }
// Attempt to insert a new access in the loop preheader. If successful, insert // the new access in DominatedAccessAnalysis so it can be used to dominate other // accesses. Also convert the current access to static and update the current // storageToDomMap since the access may already have been recorded (when it was // still dynamic). // // This function cannot add or remove instructions in the current block, but // may add instructions to the current loop's preheader. // // The required conditions for inserting a new dominating access are: // // 1. The new preheader access is not enclosed in another scope that doesn't // also enclose the current scope. // // This is inferred from the loop structure; any scope that encloses the // preheader must also enclose the entire loop. // // 2. The current access is not enclosed in another scope that doesn't also // enclose the preheader. // // As before, it is sufficient to check this access' isInner flags in // DominatedAccessAnalysis; if this access isn't enclosed by any scope within // the function, then it can't be enclosed within a scope inside the loop. // // 3. The current header has no nested conflict within its scope. // // 4. The access' source operand is available in the loop preheader. void DominatedAccessRemoval::tryInsertLoopPreheaderAccess( BeginAccessInst *BAI, DomAccessedStorage currAccessInfo) { // 2. the current access may be enclosed. if (currAccessInfo.isInner()) return; // 3. the current access must be instantaneous. if (!BAI->hasNoNestedConflict()) return; SILLoop *currLoop = loopInfo->getLoopFor(BAI->getParent()); if (!currLoop) return; SILBasicBlock *preheader = currLoop->getLoopPreheader(); if (!preheader) return; // 4. The source operand must be available in the preheader. auto sourceOperand = BAI->getOperand(); auto *sourceBB = sourceOperand->getParentBlock(); if (!domInfo->dominates(sourceBB, preheader)) return; // Insert a new access scope immediately before the // preheader's terminator. TermInst *preheaderTerm = preheader->getTerminator(); SILBuilderWithScope scopeBuilder(preheaderTerm); BeginAccessInst *newBegin = scopeBuilder.createBeginAccess( preheaderTerm->getLoc(), sourceOperand, BAI->getAccessKind(), SILAccessEnforcement::Dynamic, true /*no nested conflict*/, BAI->isFromBuiltin()); scopeBuilder.createEndAccess(preheaderTerm->getLoc(), newBegin, false); LLVM_DEBUG(llvm::dbgs() << "Created loop preheader access: " << *newBegin << "\n" << "dominating: " << *BAI << "\n"); BAI->setEnforcement(SILAccessEnforcement::Static); hasChanged = true; // Insert the new dominating instruction in both DominatedAccessAnalysis and // storageToDomMap if it has uniquely identifiable storage. if (!currAccessInfo.isUniquelyIdentifiedOrClass()) return; AccessedStorage storage = static_cast<AccessedStorage>(currAccessInfo); storage.resetSubclassData(); // Create a DomAccessedStorage for the new access with no flags set. DAA.accessMap.try_emplace(newBegin, DomAccessedStorage(storage)); // Track the new access as long as no other accesses from the same storage are // already tracked. This also necessarily replaces the current access, which // was just made static. DominatingAccess newDomAccess(newBegin, domInfo->getNode(preheader)); auto iterAndInserted = storageToDomMap.try_emplace(storage, newDomAccess); if (!iterAndInserted.second) { DominatingAccess &curDomAccess = iterAndInserted.first->second; if (curDomAccess.beginAccess == BAI) curDomAccess = newDomAccess; } }