diff options
-rw-r--r-- | lib/Transforms/NaCl/PromoteIntegers.cpp | 197 | ||||
-rw-r--r-- | test/Transforms/NaCl/promote-integers.ll | 103 |
2 files changed, 150 insertions, 150 deletions
diff --git a/lib/Transforms/NaCl/PromoteIntegers.cpp b/lib/Transforms/NaCl/PromoteIntegers.cpp index 97fb08fb55..7ab7beb69a 100644 --- a/lib/Transforms/NaCl/PromoteIntegers.cpp +++ b/lib/Transforms/NaCl/PromoteIntegers.cpp @@ -12,16 +12,18 @@ // Legal sizes are currently 1, 8, 16, 32, 64 (and higher, see note below) // Operations on illegal integers and int pointers are be changed to operate // on the next-higher legal size. -// It always maintains the invariant that the upper bits (above the size of the -// original type) are zero; therefore after operations which can overwrite these -// bits (e.g. add, shl, sext), the bits are cleared. +// It maintains no invariants about the upper bits (above the size of the +// original type); therefore before operations which can be affected by the +// value of these bits (e.g. cmp, select, lshr), the upper bits of the operands +// are cleared. // // Limitations: // 1) It can't change function signatures or global variables // 2) It won't promote (and can't expand) types larger than i64 // 3) Doesn't support div operators // 4) Doesn't handle arrays or structs (or GEPs) with illegal types -// 5) Doesn't handle constant expressions +// 5) Doesn't handle constant expressions (it also doesn't produce them, so it +// can run after ExpandConstantExpr) // //===----------------------------------------------------------------------===// @@ -303,10 +305,12 @@ static Value *splitStore(StoreInst *Inst, ConversionState &State) { return StoreHi; } -// Return a value with the bits of the operand above the size of the original -// type cleared. The operand is assumed to have been legalized already. -static Value *getClearUpper(Value *Operand, Type *OrigType, - Instruction *InsertPt) { +// Return a converted value with the bits of the operand above the size of the +// original type cleared. +static Value *getClearConverted(Value *Operand, Instruction *InsertPt, + ConversionState &State) { + Type *OrigType = Operand->getType(); + Operand = State.getConverted(Operand); // If the operand is a constant, it will have been created by // ConversionState.getConverted, which zero-extends by default. if (isa<Constant>(Operand)) @@ -358,7 +362,7 @@ static void convertInstruction(Instruction *Inst, ConversionState &State) { Value *Op = Sext->getOperand(0); Value *NewInst = NULL; // If the operand to be extended is illegal, we first need to fill its - // upper bits (which are zero) with its sign bit. + // upper bits with its sign bit. if (shouldConvert(Op)) { NewInst = getSignExtend(State.getConverted(Op), Op, Sext); } @@ -372,21 +376,13 @@ static void convertInstruction(Instruction *Inst, ConversionState &State) { getPromotedType(cast<IntegerType>(Sext->getType())), Sext->getName() + ".sext", Sext); } - // Now all the bits of the result are correct, but we need to restore - // the bits above its type to zero. - if (shouldConvert(Sext)) { - NewInst = getClearUpper(NewInst, Sext->getType(), Sext); - } assert(NewInst && "Failed to convert sign extension"); State.recordConverted(Sext, NewInst); } else if (ZExtInst *Zext = dyn_cast<ZExtInst>(Inst)) { Value *Op = Zext->getOperand(0); Value *NewInst = NULL; - // TODO(dschuff): Some of these zexts could be no-ops. if (shouldConvert(Op)) { - NewInst = getClearUpper(State.getConverted(Op), - Op->getType(), - Zext); + NewInst = getClearConverted(Op, Zext, State); } // If the converted type of the operand is the same as the converted // type of the result, we won't actually be changing the type of the @@ -402,10 +398,11 @@ static void convertInstruction(Instruction *Inst, ConversionState &State) { State.recordConverted(Zext, NewInst); } else if (TruncInst *Trunc = dyn_cast<TruncInst>(Inst)) { Value *Op = Trunc->getOperand(0); - Value *NewInst = NULL; + Value *NewInst; // If the converted type of the operand is the same as the converted - // type of the result, we won't actually be changing the type of the - // variable, just its value. + // type of the result, we don't actually need to change the type of the + // variable, just its value. However, because we don't care about the values + // of the upper bits until they are consumed, truncation can be a no-op. if (getPromotedType(Op->getType()) != getPromotedType(Trunc->getType())) { NewInst = new TruncInst( @@ -413,15 +410,9 @@ static void convertInstruction(Instruction *Inst, ConversionState &State) { getPromotedType(cast<IntegerType>(Trunc->getType())), State.getConverted(Op)->getName() + ".trunc", Trunc); + } else { + NewInst = State.getConverted(Op); } - // Restoring the upper-bits-are-zero invariant effectively truncates the - // value. - if (shouldConvert(Trunc)) { - NewInst = getClearUpper(NewInst ? NewInst : Op, - Trunc->getType(), - Trunc); - } - assert(NewInst); State.recordConverted(Trunc, NewInst); } else if (AllocaInst *Alloc = dyn_cast<AllocaInst>(Inst)) { // Don't handle arrays of illegal types, but we could handle an array @@ -455,82 +446,74 @@ static void convertInstruction(Instruction *Inst, ConversionState &State) { report_fatal_error("can't convert calls with illegal types"); } else if (BinaryOperator *Binop = dyn_cast<BinaryOperator>(Inst)) { Value *NewInst = NULL; - if (Binop->getOpcode() == Instruction::AShr) { - // The AShr operand needs to be sign-extended to the promoted size - // before shifting. Because the sign-extension is implemented with - // with AShr, it can be combined with the original operation. - Value *Op = Binop->getOperand(0); - Value *ShiftAmount = NULL; - APInt SignShiftAmt = APInt( - getPromotedType(Op->getType())->getIntegerBitWidth(), - getPromotedType(Op->getType())->getIntegerBitWidth() - - Op->getType()->getIntegerBitWidth()); - NewInst = BinaryOperator::Create( - Instruction::Shl, - State.getConverted(Op), - ConstantInt::get(getPromotedType(Op->getType()), SignShiftAmt), - State.getConverted(Op)->getName() + ".getsign", - Binop); - if (ConstantInt *C = dyn_cast<ConstantInt>( - State.getConverted(Binop->getOperand(1)))) { - ShiftAmount = ConstantInt::get(getPromotedType(Op->getType()), - SignShiftAmt + C->getValue()); - } else { - ShiftAmount = BinaryOperator::Create( - Instruction::Add, - State.getConverted(Binop->getOperand(1)), - ConstantInt::get( - getPromotedType(Binop->getOperand(1)->getType()), - SignShiftAmt), - State.getConverted(Op)->getName() + ".shamt", Binop); - } - NewInst = BinaryOperator::Create( - Instruction::AShr, - NewInst, - ShiftAmount, - Binop->getName() + ".result", Binop); - } else { - // If the original operation is not AShr, just recreate it as usual. - NewInst = BinaryOperator::Create( - Binop->getOpcode(), - State.getConverted(Binop->getOperand(0)), - State.getConverted(Binop->getOperand(1)), - Binop->getName() + ".result", Binop); - if (isa<OverflowingBinaryOperator>(NewInst)) { - cast<BinaryOperator>(NewInst)->setHasNoUnsignedWrap - (Binop->hasNoUnsignedWrap()); - cast<BinaryOperator>(NewInst)->setHasNoSignedWrap( - Binop->hasNoSignedWrap()); + switch (Binop->getOpcode()) { + case Instruction::AShr: { + // The AShr operand needs to be sign-extended to the promoted size + // before shifting. Because the sign-extension is implemented with + // with AShr, it can be combined with the original operation. + Value *Op = Binop->getOperand(0); + Value *ShiftAmount = NULL; + APInt SignShiftAmt = APInt( + getPromotedType(Op->getType())->getIntegerBitWidth(), + getPromotedType(Op->getType())->getIntegerBitWidth() - + Op->getType()->getIntegerBitWidth()); + NewInst = BinaryOperator::Create( + Instruction::Shl, + State.getConverted(Op), + ConstantInt::get(getPromotedType(Op->getType()), SignShiftAmt), + State.getConverted(Op)->getName() + ".getsign", + Binop); + if (ConstantInt *C = dyn_cast<ConstantInt>( + State.getConverted(Binop->getOperand(1)))) { + ShiftAmount = ConstantInt::get(getPromotedType(Op->getType()), + SignShiftAmt + C->getValue()); + } else { + // Clear the upper bits of the original shift amount, and add back the + // amount we shifted to get the sign bit. + ShiftAmount = getClearConverted(Binop->getOperand(1), Binop, State); + ShiftAmount = BinaryOperator::Create( + Instruction::Add, + ShiftAmount, + ConstantInt::get( + getPromotedType(Binop->getOperand(1)->getType()), + SignShiftAmt), + State.getConverted(Op)->getName() + ".shamt", Binop); + } + NewInst = BinaryOperator::Create( + Instruction::AShr, + NewInst, + ShiftAmount, + Binop->getName() + ".result", Binop); + break; } - } - // Now restore the invariant if necessary. - // This switch also sanity-checks the operation. - switch (Binop->getOpcode()) { - case Instruction::And: - case Instruction::Or: - case Instruction::Xor: case Instruction::LShr: - // These won't change the upper bits. + case Instruction::Shl: { + // For LShr, clear the upper bits of the operand before shifting them + // down into the valid part of the value. + Value *Op = Binop->getOpcode() == Instruction::LShr + ? getClearConverted(Binop->getOperand(0), Binop, State) + : State.getConverted(Binop->getOperand(0)); + NewInst = BinaryOperator::Create( + Binop->getOpcode(), Op, + // Clear the upper bits of the shift amount. + getClearConverted(Binop->getOperand(1), Binop, State), + Binop->getName() + ".result", Binop); break; - // These can change the upper bits, unless we are sure they never - // overflow. So clear them now. + } case Instruction::Add: case Instruction::Sub: case Instruction::Mul: - if (!(Binop->hasNoUnsignedWrap() && Binop->hasNoSignedWrap())) - NewInst = getClearUpper(NewInst, Binop->getType(), Binop); - break; - case Instruction::Shl: - if (!Binop->hasNoUnsignedWrap()) - NewInst = getClearUpper(NewInst, Binop->getType(), Binop); - break; - // We modified the upper bits ourselves when implementing AShr - case Instruction::AShr: - NewInst = getClearUpper(NewInst, Binop->getType(), Binop); + case Instruction::And: + case Instruction::Or: + case Instruction::Xor: + // These operations don't care about the state of the upper bits. + NewInst = BinaryOperator::Create( + Binop->getOpcode(), + State.getConverted(Binop->getOperand(0)), + State.getConverted(Binop->getOperand(1)), + Binop->getName() + ".result", Binop); break; - // We should not see FP operators here. - // We don't handle div. case Instruction::FAdd: case Instruction::FSub: case Instruction::FMul: @@ -541,18 +524,25 @@ static void convertInstruction(Instruction *Inst, ConversionState &State) { case Instruction::SRem: case Instruction::FRem: case Instruction::BinaryOpsEnd: + // We should not see FP operators here. + // We don't handle div. errs() << *Inst << "\n"; llvm_unreachable("Cannot handle binary operator"); break; } + if (isa<OverflowingBinaryOperator>(NewInst)) { + cast<BinaryOperator>(NewInst)->setHasNoUnsignedWrap( + Binop->hasNoUnsignedWrap()); + cast<BinaryOperator>(NewInst)->setHasNoSignedWrap( + Binop->hasNoSignedWrap()); + } State.recordConverted(Binop, NewInst); } else if (ICmpInst *Cmp = dyn_cast<ICmpInst>(Inst)) { Value *Op0, *Op1; // For signed compares, operands are sign-extended to their - // promoted type. For unsigned or equality compares, the comparison - // is equivalent with the larger type because they are already - // zero-extended. + // promoted type. For unsigned or equality compares, the upper bits are + // cleared. if (Cmp->isSigned()) { Op0 = getSignExtend(State.getConverted(Cmp->getOperand(0)), Cmp->getOperand(0), @@ -561,8 +551,8 @@ static void convertInstruction(Instruction *Inst, ConversionState &State) { Cmp->getOperand(1), Cmp); } else { - Op0 = State.getConverted(Cmp->getOperand(0)); - Op1 = State.getConverted(Cmp->getOperand(1)); + Op0 = getClearConverted(Cmp->getOperand(0), Cmp, State); + Op1 = getClearConverted(Cmp->getOperand(1), Cmp, State); } ICmpInst *NewInst = new ICmpInst( Cmp, Cmp->getPredicate(), Op0, Op1, ""); @@ -585,8 +575,9 @@ static void convertInstruction(Instruction *Inst, ConversionState &State) { } State.recordConverted(Phi, NewPhi); } else if (SwitchInst *Switch = dyn_cast<SwitchInst>(Inst)) { + Value *Condition = getClearConverted(Switch->getCondition(), Switch, State); SwitchInst *NewInst = SwitchInst::Create( - State.getConverted(Switch->getCondition()), + Condition, Switch->getDefaultDest(), Switch->getNumCases(), Switch); diff --git a/test/Transforms/NaCl/promote-integers.ll b/test/Transforms/NaCl/promote-integers.ll index 8d7ebac28e..1067e25fd5 100644 --- a/test/Transforms/NaCl/promote-integers.ll +++ b/test/Transforms/NaCl/promote-integers.ll @@ -3,8 +3,7 @@ declare void @consume_i16(i16 %a) ; CHECK: @sext_to_illegal -; CHECK-NEXT: %a40.sext = sext i32 %a to i64 -; CHECK-NEXT: %a40 = and i64 %a40.sext, 1099511627775 +; CHECK-NEXT: %a40 = sext i32 %a to i64 ; (0xFFFFFFFFFF) define void @sext_to_illegal(i32 %a) { %a40 = sext i32 %a to i40 @@ -16,21 +15,18 @@ define void @sext_from_illegal(i8 %a) { ; CHECK: call void @consume_i16(i16 -2) %c12 = sext i12 -2 to i16 call void @consume_i16(i16 %c12) -; CHECK: %a12.sext = sext i8 %a to i16 -; CHECK-NEXT: %a12 = and i16 %a12.sext, 4095 +; CHECK: %a12 = sext i8 %a to i16 %a12 = sext i8 %a to i12 ; CHECK: %a12.getsign = shl i16 %a12, 4 ; CHECK-NEXT: %a16 = ashr i16 %a12.getsign, 4 %a16 = sext i12 %a12 to i16 ; CHECK: %a12.getsign1 = shl i16 %a12, 4 -; CHECK-NEXT: %a12.signed = ashr i16 %a12.getsign1, 4 -; CHECK-NEXT: %a14 = and i16 %a12.signed, 16383 +; CHECK-NEXT: %a14 = ashr i16 %a12.getsign1, 4 ; (0x3FFF) %a14 = sext i12 %a12 to i14 ; CHECK-NEXT: %a12.getsign2 = shl i16 %a12, 4 -; CHECK-NEXT: %a12.signed3 = ashr i16 %a12.getsign2, 4 -; CHECK-NEXT: %a24.sext = sext i16 %a12.signed3 to i32 -; CHECK-NEXT: %a24 = and i32 %a24.sext, 16777215 +; CHECK-NEXT: %a12.signed = ashr i16 %a12.getsign2, 4 +; CHECK-NEXT: %a24 = sext i16 %a12.signed to i32 ; (0xFFFFFF) %a24 = sext i12 %a12 to i24 @@ -56,7 +52,6 @@ define void @zext_from_illegal(i8 %a) { %a40 = zext i8 %a to i40 %a18 = zext i8 %a to i18 -; TODO(dschuff): the ANDs can be no-ops when we zext from an illegal type. ; CHECK: %a32 = and i32 %a24, 16777215 ; (0xFFFFFF) %a32 = zext i24 %a24 to i32 @@ -79,6 +74,7 @@ define void @zext_from_illegal(i8 %a) { ret void } +; CHECK: @trunc_from_illegal define void @trunc_from_illegal(i8 %a) { %a24 = zext i8 %a to i24 ; CHECK: %a16 = trunc i32 %a24 to i16 @@ -86,14 +82,15 @@ define void @trunc_from_illegal(i8 %a) { ret void } -define void @trunc_to_illegal(i32 %a) { -; CHECK: %a24 = and i32 %a, 16777215 -; (0xFFFFFF) +; CHECK: @trunc_to_illegal +define void @trunc_to_illegal(i8 %a8) { + %a = zext i8 %a8 to i32 +; CHECK-NOT: trunc i32 %a +; CHECK-NOT: and %a24 = trunc i32 %a to i24 -; CHECK: %a24.trunc = trunc i32 %a24 to i16 -; CHECK-NEXT: %a12 = and i16 %a24.trunc, 4095 -; (0xFFF) +; CHECK: %a12 = trunc i32 %a24 to i16 +; CHECK-NOT: and %a12 = trunc i24 %a24 to i12 ret void } @@ -132,9 +129,8 @@ define void @and1(i32 %a) { ; CHECK: @andi3 define void @andi3(i8 %a) { -; CHECK-NEXT: and i8 %a, 7 %a3 = trunc i8 %a to i3 -; CHECK-NEXT: and i8 %a3, 2 +; CHECK: and i8 %a3, 2 %and = and i3 %a3, 2 ret void } @@ -150,37 +146,28 @@ define void @ori7(i8 %a, i8 %b) { ; CHECK: @add1 define void @add1(i16 %a) { -; CHECK-NEXT: %a24.sext = sext i16 %a to i32 -; CHECK-NEXT: %a24 = and i32 %a24.sext, 16777215 +; CHECK-NEXT: %a24 = sext i16 %a to i32 %a24 = sext i16 %a to i24 -; CHECK-NEXT: %sum.result = add i32 %a24, 16777214 -; CHECK-NEXT: %sum = and i32 %sum.result, 16777215 +; CHECK-NEXT: %sum = add i32 %a24, 16777214 %sum = add i24 %a24, -2 -; CHECK-NEXT: %sumnsw.result = add nsw i32 %a24, 16777214 -; CHECK-NEXT: %sumnsw = and i32 %sumnsw.result, 16777215 +; CHECK-NEXT: %sumnsw = add nsw i32 %a24, 16777214 %sumnsw = add nsw i24 %a24, -2 -; CHECK-NEXT: %sumnuw.result = add nuw i32 %a24, 16777214 -; CHECK-NEXT: %sumnuw = and i32 %sumnuw.result, 16777215 +; CHECK-NEXT: %sumnuw = add nuw i32 %a24, 16777214 %sumnuw = add nuw i24 %a24, -2 ; CHECK-NEXT: %sumnw = add nuw nsw i32 %a24, 16777214 -; CHECK-NOT: and %sumnw = add nuw nsw i24 %a24, -2 ret void } ; CHECK: @mul1 define void @mul1(i32 %a, i32 %b) { -; CHECK-NEXT: %a33.sext = sext i32 %a to i64 -; CHECK-NEXT: %a33 = and i64 %a33.sext, 8589934591 +; CHECK-NEXT: %a33 = sext i32 %a to i64 %a33 = sext i32 %a to i33 -; CHECK-NEXT: %b33.sext = sext i32 %b to i64 -; CHECK-NEXT: %b33 = and i64 %b33.sext, 8589934591 +; CHECK-NEXT: %b33 = sext i32 %b to i64 %b33 = sext i32 %b to i33 -; CHECK-NEXT: %product.result = mul i64 %a33, %b33 -; CHECK-NEXT: %product = and i64 %product.result, 8589934591 +; CHECK-NEXT: %product = mul i64 %a33, %b33 %product = mul i33 %a33, %b33 ; CHECK-NEXT: %prodnw = mul nuw nsw i64 %a33, %b33 -; CHECK-NOT: and %prodnw = mul nuw nsw i33 %a33, %b33 ret void } @@ -188,9 +175,16 @@ define void @mul1(i32 %a, i32 %b) { ; CHECK: @shl1 define void @shl1(i16 %a) { %a24 = zext i16 %a to i24 -; CHECK: %ashl.result = shl i32 %a24, 5 -; CHECK: %ashl = and i32 %ashl.result, 16777215 +; CHECK: %ashl = shl i32 %a24, 5 %ashl = shl i24 %a24, 5 + +; CHECK-NEXT: %ashl2 = shl i32 %a24, 1 + %ashl2 = shl i24 %a24, 4278190081 ;0xFF000001 + + %b24 = zext i16 %a to i24 +; CHECK: %b24.clear = and i32 %b24, 16777215 +; CHECK-NEXT: %bshl = shl i32 %a24, %b24.clear + %bshl = shl i24 %a24, %b24 ret void } @@ -198,7 +192,6 @@ define void @shl1(i16 %a) { define void @shlnuw(i16 %a) { %a12 = trunc i16 %a to i12 ; CHECK: %ashl = shl nuw i16 %a12, 5 -; CHECK-NOT: and %ashl = shl nuw i12 %a12, 5 ret void } @@ -206,10 +199,18 @@ define void @shlnuw(i16 %a) { ; CHECK: @lshr1 define void @lshr1(i16 %a) { %a24 = zext i16 %a to i24 -; CHECK: %b = lshr i32 %a24, 20 +; CHECK: %a24.clear = and i32 %a24, 16777215 +; CHECK-NEXT: %b = lshr i32 %a24.clear, 20 %b = lshr i24 %a24, 20 -; CHECK: %c = lshr i32 %a24, 5 +; CHECK-NEXT: %a24.clear1 = and i32 %a24, 16777215 +; CHECK-NEXT: %c = lshr i32 %a24.clear1, 5 %c = lshr i24 %a24, 5 + + %b24 = zext i16 %a to i24 + %d = lshr i24 %a24, %b24 +; CHECK: %a24.clear2 = and i32 %a24, 16777215 +; CHECK-NEXT: %b24.clear = and i32 %b24, 16777215 +; CHECK-NEXT: %d = lshr i32 %a24.clear2, %b24.clear ret void } @@ -217,13 +218,12 @@ define void @lshr1(i16 %a) { define void @ashr1(i16 %a) { %a24 = sext i16 %a to i24 ; CHECK: %a24.getsign = shl i32 %a24, 8 -; CHECK-NEXT: %b24.result = ashr i32 %a24.getsign, 19 -; CHECK-NEXT: %b24 = and i32 %b24.result, 16777215 +; CHECK-NEXT: %b24 = ashr i32 %a24.getsign, 19 %b24 = ashr i24 %a24, 11 ; CHECK-NEXT: %a24.getsign1 = shl i32 %a24, 8 -; CHECK-NEXT: %a24.shamt = add i32 %b24, 8 -; CHECK-NEXT: %c.result = ashr i32 %a24.getsign1, %a24.shamt -; CHECK-NEXT: %c = and i32 %c.result, 16777215 +; CHECK-NEXT: %b24.clear = and i32 %b24, 16777215 +; CHECK-NEXT: %a24.shamt = add i32 %b24.clear, 8 +; CHECK-NEXT: %c = ashr i32 %a24.getsign1, %a24.shamt %c = ashr i24 %a24, %b24 ret void } @@ -235,7 +235,8 @@ entry: loop: ; CHECK: %phi40 = phi i64 [ 1099511627774, %entry ], [ %phi40, %loop ] %phi40 = phi i40 [ -2, %entry ], [ %phi40, %loop ] -; CHECK-NEXT: %b = icmp eq i64 %phi40, 1099511627775 +; CHECK-NEXT: %phi40.clear = and i64 %phi40, 1099511627775 +; CHECK-NEXT: %b = icmp eq i64 %phi40.clear, 1099511627775 %b = icmp eq i40 %phi40, -1 ; CHECK-NEXT: br i1 %b, label %loop, label %end br i1 %b, label %loop, label %end @@ -246,8 +247,15 @@ end: ; CHECK: @icmp_ult define void @icmp_ult(i32 %a) { %a40 = zext i32 %a to i40 -; CHECK: %b = icmp ult i64 %a40, 1099511627774 +; CHECK: %a40.clear = and i64 %a40, 1099511627775 +; CHECK-NEXT: %b = icmp ult i64 %a40.clear, 1099511627774 %b = icmp ult i40 %a40, -2 + +; CHECK: %a40.clear1 = and i64 %a40, 1099511627775 +; CHECK-NEXT: %b40.clear = and i64 %b40, 1099511627775 +; CHECK-NEXT: %c = icmp ult i64 %a40.clear1, %b40.clear + %b40 = zext i32 %a to i40 + %c = icmp ult i40 %a40, %b40 ret void } @@ -373,7 +381,8 @@ define void @undefoperand(i32 %a) { ; CHECK: @switch ; CHECK-NEXT: %a24 = zext i16 %a to i32 -; CHECK-NEXT: switch i32 %a24, label %end [ +; CHECK-NEXT: %a24.clear = and i32 %a24, 16777215 +; CHECK-NEXT: switch i32 %a24.clear, label %end [ ; CHECK-NEXT: i32 0, label %if1 ; CHECK-NEXT: i32 1, label %if2 define void @switch(i16 %a) { |