Index: llvm/include/llvm/Transforms/Scalar/SROA.h =================================================================== --- llvm/include/llvm/Transforms/Scalar/SROA.h +++ llvm/include/llvm/Transforms/Scalar/SROA.h @@ -108,6 +108,18 @@ /// currently in the promotable queue. SetVector> SpeculatableSelects; + /// Describes the allocas introduced by rewritePartition in order to migrate + /// the debug info. + struct Fragment { + AllocaInst *Alloca; + uint64_t Offset; + uint64_t Size; + Fragment(AllocaInst *AI, uint64_t O, uint64_t S) + : Alloca(AI), Offset(O), Size(S) {} + }; + + SmallVector Fragments; + public: SROA() = default; @@ -125,6 +137,8 @@ bool presplitLoadsAndStores(AllocaInst &AI, sroa::AllocaSlices &AS); AllocaInst *rewritePartition(AllocaInst &AI, sroa::AllocaSlices &AS, sroa::Partition &P); + void salvageInstruction(Instruction *Ins); + void fixGEPWithSplitAllocas(); bool splitAlloca(AllocaInst &AI, sroa::AllocaSlices &AS); bool runOnAlloca(AllocaInst &AI); void clobberUse(Use &U); Index: llvm/lib/IR/Metadata.cpp =================================================================== --- llvm/lib/IR/Metadata.cpp +++ llvm/lib/IR/Metadata.cpp @@ -392,7 +392,6 @@ assert(From && "Expected valid value"); assert(To && "Expected valid value"); assert(From != To && "Expected changed value"); - assert(From->getType() == To->getType() && "Unexpected type change"); LLVMContext &Context = From->getType()->getContext(); auto &Store = Context.pImpl->ValuesAsMetadata; Index: llvm/lib/IR/Value.cpp =================================================================== --- llvm/lib/IR/Value.cpp +++ llvm/lib/IR/Value.cpp @@ -481,8 +481,6 @@ assert(New && "Value::replaceAllUsesWith() is invalid!"); assert(!contains(New, this) && "this->replaceAllUsesWith(expr(this)) is NOT valid!"); - assert(New->getType() == getType() && - "replaceAllUses of value with new value of different type!"); // Notify all ValueHandles (if present) that this value is going away. if (HasValueHandle) Index: llvm/lib/Transforms/Scalar/EarlyCSE.cpp =================================================================== --- llvm/lib/Transforms/Scalar/EarlyCSE.cpp +++ llvm/lib/Transforms/Scalar/EarlyCSE.cpp @@ -56,6 +56,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/GuardUtils.h" #include "llvm/Transforms/Utils/Local.h" #include @@ -1644,7 +1645,12 @@ EarlyCSE CSE(F.getParent()->getDataLayout(), TLI, TTI, DT, AC, MSSA); - return CSE.run(); + auto Changed = CSE.run(); + if (Changed) { + for (BasicBlock &BB : F) + RemoveRedundantDbgInstrs(&BB); + } + return Changed; } void getAnalysisUsage(AnalysisUsage &AU) const override { Index: llvm/lib/Transforms/Scalar/SROA.cpp =================================================================== --- llvm/lib/Transforms/Scalar/SROA.cpp +++ llvm/lib/Transforms/Scalar/SROA.cpp @@ -80,6 +80,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" #include @@ -4416,6 +4417,90 @@ return NewAI; } +void SROA::salvageInstruction(Instruction *Ins) { + SmallVector DbgUsers; + AllocaInst *AI = NULL; + int64_t ExprOffset = 0; + auto &Ctx = Ins->getContext(); + auto wrapMD = [&](Value *V) { + return MetadataAsValue::get(Ctx, ValueAsMetadata::get(V)); + }; + auto findFragmentAlloca = [&](int64_t ExprOffset) { + AllocaInst *AInst = NULL; + for (auto Fragment : SROA::Fragments) { + if ((uint64_t)(ExprOffset * 8) == Fragment.Offset) { + return Fragment.Alloca; + } + } + return AInst; + }; + if (isa(Ins)) { + for (Use &U2 : Ins->uses()) + if (isa(cast(U2.getUser()))) { + salvageInstruction(cast(U2.getUser())); + } + } + if (GetElementPtrInst *GEP = dyn_cast(Ins)) { + unsigned BitWidth = GEP->getModule()->getDataLayout().getIndexSizeInBits( + GEP->getPointerAddressSpace()); + APInt Offset(BitWidth, 0); + if (GEP->accumulateConstantOffset(GEP->getModule()->getDataLayout(), + Offset)) { + const uint64_t *Valptr = Offset.getRawData(); + if ((AI = findFragmentAlloca(*Valptr))) { + Ins->replaceAllUsesWith(AI); + DeadInsts.insert(Ins); + } + } + } + Ins->findDbgUsers(DbgUsers); + for (auto *DII : DbgUsers) { + if (!isa(DII)) + continue; + if ((DII->getExpression()->implicitPointerCount() > 0 || + DII->getExpression()->extractIfOffset(ExprOffset)) && + (AI = findFragmentAlloca(ExprOffset))) { + DII->setOperand(0, wrapMD(AI)); + } + } + for (Use &U : Ins->uses()) { + llvm::User *User = U.getUser(); + SmallVector DbgUsers; + if (isa(cast(User)) || + isa(cast(User))) + salvageInstruction(cast(User)); + } +} + +void SROA::fixGEPWithSplitAllocas() { + for (auto Fragment : SROA::Fragments) { + uint64_t NewOffset = Fragment.Offset; + for (Use &U : Fragment.Alloca->uses()) { + Instruction *I = cast(U.getUser()); + if (GetElementPtrInst *GEP = dyn_cast(I)) { + unsigned BitWidth = + GEP->getModule()->getDataLayout().getIndexSizeInBits( + GEP->getPointerAddressSpace()); + APInt Offset(BitWidth, 0); + if (GEP->accumulateConstantOffset(GEP->getModule()->getDataLayout(), + Offset)) { + + if (*Offset.getRawData()) { + NewOffset += 8 * (*(Offset.getRawData())); + for (auto Fragment2 : SROA::Fragments) { + if (Fragment2.Offset == NewOffset) { + I->replaceAllUsesWith(Fragment2.Alloca); + DeadInsts.insert(I); + break; + } + } + } + } + } + } + } +} + /// Walks the slices of an alloca and form partitions based on them, /// rewriting each of their uses. bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) { @@ -4485,17 +4570,7 @@ if (!IsSorted) llvm::sort(AS); - /// Describes the allocas introduced by rewritePartition in order to migrate - /// the debug info. - struct Fragment { - AllocaInst *Alloca; - uint64_t Offset; - uint64_t Size; - Fragment(AllocaInst *AI, uint64_t O, uint64_t S) - : Alloca(AI), Offset(O), Size(S) {} - }; - SmallVector Fragments; - + SROA::Fragments.clear(); // Rewrite each partition. for (auto &P : AS.partitions()) { if (AllocaInst *NewAI = rewritePartition(AI, AS, P)) { @@ -4506,12 +4581,18 @@ DL.getTypeSizeInBits(NewAI->getAllocatedType()).getFixedSize(); // Don't include any padding. uint64_t Size = std::min(AllocaSize, P.size() * SizeOfByte); - Fragments.push_back(Fragment(NewAI, P.beginOffset() * SizeOfByte, Size)); + SROA::Fragments.push_back( + Fragment(NewAI, P.beginOffset() * SizeOfByte, Size)); } } ++NumPartitions; } + if (AI.getModule()->getDwarfVersion() >= 5) { + salvageInstruction(&AI); + fixGEPWithSplitAllocas(); + } + NumAllocaPartitions += NumPartitions; MaxPartitionsPerAlloca.updateMax(NumPartitions); @@ -4523,7 +4604,7 @@ DIBuilder DIB(*AI.getModule(), /*AllowUnresolved*/ false); uint64_t AllocaSize = DL.getTypeSizeInBits(AI.getAllocatedType()).getFixedSize(); - for (auto Fragment : Fragments) { + for (auto Fragment : SROA::Fragments) { // Create a fragment expression describing the new partition or reuse AI's // expression if there is only one partition. auto *FragmentExpr = Expr; @@ -4640,12 +4721,18 @@ // Delete all the dead users of this alloca before splitting and rewriting it. for (Instruction *DeadUser : AS.getDeadUsers()) { + + // Try to preserve debug information attached to the instruction. + if (AI.getModule()->getDwarfVersion() >= 5) + DeadUser->salvageDebugInfo(); + // Free up everything used by this instruction. for (Use &DeadOp : DeadUser->operands()) clobberUse(DeadOp); // Now replace the uses of this instruction. - DeadUser->replaceAllUsesWith(UndefValue::get(DeadUser->getType())); + if (AI.getModule()->getDwarfVersion() < 5) + DeadUser->replaceAllUsesWith(UndefValue::get(DeadUser->getType())); // And mark it for deletion. DeadInsts.insert(DeadUser); @@ -4728,6 +4815,8 @@ LLVM_DEBUG(dbgs() << "Promoting allocas with mem2reg...\n"); PromoteMemToReg(PromotableAllocas, *DT, AC); + for (BasicBlock &BB : F) + RemoveRedundantDbgInstrs(&BB); PromotableAllocas.clear(); return true; } Index: llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_mem2reg.c =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_mem2reg.c @@ -0,0 +1,70 @@ +// RUN: clang %s -O2 -gdwarf-5 -o %t.o +// RUN: llvm-dwarfdump %t.o | FileCheck %s + +// CHECK-LABEL: DW_AT_name ("arr1") + +// 1. Test if more than one member location list is printed +// (for pointer to scalar) +// CHECK-LABEL: DW_AT_location +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptr2") + +// 2. Test if More than one member location list is printed +// (for pointer to pointer to scalar) +// CHECK-LABEL: DW_AT_location +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptrptr2") + +// 3. Test if one member location list is printed +// (for pointer to pointer to array) +// CHECK-LABEL: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptrptr3") +// +// CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_6") +// 4. Test if one member location list is printed +// (for pointer to pointer to scalar) +// CHECK-LABEL: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptrptr1") + +// CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_4") +// 5. Test if one member location list is printed +// (for pointer to array) +// CHECK-LABEL: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptr3") + +// CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_1") +// 6. Test if one member location list is printed +// (for pointer to scalar) +// CHECK-LABEL: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptr1") + +static const char *b = "opq"; +volatile int v; +int main() { + int var1 = 4; + int var2 = 5; + int arr1[2] = {2, 3}; + int *ptr1; + int *ptr2 = 0; + int *ptr3; + int **ptrptr1; + int **ptrptr2 = 0; + int **ptrptr3; + + v++; + ptr1 = &var1; + ptr2 = &var2; + ptr3 = arr1; + ptrptr1 = &ptr1; + ptrptr2 = &ptr2; + ptrptr3 = &ptr3; + v++; + + return arr1[0] + arr1[1] + *ptr1 + *ptr2 + **ptrptr1 + **ptrptr2 - 5; +} Index: llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa.c =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa.c @@ -0,0 +1,50 @@ +// RUN: clang %s -O2 -gdwarf-5 -o %t.o +// RUN: llvm-dwarfdump %t.o | FileCheck %s + +// CHECK-LABEL: DW_AT_name ("arr2") + +// 1. Test if more than 2 member location list is printed (for pointer pointing to aggregate) +// CHECK-LABEL: DW_AT_location +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// RCHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0) +// CHECK-NEXT: DW_AT_name ("ptr1") + +// 2. Test if location lists are merged to two (for pointer pointing to aggregate) +// CHECK-LABEL: DW_AT_location +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptr3") + +// 3. Test if one member location list is not omited (for pointer pointing to aggregate) +// CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_1") +// CHECK-LABEL: DW_AT_location +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptr2") + +static const char *b = "opq"; +volatile int v; +int main() { + int arr1[2] = {1, 2}; + int arr2[2] = {6, 7}; + int *ptr1 = 0; + int *ptr2; + int *ptr3 = 0; + + v++; + ptr1 = arr1; + ptr2 = arr2; + ptr3 = arr1; + (*ptr1)++; + (*ptr2)++; + (*ptr3)++; + v++; + ptr1++; + (*ptr1)++; + (*ptr2)++; + (*ptr3)++; + v++; + return arr1[0] + arr1[1] + arr2[0] + arr2[1] - 5; +} Index: llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa_inline.c =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa_inline.c @@ -0,0 +1,28 @@ +// RUN: clang %s -O2 -gdwarf-5 -o %t.o +// RUN: llvm-dwarfdump %t.o | FileCheck %s + +// CHECK: DW_TAG_inlined_subroutine + +// Test if More than 2 member location list is printed (for pointer pointing to aggregate) +// CHECK: DW_TAG_formal_parameter +// CHECK-NEXT: DW_AT_location +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0) +// CHECK-NEXT: DW_AT_abstract_origin ({{0x.+}} "ptr") + +static const char *b = "opq"; +volatile int v; +static inline void foo(int *ptr) { + (*ptr)++; + v++; + ptr++; + (*ptr)++; + v++; +} + +int main() { + int arr[2] = {1, 2}; + v++; + foo(arr); + return arr[0] + arr[1] - 5; +} Index: llvm/test/DebugInfo/X86/implicit_pointer_mem2reg.c =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/implicit_pointer_mem2reg.c @@ -0,0 +1,40 @@ +// RUN: clang %s -O2 -gdwarf-5 -S -emit-llvm -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll + +static const char *b = "opq"; +volatile int v; +int main() { + int var1 = 4; + int var2 = 5; + int arr1[2] = {2, 3}; + int *ptr1; + int *ptr2 = 0; + int *ptr3; + int **ptrptr1; + int **ptrptr2 = 0; + int **ptrptr3; + + v++; + ptr1 = &var1; + ptr2 = &var2; + ptr3 = arr1; + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD1:!.+]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 5, metadata [[MD2:!.+]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 2, metadata [[MD3:!.+]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer)) + ptrptr1 = &ptr1; + ptrptr2 = &ptr2; + ptrptr3 = &ptr3; + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD4:!.+]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer, DW_OP_LLVM_implicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 5, metadata [[MD5:!.+]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer, DW_OP_LLVM_implicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 2, metadata [[MD6:!.+]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer, DW_OP_LLVM_implicit_pointer)) + + v++; + + return arr1[0] + arr1[1] + *ptr1 + *ptr2 + **ptrptr1 + **ptrptr2 - 5; +} +// CHECK: [[MD1]] = !DILocalVariable(name: "ptr1" +// CHECK: [[MD2]] = !DILocalVariable(name: "ptr2" +// CHECK: [[MD3]] = !DILocalVariable(name: "ptr3" +// CHECK: [[MD4]] = !DILocalVariable(name: "ptrptr1" +// CHECK: [[MD5]] = !DILocalVariable(name: "ptrptr2" +// CHECK: [[MD6]] = !DILocalVariable(name: "ptrptr3" Index: llvm/test/DebugInfo/X86/implicit_pointer_sroa.c =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/implicit_pointer_sroa.c @@ -0,0 +1,36 @@ +// RUN: clang %s -O2 -gdwarf-5 -S -emit-llvm -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll + +static const char *b = "opq"; +volatile int v; +int main() { + int arr1[2] = {1, 2}; + int arr2[2] = {6, 7}; + int *ptr1 = 0; + int *ptr2; + int *ptr3 = 0; + // CHECK: call void @llvm.dbg.value(metadata i32* null, metadata [[MD1:!.+]], metadata !DIExpression()) + // CHECK: call void @llvm.dbg.value(metadata i32* null, metadata [[MD3:!.+]], metadata !DIExpression()) + v++; + ptr1 = arr1; + ptr2 = arr2; + ptr3 = arr1; + (*ptr1)++; + (*ptr2)++; + (*ptr3)++; + // CHECK: call void @llvm.dbg.value(metadata i32 7, metadata [[MD2:!.*]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 3, metadata [[MD1]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 3, metadata [[MD3]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer)) + v++; + ptr1++; + (*ptr1)++; + (*ptr2)++; + (*ptr3)++; + // CHECK: call void @llvm.dbg.value(metadata i32 8, metadata [[MD2]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD3]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer)) + v++; + return arr1[0] + arr1[1] + arr2[0] + arr2[1] - 5; +} +// CHECK: [[MD1]] = !DILocalVariable(name: "ptr1" +// CHECK: [[MD2]] = !DILocalVariable(name: "ptr2" +// CHECK: [[MD3]] = !DILocalVariable(name: "ptr3" Index: llvm/test/DebugInfo/X86/implicit_pointer_sroa_inline.c =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/implicit_pointer_sroa_inline.c @@ -0,0 +1,22 @@ +// RUN: clang %s -O2 -gdwarf-5 -S -emit-llvm -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll + +static const char *b = "opq"; +volatile int v; +static inline void foo(int *ptr) { + (*ptr)++; + // CHECK: call void @llvm.dbg.value(metadata i32 2, metadata [[MD:!.*]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer)) + v++; + ptr++; + (*ptr)++; + // CHECK: call void @llvm.dbg.value(metadata i32 3, metadata [[MD:!.*]], metadata !DIExpression(DW_OP_LLVM_implicit_pointer)) + v++; +} + +int main() { + int arr[2] = {1, 2}; + v++; + foo(arr); + return arr[0] + arr[1] - 5; +} +// CHECK: [[MD]] = !DILocalVariable(name: "ptr"