diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h index 31a1fef321995..847e628814f1b 100644 --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -2239,6 +2239,13 @@ class IRBuilderBase { // isSigned parameter. Value *CreateIntCast(Value *, Type *, const char *) = delete; + /// Cast between aggregate types that must have identical structure but may + /// differ in their leaf types. The leaf values are recursively extracted, + /// casted, and then reinserted into a value of type DestTy. The leaf types + /// must be castable using a bitcast or ptrcast, because signedness is + /// not specified. + Value *CreateAggregateCast(Value *V, Type *DestTy); + //===--------------------------------------------------------------------===// // Instruction creation methods: Compare Instructions //===--------------------------------------------------------------------===// diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp index e5cde875ab1d8..acdd99c487fa5 100644 --- a/llvm/lib/IR/IRBuilder.cpp +++ b/llvm/lib/IR/IRBuilder.cpp @@ -76,6 +76,40 @@ void IRBuilderBase::SetInstDebugLocation(Instruction *I) const { } } +Value *IRBuilderBase::CreateAggregateCast(Value *V, Type *DestTy) { + Type *SrcTy = V->getType(); + if (SrcTy == DestTy) + return V; + + if (SrcTy->isAggregateType()) { + unsigned NumElements; + if (SrcTy->isStructTy()) { + assert(DestTy->isStructTy() && "Expected StructType"); + assert(SrcTy->getStructNumElements() == DestTy->getStructNumElements() && + "Expected StructTypes with equal number of elements"); + NumElements = SrcTy->getStructNumElements(); + } else { + assert(SrcTy->isArrayTy() && DestTy->isArrayTy() && "Expected ArrayType"); + assert(SrcTy->getArrayNumElements() == DestTy->getArrayNumElements() && + "Expected ArrayTypes with equal number of elements"); + NumElements = SrcTy->getArrayNumElements(); + } + + Value *Result = PoisonValue::get(DestTy); + for (unsigned I = 0; I < NumElements; ++I) { + Type *ElementTy = SrcTy->isStructTy() ? DestTy->getStructElementType(I) + : DestTy->getArrayElementType(); + Value *Element = + CreateAggregateCast(CreateExtractValue(V, ArrayRef(I)), ElementTy); + + Result = CreateInsertValue(Result, Element, ArrayRef(I)); + } + return Result; + } + + return CreateBitOrPointerCast(V, DestTy); +} + CallInst * IRBuilderBase::createCallHelper(Function *Callee, ArrayRef Ops, const Twine &Name, Instruction *FMFSource, diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp index df47c6ea06d02..c24dd7d5dd500 100644 --- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp +++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp @@ -483,33 +483,6 @@ void MergeFunctions::replaceDirectCallers(Function *Old, Function *New) { } } -// Helper for writeThunk, -// Selects proper bitcast operation, -// but a bit simpler then CastInst::getCastOpcode. -static Value *createCast(IRBuilder<> &Builder, Value *V, Type *DestTy) { - Type *SrcTy = V->getType(); - if (SrcTy->isStructTy()) { - assert(DestTy->isStructTy()); - assert(SrcTy->getStructNumElements() == DestTy->getStructNumElements()); - Value *Result = PoisonValue::get(DestTy); - for (unsigned int I = 0, E = SrcTy->getStructNumElements(); I < E; ++I) { - Value *Element = - createCast(Builder, Builder.CreateExtractValue(V, ArrayRef(I)), - DestTy->getStructElementType(I)); - - Result = Builder.CreateInsertValue(Result, Element, ArrayRef(I)); - } - return Result; - } - assert(!DestTy->isStructTy()); - if (SrcTy->isIntegerTy() && DestTy->isPointerTy()) - return Builder.CreateIntToPtr(V, DestTy); - else if (SrcTy->isPointerTy() && DestTy->isIntegerTy()) - return Builder.CreatePtrToInt(V, DestTy); - else - return Builder.CreateBitCast(V, DestTy); -} - // Erase the instructions in PDIUnrelatedWL as they are unrelated to the // parameter debug info, from the entry block. void MergeFunctions::eraseInstsUnrelatedToPDI( @@ -761,7 +734,7 @@ void MergeFunctions::writeThunk(Function *F, Function *G) { unsigned i = 0; FunctionType *FFTy = F->getFunctionType(); for (Argument &AI : H->args()) { - Args.push_back(createCast(Builder, &AI, FFTy->getParamType(i))); + Args.push_back(Builder.CreateAggregateCast(&AI, FFTy->getParamType(i))); ++i; } @@ -776,7 +749,7 @@ void MergeFunctions::writeThunk(Function *F, Function *G) { if (H->getReturnType()->isVoidTy()) { RI = Builder.CreateRetVoid(); } else { - RI = Builder.CreateRet(createCast(Builder, CI, H->getReturnType())); + RI = Builder.CreateRet(Builder.CreateAggregateCast(CI, H->getReturnType())); } if (MergeFunctionsPDI) { diff --git a/llvm/test/Transforms/MergeFunc/crash-cast-arrays.ll b/llvm/test/Transforms/MergeFunc/crash-cast-arrays.ll new file mode 100644 index 0000000000000..6a18feba1263a --- /dev/null +++ b/llvm/test/Transforms/MergeFunc/crash-cast-arrays.ll @@ -0,0 +1,76 @@ +; RUN: opt -S -passes=mergefunc < %s | FileCheck %s + +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" + +%A = type { double } +; the intermediary struct causes A_arr and B_arr to be different types +%A_struct = type { %A } +%A_arr = type { [1 x %A_struct] } + +%B = type { double } +%B_struct = type { %B } +%B_arr = type { [1 x %B_struct] } + +; conversion between C_arr and D_arr is possible, but requires ptrcast +%C = type { i64 } +%C_struct = type { %C } +%C_arr = type { [1 x %C_struct] } + +%D = type { ptr } +%D_struct = type { %D } +%D_arr = type { [1 x %D_struct] } + +declare void @noop() + +define %A_arr @a() { +; CHECK-LABEL: define %A_arr @a() { +; CHECK-NEXT: call void @noop() +; CHECK-NEXT: ret %A_arr zeroinitializer +; + call void @noop() + ret %A_arr zeroinitializer +} + +define %C_arr @c() { +; CHECK-LABEL: define %C_arr @c() { +; CHECK-NEXT: call void @noop() +; CHECK-NEXT: ret %C_arr zeroinitializer +; + call void @noop() + ret %C_arr zeroinitializer +} + +define %B_arr @b() { +; CHECK-LABEL: define %B_arr @b() { +; CHECK-NEXT: [[TMP1:%.*]] = tail call %A_arr @a +; CHECK-NEXT: [[TMP2:%.*]] = extractvalue %A_arr [[TMP1]], 0 +; CHECK-NEXT: [[TMP3:%.*]] = extractvalue [1 x %A_struct] [[TMP2]], 0 +; CHECK-NEXT: [[TMP4:%.*]] = extractvalue %A_struct [[TMP3]], 0 +; CHECK-NEXT: [[TMP5:%.*]] = extractvalue %A [[TMP4]], 0 +; CHECK-NEXT: [[TMP6:%.*]] = insertvalue %B poison, double [[TMP5]], 0 +; CHECK-NEXT: [[TMP7:%.*]] = insertvalue %B_struct poison, %B [[TMP6]], 0 +; CHECK-NEXT: [[TMP8:%.*]] = insertvalue [1 x %B_struct] poison, %B_struct [[TMP7]], 0 +; CHECK-NEXT: [[TMP9:%.*]] = insertvalue %B_arr poison, [1 x %B_struct] [[TMP8]], 0 +; CHECK-NEXT: ret %B_arr [[TMP9]] +; + call void @noop() + ret %B_arr zeroinitializer +} + +define %D_arr @d() { +; CHECK-LABEL: define %D_arr @d() { +; CHECK-NEXT: [[TMP1:%.*]] = tail call %C_arr @c +; CHECK-NEXT: [[TMP2:%.*]] = extractvalue %C_arr [[TMP1]], 0 +; CHECK-NEXT: [[TMP3:%.*]] = extractvalue [1 x %C_struct] [[TMP2]], 0 +; CHECK-NEXT: [[TMP4:%.*]] = extractvalue %C_struct [[TMP3]], 0 +; CHECK-NEXT: [[TMP5:%.*]] = extractvalue %C [[TMP4]], 0 +; CHECK-NEXT: [[TMP10:%.*]] = inttoptr i64 [[TMP5]] to ptr +; CHECK-NEXT: [[TMP6:%.*]] = insertvalue %D poison, ptr [[TMP10]], 0 +; CHECK-NEXT: [[TMP7:%.*]] = insertvalue %D_struct poison, %D [[TMP6]], 0 +; CHECK-NEXT: [[TMP8:%.*]] = insertvalue [1 x %D_struct] poison, %D_struct [[TMP7]], 0 +; CHECK-NEXT: [[TMP9:%.*]] = insertvalue %D_arr poison, [1 x %D_struct] [[TMP8]], 0 +; CHECK-NEXT: ret %D_arr [[TMP9]] +; + call void @noop() + ret %D_arr zeroinitializer +}