diff --git a/llvm/include/llvm/Transforms/Scalar/GVN.h b/llvm/include/llvm/Transforms/Scalar/GVN.h --- a/llvm/include/llvm/Transforms/Scalar/GVN.h +++ b/llvm/include/llvm/Transforms/Scalar/GVN.h @@ -40,6 +40,7 @@ class ExtractValueInst; class Function; class FunctionPass; +class GetElementPtrInst; class ImplicitControlFlowTracking; class LoadInst; class LoopInfo; @@ -177,6 +178,7 @@ Expression createCmpExpr(unsigned Opcode, CmpInst::Predicate Predicate, Value *LHS, Value *RHS); Expression createExtractvalueExpr(ExtractValueInst *EI); + Expression createGEPExpr(GetElementPtrInst *GEP); uint32_t lookupOrAddCall(CallInst *C); uint32_t phiTranslateImpl(const BasicBlock *BB, const BasicBlock *PhiBlock, uint32_t Num, GVNPass &Gvn); diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp --- a/llvm/lib/Transforms/Scalar/GVN.cpp +++ b/llvm/lib/Transforms/Scalar/GVN.cpp @@ -307,13 +307,7 @@ GVNPass::Expression GVNPass::ValueTable::createExpr(Instruction *I) { Expression e; - // For GEPs, disambiguate based on the source element type, which is not - // implied by the result type with opaque pointers. (Conversely, the source - // element type together with the operand types does imply the result type.) - if (const auto *GEP = dyn_cast(I)) - e.type = GEP->getSourceElementType(); - else - e.type = I->getType(); + e.type = I->getType(); e.opcode = I->getOpcode(); if (const GCRelocateInst *GCR = dyn_cast(I)) { // gc.relocate is 'special' call: its second and third operands are @@ -404,6 +398,39 @@ return e; } +GVNPass::Expression GVNPass::ValueTable::createGEPExpr(GetElementPtrInst *GEP) { + Expression E; + Type *PtrTy = GEP->getType()->getScalarType(); + const DataLayout &DL = GEP->getModule()->getDataLayout(); + unsigned BitWidth = DL.getIndexTypeSizeInBits(PtrTy); + MapVector VariableOffsets; + APInt ConstantOffset(BitWidth, 0); + if (PtrTy->isOpaquePointerTy() && + GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset)) { + // For opaque pointers, convert into offset representation, to recognize + // equivalent address calculations that use different type encoding. + LLVMContext &Context = GEP->getContext(); + E.opcode = GEP->getOpcode(); + E.type = nullptr; + E.varargs.push_back(lookupOrAdd(GEP->getPointerOperand())); + for (const auto &Pair : VariableOffsets) { + E.varargs.push_back(lookupOrAdd(Pair.first)); + E.varargs.push_back(lookupOrAdd(ConstantInt::get(Context, Pair.second))); + } + if (!ConstantOffset.isZero()) + E.varargs.push_back( + lookupOrAdd(ConstantInt::get(Context, ConstantOffset))); + } else { + // If converting to offset representation fails (for typed pointers and + // scalable vectors), fall back to type-based implementation: + E.opcode = GEP->getOpcode(); + E.type = GEP->getSourceElementType(); + for (Use &Op : GEP->operands()) + E.varargs.push_back(lookupOrAdd(Op)); + } + return E; +} + //===----------------------------------------------------------------------===// // ValueTable External Functions //===----------------------------------------------------------------------===// @@ -587,9 +614,11 @@ case Instruction::InsertElement: case Instruction::ShuffleVector: case Instruction::InsertValue: - case Instruction::GetElementPtr: exp = createExpr(I); break; + case Instruction::GetElementPtr: + exp = createGEPExpr(cast(I)); + break; case Instruction::ExtractValue: exp = createExtractvalueExpr(cast(I)); break; diff --git a/llvm/test/Transforms/GVN/opaque-ptr.ll b/llvm/test/Transforms/GVN/opaque-ptr.ll --- a/llvm/test/Transforms/GVN/opaque-ptr.ll +++ b/llvm/test/Transforms/GVN/opaque-ptr.ll @@ -25,32 +25,26 @@ define void @gep_cse_offset_canonicalization(ptr %p, i64 %idx, i64 %idx2) { ; CHECK-LABEL: @gep_cse_offset_canonicalization( ; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i64, ptr [[P:%.*]], i64 1 -; CHECK-NEXT: [[GEP1_SAME1:%.*]] = getelementptr i32, ptr [[P]], i64 2 -; CHECK-NEXT: [[GEP1_SAME2:%.*]] = getelementptr i8, ptr [[P]], i64 8 ; CHECK-NEXT: [[GEP1_DIFFERENT:%.*]] = getelementptr i8, ptr [[P]], i64 12 ; CHECK-NEXT: call void @use(ptr [[GEP1]]) -; CHECK-NEXT: call void @use(ptr [[GEP1_SAME1]]) -; CHECK-NEXT: call void @use(ptr [[GEP1_SAME2]]) +; CHECK-NEXT: call void @use(ptr [[GEP1]]) +; CHECK-NEXT: call void @use(ptr [[GEP1]]) ; CHECK-NEXT: call void @use(ptr [[GEP1_DIFFERENT]]) ; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i64, ptr [[P]], i64 [[IDX:%.*]] -; CHECK-NEXT: [[GEP2_SAME:%.*]] = getelementptr { i32, i32 }, ptr [[P]], i64 [[IDX]] ; CHECK-NEXT: [[GEP2_DIFFERENT:%.*]] = getelementptr { i32, i32, i32 }, ptr [[P]], i64 [[IDX]] ; CHECK-NEXT: call void @use(ptr [[GEP2]]) -; CHECK-NEXT: call void @use(ptr [[GEP2_SAME]]) +; CHECK-NEXT: call void @use(ptr [[GEP2]]) ; CHECK-NEXT: call void @use(ptr [[GEP2_DIFFERENT]]) ; CHECK-NEXT: [[GEP3:%.*]] = getelementptr { [0 x i32], [0 x i32] }, ptr [[P]], i64 0, i32 0, i64 [[IDX]] -; CHECK-NEXT: [[GEP3_SAME1:%.*]] = getelementptr { [0 x i32], [0 x i32] }, ptr [[P]], i64 0, i32 1, i64 [[IDX]] -; CHECK-NEXT: [[GEP3_SAME2:%.*]] = getelementptr { [0 x i32], [0 x i32] }, ptr [[P]], i64 1, i32 0, i64 [[IDX]] ; CHECK-NEXT: [[GEP3_DIFFERENT:%.*]] = getelementptr { [0 x i32], [0 x i32] }, ptr [[P]], i64 0, i32 0, i64 [[IDX2:%.*]] ; CHECK-NEXT: call void @use(ptr [[GEP3]]) -; CHECK-NEXT: call void @use(ptr [[GEP3_SAME1]]) -; CHECK-NEXT: call void @use(ptr [[GEP3_SAME2]]) +; CHECK-NEXT: call void @use(ptr [[GEP3]]) +; CHECK-NEXT: call void @use(ptr [[GEP3]]) ; CHECK-NEXT: call void @use(ptr [[GEP3_DIFFERENT]]) ; CHECK-NEXT: [[GEP4:%.*]] = getelementptr [4 x i32], ptr [[P]], i64 [[IDX]], i64 [[IDX2]] -; CHECK-NEXT: [[GEP4_SAME:%.*]] = getelementptr [4 x float], ptr [[P]], i64 [[IDX]], i64 [[IDX2]] ; CHECK-NEXT: [[GEP4_DIFFERENT:%.*]] = getelementptr [4 x float], ptr [[P]], i64 [[IDX2]], i64 [[IDX]] ; CHECK-NEXT: call void @use(ptr [[GEP4]]) -; CHECK-NEXT: call void @use(ptr [[GEP4_SAME]]) +; CHECK-NEXT: call void @use(ptr [[GEP4]]) ; CHECK-NEXT: call void @use(ptr [[GEP4_DIFFERENT]]) ; CHECK-NEXT: [[GEP5:%.*]] = getelementptr , ptr [[P]], i64 1 ; CHECK-NEXT: [[GEP5_SAME:%.*]] = getelementptr , ptr [[P]], i64 1 @@ -88,6 +82,7 @@ call void @use(ptr %gep4) call void @use(ptr %gep4.same) call void @use(ptr %gep4.different) + ; TODO: %gep5 and %gep5.same are equivalent as well. %gep5 = getelementptr , ptr %p, i64 1 %gep5.same = getelementptr , ptr %p, i64 1 %gep5.different = getelementptr , ptr %p, i64 1