[llvm-commits] DIV -> U/S/FDiv Patch For Review

Reid Spencer rspencer at reidspencer.com
Tue Oct 24 10:10:25 PDT 2006


Chris,

Thanks for your detailed review.  Obviously there's still much I need to
learn about InstCombine.  I'll try to slow down a bit and review things
myself on the next patches.

Below are some comments on your comments. I've made all the changes
except for InstCombine. That will take a little more thought so I'll
follow up on InstCombine separately.

On Mon, 2006-10-23 at 22:52 -0700, Chris Lattner wrote:
> 
> On Oct 22, 2006, at 7:50 PM, Reid Spencer wrote:
> 
> > Attached is a patch to implement splitting the signless DIV
> > instruction
> > into UDiv (unsigned), SDiv (signed), and FDiv (floating point)
> > instructions. This is part of the Signless Types work I'm doing.
> 
> Overall, this patch looks like a great first step, but it has
> significant flaws.  In particular, the patch seems unable to produce a
> [su]div instruction with operands of the opposite sign from that of
> the instruction.  

Well, I didn't think it was a requirement. However, I can see why it is
because of the pending removal of signedness from types.

> Further, handling of this (very important) case is seriously buggy in
> many cases.

I will look into it.

> I strongly recommend adding a transformation to instcombine that will
> produce more divs with such operands.  For example, changing something
> like:
> 
> 
>   %X = cast uint %A to int
>   %Y = sdiv int %X, 1234
>   %Z = cast int %Y to uint
> 
> 
> into:
> 
> 
>    %Z = sdiv uint %A, 1234
> 
> 
> Fortunately, instcombine already does this for other operations
> (mul/add/and/or/xor, etc) at line 5658 of Instcombine in CVS.  Please
> add a case there for UDIV/SDIV.  This will hopefully help flush out
> bugs in the patch that I didn't catch.

My goal is not to add any new functionality and make the smallest change
set possible to get existing functionality working with the the new
instructions.  However, I see your point. This transform could help
locate bugs elsewhere. I'll add this transform.

> When you get the comments in this email address and things retested,
> please resend the patch for review.

Okay.

> Comments preceded with ***'s
> 
> 
> --- include/llvm/Instruction.def 8 Apr 2006 01:15:18 -0000 1.19
> +++ include/llvm/Instruction.def 23 Oct 2006 02:43:18 -0000
> @@ -88,52 +88,50 @@ HANDLE_TERM_INST  ( 5, Unwind     , Unwi
> ...
> 
> 
> *** Why not reserve some space for all the new instructions you plan
> to add?  That way the CVS bc format won't be changing as much.
> 
> 
> -HANDLE_BINARY_INST( 7, Add   , BinaryOperator)
> -HANDLE_BINARY_INST( 8, Sub   , BinaryOperator)
> -HANDLE_BINARY_INST( 9, Mul   , BinaryOperator)
> +HANDLE_BINARY_INST( 7, Add  , BinaryOperator)
> +HANDLE_BINARY_INST( 8, Sub  , BinaryOperator)
> +HANDLE_BINARY_INST( 9, Mul  , BinaryOperator)
> 
> 
> *** Why remove the space?
> 
> 
> --- lib/Analysis/ScalarEvolution.cpp 20 Oct 2006 07:07:24 -0000 1.54
> +++ lib/Analysis/ScalarEvolution.cpp 23 Oct 2006 02:43:20 -0000
> @@ -1382,12 +1382,12 @@ SCEVHandle ScalarEvolutionsImpl::createS
>        return SCEVAddExpr::get(getSCEV(I->getOperand(0)),
>                                getSCEV(I->getOperand(1)));
>      case Instruction::Mul:
>        return SCEVMulExpr::get(getSCEV(I->getOperand(0)),
>                                getSCEV(I->getOperand(1)));
> -    case Instruction::Div:
> -      if (V->getType()->isInteger() && V->getType()->isSigned())
> +    case Instruction::SDiv:
> +      if (V->getType()->isInteger())
>          return SCEVSDivExpr::get(getSCEV(I->getOperand(0)),
>                                   getSCEV(I->getOperand(1)));
>        break;
> 
> 
> *** No need to check for isInteger anymore.

Done.

> 
> 
> ===================================================================
> RCS file: /var/cvs/llvm/llvm/lib/AsmParser/llvmAsmParser.y,v
> retrieving revision 1.269
> diff -t -d -u -p -5 -r1.269 llvmAsmParser.y
> --- lib/AsmParser/llvmAsmParser.y 22 Oct 2006 07:03:09 -0000 1.269
> +++ lib/AsmParser/llvmAsmParser.y 23 Oct 2006 02:43:24 -0000
> @@ -811,10 +811,41 @@ static PATypeHolder HandleUpRefs(const T
> +// This template function is used to obtain the correct opcode for
> an 
> +// instruction when an obsolete opcode is encountered. The OpcodeInfo
> template
> +// keeps track of the opcode and the "obsolete" flag. These are
> generated by
> +// the lexer and obsolete will be true when the lexer encounters the
> token for
> +// an obsolete opcode. For example, "div" was replaced by [usf]div
> but we need
> +// to maintain backwards compatibility for asm files that still have
> the "div"
> +// instruction. This function handles converting div -> [usf]div
> appropriately.
> +template <class EnumKind>
> +static void sanitizeOpCode(OpcodeInfo<EnumKind> &OI, const
> PATypeHolder& Ty) {
> +  if (OI.obsolete) {
> +    switch (OI.opcode) {
> +      default:
> +        GenerateError("Invalid Obsolete OpCode");
> +        break;
> +      case Instruction::UDiv:
> +        if (Ty->isFloatingPoint()) 
> +          OI.opcode = Instruction::FDiv;
> +        else if (Ty->isSigned())
> +          OI.opcode = Instruction::SDiv;
> +        break;
> +      case Instruction::SDiv:
> +        if (Ty->isFloatingPoint()) 
> +          OI.opcode = Instruction::FDiv;
> +        else if (Ty->isUnsigned())
> +          OI.opcode = Instruction::UDiv;
> +        break;
> ...
> 
> 
> *** I like your approach.  I don't think sanitizeOpCode should be a
> template though.  This specific one only works for binops, because
> that is what enum values it has. 
>  I'd just have one function for each class of upgraded op, no
> templates.  

I was designing for the future. Soon enough there will be a category of
instructions named ConvertOps which will contain the 10 conversion
instructions that will replace cast. I was thinking they could share the
same code, but I see what you mean. Each enum corresponds to a
particular group of case values so they might as well be separate
non-template functions.

Done.

> Also, the lexer can't return an "obsolete sdiv", it appears to only
> return udiv, so the sdiv case above seems dead.  Am I missing
> something?

Nope.  It was just a little defensive programming. When depending on
things in another file, I tend to be a bit more cautious. There's
nothing to stop someone from using the RET_TOK_OBSOLETE with SDiv in
Lexer.l. However its guarding against an obscure case that probably
won't happen in practice so I'll remove it.

>   Finally, the code would be simpler if you started it as "if (!
> OI.obsolete) return;"

Yup. Done.
> 
> 
> Index: lib/Bytecode/Reader/Reader.cpp
> ===================================================================
> RCS file: /var/cvs/llvm/llvm/lib/Bytecode/Reader/Reader.cpp,v
> retrieving revision 1.199
> diff -t -d -u -p -5 -r1.199 Reader.cpp
> --- lib/Bytecode/Reader/Reader.cpp 20 Oct 2006 07:07:24 -0000 1.199
> +++ lib/Bytecode/Reader/Reader.cpp 23 Oct 2006 02:43:26 -0000
> @@ -560,10 +560,239 @@ void BytecodeReader::insertArguments(Fun
> ...
> +  // Declare the resulting instruction we might build.
> +  Instruction *Result = 0;
> +
> +  // If this is a bytecode format that did not include the
> unreachable
> +  // instruction, bump up the opcode number to adjust
> +  if (hasNoUnreachableInst) {
> +    if (Opcode >= Instruction::Unreachable &&
> +        Opcode < 62) { // 62 
> +      ++Opcode;
> +    }
> +  }
> +
> +  // First, short circuit this if no conversion is required. When
> signless
> +  // instructions were implemented the entire opcode sequence was
> revised so
> +  // we key on this first which means that the opcode value read is
> the one
> +  // we should use.
> +  if (!hasSignlessInstructions)
> +    return Result;
> 
> 
> *** If hasSignlessInstructions is true, hasNoUnreachableInst is false,
> right? 

Theoretically, yes :)

>  If so, move the fast path above the hasNoUnreachableInst check?  

Yup. Done.

> It would be more clear to return 0 here explicitly and mention in the
> method comment that this returns null if no upgrading is needed.

Except that's not quite the case. The function can return 0 but alter
the OpCode argument.  I'll fix the comment to explain in more detail.
> 
> 
> +// Upgrade obsolte constant expression opcodes (ver. 5 and prior) to
> the new 
> 
> 
> *** Typo obsolete.

Done.
> 
> 
> 
> 
> +    // need to note that we have signed integer types in prior
> versions.
> +    hasSignedIntegers = true;
> +
> +    // FALL THROUGH
> +    
> +  case 6:               // SignlessTypes Implementation (1.10
> release)
> 
> 
> *** 1.9?

Yes. I wrote that code before we had our discussion about whether this
change set was going into the 1.9 release or not. I was assuming it was
not, but would make it into the next release, 1.10
> 
> 
> Index: lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
> ===================================================================
> RCS
> file: /var/cvs/llvm/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp,v
> retrieving revision 1.294
> diff -t -d -u -p -5 -r1.294 SelectionDAGISel.cpp
> --- lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp 22 Oct 2006 23:00:53
> -0000 1.294
> +++ lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp 23 Oct 2006 02:43:28
> -0000
> +void 
> +SelectionDAGLowering::visitIntBinary(User &I, unsigned IntOp,
> unsigned VecOp) {
>    const Type *Ty = I.getType();
>    SDOperand Op1 = getValue(I.getOperand(0));
>    SDOperand Op2 = getValue(I.getOperand(1));
>  
> 
>    if (Ty->isIntegral()) {
>      setValue(&I, DAG.getNode(IntOp, Op1.getValueType(), Op1, Op2));
> -  } else if (Ty->isFloatingPoint()) {
> +  } else {
> +    const PackedType *PTy = cast<PackedType>(Ty);
> +    SDOperand Num = DAG.getConstant(PTy->getNumElements(), MVT::i32);
> +    SDOperand Typ =
> DAG.getValueType(TLI.getValueType(PTy->getElementType()));
> +    setValue(&I, DAG.getNode(VecOp, MVT::Vector, Op1, Op2, Num,
> Typ));
> +  }
> +}
> 
> 
> *** In visitIntBinary/visitFPBinary please change the condition to "if
> (const PackedType *PTy = dyn_cast<PackedType>(Ty)) {", swapping the
> order of the if conditions.  Checking "isIntegral" is a non-obvious
> way to check that it's not a vector type (your code is correct, just
> not obvious what it does).

Okay. Done.
> 
> 
> --- lib/ExecutionEngine/Interpreter/Execution.cpp 20 Oct 2006 07:07:24
> -0000 1.140
> +++ lib/ExecutionEngine/Interpreter/Execution.cpp 23 Oct 2006 02:43:29
> -0000
> @@ -87,11 +87,13 @@ GenericValue Interpreter::getConstantExp
>                            CE->getOperand(0)->getType());
>    case Instruction::Mul:
>      return executeMulInst(getOperandValue(CE->getOperand(0), SF),
>                            getOperandValue(CE->getOperand(1), SF),
>                            CE->getOperand(0)->getType());
> -  case Instruction::Div:
> +  case Instruction::SDiv:
> +  case Instruction::UDiv:
> +  case Instruction::FDiv:
>      return executeDivInst(getOperandValue(CE->getOperand(0), SF),
>                            getOperandValue(CE->getOperand(1), SF),
>                            CE->getOperand(0)->getType());
>    case Instruction::Rem:
>      return executeRemInst(getOperandValue(CE->getOperand(0), SF),
> @@ -502,11 +504,13 @@ void Interpreter::visitBinaryOperator(Bi
>  
> 
>    switch (I.getOpcode()) {
>    case Instruction::Add:   R = executeAddInst  (Src1, Src2, Ty);
> break;
>    case Instruction::Sub:   R = executeSubInst  (Src1, Src2, Ty);
> break;
>    case Instruction::Mul:   R = executeMulInst  (Src1, Src2, Ty);
> break;
> -  case Instruction::Div:   R = executeDivInst  (Src1, Src2, Ty);
> break;
> +  case Instruction::SDiv:  
> +  case Instruction::UDiv:  
> +  case Instruction::FDiv:  R = executeDivInst  (Src1, Src2, Ty);
> break;
>    case Instruction::Rem:   R = executeRemInst  (Src1, Src2, Ty);
> break;
>    case Instruction::And:   R = executeAndInst  (Src1, Src2, Ty);
> break;
>    case Instruction::Or:    R = executeOrInst   (Src1, Src2, Ty);
> break;
>    case Instruction::Xor:   R = executeXorInst  (Src1, Src2, Ty);
> break;
>    case Instruction::SetEQ: R = executeSetEQInst(Src1, Src2, Ty);
> break;

> *** This is flat out incorrect.  It should have separate
> execute*DivInst functions that do the operation not based on the type.
> This would misinterpret 'udiv int -4, 2' for example. This must be
> fixed before you checkin.
> 
Another casualty of thinking the operand types had to match the
instruction type. I've created the three functions. The SDiv case now
looks like:

static GenericValue executeSDivInst(GenericValue Src1, GenericValue
Src2,
                                   const Type *Ty) {
  GenericValue Dest;
**  if (Ty->isUnsigned())
**    Ty = Ty->getSignedVersion();
  switch (Ty->getTypeID()) {
    IMPLEMENT_BINARY_OPERATOR(/, SByte);
    IMPLEMENT_BINARY_OPERATOR(/, Short);
    IMPLEMENT_BINARY_OPERATOR(/, Int);
    IMPLEMENT_BINARY_OPERATOR(/, Long);
  default:
    std::cout << "Unhandled type for SDiv instruction: " << *Ty << "\n";
    abort();
  }
  return Dest;
}

I've added the ** lines. and removed the floating point and unsigned
cases from the switch.  Basically, I'm forcing it to select the signed
member from the GenericValue union. Since I used getSignedVersion() the
only difference should be the sign (no size difference) and I shouldn't
need to do anything special for sign extension. If the SDiv is provided
unsigned values, they will be plucked out of the union as signed values.
I did a similar thing for UDiv.  For FDiv, nothing special is needed for
sign management but there are only two cases in the switch: Float and
Double.

> 
> --- lib/Target/CBackend/Writer.cpp 22 Oct 2006 09:58:21 -0000 1.274
> +++ lib/Target/CBackend/Writer.cpp 23 Oct 2006 02:43:30 -0000
> @@ -584,11 +584,13 @@ void CWriter::printConstant(Constant *CP
> @@ -603,11 +605,13 @@ void CWriter::printConstant(Constant *CP
>        printConstant(CE->getOperand(0));
>        switch (CE->getOpcode()) {
>        case Instruction::Add: Out << " + "; break;
>        case Instruction::Sub: Out << " - "; break;
>        case Instruction::Mul: Out << " * "; break;
> -      case Instruction::Div: Out << " / "; break;
> +      case Instruction::UDiv: 
> +      case Instruction::SDiv: 
> +      case Instruction::FDiv: Out << " / "; break;
>        case Instruction::Rem: Out << " % "; break;
>        case Instruction::And: Out << " & "; break;
>        case Instruction::Or:  Out << " | "; break;
>        case Instruction::Xor: Out << " ^ "; break;
>        case Instruction::SetEQ: Out << " == "; break;
> 
> 
> *** The CBE has the same problem.  You need to force the operands to
> the right sign in the C output so that the C compiler will generate
> the appropriately signed operation.  This must be fixed before you
> checkin.

Yes. Done. I added a writeOperandWithCast(Value*, unsigned opcode)
method to the CWriter. It writes the operand with the correct cast based
on the opcode the value is used with. I call this instead of
writeOperand(Value*) to write the operands for these binary operators
(just before and after the switch statement you quoted above).

> 
> --- lib/Transforms/IPO/SimplifyLibCalls.cpp 20 Oct 2006 07:07:24 -0000
> 1.70
> +++ lib/Transforms/IPO/SimplifyLibCalls.cpp 23 Oct 2006 02:43:30 -0000
> @@ -1273,11 +1273,11 @@ public:
>          ci->replaceAllUsesWith(base);
>          ci->eraseFromParent();
>          return true;
>        } else if (Op2V == -1.0) {
>          // pow(x,-1.0)    -> 1.0/x
> -        BinaryOperator* div_inst= BinaryOperator::createDiv(
> +        BinaryOperator* div_inst= BinaryOperator::createSDiv(
>            ConstantFP::get(Ty,1.0), base, ci->getName()+".pow", ci);
>          ci->replaceAllUsesWith(div_inst);
>          ci->eraseFromParent();
>          return true;
>        }
> 
> 
> *** Shouldn't this be fdiv?  Please fix before you checkin, and add a
> regression test.

Yes, I caught that one too after I sent the patches. 

> 
> ===================================================================
> RCS file: /var/cvs/llvm/llvm/lib/VMCore/ConstantFolding.cpp,v
> retrieving revision 1.94
> diff -t -d -u -p -5 -r1.94 ConstantFolding.cpp
> --- lib/VMCore/ConstantFolding.cpp 20 Oct 2006 07:07:24 -0000 1.94
> +++ lib/VMCore/ConstantFolding.cpp 23 Oct 2006 02:43:35 -0000
> @@ -491,20 +507,32 @@ struct VISIBILITY_HIDDEN DirectIntRules
>    DEF_CAST(ULong , ConstantInt, uint64_t)
>    DEF_CAST(Float , ConstantFP , float)
>    DEF_CAST(Double, ConstantFP , double)
>  #undef DEF_CAST
>  
> 
> -  static Constant *Div(const ConstantInt *V1, const ConstantInt *V2)
> {
> -    if (V2->isNullValue()) return 0;
> +  static Constant *UDiv(const ConstantInt *V1, const ConstantInt *V2)
> {
> +    if (V2->isNullValue()) 
> +      return 0;
>      if (V2->isAllOnesValue() &&              // MIN_INT / -1
>          (BuiltinType)V1->getZExtValue() ==
> -(BuiltinType)V1->getZExtValue())
>        return 0;
>      BuiltinType R = 
>        (BuiltinType)V1->getZExtValue() /
> (BuiltinType)V2->getZExtValue();
>      return ConstantInt::get(*Ty, R);
>    }
>  
> 
> +  static Constant *SDiv(const ConstantInt *V1, const ConstantInt *V2)
> {
> +    if (V2->isNullValue()) 
> +      return 0;
> +    if (V2->isAllOnesValue() &&              // MIN_INT / -1
> +        (BuiltinType)V1->getSExtValue() ==
> -(BuiltinType)V1->getSExtValue())
> +      return 0;
> +    BuiltinType R = 
> +      (BuiltinType)V1->getSExtValue() /
> (BuiltinType)V2->getSExtValue();
> +    return ConstantInt::get(*Ty, R);
> +  }
> +
> 
> 
> You properly sign/zero extend the values here, but then proceed to do
> the wrong division.  For example, this will do signed division for
> 'udiv int -2, 2'.  What does the -constfold pass produce for:
> 
> 
> int %test() { %X = udiv int -2, 2 ret int %X }
> 
> 
> I bet it is folded to -1, which is incorrect.

There's no -constfold pass on opt, but I ran it through gccas and yes,
it does produce -1. I fixed it by changing:

BuiltinType R = 
>  (BuiltinType)V1->getSExtValue() / (BuiltinType)V2->getSExtValue();

to:

BuiltinType R = (BuiltinType)(V1->getSExtValue() / V2->getSExtValue());

That produced the value of 2147483647 ((MAX_UINT-1)/2) for your test
case which I think is correct.  Please confirm.


> 
> ===================================================================
> RCS file: /var/cvs/llvm/llvm/lib/VMCore/Constants.cpp,v
> retrieving revision 1.165
> diff -t -d -u -p -5 -r1.165 Constants.cpp
> --- lib/VMCore/Constants.cpp 20 Oct 2006 07:07:24 -0000 1.165
> +++ lib/VMCore/Constants.cpp 23 Oct 2006 02:43:36 -0000
> ..
> @@ -444,12 +446,18 @@ Constant *ConstantExpr::getSub(Constant 
>    return get(Instruction::Sub, C1, C2);
>  }
>  Constant *ConstantExpr::getMul(Constant *C1, Constant *C2) {
>    return get(Instruction::Mul, C1, C2);
>  }
> -Constant *ConstantExpr::getDiv(Constant *C1, Constant *C2) {
> -  return get(Instruction::Div, C1, C2);
> +Constant *ConstantExpr::getUDiv(Constant *C1, Constant *C2) {
> +  return get(Instruction::UDiv, C1, C2);
> +}
> +Constant *ConstantExpr::getSDiv(Constant *C1, Constant *C2) {
> +  return get(Instruction::SDiv, C1, C2);
> +}
> +Constant *ConstantExpr::getFDiv(Constant *C1, Constant *C2) {
> +  return get(Instruction::FDiv, C1, C2);
>  }
>  Constant *ConstantExpr::getRem(Constant *C1, Constant *C2) {
>    return get(Instruction::Rem, C1, C2);
>  }
>  Constant *ConstantExpr::getAnd(Constant *C1, Constant *C2) {
> 
> 
> *** This would be an excellent place to assert that the arguments are
> integer or fp (or vectors of) as appropriate.

All of these cases call ConstantExpr::get(Opcode, C1, C2). That function
provides copious asserts. I didn't think it was reasonable to double
them up, especially since the asserts in ConstantExpr::get are ifdef'd
for debug only. I took that to mean that they were performance sensitive
for a release+asserts build.  Let me know if that's not the case (i.e.
if I should remove the #ifndef DEBUG code in ConstantExpr::get).

I looked at ConstantExpr::get and tightened up the asserts there. It was
allowing FP for UDiv and SDiv and integer for FDiv. Now it doesn't.

> ===================================================================
> RCS file: /var/cvs/llvm/llvm/lib/VMCore/Instructions.cpp,v
> retrieving revision 1.43
> diff -t -d -u -p -5 -r1.43 Instructions.cpp
> --- lib/VMCore/Instructions.cpp 20 Oct 2006 07:07:24 -0000 1.43
> +++ lib/VMCore/Instructions.cpp 23 Oct 2006 02:43:36 -0000
> @@ -1020,11 +1020,11 @@ void BinaryOperator::init(BinaryOps iTyp
>    assert(LHS->getType() == RHS->getType() &&
>           "Binary operator operand types must match!");
>  #ifndef NDEBUG
>    switch (iType) {
>    case Add: case Sub:
> -  case Mul: case Div:
> +  case Mul: case UDiv: case SDiv: case FDiv:
>    case Rem:
>      assert(getType() == LHS->getType() &&
>             "Arithmetic operation should return same type as
> operands!");
>      assert((getType()->isInteger() || getType()->isFloatingPoint() ||
>              isa<PackedType>(getType())) &&
> 
> 
> *** Likewise, this would be a good place to assert that the operands
> are int/fp(or vectors of) as appropriate.

Yes. Done.

Thanks Chris.  Your comments are always useful and instructive.

Reid.




More information about the llvm-commits mailing list