[Mlir-commits] [mlir] 9349a10 - Fix side effects for LLVM integer operations (udiv, sdiv) incorrectly marked as Pure (#166648)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Mon Nov 17 09:46:59 PST 2025
Author: Jeremy Furtek
Date: 2025-11-17T09:46:56-08:00
New Revision: 9349a10f93308a196499d2c80a222476c78f1065
URL: https://github.com/llvm/llvm-project/commit/9349a10f93308a196499d2c80a222476c78f1065
DIFF: https://github.com/llvm/llvm-project/commit/9349a10f93308a196499d2c80a222476c78f1065.diff
LOG: Fix side effects for LLVM integer operations (udiv, sdiv) incorrectly marked as Pure (#166648)
This MR modifies side effect traits of some integer arithmetic
operations in the LLVM dialect.
Prior to this MR, the LLVM dialect `sdiv` and `udiv` operations were
marked as `Pure` through `tblgen` inheritance of the
`LLVM_ArithmeticOpBase` class. The `Pure` trait allowed incorrect
hoisting of `sdiv`/`udiv` operations by the
`loop-independent-code-motion` pass.
This MR modifies the `sdiv` and `udiv` LLVM operations to have traits
and code motion behavior identical to their counterparts in the `arith`
dialect, which were established by the commit/review below.
https://github.com/llvm/llvm-project/commit/ed39825be48805b174d3177f1d8d41ed84784d18
https://reviews.llvm.org/D137814
Added:
Modified:
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
mlir/test/Transforms/loop-invariant-code-motion.mlir
mlir/unittests/Dialect/LLVMIR/CMakeLists.txt
Removed:
################################################################################
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index e425e16a4b1a6..971710fa3ee13 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -39,7 +39,7 @@ class LLVM_TerminatorOp<string mnemonic, list<Trait> traits = []> :
class LLVM_ArithmeticOpBase<Type type, string mnemonic,
string instName, list<Trait> traits = []> :
LLVM_Op<mnemonic,
- !listconcat([Pure, SameOperandsAndResultType], traits)>,
+ !listconcat([SameOperandsAndResultType, NoMemoryEffect], traits)>,
LLVM_Builder<"$res = builder.Create" # instName # "($lhs, $rhs);"> {
dag commonArgs = (ins LLVM_ScalarOrVectorOf<type>:$lhs,
LLVM_ScalarOrVectorOf<type>:$rhs);
@@ -116,7 +116,8 @@ class LLVM_IntArithmeticOpWithDisjointFlag<string mnemonic, string instName,
class LLVM_FloatArithmeticOp<string mnemonic, string instName,
list<Trait> traits = []> :
LLVM_ArithmeticOpBase<LLVM_AnyFloat, mnemonic, instName,
- !listconcat([DeclareOpInterfaceMethods<FastmathFlagsInterface>], traits)> {
+ !listconcat([DeclareOpInterfaceMethods<FastmathFlagsInterface>, Pure],
+ traits)> {
dag fmfArg = (
ins DefaultValuedAttr<LLVM_FastmathFlagsAttr, "{}">:$fastmathFlags);
let arguments = !con(commonArgs, fmfArg);
@@ -149,24 +150,26 @@ class LLVM_UnaryFloatArithmeticOp<Type type, string mnemonic,
// Integer binary operations.
def LLVM_AddOp : LLVM_IntArithmeticOpWithOverflowFlag<"add", "Add",
- [Commutative]>;
-def LLVM_SubOp : LLVM_IntArithmeticOpWithOverflowFlag<"sub", "Sub", []>;
+ [Commutative, Pure]>;
+def LLVM_SubOp : LLVM_IntArithmeticOpWithOverflowFlag<"sub", "Sub", [Pure]>;
def LLVM_MulOp : LLVM_IntArithmeticOpWithOverflowFlag<"mul", "Mul",
- [Commutative]>;
-def LLVM_UDivOp : LLVM_IntArithmeticOpWithExactFlag<"udiv", "UDiv">;
-def LLVM_SDivOp : LLVM_IntArithmeticOpWithExactFlag<"sdiv", "SDiv">;
-def LLVM_URemOp : LLVM_IntArithmeticOp<"urem", "URem">;
-def LLVM_SRemOp : LLVM_IntArithmeticOp<"srem", "SRem">;
-def LLVM_AndOp : LLVM_IntArithmeticOp<"and", "And">;
-def LLVM_OrOp : LLVM_IntArithmeticOpWithDisjointFlag<"or", "Or"> {
+ [Commutative, Pure]>;
+def LLVM_UDivOp : LLVM_IntArithmeticOpWithExactFlag<"udiv", "UDiv",
+ [DeclareOpInterfaceMethods<ConditionallySpeculatable>]>;
+def LLVM_SDivOp : LLVM_IntArithmeticOpWithExactFlag<"sdiv", "SDiv",
+ [DeclareOpInterfaceMethods<ConditionallySpeculatable>]>;
+def LLVM_URemOp : LLVM_IntArithmeticOp<"urem", "URem", [Pure]>;
+def LLVM_SRemOp : LLVM_IntArithmeticOp<"srem", "SRem", [Pure]>;
+def LLVM_AndOp : LLVM_IntArithmeticOp<"and", "And", [Pure]>;
+def LLVM_OrOp : LLVM_IntArithmeticOpWithDisjointFlag<"or", "Or", [Pure]> {
let hasFolder = 1;
}
-def LLVM_XOrOp : LLVM_IntArithmeticOp<"xor", "Xor">;
-def LLVM_ShlOp : LLVM_IntArithmeticOpWithOverflowFlag<"shl", "Shl", []> {
+def LLVM_XOrOp : LLVM_IntArithmeticOp<"xor", "Xor", [Pure]>;
+def LLVM_ShlOp : LLVM_IntArithmeticOpWithOverflowFlag<"shl", "Shl", [Pure]> {
let hasFolder = 1;
}
-def LLVM_LShrOp : LLVM_IntArithmeticOpWithExactFlag<"lshr", "LShr">;
-def LLVM_AShrOp : LLVM_IntArithmeticOpWithExactFlag<"ashr", "AShr">;
+def LLVM_LShrOp : LLVM_IntArithmeticOpWithExactFlag<"lshr", "LShr", [Pure]>;
+def LLVM_AShrOp : LLVM_IntArithmeticOpWithExactFlag<"ashr", "AShr", [Pure]>;
// Base class for compare operations. A compare operation takes two operands
// of the same type and returns a boolean result. If the operands are
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 1bf4a1c508843..5b819485b1be4 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -4223,6 +4223,34 @@ LogicalResult InlineAsmOp::verify() {
return success();
}
+//===----------------------------------------------------------------------===//
+// UDivOp
+//===----------------------------------------------------------------------===//
+Speculation::Speculatability UDivOp::getSpeculatability() {
+ // X / 0 => UB
+ Value divisor = getRhs();
+ if (matchPattern(divisor, m_IntRangeWithoutZeroU()))
+ return Speculation::Speculatable;
+
+ return Speculation::NotSpeculatable;
+}
+
+//===----------------------------------------------------------------------===//
+// SDivOp
+//===----------------------------------------------------------------------===//
+Speculation::Speculatability SDivOp::getSpeculatability() {
+ // This function conservatively assumes that all signed division by -1 are
+ // not speculatable.
+ // X / 0 => UB
+ // INT_MIN / -1 => UB
+ Value divisor = getRhs();
+ if (matchPattern(divisor, m_IntRangeWithoutZeroS()) &&
+ matchPattern(divisor, m_IntRangeWithoutNegOneS()))
+ return Speculation::Speculatable;
+
+ return Speculation::NotSpeculatable;
+}
+
//===----------------------------------------------------------------------===//
// LLVMDialect initialization, type parsing, and registration.
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/Transforms/loop-invariant-code-motion.mlir b/mlir/test/Transforms/loop-invariant-code-motion.mlir
index c1604e226a334..31a4f64dd7de0 100644
--- a/mlir/test/Transforms/loop-invariant-code-motion.mlir
+++ b/mlir/test/Transforms/loop-invariant-code-motion.mlir
@@ -880,6 +880,18 @@ func.func @no_speculate_divui(
return
}
+func.func @no_speculate_udiv(
+// CHECK-LABEL: @no_speculate_udiv(
+ %num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
+ scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: llvm.udiv
+ %val = llvm.udiv %num, %denom : i32
+ }
+
+ return
+}
+
func.func @no_speculate_divsi(
// CHECK-LABEL: @no_speculate_divsi(
%num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
@@ -892,6 +904,18 @@ func.func @no_speculate_divsi(
return
}
+func.func @no_speculate_sdiv(
+// CHECK-LABEL: @no_speculate_sdiv(
+ %num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
+ scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: llvm.sdiv
+ %val = llvm.sdiv %num, %denom : i32
+ }
+
+ return
+}
+
func.func @no_speculate_ceildivui(
// CHECK-LABEL: @no_speculate_ceildivui(
%num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
@@ -928,6 +952,18 @@ func.func @no_speculate_divui_const(%num: i32, %lb: index, %ub: index, %step: in
return
}
+func.func @no_speculate_udiv_const(%num: i32, %lb: index, %ub: index, %step: index) {
+// CHECK-LABEL: @no_speculate_udiv_const(
+ %c0 = arith.constant 0 : i32
+ scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: llvm.udiv
+ %val = llvm.udiv %num, %c0 : i32
+ }
+
+ return
+}
+
func.func @speculate_divui_const(
// CHECK-LABEL: @speculate_divui_const(
%num: i32, %lb: index, %ub: index, %step: index) {
@@ -941,6 +977,19 @@ func.func @speculate_divui_const(
return
}
+func.func @speculate_udiv_const(
+// CHECK-LABEL: @speculate_udiv_const(
+ %num: i32, %lb: index, %ub: index, %step: index) {
+ %c5 = llvm.mlir.constant(5 : i32) : i32
+// CHECK: llvm.udiv
+// CHECK: scf.for
+ scf.for %i = %lb to %ub step %step {
+ %val = llvm.udiv %num, %c5 : i32
+ }
+
+ return
+}
+
func.func @no_speculate_ceildivui_const(%num: i32, %lb: index, %ub: index, %step: index) {
// CHECK-LABEL: @no_speculate_ceildivui_const(
%c0 = arith.constant 0 : i32
@@ -979,6 +1028,19 @@ func.func @no_speculate_divsi_const0(
return
}
+func.func @no_speculate_sdiv_const0(
+// CHECK-LABEL: @no_speculate_sdiv_const0(
+ %num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
+ %c0 = arith.constant 0 : i32
+ scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: llvm.sdiv
+ %val = llvm.sdiv %num, %c0 : i32
+ }
+
+ return
+}
+
func.func @no_speculate_divsi_const_minus1(
// CHECK-LABEL: @no_speculate_divsi_const_minus1(
%num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
@@ -992,6 +1054,19 @@ func.func @no_speculate_divsi_const_minus1(
return
}
+func.func @no_speculate_sdiv_const_minus1(
+// CHECK-LABEL: @no_speculate_sdiv_const_minus1(
+ %num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
+ %cm1 = arith.constant -1 : i32
+ scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: llvm.sdiv
+ %val = llvm.sdiv %num, %cm1 : i32
+ }
+
+ return
+}
+
func.func @speculate_divsi_const(
// CHECK-LABEL: @speculate_divsi_const(
%num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
@@ -1005,6 +1080,19 @@ func.func @speculate_divsi_const(
return
}
+func.func @speculate_sdiv_const(
+// CHECK-LABEL: @speculate_sdiv_const(
+ %num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
+ %c5 = arith.constant 5 : i32
+ scf.for %i = %lb to %ub step %step {
+// CHECK: llvm.sdiv
+// CHECK: scf.for
+ %val = llvm.sdiv %num, %c5 : i32
+ }
+
+ return
+}
+
func.func @no_speculate_ceildivsi_const0(
// CHECK-LABEL: @no_speculate_ceildivsi_const0(
%num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
@@ -1057,6 +1145,19 @@ func.func @no_speculate_divui_range(
return
}
+func.func @no_speculate_udiv_range(
+// CHECK-LABEL: @no_speculate_udiv_range(
+ %num: i8, %lb: index, %ub: index, %step: index) {
+ %denom = test.with_bounds {smax = 127 : i8, smin = -128 : i8, umax = 255 : i8, umin = 0 : i8} : i8
+ scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: llvm.udiv
+ %val = llvm.udiv %num, %denom : i8
+ }
+
+ return
+}
+
func.func @no_speculate_divsi_range(
// CHECK-LABEL: @no_speculate_divsi_range(
%num: i8, %lb: index, %ub: index, %step: index) {
@@ -1072,6 +1173,21 @@ func.func @no_speculate_divsi_range(
return
}
+func.func @no_speculate_sdiv_range(
+// CHECK-LABEL: @no_speculate_sdiv_range(
+ %num: i8, %lb: index, %ub: index, %step: index) {
+ %denom0 = test.with_bounds {smax = -1: i8, smin = -128 : i8, umax = 255 : i8, umin = 0 : i8} : i8
+ %denom1 = test.with_bounds {smax = 127 : i8, smin = 0 : i8, umax = 255 : i8, umin = 0 : i8} : i8
+ scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK-COUNT-2: llvm.sdiv
+ %val0 = llvm.sdiv %num, %denom0 : i8
+ %val1 = llvm.sdiv %num, %denom1 : i8
+ }
+
+ return
+}
+
func.func @no_speculate_ceildivui_range(
// CHECK-LABEL: @no_speculate_ceildivui_range(
%num: i8, %lb: index, %ub: index, %step: index) {
@@ -1113,6 +1229,19 @@ func.func @speculate_divui_range(
return
}
+func.func @speculate_udiv_range(
+// CHECK-LABEL: @speculate_udiv_range(
+ %num: i8, %lb: index, %ub: index, %step: index) {
+ %denom = test.with_bounds {smax = 127 : i8, smin = -128 : i8, umax = 255 : i8, umin = 1 : i8} : i8
+ scf.for %i = %lb to %ub step %step {
+// CHECK: llvm.udiv
+// CHECK: scf.for
+ %val = llvm.udiv %num, %denom : i8
+ }
+
+ return
+}
+
func.func @speculate_divsi_range(
// CHECK-LABEL: @speculate_divsi_range(
%num: i8, %lb: index, %ub: index, %step: index) {
@@ -1129,6 +1258,22 @@ func.func @speculate_divsi_range(
return
}
+func.func @speculate_sdiv_range(
+// CHECK-LABEL: @speculate_sdiv_range(
+ %num: i8, %lb: index, %ub: index, %step: index) {
+ %denom0 = test.with_bounds {smax = 127 : i8, smin = 1 : i8, umax = 255 : i8, umin = 0 : i8} : i8
+ %denom1 = test.with_bounds {smax = -2 : i8, smin = -128 : i8, umax = 255 : i8, umin = 0 : i8} : i8
+ scf.for %i = %lb to %ub step %step {
+// CHECK-COUNT-2: llvm.sdiv
+// CHECK: scf.for
+ %val0 = llvm.sdiv %num, %denom0 : i8
+ %val1 = llvm.sdiv %num, %denom1 : i8
+
+ }
+
+ return
+}
+
func.func @speculate_ceildivui_range(
// CHECK-LABEL: @speculate_ceildivui_range(
%num: i8, %lb: index, %ub: index, %step: index) {
diff --git a/mlir/unittests/Dialect/LLVMIR/CMakeLists.txt b/mlir/unittests/Dialect/LLVMIR/CMakeLists.txt
index 7cc130d02ad74..568126fd342cc 100644
--- a/mlir/unittests/Dialect/LLVMIR/CMakeLists.txt
+++ b/mlir/unittests/Dialect/LLVMIR/CMakeLists.txt
@@ -4,4 +4,5 @@ add_mlir_unittest(MLIRLLVMIRTests
mlir_target_link_libraries(MLIRLLVMIRTests
PRIVATE
MLIRLLVMDialect
+ MLIRInferIntRangeInterface
)
More information about the Mlir-commits
mailing list