Index: llvm/include/llvm/Transforms/IPO/IROutliner.h =================================================================== --- llvm/include/llvm/Transforms/IPO/IROutliner.h +++ llvm/include/llvm/Transforms/IPO/IROutliner.h @@ -81,6 +81,12 @@ DenseMap ExtractedArgToAgg; DenseMap AggArgToExtracted; + /// Mapping of the argument number in the deduplicated function + /// to a given constant, which is used when creating the arguments to the call + /// to the newly created deduplicated function. This is handled separately + /// since the CodeExtractor does not recognize constants. + DenseMap AggArgToConstant; + /// Used to create an outlined function. CodeExtractor *CE = nullptr; @@ -180,8 +186,11 @@ /// function if needed. /// /// \param [in] M - The module to outline from. - /// \param [in,out] Region - The region to be extracted - void findAddInputsOutputs(Module &M, OutlinableRegion &Region); + /// \param [in,out] Region - The region to be extracted. + /// \param [in] NotSame - The global value numbers of the Values in the region + /// that do not have the same Constant in each strucutrally similar region. + void findAddInputsOutputs(Module &M, OutlinableRegion &Region, + DenseSet &NotSame); /// Extract \p Region into its own function. /// Index: llvm/lib/Transforms/IPO/IROutliner.cpp =================================================================== --- llvm/lib/Transforms/IPO/IROutliner.cpp +++ llvm/lib/Transforms/IPO/IROutliner.cpp @@ -51,7 +51,7 @@ bool IgnoreGroup = false; /// Flag for whether the \ref ArgumentTypes have been defined after the - /// exraction of the first region. + /// extraction of the first region. bool InputTypesSet = false; /// The number of input values in \ref ArgumentTypes. Anything after this @@ -210,6 +210,8 @@ collectRegionsConstants(OutlinableRegion &Region, DenseMap &GVNToConstant, DenseSet &NotSame) { + bool ConstantsTheSame = true; + IRSimilarityCandidate &C = *Region.Candidate; for (IRInstructionData &ID : C) { @@ -221,11 +223,10 @@ assert(GVNOpt.hasValue() && "Expected a GVN for operand?"); unsigned GVN = GVNOpt.getValue(); - // If this global value has been found to not be the same, it could have - // just been a register, check that it is not a constant value. + // Check if this global value has been found to not be the same already. if (NotSame.find(GVN) != NotSame.end()) { if (isa(V)) - return false; + ConstantsTheSame = false; continue; } @@ -238,30 +239,27 @@ if (ConstantMatches.getValue()) continue; else - return false; + ConstantsTheSame = false; } // While this value is a register, it might not have been previously, // make sure we don't already have a constant mapped to this global value // number. if (GVNToConstant.find(GVN) != GVNToConstant.end()) - return false; + ConstantsTheSame = false; NotSame.insert(GVN); } } - return true; + return ConstantsTheSame; } void OutlinableGroup::findSameConstants(DenseSet &NotSame) { DenseMap GVNToConstant; for (OutlinableRegion *Region : Regions) - if (!collectRegionsConstants(*Region, GVNToConstant, NotSame)) { - IgnoreGroup = true; - return; - } + collectRegionsConstants(*Region, GVNToConstant, NotSame); } Function *IROutliner::createFunction(Module &M, OutlinableGroup &Group, @@ -306,16 +304,44 @@ return NewEnd; } -/// Find the GVN for the inputs that have been found by the CodeExtractor, -/// excluding the ones that will be removed by llvm.assumes as these will be -/// removed by the CodeExtractor. +/// Find the the constants that will need to be lifted into arguments +/// as they are not the same in each instance of the region. +/// +/// \param [in] C - The IRSimilarityCandidate containing the region we are +/// analyzing. +/// \param [in] NotSame - The set of global value numbers that do not have a +/// single Constant across all OutlinableRegions similar to \p C. +/// \param [out] Inputs - The list containing the global value numbers of the +/// arguments needed for the region of code. +static void findConstants(IRSimilarityCandidate &C, DenseSet &NotSame, + std::vector &Inputs) { + DenseSet InInput; + // Iterate over the instructions, and find what constants will need to be + // extracted into arguments. + for (IRInstructionDataList::iterator IDIt = C.begin(), EndIDIt = C.end(); + IDIt != EndIDIt; IDIt++) { + for (Value *V : (*IDIt).OperVals) { + // Since these are stored before any outlining, they will be in the + // global value numbering. + unsigned GVN = C.getGVN(V).getValue(); + if (Constant *CST = dyn_cast(V)) + if (NotSame.find(GVN) != NotSame.end() && + InInput.find(GVN) == InInput.end()) { + Inputs.push_back(GVN); + InInput.insert(GVN); + } + } + } +} + +/// Find the GVN for the inputs that have been found by the CodeExtractor. /// /// \param [in] C - The IRSimilarityCandidate containing the region we are /// analyzing. /// \param [in] CurrentInputs - The set of inputs found by the /// CodeExtractor. -/// \param [out] CurrentInputNumbers - The global value numbers for the -/// extracted arguments. +/// \param [out] EndInputNumbers - The global value numbers for the extracted +/// arguments. static void mapInputsToGVNs(IRSimilarityCandidate &C, SetVector &CurrentInputs, std::vector &EndInputNumbers) { @@ -331,15 +357,19 @@ /// Find the input GVNs and the output values for a region of Instructions. /// Using the code extractor, we collect the inputs to the extracted function. /// -/// The \p Region can be identifed as needing to be ignored in this function. +/// The \p Region can be identified as needing to be ignored in this function. /// It should be checked whether it should be ignored after a call to this /// function. /// /// \param [in,out] Region - The region of code to be analyzed. /// \param [out] Inputs - The global value numbers for the extracted arguments. +/// \param [in] NotSame - The global value numbers in the region that do not +/// have the same constant value in the regions structurally similar to +/// \p Region. /// \param [out] ArgInputs - The values of the inputs to the extracted function. static void getCodeExtractorArguments(OutlinableRegion &Region, std::vector &InputGVNs, + DenseSet &NotSame, SetVector &ArgInputs) { IRSimilarityCandidate &C = *Region.Candidate; @@ -387,13 +417,18 @@ return; } + findConstants(C, NotSame, InputGVNs); mapInputsToGVNs(C, OverallInputs, InputGVNs); + + // Sort the GVNs, since we now have constants included in the \ref InputGVNs + // we need to make sure they are in a deterministic order. + stable_sort(InputGVNs.begin(), InputGVNs.end()); } /// Look over the inputs and map each input argument to an argument in the -/// overall function for the regions. This creates a way to replace the -/// arguments of the extracted function, with the arguments of the new overall -/// function. +/// overall function for the OutlinableRegions. This creates a way to replace +/// the arguments of the extracted function with the arguments of the new +/// overall function. /// /// \param [in,out] Region - The region of code to be analyzed. /// \param [in] InputsGVNs - The global value numbering of the input values @@ -415,7 +450,10 @@ unsigned OriginalIndex = 0; // Find the mapping of the extracted arguments to the arguments for the - // overall function. + // overall function. Since there may be extra arguments in the overall + // function to account for the extracted constants, we have two different + // counters as we find extracted arguments, and as we come across overall + // arguments. for (unsigned InputVal : InputGVNs) { Optional InputOpt = C.fromGVN(InputVal); assert(InputOpt.hasValue() && "Global value number not found?"); @@ -424,9 +462,16 @@ if (!Group.InputTypesSet) Group.ArgumentTypes.push_back(Input->getType()); - // It is not a constant, check if it is a sunken alloca. If it is not, - // create the mapping from extracted to overall. If it is, create the - // mapping of the index to the value. + // Check if we have a constant. If we do add it to the overall argument + // number to Constant map for the region, and continue to the next input. + if (Constant *CST = dyn_cast(Input)) { + Region.AggArgToConstant.insert(std::make_pair(TypeIndex, CST)); + TypeIndex++; + continue; + } + + // It is not a constant, we create the mapping from extracted argument list + // to the overall argument list. unsigned Found = ArgInputs.count(Input); assert(Found && "Input cannot be found!"); @@ -436,10 +481,10 @@ TypeIndex++; } - // If we do not have definitions for the OutlinableGroup holding the region, - // set the length of the inputs here. We should have the same inputs for - // all of the different regions contained in the OutlinableGroup since they - // are all structurally similar to one another + // If the function type definitions for the OutlinableGroup holding the region + // have not been set, set the length of the inputs here. We should have the + // same inputs for all of the different regions contained in the + // OutlinableGroup since they are all structurally similar to one another. if (!Group.InputTypesSet) { Group.NumAggregateInputs = TypeIndex; Group.InputTypesSet = true; @@ -448,11 +493,12 @@ Region.NumExtractedInputs = OriginalIndex; } -void IROutliner::findAddInputsOutputs(Module &M, OutlinableRegion &Region) { +void IROutliner::findAddInputsOutputs(Module &M, OutlinableRegion &Region, + DenseSet &NotSame) { std::vector Inputs; SetVector ArgInputs; - getCodeExtractorArguments(Region, Inputs, ArgInputs); + getCodeExtractorArguments(Region, Inputs, NotSame, ArgInputs); if (Region.IgnoreRegion) return; @@ -484,12 +530,72 @@ // If the arguments are the same size, there are not values that need to be // made argument, or different output registers to handle. We can simply // replace the called function in this case. - assert(AggFunc->arg_size() == Call->arg_size() && - "Can only replace calls with the same number of arguments!"); + if (AggFunc->arg_size() == Call->arg_size()) { + LLVM_DEBUG(dbgs() << "Replace call to " << *Call << " with call to " + << *AggFunc << " with same number of arguments\n"); + Call->setCalledFunction(AggFunc); + return Call; + } + + // We have a different number of arguments than the new function, so + // we need to use our previously mappings off extracted argument to overall + // function argument, and constants to overall function argument to create the + // new argument list. + for (unsigned AggArgIdx = 0; AggArgIdx < AggFunc->arg_size(); AggArgIdx++) { + + ArgPair = Region.AggArgToExtracted.find(AggArgIdx); + if (ArgPair != Region.AggArgToExtracted.end()) { + Value *ArgumentValue = Call->getArgOperand(ArgPair->second); + // If we found the mapping from the extracted function to the overall + // function, we simply add it to the argument list. We use the same + // value, it just needs to honor the new order of arguments. + LLVM_DEBUG(dbgs() << "Setting argument " << AggArgIdx << " to value " + << *ArgumentValue << "\n"); + NewCallArgs.push_back(ArgumentValue); + continue; + } + + // If it is a constant, we simply add it to the argument list as a value. + if (Region.AggArgToConstant.find(AggArgIdx) != + Region.AggArgToConstant.end()) { + Constant *CST = Region.AggArgToConstant.find(AggArgIdx)->second; + LLVM_DEBUG(dbgs() << "Setting argument " << AggArgIdx << " to value " + << *CST << "\n"); + NewCallArgs.push_back(CST); + continue; + } + + // Add a nullptr value if the argument is not found in the extracted + // function. If we cannot find a value, it means it is not in use + // for the region, so we should not pass anything to it. + LLVM_DEBUG(dbgs() << "Setting argument " << AggArgIdx << " to nullptr\n"); + NewCallArgs.push_back(ConstantPointerNull::get( + static_cast(AggFunc->getArg(AggArgIdx)->getType()))); + } LLVM_DEBUG(dbgs() << "Replace call to " << *Call << " with call to " - << *AggFunc << " with same number of arguments\n"); - Call->setCalledFunction(AggFunc); + << *AggFunc << " with new set of arguments\n"); + // Create the new call instruction and erase the old one. + Call = CallInst::Create(AggFunc->getFunctionType(), AggFunc, NewCallArgs, "", + Call); + + // It is possible that the call to the outlined function is either the first + // instruction in the new block, the last instruction, or both. If either of + // these is the case, we need to make sure that we replace the instruction in + // the IRInstructionData struct with the new call. + CallInst *OldCall = Region.Call; + if (Region.NewFront->Inst == OldCall) + Region.NewFront->Inst = Call; + if (Region.NewBack->Inst == OldCall) + Region.NewBack->Inst = Call; + + // Transfer any debug information. + Call->setDebugLoc(Region.Call->getDebugLoc()); + + // Remove the old instruction. + OldCall->eraseFromParent(); + Region.Call = Call; + return Call; } @@ -518,6 +624,37 @@ } } +/// Within an extracted function, replace the constants that need to be lifted +/// into arguments with the actual argument. +/// +/// \param Region [in] - The region of extracted code to be changed. +void replaceConstants(OutlinableRegion &Region) { + OutlinableGroup &Group = *Region.Parent; + // Iterate over the constants that need to be elevated into arguments + for (std::pair &Const : Region.AggArgToConstant) { + unsigned AggArgIdx = Const.first; + Function *OutlinedFunction = Group.OutlinedFunction; + assert(OutlinedFunction && "Overall Function is not defined?"); + Constant *CST = Const.second; + Argument *Arg = Group.OutlinedFunction->getArg(AggArgIdx); + // Identify the argument it will be elevated to, and replace instances of + // that constant in the function. + + // TODO: If in the future constants do not have one global value number, + // i.e. a constant 1 could be mapped to several values, this check will + // have to be more strict. It cannot be using only replaceUsesWithIf. + + LLVM_DEBUG(dbgs() << "Replacing uses of constant " << *CST + << " in function " << *OutlinedFunction << " with " + << *Arg << "\n"); + CST->replaceUsesWithIf(Arg, [OutlinedFunction](Use &U) { + if (Instruction *I = dyn_cast(U.getUser())) + return I->getFunction() == OutlinedFunction; + return false; + }); + } +} + /// Fill the new function that will serve as the replacement function for all of /// the extracted regions of a certain structure from the first region in the /// list of regions. Replace this first region's extracted function with the @@ -544,6 +681,7 @@ CurrentGroup.OutlinedFunction->addFnAttr(A); replaceArgumentUses(*CurrentOS); + replaceConstants(*CurrentOS); // Replace the call to the extracted function with the outlined function. CurrentOS->Call = replaceCalledFunction(M, *CurrentOS); @@ -738,7 +876,7 @@ OS->CE = new (OutlinerAllocator.Allocate()) CodeExtractor(BE, nullptr, false, nullptr, nullptr, nullptr, false, false, "outlined"); - findAddInputsOutputs(M, *OS); + findAddInputsOutputs(M, *OS, NotSame); if (!OS->IgnoreRegion) OutlinedRegions.push_back(OS); else Index: llvm/test/Transforms/IROutliner/outlining-constants-vs-registers.ll =================================================================== --- llvm/test/Transforms/IROutliner/outlining-constants-vs-registers.ll +++ llvm/test/Transforms/IROutliner/outlining-constants-vs-registers.ll @@ -15,14 +15,10 @@ ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 [[TMP0:%.*]], i32* [[A]], align 4 -; CHECK-NEXT: store i32 [[TMP1:%.*]], i32* [[B]], align 4 -; CHECK-NEXT: store i32 [[TMP2:%.*]], i32* [[C]], align 4 +; CHECK-NEXT: call void @outlined_ir_func_1(i32 [[TMP0:%.*]], i32* [[A]], i32 [[TMP1:%.*]], i32* [[B]], i32 [[TMP2:%.*]], i32* [[C]]) ; CHECK-NEXT: ret void ; CHECK: next: -; CHECK-NEXT: store i32 2, i32* [[A]], align 4 -; CHECK-NEXT: store i32 3, i32* [[B]], align 4 -; CHECK-NEXT: store i32 4, i32* [[C]], align 4 +; CHECK-NEXT: call void @outlined_ir_func_1(i32 2, i32* [[A]], i32 3, i32* [[B]], i32 4, i32* [[C]]) ; CHECK-NEXT: ret void ; entry: @@ -40,7 +36,7 @@ ret void } -define void @function_with_constants_first() { +define void @function_with_constants_first(i32 %0, i32 %1, i32 %2) { ; CHECK-LABEL: @function_with_constants_first( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 @@ -49,14 +45,10 @@ ; CHECK-NEXT: [[AL:%.*]] = load i32, i32* [[A]], align 4 ; CHECK-NEXT: [[BL:%.*]] = load i32, i32* [[B]], align 4 ; CHECK-NEXT: [[CL:%.*]] = load i32, i32* [[C]], align 4 -; CHECK-NEXT: [[TMP0:%.*]] = add i32 2, [[AL]] -; CHECK-NEXT: [[TMP1:%.*]] = add i32 3, [[BL]] -; CHECK-NEXT: [[TMP2:%.*]] = add i32 4, [[CL]] +; CHECK-NEXT: call void @outlined_ir_func_0(i32 2, i32 [[AL]], i32 3, i32 [[BL]], i32 4, i32 [[CL]]) ; CHECK-NEXT: ret void ; CHECK: next: -; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP0]], [[AL]] -; CHECK-NEXT: [[TMP4:%.*]] = add i32 [[TMP1]], [[BL]] -; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[TMP2]], [[CL]] +; CHECK-NEXT: call void @outlined_ir_func_0(i32 [[TMP0:%.*]], i32 [[AL]], i32 [[TMP1:%.*]], i32 [[BL]], i32 [[TMP2:%.*]], i32 [[CL]]) ; CHECK-NEXT: ret void ; entry: @@ -66,13 +58,25 @@ %al = load i32, i32* %a %bl = load i32, i32* %b %cl = load i32, i32* %c - %0 = add i32 2, %al - %1 = add i32 3, %bl - %2 = add i32 4, %cl + %3 = add i32 2, %al + %4 = add i32 3, %bl + %5 = add i32 4, %cl ret void next: - %3 = add i32 %0, %al - %4 = add i32 %1, %bl - %5 = add i32 %2, %cl + %6 = add i32 %0, %al + %7 = add i32 %1, %bl + %8 = add i32 %2, %cl ret void } + +; CHECK: define internal void @outlined_ir_func_0(i32 [[ARG0:%.*]], i32 [[ARG1:%.*]], i32 [[ARG2:%.*]], i32 [[ARG3:%.*]], i32 [[ARG4:%.*]], i32 [[ARG5:%.*]]) +; CHECK: entry_to_outline: +; CHECK-NEXT: add i32 [[ARG0]], [[ARG1]] +; CHECK-NEXT: add i32 [[ARG2]], [[ARG3]] +; CHECK-NEXT: add i32 [[ARG4]], [[ARG5]] + +; CHECK: define internal void @outlined_ir_func_1(i32 [[ARG0:%.*]], i32* [[ARG1:%.*]], i32 [[ARG2:%.*]], i32* [[ARG3:%.*]], i32 [[ARG4:%.*]], i32* [[ARG5:%.*]]) +; CHECK: entry_to_outline: +; CHECK-NEXT: store i32 [[ARG0]], i32* [[ARG1]] +; CHECK-NEXT: store i32 [[ARG2]], i32* [[ARG3]] +; CHECK-NEXT: store i32 [[ARG4]], i32* [[ARG5]] Index: llvm/test/Transforms/IROutliner/outlining-different-constants.ll =================================================================== --- llvm/test/Transforms/IROutliner/outlining-different-constants.ll +++ llvm/test/Transforms/IROutliner/outlining-different-constants.ll @@ -2,8 +2,7 @@ ; RUN: opt -S -verify -iroutliner < %s | FileCheck %s ; This test looks at the constants in the regions, and if it they are the -; differents it does not outline them as they cannot be consolidated into the -; the same function. +; differents it elevates the constants to arguments. define void @outline_constants1() { ; CHECK-LABEL: @outline_constants1( @@ -11,10 +10,7 @@ ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 3, i32* [[A]], align 4 -; CHECK-NEXT: store i32 4, i32* [[B]], align 4 -; CHECK-NEXT: store i32 5, i32* [[C]], align 4 -; CHECK-NEXT: call void @[[FUNCTION_0:.*]](i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: call void @outlined_ir_func_0(i32 3, i32* [[A]], i32 4, i32* [[B]], i32 5, i32* [[C]]) ; CHECK-NEXT: ret void ; entry: @@ -36,10 +32,7 @@ ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 -; CHECK-NEXT: store i32 2, i32* [[A]], align 4 -; CHECK-NEXT: store i32 3, i32* [[B]], align 4 -; CHECK-NEXT: store i32 4, i32* [[C]], align 4 -; CHECK-NEXT: call void @[[FUNCTION_0]](i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: call void @outlined_ir_func_0(i32 2, i32* [[A]], i32 3, i32* [[B]], i32 4, i32* [[C]]) ; CHECK-NEXT: ret void ; entry: @@ -55,8 +48,11 @@ ret void } -; CHECK: define internal void @[[FUNCTION_0]](i32* [[ARG0:%.*]], i32* [[ARG1:%.*]], i32* [[ARG2:%.*]]) +; CHECK: define internal void @outlined_ir_func_0(i32 [[ARG0:%.*]], i32* [[ARG1:%.*]], i32 [[ARG2:%.*]], i32* [[ARG3:%.*]], i32 [[ARG4:%.*]], i32* [[ARG5:%.*]]) #0 { ; CHECK: entry_to_outline: -; CHECK-NEXT: [[AL:%.*]] = load i32, i32* [[ARG0]], align 4 -; CHECK-NEXT: [[BL:%.*]] = load i32, i32* [[ARG1]], align 4 -; CHECK-NEXT: [[CL:%.*]] = load i32, i32* [[ARG2]], align 4 +; CHECK-NEXT: store i32 [[ARG0]], i32* [[ARG1]], align 4 +; CHECK-NEXT: store i32 [[ARG2]], i32* [[ARG3]], align 4 +; CHECK-NEXT: store i32 [[ARG4]], i32* [[ARG5]], align 4 +; CHECK-NEXT: [[AL:%.*]] = load i32, i32* [[ARG1]], align 4 +; CHECK-NEXT: [[BL:%.*]] = load i32, i32* [[ARG3]], align 4 +; CHECK-NEXT: [[CL:%.*]] = load i32, i32* [[ARG5]], align 4 Index: llvm/test/Transforms/IROutliner/outlining-different-globals.ll =================================================================== --- llvm/test/Transforms/IROutliner/outlining-different-globals.ll +++ llvm/test/Transforms/IROutliner/outlining-different-globals.ll @@ -12,9 +12,7 @@ define void @outline_globals1() { ; CHECK-LABEL: @outline_globals1( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @global1, align 4 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* @global2, align 4 -; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP0]], [[TMP1]] +; CHECK-NEXT: call void @outlined_ir_func_0(i32* @global1, i32* @global2) ; CHECK-NEXT: ret void ; entry: @@ -27,9 +25,7 @@ define void @outline_globals2() { ; CHECK-LABEL: @outline_globals2( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @global3, align 4 -; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* @global4, align 4 -; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP0]], [[TMP1]] +; CHECK-NEXT: call void @outlined_ir_func_0(i32* @global3, i32* @global4) ; CHECK-NEXT: ret void ; entry: @@ -38,3 +34,9 @@ %2 = add i32 %0, %1 ret void } + +; CHECK: define internal void @outlined_ir_func_0(i32* [[ARG0:%.*]], i32* [[ARG1:%.*]]) +; CHECK: entry_to_outline: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[ARG0]] +; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[ARG1]] +; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[TMP0]], [[TMP1]]