diff options
-rw-r--r-- | lib/Transforms/Scalar/SCCP.cpp | 330 | ||||
-rw-r--r-- | test/Transforms/SCCP/ipsccp-basic.ll | 3 |
2 files changed, 206 insertions, 127 deletions
diff --git a/lib/Transforms/Scalar/SCCP.cpp b/lib/Transforms/Scalar/SCCP.cpp index 83fcd8434b..05a0eeef2d 100644 --- a/lib/Transforms/Scalar/SCCP.cpp +++ b/lib/Transforms/Scalar/SCCP.cpp @@ -159,6 +159,11 @@ class SCCPSolver : public InstVisitor<SCCPSolver> { SmallPtrSet<BasicBlock*, 8> BBExecutable;// The BBs that are executable. DenseMap<Value*, LatticeVal> ValueState; // The state each value is in. + /// StructValueState - This maintains ValueState for values that have + /// StructType, for example for formal arguments, calls, insertelement, etc. + /// + DenseMap<std::pair<Value*, unsigned>, LatticeVal> StructValueState; + /// GlobalValue - If we are tracking any values for the contents of a global /// variable, we keep a mapping from the constant accessor to the element of /// the global, to the currently known value. If the value becomes @@ -173,6 +178,10 @@ class SCCPSolver : public InstVisitor<SCCPSolver> { /// TrackedMultipleRetVals - Same as TrackedRetVals, but used for functions /// that return multiple values. DenseMap<std::pair<Function*, unsigned>, LatticeVal> TrackedMultipleRetVals; + + /// MRVFunctionsTracked - Each function in TrackedMultipleRetVals is + /// represented here for efficient lookup. + SmallPtrSet<Function*, 16> MRVFunctionsTracked; /// TrackingIncomingArguments - This is the set of functions for whose /// arguments we make optimistic assumptions about and try to prove as @@ -219,8 +228,8 @@ public: /// specified global variable if it can. This is only legal to call if /// performing Interprocedural SCCP. void TrackValueOfGlobalVariable(GlobalVariable *GV) { - const Type *ElTy = GV->getType()->getElementType(); - if (ElTy->isFirstClassType()) { + // We only track the contents of scalar globals. + if (GV->getType()->getElementType()->isSingleValueType()) { LatticeVal &IV = TrackedGlobals[GV]; if (!isa<UndefValue>(GV->getInitializer())) IV.markConstant(GV->getInitializer()); @@ -233,6 +242,7 @@ public: void AddTrackedFunction(Function *F) { // Add an entry, F -> undef. if (const StructType *STy = dyn_cast<StructType>(F->getReturnType())) { + MRVFunctionsTracked.insert(F); for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) TrackedMultipleRetVals.insert(std::make_pair(std::make_pair(F, i), LatticeVal())); @@ -264,6 +274,13 @@ public: assert(I != ValueState.end() && "V is not in valuemap!"); return I->second; } + + LatticeVal getStructLatticeValueFor(Value *V, unsigned i) const { + DenseMap<std::pair<Value*, unsigned>, LatticeVal>::const_iterator I = + StructValueState.find(std::make_pair(V, i)); + assert(I != StructValueState.end() && "V is not in valuemap!"); + return I->second; + } /// getTrackedRetVals - Get the inferred return value map. /// @@ -278,9 +295,20 @@ public: } void markOverdefined(Value *V) { + assert(!isa<StructType>(V->getType()) && "Should use other method"); markOverdefined(ValueState[V], V); } + /// markAnythingOverdefined - Mark the specified value overdefined. This + /// works with both scalars and structs. + void markAnythingOverdefined(Value *V) { + if (const StructType *STy = dyn_cast<StructType>(V->getType())) + for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) + markOverdefined(getStructValueState(V, i), V); + else + markOverdefined(V); + } + private: // markConstant - Make a value be marked as "constant". If the value // is not already a constant, add it to the instruction work list so that @@ -293,10 +321,12 @@ private: } void markConstant(Value *V, Constant *C) { + assert(!isa<StructType>(V->getType()) && "Should use other method"); markConstant(ValueState[V], V, C); } void markForcedConstant(Value *V, Constant *C) { + assert(!isa<StructType>(V->getType()) && "Should use other method"); ValueState[V].markForcedConstant(C); DEBUG(errs() << "markForcedConstant: " << *C << ": " << *V << '\n'); InstWorkList.push_back(V); @@ -330,6 +360,7 @@ private: } void mergeInValue(Value *V, LatticeVal MergeWithV) { + assert(!isa<StructType>(V->getType()) && "Should use other method"); mergeInValue(ValueState[V], V, MergeWithV); } @@ -338,8 +369,12 @@ private: /// value. This function handles the case when the value hasn't been seen yet /// by properly seeding constants etc. LatticeVal &getValueState(Value *V) { + assert(!isa<StructType>(V->getType()) && "Should use getStructValueState"); + + // TODO: Change to do insert+find in one operation. DenseMap<Value*, LatticeVal>::iterator I = ValueState.find(V); - if (I != ValueState.end()) return I->second; // Common case, in the map + if (I != ValueState.end()) + return I->second; // Common case, already in the map. LatticeVal &LV = ValueState[V]; @@ -353,6 +388,39 @@ private: return LV; } + /// getStructValueState - Return the LatticeVal object that corresponds to the + /// value/field pair. This function handles the case when the value hasn't + /// been seen yet by properly seeding constants etc. + LatticeVal &getStructValueState(Value *V, unsigned i) { + assert(isa<StructType>(V->getType()) && "Should use getValueState"); + assert(i < cast<StructType>(V->getType())->getNumElements() && + "Invalid element #"); + + // TODO: Change to do insert+find in one operation. + DenseMap<std::pair<Value*, unsigned>, LatticeVal>::iterator + I = StructValueState.find(std::make_pair(V, i)); + if (I != StructValueState.end()) + return I->second; // Common case, already in the map. + + LatticeVal &LV = StructValueState[std::make_pair(V, i)]; + + if (Constant *C = dyn_cast<Constant>(V)) { + if (isa<UndefValue>(C)) + ; // Undef values remain undefined. + else if (ConstantStruct *CS = dyn_cast<ConstantStruct>(C)) + LV.markConstant(CS->getOperand(i)); // Constants are constant. + else if (isa<ConstantAggregateZero>(C)) { + const Type *FieldTy = cast<StructType>(V->getType())->getElementType(i); + LV.markConstant(Constant::getNullValue(FieldTy)); + } else + LV.markOverdefined(); // Unknown sort of constant. + } + + // All others are underdefined by default. + return LV; + } + + /// markEdgeExecutable - Mark a basic block as executable, adding it to the BB /// work list if it is not already executable. void markEdgeExecutable(BasicBlock *Source, BasicBlock *Dest) { @@ -444,12 +512,12 @@ private: void visitUnreachableInst(TerminatorInst &I) { /*returns void*/ } void visitAllocaInst (Instruction &I) { markOverdefined(&I); } void visitVANextInst (Instruction &I) { markOverdefined(&I); } - void visitVAArgInst (Instruction &I) { markOverdefined(&I); } + void visitVAArgInst (Instruction &I) { markAnythingOverdefined(&I); } void visitInstruction(Instruction &I) { // If a new instruction is added to LLVM that we don't handle. errs() << "SCCP: Don't know how to handle: " << I; - markOverdefined(&I); // Just in case + markAnythingOverdefined(&I); // Just in case } }; @@ -596,6 +664,11 @@ bool SCCPSolver::isEdgeFeasible(BasicBlock *From, BasicBlock *To) { // successors executable. // void SCCPSolver::visitPHINode(PHINode &PN) { + // If this PN returns a struct, just mark the result overdefined. + // TODO: We could do a lot better than this if code actually uses this. + if (isa<StructType>(PN.getType())) + return markAnythingOverdefined(&PN); + if (getValueState(&PN).isOverdefined()) { // There may be instructions using this PHI node that are not overdefined // themselves. If so, make sure that they know that the PHI node operand @@ -617,7 +690,7 @@ void SCCPSolver::visitPHINode(PHINode &PN) { // and slow us down a lot. Just mark them overdefined. if (PN.getNumIncomingValues() > 64) return markOverdefined(&PN); - + // Look at all of the executable operands of the PHI node. If any of them // are overdefined, the PHI becomes overdefined as well. If they are all // constant, and they agree with each other, the PHI becomes the identical @@ -666,28 +739,26 @@ void SCCPSolver::visitReturnInst(ReturnInst &I) { if (I.getNumOperands() == 0) return; // ret void Function *F = I.getParent()->getParent(); + Value *ResultOp = I.getOperand(0); // If we are tracking the return value of this function, merge it in. - if (!TrackedRetVals.empty()) { + if (!TrackedRetVals.empty() && !isa<StructType>(ResultOp->getType())) { DenseMap<Function*, LatticeVal>::iterator TFRVI = TrackedRetVals.find(F); if (TFRVI != TrackedRetVals.end()) { - mergeInValue(TFRVI->second, F, getValueState(I.getOperand(0))); + mergeInValue(TFRVI->second, F, getValueState(ResultOp)); return; } } // Handle functions that return multiple values. - if (!TrackedMultipleRetVals.empty() && - isa<StructType>(I.getOperand(0)->getType())) { - for (unsigned i = 0, e = I.getOperand(0)->getType()->getNumContainedTypes(); - i != e; ++i) { - DenseMap<std::pair<Function*, unsigned>, LatticeVal>::iterator - It = TrackedMultipleRetVals.find(std::make_pair(F, i)); - if (It == TrackedMultipleRetVals.end()) break; - if (Value *Val = FindInsertedValue(I.getOperand(0), i, I.getContext())) - mergeInValue(It->second, F, getValueState(Val)); - } + if (!TrackedMultipleRetVals.empty()) { + if (const StructType *STy = dyn_cast<StructType>(ResultOp->getType())) + if (MRVFunctionsTracked.count(F)) + for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) + mergeInValue(TrackedMultipleRetVals[std::make_pair(F, i)], F, + getStructValueState(ResultOp, i)); + } } @@ -712,78 +783,62 @@ void SCCPSolver::visitCastInst(CastInst &I) { OpSt.getConstant(), I.getType())); } -void SCCPSolver::visitExtractValueInst(ExtractValueInst &EVI) { - Value *Aggr = EVI.getAggregateOperand(); - - // If the operand to the extractvalue is an undef, the result is undef. - if (isa<UndefValue>(Aggr)) - return; - // Currently only handle single-index extractvalues. +void SCCPSolver::visitExtractValueInst(ExtractValueInst &EVI) { + // If this returns a struct, mark all elements over defined, we don't track + // structs in structs. + if (isa<StructType>(EVI.getType())) + return markAnythingOverdefined(&EVI); + + // If this is extracting from more than one level of struct, we don't know. if (EVI.getNumIndices() != 1) return markOverdefined(&EVI); - - Function *F = 0; - if (CallInst *CI = dyn_cast<CallInst>(Aggr)) - F = CI->getCalledFunction(); - else if (InvokeInst *II = dyn_cast<InvokeInst>(Aggr)) - F = II->getCalledFunction(); - - // TODO: If IPSCCP resolves the callee of this function, we could propagate a - // result back! - if (F == 0 || TrackedMultipleRetVals.empty()) - return markOverdefined(&EVI); - - // See if we are tracking the result of the callee. If not tracking this - // function (for example, it is a declaration) just move to overdefined. - if (!TrackedMultipleRetVals.count(std::make_pair(F, *EVI.idx_begin()))) - return markOverdefined(&EVI); - - // Otherwise, the value will be merged in here as a result of CallSite - // handling. + + Value *AggVal = EVI.getAggregateOperand(); + unsigned i = *EVI.idx_begin(); + LatticeVal EltVal = getStructValueState(AggVal, i); + mergeInValue(getValueState(&EVI), &EVI, EltVal); } void SCCPSolver::visitInsertValueInst(InsertValueInst &IVI) { - Value *Aggr = IVI.getAggregateOperand(); - Value *Val = IVI.getInsertedValueOperand(); - - // If the operands to the insertvalue are undef, the result is undef. - if (isa<UndefValue>(Aggr) && isa<UndefValue>(Val)) - return; - - // Currently only handle single-index insertvalues. - if (IVI.getNumIndices() != 1) + const StructType *STy = dyn_cast<StructType>(IVI.getType()); + if (STy == 0) return markOverdefined(&IVI); - - // Currently only handle insertvalue instructions that are in a single-use - // chain that builds up a return value. - for (const InsertValueInst *TmpIVI = &IVI; ; ) { - if (!TmpIVI->hasOneUse()) - return markOverdefined(&IVI); - - const Value *V = *TmpIVI->use_begin(); - if (isa<ReturnInst>(V)) - break; - TmpIVI = dyn_cast<InsertValueInst>(V); - if (!TmpIVI) - return markOverdefined(&IVI); - } - // See if we are tracking the result of the callee. - Function *F = IVI.getParent()->getParent(); - DenseMap<std::pair<Function*, unsigned>, LatticeVal>::iterator - It = TrackedMultipleRetVals.find(std::make_pair(F, *IVI.idx_begin())); - - // Merge in the inserted member value. - if (It != TrackedMultipleRetVals.end()) - mergeInValue(It->second, F, getValueState(Val)); - - // Mark the aggregate result of the IVI overdefined; any tracking that we do - // will be done on the individual member values. - markOverdefined(&IVI); + // If this has more than one index, we can't handle it, drive all results to + // undef. + if (IVI.getNumIndices() != 1) + return markAnythingOverdefined(&IVI); + + Value *Aggr = IVI.getAggregateOperand(); + unsigned Idx = *IVI.idx_begin(); + + // Compute the result based on what we're inserting. + for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) { + // This passes through all values that aren't the inserted element. + if (i != Idx) { + LatticeVal EltVal = getStructValueState(Aggr, i); + mergeInValue(getStructValueState(&IVI, i), &IVI, EltVal); + continue; + } + + Value *Val = IVI.getInsertedValueOperand(); + if (isa<StructType>(Val->getType())) + // We don't track structs in structs. + markOverdefined(getStructValueState(&IVI, i), &IVI); + else { + LatticeVal InVal = getValueState(Val); + mergeInValue(getStructValueState(&IVI, i), &IVI, InVal); + } + } } void SCCPSolver::visitSelectInst(SelectInst &I) { + // If this select returns a struct, just mark the result overdefined. + // TODO: We could do a lot better than this if code actually uses this. + if (isa<StructType>(I.getType())) + return markAnythingOverdefined(&I); + LatticeVal CondValue = getValueState(I.getCondition()); if (CondValue.isUndefined()) return; @@ -1011,7 +1066,7 @@ void SCCPSolver::visitCmpInst(CmpInst &I) { } void SCCPSolver::visitExtractElementInst(ExtractElementInst &I) { - // FIXME : SCCP does not handle vectors properly. + // TODO : SCCP does not handle vectors properly. return markOverdefined(&I); #if 0 @@ -1027,7 +1082,7 @@ void SCCPSolver::visitExtractElementInst(ExtractElementInst &I) { } void SCCPSolver::visitInsertElementInst(InsertElementInst &I) { - // FIXME : SCCP does not handle vectors properly. + // TODO : SCCP does not handle vectors properly. return markOverdefined(&I); #if 0 LatticeVal &ValState = getValueState(I.getOperand(0)); @@ -1051,7 +1106,7 @@ void SCCPSolver::visitInsertElementInst(InsertElementInst &I) { } void SCCPSolver::visitShuffleVectorInst(ShuffleVectorInst &I) { - // FIXME : SCCP does not handle vectors properly. + // TODO : SCCP does not handle vectors properly. return markOverdefined(&I); #if 0 LatticeVal &V1State = getValueState(I.getOperand(0)); @@ -1105,6 +1160,10 @@ void SCCPSolver::visitGetElementPtrInst(GetElementPtrInst &I) { } void SCCPSolver::visitStoreInst(StoreInst &SI) { + // If this store is of a struct, ignore it. + if (isa<StructType>(SI.getOperand(0)->getType())) + return; + if (TrackedGlobals.empty() || !isa<GlobalVariable>(SI.getOperand(1))) return; @@ -1122,6 +1181,10 @@ void SCCPSolver::visitStoreInst(StoreInst &SI) { // Handle load instructions. If the operand is a constant pointer to a constant // global, we can replace the load with the loaded constant value! void SCCPSolver::visitLoadInst(LoadInst &I) { + // If this load is of a struct, just mark the result overdefined. + if (isa<StructType>(I.getType())) + return markAnythingOverdefined(&I); + LatticeVal PtrVal = getValueState(I.getOperand(0)); if (PtrVal.isUndefined()) return; // The pointer is not resolved yet! @@ -1196,7 +1259,7 @@ CallOverdefined: } // Otherwise, we don't know anything about this call, mark it overdefined. - return markOverdefined(I); + return markAnythingOverdefined(I); } // If this is a local function that doesn't have its address taken, mark its @@ -1216,47 +1279,33 @@ CallOverdefined: continue; } - mergeInValue(AI, getValueState(*CAI)); + if (const StructType *STy = dyn_cast<StructType>(AI->getType())) { + for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) + mergeInValue(getStructValueState(AI, i), AI, + getStructValueState(*CAI, i)); + } else { + mergeInValue(AI, getValueState(*CAI)); + } } } // If this is a single/zero retval case, see if we're tracking the function. - DenseMap<Function*, LatticeVal>::iterator TFRVI = TrackedRetVals.find(F); - if (TFRVI != TrackedRetVals.end()) { - // If so, propagate the return value of the callee into this call result. - mergeInValue(I, TFRVI->second); - } else if (isa<StructType>(I->getType())) { - // Check to see if we're tracking this callee, if not, handle it in the - // common path above. - DenseMap<std::pair<Function*, unsigned>, LatticeVal>::iterator - TMRVI = TrackedMultipleRetVals.find(std::make_pair(F, 0)); - if (TMRVI == TrackedMultipleRetVals.end()) - goto CallOverdefined; - - // Need to mark as overdefined, otherwise it stays undefined which - // creates extractvalue undef, <idx> - markOverdefined(I); + if (const StructType *STy = dyn_cast<StructType>(F->getReturnType())) { + if (!MRVFunctionsTracked.count(F)) + goto CallOverdefined; // Not tracking this callee. - // If we are tracking this callee, propagate the return values of the call - // into this call site. We do this by walking all the uses. Single-index - // ExtractValueInst uses can be tracked; anything more complicated is - // currently handled conservatively. - for (Value::use_iterator UI = I->use_begin(), E = I->use_end(); - UI != E; ++UI) { - if (ExtractValueInst *EVI = dyn_cast<ExtractValueInst>(*UI)) { - if (EVI->getNumIndices() == 1) { - mergeInValue(EVI, - TrackedMultipleRetVals[std::make_pair(F, *EVI->idx_begin())]); - continue; - } - } - // The aggregate value is used in a way not handled here. Assume nothing. - markOverdefined(*UI); - } + // If we are tracking this callee, propagate the result of the function + // into this call site. + for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) + mergeInValue(getStructValueState(I, i), I, + TrackedMultipleRetVals[std::make_pair(F, i)]); } else { - // Otherwise we're not tracking this callee, so handle it in the - // common path above. - goto CallOverdefined; + DenseMap<Function*, LatticeVal>::iterator TFRVI = TrackedRetVals.find(F); + if (TFRVI == TrackedRetVals.end()) + goto CallOverdefined; // Not tracking this callee. + + // If so, propagate the return value of the callee into this call result. + mergeInValue(I, TFRVI->second); } } @@ -1297,7 +1346,7 @@ void SCCPSolver::Solve() { // since all of its users will have already been marked as overdefined. // Update all of the users of this instruction's value. // - if (!getValueState(I).isOverdefined()) + if (isa<StructType>(I->getType()) || !getValueState(I).isOverdefined()) for (Value::use_iterator UI = I->use_begin(), E = I->use_end(); UI != E; ++UI) if (Instruction *I = dyn_cast<Instruction>(*UI)) @@ -1345,13 +1394,35 @@ bool SCCPSolver::ResolvedUndefsIn(Function &F) { // Look for instructions which produce undef values. if (I->getType()->isVoidTy()) continue; + if (const StructType *STy = dyn_cast<StructType>(I->getType())) { + // Only a few things that can be structs matter for undef. Just send + // all their results to overdefined. We could be more precise than this + // but it isn't worth bothering. + if (isa<CallInst>(I) || isa<SelectInst>(I)) { + for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) { + LatticeVal &LV = getStructValueState(I, i); + if (LV.isUndefined()) + markOverdefined(LV, I); + } + } + continue; + } + LatticeVal &LV = getValueState(I); if (!LV.isUndefined()) continue; + // No instructions using structs need disambiguation. + if (isa<StructType>(I->getOperand(0)->getType())) + continue; + // Get the lattice values of the first two operands for use below. LatticeVal Op0LV = getValueState(I->getOperand(0)); LatticeVal Op1LV; if (I->getNumOperands() == 2) { + // No instructions using structs need disambiguation. + if (isa<StructType>(I->getOperand(1)->getType())) + continue; + // If this is a two-operand instruction, and if both operands are // undefs, the result stays undef. Op1LV = getValueState(I->getOperand(1)); @@ -1547,7 +1618,7 @@ bool SCCP::runOnFunction(Function &F) { // Mark all arguments to the function as being overdefined. for (Function::arg_iterator AI = F.arg_begin(), E = F.arg_end(); AI != E;++AI) - Solver.markOverdefined(AI); + Solver.markAnythingOverdefined(AI); // Solve for constants. bool ResolvedUndefs = true; @@ -1578,6 +1649,10 @@ bool SCCP::runOnFunction(Function &F) { if (Inst->getType()->isVoidTy() || isa<TerminatorInst>(Inst)) continue; + // TODO: Reconstruct structs from their elements. + if (isa<StructType>(Inst->getType())) + continue; + LatticeVal IV = Solver.getLatticeValueFor(Inst); if (IV.isOverdefined()) continue; @@ -1661,8 +1736,7 @@ bool IPSCCP::runOnModule(Module &M) { // If this is a strong or ODR definition of this function, then we can // propagate information about its result into callsites of it. - if (!F->mayBeOverridden() && - !isa<StructType>(F->getReturnType())) + if (!F->mayBeOverridden()) Solver.AddTrackedFunction(F); // If this function only has direct calls that we can see, we can track its @@ -1679,7 +1753,7 @@ bool IPSCCP::runOnModule(Module &M) { // Assume nothing about the incoming arguments. for (Function::arg_iterator AI = F->arg_begin(), E = F->arg_end(); AI != E; ++AI) - Solver.markOverdefined(AI); + Solver.markAnythingOverdefined(AI); } // Loop over global variables. We inform the solver about any internal global @@ -1712,8 +1786,11 @@ bool IPSCCP::runOnModule(Module &M) { if (Solver.isBlockExecutable(F->begin())) { for (Function::arg_iterator AI = F->arg_begin(), E = F->arg_end(); AI != E; ++AI) { - if (AI->use_empty()) continue; + if (AI->use_empty() || isa<StructType>(AI->getType())) continue; + // TODO: Could use getStructLatticeValueFor to find out if the entire + // result is a constant and replace it entirely if so. + LatticeVal IV = Solver.getLatticeValueFor(AI); if (IV.isOverdefined()) continue; @@ -1752,9 +1829,12 @@ bool IPSCCP::runOnModule(Module &M) { for (BasicBlock::iterator BI = BB->begin(), E = BB->end(); BI != E; ) { Instruction *Inst = BI++; - if (Inst->getType()->isVoidTy()) + if (Inst->getType()->isVoidTy() || isa<StructType>(Inst->getType())) continue; + // TODO: Could use getStructLatticeValueFor to find out if the entire + // result is a constant and replace it entirely if so. + LatticeVal IV = Solver.getLatticeValueFor(Inst); if (IV.isOverdefined()) continue; diff --git a/test/Transforms/SCCP/ipsccp-basic.ll b/test/Transforms/SCCP/ipsccp-basic.ll index 11edcf48be..e3699209a3 100644 --- a/test/Transforms/SCCP/ipsccp-basic.ll +++ b/test/Transforms/SCCP/ipsccp-basic.ll @@ -1,5 +1,4 @@ ; RUN: opt < %s -ipsccp -S | FileCheck %s -; XFAIL: * ;;======================== test1 @@ -128,7 +127,7 @@ B: ; CHECK: define i64 @test5b() ; CHECK: A: ; CHECK-NEXT: %c = call i64 @test5c(%0 %a) -; CHECK-NEXT: ret i64 %c +; CHECK-NEXT: ret i64 5 define internal i64 @test5c({i64,i64} %a) { %b = extractvalue {i64,i64} %a, 0 |