From f72fb679eff7de84e3e18b75d63a18cb3510bcdd Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 9 Sep 2008 01:02:47 +0000 Subject: [PATCH] Extend the vcmp/fcmp LLVM IR instructions to take vectors as arguments and, if so, to return a vector of boolean as a result; Extend the select LLVM IR instruction to allow you to specify a result type which is a vector of boolean, in which case the result will be an element-wise selection instead of choosing one vector or the other; and Update LangRef.html to describe these changes. This patch was contributed by Preston Gurd! git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@55969 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LangRef.html | 71 ++++++++++++++++++++-------- include/llvm/Bitcode/LLVMBitCodes.h | 4 +- include/llvm/InstrTypes.h | 8 ++++ include/llvm/Instructions.h | 22 +++++---- lib/AsmParser/llvmAsmParser.y | 29 ++++++++---- lib/Bitcode/Reader/BitcodeReader.cpp | 29 +++++++++++- lib/Bitcode/Writer/BitcodeWriter.cpp | 18 ++++++- lib/VMCore/Verifier.cpp | 21 ++++++-- test/Assembler/vbool-cmp.ll | 15 ++++++ test/Assembler/vector-select.ll | 11 +++++ 10 files changed, 181 insertions(+), 47 deletions(-) create mode 100644 test/Assembler/vbool-cmp.ll create mode 100644 test/Assembler/vector-select.ll diff --git a/docs/LangRef.html b/docs/LangRef.html index 1beb670765d..4921707338c 100644 --- a/docs/LangRef.html +++ b/docs/LangRef.html @@ -3846,11 +3846,12 @@ instructions, which defy better classification.

Syntax:
-
  <result> = icmp <cond> <ty> <op1>, <op2>   ; yields {i1}:result
+
  <result> = icmp <cond> <ty> <op1>, <op2>   ; yields {i1} or {<N x i1>}:result
 
Overview:
-

The 'icmp' instruction returns a boolean value based on comparison -of its two integer or pointer operands.

+

The 'icmp' instruction returns a boolean value or +a vector of boolean values based on comparison +of its two integer, integer vector, or pointer operands.

Arguments:

The 'icmp' instruction takes three operands. The first operand is the condition code indicating the kind of comparison to perform. It is not @@ -3868,11 +3869,13 @@ a value, just a keyword. The possible condition code are:

  • sle: signed less or equal
  • The remaining two arguments must be integer or -pointer typed. They must also be identical types.

    +pointer +or integer vector typed. +They must also be identical types.

    Semantics:

    The 'icmp' compares op1 and op2 according to the condition code given as cond. The comparison performed always -yields a i1 result, as follows: +yields either an i1 or vector of i1 result, as follows:

    1. eq: yields true if the operands are equal, false otherwise. No sign interpretation is necessary or performed. @@ -3898,6 +3901,11 @@ yields a i1 result, as follows:

    If the operands are pointer typed, the pointer values are compared as if they were integers.

    +

    If the operands are integer vectors, then they are compared +element by element. The result is an i1 vector with +the same number of elements as the values being compared. +Otherwise, the result is an i1. +

    Example:
      <result> = icmp eq i32 4, 5          ; yields: result=false
    @@ -3914,11 +3922,19 @@ values are compared as if they were integers.

    Syntax:
    -
      <result> = fcmp <cond> <ty> <op1>, <op2>     ; yields {i1}:result
    +
      <result> = fcmp <cond> <ty> <op1>, <op2>     ; yields {i1} or {<N x i1>}:result
     
    Overview:
    -

    The 'fcmp' instruction returns a boolean value based on comparison -of its floating point operands.

    +

    The 'fcmp' instruction returns a boolean value +or vector of boolean values based on comparison +of its operands. +

    +If the operands are floating point scalars, then the result +type is a boolean (i1). +

    +

    If the operands are floating point vectors, then the result type +is a vector of boolean with the same number of elements as the +operands being compared.

    Arguments:

    The 'fcmp' instruction takes three operands. The first operand is the condition code indicating the kind of comparison to perform. It is not @@ -3943,13 +3959,17 @@ a value, just a keyword. The possible condition code are:

    Ordered means that neither operand is a QNAN while unordered means that either operand may be a QNAN.

    -

    The val1 and val2 arguments must be -floating point typed. They must have identical -types.

    +

    Each of val1 and val2 arguments must be +either a floating point type +or a vector of floating point type. +They must have identical types.

    Semantics:

    The 'fcmp' instruction compares op1 and op2 -according to the condition code given as cond. The comparison performed -always yields a i1 result, as follows: +according to the condition code given as cond. +If the operands are vectors, then the vectors are compared +element by element. +Each comparison performed +always yields an i1 result, as follows:

    1. false: always yields false, regardless of operands.
    2. oeq: yields true if both operands are not a QNAN and @@ -3983,9 +4003,9 @@ always yields a i1 result, as follows:
      Example:
        <result> = fcmp oeq float 4.0, 5.0    ; yields: result=false
      -  <result> = icmp one float 4.0, 5.0    ; yields: result=true
      -  <result> = icmp olt float 4.0, 5.0    ; yields: result=true
      -  <result> = icmp ueq double 1.0, 2.0   ; yields: result=false
      +  <result> = fcmp one float 4.0, 5.0    ; yields: result=true
      +  <result> = fcmp olt float 4.0, 5.0    ; yields: result=true
      +  <result> = fcmp ueq double 1.0, 2.0   ; yields: result=false
       
    @@ -4016,7 +4036,7 @@ a value, just a keyword. The possible condition code are:
  • slt: signed less than
  • sle: signed less or equal
  • -

    The remaining two arguments must be vector of +

    The remaining two arguments must be vector or integer typed. They must also be identical types.

    Semantics:

    The 'vicmp' instruction compares op1 and op2 @@ -4140,7 +4160,9 @@ Loop: ; Infinite loop that counts from 0 on up...

    Syntax:
    -  <result> = select i1 <cond>, <ty> <val1>, <ty> <val2>             ; yields ty
    +  <result> = select selty <cond>, <ty> <val1>, <ty> <val2>             ; yields ty
    +
    +  selty is either i1 or {<N x i1>}
     
    Overview:
    @@ -4154,18 +4176,25 @@ condition, without branching.
    Arguments:

    -The 'select' instruction requires an 'i1' value indicating the +The 'select' instruction requires an 'i1' value or +a vector of 'i1' values indicating the condition, and two values of the same first class -type. If the val1/val2 are vectors, the entire vectors are selected, not +type. If the val1/val2 are vectors and +the condition is a scalar, then entire vectors are selected, not individual elements.

    Semantics:

    -If the i1 condition evaluates is 1, the instruction returns the first +If the condition is an i1 and it evaluates to 1, the instruction returns the first value argument; otherwise, it returns the second value argument.

    +

    +If the condition is a vector of i1, then the value arguments must +be vectors of the same size, and the selection is done element +by element. +

    Example:
    diff --git a/include/llvm/Bitcode/LLVMBitCodes.h b/include/llvm/Bitcode/LLVMBitCodes.h index 12607dcd324..d1dcc74437d 100644 --- a/include/llvm/Bitcode/LLVMBitCodes.h +++ b/include/llvm/Bitcode/LLVMBitCodes.h @@ -205,7 +205,9 @@ namespace bitc { // FIXME: Remove GETRESULT in favor of EXTRACTVAL in LLVM 3.0 FUNC_CODE_INST_GETRESULT = 25, // GETRESULT: [ty, opval, n] FUNC_CODE_INST_EXTRACTVAL = 26, // EXTRACTVAL: [n x operands] - FUNC_CODE_INST_INSERTVAL = 27 // INSERTVAL: [n x operands] + FUNC_CODE_INST_INSERTVAL = 27, // INSERTVAL: [n x operands] + // fcmp/icmp returning vector of Int1Ty, NOT for vicmp/vfcmp + FUNC_CODE_INST_VCMP = 28 // VCMP: [opty, opval, opval, pred] }; } // End bitc namespace } // End llvm namespace diff --git a/include/llvm/InstrTypes.h b/include/llvm/InstrTypes.h index 430c772aec4..74bb6f53bdd 100644 --- a/include/llvm/InstrTypes.h +++ b/include/llvm/InstrTypes.h @@ -18,6 +18,7 @@ #include "llvm/Instruction.h" #include "llvm/OperandTraits.h" +#include "llvm/DerivedTypes.h" namespace llvm { @@ -732,6 +733,13 @@ public: static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } + /// @brief Create a result type for fcmp/icmp (but not vicmp/vfcmp) + static const Type* makeCmpResultType(const Type* opnd_type) { + if (const VectorType* vt = dyn_cast(opnd_type)) { + return VectorType::get(Type::Int1Ty, vt->getNumElements()); + } + return Type::Int1Ty; + } /// Backward-compatible interfaces /// @deprecated in 2.4, do not use, will disappear soon static CmpInst *create(OtherOps Op, unsigned short predicate, Value *S1, diff --git a/include/llvm/Instructions.h b/include/llvm/Instructions.h index 355103cfa64..8ae9c375fb7 100644 --- a/include/llvm/Instructions.h +++ b/include/llvm/Instructions.h @@ -621,7 +621,8 @@ public: Value *RHS, ///< The right-hand-side of the expression const std::string &NameStr = "", ///< Name of the instruction Instruction *InsertBefore = 0 ///< Where to insert - ) : CmpInst(Type::Int1Ty, Instruction::ICmp, pred, LHS, RHS, NameStr, + ) : CmpInst(makeCmpResultType(LHS->getType()), + Instruction::ICmp, pred, LHS, RHS, NameStr, InsertBefore) { assert(pred >= CmpInst::FIRST_ICMP_PREDICATE && pred <= CmpInst::LAST_ICMP_PREDICATE && @@ -629,7 +630,7 @@ public: assert(getOperand(0)->getType() == getOperand(1)->getType() && "Both operands to ICmp instruction are not of the same type!"); // Check that the operands are the right type - assert((getOperand(0)->getType()->isInteger() || + assert((getOperand(0)->getType()->isIntOrIntVector() || isa(getOperand(0)->getType())) && "Invalid operand types for ICmp instruction"); } @@ -641,7 +642,8 @@ public: Value *RHS, ///< The right-hand-side of the expression const std::string &NameStr, ///< Name of the instruction BasicBlock *InsertAtEnd ///< Block to insert into. - ) : CmpInst(Type::Int1Ty, Instruction::ICmp, pred, LHS, RHS, NameStr, + ) : CmpInst(makeCmpResultType(LHS->getType()), + Instruction::ICmp, pred, LHS, RHS, NameStr, InsertAtEnd) { assert(pred >= CmpInst::FIRST_ICMP_PREDICATE && pred <= CmpInst::LAST_ICMP_PREDICATE && @@ -649,7 +651,7 @@ public: assert(getOperand(0)->getType() == getOperand(1)->getType() && "Both operands to ICmp instruction are not of the same type!"); // Check that the operands are the right type - assert((getOperand(0)->getType()->isInteger() || + assert((getOperand(0)->getType()->isIntOrIntVector() || isa(getOperand(0)->getType())) && "Invalid operand types for ICmp instruction"); } @@ -754,6 +756,7 @@ public: static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } + }; //===----------------------------------------------------------------------===// @@ -773,14 +776,15 @@ public: Value *RHS, ///< The right-hand-side of the expression const std::string &NameStr = "", ///< Name of the instruction Instruction *InsertBefore = 0 ///< Where to insert - ) : CmpInst(Type::Int1Ty, Instruction::FCmp, pred, LHS, RHS, NameStr, + ) : CmpInst(makeCmpResultType(LHS->getType()), + Instruction::FCmp, pred, LHS, RHS, NameStr, InsertBefore) { assert(pred <= FCmpInst::LAST_FCMP_PREDICATE && "Invalid FCmp predicate value"); assert(getOperand(0)->getType() == getOperand(1)->getType() && "Both operands to FCmp instruction are not of the same type!"); // Check that the operands are the right type - assert(getOperand(0)->getType()->isFloatingPoint() && + assert(getOperand(0)->getType()->isFPOrFPVector() && "Invalid operand types for FCmp instruction"); } @@ -791,14 +795,15 @@ public: Value *RHS, ///< The right-hand-side of the expression const std::string &NameStr, ///< Name of the instruction BasicBlock *InsertAtEnd ///< Block to insert into. - ) : CmpInst(Type::Int1Ty, Instruction::FCmp, pred, LHS, RHS, NameStr, + ) : CmpInst(makeCmpResultType(LHS->getType()), + Instruction::FCmp, pred, LHS, RHS, NameStr, InsertAtEnd) { assert(pred <= FCmpInst::LAST_FCMP_PREDICATE && "Invalid FCmp predicate value"); assert(getOperand(0)->getType() == getOperand(1)->getType() && "Both operands to FCmp instruction are not of the same type!"); // Check that the operands are the right type - assert(getOperand(0)->getType()->isFloatingPoint() && + assert(getOperand(0)->getType()->isFPOrFPVector() && "Invalid operand types for FCmp instruction"); } @@ -837,6 +842,7 @@ public: static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } + }; //===----------------------------------------------------------------------===// diff --git a/lib/AsmParser/llvmAsmParser.y b/lib/AsmParser/llvmAsmParser.y index 5db76429b51..edc84c1fa14 100644 --- a/lib/AsmParser/llvmAsmParser.y +++ b/lib/AsmParser/llvmAsmParser.y @@ -1102,7 +1102,7 @@ Module *llvm::RunVMAsmParser(llvm::MemoryBuffer *MB) { %token ADD SUB MUL UDIV SDIV FDIV UREM SREM FREM AND OR XOR %token SHL LSHR ASHR -%token ICMP FCMP VICMP VFCMP +%token ICMP FCMP VICMP VFCMP %type IPredicates %type FPredicates %token EQ NE SLT SGT SLE SGE ULT UGT ULE UGE @@ -3097,8 +3097,6 @@ InstVal : ArithmeticOps Types ValueRef ',' ValueRef { | ICMP IPredicates Types ValueRef ',' ValueRef { if (!UpRefs.empty()) GEN_ERROR("Invalid upreference in type: " + (*$3)->getDescription()); - if (isa((*$3).get())) - GEN_ERROR("Vector types not supported by icmp instruction"); Value* tmpVal1 = getVal(*$3, $4); CHECK_FOR_ERROR Value* tmpVal2 = getVal(*$3, $6); @@ -3111,8 +3109,6 @@ InstVal : ArithmeticOps Types ValueRef ',' ValueRef { | FCMP FPredicates Types ValueRef ',' ValueRef { if (!UpRefs.empty()) GEN_ERROR("Invalid upreference in type: " + (*$3)->getDescription()); - if (isa((*$3).get())) - GEN_ERROR("Vector types not supported by fcmp instruction"); Value* tmpVal1 = getVal(*$3, $4); CHECK_FOR_ERROR Value* tmpVal2 = getVal(*$3, $6); @@ -3133,7 +3129,7 @@ InstVal : ArithmeticOps Types ValueRef ',' ValueRef { CHECK_FOR_ERROR $$ = CmpInst::Create($1, $2, tmpVal1, tmpVal2); if ($$ == 0) - GEN_ERROR("icmp operator returned null"); + GEN_ERROR("vicmp operator returned null"); delete $3; } | VFCMP FPredicates Types ValueRef ',' ValueRef { @@ -3147,7 +3143,7 @@ InstVal : ArithmeticOps Types ValueRef ',' ValueRef { CHECK_FOR_ERROR $$ = CmpInst::Create($1, $2, tmpVal1, tmpVal2); if ($$ == 0) - GEN_ERROR("fcmp operator returned null"); + GEN_ERROR("vfcmp operator returned null"); delete $3; } | CastOps ResolvedVal TO Types { @@ -3163,10 +3159,23 @@ InstVal : ArithmeticOps Types ValueRef ',' ValueRef { delete $4; } | SELECT ResolvedVal ',' ResolvedVal ',' ResolvedVal { - if ($2->getType() != Type::Int1Ty) - GEN_ERROR("select condition must be boolean"); + if (isa($2->getType())) { + // vector select + if (!isa($4->getType()) + || !isa($6->getType()) ) + GEN_ERROR("vector select value types must be vector types"); + const VectorType* cond_type = cast($2->getType()); + const VectorType* select_type = cast($4->getType()); + if (cond_type->getElementType() != Type::Int1Ty) + GEN_ERROR("vector select condition element type must be boolean"); + if (cond_type->getNumElements() != select_type->getNumElements()) + GEN_ERROR("vector select number of elements must be the same"); + } else { + if ($2->getType() != Type::Int1Ty) + GEN_ERROR("select condition must be boolean"); + } if ($4->getType() != $6->getType()) - GEN_ERROR("select value types should match"); + GEN_ERROR("select value types must match"); $$ = SelectInst::Create($2, $4, $6); CHECK_FOR_ERROR } diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp index 0d6b6f2edbe..eeba0e5ed81 100644 --- a/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1499,8 +1499,19 @@ bool BitcodeReader::ParseFunctionBody(Function *F) { Value *TrueVal, *FalseVal, *Cond; if (getValueTypePair(Record, OpNum, NextValueNo, TrueVal) || getValue(Record, OpNum, TrueVal->getType(), FalseVal) || - getValue(Record, OpNum, Type::Int1Ty, Cond)) + getValue(Record, OpNum, 0 /*skip type check*/, Cond)) return Error("Invalid SELECT record"); + + // select condition can be either i1 or [N x i1] + if (const VectorType* vector_type = dyn_cast(Cond->getType())) { + // expect + if (vector_type->getElementType() != Type::Int1Ty) + return Error("Invalid SELECT condition type"); + } else { + // expect i1 + if (Cond->getType() != Type::Int1Ty) + return Error("Invalid SELECT condition type"); + } I = SelectInst::Create(Cond, TrueVal, FalseVal); break; @@ -1563,6 +1574,22 @@ bool BitcodeReader::ParseFunctionBody(Function *F) { I = new VICmpInst((ICmpInst::Predicate)Record[OpNum], LHS, RHS); break; } + case bitc::FUNC_CODE_INST_VCMP: { // VCMP: [opty, opval, opval, pred] + // Fcmp/ICmp returning vector of bool + unsigned OpNum = 0; + Value *LHS, *RHS; + if (getValueTypePair(Record, OpNum, NextValueNo, LHS) || + getValue(Record, OpNum, LHS->getType(), RHS) || + OpNum+1 != Record.size()) + return Error("Invalid VCMP record"); + + // will always be vector + if (LHS->getType()->isFPOrFPVector()) + I = new FCmpInst((FCmpInst::Predicate)Record[OpNum], LHS, RHS); + else + I = new ICmpInst((ICmpInst::Predicate)Record[OpNum], LHS, RHS); + break; + } case bitc::FUNC_CODE_INST_GETRESULT: { // GETRESULT: [ty, val, n] if (Record.size() != 2) return Error("Invalid GETRESULT record"); diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp index b8aab5acc19..19f5beaf19f 100644 --- a/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -641,7 +641,14 @@ static void WriteConstants(unsigned FirstVal, unsigned LastVal, case Instruction::FCmp: case Instruction::VICmp: case Instruction::VFCmp: - Code = bitc::CST_CODE_CE_CMP; + if (isa(C->getOperand(0)->getType()) + && (CE->getOpcode() == Instruction::ICmp + || CE->getOpcode() == Instruction::FCmp)) { + // compare returning vector of Int1Ty + assert(0 && "Unsupported constant!"); + } else { + Code = bitc::CST_CODE_CE_CMP; + } Record.push_back(VE.getTypeID(C->getOperand(0)->getType())); Record.push_back(VE.getValueID(C->getOperand(0))); Record.push_back(VE.getValueID(C->getOperand(1))); @@ -765,7 +772,14 @@ static void WriteInstruction(const Instruction &I, unsigned InstID, case Instruction::FCmp: case Instruction::VICmp: case Instruction::VFCmp: - Code = bitc::FUNC_CODE_INST_CMP; + if (isa(I.getOperand(0)->getType()) + && (I.getOpcode() == Instruction::ICmp + || I.getOpcode() == Instruction::FCmp)) { + // compare returning vector of Int1Ty + Code = bitc::FUNC_CODE_INST_VCMP; + } else { + Code = bitc::FUNC_CODE_INST_CMP; + } PushValueAndType(I.getOperand(0), InstID, Vals, VE); Vals.push_back(VE.getValueID(I.getOperand(1))); Vals.push_back(cast(I).getPredicate()); diff --git a/lib/VMCore/Verifier.cpp b/lib/VMCore/Verifier.cpp index 805fb2564a4..af11fce5e72 100644 --- a/lib/VMCore/Verifier.cpp +++ b/lib/VMCore/Verifier.cpp @@ -659,8 +659,21 @@ void Verifier::visitSwitchInst(SwitchInst &SI) { } void Verifier::visitSelectInst(SelectInst &SI) { - Assert1(SI.getCondition()->getType() == Type::Int1Ty, - "Select condition type must be bool!", &SI); + if (const VectorType* vt + = dyn_cast(SI.getCondition()->getType())) { + Assert1( vt->getElementType() == Type::Int1Ty, + "Select condition type must be vector of bool!", &SI); + if (const VectorType* val_vt + = dyn_cast(SI.getTrueValue()->getType())) { + Assert1( vt->getNumElements() == val_vt->getNumElements(), + "Select vector size != value vector size", &SI); + } else { + Assert1(0, "Vector select values must have vector types", &SI); + } + } else { + Assert1(SI.getCondition()->getType() == Type::Int1Ty, + "Select condition type must be bool!", &SI); + } Assert1(SI.getTrueValue()->getType() == SI.getFalseValue()->getType(), "Select values must have identical types!", &SI); Assert1(SI.getTrueValue()->getType() == SI.getType(), @@ -1028,7 +1041,7 @@ void Verifier::visitICmpInst(ICmpInst& IC) { Assert1(Op0Ty == Op1Ty, "Both operands to ICmp instruction are not of the same type!", &IC); // Check that the operands are the right type - Assert1(Op0Ty->isInteger() || isa(Op0Ty), + Assert1(Op0Ty->isIntOrIntVector() || isa(Op0Ty), "Invalid operand types for ICmp instruction", &IC); visitInstruction(IC); } @@ -1040,7 +1053,7 @@ void Verifier::visitFCmpInst(FCmpInst& FC) { Assert1(Op0Ty == Op1Ty, "Both operands to FCmp instruction are not of the same type!", &FC); // Check that the operands are the right type - Assert1(Op0Ty->isFloatingPoint(), + Assert1(Op0Ty->isFPOrFPVector(), "Invalid operand types for FCmp instruction", &FC); visitInstruction(FC); } diff --git a/test/Assembler/vbool-cmp.ll b/test/Assembler/vbool-cmp.ll new file mode 100644 index 00000000000..ac8fb29362f --- /dev/null +++ b/test/Assembler/vbool-cmp.ll @@ -0,0 +1,15 @@ +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | grep {icmp slt} +; rudimentary test of fcmp/icmp on vectors returning vector of bool + +define <4 x i1> @ffoo(<4 x float> %a, <4 x float> %b) nounwind { +entry: + %cmp = fcmp olt <4 x float> %a, %b ; <4 x i1> [#uses=1] + ret <4 x i1> %cmp +} + +define <4 x i1> @ifoo(<4 x i32> %a, <4 x i32> %b) nounwind { +entry: + %cmp = icmp slt <4 x i32> %a, %b ; <4 x i1> [#uses=1] + ret <4 x i1> %cmp +} + diff --git a/test/Assembler/vector-select.ll b/test/Assembler/vector-select.ll new file mode 100644 index 00000000000..87af602aaf5 --- /dev/null +++ b/test/Assembler/vector-select.ll @@ -0,0 +1,11 @@ +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | grep select +; rudimentary test of select on vectors returning vector of bool + +define <4 x i32> @foo(<4 x i32> %a, <4 x i32> %b, + <4 x i1> %cond) nounwind { +entry: + %cmp = select <4 x i1> %cond, <4 x i32> %a, <4 x i32> %b + ; <4 x i32> [#uses=1] + ret <4 x i32> %cmp +} +