blob: 395e6a896ba06932448812a6049fa1392c9aa24e [file] [log] [blame] [edit]
//===- SimplifyStructRegSignatures.cpp - struct regs to struct pointers----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass replaces function signatures exposing struct registers
// to byval pointer-based signatures.
//
// There are 2 types of signatures that are thus changed:
//
// @foo(%some_struct %val) -> @foo(%some_struct* byval %val)
// and
// %someStruct @bar(<other_args>) -> void @bar(%someStruct* sret, <other_args>)
//
// Such function types may appear in other type declarations, for example:
//
// %a_struct = type { void (%some_struct)*, i32 }
//
// We map such types to corresponding types, mapping the function types
// appropriately:
//
// %a_struct.0 = type { void (%some_struct*)*, i32 }
//===----------------------------------------------------------------------===//
#include "SimplifiedFuncTypeMap.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/ilist.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Use.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/Pass.h"
#include "llvm/PassInfo.h"
#include "llvm/PassRegistry.h"
#include "llvm/PassSupport.h"
#include "llvm/Transforms/NaCl.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstddef>
using namespace llvm;
namespace {
static const unsigned int TypicalFuncArity = 8;
static bool shouldPromote(const Type *Ty) {
return Ty->isAggregateType();
}
// Utility class. For any given type, get the associated type that is free of
// struct register arguments.
class TypeMapper : public SimplifiedFuncTypeMap {
protected:
MappingResult getSimpleFuncType(LLVMContext &Ctx, StructMap &Tentatives,
FunctionType *OldFnTy) override {
Type *OldRetType = OldFnTy->getReturnType();
Type *NewRetType = OldRetType;
Type *Void = Type::getVoidTy(Ctx);
ParamTypeVector NewArgs;
bool Changed = false;
// Struct register returns become the first parameter of the new FT.
// The new FT has void for the return type
if (shouldPromote(OldRetType)) {
NewRetType = Void;
Changed = true;
NewArgs.push_back(getSimpleArgumentType(Ctx, OldRetType, Tentatives));
}
for (auto OldParam : OldFnTy->params()) {
auto NewType = getSimpleArgumentType(Ctx, OldParam, Tentatives);
Changed |= NewType.isChanged();
NewArgs.push_back(NewType);
}
Type *NewFuncType =
FunctionType::get(NewRetType, NewArgs, OldFnTy->isVarArg());
return {NewFuncType, Changed};
}
private:
// Get the simplified type of a function argument.
MappingResult getSimpleArgumentType(LLVMContext &Ctx, Type *Ty,
StructMap &Tentatives) {
// struct registers become pointers to simple structs
if (shouldPromote(Ty)) {
return {PointerType::get(
getSimpleAggregateTypeInternal(Ctx, Ty, Tentatives), 0),
true};
}
return getSimpleAggregateTypeInternal(Ctx, Ty, Tentatives);
}
};
// This is a ModulePass because the pass recreates functions in
// order to change their signatures.
class SimplifyStructRegSignatures : public ModulePass {
public:
static char ID;
SimplifyStructRegSignatures() : ModulePass(ID) {
initializeSimplifyStructRegSignaturesPass(*PassRegistry::getPassRegistry());
}
virtual bool runOnModule(Module &M);
private:
TypeMapper Mapper;
DenseSet<Function *> FunctionsToDelete;
SetVector<CallInst *> CallsToPatch;
SetVector<InvokeInst *> InvokesToPatch;
DenseMap<Function *, Function *> FunctionMap;
struct FunctionAddressing {
Value *Temp;
Function *Old;
FunctionAddressing(Value *Temp, Function *Old) : Temp(Temp), Old(Old) {}
};
std::vector<FunctionAddressing> FunctionAddressings;
bool
simplifyFunction(LLVMContext &Ctx, Function *OldFunc);
void scheduleInstructionsForCleanup(Function *NewFunc);
template <class TCall>
void fixCallSite(LLVMContext &Ctx, TCall *Call, unsigned PreferredAlignment);
void fixFunctionBody(LLVMContext &Ctx, Function *OldFunc, Function *NewFunc);
template <class TCall>
TCall *fixCallTargetAndArguments(LLVMContext &Ctx, IRBuilder<> &Builder,
TCall *OldCall, Value *NewTarget,
FunctionType *NewType,
BasicBlock::iterator AllocaInsPoint,
Value *ExtraArg = nullptr);
void checkNoUnsupportedInstructions(LLVMContext &Ctx, Function *Fct);
};
}
char SimplifyStructRegSignatures::ID = 0;
INITIALIZE_PASS(
SimplifyStructRegSignatures, "simplify-struct-reg-signatures",
"Simplify function signatures by removing struct register parameters",
false, false)
// Update the arg names for a newly created function.
static void UpdateArgNames(Function *OldFunc, Function *NewFunc) {
auto NewArgIter = NewFunc->arg_begin();
if (shouldPromote(OldFunc->getReturnType())) {
NewArgIter->setName("retVal");
NewArgIter++;
}
for (const Argument &OldArg : OldFunc->args()) {
Argument *NewArg = &*NewArgIter++;
NewArg->setName(OldArg.getName() +
(shouldPromote(OldArg.getType()) ? ".ptr" : ""));
}
}
// Replace all uses of an old value with a new one, disregarding the type. We
// correct the types after we wire the new parameters in, in fixFunctionBody.
static void BlindReplace(Value *Old, Value *New) {
for (auto UseIter = Old->use_begin(), E = Old->use_end(); E != UseIter;) {
Use &AUse = *(UseIter++);
AUse.set(New);
}
}
// Adapt the body of a function for the new arguments.
static void ConvertArgumentValue(Value *Old, Value *New, Instruction *InsPoint,
const bool IsAggregateToPtr) {
if (Old == New)
return;
if (Old->getType() == New->getType()) {
Old->replaceAllUsesWith(New);
New->takeName(Old);
return;
}
BlindReplace(Old, (IsAggregateToPtr
? new LoadInst(New, Old->getName() + ".sreg", InsPoint)
: New));
}
// Fix returns. Return true if fixes were needed.
static void FixReturn(Function *OldFunc, Function *NewFunc) {
Argument *FirstNewArg = &*NewFunc->arg_begin();
for (auto BIter = NewFunc->begin(), LastBlock = NewFunc->end();
LastBlock != BIter;) {
BasicBlock *BB = &*BIter++;
for (auto IIter = BB->begin(), LastI = BB->end(); LastI != IIter;) {
Instruction *Instr = &*IIter++;
if (ReturnInst *Ret = dyn_cast<ReturnInst>(Instr)) {
auto RetVal = Ret->getReturnValue();
IRBuilder<> Builder(Ret);
StoreInst *Store = Builder.CreateStore(RetVal, FirstNewArg);
Store->setAlignment(FirstNewArg->getParamAlignment());
Builder.CreateRetVoid();
Ret->eraseFromParent();
}
}
}
}
/// In the next two functions, `RetIndex` is the index of the possibly promoted
/// return.
/// Ie if the return is promoted, `RetIndex` should be `1`, else `0`.
static AttributeList CopyRetAttributes(LLVMContext &C, const DataLayout &DL,
const AttributeList From, Type *RetTy,
const unsigned RetIndex) {
AttributeList NewAttrs;
if (RetIndex != 0) {
NewAttrs = NewAttrs.addAttribute(C, RetIndex, Attribute::StructRet);
NewAttrs = NewAttrs.addAttribute(C, RetIndex, Attribute::NonNull);
NewAttrs = NewAttrs.addAttribute(C, RetIndex, Attribute::NoCapture);
if (RetTy->isSized()) {
NewAttrs = NewAttrs.addDereferenceableAttr(C, RetIndex,
DL.getTypeAllocSize(RetTy));
}
} else {
NewAttrs = NewAttrs.addAttributes(C, RetIndex, From.getRetAttributes());
}
auto FnAttrs = From.getFnAttributes();
if (RetIndex != 0) {
FnAttrs = FnAttrs.removeAttribute(C, Attribute::ReadOnly);
FnAttrs = FnAttrs.removeAttribute(C, Attribute::ReadNone);
}
NewAttrs = NewAttrs.addAttributes(C, AttributeList::FunctionIndex, FnAttrs);
return NewAttrs;
}
/// Iff the argument in question was promoted, `NewArgTy` should be non-null.
static AttributeList CopyArgAttributes(AttributeList NewAttrs, LLVMContext &C,
const DataLayout &DL,
const AttributeList From,
const unsigned OldArg, Type *NewArgTy,
const unsigned RetIndex) {
const unsigned NewIndex = RetIndex + OldArg + 1;
if (!NewArgTy) {
AttributeSet OldAttrs = From.getParamAttributes(OldArg);
if (OldAttrs.getNumAttributes() == 0) {
return NewAttrs;
}
// move the params to the new index position:
AttrBuilder B(AttributeList(), NewIndex);
for (Attribute A : OldAttrs) {
B.addAttribute(A);
}
NewAttrs = NewAttrs.addAttributes(C, NewIndex, B);
return NewAttrs;
} else {
NewAttrs = NewAttrs.addAttribute(C, NewIndex, Attribute::NonNull);
NewAttrs = NewAttrs.addAttribute(C, NewIndex, Attribute::NoCapture);
NewAttrs = NewAttrs.addAttribute(C, NewIndex, Attribute::ReadOnly);
if (NewArgTy->isSized()) {
NewAttrs = NewAttrs.addDereferenceableAttr(C, NewIndex,
DL.getTypeAllocSize(NewArgTy));
}
return NewAttrs;
}
}
// TODO (mtrofin): is this comprehensive?
template <class TCall>
void CopyCallAttributesAndMetadata(TCall *Orig, TCall *NewCall) {
NewCall->setCallingConv(Orig->getCallingConv());
NewCall->setAttributes(NewCall->getAttributes().addAttributes(
Orig->getContext(), AttributeList::FunctionIndex,
Orig->getAttributes().getFnAttributes()));
NewCall->takeName(Orig);
}
static InvokeInst *CreateCallFrom(InvokeInst *Orig, Value *Target,
ArrayRef<Value *> &Args,
IRBuilder<> &Builder) {
auto Ret = Builder.CreateInvoke(Target, Orig->getNormalDest(),
Orig->getUnwindDest(), Args);
CopyCallAttributesAndMetadata(Orig, Ret);
return Ret;
}
static CallInst *CreateCallFrom(CallInst *Orig, Value *Target,
ArrayRef<Value *> &Args, IRBuilder<> &Builder) {
CallInst *Ret = Builder.CreateCall(Target, Args);
Ret->setTailCallKind(Orig->getTailCallKind());
CopyCallAttributesAndMetadata(Orig, Ret);
return Ret;
}
// Insert Alloca at a specified location (normally, beginning of function)
// to avoid memory leaks if reason for inserting the Alloca
// (typically a call/invoke) is in a loop.
static AllocaInst *InsertAllocaAtLocation(IRBuilder<> &Builder,
BasicBlock::iterator &AllocaInsPoint,
Type *ValType) {
auto SavedInsPoint = Builder.GetInsertPoint();
Builder.SetInsertPoint(&*AllocaInsPoint);
auto *Alloca = Builder.CreateAlloca(ValType);
AllocaInsPoint = Builder.GetInsertPoint();
Builder.SetInsertPoint(&*SavedInsPoint);
return Alloca;
}
// Fix a call site by handing return type changes and/or parameter type and
// attribute changes.
template <class TCall>
void SimplifyStructRegSignatures::fixCallSite(LLVMContext &Ctx, TCall *OldCall,
unsigned PreferredAlignment) {
Value *NewTarget = OldCall->getCalledValue();
bool IsTargetFunction = false;
if (Function *CalledFunc = dyn_cast<Function>(NewTarget)) {
NewTarget = this->FunctionMap[CalledFunc];
IsTargetFunction = true;
}
assert(NewTarget);
auto *NewType = cast<FunctionType>(
Mapper.getSimpleType(Ctx, NewTarget->getType())->getPointerElementType());
IRBuilder<> Builder(OldCall);
if (!IsTargetFunction) {
NewTarget = Builder.CreateBitCast(NewTarget, NewType->getPointerTo());
}
auto *OldRetType = OldCall->getType();
const bool IsSRet =
!OldCall->getType()->isVoidTy() && NewType->getReturnType()->isVoidTy();
auto AllocaInsPoint =
OldCall->getParent()->getParent()->getEntryBlock().getFirstInsertionPt();
if (IsSRet) {
auto *Alloca = InsertAllocaAtLocation(Builder, AllocaInsPoint, OldRetType);
Alloca->takeName(OldCall);
Alloca->setAlignment(PreferredAlignment);
auto *NewCall = fixCallTargetAndArguments(Ctx, Builder, OldCall, NewTarget,
NewType, AllocaInsPoint, Alloca);
assert(NewCall);
if (auto *Invoke = dyn_cast<InvokeInst>(OldCall))
Builder.SetInsertPoint(&*Invoke->getNormalDest()->getFirstInsertionPt());
auto *Load = Builder.CreateLoad(Alloca, Alloca->getName() + ".sreg");
Load->setAlignment(Alloca->getAlignment());
OldCall->replaceAllUsesWith(Load);
} else {
auto *NewCall = fixCallTargetAndArguments(Ctx, Builder, OldCall, NewTarget,
NewType, AllocaInsPoint);
OldCall->replaceAllUsesWith(NewCall);
}
OldCall->eraseFromParent();
}
template <class TCall>
TCall *SimplifyStructRegSignatures::fixCallTargetAndArguments(
LLVMContext &Ctx, IRBuilder<> &Builder, TCall *OldCall, Value *NewTarget,
FunctionType *NewType, BasicBlock::iterator AllocaInsPoint,
Value *ExtraArg) {
SmallVector<Value *, TypicalFuncArity> NewArgs;
const DataLayout &DL = OldCall->getParent() // BB
->getParent() // F
->getParent() // M
->getDataLayout();
const AttributeList OldSet = OldCall->getAttributes();
unsigned argOffset = ExtraArg ? 1 : 0;
const unsigned RetSlot = AttributeList::ReturnIndex + argOffset;
if (ExtraArg)
NewArgs.push_back(ExtraArg);
AttributeList NewSet =
CopyRetAttributes(Ctx, DL, OldSet, OldCall->getType(), RetSlot);
// Go over the argument list used in the call/invoke, in order to
// correctly deal with varargs scenarios.
unsigned NumActualParams = OldCall->getNumArgOperands();
unsigned VarargMark = NewType->getNumParams();
for (unsigned ArgPos = 0; ArgPos < NumActualParams; ArgPos++) {
Use &OldArgUse = OldCall->getOperandUse(ArgPos);
Value *OldArg = OldArgUse;
Type *OldArgType = OldArg->getType();
unsigned NewArgPos = OldArgUse.getOperandNo() + argOffset;
Type *NewArgType = NewArgPos < VarargMark ? NewType->getFunctionParamType(NewArgPos) : nullptr;
Type *InnerNewArgType = nullptr;
if (OldArgType != NewArgType && shouldPromote(OldArgType)) {
if (NewArgPos >= VarargMark) {
errs() << *OldCall << '\n';
report_fatal_error("Aggregate register vararg is not supported");
}
auto *Alloca =
InsertAllocaAtLocation(Builder, AllocaInsPoint, OldArgType);
Alloca->setName(OldArg->getName() + ".ptr");
Builder.CreateStore(OldArg, Alloca);
NewArgs.push_back(Alloca);
InnerNewArgType = NewArgType->getPointerElementType();
} else if (NewArgType && OldArgType != NewArgType && isa<Function>(OldArg)) {
// If a function pointer has a changed type due to struct reg changes, it will still have
// the wrong type here, since we may have not changed that method yet. We'll fix it up
// later, and meanwhile place an undef of the right type in that slot.
Value *Temp = UndefValue::get(NewArgType);
FunctionAddressings.emplace_back(Temp, cast<Function>(OldArg));
NewArgs.push_back(Temp);
} else if (NewArgType && OldArgType != NewArgType && OldArgType->isPointerTy()) {
// This would be a function ptr or would have a function type nested in
// it.
NewArgs.push_back(Builder.CreatePointerCast(OldArg, NewArgType));
} else {
NewArgs.push_back(OldArg);
}
NewSet = CopyArgAttributes(NewSet, Ctx, DL, OldSet, ArgPos, InnerNewArgType,
RetSlot);
}
if (isa<Instruction>(NewTarget)) {
Type* NewPointerType = PointerType::get(NewType, 0);
if (NewPointerType != OldCall->getType()) {
// This is a function pointer, and it has the wrong type after our
// changes. Bitcast it.
NewTarget = Builder.CreateBitCast(NewTarget, NewPointerType, ".casttarget");
}
}
ArrayRef<Value *> ArrRef = NewArgs;
TCall *NewCall = CreateCallFrom(OldCall, NewTarget, ArrRef, Builder);
NewCall->setAttributes(NewSet);
return NewCall;
}
void
SimplifyStructRegSignatures::scheduleInstructionsForCleanup(Function *NewFunc) {
for (auto &BBIter : NewFunc->getBasicBlockList()) {
for (auto &IIter : BBIter.getInstList()) {
if (CallInst *Call = dyn_cast<CallInst>(&IIter)) {
if (Function *F = dyn_cast<Function>(Call->getCalledValue())) {
if (F->isIntrinsic()) {
continue;
}
}
CallsToPatch.insert(Call);
} else if (InvokeInst *Invoke = dyn_cast<InvokeInst>(&IIter)) {
InvokesToPatch.insert(Invoke);
}
}
}
}
// Change function body in the light of type changes.
void SimplifyStructRegSignatures::fixFunctionBody(LLVMContext &Ctx,
Function *OldFunc,
Function *NewFunc) {
const DataLayout &DL = OldFunc->getParent()->getDataLayout();
bool returnWasFixed = shouldPromote(OldFunc->getReturnType());
const AttributeList OldSet = OldFunc->getAttributes();
const unsigned RetSlot = AttributeList::ReturnIndex + (returnWasFixed ? 1 : 0);
AttributeList NewSet =
CopyRetAttributes(Ctx, DL, OldSet, OldFunc->getReturnType(), RetSlot);
Instruction *InsPoint = &*NewFunc->begin()->begin();
auto NewArgIter = NewFunc->arg_begin();
// Advance one more if we used to return a struct register.
if (returnWasFixed)
NewArgIter++;
// Wire new parameters in.
unsigned ArgIndex = 0;
for (auto ArgIter = OldFunc->arg_begin(), E = OldFunc->arg_end();
E != ArgIter; ArgIndex++) {
Argument *OldArg = &*ArgIter++;
Argument *NewArg = &*NewArgIter++;
const bool IsAggregateToPtr =
shouldPromote(OldArg->getType()) && NewArg->getType()->isPointerTy();
if (!NewFunc->empty()) {
ConvertArgumentValue(OldArg, NewArg, InsPoint, IsAggregateToPtr);
}
Type *Inner = nullptr;
if (IsAggregateToPtr) {
Inner = NewArg->getType()->getPointerElementType();
}
NewSet =
CopyArgAttributes(NewSet, Ctx, DL, OldSet, ArgIndex, Inner, RetSlot);
}
NewFunc->setAttributes(NewSet);
// Now fix instruction types. We know that each value could only possibly be
// of a simplified type. At the end of this, call sites will be invalid, but
// we handle that afterwards, to make sure we have all the functions changed
// first (so that calls have valid targets)
for (auto BBIter = NewFunc->begin(), LBlock = NewFunc->end();
LBlock != BBIter;) {
auto Block = BBIter++;
for (auto IIter = Block->begin(), LIns = Block->end(); LIns != IIter;) {
auto Instr = IIter++;
auto *NewTy = Mapper.getSimpleType(Ctx, Instr->getType());
Instr->mutateType(NewTy);
if (isa<CallInst>(Instr) ||
isa<InvokeInst>(Instr)) {
continue;
}
for (unsigned OpI = 0; OpI < Instr->getNumOperands(); OpI++) {
if(Constant *C = dyn_cast<Constant>(Instr->getOperand(OpI))) {
auto *NewTy = Mapper.getSimpleType(Ctx, C->getType());
if (NewTy == C->getType()) { continue; }
const auto CastOp = CastInst::getCastOpcode(C, false, NewTy, false);
auto *NewOp = ConstantExpr::getCast(CastOp, C, NewTy);
Instr->setOperand(OpI, NewOp);
}
}
}
}
if (returnWasFixed)
FixReturn(OldFunc, NewFunc);
}
// Ensure function is simplified, returning true if the function
// had to be changed.
bool SimplifyStructRegSignatures::simplifyFunction(
LLVMContext &Ctx, Function *OldFunc) {
auto *OldFT = OldFunc->getFunctionType();
auto *NewFT = cast<FunctionType>(Mapper.getSimpleType(Ctx, OldFT));
Function *&AssociatedFctLoc = FunctionMap[OldFunc];
if (NewFT != OldFT) {
auto *NewFunc = Function::Create(NewFT, OldFunc->getLinkage());
AssociatedFctLoc = NewFunc;
OldFunc->getParent()->getFunctionList().insert(OldFunc->getIterator(), NewFunc);
NewFunc->takeName(OldFunc);
UpdateArgNames(OldFunc, NewFunc);
NewFunc->getBasicBlockList().splice(NewFunc->begin(),
OldFunc->getBasicBlockList());
fixFunctionBody(Ctx, OldFunc, NewFunc);
Constant *Cast = ConstantExpr::getPointerCast(NewFunc, OldFunc->getType());
OldFunc->replaceAllUsesWith(Cast);
FunctionsToDelete.insert(OldFunc);
} else {
AssociatedFctLoc = OldFunc;
}
scheduleInstructionsForCleanup(AssociatedFctLoc);
return NewFT != OldFT;
}
bool SimplifyStructRegSignatures::runOnModule(Module &M) {
bool Changed = false;
unsigned PreferredAlignment = 0;
PreferredAlignment = M.getDataLayout().getStackAlignment();
LLVMContext &Ctx = M.getContext();
// Change function signatures and fix a changed function body by
// wiring the new arguments. Call sites are unchanged at this point.
for (Module::iterator Iter = M.begin(), E = M.end(); Iter != E;) {
Function *Func = &*Iter++;
if (Func->isIntrinsic()) {
// Can't rewrite intrinsics.
continue;
}
checkNoUnsupportedInstructions(Ctx, Func);
Changed |= simplifyFunction(Ctx, Func);
}
// Fix call sites.
for (auto &CallToFix : CallsToPatch) {
fixCallSite(Ctx, CallToFix, PreferredAlignment);
}
for (auto &InvokeToFix : InvokesToPatch) {
fixCallSite(Ctx, InvokeToFix, PreferredAlignment);
}
// Update taking of a function's address from a parameter
for (auto &Addressing : FunctionAddressings) {
Value *Temp = Addressing.Temp;
Function *Old = Addressing.Old;
Function *New = FunctionMap[Old];
assert(New);
Temp->replaceAllUsesWith(New);
}
// Remaining uses of functions we modified (like in a global vtable)
// can be handled via a constantexpr bitcast
for (auto &Old : FunctionsToDelete) {
Function *New = FunctionMap[Old];
assert(New);
Old->replaceAllUsesWith(ConstantExpr::getBitCast(New, Old->getType()));
}
// Delete leftover functions - the ones with old signatures.
for (auto &ToDelete : FunctionsToDelete) {
ToDelete->eraseFromParent();
}
return Changed;
}
void
SimplifyStructRegSignatures::checkNoUnsupportedInstructions(LLVMContext &Ctx,
Function *Fct) {
for (auto &BB : Fct->getBasicBlockList()) {
for (auto &Inst : BB.getInstList()) {
if (auto *Landing = dyn_cast<LandingPadInst>(&Inst)) {
auto *LType = Fct->getPersonalityFn()->getType();
if (LType != Mapper.getSimpleType(Ctx, LType)) {
errs() << *Landing << '\n';
report_fatal_error("Landing pads with aggregate register "
"signatures are not supported.");
}
} else if (auto *Resume = dyn_cast<ResumeInst>(&Inst)) {
auto *RType = Resume->getValue()->getType();
if (RType != Mapper.getSimpleType(Ctx, RType)) {
errs() << *Resume << '\n';
report_fatal_error(
"Resumes with aggregate register signatures are not supported.");
}
}
}
}
}
ModulePass *llvm::createSimplifyStructRegSignaturesPass() {
return new SimplifyStructRegSignatures();
}