[llvm-commits] [126124] CEIL_DIV_EXPR performs integer division with rounding

clattner at apple.com clattner at apple.com
Sat Apr 14 12:42:51 PDT 2007


Revision: 126124
Author:   clattner
Date:     2007-04-14 12:42:51 -0700 (Sat, 14 Apr 2007)

Log Message:
-----------
CEIL_DIV_EXPR performs integer division with rounding
towards positive infinity, thus 3 cdiv 2 = 2 while
-3 cdiv 2 = -1.  Testing is tricky, because CEIL_DIV_EXPR
is only generated in unusual circumstances, giving little
coverage, so I modified the llvm- and mainline gcc compilers
to output CEIL_DIV_EXPR for normal division, and compared
the results for all possible signed and unsigned i8 values
for the left- and right-hand sides.

Patch by Duncan Sands.

Modified Paths:
--------------
    apple-local/branches/llvm/gcc/llvm-convert.cpp
    apple-local/branches/llvm/gcc/llvm-internal.h

Modified: apple-local/branches/llvm/gcc/llvm-convert.cpp
===================================================================
--- apple-local/branches/llvm/gcc/llvm-convert.cpp	2007-04-14 18:14:58 UTC (rev 126123)
+++ apple-local/branches/llvm/gcc/llvm-convert.cpp	2007-04-14 19:42:51 UTC (rev 126124)
@@ -779,6 +779,7 @@
       Result = EmitBinOp(exp, DestLoc, Instruction::SDiv);
     break;
   case RDIV_EXPR: Result = EmitBinOp(exp, DestLoc, Instruction::FDiv); break;
+  case CEIL_DIV_EXPR: Result = EmitCEIL_DIV_EXPR(exp); break;
   case ROUND_DIV_EXPR: Result = EmitROUND_DIV_EXPR(exp); break;
   case TRUNC_MOD_EXPR: 
     if (TYPE_UNSIGNED(TREE_TYPE(exp)))
@@ -3200,6 +3201,82 @@
   return new SelectInst(SameAsRem, Rem, RemPlusRHS, "mod", CurBB);
 }
 
+Value *TreeToLLVM::EmitCEIL_DIV_EXPR(tree exp) {
+  // Notation: CEIL_DIV_EXPR <-> CDiv, TRUNC_DIV_EXPR <-> Div.
+
+  // CDiv calculates LHS/RHS by rounding up to the nearest integer.  In terms
+  // of Div this means if the values of LHS and RHS have opposite signs or if
+  // LHS is zero, then CDiv necessarily equals Div; and
+  //   LHS CDiv RHS = (LHS - Sign(RHS)) Div RHS + 1
+  // otherwise.
+
+  const Type *Ty = ConvertType(TREE_TYPE(exp));
+  Constant *Zero = ConstantInt::get(Ty, 0);
+  Constant *One = ConstantInt::get(Ty, 1);
+  Constant *MinusOne = ConstantInt::get(Ty, -1);
+
+  Value *LHS = Emit(TREE_OPERAND(exp, 0), 0);
+  Value *RHS = Emit(TREE_OPERAND(exp, 1), 0);
+
+  if (!TYPE_UNSIGNED(TREE_TYPE(exp))) {
+    // In the case of signed arithmetic, we calculate CDiv as follows:
+    //   LHS CDiv RHS = (LHS - Sign(RHS) * Offset) Div RHS + Offset,
+    // where Offset is 1 if LHS and RHS have the same sign and LHS is
+    // not zero, and 0 otherwise.
+
+    // On some machines INT_MIN Div -1 traps.  You might expect a trap for
+    // INT_MIN CDiv -1 too, but this implementation will not generate one.
+    // Quick quiz question: what value is returned for INT_MIN CDiv -1?
+
+    // Determine the signs of LHS and RHS, and whether they have the same sign.
+    Value *LHSIsPositive = new ICmpInst(ICmpInst::ICMP_SGE, LHS, Zero, "tmp",
+                                        CurBB);
+    Value *RHSIsPositive = new ICmpInst(ICmpInst::ICMP_SGE, RHS, Zero, "tmp",
+                                        CurBB);
+    Value *HaveSameSign = new ICmpInst(ICmpInst::ICMP_EQ, LHSIsPositive,
+                                       RHSIsPositive, "tmp", CurBB);
+
+    // Offset equals 1 if LHS and RHS have the same sign and LHS is not zero ...
+    Value *LHSNotZero = new ICmpInst(ICmpInst::ICMP_NE, LHS, Zero, "tmp",
+                                     CurBB);
+    Value *OffsetOne = BinaryOperator::create(Instruction::And, HaveSameSign,
+                                              LHSNotZero, "tmp", CurBB);
+    // ... otherwise it is 0.
+    Value *Offset = new SelectInst(OffsetOne, One, Zero, "tmp", CurBB);
+
+    // Calculate Sign(RHS) ...
+    Value *SignRHS = new SelectInst(RHSIsPositive, One, MinusOne, "tmp", CurBB);
+    // ... and Sign(RHS) * Offset
+    Value *SignedOffset = CastToType(Instruction::SExt, OffsetOne, Ty);
+    SignedOffset = BinaryOperator::create(Instruction::And, SignRHS,
+                                          SignedOffset, "tmp", CurBB);
+
+    // Return CDiv = (LHS - Sign(RHS) * Offset) Div RHS + Offset.
+    Value *CDiv = BinaryOperator::create(Instruction::Sub, LHS, SignedOffset,
+                                         "tmp", CurBB);
+    CDiv = BinaryOperator::create(Instruction::SDiv, CDiv, RHS, "tmp", CurBB);
+    return BinaryOperator::create(Instruction::Add, CDiv, Offset, "cdiv",
+                                  CurBB);
+  } else {
+    // In the case of unsigned arithmetic, LHS and RHS necessarily have the
+    // same sign, so we can use
+    //   LHS CDiv RHS = (LHS - 1) Div RHS + 1
+    // as long as LHS is non-zero.
+
+    // Offset is 1 if LHS is non-zero, 0 otherwise.
+    Value *LHSNotZero = new ICmpInst(ICmpInst::ICMP_NE, LHS, Zero, "tmp",
+                                     CurBB);
+    Value *Offset = new SelectInst(LHSNotZero, One, Zero, "tmp", CurBB);
+
+    // Return CDiv = (LHS - Offset) Div RHS + Offset.
+    Value *CDiv = BinaryOperator::create(Instruction::Sub, LHS, Offset, "tmp",
+                                         CurBB);
+    CDiv = BinaryOperator::create(Instruction::UDiv, CDiv, RHS, "tmp", CurBB);
+    return BinaryOperator::create(Instruction::Add, CDiv, Offset, "cdiv",
+                                  CurBB);
+  }
+}
+
 Value *TreeToLLVM::EmitROUND_DIV_EXPR(tree exp) {
   // Notation: ROUND_DIV_EXPR <-> RDiv, TRUNC_DIV_EXPR <-> Div.
   

Modified: apple-local/branches/llvm/gcc/llvm-internal.h
===================================================================
--- apple-local/branches/llvm/gcc/llvm-internal.h	2007-04-14 18:14:58 UTC (rev 126123)
+++ apple-local/branches/llvm/gcc/llvm-internal.h	2007-04-14 19:42:51 UTC (rev 126124)
@@ -466,6 +466,7 @@
   Value *EmitMinMaxExpr(tree_node *exp, unsigned UIPred, unsigned SIPred, 
                         unsigned Opc);
   Value *EmitFLOOR_MOD_EXPR(tree_node *exp, Value *DestLoc);
+  Value *EmitCEIL_DIV_EXPR(tree_node *exp);
   Value *EmitROUND_DIV_EXPR(tree_node *exp);
 
   // Inline Assembly and Register Variables.





More information about the llvm-commits mailing list