struct llvm::GVNPass::Expression {
uint32_t opcode;
bool commutative = false;
+ // The type is not necessarily the result type of the expression, it may be
+ // any additional type needed to disambiguate the expression.
Type *type = nullptr;
SmallVector<uint32_t, 4> varargs;
GVNPass::Expression GVNPass::ValueTable::createExpr(Instruction *I) {
Expression e;
- e.type = I->getType();
+ // 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<GetElementPtrInst>(I))
+ e.type = GEP->getSourceElementType();
+ else
+ e.type = I->getType();
e.opcode = I->getOpcode();
if (const GCRelocateInst *GCR = dyn_cast<GCRelocateInst>(I)) {
// gc.relocate is 'special' call: its second and third operands are
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -gvn -opaque-pointers < %s | FileCheck %s
+
+declare void @use(ptr)
+
+define void @test(ptr %p) {
+; CHECK-LABEL: @test(
+; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i32, ptr [[P:%.*]], i64 1
+; CHECK-NEXT: [[GEP3:%.*]] = getelementptr i64, ptr [[P]], i64 1
+; CHECK-NEXT: call void @use(ptr [[GEP1]])
+; CHECK-NEXT: call void @use(ptr [[GEP1]])
+; CHECK-NEXT: call void @use(ptr [[GEP3]])
+; CHECK-NEXT: ret void
+;
+ %gep1 = getelementptr i32, ptr %p, i64 1
+ %gep2 = getelementptr i32, ptr %p, i64 1
+ %gep3 = getelementptr i64, ptr %p, i64 1
+ call void @use(ptr %gep1)
+ call void @use(ptr %gep2)
+ call void @use(ptr %gep3)
+ ret void
+}