[llvm-commits] llvm-gcc4: implement FLOOR_MOD_EXPR and ROUND_DIV_EXPR
Duncan Sands
baldrick at free.fr
Fri Jan 19 05:09:05 PST 2007
FLOOR_MOD_EXPR is generated by the Ada and Fortran front-ends.
ROUND_DIV_EXPR is only generated by the Ada front-end.
Tested by taking all possible combinations of 8 bit signed and unsigned
input operands, and checking that the results coincide with those produced
by mainline gcc. I also checked by hand the output from taking all possible
3 bit inputs. I also checked that results agree with mainline gcc for a
range of 32 bit inputs, including extreme large and small values. I would
love to provide testcases, but this is not so easy. For FLOOR_MOD_EXPR, my
testcases are written in Ada since there seems to be no way of getting any
of the C-like front-ends to produce FLOOR_MOD_EXPR. It would be nice to have
an Ada language LLVM testsuite, but it's too early for that: the Ada front-end
only builds partially right now, and I had to do evil tricks to get working
programs out of the testcases. The situation for ROUND_DIV_EXPR is even worse:
the Ada front-end only produces this expression in special circumstances, making
it hard to get decent coverage. I ended up hacking the compiler to produce
ROUND_DIV_EXPR instead of TRUNC_DIV_EXPR (= normal integer division). With
this change a testcase can be written in C. But I somehow have the feeling
that you don't want testcases that require being built with a specially modified
compiler!
Index: gcc/llvm-convert.cpp
===================================================================
--- gcc/llvm-convert.cpp (revision 250)
+++ gcc/llvm-convert.cpp (working copy)
@@ -609,12 +609,18 @@
case RDIV_EXPR:
Result = EmitBinOp(exp, DestLoc, Instruction::FDiv);
break;
+ case ROUND_DIV_EXPR:
+ Result = EmitROUND_DIV_EXPR(exp);
+ break;
case TRUNC_MOD_EXPR:
if (TYPE_UNSIGNED(TREE_TYPE(exp)))
Result = EmitBinOp(exp, DestLoc, Instruction::URem);
else
Result = EmitBinOp(exp, DestLoc, Instruction::SRem);
break;
+ case FLOOR_MOD_EXPR:
+ Result = EmitFLOOR_MOD_EXPR(exp, DestLoc);
+ break;
case BIT_AND_EXPR: Result = EmitBinOp(exp, DestLoc, Instruction::And);break;
case BIT_IOR_EXPR: Result = EmitBinOp(exp, DestLoc, Instruction::Or );break;
case BIT_XOR_EXPR: Result = EmitBinOp(exp, DestLoc, Instruction::Xor);break;
@@ -2590,6 +2607,143 @@
TREE_CODE(exp) == MAX_EXPR ? "max" : "min", CurBB);
}
+Value *TreeToLLVM::EmitFLOOR_MOD_EXPR(tree exp, Value *DestLoc) {
+ // Notation: FLOOR_MOD_EXPR <-> Mod, TRUNC_MOD_EXPR <-> Rem.
+
+ // We express Mod in terms of Rem as follows: if RHS exactly divides LHS,
+ // or the values of LHS and RHS have the same sign, then Mod equals Rem.
+ // Otherwise Mod equals Rem + RHS. This means that LHS Mod RHS traps iff
+ // LHS Rem RHS traps.
+
+ if (TYPE_UNSIGNED(TREE_TYPE(exp)))
+ // LHS and RHS values must have the same sign if their type is unsigned.
+ return EmitBinOp(exp, DestLoc, Instruction::URem);
+
+ const Type *Ty = ConvertType(TREE_TYPE(exp));
+ Constant *Zero = ConstantInt::get(Ty, 0);
+
+ Value *LHS = Emit(TREE_OPERAND(exp, 0), 0);
+ Value *RHS = Emit(TREE_OPERAND(exp, 1), 0);
+
+ // The two possible values for Mod.
+ Value *Rem = BinaryOperator::create(Instruction::SRem, LHS, RHS, "rem",
+ CurBB);
+ Value *RemPlusRHS = BinaryOperator::create(Instruction::Add, Rem, RHS, "tmp",
+ CurBB);
+
+ // HaveSameSign: (LHS >= 0) == (RHS >= 0).
+ 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);
+
+ // RHS exactly divides LHS iff Rem is zero.
+ Value *RemIsZero = new ICmpInst(ICmpInst::ICMP_EQ, Rem, Zero, "tmp", CurBB);
+
+ Value *SameAsRem = BinaryOperator::create(Instruction::Or, HaveSameSign,
+ RemIsZero, "tmp", CurBB);
+ return new SelectInst(SameAsRem, Rem, RemPlusRHS, "mod", CurBB);
+}
+
+Value *TreeToLLVM::EmitROUND_DIV_EXPR(tree exp) {
+ // Notation: ROUND_DIV_EXPR <-> RDiv, TRUNC_DIV_EXPR <-> Div.
+
+ // RDiv calculates LHS/RHS by rounding to the nearest integer. Ties
+ // are broken by rounding away from zero. In terms of Div this means:
+ // LHS RDiv RHS = (LHS + (RHS Div 2)) Div RHS
+ // if the values of LHS and RHS have the same sign; and
+ // LHS RDiv RHS = (LHS - (RHS Div 2)) Div RHS
+ // if the values of LHS and RHS differ in sign. The intermediate
+ // expressions in these formulae can overflow, so some tweaking is
+ // required to ensure correct results. The details depend on whether
+ // we are doing signed or unsigned arithmetic.
+
+ const Type *Ty = ConvertType(TREE_TYPE(exp));
+ Constant *Zero = ConstantInt::get(Ty, 0);
+ Constant *Two = ConstantInt::get(Ty, 2);
+
+ 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 RDiv as follows:
+ // LHS RDiv RHS = (sign) ( (|LHS| + (|RHS| UDiv 2)) UDiv |RHS| ),
+ // where sign is +1 if LHS and RHS have the same sign, -1 if their
+ // signs differ. Doing the computation unsigned ensures that there
+ // is no overflow.
+
+ // On some machines INT_MIN Div -1 traps. You might expect a trap for
+ // INT_MIN RDiv -1 too, but this implementation will not generate one.
+ // Quick quiz question: what value is returned for INT_MIN RDiv -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);
+
+ // Calculate |LHS| ...
+ Value *MinusLHS = BinaryOperator::createNeg(LHS, "tmp", CurBB);
+ Value *AbsLHS = new SelectInst(LHSIsPositive, LHS, MinusLHS, "abs_" +
+ LHS->getName(), CurBB);
+ // ... and |RHS|
+ Value *MinusRHS = BinaryOperator::createNeg(RHS, "tmp", CurBB);
+ Value *AbsRHS = new SelectInst(RHSIsPositive, RHS, MinusRHS, "abs_" +
+ RHS->getName(), CurBB);
+
+ // Calculate AbsRDiv = (|LHS| + (|RHS| UDiv 2)) UDiv |RHS|.
+ Value *HalfAbsRHS = BinaryOperator::create(Instruction::UDiv, AbsRHS, Two,
+ "tmp", CurBB);
+ Value *Numerator = BinaryOperator::create(Instruction::Add, AbsLHS,
+ HalfAbsRHS, "tmp", CurBB);
+ Value *AbsRDiv = BinaryOperator::create(Instruction::UDiv, Numerator,
+ AbsRHS, "tmp", CurBB);
+
+ // Return AbsRDiv or -AbsRDiv according to whether LHS and RHS have the
+ // same sign or not.
+ Value *MinusAbsRDiv = BinaryOperator::createNeg(AbsRDiv, "tmp", CurBB);
+ return new SelectInst(HaveSameSign, AbsRDiv, MinusAbsRDiv, "rdiv", CurBB);
+ } else {
+ // In the case of unsigned arithmetic, LHS and RHS necessarily have the
+ // same sign, however overflow is a problem. We want to use the formula
+ // LHS RDiv RHS = (LHS + (RHS Div 2)) Div RHS,
+ // but if LHS + (RHS Div 2) overflows then we get the wrong result. Since
+ // the use of a conditional branch seems to be unavoidable, we choose the
+ // simple solution of explicitly checking for overflow, and using
+ // LHS RDiv RHS = ((LHS + (RHS Div 2)) - RHS) Div RHS + 1
+ // if it occurred.
+
+ // Usually the numerator is LHS + (RHS Div 2); calculate this.
+ Value *HalfRHS = BinaryOperator::create(Instruction::UDiv, RHS, Two, "tmp",
+ CurBB);
+ Value *Numerator = BinaryOperator::create(Instruction::Add, LHS, HalfRHS,
+ "tmp", CurBB);
+
+ // Did the calculation overflow?
+ Value *Overflowed = new ICmpInst(ICmpInst::ICMP_ULT, Numerator, HalfRHS,
+ "tmp", CurBB);
+
+ // If so, use (LHS + (RHS Div 2)) - RHS for the numerator instead.
+ Value *AltNumerator = BinaryOperator::create(Instruction::Sub, Numerator,
+ RHS, "tmp", CurBB);
+ Numerator = new SelectInst(Overflowed, AltNumerator, Numerator, "tmp",
+ CurBB);
+
+ // Quotient = Numerator / RHS.
+ Value *Quotient = BinaryOperator::create(Instruction::UDiv, Numerator, RHS,
+ "tmp", CurBB);
+
+ // Return Quotient unless we overflowed, in which case return Quotient + 1.
+ return BinaryOperator::create(Instruction::Add, Quotient,
+ CastToUIntType(Overflowed, Ty), "rdiv",
+ CurBB);
+ }
+}
+
//===----------------------------------------------------------------------===//
// ... Inline Assembly and Register Variables ...
//===----------------------------------------------------------------------===//
Index: gcc/llvm-internal.h
===================================================================
--- gcc/llvm-internal.h (revision 250)
+++ gcc/llvm-internal.h (working copy)
@@ -408,6 +408,8 @@
Value *EmitRotateOp(tree_node *exp, unsigned Opc1, unsigned Opc2);
Value *EmitMinMaxExpr(tree_node *exp, unsigned UIPred, unsigned SIPred,
unsigned Opc);
+ Value *EmitFLOOR_MOD_EXPR(tree_node *exp, Value *DestLoc);
+ Value *EmitROUND_DIV_EXPR(tree_node *exp);
// Inline Assembly and Register Variables.
Value *EmitASM_EXPR(tree_node *exp);
More information about the llvm-commits
mailing list