[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