Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -2071,7 +2071,7 @@ given the default specifications above, the i7 type will use the alignment of i8 (next largest) while both i65 and i256 will use the alignment of i64 (largest specified). -#. If no match is found, and the type sought is a vector type, then the +#.If no match is found, and the type sought is a vector type, then the largest vector type that is smaller than the sought vector type will be used as a fall back. This happens because <128 x double> can be implemented in terms of 64 <2 x double>, for example. @@ -5066,6 +5066,49 @@ ... !0 = !{i64 (i64, i64)* @add, i64 (i64, i64)* @sub} +'``callback``' Metadata +^^^^^^^^^^^^^^^^^^^^^^ + +``callback`` metadata may be attached to function definitions and declarations. +The metadata specifies a callback callee, the callback arguments. If +``callback`` metadata is attached, the function is a broker which will invoke +the callback callee zero or more times with the arguments specified. The intent +of this metadata is to facilitate interprocedural optimizations in the presence +of callbacks, e.g., ``pthread_create``. For example, in the code below, the +``pthread_create`` function is marked as a broker through the ``callback`` +metadata. The first metadata entry describes how variadic arguments are +handled, the second where the callback callee can be found, the remaining ones +specify which values are not-inspected but only passed as arguments to the +callback callee. All arguments passed to the callback callee have to be +described by the metadata. They can be unknown (``i64 0``), forwarded and +non-inspected arguments of the broker function (``i64 X``, with ``X`` between +one and the number of non-variadic broker function arguments), or forwarded and +non-inspected variadic arguments. The order in which they are listed is the +same in which they are passed to the callback callee, with the variadic +arguments always trailing at the end. In the example, the callback callee only +takes a single argument which is the third one to the broker function. + +.. code-block:: llvm + + declare !callback !2 dso_local i32 @pthread_create(i64*, %union.pthread_attr_t*, i8* (i8*)*, i8*) + + ... + !2 = !{i1 false, i64 2, i64 3} + +Another example is shown below. The callback callee is the second argument of +the ``__kmpc_fork_call`` function (identified by the ``i64 2``). The callee is +given two unknown values (each identified by the ``i64 0``) and afterwards all +variadic arguments that are passed to the ``__kmpc_fork_call`` call (due to the +initial ``i1 true``). + +.. code-block:: llvm + + declare !callback !0 dso_local void @__kmpc_fork_call(%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) + + ... + !0 = !{i1 true, i64 2, i64 0, i64 0} + + '``unpredictable``' Metadata ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -5804,7 +5847,9 @@ framework:: !0 = !{ !"-lz" }, - !1 = !{ !"-framework", !"Cocoa" } } } + !1 = !{ !"-framework", !"Cocoa" } +} +} !llvm.linker.options = !{ !0, !1 } The metadata encoding as lists of lists of options, as opposed to a collapsed Index: include/llvm/IR/CallSite.h =================================================================== --- include/llvm/IR/CallSite.h +++ include/llvm/IR/CallSite.h @@ -683,6 +683,171 @@ User::op_iterator getCallee() const; }; +/// AbstractCallSite +/// +/// An abstract call site is a wrapper that allows to treat direct, +/// indirect, and callback calls the same. If an abstract call site +/// represents a direct or indirect call site it behaves like a stripped +/// down version of a normal call site object. The abstract call site can +/// also represent a callback call, thus the fact that the initially +/// called function (=broker) may invoke a third one (=callback callee). +/// In this case, the abstract call site hides the middle man, hence the +/// broker function. The result is a representation of the callback call, +/// inside the broker, but in the context of the original call to the broker. +/// +/// There are up to three functions involved when we talk about callback call +/// sites. The caller (1), which invokes the broker function. The broker +/// function (2), that will invoke the callee zero or more times. And finally +/// the callee (3), which is the target of the callback call. +/// +/// The abstract call site will handle the mapping from parameters to arguments +/// depending on the semantic of the broker function. However, it is important +/// to note that the mapping is often partial. Thus, some arguments of the +/// call/invoke instruction are mapped to parameters of the callee while others +/// are not. +class AbstractCallSite { +public: + + /// The encoding of a callback with regards to the underlying instruction. + struct CallbackInfo { + + /// For direct/indirect calls the parameter encoding is empty. If it is not, + /// the abstract call site represents a callback. In that case, the first + /// element of the encoding vector represents which argument of the call + /// site CS is the callback callee. The remaining elements map parameters + /// (identified by their position) to the arguments that will be passed + /// through (also identified by position but in the call site instruction). + /// + /// NOTE that we use clang/source argument numbers (starting at 1) and not + /// LLVM argument numbers (starting at 0). The 0 entries represent unknown + /// values that are passed to the callee. + using ParameterEncodingTy = SmallVector; + ParameterEncodingTy ParameterEncoding; + + }; + +private: + + /// The underlying call site: + /// caller -> callee, if this is a direct or indirect call site + /// caller -> broker function, if this is a callback call site + CallSite CS; + + /// The encoding of a callback with regards to the underlying instruction. + CallbackInfo CI; + +public: + /// Sole constructor for abstract call sites (ACS). + AbstractCallSite(const Use *U); + + /// Conversion operator to conveniently check for a valid/initialized ACS. + explicit operator bool() const { return (bool)CS; } + + /// Return the underlying instruction. + Instruction *getInstruction() const { return CS.getInstruction(); } + + /// Return the call site abstraction for the underlying instruction. + CallSite getCallSite() const { return CS; } + + /// Return true if this ACS represents a direct call. + bool isDirectCall() const { + return !isCallbackCall() && !CS.isIndirectCall(); + } + + /// Return true if this ACS represents an indirect call. + bool isIndirectCall() const { + return !isCallbackCall() && CS.isIndirectCall(); + } + + /// Return true if this ACS represents a callback call. + bool isCallbackCall() const { + // For a callback call site the callee is ALWAYS stored first in the + // transitive values vector. Thus, a non-empty vector indicates a callback. + return !CI.ParameterEncoding.empty(); + } + + /// Return true if @p UI is the use that defines the callee of this ACS. + bool isCallee(Value::const_user_iterator UI) const { + return isCallee(&UI.getUse()); + } + + /// Return true if @p U is the use that defines the callee of this ACS. + bool isCallee(const Use *U) const { + if (isDirectCall()) + return CS.isCallee(U); + + assert(!CI.ParameterEncoding.empty() && + "Callback without parameter encoding!"); + + // Substract one for the conversion from clang/source argument numbers to + // LLVM argument numbers. + return CS.getArgumentNo(U) == CI.ParameterEncoding[0] - 1; + } + + /// Return the number of parameters of the callee. + unsigned getNumArgOperands() const { + if (isDirectCall()) + return CS.getNumArgOperands(); + // Subtract 1 for the callee encoding. + return CI.ParameterEncoding.size() - 1; + } + + /// Return the operand index of the underlying instruction associated with @p + /// Arg. + int getCallArgOperandNo(Argument &Arg) const { + return getCallArgOperandNo(Arg.getArgNo()); + } + + /// Return the operand index of the underlying instruction associated with + /// the function parameter number @p ArgNo. + int getCallArgOperandNo(unsigned ArgNo) const { + if (isDirectCall()) + return ArgNo; + // Add 1 for the callee encoding. Substract one for the conversion from + // clang/source argument numbers to LLVM argument numbers. + return CI.ParameterEncoding[ArgNo + 1] - 1; + } + + /// Return the operand of the underlying instruction associated with @p Arg. + Value *getCallArgOperand(Argument &Arg) const { + return getCallArgOperand(Arg.getArgNo()); + } + + /// Return the operand of the underlying instruction associated with the + /// function parameter number @p ArgNo. + Value *getCallArgOperand(unsigned ArgNo) const { + if (isDirectCall()) + return CS.getArgOperand(ArgNo); + // Add 1 for the callee encoding. Substract one for the conversion from + // clang/source argument numbers to LLVM argument numbers. + return CI.ParameterEncoding[ArgNo + 1] > 0 + ? CS.getArgOperand(CI.ParameterEncoding[ArgNo + 1] - 1) + : nullptr; + } + + /// Return the operand index of the underlying instruction associated with the + /// callee of this ACS. Only valid for callback calls! + int getCallArgOperandNoForCallee() const { + assert(isCallbackCall()); + assert(CI.ParameterEncoding.size() && CI.ParameterEncoding[0] > 0); + return CI.ParameterEncoding[0] - 1; + } + + /// Return the pointer to function that is being called. + Value *getCalledValue() const { + if (isDirectCall()) + return CS.getCalledValue(); + return CS.getArgOperand(getCallArgOperandNoForCallee()); + } + + /// Return the function being called if this is a direct call, otherwise + /// return null (if it's an indirect call). + Function *getCalledFunction() const { + Value *V = getCalledValue(); + return V ? dyn_cast(V->stripPointerCasts()) : nullptr; + } +}; + template <> struct DenseMapInfo { using BaseInfo = DenseMapInfo; Index: include/llvm/IR/LLVMContext.h =================================================================== --- include/llvm/IR/LLVMContext.h +++ include/llvm/IR/LLVMContext.h @@ -102,6 +102,7 @@ MD_associated = 22, // "associated" MD_callees = 23, // "callees" MD_irr_loop = 24, // "irr_loop" + MD_callback = 25, // "callback" }; /// Known operand bundle tag IDs, which always have the same value. All Index: include/llvm/IR/MDBuilder.h =================================================================== --- include/llvm/IR/MDBuilder.h +++ include/llvm/IR/MDBuilder.h @@ -94,6 +94,14 @@ /// calls. MDNode *createCallees(ArrayRef Callees); + //===------------------------------------------------------------------===// + // Callback metadata. + //===------------------------------------------------------------------===// + + /// Return metadata indicating a possible callback (see AbstractCallSites). + MDNode *createCallback(unsigned CalleeArgNo, ArrayRef Arguments, + bool VarArg); + //===------------------------------------------------------------------===// // AA metadata. //===------------------------------------------------------------------===// Index: lib/IR/AbstractCallSite.cpp =================================================================== --- /dev/null +++ lib/IR/AbstractCallSite.cpp @@ -0,0 +1,118 @@ +//===-- AbstractCallSite.cpp - Implementation of abstract call sites ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements abstract call sites which unify the interface for +// direct, indirect, and callback call sites. +// +// For more information see: +// https://llvm.org/devmtg/2018-10/talk-abstracts.html#talk20 +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/IR/CallSite.h" +#include "llvm/Support/Debug.h" + +using namespace llvm; + +#define DEBUG_TYPE "abstract-call-sites" + +STATISTIC(NumCallbackCallSites, "Number of callback call sites created"); +STATISTIC(NumDirectAbstractCallSites, + "Number of direct abstract call sites created"); +STATISTIC(NumInvalidAbstractCallSitesUnknownUse, + "Number of invalid abstract call sites created (unknown use)"); +STATISTIC(NumInvalidAbstractCallSitesUnknownCallee, + "Number of invalid abstract call sites created (unknown callee)"); +STATISTIC(NumInvalidAbstractCallSitesNoCallback, + "Number of invalid abstract call sites created (no callback)"); + +/// Create an abstract call site from a use. +AbstractCallSite::AbstractCallSite(const Use *U) : CS(U->getUser()) { + + // First handle unknown users. + if (!CS) { + + // If the use is actually in a constant cast expression which itself + // has only one use, we look through the constant cast expression. + // This happens by updating the use @p U to the use of the constant + // cast expression and afterwards re-initializing CS accordingly. + if (ConstantExpr *CE = dyn_cast(U->getUser())) + if (CE->getNumUses() == 1 && CE->isCast()) { + U = &*CE->use_begin(); + CS = CallSite(U->getUser()); + } + + if (!CS) { + NumInvalidAbstractCallSitesUnknownUse++; + return; + } + } + + // Then handle direct or indirect calls. Thus, if U is the callee of the + // call site CS it is not a callback and we are done. + if (CS.isCallee(U)) { + NumDirectAbstractCallSites++; + return; + } + + // If we cannot identify the broker function we cannot create a callback and + // invalidate the abstract call site. + Function *Callee = CS.getCalledFunction(); + if (!Callee) { + NumInvalidAbstractCallSitesUnknownCallee++; + CS = static_cast(nullptr); + return; + } + + MDNode *CallbackMD = Callee->getMetadata(LLVMContext::MD_callback); + if (!CallbackMD) { + NumInvalidAbstractCallSitesNoCallback++; + CS = static_cast(nullptr); + return; + } + + NumCallbackCallSites++; + + assert(CallbackMD->getNumOperands() >= 2 && + "Incomplete !callback metadata"); + + unsigned NumCallOperands = CS.getNumArgOperands(); + + // Skip the var-arg flag when reading the metadata. + for (unsigned u = 1, e = CallbackMD->getNumOperands(); u < e; u++) { + Metadata *OpAsM = CallbackMD->getOperand(u).get(); + auto *OpAsCM = cast(OpAsM); + assert(OpAsCM->getType()->isIntegerTy(64) && + "Malformed !callback metadata"); + + uint64_t Idx = cast(OpAsCM->getValue())->getZExtValue(); + assert(Idx <= NumCallOperands && "Out-of-bounds !callback metadata index"); + + CI.ParameterEncoding.push_back(Idx); + } + + if (!Callee->isVarArg()) + return; + + Metadata *VarArgFlagAsM = CallbackMD->getOperand(0).get(); + auto *VarArgFlagAsCM = cast(VarArgFlagAsM); + assert(VarArgFlagAsCM->getType()->isIntegerTy(1) && + "Malformed !callback metadata var-arg flag"); + + if (VarArgFlagAsCM->getValue()->isNullValue()) + return; + + // Add all variadic arguments at the end. Note the addition of 1 is necessary + // to adjust for the LLVM cs. clang/source counting of arguments. + for (unsigned u = Callee->arg_size(); u < NumCallOperands; u++) + CI.ParameterEncoding.push_back(u + 1); +} + Index: lib/IR/CMakeLists.txt =================================================================== --- lib/IR/CMakeLists.txt +++ lib/IR/CMakeLists.txt @@ -3,6 +3,7 @@ add_public_tablegen_target(AttributeCompatFuncTableGen) add_llvm_library(LLVMCore + AbstractCallSite.cpp AsmWriter.cpp Attributes.cpp AutoUpgrade.cpp Index: lib/IR/LLVMContext.cpp =================================================================== --- lib/IR/LLVMContext.cpp +++ lib/IR/LLVMContext.cpp @@ -61,6 +61,7 @@ {MD_associated, "associated"}, {MD_callees, "callees"}, {MD_irr_loop, "irr_loop"}, + {MD_callback, "callback"}, }; for (auto &MDKind : MDKinds) { Index: lib/IR/MDBuilder.cpp =================================================================== --- lib/IR/MDBuilder.cpp +++ lib/IR/MDBuilder.cpp @@ -107,6 +107,19 @@ return MDNode::get(Context, Ops); } +MDNode *MDBuilder::createCallback(unsigned CalleeArgNo, + ArrayRef Arguments, bool VarArg) { + SmallVector Ops; + Type *Int1 = Type::getInt1Ty(Context); + Ops.push_back(createConstant(ConstantInt::get(Int1, VarArg))); + Type *Int64 = Type::getInt64Ty(Context); + Ops.push_back(createConstant(ConstantInt::get(Int64, CalleeArgNo))); + for (unsigned ArgNo : Arguments) + Ops.push_back(createConstant(ConstantInt::get(Int64, ArgNo))); + + return MDNode::get(Context, Ops); +} + MDNode *MDBuilder::createAnonymousAARoot(StringRef Name, MDNode *Extra) { // To ensure uniqueness the root node is self-referential. auto Dummy = MDNode::getTemporary(Context, None); Index: lib/Transforms/IPO/IPConstantPropagation.cpp =================================================================== --- lib/Transforms/IPO/IPConstantPropagation.cpp +++ lib/Transforms/IPO/IPConstantPropagation.cpp @@ -62,32 +62,27 @@ // Ignore blockaddress uses. if (isa(UR)) continue; - // Used by a non-instruction, or not the callee of a function, do not - // transform. - if (!isa(UR) && !isa(UR)) - return false; - - CallSite CS(cast(UR)); - if (!CS.isCallee(&U)) + // If no abstract call site was created the use is invalid. + AbstractCallSite ACS(&U); + if (!ACS) return false; // Check out all of the potentially constant arguments. Note that we don't // inspect varargs here. - CallSite::arg_iterator AI = CS.arg_begin(); Function::arg_iterator Arg = F.arg_begin(); - for (unsigned i = 0, e = ArgumentConstants.size(); i != e; - ++i, ++AI, ++Arg) { + for (unsigned i = 0, e = ArgumentConstants.size(); i != e; ++i, ++Arg) { // If this argument is known non-constant, ignore it. if (ArgumentConstants[i].second) continue; - Constant *C = dyn_cast(*AI); + Value *V = ACS.getCallArgOperand(i); + Constant *C = dyn_cast_or_null(V); if (C && ArgumentConstants[i].first == nullptr) { ArgumentConstants[i].first = C; // First constant seen. } else if (C && ArgumentConstants[i].first == C) { // Still the constant value we think it is. - } else if (*AI == &*Arg) { + } else if (V == &*Arg) { // Ignore recursive calls passing argument down. } else { // Argument became non-constant. If all arguments are non-constant now, Index: test/ThinLTO/X86/lazyload_metadata.ll =================================================================== --- test/ThinLTO/X86/lazyload_metadata.ll +++ test/ThinLTO/X86/lazyload_metadata.ll @@ -10,13 +10,13 @@ ; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \ ; RUN: -o /dev/null -stats \ ; RUN: 2>&1 | FileCheck %s -check-prefix=LAZY -; LAZY: 55 bitcode-reader - Number of Metadata records loaded +; LAZY: 57 bitcode-reader - Number of Metadata records loaded ; LAZY: 2 bitcode-reader - Number of MDStrings loaded ; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \ ; RUN: -o /dev/null -disable-ondemand-mds-loading -stats \ ; RUN: 2>&1 | FileCheck %s -check-prefix=NOTLAZY -; NOTLAZY: 64 bitcode-reader - Number of Metadata records loaded +; NOTLAZY: 66 bitcode-reader - Number of Metadata records loaded ; NOTLAZY: 7 bitcode-reader - Number of MDStrings loaded Index: test/Transforms/IPConstantProp/openmp_parallel_for.ll =================================================================== --- /dev/null +++ test/Transforms/IPConstantProp/openmp_parallel_for.ll @@ -0,0 +1,119 @@ +; RUN: opt -S -ipconstprop < %s | FileCheck %s +; +; void bar(int, float, double); +; +; void foo(int N) { +; float p = 3; +; double q = 5; +; N = 7; +; +; #pragma omp parallel for firstprivate(q) +; for (int i = 2; i < N; i++) { +; bar(i, p, q); +; } +; } +; +; Verify the constant value of q is propagated into the outlined function. +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +%struct.ident_t = type { i32, i32, i32, i32, i8* } + +@.str = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1 +@0 = private unnamed_addr global %struct.ident_t { i32 0, i32 514, i32 0, i32 0, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 0) }, align 8 +@1 = private unnamed_addr global %struct.ident_t { i32 0, i32 2, i32 0, i32 0, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 0) }, align 8 + +define dso_local void @foo(i32 %N) { +entry: + %N.addr = alloca i32, align 4 + %p = alloca float, align 4 + store i32 %N, i32* %N.addr, align 4 + store float 3.000000e+00, float* %p, align 4 + store i32 7, i32* %N.addr, align 4 + call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* nonnull @1, i32 3, void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*, float*, i64)* @.omp_outlined. to void (i32*, i32*, ...)*), i32* nonnull %N.addr, float* nonnull %p, i64 4617315517961601024) + ret void +} + +define internal void @.omp_outlined.(i32* noalias %.global_tid., i32* noalias %.bound_tid., i32* dereferenceable(4) %N, float* dereferenceable(4) %p, i64 %q) { +entry: + %q.addr = alloca i64, align 8 + %.omp.lb = alloca i32, align 4 + %.omp.ub = alloca i32, align 4 + %.omp.stride = alloca i32, align 4 + %.omp.is_last = alloca i32, align 4 +; CHECK: store i64 4617315517961601024, i64* %q.addr, align 8 + store i64 %q, i64* %q.addr, align 8 + %conv = bitcast i64* %q.addr to double* + %tmp = load i32, i32* %N, align 4 + %sub3 = add nsw i32 %tmp, -3 + %cmp = icmp sgt i32 %tmp, 2 + br i1 %cmp, label %omp.precond.then, label %omp.precond.end + +omp.precond.then: ; preds = %entry + store i32 0, i32* %.omp.lb, align 4 + store i32 %sub3, i32* %.omp.ub, align 4 + store i32 1, i32* %.omp.stride, align 4 + store i32 0, i32* %.omp.is_last, align 4 + %tmp5 = load i32, i32* %.global_tid., align 4 + call void @__kmpc_for_static_init_4(%struct.ident_t* nonnull @0, i32 %tmp5, i32 34, i32* nonnull %.omp.is_last, i32* nonnull %.omp.lb, i32* nonnull %.omp.ub, i32* nonnull %.omp.stride, i32 1, i32 1) + %tmp6 = load i32, i32* %.omp.ub, align 4 + %cmp6 = icmp sgt i32 %tmp6, %sub3 + br i1 %cmp6, label %cond.true, label %cond.false + +cond.true: ; preds = %omp.precond.then + br label %cond.end + +cond.false: ; preds = %omp.precond.then + %tmp7 = load i32, i32* %.omp.ub, align 4 + br label %cond.end + +cond.end: ; preds = %cond.false, %cond.true + %cond = phi i32 [ %sub3, %cond.true ], [ %tmp7, %cond.false ] + store i32 %cond, i32* %.omp.ub, align 4 + %tmp8 = load i32, i32* %.omp.lb, align 4 + br label %omp.inner.for.cond + +omp.inner.for.cond: ; preds = %omp.inner.for.inc, %cond.end + %.omp.iv.0 = phi i32 [ %tmp8, %cond.end ], [ %add11, %omp.inner.for.inc ] + %tmp9 = load i32, i32* %.omp.ub, align 4 + %cmp8 = icmp sgt i32 %.omp.iv.0, %tmp9 + br i1 %cmp8, label %omp.inner.for.cond.cleanup, label %omp.inner.for.body + +omp.inner.for.cond.cleanup: ; preds = %omp.inner.for.cond + br label %omp.inner.for.end + +omp.inner.for.body: ; preds = %omp.inner.for.cond + %add10 = add nsw i32 %.omp.iv.0, 2 + %tmp10 = load float, float* %p, align 4 + %tmp11 = load double, double* %conv, align 8 + call void @bar(i32 %add10, float %tmp10, double %tmp11) + br label %omp.body.continue + +omp.body.continue: ; preds = %omp.inner.for.body + br label %omp.inner.for.inc + +omp.inner.for.inc: ; preds = %omp.body.continue + %add11 = add nsw i32 %.omp.iv.0, 1 + br label %omp.inner.for.cond + +omp.inner.for.end: ; preds = %omp.inner.for.cond.cleanup + br label %omp.loop.exit + +omp.loop.exit: ; preds = %omp.inner.for.end + %tmp12 = load i32, i32* %.global_tid., align 4 + call void @__kmpc_for_static_fini(%struct.ident_t* nonnull @0, i32 %tmp12) + br label %omp.precond.end + +omp.precond.end: ; preds = %omp.loop.exit, %entry + ret void +} + +declare dso_local void @__kmpc_for_static_init_4(%struct.ident_t*, i32, i32, i32*, i32*, i32*, i32*, i32, i32) + +declare dso_local void @bar(i32, float, double) + +declare dso_local void @__kmpc_for_static_fini(%struct.ident_t*, i32) + +declare !callback !0 dso_local void @__kmpc_fork_call(%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) + +!0 = !{i1 true, i64 3, i64 0, i64 0} Index: test/Transforms/IPConstantProp/pthreads.ll =================================================================== --- /dev/null +++ test/Transforms/IPConstantProp/pthreads.ll @@ -0,0 +1,48 @@ +; RUN: opt -ipconstprop -S < %s | FileCheck %s +; +; #include +; +; void *GlobalVPtr; +; +; static void *foo(void *arg) { return arg; } +; static void *bar(void *arg) { return arg; } +; +; int main() { +; pthread_t thread; +; pthread_create(&thread, NULL, foo, NULL); +; pthread_create(&thread, NULL, bar, &GlobalVPtr); +; return 0; +; } +; +; Verify the constant values NULL and &GlobalVPtr are propagated into foo and +; bar, respectively. +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +%union.pthread_attr_t = type { i64, [48 x i8] } + +@GlobalVPtr = common dso_local global i8* null, align 8 + +define dso_local i32 @main() { +entry: + %thread = alloca i64, align 8 + %call = call i32 @pthread_create(i64* nonnull %thread, %union.pthread_attr_t* null, i8* (i8*)* nonnull @foo, i8* null) + %call1 = call i32 @pthread_create(i64* nonnull %thread, %union.pthread_attr_t* null, i8* (i8*)* nonnull @bar, i8* bitcast (i8** @GlobalVPtr to i8*)) + ret i32 0 +} + +declare !callback !0 dso_local i32 @pthread_create(i64*, %union.pthread_attr_t*, i8* (i8*)*, i8*) + +define internal i8* @foo(i8* %arg) { +entry: +; CHECK: ret i8* null + ret i8* %arg +} + +define internal i8* @bar(i8* %arg) { +entry: +; CHECK: ret i8* bitcast (i8** @GlobalVPtr to i8*) + ret i8* %arg +} + +!0 = !{i1 false, i64 3, i64 4}