Index: include/llvm/Analysis/ImportArguments.h =================================================================== --- include/llvm/Analysis/ImportArguments.h +++ include/llvm/Analysis/ImportArguments.h @@ -0,0 +1,71 @@ +//===- ImportArguments.h - Imports arguments --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This module pass displays constants and simple constraints applied to integer +// arguments for imported functions. It can help to infer how an API is used. +// +// For example, if the mmap and mprotect libc function never use the PROT_EXEC +// flag; it can be assumed the module does not generate dynamic code. +// +// It outputs a YAML format: +// +// caller: check_set_after +// import: externalfunction +// arguments: +// 1: +// - type: eq +// value: 3 +// ... +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_IMPORT_ARGS_H +#define LLVM_ANALYSIS_IMPORT_ARGS_H + +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +namespace import_args { + +// Use Float condition for AND predicates +#define ICMP_AND CmpInst::FCMP_TRUE +#define ICMP_NAND CmpInst::FCMP_FALSE + +// Represents a predicate on an argument +struct Expression { + CmpInst::Predicate Type; + uint64_t Value; +}; + +typedef std::vector ExpressionList; +typedef std::map ArgumentMap; + +struct ImportCall { + std::string Caller; + std::string Import; + ArgumentMap Arguments; + Optional Filename; + Optional LineNumber; +}; + +} // end namespace import_args + +class ImportArgumentsPrinterPass : public PassInfoMixin { + raw_ostream &OS; + + public: + explicit ImportArgumentsPrinterPass(raw_ostream &OS) : OS(OS) {} + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +} // end namespace llvm + +#endif Index: lib/Analysis/CMakeLists.txt =================================================================== --- lib/Analysis/CMakeLists.txt +++ lib/Analysis/CMakeLists.txt @@ -30,6 +30,7 @@ EHPersonalities.cpp GlobalsModRef.cpp IVUsers.cpp + ImportArguments.cpp IndirectCallPromotionAnalysis.cpp InlineCost.cpp InstCount.cpp Index: lib/Analysis/ImportArguments.cpp =================================================================== --- lib/Analysis/ImportArguments.cpp +++ lib/Analysis/ImportArguments.cpp @@ -0,0 +1,353 @@ +//===- ImportArguments.cpp - Imports arguments ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This module pass displays constants and simple constraints applied to integer +// arguments for imported functions. It can help to infer how an API is used. +// +// For example, if the mmap and mprotect libc function never use the PROT_EXEC +// flag; it can be assumed the module does not generate dynamic code. +// +// It outputs a YAML format: +// +// caller: check_set_after +// import: externalfunction +// arguments: +// 1: +// - type: eq +// value: 3 +// ... +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/ImportArguments.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/Dominators.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace llvm; + +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::import_args::Expression) + +namespace llvm { + +namespace yaml { + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, CmpInst::Predicate &Type) { + IO.enumCase(Type, "and", ICMP_AND); + IO.enumCase(Type, "nand", ICMP_NAND); + for (uint32_t i = CmpInst::FIRST_ICMP_PREDICATE; + i <= CmpInst::LAST_ICMP_PREDICATE; + i++) { + auto V = static_cast(i); + auto N = CmpInst::getPredicateName(V); + IO.enumCase(Type, N.data(), V); + } + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, import_args::Expression &Entry) { + IO.mapRequired("type", Entry.Type); + IO.mapRequired("value", Entry.Value); + } +}; + +template <> struct CustomMappingTraits { + static void inputOne(IO &IO, StringRef Key, import_args::ArgumentMap &V) { + uint32_t KeyInt; + if (Key.getAsInteger(0, KeyInt)) { + IO.setError("key not integer"); + return; + } + IO.mapRequired(Key.str().c_str(), V[KeyInt]); + } + static void output(IO &IO, import_args::ArgumentMap &V) { + for (auto &E : V) + IO.mapRequired(utostr(E.first).c_str(), E.second); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, import_args::ImportCall &Entry) { + IO.mapRequired("caller", Entry.Caller); + IO.mapRequired("import", Entry.Import); + IO.mapRequired("arguments", Entry.Arguments); + IO.mapOptional("filename", Entry.Filename); + IO.mapOptional("line", Entry.LineNumber); + } +}; + +} // end namespace yaml + +class ImportArgumentAnalysis { + FunctionAnalysisManager *FAM; + std::vector Result; + + // Return true if the function is an external dependency + // An external function has one reference to a null function (external node) + bool isExternalFunction(CallGraph &CG, const Function *F) { + if (!F) + return false; + + const CallGraphNode *N = CG[F]; + if (N->size() != 1 || N->begin()->second->getFunction() != nullptr) + return false; + + return true; + } + + // Return false if the value is not a Constant integer + bool addConstantIntToExpressionList(const Value *V, import_args::ExpressionList& EList, + CmpInst::Predicate CT = CmpInst::ICMP_EQ) { + if (!isa(V)) + return false; + + import_args::Expression E; + E.Type = CT; + E.Value = *cast(V)->getValue().getRawData(); + EList.push_back(E); + return true; + } + + // Shortcut to get the operand not equal to a specific value + const Value *getFirstOtherOperand(const Instruction *I, const Value *R) { + unsigned Number = I->getNumOperands(); + for (unsigned Index = 0; Index < Number; Index++) { + const Value *V = I->getOperand(Index); + if (V != NULL && V != R) + return V; + } + return NULL; + } + + // Ensure only one path leads to the target and set the predicate on that edge + bool verifyDominateEdgeCmp(DominatorTree &DOM, const ICmpInst *Cmp, Instruction *I, + CmpInst::Predicate *P) { + const BasicBlock *Block = Cmp->getParent(); + const Instruction *Terminator = Block->getTerminator(); + + if (Terminator == NULL || !isa(Terminator)) + return false; + + const BranchInst *Branch = cast(Terminator); + if (Branch->isUnconditional() || Branch->getCondition() != Cmp) + return false; + + BasicBlock *Target = I->getParent(); + BasicBlockEdge FirstBlock(Block, Branch->getSuccessor(0)); + BasicBlockEdge SecondBlock(Block, Branch->getSuccessor(1)); + bool D = DOM.dominates(SecondBlock, Target); + if (DOM.dominates(FirstBlock, Target) == D) + return false; + *P = Cmp->getPredicate(); + if (D) + *P = CmpInst::getInversePredicate(*P); + return true; + } + + // Identify straightforward constraints to fill the expression list + void identifyConstraint(DominatorTree &DOM, const Instruction *UI, + Instruction *Src, import_args::ExpressionList &C, + const Value *L) { + CmpInst::Predicate Type = CmpInst::ICMP_EQ; + const Value *V = nullptr; + + // Matching and -> cmp constraint + if (UI->getOpcode() == Instruction::And) { + V = getFirstOtherOperand(UI, L); + if (!isa(V) || !UI->hasOneUse()) + return; + UI = UI->user_back(); + Type = ICMP_AND; + } + + // Handle load/cmp and load/and/cmp branching + if (isa(UI)) { + CmpInst::Predicate Pred; + const ICmpInst *Cmp = dyn_cast(UI); + + if (Type == ICMP_AND && !Cmp->isEquality()) + return; + + // Check that only one edge dominates the target, therefore it + // applies a straightforward constraint. Also output this edge + // predicate. + if (!verifyDominateEdgeCmp(DOM, Cmp, Src, &Pred)) + return; + + if (Type == ICMP_AND) { + if (Pred == CmpInst::ICMP_EQ) + Type = ICMP_NAND; + } else { + Type = Pred; + } + + if (!V) + V = getFirstOtherOperand(Cmp, L); + addConstantIntToExpressionList(V, C, Type); + } + } + + // Find all dominating memory write or constraints. + // Return false if none were found. + bool searchPriorMemoryWriteOrConstraints(DominatorTree &DOM, + Instruction *Src, + Value *V, + SmallVectorImpl &Access) { + // Identify candidate that dominate the source instruction + for (const auto &UR : V->users()) { + if (!isa(UR)) + continue; + + Instruction *UI = cast(UR); + if (!isa(UI) && !isa(UI) && + !isa(UI) && UI->getOpcode() != Instruction::And) { + continue; + } + + if (DOM.dominates(Src, UI)) + continue; + Access.push_back(UI); + } + + // Use domination to remove memory access that do not impact + // the final argument + for (auto it = Access.begin(); it != Access.end(); /* empty */) { + if (!DOM.dominates(*it, Src)) { + ++it; + continue; + } + bool Erased = false; + for (auto zt = Access.begin(); zt != Access.end(); ++zt) { + if (*zt == *it) + continue; + // Do not clean previous instruction except if StoreInst + // of non ConstantInt (usually argument assignment) + if (!isa(*zt)) { + if (!isa(*it) || + isa(cast(*it)->getValueOperand())) + continue; + } + if (DOM.dominates(*zt, Src) && DOM.dominates(*it, *zt)) { + it = Access.erase(it); + Erased = true; + break; + } + } + if (!Erased) + ++it; + } + + return Access.size() != 0; + } + + // Return true if we could create a consistent expression list. + // if one path/state is unknown, return false and skip this argument. + bool findMemoryDependences(Instruction *Src, Value *V, import_args::ExpressionList &EList) { + SmallVector Access; + Function *F = Src->getFunction(); + auto &DOM = FAM->getResult(*F); + + if (!searchPriorMemoryWriteOrConstraints(DOM, Src, V, Access)) + return false; + + // For the remaining instructions, a StoreInst must be a ConstantInt + // assignment. A LoadInst can have constraints linked to it. Other + // instructions can be direct constraints (on O1+). + for (const auto &I : Access) { + if (const StoreInst *SI = dyn_cast(I)) { + if (!addConstantIntToExpressionList(SI->getValueOperand(), EList)) + return false; + } else if (isa(I)) { + for (const auto &U : I->users()) + identifyConstraint(DOM, cast(U), Src, EList, I); + } else { + identifyConstraint(DOM, I, Src, EList, V); + } + } + + return true; + } + + // Find value or simple constraints applied to arguments on this call site + void runOnCallSite(CallInst *CI) { + import_args::ImportCall ImpDesc; + + ImpDesc.Caller = CI->getFunction()->getName(); + ImpDesc.Import = CI->getCalledFunction()->getName(); + if (const DILocation *Loc = CI->getDebugLoc()) { + ImpDesc.LineNumber = Loc->getLine(); + ImpDesc.Filename = Loc->getFilename(); + } + + for (const auto &Arg : CI->arg_operands()) { + import_args::ExpressionList EList; + + if (isa(Arg)) { + addConstantIntToExpressionList(Arg, EList); + } else if (isa(Arg)) { + auto LI = cast(Arg); + if (!findMemoryDependences(LI, LI->getPointerOperand(), EList)) + continue; + } else if (isa(Arg)) { + SelectInst *SI = cast(Arg); + if (!isa(SI->getTrueValue()) || !isa(SI->getFalseValue())) + continue; + addConstantIntToExpressionList(SI->getTrueValue(), EList); + addConstantIntToExpressionList(SI->getFalseValue(), EList); + } else { + if (!findMemoryDependences(CI, Arg, EList)) + continue; + } + + if (EList.size() != 0) + ImpDesc.Arguments[Arg.getOperandNo()] = EList; + } + Result.push_back(ImpDesc); + } + + public: + ImportArgumentAnalysis() : FAM(nullptr) { } + + void run(Module &M, ModuleAnalysisManager &MAM) { + FAM = &MAM.getResult(M).getManager(); + CallGraph &Graph = MAM.getResult(M); + + // Go through functions sequentially so the output is predictable for testing + // or comparison. + for (const auto &F : M) { + const auto &Node = Graph[&F]; + for (const auto &E : *Node) { + if (isExternalFunction(Graph, E.second->getFunction())) + runOnCallSite(cast(E.first)); + } + } + } + + // Print a YAML representation of the result + void print(raw_ostream &OS) { + yaml::Output Out(OS); + for (auto &E : Result) { + Out << E; + } + } +}; + +PreservedAnalyses ImportArgumentsPrinterPass::run(Module &M, ModuleAnalysisManager &AM) { + ImportArgumentAnalysis Analysis; + Analysis.run(M, AM); + Analysis.print(OS); + return PreservedAnalyses::all(); +} + +} // end namespace llvm Index: lib/Passes/PassBuilder.cpp =================================================================== --- lib/Passes/PassBuilder.cpp +++ lib/Passes/PassBuilder.cpp @@ -34,6 +34,7 @@ #include "llvm/Analysis/DominanceFrontier.h" #include "llvm/Analysis/GlobalsModRef.h" #include "llvm/Analysis/IVUsers.h" +#include "llvm/Analysis/ImportArguments.h" #include "llvm/Analysis/LazyCallGraph.h" #include "llvm/Analysis/LazyValueInfo.h" #include "llvm/Analysis/LoopAccessAnalysis.h" Index: lib/Passes/PassRegistry.def =================================================================== --- lib/Passes/PassRegistry.def +++ lib/Passes/PassRegistry.def @@ -67,6 +67,7 @@ MODULE_PASS("print", PrintModulePass(dbgs())) MODULE_PASS("print-lcg", LazyCallGraphPrinterPass(dbgs())) MODULE_PASS("print-lcg-dot", LazyCallGraphDOTPrinterPass(dbgs())) +MODULE_PASS("print-import-args", ImportArgumentsPrinterPass(dbgs())) MODULE_PASS("rewrite-symbols", RewriteSymbolPass()) MODULE_PASS("rpo-functionattrs", ReversePostOrderFunctionAttrsPass()) MODULE_PASS("sample-profile", SampleProfileLoaderPass()) Index: test/Analysis/ImportArguments/O0.ll =================================================================== --- test/Analysis/ImportArguments/O0.ll +++ test/Analysis/ImportArguments/O0.ll @@ -0,0 +1,236 @@ +; RUN: opt < %s -passes='print-import-args' 2>&1 | FileCheck --check-prefixes=CHECK,NOOPT %S/checks.txt +@.str = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +@rand_value = common global i32 0, align 4 + +; Function Attrs: noinline nounwind uwtable +define void @only_constants(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + %0 = load i32, i32* %value.addr, align 4 + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %0, i32 1, i32 2, i32 3, i32 255) + ret void +} + +declare void @externalfunction(i8*, ...) #1 + +; Function Attrs: noinline nounwind uwtable +define void @constraint_eq(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + %0 = load i32, i32* %value.addr, align 4 + %cmp = icmp ne i32 %0, 4 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + br label %return + +if.end: ; preds = %entry + %1 = load i32, i32* %value.addr, align 4 + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %1) + br label %return + +return: ; preds = %if.end, %if.then + ret void +} + +; Function Attrs: noinline nounwind uwtable +define void @preset(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + store i32 3, i32* %value.addr, align 4 + %0 = load i32, i32* %value.addr, align 4 + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %0) + ret void +} + +; Function Attrs: noinline nounwind uwtable +define void @unknown_value(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + %0 = load i32, i32* @rand_value, align 4 + %cmp = icmp eq i32 %0, 0 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + store i32 3, i32* %value.addr, align 4 + br label %if.end + +if.end: ; preds = %if.then, %entry + %1 = load i32, i32* %value.addr, align 4 + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %1) + ret void +} + +; Function Attrs: noinline nounwind uwtable +define void @rand_def_values(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + store i32 1, i32* %value.addr, align 4 + %0 = load i32, i32* @rand_value, align 4 + %cmp = icmp eq i32 %0, 0 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + store i32 3, i32* %value.addr, align 4 + br label %if.end + +if.end: ; preds = %if.then, %entry + %1 = load i32, i32* %value.addr, align 4 + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %1) + ret void +} + +; Function Attrs: noinline nounwind uwtable +define void @set_arg_final(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + %v = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + %0 = load i32, i32* %value.addr, align 4 + store i32 %0, i32* %v, align 4 + %1 = load i32, i32* %value.addr, align 4 + %cmp = icmp eq i32 %1, 0 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + %2 = load i32, i32* %v, align 4 + %inc = add nsw i32 %2, 1 + store i32 %inc, i32* %v, align 4 + br label %if.end + +if.end: ; preds = %if.then, %entry + %3 = load i32, i32* %value.addr, align 4 + %4 = load i32, i32* %v, align 4 + %mul = mul nsw i32 %4, %3 + store i32 %mul, i32* %v, align 4 + %5 = load i32, i32* %v, align 4 + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %5) + ret void +} + +; Function Attrs: noinline nounwind uwtable +define void @set_after(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + %0 = load i32, i32* %value.addr, align 4 + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %0) + store i32 4, i32* %value.addr, align 4 + %1 = load i32, i32* @rand_value, align 4 + %tobool = icmp ne i32 %1, 0 + br i1 %tobool, label %if.then, label %if.end + +if.then: ; preds = %entry + store i32 5, i32* %value.addr, align 4 + br label %if.end + +if.end: ; preds = %if.then, %entry + ret void +} + +; Function Attrs: noinline nounwind uwtable +define void @check_and(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + %0 = load i32, i32* %value.addr, align 4 + %and = and i32 %0, 2 + %tobool = icmp ne i32 %and, 0 + br i1 %tobool, label %if.then, label %if.end + +if.then: ; preds = %entry + br label %return + +if.end: ; preds = %entry + %1 = load i32, i32* %value.addr, align 4 + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %1) + br label %return + +return: ; preds = %if.end, %if.then + ret void +} + +; Function Attrs: noinline nounwind uwtable +define void @set_and_check_before(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + store i32 3, i32* %value.addr, align 4 + %0 = load i32, i32* %value.addr, align 4 + %and = and i32 %0, 2 + %tobool = icmp ne i32 %and, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %entry + br label %return + +if.end: ; preds = %entry + %1 = load i32, i32* %value.addr, align 4 + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %1) + br label %return + +return: ; preds = %if.end, %if.then + ret void +} + +; Function Attrs: noinline nounwind uwtable +define void @check_set_after(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + %0 = load i32, i32* %value.addr, align 4 + %and = and i32 %0, 2 + %tobool = icmp ne i32 %and, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %entry + br label %return + +if.end: ; preds = %entry + store i32 3, i32* %value.addr, align 4 + %1 = load i32, i32* %value.addr, align 4 + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %1) + br label %return + +return: ; preds = %if.end, %if.then + ret void +} + +; Function Attrs: noinline nounwind uwtable +define void @question_const(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + %0 = load i32, i32* %value.addr, align 4 + %tobool = icmp ne i32 %0, 0 + %cond = select i1 %tobool, i32 1, i32 2 + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %cond) + ret void +} + +; Function Attrs: noinline nounwind uwtable +define void @question_unknown(i32 %value) #0 { +entry: + %value.addr = alloca i32, align 4 + store i32 %value, i32* %value.addr, align 4 + %0 = load i32, i32* %value.addr, align 4 + %tobool = icmp ne i32 %0, 0 + br i1 %tobool, label %cond.true, label %cond.false + +cond.true: ; preds = %entry + %1 = load i32, i32* %value.addr, align 4 + br label %cond.end + +cond.false: ; preds = %entry + br label %cond.end + +cond.end: ; preds = %cond.false, %cond.true + %cond = phi i32 [ %1, %cond.true ], [ 3, %cond.false ] + call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i32 0, i32 0), i32 %cond) + ret void +} Index: test/Analysis/ImportArguments/O1.ll =================================================================== --- test/Analysis/ImportArguments/O1.ll +++ test/Analysis/ImportArguments/O1.ll @@ -0,0 +1,127 @@ +; RUN: opt < %s -passes='print-import-args' 2>&1 | FileCheck %S/checks.txt + +@.str = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +@rand_value = common local_unnamed_addr global i32 0, align 4 + +; Function Attrs: nounwind uwtable +define void @only_constants(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %value, i32 1, i32 2, i32 3, i32 255) #2 + ret void +} + +declare void @externalfunction(i8*, ...) local_unnamed_addr #1 + +; Function Attrs: nounwind uwtable +define void @constraint_eq(i32 %value) #0 { +entry: + %cmp = icmp eq i32 %value, 4 + br i1 %cmp, label %if.end, label %return + +if.end: ; preds = %entry + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %value) #2 + br label %return + +return: ; preds = %entry, %if.end + ret void +} + +; Function Attrs: nounwind uwtable +define void @preset(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 3) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @unknown_value(i32 %value) #0 { +entry: + %0 = load i32, i32* @rand_value, align 4 + %cmp = icmp eq i32 %0, 0 + %.value = select i1 %cmp, i32 3, i32 %value + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %.value) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @rand_def_values(i32 %value) #0 { +entry: + %0 = load i32, i32* @rand_value, align 4 + %cmp = icmp eq i32 %0, 0 + %. = select i1 %cmp, i32 3, i32 1 + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %.) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @set_arg_final(i32 %value) #0 { +entry: + %cmp = icmp eq i32 %value, 0 + %inc = zext i1 %cmp to i32 + %inc.value = add nsw i32 %inc, %value + %mul = mul nsw i32 %inc.value, %value + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %mul) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @set_after(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %value) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @check_and(i32 %value) #0 { +entry: + %and = and i32 %value, 2 + %tobool = icmp eq i32 %and, 0 + br i1 %tobool, label %if.end, label %return + +if.end: ; preds = %entry + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %value) #2 + br label %return + +return: ; preds = %entry, %if.end + ret void +} + +; Function Attrs: nounwind uwtable +define void @set_and_check_before(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 3) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @check_set_after(i32 %value) #0 { +entry: + %and = and i32 %value, 2 + %tobool = icmp eq i32 %and, 0 + br i1 %tobool, label %return, label %if.end + +if.end: ; preds = %entry + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 3) #2 + br label %return + +return: ; preds = %entry, %if.end + ret void +} + +; Function Attrs: nounwind uwtable +define void @question_const(i32 %value) #0 { +entry: + %tobool = icmp ne i32 %value, 0 + %cond = select i1 %tobool, i32 1, i32 2 + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %cond) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @question_unknown(i32 %value) #0 { +entry: + %tobool = icmp ne i32 %value, 0 + %cond = select i1 %tobool, i32 %value, i32 3 + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %cond) #2 + ret void +} Index: test/Analysis/ImportArguments/O2.ll =================================================================== --- test/Analysis/ImportArguments/O2.ll +++ test/Analysis/ImportArguments/O2.ll @@ -0,0 +1,127 @@ +; RUN: opt < %s -passes='print-import-args' 2>&1 | FileCheck %S/checks.txt + +@.str = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +@rand_value = common local_unnamed_addr global i32 0, align 4 + +; Function Attrs: nounwind uwtable +define void @only_constants(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %value, i32 1, i32 2, i32 3, i32 255) #2 + ret void +} + +declare void @externalfunction(i8*, ...) local_unnamed_addr #1 + +; Function Attrs: nounwind uwtable +define void @constraint_eq(i32 %value) #0 { +entry: + %cmp = icmp eq i32 %value, 4 + br i1 %cmp, label %if.end, label %return + +if.end: ; preds = %entry + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 4) #2 + br label %return + +return: ; preds = %entry, %if.end + ret void +} + +; Function Attrs: nounwind uwtable +define void @preset(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 3) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @unknown_value(i32 %value) #0 { +entry: + %0 = load i32, i32* @rand_value, align 4 + %cmp = icmp eq i32 %0, 0 + %.value = select i1 %cmp, i32 3, i32 %value + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %.value) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @rand_def_values(i32 %value) #0 { +entry: + %0 = load i32, i32* @rand_value, align 4 + %cmp = icmp eq i32 %0, 0 + %. = select i1 %cmp, i32 3, i32 1 + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %.) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @set_arg_final(i32 %value) #0 { +entry: + %cmp = icmp eq i32 %value, 0 + %inc = zext i1 %cmp to i32 + %inc.value = add nsw i32 %inc, %value + %mul = mul nsw i32 %inc.value, %value + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %mul) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @set_after(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %value) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @check_and(i32 %value) #0 { +entry: + %and = and i32 %value, 2 + %tobool = icmp eq i32 %and, 0 + br i1 %tobool, label %if.end, label %return + +if.end: ; preds = %entry + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %value) #2 + br label %return + +return: ; preds = %entry, %if.end + ret void +} + +; Function Attrs: nounwind uwtable +define void @set_and_check_before(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 3) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @check_set_after(i32 %value) #0 { +entry: + %and = and i32 %value, 2 + %tobool = icmp eq i32 %and, 0 + br i1 %tobool, label %return, label %if.end + +if.end: ; preds = %entry + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 3) #2 + br label %return + +return: ; preds = %entry, %if.end + ret void +} + +; Function Attrs: nounwind uwtable +define void @question_const(i32 %value) #0 { +entry: + %tobool = icmp ne i32 %value, 0 + %cond = select i1 %tobool, i32 1, i32 2 + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %cond) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @question_unknown(i32 %value) #0 { +entry: + %tobool = icmp ne i32 %value, 0 + %cond = select i1 %tobool, i32 %value, i32 3 + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %cond) #2 + ret void +} Index: test/Analysis/ImportArguments/O3.ll =================================================================== --- test/Analysis/ImportArguments/O3.ll +++ test/Analysis/ImportArguments/O3.ll @@ -0,0 +1,127 @@ +; RUN: opt < %s -passes='print-import-args' 2>&1 | FileCheck %S/checks.txt + +@.str = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +@rand_value = common local_unnamed_addr global i32 0, align 4 + +; Function Attrs: nounwind uwtable +define void @only_constants(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %value, i32 1, i32 2, i32 3, i32 255) #2 + ret void +} + +declare void @externalfunction(i8*, ...) local_unnamed_addr #1 + +; Function Attrs: nounwind uwtable +define void @constraint_eq(i32 %value) #0 { +entry: + %cmp = icmp eq i32 %value, 4 + br i1 %cmp, label %if.end, label %return + +if.end: ; preds = %entry + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 4) #2 + br label %return + +return: ; preds = %entry, %if.end + ret void +} + +; Function Attrs: nounwind uwtable +define void @preset(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 3) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @unknown_value(i32 %value) #0 { +entry: + %0 = load i32, i32* @rand_value, align 4 + %cmp = icmp eq i32 %0, 0 + %.value = select i1 %cmp, i32 3, i32 %value + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %.value) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @rand_def_values(i32 %value) #0 { +entry: + %0 = load i32, i32* @rand_value, align 4 + %cmp = icmp eq i32 %0, 0 + %. = select i1 %cmp, i32 3, i32 1 + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %.) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @set_arg_final(i32 %value) #0 { +entry: + %cmp = icmp eq i32 %value, 0 + %inc = zext i1 %cmp to i32 + %inc.value = add nsw i32 %inc, %value + %mul = mul nsw i32 %inc.value, %value + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %mul) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @set_after(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %value) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @check_and(i32 %value) #0 { +entry: + %and = and i32 %value, 2 + %tobool = icmp eq i32 %and, 0 + br i1 %tobool, label %if.end, label %return + +if.end: ; preds = %entry + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %value) #2 + br label %return + +return: ; preds = %entry, %if.end + ret void +} + +; Function Attrs: nounwind uwtable +define void @set_and_check_before(i32 %value) #0 { +entry: + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 3) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @check_set_after(i32 %value) #0 { +entry: + %and = and i32 %value, 2 + %tobool = icmp eq i32 %and, 0 + br i1 %tobool, label %return, label %if.end + +if.end: ; preds = %entry + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 3) #2 + br label %return + +return: ; preds = %entry, %if.end + ret void +} + +; Function Attrs: nounwind uwtable +define void @question_const(i32 %value) #0 { +entry: + %tobool = icmp ne i32 %value, 0 + %cond = select i1 %tobool, i32 1, i32 2 + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %cond) #2 + ret void +} + +; Function Attrs: nounwind uwtable +define void @question_unknown(i32 %value) #0 { +entry: + %tobool = icmp ne i32 %value, 0 + %cond = select i1 %tobool, i32 %value, i32 3 + tail call void (i8*, ...) @externalfunction(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str, i64 0, i64 0), i32 %cond) #2 + ret void +} Index: test/Analysis/ImportArguments/checks.txt =================================================================== --- test/Analysis/ImportArguments/checks.txt +++ test/Analysis/ImportArguments/checks.txt @@ -0,0 +1,98 @@ +; CHECK: caller: only_constants +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: 2: +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 1 +; CHECK-NEXT: 3: +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 2 +; CHECK-NEXT: 4: +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 3 +; CHECK-NEXT: 5: +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 255 +; CHECK-NEXT: ... + +; CHECK: caller: constraint_eq +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: 1: +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 4 +; CHECK-NEXT: ... + +; CHECK: caller: preset +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: 1: +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 3 +; CHECK-NEXT: ... + +; CHECK: caller: unknown_value +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: ... + +; CHECK: caller: rand_def_values +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: 1: +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 3 +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 1 +; CHECK-NEXT: ... + +; CHECK: caller: set_arg_final +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: ... + +; CHECK: caller: set_after +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: ... + +; CHECK: caller: check_and +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: 1: +; CHECK-NEXT: - type: nand +; CHECK-NEXT: value: 2 +; CHECK-NEXT: ... + +; CHECK: caller: set_and_check_before +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: 1: +; NOOPT-NEXT: - type: and +; NOOPT-NEXT: value: 2 +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 3 +; CHECK-NEXT: ... + +; CHECK: caller: check_set_after +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: 1: +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 3 +; CHECK-NEXT: ... + +; CHECK: caller: question_const +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: 1: +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 1 +; CHECK-NEXT: - type: eq +; CHECK-NEXT: value: 2 +; CHECK-NEXT: ... + +; CHECK: caller: question_unknown +; CHECK-NEXT: import: externalfunction +; CHECK-NEXT: arguments: +; CHECK-NEXT: ... Index: test/Analysis/ImportArguments/lit.local.cfg =================================================================== --- test/Analysis/ImportArguments/lit.local.cfg +++ test/Analysis/ImportArguments/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.ll'] Index: test/Analysis/ImportArguments/source.c =================================================================== --- test/Analysis/ImportArguments/source.c +++ test/Analysis/ImportArguments/source.c @@ -0,0 +1,97 @@ +// Source used to generate the different ll optimization with minor clean-up +int rand(void); +int rand_value; +void externalfunction(const char *fmt, ...); + +void only_constants(int value) { + externalfunction("", value, 1, 2, 3, 0xFF); +} + +void constraint_eq(int value) { + if (value != 4) + return; + externalfunction("", value); +} + +void preset(int value) { + value = 3; + externalfunction("", value); +} + +void unknown_value(int value) { + if (rand_value == 0) + value = 3; + externalfunction("", value); +} + +void rand_def_values(int value) { + value = 1; + if (rand_value == 0) + value = 3; + externalfunction("", value); +} + +void set_arg_final(int value) { + int v = value; + if (value == 0) + v++; + v *= value; + externalfunction("", v); +} + +void set_after(int value) { + externalfunction("", value); + value = 4; + if (rand_value) + value = 5; +} + +void check_and(int value) { + if (value & 2) + return; + externalfunction("", value); +} + +void set_and_check_before(int value) { + value = 3; + if (!(value & 2)) + return; + externalfunction("", value); +} + +void check_set_after(int value) { + if (!(value & 2)) + return; + value = 3; + externalfunction("", value); +} + +void question_const(int value) { + externalfunction("", (value ? 1 : 2)); +} + +void question_unknown(int value) { + externalfunction("", (value ? value : 3)); +} + +typedef void (*func_ptr)(int); + +int main(int argc, char **argv) { + rand_value = rand(); + func_ptr functions[] = { + only_constants, + constraint_eq, + preset, + unknown_value, + rand_def_values, + set_arg_final, + set_after, + check_and, + set_and_check_before, + check_set_after, + question_const, + question_unknown, + }; + functions[argc % sizeof(functions)](argc); + return 0; +}