Skip to content

Enable canonicalizing switch_enums in OSSA #61650

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions include/swift/SILOptimizer/Transforms/SimplifyCFG.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
//===-------------------- SimplifyCFG.h -----------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 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
//
//===----------------------------------------------------------------------===//


#ifndef SWIFT_SILOPTIMIZER_SIMPLIFYCFG_H
#define SWIFT_SILOPTIMIZER_SIMPLIFYCFG_H

#include "swift/SILOptimizer/Utils/ConstantFolding.h"
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"

using namespace swift;

struct ThreadInfo;

class SimplifyCFG {
SILOptFunctionBuilder FuncBuilder;
SILFunction &Fn;
SILFunctionTransform &transform;
SILPassManager *PM = nullptr;

// DeadEndBlocks remains conservatively valid across updates that rewrite
// branches and remove edges. Any transformation that adds a block must call
// updateForReachableBlock(). Removing a block causes a dangling pointer
// within DeadEndBlocks, but this pointer can't be accessed by queries.
DeadEndBlocks *deBlocks = nullptr;

// WorklistList is the actual list that we iterate over (for determinism).
// Slots may be null, which should be ignored.
SmallVector<SILBasicBlock *, 32> WorklistList;
// WorklistMap keeps track of which slot a BB is in, allowing efficient
// containment query, and allows efficient removal.
llvm::SmallDenseMap<SILBasicBlock *, unsigned, 32> WorklistMap;
// Keep track of loop headers - we don't want to jump-thread through them.
SmallPtrSet<SILBasicBlock *, 32> LoopHeaders;
// The set of cloned loop headers to avoid infinite loop peeling. Blocks in
// this set may or may not still be LoopHeaders.
// (ultimately this can be used to eliminate findLoopHeaders)
SmallPtrSet<SILBasicBlock *, 4> ClonedLoopHeaders;
// The cost (~ number of copied instructions) of jump threading per basic
// block. Used to prevent infinite jump threading loops.
llvm::SmallDenseMap<SILBasicBlock *, int, 8> JumpThreadingCost;

// Dominance and post-dominance info for the current function
DominanceInfo *DT = nullptr;

ConstantFolder ConstFolder;

// True if the function has a large amount of blocks. In this case we turn off
// some expensive optimizations.
bool isVeryLargeFunction = false;

void constFoldingCallback(SILInstruction *I) {
// If a terminal instruction gets constant folded (like cond_br), it
// enables further simplify-CFG optimizations.
if (isa<TermInst>(I))
addToWorklist(I->getParent());
}

bool ShouldVerify;
bool EnableJumpThread;

public:
SimplifyCFG(SILFunction &Fn, SILFunctionTransform &T, bool Verify,
bool EnableJumpThread)
: FuncBuilder(T), Fn(Fn), transform(T), PM(T.getPassManager()),
ConstFolder(FuncBuilder, PM->getOptions().AssertConfig,
/* EnableDiagnostics */ false,
[&](SILInstruction *I) { constFoldingCallback(I); }),
ShouldVerify(Verify), EnableJumpThread(EnableJumpThread) {}

bool run();

bool simplifyBlockArgs();
bool simplifyBlocks();
bool canonicalizeSwitchEnums();
bool simplifyThreadedTerminators();
bool dominatorBasedSimplifications(SILFunction &Fn, DominanceInfo *DT);
bool dominatorBasedSimplify(DominanceAnalysis *DA);
bool threadEdge(const ThreadInfo &ti);

/// Remove the basic block if it has no predecessors. Returns true
/// If the block was removed.
bool removeIfDead(SILBasicBlock *BB);

bool tryJumpThreading(BranchInst *BI);
bool tailDuplicateObjCMethodCallSuccessorBlocks();
bool simplifyAfterDroppingPredecessor(SILBasicBlock *BB);

bool simplifyBranchOperands(OperandValueArrayRef Operands);
bool simplifyBranchBlock(BranchInst *BI);
bool simplifyCondBrBlock(CondBranchInst *BI);
bool simplifyCheckedCastBranchBlock(CheckedCastBranchInst *CCBI);
bool simplifyCheckedCastAddrBranchBlock(CheckedCastAddrBranchInst *CCABI);
bool simplifyTryApplyBlock(TryApplyInst *TAI);
bool simplifySwitchValueBlock(SwitchValueInst *SVI);
bool simplifyTermWithIdenticalDestBlocks(SILBasicBlock *BB);
bool simplifySwitchEnumUnreachableBlocks(SwitchEnumInst *SEI);
bool simplifySwitchEnumBlock(SwitchEnumInst *SEI);
bool simplifyUnreachableBlock(UnreachableInst *UI);
bool simplifyProgramTerminationBlock(SILBasicBlock *BB);
bool simplifyArgument(SILBasicBlock *BB, unsigned i);
bool simplifyArgs(SILBasicBlock *BB);
bool simplifySwitchEnumOnObjcClassOptional(SwitchEnumInst *SEI);

private:
// Called when \p newBlock inherits the former predecessors of \p
// oldBlock. e.g. if \p oldBlock was a loop header, then newBlock is now a
// loop header.
void substitutedBlockPreds(SILBasicBlock *oldBlock, SILBasicBlock *newBlock) {
if (LoopHeaders.count(oldBlock))
LoopHeaders.insert(newBlock);
if (ClonedLoopHeaders.count(oldBlock))
ClonedLoopHeaders.insert(newBlock);
}

void clearWorklist() {
WorklistMap.clear();
WorklistList.clear();
}

/// popWorklist - Return the next basic block to look at, or null if the
/// worklist is empty. This handles skipping over null entries in the
/// worklist.
SILBasicBlock *popWorklist() {
while (!WorklistList.empty())
if (auto *BB = WorklistList.pop_back_val()) {
WorklistMap.erase(BB);
return BB;
}

return nullptr;
}

/// addToWorklist - Add the specified block to the work list if it isn't
/// already present.
void addToWorklist(SILBasicBlock *BB) {
unsigned &Entry = WorklistMap[BB];
if (Entry != 0)
return;
WorklistList.push_back(BB);
Entry = WorklistList.size();
}

/// removeFromWorklist - Remove the specified block from the worklist if
/// present.
void removeFromWorklist(SILBasicBlock *BB) {
assert(BB && "Cannot add null pointer to the worklist");
auto It = WorklistMap.find(BB);
if (It == WorklistMap.end())
return;

// If the BB is in the worklist, null out its entry.
if (It->second) {
assert(WorklistList[It->second - 1] == BB && "Consistency error");
WorklistList[It->second - 1] = nullptr;
}

// Remove it from the map as well.
WorklistMap.erase(It);

if (LoopHeaders.count(BB)) {
LoopHeaders.erase(BB);
ClonedLoopHeaders.erase(BB);
}
}

void findLoopHeaders();
bool addToWorklistAfterSplittingEdges(SILBasicBlock *BB);
};

#endif
Loading