| //===--- SwitchEnumBuilder.cpp --------------------------------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "SwitchEnumBuilder.h" |
| #include "SILGenFunction.h" |
| #include "swift/SIL/SILLocation.h" |
| |
| using namespace swift; |
| using namespace Lowering; |
| |
| //===----------------------------------------------------------------------===// |
| // SwitchCaseFullExpr Implementation |
| //===----------------------------------------------------------------------===// |
| |
| SwitchCaseFullExpr::SwitchCaseFullExpr(SILGenFunction &SGF, CleanupLocation loc) |
| : SGF(SGF), scope(SGF, loc), loc(loc), branchDest() {} |
| |
| SwitchCaseFullExpr::SwitchCaseFullExpr(SILGenFunction &SGF, CleanupLocation loc, |
| SwitchCaseBranchDest branchDest) |
| : SGF(SGF), scope(SGF, loc), loc(loc), branchDest(branchDest) {} |
| |
| void SwitchCaseFullExpr::exitAndBranch(SILLocation loc, |
| ArrayRef<SILValue> branchArgs) { |
| assert(bool(branchDest) && "Must have a branch destination!"); |
| assert(SGF.B.hasValidInsertionPoint()); |
| scope.pop(); |
| |
| // Then either do a direct branch or a branch + cleanups. |
| if (SILBasicBlock *block = branchDest.getBlock()) { |
| SGF.B.createBranch(loc, block, branchArgs); |
| return; |
| } |
| |
| SGF.Cleanups.emitBranchAndCleanups(branchDest.getJumpDest(), loc, branchArgs); |
| } |
| |
| void SwitchCaseFullExpr::exit() { |
| assert(!bool(branchDest) && |
| "Should not call this if we do have a continuation block"); |
| assert(SGF.B.hasValidInsertionPoint()); |
| scope.pop(); |
| } |
| |
| SwitchCaseFullExpr::~SwitchCaseFullExpr() { |
| assert(!scope.isValid() && "Did not pop scope?!"); |
| } |
| |
| void SwitchCaseFullExpr::unreachableExit() { |
| // This is important to ensure that we do not actually emit any cleanups since |
| // we already know that an unreachable was emitted. |
| assert(!SGF.B.hasValidInsertionPoint() && "Expected to pop scope without a " |
| "valid insertion point!"); |
| scope.pop(); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // SwitchEnumBuilder Implementation |
| //===----------------------------------------------------------------------===// |
| |
| void SwitchEnumBuilder::emit() && { |
| bool isAddressOnly = |
| subjectExprOperand.getType().isAddressOnly(builder.getFunction()) && |
| getSGF().silConv.useLoweredAddresses(); |
| using DeclBlockPair = std::pair<EnumElementDecl *, SILBasicBlock *>; |
| { |
| // TODO: We could store the data in CaseBB form and not have to do this. |
| llvm::SmallVector<DeclBlockPair, 8> caseBlocks; |
| llvm::SmallVector<ProfileCounter, 8> caseBlockCounts; |
| std::transform(caseDataArray.begin(), caseDataArray.end(), |
| std::back_inserter(caseBlocks), |
| [](NormalCaseData &caseData) -> DeclBlockPair { |
| return {caseData.decl, caseData.block}; |
| }); |
| std::transform(caseDataArray.begin(), caseDataArray.end(), |
| std::back_inserter(caseBlockCounts), |
| [](NormalCaseData &caseData) -> ProfileCounter { |
| return caseData.count; |
| }); |
| SILBasicBlock *defaultBlock = |
| defaultBlockData ? defaultBlockData->block : nullptr; |
| ProfileCounter defaultBlockCount = |
| defaultBlockData ? defaultBlockData->count : ProfileCounter(); |
| ArrayRef<ProfileCounter> caseBlockCountsRef = caseBlockCounts; |
| if (isAddressOnly) { |
| builder.createSwitchEnumAddr(loc, subjectExprOperand.getValue(), |
| defaultBlock, caseBlocks, caseBlockCountsRef, |
| defaultBlockCount); |
| } else { |
| if (subjectExprOperand.getType().isAddress()) { |
| // TODO: Refactor this into a maybe load. |
| if (subjectExprOperand.hasCleanup()) { |
| subjectExprOperand = builder.createLoadTake(loc, subjectExprOperand); |
| } else { |
| subjectExprOperand = builder.createLoadCopy(loc, subjectExprOperand); |
| } |
| } |
| builder.createSwitchEnum(loc, subjectExprOperand.forward(getSGF()), |
| defaultBlock, caseBlocks, caseBlockCountsRef, |
| defaultBlockCount); |
| } |
| } |
| |
| // If we are asked to create a default block and it is specified that the |
| // default block should be emitted before normal cases, emit it now. |
| if (defaultBlockData && |
| defaultBlockData->dispatchTime == |
| DefaultDispatchTime::BeforeNormalCases) { |
| SILBasicBlock *defaultBlock = defaultBlockData->block; |
| SwitchCaseBranchDest branchDest = defaultBlockData->branchDest; |
| DefaultCaseHandler handler = defaultBlockData->handler; |
| |
| // Don't allow cleanups to escape the conditional block. |
| SwitchCaseFullExpr presentScope(builder.getSILGenFunction(), |
| CleanupLocation::get(loc), branchDest); |
| builder.emitBlock(defaultBlock); |
| ManagedValue input = subjectExprOperand; |
| if (!isAddressOnly) { |
| input = builder.createOwnedPhiArgument(subjectExprOperand.getType()); |
| } |
| handler(input, std::move(presentScope)); |
| builder.clearInsertionPoint(); |
| } |
| |
| for (NormalCaseData &caseData : caseDataArray) { |
| EnumElementDecl *decl = caseData.decl; |
| SILBasicBlock *caseBlock = caseData.block; |
| SwitchCaseBranchDest branchDest = caseData.branchDest; |
| NormalCaseHandler handler = caseData.handler; |
| |
| // Don't allow cleanups to escape the conditional block. |
| SwitchCaseFullExpr presentScope(builder.getSILGenFunction(), |
| CleanupLocation::get(loc), branchDest); |
| |
| builder.emitBlock(caseBlock); |
| |
| ManagedValue input; |
| if (decl->hasAssociatedValues()) { |
| // Pull the payload out if we have one. |
| SILType inputType = subjectExprOperand.getType().getEnumElementType( |
| decl, builder.getModule(), builder.getFunction()); |
| input = subjectExprOperand; |
| if (!isAddressOnly) { |
| input = builder.createOwnedPhiArgument(inputType); |
| } |
| } |
| handler(input, std::move(presentScope)); |
| builder.clearInsertionPoint(); |
| } |
| |
| // If we are asked to create a default block and it is specified that the |
| // default block should be emitted after normal cases, emit it now. |
| if (defaultBlockData && |
| defaultBlockData->dispatchTime == DefaultDispatchTime::AfterNormalCases) { |
| SILBasicBlock *defaultBlock = defaultBlockData->block; |
| auto branchDest = defaultBlockData->branchDest; |
| DefaultCaseHandler handler = defaultBlockData->handler; |
| |
| // Don't allow cleanups to escape the conditional block. |
| SwitchCaseFullExpr presentScope(builder.getSILGenFunction(), |
| CleanupLocation::get(loc), branchDest); |
| builder.emitBlock(defaultBlock); |
| ManagedValue input = subjectExprOperand; |
| if (!isAddressOnly) { |
| input = builder.createOwnedPhiArgument(subjectExprOperand.getType()); |
| } |
| handler(input, std::move(presentScope)); |
| builder.clearInsertionPoint(); |
| } |
| } |