You've already forked linux-packaging-mono
Imported Upstream version 5.18.0.207
Former-commit-id: 3b152f462918d427ce18620a2cbe4f8b79650449
This commit is contained in:
parent
8e12397d70
commit
eb85e2fc17
9
external/llvm/lib/Transforms/CMakeLists.txt
vendored
9
external/llvm/lib/Transforms/CMakeLists.txt
vendored
@ -1,9 +0,0 @@
|
||||
add_subdirectory(Utils)
|
||||
add_subdirectory(Instrumentation)
|
||||
add_subdirectory(InstCombine)
|
||||
add_subdirectory(Scalar)
|
||||
add_subdirectory(IPO)
|
||||
add_subdirectory(Vectorize)
|
||||
add_subdirectory(Hello)
|
||||
add_subdirectory(ObjCARC)
|
||||
add_subdirectory(Coroutines)
|
@ -1,11 +0,0 @@
|
||||
add_llvm_library(LLVMCoroutines
|
||||
Coroutines.cpp
|
||||
CoroCleanup.cpp
|
||||
CoroEarly.cpp
|
||||
CoroElide.cpp
|
||||
CoroFrame.cpp
|
||||
CoroSplit.cpp
|
||||
|
||||
DEPENDS
|
||||
intrinsics_gen
|
||||
)
|
@ -1,137 +0,0 @@
|
||||
//===- CoroCleanup.cpp - Coroutine Cleanup Pass ---------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This pass lowers all remaining coroutine intrinsics.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CoroInternal.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "coro-cleanup"
|
||||
|
||||
namespace {
|
||||
// Created on demand if CoroCleanup pass has work to do.
|
||||
struct Lowerer : coro::LowererBase {
|
||||
IRBuilder<> Builder;
|
||||
Lowerer(Module &M) : LowererBase(M), Builder(Context) {}
|
||||
bool lowerRemainingCoroIntrinsics(Function &F);
|
||||
};
|
||||
}
|
||||
|
||||
static void simplifyCFG(Function &F) {
|
||||
llvm::legacy::FunctionPassManager FPM(F.getParent());
|
||||
FPM.add(createCFGSimplificationPass());
|
||||
|
||||
FPM.doInitialization();
|
||||
FPM.run(F);
|
||||
FPM.doFinalization();
|
||||
}
|
||||
|
||||
static void lowerSubFn(IRBuilder<> &Builder, CoroSubFnInst *SubFn) {
|
||||
Builder.SetInsertPoint(SubFn);
|
||||
Value *FrameRaw = SubFn->getFrame();
|
||||
int Index = SubFn->getIndex();
|
||||
|
||||
auto *FrameTy = StructType::get(
|
||||
SubFn->getContext(), {Builder.getInt8PtrTy(), Builder.getInt8PtrTy()});
|
||||
PointerType *FramePtrTy = FrameTy->getPointerTo();
|
||||
|
||||
Builder.SetInsertPoint(SubFn);
|
||||
auto *FramePtr = Builder.CreateBitCast(FrameRaw, FramePtrTy);
|
||||
auto *Gep = Builder.CreateConstInBoundsGEP2_32(FrameTy, FramePtr, 0, Index);
|
||||
auto *Load = Builder.CreateLoad(Gep);
|
||||
|
||||
SubFn->replaceAllUsesWith(Load);
|
||||
}
|
||||
|
||||
bool Lowerer::lowerRemainingCoroIntrinsics(Function &F) {
|
||||
bool Changed = false;
|
||||
|
||||
for (auto IB = inst_begin(F), E = inst_end(F); IB != E;) {
|
||||
Instruction &I = *IB++;
|
||||
if (auto *II = dyn_cast<IntrinsicInst>(&I)) {
|
||||
switch (II->getIntrinsicID()) {
|
||||
default:
|
||||
continue;
|
||||
case Intrinsic::coro_begin:
|
||||
II->replaceAllUsesWith(II->getArgOperand(1));
|
||||
break;
|
||||
case Intrinsic::coro_free:
|
||||
II->replaceAllUsesWith(II->getArgOperand(1));
|
||||
break;
|
||||
case Intrinsic::coro_alloc:
|
||||
II->replaceAllUsesWith(ConstantInt::getTrue(Context));
|
||||
break;
|
||||
case Intrinsic::coro_id:
|
||||
II->replaceAllUsesWith(ConstantTokenNone::get(Context));
|
||||
break;
|
||||
case Intrinsic::coro_subfn_addr:
|
||||
lowerSubFn(Builder, cast<CoroSubFnInst>(II));
|
||||
break;
|
||||
}
|
||||
II->eraseFromParent();
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Changed) {
|
||||
// After replacement were made we can cleanup the function body a little.
|
||||
simplifyCFG(F);
|
||||
}
|
||||
return Changed;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Top Level Driver
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
|
||||
struct CoroCleanup : FunctionPass {
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
|
||||
CoroCleanup() : FunctionPass(ID) {
|
||||
initializeCoroCleanupPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
std::unique_ptr<Lowerer> L;
|
||||
|
||||
// This pass has work to do only if we find intrinsics we are going to lower
|
||||
// in the module.
|
||||
bool doInitialization(Module &M) override {
|
||||
if (coro::declaresIntrinsics(M, {"llvm.coro.alloc", "llvm.coro.begin",
|
||||
"llvm.coro.subfn.addr", "llvm.coro.free",
|
||||
"llvm.coro.id"}))
|
||||
L = llvm::make_unique<Lowerer>(M);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool runOnFunction(Function &F) override {
|
||||
if (L)
|
||||
return L->lowerRemainingCoroIntrinsics(F);
|
||||
return false;
|
||||
}
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
if (!L)
|
||||
AU.setPreservesAll();
|
||||
}
|
||||
StringRef getPassName() const override { return "Coroutine Cleanup"; }
|
||||
};
|
||||
}
|
||||
|
||||
char CoroCleanup::ID = 0;
|
||||
INITIALIZE_PASS(CoroCleanup, "coro-cleanup",
|
||||
"Lower all coroutine related intrinsics", false, false)
|
||||
|
||||
Pass *llvm::createCoroCleanupPass() { return new CoroCleanup(); }
|
@ -1,223 +0,0 @@
|
||||
//===- CoroEarly.cpp - Coroutine Early Function Pass ----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This pass lowers coroutine intrinsics that hide the details of the exact
|
||||
// calling convention for coroutine resume and destroy functions and details of
|
||||
// the structure of the coroutine frame.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CoroInternal.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Pass.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "coro-early"
|
||||
|
||||
namespace {
|
||||
// Created on demand if CoroEarly pass has work to do.
|
||||
class Lowerer : public coro::LowererBase {
|
||||
IRBuilder<> Builder;
|
||||
PointerType *const AnyResumeFnPtrTy;
|
||||
|
||||
void lowerResumeOrDestroy(CallSite CS, CoroSubFnInst::ResumeKind);
|
||||
void lowerCoroPromise(CoroPromiseInst *Intrin);
|
||||
void lowerCoroDone(IntrinsicInst *II);
|
||||
|
||||
public:
|
||||
Lowerer(Module &M)
|
||||
: LowererBase(M), Builder(Context),
|
||||
AnyResumeFnPtrTy(FunctionType::get(Type::getVoidTy(Context), Int8Ptr,
|
||||
/*isVarArg=*/false)
|
||||
->getPointerTo()) {}
|
||||
bool lowerEarlyIntrinsics(Function &F);
|
||||
};
|
||||
}
|
||||
|
||||
// Replace a direct call to coro.resume or coro.destroy with an indirect call to
|
||||
// an address returned by coro.subfn.addr intrinsic. This is done so that
|
||||
// CGPassManager recognizes devirtualization when CoroElide pass replaces a call
|
||||
// to coro.subfn.addr with an appropriate function address.
|
||||
void Lowerer::lowerResumeOrDestroy(CallSite CS,
|
||||
CoroSubFnInst::ResumeKind Index) {
|
||||
Value *ResumeAddr =
|
||||
makeSubFnCall(CS.getArgOperand(0), Index, CS.getInstruction());
|
||||
CS.setCalledFunction(ResumeAddr);
|
||||
CS.setCallingConv(CallingConv::Fast);
|
||||
}
|
||||
|
||||
// Coroutine promise field is always at the fixed offset from the beginning of
|
||||
// the coroutine frame. i8* coro.promise(i8*, i1 from) intrinsic adds an offset
|
||||
// to a passed pointer to move from coroutine frame to coroutine promise and
|
||||
// vice versa. Since we don't know exactly which coroutine frame it is, we build
|
||||
// a coroutine frame mock up starting with two function pointers, followed by a
|
||||
// properly aligned coroutine promise field.
|
||||
// TODO: Handle the case when coroutine promise alloca has align override.
|
||||
void Lowerer::lowerCoroPromise(CoroPromiseInst *Intrin) {
|
||||
Value *Operand = Intrin->getArgOperand(0);
|
||||
unsigned Alignement = Intrin->getAlignment();
|
||||
Type *Int8Ty = Builder.getInt8Ty();
|
||||
|
||||
auto *SampleStruct =
|
||||
StructType::get(Context, {AnyResumeFnPtrTy, AnyResumeFnPtrTy, Int8Ty});
|
||||
const DataLayout &DL = TheModule.getDataLayout();
|
||||
int64_t Offset = alignTo(
|
||||
DL.getStructLayout(SampleStruct)->getElementOffset(2), Alignement);
|
||||
if (Intrin->isFromPromise())
|
||||
Offset = -Offset;
|
||||
|
||||
Builder.SetInsertPoint(Intrin);
|
||||
Value *Replacement =
|
||||
Builder.CreateConstInBoundsGEP1_32(Int8Ty, Operand, Offset);
|
||||
|
||||
Intrin->replaceAllUsesWith(Replacement);
|
||||
Intrin->eraseFromParent();
|
||||
}
|
||||
|
||||
// When a coroutine reaches final suspend point, it zeros out ResumeFnAddr in
|
||||
// the coroutine frame (it is UB to resume from a final suspend point).
|
||||
// The llvm.coro.done intrinsic is used to check whether a coroutine is
|
||||
// suspended at the final suspend point or not.
|
||||
void Lowerer::lowerCoroDone(IntrinsicInst *II) {
|
||||
Value *Operand = II->getArgOperand(0);
|
||||
|
||||
// ResumeFnAddr is the first pointer sized element of the coroutine frame.
|
||||
auto *FrameTy = Int8Ptr;
|
||||
PointerType *FramePtrTy = FrameTy->getPointerTo();
|
||||
|
||||
Builder.SetInsertPoint(II);
|
||||
auto *BCI = Builder.CreateBitCast(Operand, FramePtrTy);
|
||||
auto *Gep = Builder.CreateConstInBoundsGEP1_32(FrameTy, BCI, 0);
|
||||
auto *Load = Builder.CreateLoad(Gep);
|
||||
auto *Cond = Builder.CreateICmpEQ(Load, NullPtr);
|
||||
|
||||
II->replaceAllUsesWith(Cond);
|
||||
II->eraseFromParent();
|
||||
}
|
||||
|
||||
// Prior to CoroSplit, calls to coro.begin needs to be marked as NoDuplicate,
|
||||
// as CoroSplit assumes there is exactly one coro.begin. After CoroSplit,
|
||||
// NoDuplicate attribute will be removed from coro.begin otherwise, it will
|
||||
// interfere with inlining.
|
||||
static void setCannotDuplicate(CoroIdInst *CoroId) {
|
||||
for (User *U : CoroId->users())
|
||||
if (auto *CB = dyn_cast<CoroBeginInst>(U))
|
||||
CB->setCannotDuplicate();
|
||||
}
|
||||
|
||||
bool Lowerer::lowerEarlyIntrinsics(Function &F) {
|
||||
bool Changed = false;
|
||||
CoroIdInst *CoroId = nullptr;
|
||||
SmallVector<CoroFreeInst *, 4> CoroFrees;
|
||||
for (auto IB = inst_begin(F), IE = inst_end(F); IB != IE;) {
|
||||
Instruction &I = *IB++;
|
||||
if (auto CS = CallSite(&I)) {
|
||||
switch (CS.getIntrinsicID()) {
|
||||
default:
|
||||
continue;
|
||||
case Intrinsic::coro_free:
|
||||
CoroFrees.push_back(cast<CoroFreeInst>(&I));
|
||||
break;
|
||||
case Intrinsic::coro_suspend:
|
||||
// Make sure that final suspend point is not duplicated as CoroSplit
|
||||
// pass expects that there is at most one final suspend point.
|
||||
if (cast<CoroSuspendInst>(&I)->isFinal())
|
||||
CS.setCannotDuplicate();
|
||||
break;
|
||||
case Intrinsic::coro_end:
|
||||
// Make sure that fallthrough coro.end is not duplicated as CoroSplit
|
||||
// pass expects that there is at most one fallthrough coro.end.
|
||||
if (cast<CoroEndInst>(&I)->isFallthrough())
|
||||
CS.setCannotDuplicate();
|
||||
break;
|
||||
case Intrinsic::coro_id:
|
||||
// Mark a function that comes out of the frontend that has a coro.id
|
||||
// with a coroutine attribute.
|
||||
if (auto *CII = cast<CoroIdInst>(&I)) {
|
||||
if (CII->getInfo().isPreSplit()) {
|
||||
F.addFnAttr(CORO_PRESPLIT_ATTR, UNPREPARED_FOR_SPLIT);
|
||||
setCannotDuplicate(CII);
|
||||
CII->setCoroutineSelf();
|
||||
CoroId = cast<CoroIdInst>(&I);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Intrinsic::coro_resume:
|
||||
lowerResumeOrDestroy(CS, CoroSubFnInst::ResumeIndex);
|
||||
break;
|
||||
case Intrinsic::coro_destroy:
|
||||
lowerResumeOrDestroy(CS, CoroSubFnInst::DestroyIndex);
|
||||
break;
|
||||
case Intrinsic::coro_promise:
|
||||
lowerCoroPromise(cast<CoroPromiseInst>(&I));
|
||||
break;
|
||||
case Intrinsic::coro_done:
|
||||
lowerCoroDone(cast<IntrinsicInst>(&I));
|
||||
break;
|
||||
}
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
// Make sure that all CoroFree reference the coro.id intrinsic.
|
||||
// Token type is not exposed through coroutine C/C++ builtins to plain C, so
|
||||
// we allow specifying none and fixing it up here.
|
||||
if (CoroId)
|
||||
for (CoroFreeInst *CF : CoroFrees)
|
||||
CF->setArgOperand(0, CoroId);
|
||||
return Changed;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Top Level Driver
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
|
||||
struct CoroEarly : public FunctionPass {
|
||||
static char ID; // Pass identification, replacement for typeid.
|
||||
CoroEarly() : FunctionPass(ID) {
|
||||
initializeCoroEarlyPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
std::unique_ptr<Lowerer> L;
|
||||
|
||||
// This pass has work to do only if we find intrinsics we are going to lower
|
||||
// in the module.
|
||||
bool doInitialization(Module &M) override {
|
||||
if (coro::declaresIntrinsics(M, {"llvm.coro.id", "llvm.coro.destroy",
|
||||
"llvm.coro.done", "llvm.coro.end",
|
||||
"llvm.coro.free", "llvm.coro.promise",
|
||||
"llvm.coro.resume", "llvm.coro.suspend"}))
|
||||
L = llvm::make_unique<Lowerer>(M);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool runOnFunction(Function &F) override {
|
||||
if (!L)
|
||||
return false;
|
||||
|
||||
return L->lowerEarlyIntrinsics(F);
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.setPreservesCFG();
|
||||
}
|
||||
StringRef getPassName() const override {
|
||||
return "Lower early coroutine intrinsics";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
char CoroEarly::ID = 0;
|
||||
INITIALIZE_PASS(CoroEarly, "coro-early", "Lower early coroutine intrinsics",
|
||||
false, false)
|
||||
|
||||
Pass *llvm::createCoroEarlyPass() { return new CoroEarly(); }
|
@ -1,321 +0,0 @@
|
||||
//===- CoroElide.cpp - Coroutine Frame Allocation Elision Pass ------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This pass replaces dynamic allocation of coroutine frame with alloca and
|
||||
// replaces calls to llvm.coro.resume and llvm.coro.destroy with direct calls
|
||||
// to coroutine sub-functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CoroInternal.h"
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/Analysis/InstructionSimplify.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "coro-elide"
|
||||
|
||||
namespace {
|
||||
// Created on demand if CoroElide pass has work to do.
|
||||
struct Lowerer : coro::LowererBase {
|
||||
SmallVector<CoroIdInst *, 4> CoroIds;
|
||||
SmallVector<CoroBeginInst *, 1> CoroBegins;
|
||||
SmallVector<CoroAllocInst *, 1> CoroAllocs;
|
||||
SmallVector<CoroSubFnInst *, 4> ResumeAddr;
|
||||
SmallVector<CoroSubFnInst *, 4> DestroyAddr;
|
||||
SmallVector<CoroFreeInst *, 1> CoroFrees;
|
||||
|
||||
Lowerer(Module &M) : LowererBase(M) {}
|
||||
|
||||
void elideHeapAllocations(Function *F, Type *FrameTy, AAResults &AA);
|
||||
bool shouldElide() const;
|
||||
bool processCoroId(CoroIdInst *, AAResults &AA);
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
// Go through the list of coro.subfn.addr intrinsics and replace them with the
|
||||
// provided constant.
|
||||
static void replaceWithConstant(Constant *Value,
|
||||
SmallVectorImpl<CoroSubFnInst *> &Users) {
|
||||
if (Users.empty())
|
||||
return;
|
||||
|
||||
// See if we need to bitcast the constant to match the type of the intrinsic
|
||||
// being replaced. Note: All coro.subfn.addr intrinsics return the same type,
|
||||
// so we only need to examine the type of the first one in the list.
|
||||
Type *IntrTy = Users.front()->getType();
|
||||
Type *ValueTy = Value->getType();
|
||||
if (ValueTy != IntrTy) {
|
||||
// May need to tweak the function type to match the type expected at the
|
||||
// use site.
|
||||
assert(ValueTy->isPointerTy() && IntrTy->isPointerTy());
|
||||
Value = ConstantExpr::getBitCast(Value, IntrTy);
|
||||
}
|
||||
|
||||
// Now the value type matches the type of the intrinsic. Replace them all!
|
||||
for (CoroSubFnInst *I : Users)
|
||||
replaceAndRecursivelySimplify(I, Value);
|
||||
}
|
||||
|
||||
// See if any operand of the call instruction references the coroutine frame.
|
||||
static bool operandReferences(CallInst *CI, AllocaInst *Frame, AAResults &AA) {
|
||||
for (Value *Op : CI->operand_values())
|
||||
if (AA.alias(Op, Frame) != NoAlias)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look for any tail calls referencing the coroutine frame and remove tail
|
||||
// attribute from them, since now coroutine frame resides on the stack and tail
|
||||
// call implies that the function does not references anything on the stack.
|
||||
static void removeTailCallAttribute(AllocaInst *Frame, AAResults &AA) {
|
||||
Function &F = *Frame->getFunction();
|
||||
MemoryLocation Mem(Frame);
|
||||
for (Instruction &I : instructions(F))
|
||||
if (auto *Call = dyn_cast<CallInst>(&I))
|
||||
if (Call->isTailCall() && operandReferences(Call, Frame, AA)) {
|
||||
// FIXME: If we ever hit this check. Evaluate whether it is more
|
||||
// appropriate to retain musttail and allow the code to compile.
|
||||
if (Call->isMustTailCall())
|
||||
report_fatal_error("Call referring to the coroutine frame cannot be "
|
||||
"marked as musttail");
|
||||
Call->setTailCall(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Given a resume function @f.resume(%f.frame* %frame), returns %f.frame type.
|
||||
static Type *getFrameType(Function *Resume) {
|
||||
auto *ArgType = Resume->arg_begin()->getType();
|
||||
return cast<PointerType>(ArgType)->getElementType();
|
||||
}
|
||||
|
||||
// Finds first non alloca instruction in the entry block of a function.
|
||||
static Instruction *getFirstNonAllocaInTheEntryBlock(Function *F) {
|
||||
for (Instruction &I : F->getEntryBlock())
|
||||
if (!isa<AllocaInst>(&I))
|
||||
return &I;
|
||||
llvm_unreachable("no terminator in the entry block");
|
||||
}
|
||||
|
||||
// To elide heap allocations we need to suppress code blocks guarded by
|
||||
// llvm.coro.alloc and llvm.coro.free instructions.
|
||||
void Lowerer::elideHeapAllocations(Function *F, Type *FrameTy, AAResults &AA) {
|
||||
LLVMContext &C = FrameTy->getContext();
|
||||
auto *InsertPt =
|
||||
getFirstNonAllocaInTheEntryBlock(CoroIds.front()->getFunction());
|
||||
|
||||
// Replacing llvm.coro.alloc with false will suppress dynamic
|
||||
// allocation as it is expected for the frontend to generate the code that
|
||||
// looks like:
|
||||
// id = coro.id(...)
|
||||
// mem = coro.alloc(id) ? malloc(coro.size()) : 0;
|
||||
// coro.begin(id, mem)
|
||||
auto *False = ConstantInt::getFalse(C);
|
||||
for (auto *CA : CoroAllocs) {
|
||||
CA->replaceAllUsesWith(False);
|
||||
CA->eraseFromParent();
|
||||
}
|
||||
|
||||
// FIXME: Design how to transmit alignment information for every alloca that
|
||||
// is spilled into the coroutine frame and recreate the alignment information
|
||||
// here. Possibly we will need to do a mini SROA here and break the coroutine
|
||||
// frame into individual AllocaInst recreating the original alignment.
|
||||
const DataLayout &DL = F->getParent()->getDataLayout();
|
||||
auto *Frame = new AllocaInst(FrameTy, DL.getAllocaAddrSpace(), "", InsertPt);
|
||||
auto *FrameVoidPtr =
|
||||
new BitCastInst(Frame, Type::getInt8PtrTy(C), "vFrame", InsertPt);
|
||||
|
||||
for (auto *CB : CoroBegins) {
|
||||
CB->replaceAllUsesWith(FrameVoidPtr);
|
||||
CB->eraseFromParent();
|
||||
}
|
||||
|
||||
// Since now coroutine frame lives on the stack we need to make sure that
|
||||
// any tail call referencing it, must be made non-tail call.
|
||||
removeTailCallAttribute(Frame, AA);
|
||||
}
|
||||
|
||||
bool Lowerer::shouldElide() const {
|
||||
// If no CoroAllocs, we cannot suppress allocation, so elision is not
|
||||
// possible.
|
||||
if (CoroAllocs.empty())
|
||||
return false;
|
||||
|
||||
// Check that for every coro.begin there is a coro.destroy directly
|
||||
// referencing the SSA value of that coro.begin. If the value escaped, then
|
||||
// coro.destroy would have been referencing a memory location storing that
|
||||
// value and not the virtual register.
|
||||
|
||||
SmallPtrSet<CoroBeginInst *, 8> ReferencedCoroBegins;
|
||||
|
||||
for (CoroSubFnInst *DA : DestroyAddr) {
|
||||
if (auto *CB = dyn_cast<CoroBeginInst>(DA->getFrame()))
|
||||
ReferencedCoroBegins.insert(CB);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// If size of the set is the same as total number of CoroBegins, means we
|
||||
// found a coro.free or coro.destroy mentioning a coro.begin and we can
|
||||
// perform heap elision.
|
||||
return ReferencedCoroBegins.size() == CoroBegins.size();
|
||||
}
|
||||
|
||||
bool Lowerer::processCoroId(CoroIdInst *CoroId, AAResults &AA) {
|
||||
CoroBegins.clear();
|
||||
CoroAllocs.clear();
|
||||
CoroFrees.clear();
|
||||
ResumeAddr.clear();
|
||||
DestroyAddr.clear();
|
||||
|
||||
// Collect all coro.begin and coro.allocs associated with this coro.id.
|
||||
for (User *U : CoroId->users()) {
|
||||
if (auto *CB = dyn_cast<CoroBeginInst>(U))
|
||||
CoroBegins.push_back(CB);
|
||||
else if (auto *CA = dyn_cast<CoroAllocInst>(U))
|
||||
CoroAllocs.push_back(CA);
|
||||
else if (auto *CF = dyn_cast<CoroFreeInst>(U))
|
||||
CoroFrees.push_back(CF);
|
||||
}
|
||||
|
||||
// Collect all coro.subfn.addrs associated with coro.begin.
|
||||
// Note, we only devirtualize the calls if their coro.subfn.addr refers to
|
||||
// coro.begin directly. If we run into cases where this check is too
|
||||
// conservative, we can consider relaxing the check.
|
||||
for (CoroBeginInst *CB : CoroBegins) {
|
||||
for (User *U : CB->users())
|
||||
if (auto *II = dyn_cast<CoroSubFnInst>(U))
|
||||
switch (II->getIndex()) {
|
||||
case CoroSubFnInst::ResumeIndex:
|
||||
ResumeAddr.push_back(II);
|
||||
break;
|
||||
case CoroSubFnInst::DestroyIndex:
|
||||
DestroyAddr.push_back(II);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unexpected coro.subfn.addr constant");
|
||||
}
|
||||
}
|
||||
|
||||
// PostSplit coro.id refers to an array of subfunctions in its Info
|
||||
// argument.
|
||||
ConstantArray *Resumers = CoroId->getInfo().Resumers;
|
||||
assert(Resumers && "PostSplit coro.id Info argument must refer to an array"
|
||||
"of coroutine subfunctions");
|
||||
auto *ResumeAddrConstant =
|
||||
ConstantExpr::getExtractValue(Resumers, CoroSubFnInst::ResumeIndex);
|
||||
|
||||
replaceWithConstant(ResumeAddrConstant, ResumeAddr);
|
||||
|
||||
bool ShouldElide = shouldElide();
|
||||
|
||||
auto *DestroyAddrConstant = ConstantExpr::getExtractValue(
|
||||
Resumers,
|
||||
ShouldElide ? CoroSubFnInst::CleanupIndex : CoroSubFnInst::DestroyIndex);
|
||||
|
||||
replaceWithConstant(DestroyAddrConstant, DestroyAddr);
|
||||
|
||||
if (ShouldElide) {
|
||||
auto *FrameTy = getFrameType(cast<Function>(ResumeAddrConstant));
|
||||
elideHeapAllocations(CoroId->getFunction(), FrameTy, AA);
|
||||
coro::replaceCoroFree(CoroId, /*Elide=*/true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// See if there are any coro.subfn.addr instructions referring to coro.devirt
|
||||
// trigger, if so, replace them with a direct call to devirt trigger function.
|
||||
static bool replaceDevirtTrigger(Function &F) {
|
||||
SmallVector<CoroSubFnInst *, 1> DevirtAddr;
|
||||
for (auto &I : instructions(F))
|
||||
if (auto *SubFn = dyn_cast<CoroSubFnInst>(&I))
|
||||
if (SubFn->getIndex() == CoroSubFnInst::RestartTrigger)
|
||||
DevirtAddr.push_back(SubFn);
|
||||
|
||||
if (DevirtAddr.empty())
|
||||
return false;
|
||||
|
||||
Module &M = *F.getParent();
|
||||
Function *DevirtFn = M.getFunction(CORO_DEVIRT_TRIGGER_FN);
|
||||
assert(DevirtFn && "coro.devirt.fn not found");
|
||||
replaceWithConstant(DevirtFn, DevirtAddr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Top Level Driver
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
struct CoroElide : FunctionPass {
|
||||
static char ID;
|
||||
CoroElide() : FunctionPass(ID) {
|
||||
initializeCoroElidePass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
std::unique_ptr<Lowerer> L;
|
||||
|
||||
bool doInitialization(Module &M) override {
|
||||
if (coro::declaresIntrinsics(M, {"llvm.coro.id"}))
|
||||
L = llvm::make_unique<Lowerer>(M);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool runOnFunction(Function &F) override {
|
||||
if (!L)
|
||||
return false;
|
||||
|
||||
bool Changed = false;
|
||||
|
||||
if (F.hasFnAttribute(CORO_PRESPLIT_ATTR))
|
||||
Changed = replaceDevirtTrigger(F);
|
||||
|
||||
L->CoroIds.clear();
|
||||
|
||||
// Collect all PostSplit coro.ids.
|
||||
for (auto &I : instructions(F))
|
||||
if (auto *CII = dyn_cast<CoroIdInst>(&I))
|
||||
if (CII->getInfo().isPostSplit())
|
||||
// If it is the coroutine itself, don't touch it.
|
||||
if (CII->getCoroutine() != CII->getFunction())
|
||||
L->CoroIds.push_back(CII);
|
||||
|
||||
// If we did not find any coro.id, there is nothing to do.
|
||||
if (L->CoroIds.empty())
|
||||
return Changed;
|
||||
|
||||
AAResults &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();
|
||||
|
||||
for (auto *CII : L->CoroIds)
|
||||
Changed |= L->processCoroId(CII, AA);
|
||||
|
||||
return Changed;
|
||||
}
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.addRequired<AAResultsWrapperPass>();
|
||||
}
|
||||
StringRef getPassName() const override { return "Coroutine Elision"; }
|
||||
};
|
||||
}
|
||||
|
||||
char CoroElide::ID = 0;
|
||||
INITIALIZE_PASS_BEGIN(
|
||||
CoroElide, "coro-elide",
|
||||
"Coroutine frame allocation elision and indirect calls replacement", false,
|
||||
false)
|
||||
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
|
||||
INITIALIZE_PASS_END(
|
||||
CoroElide, "coro-elide",
|
||||
"Coroutine frame allocation elision and indirect calls replacement", false,
|
||||
false)
|
||||
|
||||
Pass *llvm::createCoroElidePass() { return new CoroElide(); }
|
File diff suppressed because it is too large
Load Diff
323
external/llvm/lib/Transforms/Coroutines/CoroInstr.h
vendored
323
external/llvm/lib/Transforms/Coroutines/CoroInstr.h
vendored
@ -1,323 +0,0 @@
|
||||
//===-- CoroInstr.h - Coroutine Intrinsics Instruction Wrappers -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This file defines classes that make it really easy to deal with intrinsic
|
||||
// functions with the isa/dyncast family of functions. In particular, this
|
||||
// allows you to do things like:
|
||||
//
|
||||
// if (auto *SF = dyn_cast<CoroSubFnInst>(Inst))
|
||||
// ... SF->getFrame() ...
|
||||
//
|
||||
// All intrinsic function calls are instances of the call instruction, so these
|
||||
// are all subclasses of the CallInst class. Note that none of these classes
|
||||
// has state or virtual methods, which is an important part of this gross/neat
|
||||
// hack working.
|
||||
//
|
||||
// The helpful comment above is borrowed from llvm/IntrinsicInst.h, we keep
|
||||
// coroutine intrinsic wrappers here since they are only used by the passes in
|
||||
// the Coroutine library.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TRANSFORMS_COROUTINES_COROINSTR_H
|
||||
#define LLVM_LIB_TRANSFORMS_COROUTINES_COROINSTR_H
|
||||
|
||||
#include "llvm/IR/GlobalVariable.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// This class represents the llvm.coro.subfn.addr instruction.
|
||||
class LLVM_LIBRARY_VISIBILITY CoroSubFnInst : public IntrinsicInst {
|
||||
enum { FrameArg, IndexArg };
|
||||
|
||||
public:
|
||||
enum ResumeKind {
|
||||
RestartTrigger = -1,
|
||||
ResumeIndex,
|
||||
DestroyIndex,
|
||||
CleanupIndex,
|
||||
IndexLast,
|
||||
IndexFirst = RestartTrigger
|
||||
};
|
||||
|
||||
Value *getFrame() const { return getArgOperand(FrameArg); }
|
||||
ResumeKind getIndex() const {
|
||||
int64_t Index = getRawIndex()->getValue().getSExtValue();
|
||||
assert(Index >= IndexFirst && Index < IndexLast &&
|
||||
"unexpected CoroSubFnInst index argument");
|
||||
return static_cast<ResumeKind>(Index);
|
||||
}
|
||||
|
||||
ConstantInt *getRawIndex() const {
|
||||
return cast<ConstantInt>(getArgOperand(IndexArg));
|
||||
}
|
||||
|
||||
// Methods to support type inquiry through isa, cast, and dyn_cast:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::coro_subfn_addr;
|
||||
}
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
};
|
||||
|
||||
/// This represents the llvm.coro.alloc instruction.
|
||||
class LLVM_LIBRARY_VISIBILITY CoroAllocInst : public IntrinsicInst {
|
||||
public:
|
||||
// Methods to support type inquiry through isa, cast, and dyn_cast:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::coro_alloc;
|
||||
}
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
};
|
||||
|
||||
/// This represents the llvm.coro.alloc instruction.
|
||||
class LLVM_LIBRARY_VISIBILITY CoroIdInst : public IntrinsicInst {
|
||||
enum { AlignArg, PromiseArg, CoroutineArg, InfoArg };
|
||||
|
||||
public:
|
||||
CoroAllocInst *getCoroAlloc() {
|
||||
for (User *U : users())
|
||||
if (auto *CA = dyn_cast<CoroAllocInst>(U))
|
||||
return CA;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IntrinsicInst *getCoroBegin() {
|
||||
for (User *U : users())
|
||||
if (auto *II = dyn_cast<IntrinsicInst>(U))
|
||||
if (II->getIntrinsicID() == Intrinsic::coro_begin)
|
||||
return II;
|
||||
llvm_unreachable("no coro.begin associated with coro.id");
|
||||
}
|
||||
|
||||
AllocaInst *getPromise() const {
|
||||
Value *Arg = getArgOperand(PromiseArg);
|
||||
return isa<ConstantPointerNull>(Arg)
|
||||
? nullptr
|
||||
: cast<AllocaInst>(Arg->stripPointerCasts());
|
||||
}
|
||||
|
||||
void clearPromise() {
|
||||
Value *Arg = getArgOperand(PromiseArg);
|
||||
setArgOperand(PromiseArg,
|
||||
ConstantPointerNull::get(Type::getInt8PtrTy(getContext())));
|
||||
if (isa<AllocaInst>(Arg))
|
||||
return;
|
||||
assert((isa<BitCastInst>(Arg) || isa<GetElementPtrInst>(Arg)) &&
|
||||
"unexpected instruction designating the promise");
|
||||
// TODO: Add a check that any remaining users of Inst are after coro.begin
|
||||
// or add code to move the users after coro.begin.
|
||||
auto *Inst = cast<Instruction>(Arg);
|
||||
if (Inst->use_empty()) {
|
||||
Inst->eraseFromParent();
|
||||
return;
|
||||
}
|
||||
Inst->moveBefore(getCoroBegin()->getNextNode());
|
||||
}
|
||||
|
||||
// Info argument of coro.id is
|
||||
// fresh out of the frontend: null ;
|
||||
// outlined : {Init, Return, Susp1, Susp2, ...} ;
|
||||
// postsplit : [resume, destroy, cleanup] ;
|
||||
//
|
||||
// If parts of the coroutine were outlined to protect against undesirable
|
||||
// code motion, these functions will be stored in a struct literal referred to
|
||||
// by the Info parameter. Note: this is only needed before coroutine is split.
|
||||
//
|
||||
// After coroutine is split, resume functions are stored in an array
|
||||
// referred to by this parameter.
|
||||
|
||||
struct Info {
|
||||
ConstantStruct *OutlinedParts = nullptr;
|
||||
ConstantArray *Resumers = nullptr;
|
||||
|
||||
bool hasOutlinedParts() const { return OutlinedParts != nullptr; }
|
||||
bool isPostSplit() const { return Resumers != nullptr; }
|
||||
bool isPreSplit() const { return !isPostSplit(); }
|
||||
};
|
||||
Info getInfo() const {
|
||||
Info Result;
|
||||
auto *GV = dyn_cast<GlobalVariable>(getRawInfo());
|
||||
if (!GV)
|
||||
return Result;
|
||||
|
||||
assert(GV->isConstant() && GV->hasDefinitiveInitializer());
|
||||
Constant *Initializer = GV->getInitializer();
|
||||
if ((Result.OutlinedParts = dyn_cast<ConstantStruct>(Initializer)))
|
||||
return Result;
|
||||
|
||||
Result.Resumers = cast<ConstantArray>(Initializer);
|
||||
return Result;
|
||||
}
|
||||
Constant *getRawInfo() const {
|
||||
return cast<Constant>(getArgOperand(InfoArg)->stripPointerCasts());
|
||||
}
|
||||
|
||||
void setInfo(Constant *C) { setArgOperand(InfoArg, C); }
|
||||
|
||||
Function *getCoroutine() const {
|
||||
return cast<Function>(getArgOperand(CoroutineArg)->stripPointerCasts());
|
||||
}
|
||||
void setCoroutineSelf() {
|
||||
assert(isa<ConstantPointerNull>(getArgOperand(CoroutineArg)) &&
|
||||
"Coroutine argument is already assigned");
|
||||
auto *const Int8PtrTy = Type::getInt8PtrTy(getContext());
|
||||
setArgOperand(CoroutineArg,
|
||||
ConstantExpr::getBitCast(getFunction(), Int8PtrTy));
|
||||
}
|
||||
|
||||
// Methods to support type inquiry through isa, cast, and dyn_cast:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::coro_id;
|
||||
}
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
};
|
||||
|
||||
/// This represents the llvm.coro.frame instruction.
|
||||
class LLVM_LIBRARY_VISIBILITY CoroFrameInst : public IntrinsicInst {
|
||||
public:
|
||||
// Methods to support type inquiry through isa, cast, and dyn_cast:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::coro_frame;
|
||||
}
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
};
|
||||
|
||||
/// This represents the llvm.coro.free instruction.
|
||||
class LLVM_LIBRARY_VISIBILITY CoroFreeInst : public IntrinsicInst {
|
||||
enum { IdArg, FrameArg };
|
||||
|
||||
public:
|
||||
Value *getFrame() const { return getArgOperand(FrameArg); }
|
||||
|
||||
// Methods to support type inquiry through isa, cast, and dyn_cast:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::coro_free;
|
||||
}
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
};
|
||||
|
||||
/// This class represents the llvm.coro.begin instruction.
|
||||
class LLVM_LIBRARY_VISIBILITY CoroBeginInst : public IntrinsicInst {
|
||||
enum { IdArg, MemArg };
|
||||
|
||||
public:
|
||||
CoroIdInst *getId() const { return cast<CoroIdInst>(getArgOperand(IdArg)); }
|
||||
|
||||
Value *getMem() const { return getArgOperand(MemArg); }
|
||||
|
||||
// Methods for support type inquiry through isa, cast, and dyn_cast:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::coro_begin;
|
||||
}
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
};
|
||||
|
||||
/// This represents the llvm.coro.save instruction.
|
||||
class LLVM_LIBRARY_VISIBILITY CoroSaveInst : public IntrinsicInst {
|
||||
public:
|
||||
// Methods to support type inquiry through isa, cast, and dyn_cast:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::coro_save;
|
||||
}
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
};
|
||||
|
||||
/// This represents the llvm.coro.promise instruction.
|
||||
class LLVM_LIBRARY_VISIBILITY CoroPromiseInst : public IntrinsicInst {
|
||||
enum { FrameArg, AlignArg, FromArg };
|
||||
|
||||
public:
|
||||
bool isFromPromise() const {
|
||||
return cast<Constant>(getArgOperand(FromArg))->isOneValue();
|
||||
}
|
||||
unsigned getAlignment() const {
|
||||
return cast<ConstantInt>(getArgOperand(AlignArg))->getZExtValue();
|
||||
}
|
||||
|
||||
// Methods to support type inquiry through isa, cast, and dyn_cast:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::coro_promise;
|
||||
}
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
};
|
||||
|
||||
/// This represents the llvm.coro.suspend instruction.
|
||||
class LLVM_LIBRARY_VISIBILITY CoroSuspendInst : public IntrinsicInst {
|
||||
enum { SaveArg, FinalArg };
|
||||
|
||||
public:
|
||||
CoroSaveInst *getCoroSave() const {
|
||||
Value *Arg = getArgOperand(SaveArg);
|
||||
if (auto *SI = dyn_cast<CoroSaveInst>(Arg))
|
||||
return SI;
|
||||
assert(isa<ConstantTokenNone>(Arg));
|
||||
return nullptr;
|
||||
}
|
||||
bool isFinal() const {
|
||||
return cast<Constant>(getArgOperand(FinalArg))->isOneValue();
|
||||
}
|
||||
|
||||
// Methods to support type inquiry through isa, cast, and dyn_cast:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::coro_suspend;
|
||||
}
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
};
|
||||
|
||||
/// This represents the llvm.coro.size instruction.
|
||||
class LLVM_LIBRARY_VISIBILITY CoroSizeInst : public IntrinsicInst {
|
||||
public:
|
||||
// Methods to support type inquiry through isa, cast, and dyn_cast:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::coro_size;
|
||||
}
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
};
|
||||
|
||||
/// This represents the llvm.coro.end instruction.
|
||||
class LLVM_LIBRARY_VISIBILITY CoroEndInst : public IntrinsicInst {
|
||||
enum { FrameArg, UnwindArg };
|
||||
|
||||
public:
|
||||
bool isFallthrough() const { return !isUnwind(); }
|
||||
bool isUnwind() const {
|
||||
return cast<Constant>(getArgOperand(UnwindArg))->isOneValue();
|
||||
}
|
||||
|
||||
// Methods to support type inquiry through isa, cast, and dyn_cast:
|
||||
static bool classof(const IntrinsicInst *I) {
|
||||
return I->getIntrinsicID() == Intrinsic::coro_end;
|
||||
}
|
||||
static bool classof(const Value *V) {
|
||||
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
||||
}
|
||||
};
|
||||
|
||||
} // End namespace llvm.
|
||||
|
||||
#endif
|
@ -1,107 +0,0 @@
|
||||
//===- CoroInternal.h - Internal Coroutine interfaces ---------*- C++ -*---===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Common definitions/declarations used internally by coroutine lowering passes.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H
|
||||
#define LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H
|
||||
|
||||
#include "CoroInstr.h"
|
||||
#include "llvm/Transforms/Coroutines.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class CallGraph;
|
||||
class CallGraphSCC;
|
||||
class PassRegistry;
|
||||
|
||||
void initializeCoroEarlyPass(PassRegistry &);
|
||||
void initializeCoroSplitPass(PassRegistry &);
|
||||
void initializeCoroElidePass(PassRegistry &);
|
||||
void initializeCoroCleanupPass(PassRegistry &);
|
||||
|
||||
// CoroEarly pass marks every function that has coro.begin with a string
|
||||
// attribute "coroutine.presplit"="0". CoroSplit pass processes the coroutine
|
||||
// twice. First, it lets it go through complete IPO optimization pipeline as a
|
||||
// single function. It forces restart of the pipeline by inserting an indirect
|
||||
// call to an empty function "coro.devirt.trigger" which is devirtualized by
|
||||
// CoroElide pass that triggers a restart of the pipeline by CGPassManager.
|
||||
// When CoroSplit pass sees the same coroutine the second time, it splits it up,
|
||||
// adds coroutine subfunctions to the SCC to be processed by IPO pipeline.
|
||||
|
||||
#define CORO_PRESPLIT_ATTR "coroutine.presplit"
|
||||
#define UNPREPARED_FOR_SPLIT "0"
|
||||
#define PREPARED_FOR_SPLIT "1"
|
||||
|
||||
#define CORO_DEVIRT_TRIGGER_FN "coro.devirt.trigger"
|
||||
|
||||
namespace coro {
|
||||
|
||||
bool declaresIntrinsics(Module &M, std::initializer_list<StringRef>);
|
||||
void replaceAllCoroAllocs(CoroBeginInst *CB, bool Replacement);
|
||||
void replaceAllCoroFrees(CoroBeginInst *CB, Value *Replacement);
|
||||
void replaceCoroFree(CoroIdInst *CoroId, bool Elide);
|
||||
void updateCallGraph(Function &Caller, ArrayRef<Function *> Funcs,
|
||||
CallGraph &CG, CallGraphSCC &SCC);
|
||||
|
||||
// Keeps data and helper functions for lowering coroutine intrinsics.
|
||||
struct LowererBase {
|
||||
Module &TheModule;
|
||||
LLVMContext &Context;
|
||||
PointerType *const Int8Ptr;
|
||||
FunctionType *const ResumeFnType;
|
||||
ConstantPointerNull *const NullPtr;
|
||||
|
||||
LowererBase(Module &M);
|
||||
Value *makeSubFnCall(Value *Arg, int Index, Instruction *InsertPt);
|
||||
};
|
||||
|
||||
// Holds structural Coroutine Intrinsics for a particular function and other
|
||||
// values used during CoroSplit pass.
|
||||
struct LLVM_LIBRARY_VISIBILITY Shape {
|
||||
CoroBeginInst *CoroBegin;
|
||||
SmallVector<CoroEndInst *, 4> CoroEnds;
|
||||
SmallVector<CoroSizeInst *, 2> CoroSizes;
|
||||
SmallVector<CoroSuspendInst *, 4> CoroSuspends;
|
||||
|
||||
// Field Indexes for known coroutine frame fields.
|
||||
enum {
|
||||
ResumeField,
|
||||
DestroyField,
|
||||
PromiseField,
|
||||
IndexField,
|
||||
LastKnownField = IndexField
|
||||
};
|
||||
|
||||
StructType *FrameTy;
|
||||
Instruction *FramePtr;
|
||||
BasicBlock *AllocaSpillBlock;
|
||||
SwitchInst *ResumeSwitch;
|
||||
AllocaInst *PromiseAlloca;
|
||||
bool HasFinalSuspend;
|
||||
|
||||
IntegerType *getIndexType() const {
|
||||
assert(FrameTy && "frame type not assigned");
|
||||
return cast<IntegerType>(FrameTy->getElementType(IndexField));
|
||||
}
|
||||
ConstantInt *getIndex(uint64_t Value) const {
|
||||
return ConstantInt::get(getIndexType(), Value);
|
||||
}
|
||||
|
||||
Shape() = default;
|
||||
explicit Shape(Function &F) { buildFrom(F); }
|
||||
void buildFrom(Function &F);
|
||||
};
|
||||
|
||||
void buildCoroutineFrame(Function &F, Shape &Shape);
|
||||
|
||||
} // End namespace coro.
|
||||
} // End namespace llvm
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,345 +0,0 @@
|
||||
//===- Coroutines.cpp -----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the common infrastructure for Coroutine Passes.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CoroInstr.h"
|
||||
#include "CoroInternal.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Analysis/CallGraph.h"
|
||||
#include "llvm/Analysis/CallGraphSCCPass.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Transforms/Coroutines.h"
|
||||
#include "llvm/Transforms/IPO.h"
|
||||
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
||||
#include "llvm/Transforms/Utils/Local.h"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
void llvm::initializeCoroutines(PassRegistry &Registry) {
|
||||
initializeCoroEarlyPass(Registry);
|
||||
initializeCoroSplitPass(Registry);
|
||||
initializeCoroElidePass(Registry);
|
||||
initializeCoroCleanupPass(Registry);
|
||||
}
|
||||
|
||||
static void addCoroutineOpt0Passes(const PassManagerBuilder &Builder,
|
||||
legacy::PassManagerBase &PM) {
|
||||
PM.add(createCoroSplitPass());
|
||||
PM.add(createCoroElidePass());
|
||||
|
||||
PM.add(createBarrierNoopPass());
|
||||
PM.add(createCoroCleanupPass());
|
||||
}
|
||||
|
||||
static void addCoroutineEarlyPasses(const PassManagerBuilder &Builder,
|
||||
legacy::PassManagerBase &PM) {
|
||||
PM.add(createCoroEarlyPass());
|
||||
}
|
||||
|
||||
static void addCoroutineScalarOptimizerPasses(const PassManagerBuilder &Builder,
|
||||
legacy::PassManagerBase &PM) {
|
||||
PM.add(createCoroElidePass());
|
||||
}
|
||||
|
||||
static void addCoroutineSCCPasses(const PassManagerBuilder &Builder,
|
||||
legacy::PassManagerBase &PM) {
|
||||
PM.add(createCoroSplitPass());
|
||||
}
|
||||
|
||||
static void addCoroutineOptimizerLastPasses(const PassManagerBuilder &Builder,
|
||||
legacy::PassManagerBase &PM) {
|
||||
PM.add(createCoroCleanupPass());
|
||||
}
|
||||
|
||||
void llvm::addCoroutinePassesToExtensionPoints(PassManagerBuilder &Builder) {
|
||||
Builder.addExtension(PassManagerBuilder::EP_EarlyAsPossible,
|
||||
addCoroutineEarlyPasses);
|
||||
Builder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
|
||||
addCoroutineOpt0Passes);
|
||||
Builder.addExtension(PassManagerBuilder::EP_CGSCCOptimizerLate,
|
||||
addCoroutineSCCPasses);
|
||||
Builder.addExtension(PassManagerBuilder::EP_ScalarOptimizerLate,
|
||||
addCoroutineScalarOptimizerPasses);
|
||||
Builder.addExtension(PassManagerBuilder::EP_OptimizerLast,
|
||||
addCoroutineOptimizerLastPasses);
|
||||
}
|
||||
|
||||
// Construct the lowerer base class and initialize its members.
|
||||
coro::LowererBase::LowererBase(Module &M)
|
||||
: TheModule(M), Context(M.getContext()),
|
||||
Int8Ptr(Type::getInt8PtrTy(Context)),
|
||||
ResumeFnType(FunctionType::get(Type::getVoidTy(Context), Int8Ptr,
|
||||
/*isVarArg=*/false)),
|
||||
NullPtr(ConstantPointerNull::get(Int8Ptr)) {}
|
||||
|
||||
// Creates a sequence of instructions to obtain a resume function address using
|
||||
// llvm.coro.subfn.addr. It generates the following sequence:
|
||||
//
|
||||
// call i8* @llvm.coro.subfn.addr(i8* %Arg, i8 %index)
|
||||
// bitcast i8* %2 to void(i8*)*
|
||||
|
||||
Value *coro::LowererBase::makeSubFnCall(Value *Arg, int Index,
|
||||
Instruction *InsertPt) {
|
||||
auto *IndexVal = ConstantInt::get(Type::getInt8Ty(Context), Index);
|
||||
auto *Fn = Intrinsic::getDeclaration(&TheModule, Intrinsic::coro_subfn_addr);
|
||||
|
||||
assert(Index >= CoroSubFnInst::IndexFirst &&
|
||||
Index < CoroSubFnInst::IndexLast &&
|
||||
"makeSubFnCall: Index value out of range");
|
||||
auto *Call = CallInst::Create(Fn, {Arg, IndexVal}, "", InsertPt);
|
||||
|
||||
auto *Bitcast =
|
||||
new BitCastInst(Call, ResumeFnType->getPointerTo(), "", InsertPt);
|
||||
return Bitcast;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
static bool isCoroutineIntrinsicName(StringRef Name) {
|
||||
// NOTE: Must be sorted!
|
||||
static const char *const CoroIntrinsics[] = {
|
||||
"llvm.coro.alloc", "llvm.coro.begin", "llvm.coro.destroy",
|
||||
"llvm.coro.done", "llvm.coro.end", "llvm.coro.frame",
|
||||
"llvm.coro.free", "llvm.coro.id", "llvm.coro.param",
|
||||
"llvm.coro.promise", "llvm.coro.resume", "llvm.coro.save",
|
||||
"llvm.coro.size", "llvm.coro.subfn.addr", "llvm.coro.suspend",
|
||||
};
|
||||
return Intrinsic::lookupLLVMIntrinsicByName(CoroIntrinsics, Name) != -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Verifies if a module has named values listed. Also, in debug mode verifies
|
||||
// that names are intrinsic names.
|
||||
bool coro::declaresIntrinsics(Module &M,
|
||||
std::initializer_list<StringRef> List) {
|
||||
for (StringRef Name : List) {
|
||||
assert(isCoroutineIntrinsicName(Name) && "not a coroutine intrinsic");
|
||||
if (M.getNamedValue(Name))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Replace all coro.frees associated with the provided CoroId either with 'null'
|
||||
// if Elide is true and with its frame parameter otherwise.
|
||||
void coro::replaceCoroFree(CoroIdInst *CoroId, bool Elide) {
|
||||
SmallVector<CoroFreeInst *, 4> CoroFrees;
|
||||
for (User *U : CoroId->users())
|
||||
if (auto CF = dyn_cast<CoroFreeInst>(U))
|
||||
CoroFrees.push_back(CF);
|
||||
|
||||
if (CoroFrees.empty())
|
||||
return;
|
||||
|
||||
Value *Replacement =
|
||||
Elide ? ConstantPointerNull::get(Type::getInt8PtrTy(CoroId->getContext()))
|
||||
: CoroFrees.front()->getFrame();
|
||||
|
||||
for (CoroFreeInst *CF : CoroFrees) {
|
||||
CF->replaceAllUsesWith(Replacement);
|
||||
CF->eraseFromParent();
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This code is stolen from CallGraph::addToCallGraph(Function *F), which
|
||||
// happens to be private. It is better for this functionality exposed by the
|
||||
// CallGraph.
|
||||
static void buildCGN(CallGraph &CG, CallGraphNode *Node) {
|
||||
Function *F = Node->getFunction();
|
||||
|
||||
// Look for calls by this function.
|
||||
for (Instruction &I : instructions(F))
|
||||
if (CallSite CS = CallSite(cast<Value>(&I))) {
|
||||
const Function *Callee = CS.getCalledFunction();
|
||||
if (!Callee || !Intrinsic::isLeaf(Callee->getIntrinsicID()))
|
||||
// Indirect calls of intrinsics are not allowed so no need to check.
|
||||
// We can be more precise here by using TargetArg returned by
|
||||
// Intrinsic::isLeaf.
|
||||
Node->addCalledFunction(CS, CG.getCallsExternalNode());
|
||||
else if (!Callee->isIntrinsic())
|
||||
Node->addCalledFunction(CS, CG.getOrInsertFunction(Callee));
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild CGN after we extracted parts of the code from ParentFunc into
|
||||
// NewFuncs. Builds CGNs for the NewFuncs and adds them to the current SCC.
|
||||
void coro::updateCallGraph(Function &ParentFunc, ArrayRef<Function *> NewFuncs,
|
||||
CallGraph &CG, CallGraphSCC &SCC) {
|
||||
// Rebuild CGN from scratch for the ParentFunc
|
||||
auto *ParentNode = CG[&ParentFunc];
|
||||
ParentNode->removeAllCalledFunctions();
|
||||
buildCGN(CG, ParentNode);
|
||||
|
||||
SmallVector<CallGraphNode *, 8> Nodes(SCC.begin(), SCC.end());
|
||||
|
||||
for (Function *F : NewFuncs) {
|
||||
CallGraphNode *Callee = CG.getOrInsertFunction(F);
|
||||
Nodes.push_back(Callee);
|
||||
buildCGN(CG, Callee);
|
||||
}
|
||||
|
||||
SCC.initialize(Nodes);
|
||||
}
|
||||
|
||||
static void clear(coro::Shape &Shape) {
|
||||
Shape.CoroBegin = nullptr;
|
||||
Shape.CoroEnds.clear();
|
||||
Shape.CoroSizes.clear();
|
||||
Shape.CoroSuspends.clear();
|
||||
|
||||
Shape.FrameTy = nullptr;
|
||||
Shape.FramePtr = nullptr;
|
||||
Shape.AllocaSpillBlock = nullptr;
|
||||
Shape.ResumeSwitch = nullptr;
|
||||
Shape.PromiseAlloca = nullptr;
|
||||
Shape.HasFinalSuspend = false;
|
||||
}
|
||||
|
||||
static CoroSaveInst *createCoroSave(CoroBeginInst *CoroBegin,
|
||||
CoroSuspendInst *SuspendInst) {
|
||||
Module *M = SuspendInst->getModule();
|
||||
auto *Fn = Intrinsic::getDeclaration(M, Intrinsic::coro_save);
|
||||
auto *SaveInst =
|
||||
cast<CoroSaveInst>(CallInst::Create(Fn, CoroBegin, "", SuspendInst));
|
||||
assert(!SuspendInst->getCoroSave());
|
||||
SuspendInst->setArgOperand(0, SaveInst);
|
||||
return SaveInst;
|
||||
}
|
||||
|
||||
// Collect "interesting" coroutine intrinsics.
|
||||
void coro::Shape::buildFrom(Function &F) {
|
||||
size_t FinalSuspendIndex = 0;
|
||||
clear(*this);
|
||||
SmallVector<CoroFrameInst *, 8> CoroFrames;
|
||||
SmallVector<CoroSaveInst *, 2> UnusedCoroSaves;
|
||||
|
||||
for (Instruction &I : instructions(F)) {
|
||||
if (auto II = dyn_cast<IntrinsicInst>(&I)) {
|
||||
switch (II->getIntrinsicID()) {
|
||||
default:
|
||||
continue;
|
||||
case Intrinsic::coro_size:
|
||||
CoroSizes.push_back(cast<CoroSizeInst>(II));
|
||||
break;
|
||||
case Intrinsic::coro_frame:
|
||||
CoroFrames.push_back(cast<CoroFrameInst>(II));
|
||||
break;
|
||||
case Intrinsic::coro_save:
|
||||
// After optimizations, coro_suspends using this coro_save might have
|
||||
// been removed, remember orphaned coro_saves to remove them later.
|
||||
if (II->use_empty())
|
||||
UnusedCoroSaves.push_back(cast<CoroSaveInst>(II));
|
||||
break;
|
||||
case Intrinsic::coro_suspend:
|
||||
CoroSuspends.push_back(cast<CoroSuspendInst>(II));
|
||||
if (CoroSuspends.back()->isFinal()) {
|
||||
if (HasFinalSuspend)
|
||||
report_fatal_error(
|
||||
"Only one suspend point can be marked as final");
|
||||
HasFinalSuspend = true;
|
||||
FinalSuspendIndex = CoroSuspends.size() - 1;
|
||||
}
|
||||
break;
|
||||
case Intrinsic::coro_begin: {
|
||||
auto CB = cast<CoroBeginInst>(II);
|
||||
if (CB->getId()->getInfo().isPreSplit()) {
|
||||
if (CoroBegin)
|
||||
report_fatal_error(
|
||||
"coroutine should have exactly one defining @llvm.coro.begin");
|
||||
CB->addAttribute(AttributeList::ReturnIndex, Attribute::NonNull);
|
||||
CB->addAttribute(AttributeList::ReturnIndex, Attribute::NoAlias);
|
||||
CB->removeAttribute(AttributeList::FunctionIndex,
|
||||
Attribute::NoDuplicate);
|
||||
CoroBegin = CB;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Intrinsic::coro_end:
|
||||
CoroEnds.push_back(cast<CoroEndInst>(II));
|
||||
if (CoroEnds.back()->isFallthrough()) {
|
||||
// Make sure that the fallthrough coro.end is the first element in the
|
||||
// CoroEnds vector.
|
||||
if (CoroEnds.size() > 1) {
|
||||
if (CoroEnds.front()->isFallthrough())
|
||||
report_fatal_error(
|
||||
"Only one coro.end can be marked as fallthrough");
|
||||
std::swap(CoroEnds.front(), CoroEnds.back());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If for some reason, we were not able to find coro.begin, bailout.
|
||||
if (!CoroBegin) {
|
||||
// Replace coro.frame which are supposed to be lowered to the result of
|
||||
// coro.begin with undef.
|
||||
auto *Undef = UndefValue::get(Type::getInt8PtrTy(F.getContext()));
|
||||
for (CoroFrameInst *CF : CoroFrames) {
|
||||
CF->replaceAllUsesWith(Undef);
|
||||
CF->eraseFromParent();
|
||||
}
|
||||
|
||||
// Replace all coro.suspend with undef and remove related coro.saves if
|
||||
// present.
|
||||
for (CoroSuspendInst *CS : CoroSuspends) {
|
||||
CS->replaceAllUsesWith(UndefValue::get(CS->getType()));
|
||||
CS->eraseFromParent();
|
||||
if (auto *CoroSave = CS->getCoroSave())
|
||||
CoroSave->eraseFromParent();
|
||||
}
|
||||
|
||||
// Replace all coro.ends with unreachable instruction.
|
||||
for (CoroEndInst *CE : CoroEnds)
|
||||
changeToUnreachable(CE, /*UseLLVMTrap=*/false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The coro.free intrinsic is always lowered to the result of coro.begin.
|
||||
for (CoroFrameInst *CF : CoroFrames) {
|
||||
CF->replaceAllUsesWith(CoroBegin);
|
||||
CF->eraseFromParent();
|
||||
}
|
||||
|
||||
// Canonicalize coro.suspend by inserting a coro.save if needed.
|
||||
for (CoroSuspendInst *CS : CoroSuspends)
|
||||
if (!CS->getCoroSave())
|
||||
createCoroSave(CoroBegin, CS);
|
||||
|
||||
// Move final suspend to be the last element in the CoroSuspends vector.
|
||||
if (HasFinalSuspend &&
|
||||
FinalSuspendIndex != CoroSuspends.size() - 1)
|
||||
std::swap(CoroSuspends[FinalSuspendIndex], CoroSuspends.back());
|
||||
|
||||
// Remove orphaned coro.saves.
|
||||
for (CoroSaveInst *CoroSave : UnusedCoroSaves)
|
||||
CoroSave->eraseFromParent();
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
;===- ./lib/Transforms/Coroutines/LLVMBuild.txt ----------------*- Conf -*--===;
|
||||
;
|
||||
; The LLVM Compiler Infrastructure
|
||||
;
|
||||
; This file is distributed under the University of Illinois Open Source
|
||||
; License. See LICENSE.TXT for details.
|
||||
;
|
||||
;===------------------------------------------------------------------------===;
|
||||
;
|
||||
; This is an LLVMBuild description file for the components in this subdirectory.
|
||||
;
|
||||
; For more information on the LLVMBuild system, please see:
|
||||
;
|
||||
; http://llvm.org/docs/LLVMBuild.html
|
||||
;
|
||||
;===------------------------------------------------------------------------===;
|
||||
|
||||
[component_0]
|
||||
type = Library
|
||||
name = Coroutines
|
||||
parent = Transforms
|
||||
required_libraries = Analysis Core IPO Scalar Support TransformUtils
|
@ -1,20 +0,0 @@
|
||||
# If we don't need RTTI or EH, there's no reason to export anything
|
||||
# from the hello plugin.
|
||||
if( NOT LLVM_REQUIRES_RTTI )
|
||||
if( NOT LLVM_REQUIRES_EH )
|
||||
set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Hello.exports)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32 OR CYGWIN)
|
||||
set(LLVM_LINK_COMPONENTS Core Support)
|
||||
endif()
|
||||
|
||||
add_llvm_loadable_module( LLVMHello
|
||||
Hello.cpp
|
||||
|
||||
DEPENDS
|
||||
intrinsics_gen
|
||||
PLUGIN_TOOL
|
||||
opt
|
||||
)
|
65
external/llvm/lib/Transforms/Hello/Hello.cpp
vendored
65
external/llvm/lib/Transforms/Hello/Hello.cpp
vendored
@ -1,65 +0,0 @@
|
||||
//===- Hello.cpp - Example code from "Writing an LLVM Pass" ---------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements two versions of the LLVM "Hello World" pass described
|
||||
// in docs/WritingAnLLVMPass.html
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "hello"
|
||||
|
||||
STATISTIC(HelloCounter, "Counts number of functions greeted");
|
||||
|
||||
namespace {
|
||||
// Hello - The first implementation, without getAnalysisUsage.
|
||||
struct Hello : public FunctionPass {
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
Hello() : FunctionPass(ID) {}
|
||||
|
||||
bool runOnFunction(Function &F) override {
|
||||
++HelloCounter;
|
||||
errs() << "Hello: ";
|
||||
errs().write_escaped(F.getName()) << '\n';
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
char Hello::ID = 0;
|
||||
static RegisterPass<Hello> X("hello", "Hello World Pass");
|
||||
|
||||
namespace {
|
||||
// Hello2 - The second implementation with getAnalysisUsage implemented.
|
||||
struct Hello2 : public FunctionPass {
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
Hello2() : FunctionPass(ID) {}
|
||||
|
||||
bool runOnFunction(Function &F) override {
|
||||
++HelloCounter;
|
||||
errs() << "Hello: ";
|
||||
errs().write_escaped(F.getName()) << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
// We don't modify the program, so we preserve all analyses.
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.setPreservesAll();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
char Hello2::ID = 0;
|
||||
static RegisterPass<Hello2>
|
||||
Y("hello2", "Hello World Pass (with getAnalysisUsage implemented)");
|
155
external/llvm/lib/Transforms/IPO/AlwaysInliner.cpp
vendored
155
external/llvm/lib/Transforms/IPO/AlwaysInliner.cpp
vendored
@ -1,155 +0,0 @@
|
||||
//===- InlineAlways.cpp - Code to inline always_inline functions ----------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements a custom inliner that handles only functions that
|
||||
// are marked as "always inline".
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Transforms/IPO/AlwaysInliner.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/Analysis/AssumptionCache.h"
|
||||
#include "llvm/Analysis/InlineCost.h"
|
||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/CallingConv.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/Transforms/IPO.h"
|
||||
#include "llvm/Transforms/IPO/Inliner.h"
|
||||
#include "llvm/Transforms/Utils/Cloning.h"
|
||||
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "inline"
|
||||
|
||||
PreservedAnalyses AlwaysInlinerPass::run(Module &M, ModuleAnalysisManager &) {
|
||||
InlineFunctionInfo IFI;
|
||||
SmallSetVector<CallSite, 16> Calls;
|
||||
bool Changed = false;
|
||||
SmallVector<Function *, 16> InlinedFunctions;
|
||||
for (Function &F : M)
|
||||
if (!F.isDeclaration() && F.hasFnAttribute(Attribute::AlwaysInline) &&
|
||||
isInlineViable(F)) {
|
||||
Calls.clear();
|
||||
|
||||
for (User *U : F.users())
|
||||
if (auto CS = CallSite(U))
|
||||
if (CS.getCalledFunction() == &F)
|
||||
Calls.insert(CS);
|
||||
|
||||
for (CallSite CS : Calls)
|
||||
// FIXME: We really shouldn't be able to fail to inline at this point!
|
||||
// We should do something to log or check the inline failures here.
|
||||
Changed |= InlineFunction(CS, IFI);
|
||||
|
||||
// Remember to try and delete this function afterward. This both avoids
|
||||
// re-walking the rest of the module and avoids dealing with any iterator
|
||||
// invalidation issues while deleting functions.
|
||||
InlinedFunctions.push_back(&F);
|
||||
}
|
||||
|
||||
// Remove any live functions.
|
||||
erase_if(InlinedFunctions, [&](Function *F) {
|
||||
F->removeDeadConstantUsers();
|
||||
return !F->isDefTriviallyDead();
|
||||
});
|
||||
|
||||
// Delete the non-comdat ones from the module and also from our vector.
|
||||
auto NonComdatBegin = partition(
|
||||
InlinedFunctions, [&](Function *F) { return F->hasComdat(); });
|
||||
for (Function *F : make_range(NonComdatBegin, InlinedFunctions.end()))
|
||||
M.getFunctionList().erase(F);
|
||||
InlinedFunctions.erase(NonComdatBegin, InlinedFunctions.end());
|
||||
|
||||
if (!InlinedFunctions.empty()) {
|
||||
// Now we just have the comdat functions. Filter out the ones whose comdats
|
||||
// are not actually dead.
|
||||
filterDeadComdatFunctions(M, InlinedFunctions);
|
||||
// The remaining functions are actually dead.
|
||||
for (Function *F : InlinedFunctions)
|
||||
M.getFunctionList().erase(F);
|
||||
}
|
||||
|
||||
return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// Inliner pass which only handles "always inline" functions.
|
||||
///
|
||||
/// Unlike the \c AlwaysInlinerPass, this uses the more heavyweight \c Inliner
|
||||
/// base class to provide several facilities such as array alloca merging.
|
||||
class AlwaysInlinerLegacyPass : public LegacyInlinerBase {
|
||||
|
||||
public:
|
||||
AlwaysInlinerLegacyPass() : LegacyInlinerBase(ID, /*InsertLifetime*/ true) {
|
||||
initializeAlwaysInlinerLegacyPassPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
AlwaysInlinerLegacyPass(bool InsertLifetime)
|
||||
: LegacyInlinerBase(ID, InsertLifetime) {
|
||||
initializeAlwaysInlinerLegacyPassPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
/// Main run interface method. We override here to avoid calling skipSCC().
|
||||
bool runOnSCC(CallGraphSCC &SCC) override { return inlineCalls(SCC); }
|
||||
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
|
||||
InlineCost getInlineCost(CallSite CS) override;
|
||||
|
||||
using llvm::Pass::doFinalization;
|
||||
bool doFinalization(CallGraph &CG) override {
|
||||
return removeDeadFunctions(CG, /*AlwaysInlineOnly=*/true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
char AlwaysInlinerLegacyPass::ID = 0;
|
||||
INITIALIZE_PASS_BEGIN(AlwaysInlinerLegacyPass, "always-inline",
|
||||
"Inliner for always_inline functions", false, false)
|
||||
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
|
||||
INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass)
|
||||
INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass)
|
||||
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
|
||||
INITIALIZE_PASS_END(AlwaysInlinerLegacyPass, "always-inline",
|
||||
"Inliner for always_inline functions", false, false)
|
||||
|
||||
Pass *llvm::createAlwaysInlinerLegacyPass(bool InsertLifetime) {
|
||||
return new AlwaysInlinerLegacyPass(InsertLifetime);
|
||||
}
|
||||
|
||||
/// \brief Get the inline cost for the always-inliner.
|
||||
///
|
||||
/// The always inliner *only* handles functions which are marked with the
|
||||
/// attribute to force inlining. As such, it is dramatically simpler and avoids
|
||||
/// using the powerful (but expensive) inline cost analysis. Instead it uses
|
||||
/// a very simple and boring direct walk of the instructions looking for
|
||||
/// impossible-to-inline constructs.
|
||||
///
|
||||
/// Note, it would be possible to go to some lengths to cache the information
|
||||
/// computed here, but as we only expect to do this for relatively few and
|
||||
/// small functions which have the explicit attribute to force inlining, it is
|
||||
/// likely not worth it in practice.
|
||||
InlineCost AlwaysInlinerLegacyPass::getInlineCost(CallSite CS) {
|
||||
Function *Callee = CS.getCalledFunction();
|
||||
|
||||
// Only inline direct calls to functions with always-inline attributes
|
||||
// that are viable for inlining. FIXME: We shouldn't even get here for
|
||||
// declarations.
|
||||
if (Callee && !Callee->isDeclaration() &&
|
||||
CS.hasFnAttr(Attribute::AlwaysInline) && isInlineViable(*Callee))
|
||||
return InlineCost::getAlways();
|
||||
|
||||
return InlineCost::getNever();
|
||||
}
|
1100
external/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
vendored
1100
external/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
vendored
File diff suppressed because it is too large
Load Diff
@ -1,47 +0,0 @@
|
||||
//===- BarrierNoopPass.cpp - A barrier pass for the pass manager ----------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// NOTE: DO NOT USE THIS IF AVOIDABLE
|
||||
//
|
||||
// This pass is a nonce pass intended to allow manipulation of the implicitly
|
||||
// nesting pass manager. For example, it can be used to cause a CGSCC pass
|
||||
// manager to be closed prior to running a new collection of function passes.
|
||||
//
|
||||
// FIXME: This is a huge HACK. This should be removed when the pass manager's
|
||||
// nesting is made explicit instead of implicit.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Transforms/IPO.h"
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
/// \brief A nonce module pass used to place a barrier in a pass manager.
|
||||
///
|
||||
/// There is no mechanism for ending a CGSCC pass manager once one is started.
|
||||
/// This prevents extension points from having clear deterministic ordering
|
||||
/// when they are phrased as non-module passes.
|
||||
class BarrierNoop : public ModulePass {
|
||||
public:
|
||||
static char ID; // Pass identification.
|
||||
|
||||
BarrierNoop() : ModulePass(ID) {
|
||||
initializeBarrierNoopPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override { return false; }
|
||||
};
|
||||
}
|
||||
|
||||
ModulePass *llvm::createBarrierNoopPass() { return new BarrierNoop(); }
|
||||
|
||||
char BarrierNoop::ID = 0;
|
||||
INITIALIZE_PASS(BarrierNoop, "barrier", "A No-Op Barrier Pass",
|
||||
false, false)
|
41
external/llvm/lib/Transforms/IPO/CMakeLists.txt
vendored
41
external/llvm/lib/Transforms/IPO/CMakeLists.txt
vendored
@ -1,41 +0,0 @@
|
||||
add_llvm_library(LLVMipo
|
||||
AlwaysInliner.cpp
|
||||
ArgumentPromotion.cpp
|
||||
BarrierNoopPass.cpp
|
||||
CalledValuePropagation.cpp
|
||||
ConstantMerge.cpp
|
||||
CrossDSOCFI.cpp
|
||||
DeadArgumentElimination.cpp
|
||||
ElimAvailExtern.cpp
|
||||
ExtractGV.cpp
|
||||
ForceFunctionAttrs.cpp
|
||||
FunctionAttrs.cpp
|
||||
FunctionImport.cpp
|
||||
GlobalDCE.cpp
|
||||
GlobalOpt.cpp
|
||||
GlobalSplit.cpp
|
||||
IPConstantPropagation.cpp
|
||||
IPO.cpp
|
||||
InferFunctionAttrs.cpp
|
||||
InlineSimple.cpp
|
||||
Inliner.cpp
|
||||
Internalize.cpp
|
||||
LoopExtractor.cpp
|
||||
LowerTypeTests.cpp
|
||||
MergeFunctions.cpp
|
||||
PartialInlining.cpp
|
||||
PassManagerBuilder.cpp
|
||||
PruneEH.cpp
|
||||
SampleProfile.cpp
|
||||
StripDeadPrototypes.cpp
|
||||
StripSymbols.cpp
|
||||
ThinLTOBitcodeWriter.cpp
|
||||
WholeProgramDevirt.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms
|
||||
${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms/IPO
|
||||
|
||||
DEPENDS
|
||||
intrinsics_gen
|
||||
)
|
@ -1,423 +0,0 @@
|
||||
//===- CalledValuePropagation.cpp - Propagate called values -----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements a transformation that attaches !callees metadata to
|
||||
// indirect call sites. For a given call site, the metadata, if present,
|
||||
// indicates the set of functions the call site could possibly target at
|
||||
// run-time. This metadata is added to indirect call sites when the set of
|
||||
// possible targets can be determined by analysis and is known to be small. The
|
||||
// analysis driving the transformation is similar to constant propagation and
|
||||
// makes uses of the generic sparse propagation solver.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Transforms/IPO/CalledValuePropagation.h"
|
||||
#include "llvm/Analysis/SparsePropagation.h"
|
||||
#include "llvm/Analysis/ValueLatticeUtils.h"
|
||||
#include "llvm/IR/InstVisitor.h"
|
||||
#include "llvm/IR/MDBuilder.h"
|
||||
#include "llvm/Transforms/IPO.h"
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "called-value-propagation"
|
||||
|
||||
/// The maximum number of functions to track per lattice value. Once the number
|
||||
/// of functions a call site can possibly target exceeds this threshold, it's
|
||||
/// lattice value becomes overdefined. The number of possible lattice values is
|
||||
/// bounded by Ch(F, M), where F is the number of functions in the module and M
|
||||
/// is MaxFunctionsPerValue. As such, this value should be kept very small. We
|
||||
/// likely can't do anything useful for call sites with a large number of
|
||||
/// possible targets, anyway.
|
||||
static cl::opt<unsigned> MaxFunctionsPerValue(
|
||||
"cvp-max-functions-per-value", cl::Hidden, cl::init(4),
|
||||
cl::desc("The maximum number of functions to track per lattice value"));
|
||||
|
||||
namespace {
|
||||
/// To enable interprocedural analysis, we assign LLVM values to the following
|
||||
/// groups. The register group represents SSA registers, the return group
|
||||
/// represents the return values of functions, and the memory group represents
|
||||
/// in-memory values. An LLVM Value can technically be in more than one group.
|
||||
/// It's necessary to distinguish these groups so we can, for example, track a
|
||||
/// global variable separately from the value stored at its location.
|
||||
enum class IPOGrouping { Register, Return, Memory };
|
||||
|
||||
/// Our LatticeKeys are PointerIntPairs composed of LLVM values and groupings.
|
||||
using CVPLatticeKey = PointerIntPair<Value *, 2, IPOGrouping>;
|
||||
|
||||
/// The lattice value type used by our custom lattice function. It holds the
|
||||
/// lattice state, and a set of functions.
|
||||
class CVPLatticeVal {
|
||||
public:
|
||||
/// The states of the lattice values. Only the FunctionSet state is
|
||||
/// interesting. It indicates the set of functions to which an LLVM value may
|
||||
/// refer.
|
||||
enum CVPLatticeStateTy { Undefined, FunctionSet, Overdefined, Untracked };
|
||||
|
||||
/// Comparator for sorting the functions set. We want to keep the order
|
||||
/// deterministic for testing, etc.
|
||||
struct Compare {
|
||||
bool operator()(const Function *LHS, const Function *RHS) const {
|
||||
return LHS->getName() < RHS->getName();
|
||||
}
|
||||
};
|
||||
|
||||
CVPLatticeVal() : LatticeState(Undefined) {}
|
||||
CVPLatticeVal(CVPLatticeStateTy LatticeState) : LatticeState(LatticeState) {}
|
||||
CVPLatticeVal(std::set<Function *, Compare> &&Functions)
|
||||
: LatticeState(FunctionSet), Functions(Functions) {}
|
||||
|
||||
/// Get a reference to the functions held by this lattice value. The number
|
||||
/// of functions will be zero for states other than FunctionSet.
|
||||
const std::set<Function *, Compare> &getFunctions() const {
|
||||
return Functions;
|
||||
}
|
||||
|
||||
/// Returns true if the lattice value is in the FunctionSet state.
|
||||
bool isFunctionSet() const { return LatticeState == FunctionSet; }
|
||||
|
||||
bool operator==(const CVPLatticeVal &RHS) const {
|
||||
return LatticeState == RHS.LatticeState && Functions == RHS.Functions;
|
||||
}
|
||||
|
||||
bool operator!=(const CVPLatticeVal &RHS) const {
|
||||
return LatticeState != RHS.LatticeState || Functions != RHS.Functions;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Holds the state this lattice value is in.
|
||||
CVPLatticeStateTy LatticeState;
|
||||
|
||||
/// Holds functions indicating the possible targets of call sites. This set
|
||||
/// is empty for lattice values in the undefined, overdefined, and untracked
|
||||
/// states. The maximum size of the set is controlled by
|
||||
/// MaxFunctionsPerValue. Since most LLVM values are expected to be in
|
||||
/// uninteresting states (i.e., overdefined), CVPLatticeVal objects should be
|
||||
/// small and efficiently copyable.
|
||||
std::set<Function *, Compare> Functions;
|
||||
};
|
||||
|
||||
/// The custom lattice function used by the generic sparse propagation solver.
|
||||
/// It handles merging lattice values and computing new lattice values for
|
||||
/// constants, arguments, values returned from trackable functions, and values
|
||||
/// located in trackable global variables. It also computes the lattice values
|
||||
/// that change as a result of executing instructions.
|
||||
class CVPLatticeFunc
|
||||
: public AbstractLatticeFunction<CVPLatticeKey, CVPLatticeVal> {
|
||||
public:
|
||||
CVPLatticeFunc()
|
||||
: AbstractLatticeFunction(CVPLatticeVal(CVPLatticeVal::Undefined),
|
||||
CVPLatticeVal(CVPLatticeVal::Overdefined),
|
||||
CVPLatticeVal(CVPLatticeVal::Untracked)) {}
|
||||
|
||||
/// Compute and return a CVPLatticeVal for the given CVPLatticeKey.
|
||||
CVPLatticeVal ComputeLatticeVal(CVPLatticeKey Key) override {
|
||||
switch (Key.getInt()) {
|
||||
case IPOGrouping::Register:
|
||||
if (isa<Instruction>(Key.getPointer())) {
|
||||
return getUndefVal();
|
||||
} else if (auto *A = dyn_cast<Argument>(Key.getPointer())) {
|
||||
if (canTrackArgumentsInterprocedurally(A->getParent()))
|
||||
return getUndefVal();
|
||||
} else if (auto *C = dyn_cast<Constant>(Key.getPointer())) {
|
||||
return computeConstant(C);
|
||||
}
|
||||
return getOverdefinedVal();
|
||||
case IPOGrouping::Memory:
|
||||
case IPOGrouping::Return:
|
||||
if (auto *GV = dyn_cast<GlobalVariable>(Key.getPointer())) {
|
||||
if (canTrackGlobalVariableInterprocedurally(GV))
|
||||
return computeConstant(GV->getInitializer());
|
||||
} else if (auto *F = cast<Function>(Key.getPointer()))
|
||||
if (canTrackReturnsInterprocedurally(F))
|
||||
return getUndefVal();
|
||||
}
|
||||
return getOverdefinedVal();
|
||||
}
|
||||
|
||||
/// Merge the two given lattice values. The interesting cases are merging two
|
||||
/// FunctionSet values and a FunctionSet value with an Undefined value. For
|
||||
/// these cases, we simply union the function sets. If the size of the union
|
||||
/// is greater than the maximum functions we track, the merged value is
|
||||
/// overdefined.
|
||||
CVPLatticeVal MergeValues(CVPLatticeVal X, CVPLatticeVal Y) override {
|
||||
if (X == getOverdefinedVal() || Y == getOverdefinedVal())
|
||||
return getOverdefinedVal();
|
||||
if (X == getUndefVal() && Y == getUndefVal())
|
||||
return getUndefVal();
|
||||
std::set<Function *, CVPLatticeVal::Compare> Union;
|
||||
std::set_union(X.getFunctions().begin(), X.getFunctions().end(),
|
||||
Y.getFunctions().begin(), Y.getFunctions().end(),
|
||||
std::inserter(Union, Union.begin()),
|
||||
CVPLatticeVal::Compare{});
|
||||
if (Union.size() > MaxFunctionsPerValue)
|
||||
return getOverdefinedVal();
|
||||
return CVPLatticeVal(std::move(Union));
|
||||
}
|
||||
|
||||
/// Compute the lattice values that change as a result of executing the given
|
||||
/// instruction. The changed values are stored in \p ChangedValues. We handle
|
||||
/// just a few kinds of instructions since we're only propagating values that
|
||||
/// can be called.
|
||||
void ComputeInstructionState(
|
||||
Instruction &I, DenseMap<CVPLatticeKey, CVPLatticeVal> &ChangedValues,
|
||||
SparseSolver<CVPLatticeKey, CVPLatticeVal> &SS) override {
|
||||
switch (I.getOpcode()) {
|
||||
case Instruction::Call:
|
||||
return visitCallSite(cast<CallInst>(&I), ChangedValues, SS);
|
||||
case Instruction::Invoke:
|
||||
return visitCallSite(cast<InvokeInst>(&I), ChangedValues, SS);
|
||||
case Instruction::Load:
|
||||
return visitLoad(*cast<LoadInst>(&I), ChangedValues, SS);
|
||||
case Instruction::Ret:
|
||||
return visitReturn(*cast<ReturnInst>(&I), ChangedValues, SS);
|
||||
case Instruction::Select:
|
||||
return visitSelect(*cast<SelectInst>(&I), ChangedValues, SS);
|
||||
case Instruction::Store:
|
||||
return visitStore(*cast<StoreInst>(&I), ChangedValues, SS);
|
||||
default:
|
||||
return visitInst(I, ChangedValues, SS);
|
||||
}
|
||||
}
|
||||
|
||||
/// Print the given CVPLatticeVal to the specified stream.
|
||||
void PrintLatticeVal(CVPLatticeVal LV, raw_ostream &OS) override {
|
||||
if (LV == getUndefVal())
|
||||
OS << "Undefined ";
|
||||
else if (LV == getOverdefinedVal())
|
||||
OS << "Overdefined";
|
||||
else if (LV == getUntrackedVal())
|
||||
OS << "Untracked ";
|
||||
else
|
||||
OS << "FunctionSet";
|
||||
}
|
||||
|
||||
/// Print the given CVPLatticeKey to the specified stream.
|
||||
void PrintLatticeKey(CVPLatticeKey Key, raw_ostream &OS) override {
|
||||
if (Key.getInt() == IPOGrouping::Register)
|
||||
OS << "<reg> ";
|
||||
else if (Key.getInt() == IPOGrouping::Memory)
|
||||
OS << "<mem> ";
|
||||
else if (Key.getInt() == IPOGrouping::Return)
|
||||
OS << "<ret> ";
|
||||
if (isa<Function>(Key.getPointer()))
|
||||
OS << Key.getPointer()->getName();
|
||||
else
|
||||
OS << *Key.getPointer();
|
||||
}
|
||||
|
||||
/// We collect a set of indirect calls when visiting call sites. This method
|
||||
/// returns a reference to that set.
|
||||
SmallPtrSetImpl<Instruction *> &getIndirectCalls() { return IndirectCalls; }
|
||||
|
||||
private:
|
||||
/// Holds the indirect calls we encounter during the analysis. We will attach
|
||||
/// metadata to these calls after the analysis indicating the functions the
|
||||
/// calls can possibly target.
|
||||
SmallPtrSet<Instruction *, 32> IndirectCalls;
|
||||
|
||||
/// Compute a new lattice value for the given constant. The constant, after
|
||||
/// stripping any pointer casts, should be a Function. We ignore null
|
||||
/// pointers as an optimization, since calling these values is undefined
|
||||
/// behavior.
|
||||
CVPLatticeVal computeConstant(Constant *C) {
|
||||
if (isa<ConstantPointerNull>(C))
|
||||
return CVPLatticeVal(CVPLatticeVal::FunctionSet);
|
||||
if (auto *F = dyn_cast<Function>(C->stripPointerCasts()))
|
||||
return CVPLatticeVal({F});
|
||||
return getOverdefinedVal();
|
||||
}
|
||||
|
||||
/// Handle return instructions. The function's return state is the merge of
|
||||
/// the returned value state and the function's return state.
|
||||
void visitReturn(ReturnInst &I,
|
||||
DenseMap<CVPLatticeKey, CVPLatticeVal> &ChangedValues,
|
||||
SparseSolver<CVPLatticeKey, CVPLatticeVal> &SS) {
|
||||
Function *F = I.getParent()->getParent();
|
||||
if (F->getReturnType()->isVoidTy())
|
||||
return;
|
||||
auto RegI = CVPLatticeKey(I.getReturnValue(), IPOGrouping::Register);
|
||||
auto RetF = CVPLatticeKey(F, IPOGrouping::Return);
|
||||
ChangedValues[RetF] =
|
||||
MergeValues(SS.getValueState(RegI), SS.getValueState(RetF));
|
||||
}
|
||||
|
||||
/// Handle call sites. The state of a called function's formal arguments is
|
||||
/// the merge of the argument state with the call sites corresponding actual
|
||||
/// argument state. The call site state is the merge of the call site state
|
||||
/// with the returned value state of the called function.
|
||||
void visitCallSite(CallSite CS,
|
||||
DenseMap<CVPLatticeKey, CVPLatticeVal> &ChangedValues,
|
||||
SparseSolver<CVPLatticeKey, CVPLatticeVal> &SS) {
|
||||
Function *F = CS.getCalledFunction();
|
||||
Instruction *I = CS.getInstruction();
|
||||
auto RegI = CVPLatticeKey(I, IPOGrouping::Register);
|
||||
|
||||
// If this is an indirect call, save it so we can quickly revisit it when
|
||||
// attaching metadata.
|
||||
if (!F)
|
||||
IndirectCalls.insert(I);
|
||||
|
||||
// If we can't track the function's return values, there's nothing to do.
|
||||
if (!F || !canTrackReturnsInterprocedurally(F)) {
|
||||
ChangedValues[RegI] = getOverdefinedVal();
|
||||
return;
|
||||
}
|
||||
|
||||
// Inform the solver that the called function is executable, and perform
|
||||
// the merges for the arguments and return value.
|
||||
SS.MarkBlockExecutable(&F->front());
|
||||
auto RetF = CVPLatticeKey(F, IPOGrouping::Return);
|
||||
for (Argument &A : F->args()) {
|
||||
auto RegFormal = CVPLatticeKey(&A, IPOGrouping::Register);
|
||||
auto RegActual =
|
||||
CVPLatticeKey(CS.getArgument(A.getArgNo()), IPOGrouping::Register);
|
||||
ChangedValues[RegFormal] =
|
||||
MergeValues(SS.getValueState(RegFormal), SS.getValueState(RegActual));
|
||||
}
|
||||
ChangedValues[RegI] =
|
||||
MergeValues(SS.getValueState(RegI), SS.getValueState(RetF));
|
||||
}
|
||||
|
||||
/// Handle select instructions. The select instruction state is the merge the
|
||||
/// true and false value states.
|
||||
void visitSelect(SelectInst &I,
|
||||
DenseMap<CVPLatticeKey, CVPLatticeVal> &ChangedValues,
|
||||
SparseSolver<CVPLatticeKey, CVPLatticeVal> &SS) {
|
||||
auto RegI = CVPLatticeKey(&I, IPOGrouping::Register);
|
||||
auto RegT = CVPLatticeKey(I.getTrueValue(), IPOGrouping::Register);
|
||||
auto RegF = CVPLatticeKey(I.getFalseValue(), IPOGrouping::Register);
|
||||
ChangedValues[RegI] =
|
||||
MergeValues(SS.getValueState(RegT), SS.getValueState(RegF));
|
||||
}
|
||||
|
||||
/// Handle load instructions. If the pointer operand of the load is a global
|
||||
/// variable, we attempt to track the value. The loaded value state is the
|
||||
/// merge of the loaded value state with the global variable state.
|
||||
void visitLoad(LoadInst &I,
|
||||
DenseMap<CVPLatticeKey, CVPLatticeVal> &ChangedValues,
|
||||
SparseSolver<CVPLatticeKey, CVPLatticeVal> &SS) {
|
||||
auto RegI = CVPLatticeKey(&I, IPOGrouping::Register);
|
||||
if (auto *GV = dyn_cast<GlobalVariable>(I.getPointerOperand())) {
|
||||
auto MemGV = CVPLatticeKey(GV, IPOGrouping::Memory);
|
||||
ChangedValues[RegI] =
|
||||
MergeValues(SS.getValueState(RegI), SS.getValueState(MemGV));
|
||||
} else {
|
||||
ChangedValues[RegI] = getOverdefinedVal();
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle store instructions. If the pointer operand of the store is a
|
||||
/// global variable, we attempt to track the value. The global variable state
|
||||
/// is the merge of the stored value state with the global variable state.
|
||||
void visitStore(StoreInst &I,
|
||||
DenseMap<CVPLatticeKey, CVPLatticeVal> &ChangedValues,
|
||||
SparseSolver<CVPLatticeKey, CVPLatticeVal> &SS) {
|
||||
auto *GV = dyn_cast<GlobalVariable>(I.getPointerOperand());
|
||||
if (!GV)
|
||||
return;
|
||||
auto RegI = CVPLatticeKey(I.getValueOperand(), IPOGrouping::Register);
|
||||
auto MemGV = CVPLatticeKey(GV, IPOGrouping::Memory);
|
||||
ChangedValues[MemGV] =
|
||||
MergeValues(SS.getValueState(RegI), SS.getValueState(MemGV));
|
||||
}
|
||||
|
||||
/// Handle all other instructions. All other instructions are marked
|
||||
/// overdefined.
|
||||
void visitInst(Instruction &I,
|
||||
DenseMap<CVPLatticeKey, CVPLatticeVal> &ChangedValues,
|
||||
SparseSolver<CVPLatticeKey, CVPLatticeVal> &SS) {
|
||||
auto RegI = CVPLatticeKey(&I, IPOGrouping::Register);
|
||||
ChangedValues[RegI] = getOverdefinedVal();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace llvm {
|
||||
/// A specialization of LatticeKeyInfo for CVPLatticeKeys. The generic solver
|
||||
/// must translate between LatticeKeys and LLVM Values when adding Values to
|
||||
/// its work list and inspecting the state of control-flow related values.
|
||||
template <> struct LatticeKeyInfo<CVPLatticeKey> {
|
||||
static inline Value *getValueFromLatticeKey(CVPLatticeKey Key) {
|
||||
return Key.getPointer();
|
||||
}
|
||||
static inline CVPLatticeKey getLatticeKeyFromValue(Value *V) {
|
||||
return CVPLatticeKey(V, IPOGrouping::Register);
|
||||
}
|
||||
};
|
||||
} // namespace llvm
|
||||
|
||||
static bool runCVP(Module &M) {
|
||||
// Our custom lattice function and generic sparse propagation solver.
|
||||
CVPLatticeFunc Lattice;
|
||||
SparseSolver<CVPLatticeKey, CVPLatticeVal> Solver(&Lattice);
|
||||
|
||||
// For each function in the module, if we can't track its arguments, let the
|
||||
// generic solver assume it is executable.
|
||||
for (Function &F : M)
|
||||
if (!F.isDeclaration() && !canTrackArgumentsInterprocedurally(&F))
|
||||
Solver.MarkBlockExecutable(&F.front());
|
||||
|
||||
// Solver our custom lattice. In doing so, we will also build a set of
|
||||
// indirect call sites.
|
||||
Solver.Solve();
|
||||
|
||||
// Attach metadata to the indirect call sites that were collected indicating
|
||||
// the set of functions they can possibly target.
|
||||
bool Changed = false;
|
||||
MDBuilder MDB(M.getContext());
|
||||
for (Instruction *C : Lattice.getIndirectCalls()) {
|
||||
CallSite CS(C);
|
||||
auto RegI = CVPLatticeKey(CS.getCalledValue(), IPOGrouping::Register);
|
||||
CVPLatticeVal LV = Solver.getExistingValueState(RegI);
|
||||
if (!LV.isFunctionSet() || LV.getFunctions().empty())
|
||||
continue;
|
||||
MDNode *Callees = MDB.createCallees(SmallVector<Function *, 4>(
|
||||
LV.getFunctions().begin(), LV.getFunctions().end()));
|
||||
C->setMetadata(LLVMContext::MD_callees, Callees);
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
PreservedAnalyses CalledValuePropagationPass::run(Module &M,
|
||||
ModuleAnalysisManager &) {
|
||||
runCVP(M);
|
||||
return PreservedAnalyses::all();
|
||||
}
|
||||
|
||||
namespace {
|
||||
class CalledValuePropagationLegacyPass : public ModulePass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.setPreservesAll();
|
||||
}
|
||||
|
||||
CalledValuePropagationLegacyPass() : ModulePass(ID) {
|
||||
initializeCalledValuePropagationLegacyPassPass(
|
||||
*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override {
|
||||
if (skipModule(M))
|
||||
return false;
|
||||
return runCVP(M);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
char CalledValuePropagationLegacyPass::ID = 0;
|
||||
INITIALIZE_PASS(CalledValuePropagationLegacyPass, "called-value-propagation",
|
||||
"Called Value Propagation", false, false)
|
||||
|
||||
ModulePass *llvm::createCalledValuePropagationPass() {
|
||||
return new CalledValuePropagationLegacyPass();
|
||||
}
|
260
external/llvm/lib/Transforms/IPO/ConstantMerge.cpp
vendored
260
external/llvm/lib/Transforms/IPO/ConstantMerge.cpp
vendored
@ -1,260 +0,0 @@
|
||||
//===- ConstantMerge.cpp - Merge duplicate global constants ---------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the interface to a pass that merges duplicate global
|
||||
// constants together into a single constant that is shared. This is useful
|
||||
// because some passes (ie TraceValues) insert a lot of string constants into
|
||||
// the program, regardless of whether or not an existing string is available.
|
||||
//
|
||||
// Algorithm: ConstantMerge is designed to build up a map of available constants
|
||||
// and eliminate duplicates when it is initialized.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Transforms/IPO/ConstantMerge.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
#include "llvm/IR/GlobalValue.h"
|
||||
#include "llvm/IR/GlobalVariable.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Transforms/IPO.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "constmerge"
|
||||
|
||||
STATISTIC(NumMerged, "Number of global constants merged");
|
||||
|
||||
/// Find values that are marked as llvm.used.
|
||||
static void FindUsedValues(GlobalVariable *LLVMUsed,
|
||||
SmallPtrSetImpl<const GlobalValue*> &UsedValues) {
|
||||
if (!LLVMUsed) return;
|
||||
ConstantArray *Inits = cast<ConstantArray>(LLVMUsed->getInitializer());
|
||||
|
||||
for (unsigned i = 0, e = Inits->getNumOperands(); i != e; ++i) {
|
||||
Value *Operand = Inits->getOperand(i)->stripPointerCastsNoFollowAliases();
|
||||
GlobalValue *GV = cast<GlobalValue>(Operand);
|
||||
UsedValues.insert(GV);
|
||||
}
|
||||
}
|
||||
|
||||
// True if A is better than B.
|
||||
static bool IsBetterCanonical(const GlobalVariable &A,
|
||||
const GlobalVariable &B) {
|
||||
if (!A.hasLocalLinkage() && B.hasLocalLinkage())
|
||||
return true;
|
||||
|
||||
if (A.hasLocalLinkage() && !B.hasLocalLinkage())
|
||||
return false;
|
||||
|
||||
return A.hasGlobalUnnamedAddr();
|
||||
}
|
||||
|
||||
static bool hasMetadataOtherThanDebugLoc(const GlobalVariable *GV) {
|
||||
SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
|
||||
GV->getAllMetadata(MDs);
|
||||
for (const auto &V : MDs)
|
||||
if (V.first != LLVMContext::MD_dbg)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void copyDebugLocMetadata(const GlobalVariable *From,
|
||||
GlobalVariable *To) {
|
||||
SmallVector<DIGlobalVariableExpression *, 1> MDs;
|
||||
From->getDebugInfo(MDs);
|
||||
for (auto MD : MDs)
|
||||
To->addDebugInfo(MD);
|
||||
}
|
||||
|
||||
static unsigned getAlignment(GlobalVariable *GV) {
|
||||
unsigned Align = GV->getAlignment();
|
||||
if (Align)
|
||||
return Align;
|
||||
return GV->getParent()->getDataLayout().getPreferredAlignment(GV);
|
||||
}
|
||||
|
||||
static bool mergeConstants(Module &M) {
|
||||
// Find all the globals that are marked "used". These cannot be merged.
|
||||
SmallPtrSet<const GlobalValue*, 8> UsedGlobals;
|
||||
FindUsedValues(M.getGlobalVariable("llvm.used"), UsedGlobals);
|
||||
FindUsedValues(M.getGlobalVariable("llvm.compiler.used"), UsedGlobals);
|
||||
|
||||
// Map unique constants to globals.
|
||||
DenseMap<Constant *, GlobalVariable *> CMap;
|
||||
|
||||
// Replacements - This vector contains a list of replacements to perform.
|
||||
SmallVector<std::pair<GlobalVariable*, GlobalVariable*>, 32> Replacements;
|
||||
|
||||
bool MadeChange = false;
|
||||
|
||||
// Iterate constant merging while we are still making progress. Merging two
|
||||
// constants together may allow us to merge other constants together if the
|
||||
// second level constants have initializers which point to the globals that
|
||||
// were just merged.
|
||||
while (true) {
|
||||
// First: Find the canonical constants others will be merged with.
|
||||
for (Module::global_iterator GVI = M.global_begin(), E = M.global_end();
|
||||
GVI != E; ) {
|
||||
GlobalVariable *GV = &*GVI++;
|
||||
|
||||
// If this GV is dead, remove it.
|
||||
GV->removeDeadConstantUsers();
|
||||
if (GV->use_empty() && GV->hasLocalLinkage()) {
|
||||
GV->eraseFromParent();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only process constants with initializers in the default address space.
|
||||
if (!GV->isConstant() || !GV->hasDefinitiveInitializer() ||
|
||||
GV->getType()->getAddressSpace() != 0 || GV->hasSection() ||
|
||||
// Don't touch values marked with attribute(used).
|
||||
UsedGlobals.count(GV))
|
||||
continue;
|
||||
|
||||
// This transformation is legal for weak ODR globals in the sense it
|
||||
// doesn't change semantics, but we really don't want to perform it
|
||||
// anyway; it's likely to pessimize code generation, and some tools
|
||||
// (like the Darwin linker in cases involving CFString) don't expect it.
|
||||
if (GV->isWeakForLinker())
|
||||
continue;
|
||||
|
||||
// Don't touch globals with metadata other then !dbg.
|
||||
if (hasMetadataOtherThanDebugLoc(GV))
|
||||
continue;
|
||||
|
||||
Constant *Init = GV->getInitializer();
|
||||
|
||||
// Check to see if the initializer is already known.
|
||||
GlobalVariable *&Slot = CMap[Init];
|
||||
|
||||
// If this is the first constant we find or if the old one is local,
|
||||
// replace with the current one. If the current is externally visible
|
||||
// it cannot be replace, but can be the canonical constant we merge with.
|
||||
if (!Slot || IsBetterCanonical(*GV, *Slot))
|
||||
Slot = GV;
|
||||
}
|
||||
|
||||
// Second: identify all globals that can be merged together, filling in
|
||||
// the Replacements vector. We cannot do the replacement in this pass
|
||||
// because doing so may cause initializers of other globals to be rewritten,
|
||||
// invalidating the Constant* pointers in CMap.
|
||||
for (Module::global_iterator GVI = M.global_begin(), E = M.global_end();
|
||||
GVI != E; ) {
|
||||
GlobalVariable *GV = &*GVI++;
|
||||
|
||||
// Only process constants with initializers in the default address space.
|
||||
if (!GV->isConstant() || !GV->hasDefinitiveInitializer() ||
|
||||
GV->getType()->getAddressSpace() != 0 || GV->hasSection() ||
|
||||
// Don't touch values marked with attribute(used).
|
||||
UsedGlobals.count(GV))
|
||||
continue;
|
||||
|
||||
// We can only replace constant with local linkage.
|
||||
if (!GV->hasLocalLinkage())
|
||||
continue;
|
||||
|
||||
Constant *Init = GV->getInitializer();
|
||||
|
||||
// Check to see if the initializer is already known.
|
||||
GlobalVariable *Slot = CMap[Init];
|
||||
|
||||
if (!Slot || Slot == GV)
|
||||
continue;
|
||||
|
||||
if (!Slot->hasGlobalUnnamedAddr() && !GV->hasGlobalUnnamedAddr())
|
||||
continue;
|
||||
|
||||
if (hasMetadataOtherThanDebugLoc(GV))
|
||||
continue;
|
||||
|
||||
if (!GV->hasGlobalUnnamedAddr())
|
||||
Slot->setUnnamedAddr(GlobalValue::UnnamedAddr::None);
|
||||
|
||||
// Make all uses of the duplicate constant use the canonical version.
|
||||
Replacements.push_back(std::make_pair(GV, Slot));
|
||||
}
|
||||
|
||||
if (Replacements.empty())
|
||||
return MadeChange;
|
||||
CMap.clear();
|
||||
|
||||
// Now that we have figured out which replacements must be made, do them all
|
||||
// now. This avoid invalidating the pointers in CMap, which are unneeded
|
||||
// now.
|
||||
for (unsigned i = 0, e = Replacements.size(); i != e; ++i) {
|
||||
// Bump the alignment if necessary.
|
||||
if (Replacements[i].first->getAlignment() ||
|
||||
Replacements[i].second->getAlignment()) {
|
||||
Replacements[i].second->setAlignment(
|
||||
std::max(getAlignment(Replacements[i].first),
|
||||
getAlignment(Replacements[i].second)));
|
||||
}
|
||||
|
||||
copyDebugLocMetadata(Replacements[i].first, Replacements[i].second);
|
||||
|
||||
// Eliminate any uses of the dead global.
|
||||
Replacements[i].first->replaceAllUsesWith(Replacements[i].second);
|
||||
|
||||
// Delete the global value from the module.
|
||||
assert(Replacements[i].first->hasLocalLinkage() &&
|
||||
"Refusing to delete an externally visible global variable.");
|
||||
Replacements[i].first->eraseFromParent();
|
||||
}
|
||||
|
||||
NumMerged += Replacements.size();
|
||||
Replacements.clear();
|
||||
}
|
||||
}
|
||||
|
||||
PreservedAnalyses ConstantMergePass::run(Module &M, ModuleAnalysisManager &) {
|
||||
if (!mergeConstants(M))
|
||||
return PreservedAnalyses::all();
|
||||
return PreservedAnalyses::none();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct ConstantMergeLegacyPass : public ModulePass {
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
|
||||
ConstantMergeLegacyPass() : ModulePass(ID) {
|
||||
initializeConstantMergeLegacyPassPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
// For this pass, process all of the globals in the module, eliminating
|
||||
// duplicate constants.
|
||||
bool runOnModule(Module &M) override {
|
||||
if (skipModule(M))
|
||||
return false;
|
||||
return mergeConstants(M);
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
char ConstantMergeLegacyPass::ID = 0;
|
||||
|
||||
INITIALIZE_PASS(ConstantMergeLegacyPass, "constmerge",
|
||||
"Merge Duplicate Global Constants", false, false)
|
||||
|
||||
ModulePass *llvm::createConstantMergePass() {
|
||||
return new ConstantMergeLegacyPass();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user