Model UB in integer division operations in the arith dialect
authorSanjoy Das <sanjoy.das@getcruise.com>
Fri, 11 Nov 2022 05:31:33 +0000 (21:31 -0800)
committerSanjoy Das <sanjoy.das@getcruise.com>
Fri, 11 Nov 2022 18:42:31 +0000 (10:42 -0800)
Before this commit `arith.{ceil}div{u|s}i` were marked `Pure` which is
incorrect because these operations invoke UB on certain inputs.

Fixes: https://github.com/llvm/llvm-project/issues/58700

Reviewed By: kuhar

Differential Revision: https://reviews.llvm.org/D137814

mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
mlir/lib/Dialect/Arith/IR/ArithOps.cpp
mlir/test/Transforms/loop-invariant-code-motion.mlir

index 3d6cef9..cc1801b 100644 (file)
@@ -20,12 +20,13 @@ include "mlir/IR/BuiltinAttributeInterfaces.td"
 include "mlir/IR/OpAsmInterface.td"
 include "mlir/IR/EnumAttr.td"
 
-// Base class for Arith dialect ops. Ops in this dialect have no side
+// Base class for Arith dialect ops. Ops in this dialect have no memory
 // effects and can be applied element-wise to vectors and tensors.
 class Arith_Op<string mnemonic, list<Trait> traits = []> :
-    Op<Arith_Dialect, mnemonic, traits # [Pure,
-    DeclareOpInterfaceMethods<VectorUnrollOpInterface>] #
-    ElementwiseMappable.traits>;
+    Op<Arith_Dialect, mnemonic,
+       traits #
+       [DeclareOpInterfaceMethods<VectorUnrollOpInterface>, NoMemoryEffect] #
+       ElementwiseMappable.traits>;
 
 // Base class for integer and floating point arithmetic ops. All ops have one
 // result, require operands and results to be of the same type, and can accept
@@ -35,7 +36,7 @@ class Arith_ArithOp<string mnemonic, list<Trait> traits = []> :
 
 // Base class for unary arithmetic operations.
 class Arith_UnaryOp<string mnemonic, list<Trait> traits = []> :
-    Arith_ArithOp<mnemonic, traits> {
+    Arith_ArithOp<mnemonic, traits # [Pure]> {
   let assemblyFormat = "$operand attr-dict `:` type($result)";
 }
 
@@ -47,7 +48,7 @@ class Arith_BinaryOp<string mnemonic, list<Trait> traits = []> :
 
 // Base class for ternary arithmetic operations.
 class Arith_TernaryOp<string mnemonic, list<Trait> traits = []> :
-    Arith_ArithOp<mnemonic, traits> {
+    Arith_ArithOp<mnemonic, traits # [Pure]> {
   let assemblyFormat = "$a `,` $b `,` $c attr-dict `:` type($result)";
 }
 
@@ -58,6 +59,10 @@ class Arith_IntBinaryOp<string mnemonic, list<Trait> traits = []> :
     Arguments<(ins SignlessIntegerLike:$lhs, SignlessIntegerLike:$rhs)>,
     Results<(outs SignlessIntegerLike:$result)>;
 
+// Base class for integer binary operations without undefined behavior.
+class Arith_TotalIntBinaryOp<string mnemonic, list<Trait> traits = []> :
+    Arith_IntBinaryOp<mnemonic, traits # [Pure]>;
+
 // Base class for floating point unary operations.
 class Arith_FloatUnaryOp<string mnemonic, list<Trait> traits = []> :
     Arith_UnaryOp<mnemonic,
@@ -73,7 +78,7 @@ class Arith_FloatUnaryOp<string mnemonic, list<Trait> traits = []> :
 // Base class for floating point binary operations.
 class Arith_FloatBinaryOp<string mnemonic, list<Trait> traits = []> :
     Arith_BinaryOp<mnemonic,
-      !listconcat([DeclareOpInterfaceMethods<ArithFastMathInterface>],
+      !listconcat([Pure, DeclareOpInterfaceMethods<ArithFastMathInterface>],
                   traits)>,
     Arguments<(ins FloatLike:$lhs, FloatLike:$rhs,
       DefaultValuedAttr<Arith_FastMathAttr, "FastMathFlags::none">:$fastmath)>,
@@ -86,7 +91,7 @@ class Arith_FloatBinaryOp<string mnemonic, list<Trait> traits = []> :
 // result. If either is a shaped type, then the other must be of the same shape.
 class Arith_CastOp<string mnemonic, TypeConstraint From, TypeConstraint To,
                    list<Trait> traits = []> :
-    Arith_Op<mnemonic, traits # [SameOperandsAndResultShape,
+    Arith_Op<mnemonic, traits # [Pure, SameOperandsAndResultShape,
       DeclareOpInterfaceMethods<CastOpInterface>]>,
     Arguments<(ins From:$in)>,
     Results<(outs To:$out)> {
@@ -121,7 +126,7 @@ class Arith_FToFCastOp<string mnemonic, list<Trait> traits = []> :
 // and returns a single `BoolLike` result. If the operand type is a vector or
 // tensor, then the result will be one of `i1` of the same shape.
 class Arith_CompareOp<string mnemonic, list<Trait> traits = []> :
-    Arith_Op<mnemonic, traits # [SameTypeOperands, TypesMatchWith<
+    Arith_Op<mnemonic, traits # [Pure, SameTypeOperands, TypesMatchWith<
     "result type has i1 element type and same shape as operands",
     "lhs", "result", "::getI1SameShape($_self)">]> {
   let results = (outs BoolLike:$result);
@@ -191,7 +196,7 @@ def Arith_ConstantOp : Op<Arith_Dialect, "constant",
 // AddIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_AddIOp : Arith_IntBinaryOp<"addi", [Commutative]> {
+def Arith_AddIOp : Arith_TotalIntBinaryOp<"addi", [Commutative]> {
   let summary = "integer addition operation";
   let description = [{
     The `addi` operation takes two operands and returns one result, each of
@@ -217,7 +222,7 @@ def Arith_AddIOp : Arith_IntBinaryOp<"addi", [Commutative]> {
 }
 
 
-def Arith_AddUICarryOp : Arith_Op<"addui_carry", [Commutative,
+def Arith_AddUICarryOp : Arith_Op<"addui_carry", [Pure, Commutative,
     AllTypesMatch<["lhs", "rhs", "sum"]>]> {
   let summary = "unsigned integer addition operation returning sum and carry";
   let description = [{
@@ -264,7 +269,7 @@ def Arith_AddUICarryOp : Arith_Op<"addui_carry", [Commutative,
 // SubIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_SubIOp : Arith_IntBinaryOp<"subi"> {
+def Arith_SubIOp : Arith_TotalIntBinaryOp<"subi"> {
   let summary = "integer subtraction operation";
   let hasFolder = 1;
   let hasCanonicalizer = 1;
@@ -274,7 +279,7 @@ def Arith_SubIOp : Arith_IntBinaryOp<"subi"> {
 // MulIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_MulIOp : Arith_IntBinaryOp<"muli", [Commutative]> {
+def Arith_MulIOp : Arith_TotalIntBinaryOp<"muli", [Commutative]> {
   let summary = "integer multiplication operation";
   let hasFolder = 1;
 }
@@ -283,7 +288,7 @@ def Arith_MulIOp : Arith_IntBinaryOp<"muli", [Commutative]> {
 // DivUIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_DivUIOp : Arith_IntBinaryOp<"divui"> {
+def Arith_DivUIOp : Arith_IntBinaryOp<"divui", [ConditionallySpeculatable]> {
   let summary = "unsigned integer division operation";
   let description = [{
     Unsigned integer division. Rounds towards zero. Treats the leading bit as
@@ -306,6 +311,12 @@ def Arith_DivUIOp : Arith_IntBinaryOp<"divui"> {
     %x = arith.divui %y, %z : tensor<4x?xi8>
     ```
   }];
+
+  let extraClassDeclaration = [{
+    /// Interface method for ConditionallySpeculatable.
+    Speculation::Speculatability getSpeculatability();
+  }];
+
   let hasFolder = 1;
 }
 
@@ -313,7 +324,7 @@ def Arith_DivUIOp : Arith_IntBinaryOp<"divui"> {
 // DivSIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_DivSIOp : Arith_IntBinaryOp<"divsi"> {
+def Arith_DivSIOp : Arith_IntBinaryOp<"divsi", [ConditionallySpeculatable]> {
   let summary = "signed integer division operation";
   let description = [{
     Signed integer division. Rounds towards zero. Treats the leading bit as
@@ -335,6 +346,12 @@ def Arith_DivSIOp : Arith_IntBinaryOp<"divsi"> {
     %x = arith.divsi %y, %z : tensor<4x?xi8>
     ```
   }];
+
+  let extraClassDeclaration = [{
+    /// Interface method for ConditionallySpeculatable.
+    Speculation::Speculatability getSpeculatability();
+  }];
+
   let hasFolder = 1;
 }
 
@@ -342,7 +359,8 @@ def Arith_DivSIOp : Arith_IntBinaryOp<"divsi"> {
 // CeilDivUIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_CeilDivUIOp : Arith_IntBinaryOp<"ceildivui"> {
+def Arith_CeilDivUIOp : Arith_IntBinaryOp<"ceildivui",
+                                          [ConditionallySpeculatable]> {
   let summary = "unsigned ceil integer division operation";
   let description = [{
     Unsigned integer division. Rounds towards positive infinity. Treats the
@@ -359,6 +377,12 @@ def Arith_CeilDivUIOp : Arith_IntBinaryOp<"ceildivui"> {
     %a = arith.ceildivui %b, %c : i64
     ```
   }];
+
+  let extraClassDeclaration = [{
+    /// Interface method for ConditionallySpeculatable.
+    Speculation::Speculatability getSpeculatability();
+  }];
+
   let hasFolder = 1;
 }
 
@@ -366,7 +390,8 @@ def Arith_CeilDivUIOp : Arith_IntBinaryOp<"ceildivui"> {
 // CeilDivSIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_CeilDivSIOp : Arith_IntBinaryOp<"ceildivsi"> {
+def Arith_CeilDivSIOp : Arith_IntBinaryOp<"ceildivsi",
+                                          [ConditionallySpeculatable]> {
   let summary = "signed ceil integer division operation";
   let description = [{
     Signed integer division. Rounds towards positive infinity, i.e. `7 / -2 = -3`.
@@ -381,6 +406,12 @@ def Arith_CeilDivSIOp : Arith_IntBinaryOp<"ceildivsi"> {
     %a = arith.ceildivsi %b, %c : i64
     ```
   }];
+
+  let extraClassDeclaration = [{
+    /// Interface method for ConditionallySpeculatable.
+    Speculation::Speculatability getSpeculatability();
+  }];
+
   let hasFolder = 1;
 }
 
@@ -388,7 +419,7 @@ def Arith_CeilDivSIOp : Arith_IntBinaryOp<"ceildivsi"> {
 // FloorDivSIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_FloorDivSIOp : Arith_IntBinaryOp<"floordivsi"> {
+def Arith_FloorDivSIOp : Arith_TotalIntBinaryOp<"floordivsi"> {
   let summary = "signed floor integer division operation";
   let description = [{
     Signed integer division. Rounds towards negative infinity, i.e. `5 / -2 = -3`.
@@ -411,7 +442,7 @@ def Arith_FloorDivSIOp : Arith_IntBinaryOp<"floordivsi"> {
 // RemUIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_RemUIOp : Arith_IntBinaryOp<"remui"> {
+def Arith_RemUIOp : Arith_TotalIntBinaryOp<"remui"> {
   let summary = "unsigned integer division remainder operation";
   let description = [{
     Unsigned integer division remainder. Treats the leading bit as the most
@@ -440,7 +471,7 @@ def Arith_RemUIOp : Arith_IntBinaryOp<"remui"> {
 // RemSIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_RemSIOp : Arith_IntBinaryOp<"remsi"> {
+def Arith_RemSIOp : Arith_TotalIntBinaryOp<"remsi"> {
   let summary = "signed integer division remainder operation";
   let description = [{
     Signed integer division remainder. Treats the leading bit as sign, i.e. `6 %
@@ -469,7 +500,7 @@ def Arith_RemSIOp : Arith_IntBinaryOp<"remsi"> {
 // AndIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_AndIOp : Arith_IntBinaryOp<"andi", [Commutative, Idempotent]> {
+def Arith_AndIOp : Arith_TotalIntBinaryOp<"andi", [Commutative, Idempotent]> {
   let summary = "integer binary and";
   let description = [{
     The `andi` operation takes two operands and returns one result, each of
@@ -498,7 +529,7 @@ def Arith_AndIOp : Arith_IntBinaryOp<"andi", [Commutative, Idempotent]> {
 // OrIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_OrIOp : Arith_IntBinaryOp<"ori", [Commutative, Idempotent]> {
+def Arith_OrIOp : Arith_TotalIntBinaryOp<"ori", [Commutative, Idempotent]> {
   let summary = "integer binary or";
   let description = [{
     The `ori` operation takes two operands and returns one result, each of these
@@ -527,7 +558,7 @@ def Arith_OrIOp : Arith_IntBinaryOp<"ori", [Commutative, Idempotent]> {
 // XOrIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_XOrIOp : Arith_IntBinaryOp<"xori", [Commutative]> {
+def Arith_XOrIOp : Arith_TotalIntBinaryOp<"xori", [Commutative]> {
   let summary = "integer binary xor";
   let description = [{
     The `xori` operation takes two operands and returns one result, each of
@@ -556,7 +587,7 @@ def Arith_XOrIOp : Arith_IntBinaryOp<"xori", [Commutative]> {
 // ShLIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_ShLIOp : Arith_IntBinaryOp<"shli"> {
+def Arith_ShLIOp : Arith_TotalIntBinaryOp<"shli"> {
   let summary = "integer left-shift";
   let description = [{
     The `shli` operation shifts an integer value to the left by a variable
@@ -577,7 +608,7 @@ def Arith_ShLIOp : Arith_IntBinaryOp<"shli"> {
 // ShRUIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_ShRUIOp : Arith_IntBinaryOp<"shrui"> {
+def Arith_ShRUIOp : Arith_TotalIntBinaryOp<"shrui"> {
   let summary = "unsigned integer right-shift";
   let description = [{
     The `shrui` operation shifts an integer value to the right by a variable
@@ -599,7 +630,7 @@ def Arith_ShRUIOp : Arith_IntBinaryOp<"shrui"> {
 // ShRSIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_ShRSIOp : Arith_IntBinaryOp<"shrsi"> {
+def Arith_ShRSIOp : Arith_TotalIntBinaryOp<"shrsi"> {
   let summary = "signed integer right-shift";
   let description = [{
     The `shrsi` operation shifts an integer value to the right by a variable
@@ -740,7 +771,7 @@ def Arith_MaxFOp : Arith_FloatBinaryOp<"maxf", [Commutative]> {
 // MaxSIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_MaxSIOp : Arith_IntBinaryOp<"maxsi", [Commutative]> {
+def Arith_MaxSIOp : Arith_TotalIntBinaryOp<"maxsi", [Commutative]> {
   let summary = "signed integer maximum operation";
   let hasFolder = 1;
 }
@@ -749,7 +780,7 @@ def Arith_MaxSIOp : Arith_IntBinaryOp<"maxsi", [Commutative]> {
 // MaxUIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_MaxUIOp : Arith_IntBinaryOp<"maxui", [Commutative]> {
+def Arith_MaxUIOp : Arith_TotalIntBinaryOp<"maxui", [Commutative]> {
   let summary = "unsigned integer maximum operation";
   let hasFolder = 1;
 }
@@ -784,7 +815,7 @@ def Arith_MinFOp : Arith_FloatBinaryOp<"minf", [Commutative]> {
 // MinSIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_MinSIOp : Arith_IntBinaryOp<"minsi", [Commutative]> {
+def Arith_MinSIOp : Arith_TotalIntBinaryOp<"minsi", [Commutative]> {
   let summary = "signed integer minimum operation";
   let hasFolder = 1;
 }
@@ -793,7 +824,7 @@ def Arith_MinSIOp : Arith_IntBinaryOp<"minsi", [Commutative]> {
 // MinUIOp
 //===----------------------------------------------------------------------===//
 
-def Arith_MinUIOp : Arith_IntBinaryOp<"minui", [Commutative]> {
+def Arith_MinUIOp : Arith_TotalIntBinaryOp<"minui", [Commutative]> {
   let summary = "unsigned integer minimum operation";
   let hasFolder = 1;
 }
@@ -1250,7 +1281,7 @@ def Arith_CmpFOp : Arith_CompareOp<"cmpf"> {
 // SelectOp
 //===----------------------------------------------------------------------===//
 
-def SelectOp : Arith_Op<"select", [
+def SelectOp : Arith_Op<"select", [Pure,
     AllTypesMatch<["true_value", "false_value", "result"]>,
     DeclareOpInterfaceMethods<InferIntRangeInterface>,
   ] # ElementwiseMappable.traits> {
index 2c0fc51..eaf8b31 100644 (file)
@@ -367,6 +367,12 @@ OpFoldResult arith::DivUIOp::fold(ArrayRef<Attribute> operands) {
   return div0 ? Attribute() : result;
 }
 
+Speculation::Speculatability arith::DivUIOp::getSpeculatability() {
+  // X / 0 => UB
+  return matchPattern(getRhs(), m_NonZero()) ? Speculation::Speculatable
+                                             : Speculation::NotSpeculatable;
+}
+
 //===----------------------------------------------------------------------===//
 // DivSIOp
 //===----------------------------------------------------------------------===//
@@ -390,6 +396,18 @@ OpFoldResult arith::DivSIOp::fold(ArrayRef<Attribute> operands) {
   return overflowOrDiv0 ? Attribute() : result;
 }
 
+Speculation::Speculatability arith::DivSIOp::getSpeculatability() {
+  bool mayHaveUB = true;
+
+  APInt constRHS;
+  // X / 0 => UB
+  // INT_MIN / -1 => UB
+  if (matchPattern(getRhs(), m_ConstantInt(&constRHS)))
+    mayHaveUB = constRHS.isAllOnes() || constRHS.isZero();
+
+  return mayHaveUB ? Speculation::NotSpeculatable : Speculation::Speculatable;
+}
+
 //===----------------------------------------------------------------------===//
 // Ceil and floor division folding helpers
 //===----------------------------------------------------------------------===//
@@ -428,6 +446,12 @@ OpFoldResult arith::CeilDivUIOp::fold(ArrayRef<Attribute> operands) {
   return overflowOrDiv0 ? Attribute() : result;
 }
 
+Speculation::Speculatability arith::CeilDivUIOp::getSpeculatability() {
+  // X / 0 => UB
+  return matchPattern(getRhs(), m_NonZero()) ? Speculation::Speculatable
+                                             : Speculation::NotSpeculatable;
+}
+
 //===----------------------------------------------------------------------===//
 // CeilDivSIOp
 //===----------------------------------------------------------------------===//
@@ -477,6 +501,18 @@ OpFoldResult arith::CeilDivSIOp::fold(ArrayRef<Attribute> operands) {
   return overflowOrDiv0 ? Attribute() : result;
 }
 
+Speculation::Speculatability arith::CeilDivSIOp::getSpeculatability() {
+  bool mayHaveUB = true;
+
+  APInt constRHS;
+  // X / 0 => UB
+  // INT_MIN / -1 => UB
+  if (matchPattern(getRhs(), m_ConstantInt(&constRHS)))
+    mayHaveUB = constRHS.isAllOnes() || constRHS.isZero();
+
+  return mayHaveUB ? Speculation::NotSpeculatable : Speculation::Speculatable;
+}
+
 //===----------------------------------------------------------------------===//
 // FloorDivSIOp
 //===----------------------------------------------------------------------===//
index 9f5d9a5..4d2b5b7 100644 (file)
@@ -696,3 +696,181 @@ func.func @speculate_memref_dim_known_rank_known_dim_inbounds(
 
   return
 }
+
+// -----
+
+func.func @no_speculate_divui(
+// CHECK-LABEL: @no_speculate_divui(
+    %num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
+  scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: arith.divui
+    %val = arith.divui %num, %denom : i32
+  }
+
+  return
+}
+
+func.func @no_speculate_divsi(
+// CHECK-LABEL: @no_speculate_divsi(
+    %num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
+  scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: arith.divsi
+    %val = arith.divsi %num, %denom : i32
+  }
+
+  return
+}
+
+func.func @no_speculate_ceildivui(
+// CHECK-LABEL: @no_speculate_ceildivui(
+    %num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
+  scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: arith.ceildivui
+    %val = arith.ceildivui %num, %denom : i32
+  }
+
+  return
+}
+
+func.func @no_speculate_ceildivsi(
+// CHECK-LABEL: @no_speculate_ceildivsi(
+    %num: i32, %denom: i32, %lb: index, %ub: index, %step: index) {
+  scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: arith.ceildivsi
+    %val = arith.ceildivsi %num, %denom : i32
+  }
+
+  return
+}
+
+func.func @no_speculate_divui_const(%num: i32, %lb: index, %ub: index, %step: index) {
+// CHECK-LABEL: @no_speculate_divui_const(
+  %c0 = arith.constant 0 : i32
+  scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: arith.divui
+    %val = arith.divui %num, %c0 : i32
+  }
+
+  return
+}
+
+func.func @speculate_divui_const(
+// CHECK-LABEL: @speculate_divui_const(
+    %num: i32, %lb: index, %ub: index, %step: index) {
+  %c5 = arith.constant 5 : i32
+// CHECK: arith.divui
+// CHECK: scf.for
+  scf.for %i = %lb to %ub step %step {
+    %val = arith.divui %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
+  scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: arith.ceildivui
+    %val = arith.ceildivui %num, %c0 : i32
+  }
+
+  return
+}
+
+func.func @speculate_ceildivui_const(
+// CHECK-LABEL: @speculate_ceildivui_const(
+    %num: i32, %lb: index, %ub: index, %step: index) {
+  %c5 = arith.constant 5 : i32
+// CHECK: arith.ceildivui
+// CHECK: scf.for
+  scf.for %i = %lb to %ub step %step {
+    %val = arith.ceildivui %num, %c5 : i32
+  }
+
+  return
+}
+
+func.func @no_speculate_divsi_const0(
+// CHECK-LABEL: @no_speculate_divsi_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: arith.divsi
+    %val = arith.divsi %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) {
+  %cm1 = arith.constant -1 : i32
+  scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: arith.divsi
+    %val = arith.divsi %num, %cm1 : i32
+  }
+
+  return
+}
+
+func.func @speculate_divsi_const(
+// CHECK-LABEL: @speculate_divsi_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: arith.divsi
+// CHECK: scf.for
+    %val = arith.divsi %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) {
+  %c0 = arith.constant 0 : i32
+  scf.for %i = %lb to %ub step %step {
+// CHECK: scf.for
+// CHECK: arith.ceildivsi
+    %val = arith.ceildivsi %num, %c0 : i32
+  }
+
+  return
+}
+
+func.func @no_speculate_ceildivsi_const_minus1(
+// CHECK-LABEL: @no_speculate_ceildivsi_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: arith.ceildivsi
+    %val = arith.ceildivsi %num, %cm1 : i32
+  }
+
+  return
+}
+
+func.func @speculate_ceildivsi_const(
+// CHECK-LABEL: @speculate_ceildivsi_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: arith.ceildivsi
+// CHECK: scf.for
+    %val = arith.ceildivsi %num, %c5 : i32
+  }
+
+  return
+}