[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