[llvm] c6e3e27 - [GVN] Assign unique VNs to calls with operand bundles (#211784)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 27 02:39:59 PDT 2026
Author: Momchil Velikov
Date: 2026-08-27T10:39:19+01:00
New Revision: c6e3e27b787decbefdac5fb3abdf79e6d4cf328b
URL: https://github.com/llvm/llvm-project/commit/c6e3e27b787decbefdac5fb3abdf79e6d4cf328b
DIFF: https://github.com/llvm/llvm-project/commit/c6e3e27b787decbefdac5fb3abdf79e6d4cf328b.diff
LOG: [GVN] Assign unique VNs to calls with operand bundles (#211784)
Call instructions with operand bundles may be assigned the same value
number, even if operand bundles differ. The GVN may eliminate one of the
calls in favour of another and drop one of the operand bundles.
Work around this by assigning unique value numbers to calls with operand
bundles.
Added:
llvm/test/Transforms/GVN/operand-bundle-unique-vn.ll
Modified:
llvm/lib/Transforms/Scalar/GVN.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index ddd8e62f393ee..bd3fceb1ff3d2 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -509,6 +509,14 @@ uint32_t GVNPass::ValueTable::lookupOrAddCall(CallInst *C) {
return NextValueNumber++;
}
+ // Conservatively assign unique value numbers to calls with operand bundles.
+ // TODO: Bundle names could be included in the value numbering expression to
+ // allow combining calls with identical bundles.
+ if (C->hasOperandBundles()) {
+ ValueNumbering[C] = NextValueNumber;
+ return NextValueNumber++;
+ }
+
if (AA->doesNotAccessMemory(C)) {
Expression Exp = createExpr(C);
uint32_t E = assignExpNewValueNum(Exp).first;
diff --git a/llvm/test/Transforms/GVN/operand-bundle-unique-vn.ll b/llvm/test/Transforms/GVN/operand-bundle-unique-vn.ll
new file mode 100644
index 0000000000000..b79512d84e93c
--- /dev/null
+++ b/llvm/test/Transforms/GVN/operand-bundle-unique-vn.ll
@@ -0,0 +1,70 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -p gvn < %s | FileCheck %s
+
+
+; Check that the calls are not CSEd because they have
diff erent operand bundle
+; tags.
+define i32 @no_drop_bundle(ptr %p) {
+; CHECK-LABEL: define i32 @no_drop_bundle(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[U:%.*]] = call i32 @g(i1 true) #[[ATTR1:[0-9]+]] [ "foo"(ptr [[P]]) ]
+; CHECK-NEXT: [[V:%.*]] = call i32 @g(i1 true) #[[ATTR1]] [ "bar"(ptr [[P]]) ]
+; CHECK-NEXT: [[W:%.*]] = add i32 [[U]], [[V]]
+; CHECK-NEXT: ret i32 [[W]]
+;
+ %u = call i32 @g(i1 true) memory(none) ["foo"(ptr %p)]
+ %v = call i32 @g(i1 true) memory(none) ["bar"(ptr %p)]
+ %w = add i32 %u, %v
+ ret i32 %w
+}
+
+; Check that the calls are not CSEd because they have
diff erent operand bundle
+; arguments.
+define i32 @
diff _args(ptr %p, ptr %q) {
+; CHECK-LABEL: define i32 @
diff _args(
+; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]]) {
+; CHECK-NEXT: [[U:%.*]] = call i32 @g(i1 true) #[[ATTR1]] [ "foo"(ptr [[P]]) ]
+; CHECK-NEXT: [[V:%.*]] = call i32 @g(i1 true) #[[ATTR1]] [ "foo"(ptr [[Q]]) ]
+; CHECK-NEXT: [[W:%.*]] = add i32 [[U]], [[V]]
+; CHECK-NEXT: ret i32 [[W]]
+;
+ %u = call i32 @g(i1 true) memory(none) ["foo"(ptr %p)]
+ %v = call i32 @g(i1 true) memory(none) ["foo"(ptr %q)]
+ %w = add i32 %u, %v
+ ret i32 %w
+}
+
+; Check that the calls are not CSEd because they access memory in unknown ways.
+define i32 @mem_access(ptr %p) {
+; CHECK-LABEL: define i32 @mem_access(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[U:%.*]] = call i32 @g(i1 true) [ "foo"(ptr [[P]]) ]
+; CHECK-NEXT: [[V:%.*]] = call i32 @g(i1 true) [ "foo"(ptr [[P]]) ]
+; CHECK-NEXT: [[W:%.*]] = add i32 [[U]], [[V]]
+; CHECK-NEXT: ret i32 [[W]]
+;
+ %u = call i32 @g(i1 true) ["foo"(ptr %p)]
+ %v = call i32 @g(i1 true) ["foo"(ptr %p)]
+ %w = add i32 %u, %v
+ ret i32 %w
+}
+
+; Check that the calls are CSEd because of the conservative treatment of operand
+; bundles.
+; TODO: Perhaps this can be made more precise by including the bundle name in
+; the value numbering expression.
+define i32 @no_mem_access(ptr %p) {
+; CHECK-LABEL: define i32 @no_mem_access(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[U:%.*]] = call i32 @g(i1 true) #[[ATTR1]] [ "align"(ptr [[P]], i32 8) ]
+; CHECK-NEXT: [[V:%.*]] = call i32 @g(i1 true) #[[ATTR1]] [ "align"(ptr [[P]], i32 8) ]
+; CHECK-NEXT: [[W:%.*]] = add i32 [[U]], [[V]]
+; CHECK-NEXT: ret i32 [[W]]
+;
+ %u = call i32 @g(i1 true) memory(none) ["align"(ptr %p, i32 8)]
+ %v = call i32 @g(i1 true) memory(none) ["align"(ptr %p, i32 8)]
+ %w = add i32 %u, %v
+ ret i32 %w
+}
+
+declare void @g(i1) nounwind willreturn
More information about the llvm-commits
mailing list