[llvm] r265762 - Don't IPO over functions that can be de-refined

Benjamin Kramer via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 8 06:08:44 PDT 2016


I'm seeing "missed optimization" fallout from this.

$ cat foo.cc
inline void foo(const char *, ...) {}

int main() {
  foo("bar");
}

$ clang-old -S -o - log.cc -O2|grep -c bar
0
$ clang-new -S -o - log.cc -O2|grep -c bar
1

Is this something we want to fix or is that how it's supposed to work
now. GCC happily discards foo() here.

On Fri, Apr 8, 2016 at 2:48 AM, Sanjoy Das via llvm-commits
<llvm-commits at lists.llvm.org> wrote:
> Author: sanjoy
> Date: Thu Apr  7 19:48:30 2016
> New Revision: 265762
>
> URL: http://llvm.org/viewvc/llvm-project?rev=265762&view=rev
> Log:
> Don't IPO over functions that can be de-refined
>
> Summary:
> Fixes PR26774.
>
> If you're aware of the issue, feel free to skip the "Motivation"
> section and jump directly to "This patch".
>
> Motivation:
>
> I define "refinement" as discarding behaviors from a program that the
> optimizer has license to discard.  So transforming:
>
> ```
> void f(unsigned x) {
>   unsigned t = 5 / x;
>   (void)t;
> }
> ```
>
> to
>
> ```
> void f(unsigned x) { }
> ```
>
> is refinement, since the behavior went from "if x == 0 then undefined
> else nothing" to "nothing" (the optimizer has license to discard
> undefined behavior).
>
> Refinement is a fundamental aspect of many mid-level optimizations done
> by LLVM.  For instance, transforming `x == (x + 1)` to `false` also
> involves refinement since the expression's value went from "if x is
> `undef` then { `true` or `false` } else { `false` }" to "`false`" (by
> definition, the optimizer has license to fold `undef` to any non-`undef`
> value).
>
> Unfortunately, refinement implies that the optimizer cannot assume
> that the implementation of a function it can see has all of the
> behavior an unoptimized or a differently optimized version of the same
> function can have.  This is a problem for functions with comdat
> linkage, where a function can be replaced by an unoptimized or a
> differently optimized version of the same source level function.
>
> For instance, FunctionAttrs cannot assume a comdat function is
> actually `readnone` even if it does not have any loads or stores in
> it; since there may have been loads and stores in the "original
> function" that were refined out in the currently visible variant, and
> at the link step the linker may in fact choose an implementation with
> a load or a store.  As an example, consider a function that does two
> atomic loads from the same memory location, and writes to memory only
> if the two values are not equal.  The optimizer is allowed to refine
> this function by first CSE'ing the two loads, and the folding the
> comparision to always report that the two values are equal.  Such a
> refined variant will look like it is `readonly`.  However, the
> unoptimized version of the function can still write to memory (since
> the two loads //can// result in different values), and selecting the
> unoptimized version at link time will retroactively invalidate
> transforms we may have done under the assumption that the function
> does not write to memory.
>
> Note: this is not just a problem with atomics or with linking
> differently optimized object files.  See PR26774 for more realistic
> examples that involved neither.
>
> This patch:
>
> This change introduces a new set of linkage types, predicated as
> `GlobalValue::mayBeDerefined` that returns true if the linkage type
> allows a function to be replaced by a differently optimized variant at
> link time.  It then changes a set of IPO passes to bail out if they see
> such a function.
>
> Reviewers: chandlerc, hfinkel, dexonsmith, joker.eph, rnk
>
> Subscribers: mcrosier, llvm-commits
>
> Differential Revision: http://reviews.llvm.org/D18634
>
> Added:
>     llvm/trunk/test/Analysis/GlobalsModRef/comdat-ipo.ll
>     llvm/trunk/test/Transforms/FunctionAttrs/comdat-ipo.ll
>     llvm/trunk/test/Transforms/IPConstantProp/comdat-ipo.ll
>     llvm/trunk/test/Transforms/Inline/comdat-ipo.ll
>     llvm/trunk/test/Transforms/ObjCARC/comdat-ipo.ll
>     llvm/trunk/test/Transforms/SCCP/comdat-ipo.ll
> Modified:
>     llvm/trunk/include/llvm/IR/GlobalValue.h
>     llvm/trunk/include/llvm/IR/GlobalVariable.h
>     llvm/trunk/lib/Analysis/BasicAliasAnalysis.cpp
>     llvm/trunk/lib/Analysis/ConstantFolding.cpp
>     llvm/trunk/lib/Analysis/GlobalsModRef.cpp
>     llvm/trunk/lib/Analysis/InlineCost.cpp
>     llvm/trunk/lib/Analysis/InstructionSimplify.cpp
>     llvm/trunk/lib/Analysis/Loads.cpp
>     llvm/trunk/lib/Analysis/MemoryBuiltins.cpp
>     llvm/trunk/lib/Analysis/ScalarEvolution.cpp
>     llvm/trunk/lib/Analysis/ValueTracking.cpp
>     llvm/trunk/lib/IR/Value.cpp
>     llvm/trunk/lib/IR/Verifier.cpp
>     llvm/trunk/lib/Transforms/IPO/DeadArgumentElimination.cpp
>     llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp
>     llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp
>     llvm/trunk/lib/Transforms/IPO/IPConstantPropagation.cpp
>     llvm/trunk/lib/Transforms/IPO/MergeFunctions.cpp
>     llvm/trunk/lib/Transforms/IPO/PruneEH.cpp
>     llvm/trunk/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
>     llvm/trunk/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp
>     llvm/trunk/lib/Transforms/ObjCARC/ObjCARCContract.cpp
>     llvm/trunk/lib/Transforms/Scalar/SCCP.cpp
>     llvm/trunk/lib/Transforms/Scalar/SROA.cpp
>     llvm/trunk/lib/Transforms/Utils/Evaluator.cpp
>     llvm/trunk/test/Verifier/alias.ll
>
> Modified: llvm/trunk/include/llvm/IR/GlobalValue.h
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/GlobalValue.h?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/include/llvm/IR/GlobalValue.h (original)
> +++ llvm/trunk/include/llvm/IR/GlobalValue.h Thu Apr  7 19:48:30 2016
> @@ -96,6 +96,56 @@ private:
>    void destroyConstantImpl();
>    Value *handleOperandChangeImpl(Value *From, Value *To);
>
> +  /// Returns true if the definition of this global may be replaced by a
> +  /// differently optimized variant of the same source level function at link
> +  /// time.
> +  bool mayBeDerefined() const {
> +    switch (getLinkage()) {
> +    case WeakODRLinkage:
> +    case LinkOnceODRLinkage:
> +    case AvailableExternallyLinkage:
> +      return true;
> +
> +    case WeakAnyLinkage:
> +    case LinkOnceAnyLinkage:
> +    case CommonLinkage:
> +    case ExternalWeakLinkage:
> +    case ExternalLinkage:
> +    case AppendingLinkage:
> +    case InternalLinkage:
> +    case PrivateLinkage:
> +      return mayBeOverridden();
> +    }
> +
> +    llvm_unreachable("Fully covered switch above!");
> +  }
> +
> +  /// Whether the definition of this global may be replaced by something
> +  /// non-equivalent at link time. For example, if a function has weak linkage
> +  /// then the code defining it may be replaced by different code.
> +  bool mayBeOverridden() const {
> +    switch (getLinkage()) {
> +    case WeakAnyLinkage:
> +    case LinkOnceAnyLinkage:
> +    case CommonLinkage:
> +    case ExternalWeakLinkage:
> +      return true;
> +
> +    case AvailableExternallyLinkage:
> +    case LinkOnceODRLinkage:
> +    case WeakODRLinkage:
> +      // The above three cannot be overridden but can be de-refined.
> +
> +    case ExternalLinkage:
> +    case AppendingLinkage:
> +    case InternalLinkage:
> +    case PrivateLinkage:
> +      return false;
> +    }
> +
> +    llvm_unreachable("Fully covered switch above!");
> +  }
> +
>  protected:
>    /// \brief The intrinsic ID for this subclass (which must be a Function).
>    ///
> @@ -242,14 +292,6 @@ public:
>             isAvailableExternallyLinkage(Linkage);
>    }
>
> -  /// Whether the definition of this global may be replaced by something
> -  /// non-equivalent at link time. For example, if a function has weak linkage
> -  /// then the code defining it may be replaced by different code.
> -  static bool mayBeOverridden(LinkageTypes Linkage) {
> -    return Linkage == WeakAnyLinkage || Linkage == LinkOnceAnyLinkage ||
> -           Linkage == CommonLinkage || Linkage == ExternalWeakLinkage;
> -  }
> -
>    /// Whether the definition of this global may be replaced at link time.  NB:
>    /// Using this method outside of the code generators is almost always a
>    /// mistake: when working at the IR level use mayBeOverridden instead as it
> @@ -260,6 +302,52 @@ public:
>             Linkage == CommonLinkage || Linkage == ExternalWeakLinkage;
>    }
>
> +  /// Return true if the currently visible definition of this global (if any) is
> +  /// exactly the definition we will see at runtime.
> +  ///
> +  /// Non-exact linkage types inhibits most non-inlining IPO, since a
> +  /// differently optimized variant of the same function can have different
> +  /// observable or undefined behavior than in the variant currently visible.
> +  /// For instance, we could have started with
> +  ///
> +  ///   void foo(int *v) {
> +  ///     int t = 5 / v[0];
> +  ///     (void) t;
> +  ///   }
> +  ///
> +  /// and "refined" it to
> +  ///
> +  ///   void foo(int *v) { }
> +  ///
> +  /// However, we cannot infer readnone for `foo`, since that would justify
> +  /// DSE'ing a store to `v[0]` across a call to `foo`, which can cause
> +  /// undefined behavior if the linker replaces the actual call destination with
> +  /// the unoptimized `foo`.
> +  ///
> +  /// Inlining is okay across non-exact linkage types as long as they're not
> +  /// interposable (see \c isInterposable), since in such cases the currently
> +  /// visible variant is *a* correct implementation of the original source
> +  /// function; it just isn't the *only* correct implementation.
> +  bool isDefinitionExact() const {
> +    return !mayBeDerefined();
> +  }
> +
> +  /// Return true if this global has an exact defintion.
> +  bool hasExactDefinition() const {
> +    // While this computes exactly the same thing as
> +    // isStrongDefinitionForLinker, the intended uses are different.  This
> +    // function is intended to help decide if specific inter-procedural
> +    // transforms are correct, while isStrongDefinitionForLinker's intended use
> +    // is in low level code generation.
> +    return !isDeclaration() && isDefinitionExact();
> +  }
> +
> +  /// Return true if this global's definition can be substituted with an
> +  /// *arbitrary* definition at link time.  We cannot do any IPO or inlinining
> +  /// across interposable call edges, since the callee can be replaced with
> +  /// something arbitrary at link time.
> +  bool isInterposable() const { return mayBeOverridden(); }
> +
>    bool hasExternalLinkage() const { return isExternalLinkage(getLinkage()); }
>    bool hasAvailableExternallyLinkage() const {
>      return isAvailableExternallyLinkage(getLinkage());
> @@ -291,8 +379,6 @@ public:
>      return isDiscardableIfUnused(getLinkage());
>    }
>
> -  bool mayBeOverridden() const { return mayBeOverridden(getLinkage()); }
> -
>    bool isWeakForLinker() const { return isWeakForLinker(getLinkage()); }
>
>    /// Copy all additional attributes (those not needed to create a GlobalValue)
> @@ -365,6 +451,10 @@ public:
>
>    /// Returns true if this global's definition will be the one chosen by the
>    /// linker.
> +  ///
> +  /// NB! Ideally this should not be used at the IR level at all.  If you're
> +  /// interested in optimization constraints implied by the linker's ability to
> +  /// choose an implementation, prefer using \c hasExactDefinition.
>    bool isStrongDefinitionForLinker() const {
>      return !(isDeclarationForLinker() || isWeakForLinker());
>    }
>
> Modified: llvm/trunk/include/llvm/IR/GlobalVariable.h
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/GlobalVariable.h?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/include/llvm/IR/GlobalVariable.h (original)
> +++ llvm/trunk/include/llvm/IR/GlobalVariable.h Thu Apr  7 19:48:30 2016
> @@ -94,9 +94,9 @@ public:
>    /// unique.
>    inline bool hasDefinitiveInitializer() const {
>      return hasInitializer() &&
> -      // The initializer of a global variable with weak linkage may change at
> -      // link time.
> -      !mayBeOverridden() &&
> +      // The initializer of a global variable may change to something arbitrary
> +      // at link time.
> +      !isInterposable() &&
>        // The initializer of a global variable with the externally_initialized
>        // marker may change at runtime before C++ initializers are evaluated.
>        !isExternallyInitialized();
>
> Modified: llvm/trunk/lib/Analysis/BasicAliasAnalysis.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/BasicAliasAnalysis.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Analysis/BasicAliasAnalysis.cpp (original)
> +++ llvm/trunk/lib/Analysis/BasicAliasAnalysis.cpp Thu Apr  7 19:48:30 2016
> @@ -359,7 +359,7 @@ static int64_t adjustToPointerSize(int64
>      if (!Op) {
>        // The only non-operator case we can handle are GlobalAliases.
>        if (const GlobalAlias *GA = dyn_cast<GlobalAlias>(V)) {
> -        if (!GA->mayBeOverridden()) {
> +        if (!GA->isInterposable()) {
>            V = GA->getAliasee();
>            continue;
>          }
>
> Modified: llvm/trunk/lib/Analysis/ConstantFolding.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/ConstantFolding.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Analysis/ConstantFolding.cpp (original)
> +++ llvm/trunk/lib/Analysis/ConstantFolding.cpp Thu Apr  7 19:48:30 2016
> @@ -531,7 +531,7 @@ Constant *llvm::ConstantFoldLoadFromCons
>        return GV->getInitializer();
>
>    if (auto *GA = dyn_cast<GlobalAlias>(C))
> -    if (GA->getAliasee() && !GA->mayBeOverridden())
> +    if (GA->getAliasee() && !GA->isInterposable())
>        return ConstantFoldLoadFromConstPtr(GA->getAliasee(), Ty, DL);
>
>    // If the loaded value isn't a constant expr, we can't handle it.
>
> Modified: llvm/trunk/lib/Analysis/GlobalsModRef.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/GlobalsModRef.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Analysis/GlobalsModRef.cpp (original)
> +++ llvm/trunk/lib/Analysis/GlobalsModRef.cpp Thu Apr  7 19:48:30 2016
> @@ -471,9 +471,10 @@ void GlobalsAAResult::AnalyzeCallGraph(C
>      const std::vector<CallGraphNode *> &SCC = *I;
>      assert(!SCC.empty() && "SCC with no functions?");
>
> -    if (!SCC[0]->getFunction() || SCC[0]->getFunction()->mayBeOverridden()) {
> -      // Calls externally or is weak - can't say anything useful. Remove any existing
> -      // function records (may have been created when scanning globals).
> +    if (!SCC[0]->getFunction() || !SCC[0]->getFunction()->isDefinitionExact()) {
> +      // Calls externally or not exact - can't say anything useful. Remove any
> +      // existing function records (may have been created when scanning
> +      // globals).
>        for (auto *Node : SCC)
>          FunctionInfos.erase(Node->getFunction());
>        continue;
> @@ -699,7 +700,7 @@ bool GlobalsAAResult::isNonEscapingGloba
>        auto *InputGVar = dyn_cast<GlobalVariable>(InputGV);
>        if (GVar && InputGVar &&
>            !GVar->isDeclaration() && !InputGVar->isDeclaration() &&
> -          !GVar->mayBeOverridden() && !InputGVar->mayBeOverridden()) {
> +          !GVar->isInterposable() && !InputGVar->isInterposable()) {
>          Type *GVType = GVar->getInitializer()->getType();
>          Type *InputGVType = InputGVar->getInitializer()->getType();
>          if (GVType->isSized() && InputGVType->isSized() &&
>
> Modified: llvm/trunk/lib/Analysis/InlineCost.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/InlineCost.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Analysis/InlineCost.cpp (original)
> +++ llvm/trunk/lib/Analysis/InlineCost.cpp Thu Apr  7 19:48:30 2016
> @@ -1125,7 +1125,7 @@ ConstantInt *CallAnalyzer::stripAndCompu
>      } else if (Operator::getOpcode(V) == Instruction::BitCast) {
>        V = cast<Operator>(V)->getOperand(0);
>      } else if (GlobalAlias *GA = dyn_cast<GlobalAlias>(V)) {
> -      if (GA->mayBeOverridden())
> +      if (GA->isInterposable())
>          break;
>        V = GA->getAliasee();
>      } else {
> @@ -1477,10 +1477,11 @@ InlineCost llvm::getInlineCost(CallSite
>    if (CS.getCaller()->hasFnAttribute(Attribute::OptimizeNone))
>      return llvm::InlineCost::getNever();
>
> -  // Don't inline functions which can be redefined at link-time to mean
> -  // something else.  Don't inline functions marked noinline or call sites
> -  // marked noinline.
> -  if (Callee->mayBeOverridden() ||
> +  // Don't inline functions which can be interposed at link-time.  Don't inline
> +  // functions marked noinline or call sites marked noinline.
> +  // Note: inlining non-exact non-interposable fucntions is fine, since we know
> +  // we have *a* correct implementation of the source level function.
> +  if (Callee->isInterposable() ||
>        Callee->hasFnAttribute(Attribute::NoInline) || CS.isNoInline())
>      return llvm::InlineCost::getNever();
>
>
> Modified: llvm/trunk/lib/Analysis/InstructionSimplify.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/InstructionSimplify.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Analysis/InstructionSimplify.cpp (original)
> +++ llvm/trunk/lib/Analysis/InstructionSimplify.cpp Thu Apr  7 19:48:30 2016
> @@ -616,7 +616,7 @@ static Constant *stripAndComputeConstant
>      } else if (Operator::getOpcode(V) == Instruction::BitCast) {
>        V = cast<Operator>(V)->getOperand(0);
>      } else if (GlobalAlias *GA = dyn_cast<GlobalAlias>(V)) {
> -      if (GA->mayBeOverridden())
> +      if (GA->isInterposable())
>          break;
>        V = GA->getAliasee();
>      } else {
>
> Modified: llvm/trunk/lib/Analysis/Loads.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/Loads.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Analysis/Loads.cpp (original)
> +++ llvm/trunk/lib/Analysis/Loads.cpp Thu Apr  7 19:48:30 2016
> @@ -299,9 +299,9 @@ bool llvm::isSafeToLoadUnconditionally(V
>      BaseAlign = AI->getAlignment();
>    } else if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(Base)) {
>      // Global variables are not necessarily safe to load from if they are
> -    // overridden. Their size may change or they may be weak and require a test
> -    // to determine if they were in fact provided.
> -    if (!GV->mayBeOverridden()) {
> +    // interposed arbitrarily. Their size may change or they may be weak and
> +    // require a test to determine if they were in fact provided.
> +    if (!GV->isInterposable()) {
>        BaseType = GV->getType()->getElementType();
>        BaseAlign = GV->getAlignment();
>      }
>
> Modified: llvm/trunk/lib/Analysis/MemoryBuiltins.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/MemoryBuiltins.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Analysis/MemoryBuiltins.cpp (original)
> +++ llvm/trunk/lib/Analysis/MemoryBuiltins.cpp Thu Apr  7 19:48:30 2016
> @@ -529,7 +529,7 @@ SizeOffsetType ObjectSizeOffsetVisitor::
>  }
>
>  SizeOffsetType ObjectSizeOffsetVisitor::visitGlobalAlias(GlobalAlias &GA) {
> -  if (GA.mayBeOverridden())
> +  if (GA.isInterposable())
>      return unknown();
>    return compute(GA.getAliasee());
>  }
>
> Modified: llvm/trunk/lib/Analysis/ScalarEvolution.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/ScalarEvolution.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Analysis/ScalarEvolution.cpp (original)
> +++ llvm/trunk/lib/Analysis/ScalarEvolution.cpp Thu Apr  7 19:48:30 2016
> @@ -4828,7 +4828,7 @@ const SCEV *ScalarEvolution::createSCEV(
>    else if (isa<ConstantPointerNull>(V))
>      return getZero(V->getType());
>    else if (GlobalAlias *GA = dyn_cast<GlobalAlias>(V))
> -    return GA->mayBeOverridden() ? getUnknown(V) : getSCEV(GA->getAliasee());
> +    return GA->isInterposable() ? getUnknown(V) : getSCEV(GA->getAliasee());
>    else if (!isa<ConstantExpr>(V))
>      return getUnknown(V);
>
>
> Modified: llvm/trunk/lib/Analysis/ValueTracking.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/ValueTracking.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Analysis/ValueTracking.cpp (original)
> +++ llvm/trunk/lib/Analysis/ValueTracking.cpp Thu Apr  7 19:48:30 2016
> @@ -1450,7 +1450,7 @@ void computeKnownBits(Value *V, APInt &K
>    // A weak GlobalAlias is totally unknown. A non-weak GlobalAlias has
>    // the bits of its aliasee.
>    if (GlobalAlias *GA = dyn_cast<GlobalAlias>(V)) {
> -    if (!GA->mayBeOverridden())
> +    if (!GA->isInterposable())
>        computeKnownBits(GA->getAliasee(), KnownZero, KnownOne, Depth + 1, Q);
>      return;
>    }
> @@ -2640,7 +2640,7 @@ Value *llvm::GetPointerBaseWithConstantO
>                 Operator::getOpcode(Ptr) == Instruction::AddrSpaceCast) {
>        Ptr = cast<Operator>(Ptr)->getOperand(0);
>      } else if (GlobalAlias *GA = dyn_cast<GlobalAlias>(Ptr)) {
> -      if (GA->mayBeOverridden())
> +      if (GA->isInterposable())
>          break;
>        Ptr = GA->getAliasee();
>      } else {
> @@ -2836,7 +2836,7 @@ Value *llvm::GetUnderlyingObject(Value *
>                 Operator::getOpcode(V) == Instruction::AddrSpaceCast) {
>        V = cast<Operator>(V)->getOperand(0);
>      } else if (GlobalAlias *GA = dyn_cast<GlobalAlias>(V)) {
> -      if (GA->mayBeOverridden())
> +      if (GA->isInterposable())
>          return V;
>        V = GA->getAliasee();
>      } else {
>
> Modified: llvm/trunk/lib/IR/Value.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/Value.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/IR/Value.cpp (original)
> +++ llvm/trunk/lib/IR/Value.cpp Thu Apr  7 19:48:30 2016
> @@ -460,7 +460,7 @@ static Value *stripPointerCastsAndOffset
>                 Operator::getOpcode(V) == Instruction::AddrSpaceCast) {
>        V = cast<Operator>(V)->getOperand(0);
>      } else if (GlobalAlias *GA = dyn_cast<GlobalAlias>(V)) {
> -      if (StripKind == PSK_ZeroIndices || GA->mayBeOverridden())
> +      if (StripKind == PSK_ZeroIndices || GA->isInterposable())
>          return V;
>        V = GA->getAliasee();
>      } else {
>
> Modified: llvm/trunk/lib/IR/Verifier.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/Verifier.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/IR/Verifier.cpp (original)
> +++ llvm/trunk/lib/IR/Verifier.cpp Thu Apr  7 19:48:30 2016
> @@ -626,7 +626,7 @@ void Verifier::visitAliaseeSubExpr(Small
>      if (const auto *GA2 = dyn_cast<GlobalAlias>(GV)) {
>        Assert(Visited.insert(GA2).second, "Aliases cannot form a cycle", &GA);
>
> -      Assert(!GA2->mayBeOverridden(), "Alias cannot point to a weak alias",
> +      Assert(!GA2->isInterposable(), "Alias cannot point to an interposable alias",
>               &GA);
>      } else {
>        // Only continue verifying subexpressions of GlobalAliases.
>
> Modified: llvm/trunk/lib/Transforms/IPO/DeadArgumentElimination.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/DeadArgumentElimination.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/IPO/DeadArgumentElimination.cpp (original)
> +++ llvm/trunk/lib/Transforms/IPO/DeadArgumentElimination.cpp Thu Apr  7 19:48:30 2016
> @@ -329,7 +329,7 @@ bool DAE::RemoveDeadArgumentsFromCallers
>    //   %v = load i32 %p
>    //   ret void
>    // }
> -  if (!Fn.isStrongDefinitionForLinker())
> +  if (!Fn.hasExactDefinition())
>      return false;
>
>    // Functions with local linkage should already have been handled, except the
>
> Modified: llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp (original)
> +++ llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp Thu Apr  7 19:48:30 2016
> @@ -69,9 +69,10 @@ static MemoryAccessKind checkFunctionMem
>      // Already perfect!
>      return MAK_ReadNone;
>
> -  // Definitions with weak linkage may be overridden at linktime with
> -  // something that writes memory, so treat them like declarations.
> -  if (F.isDeclaration() || F.mayBeOverridden()) {
> +  // Non-exact function definitions may not be selected at link time, and an
> +  // alternative version that writes to memory may be selected.  See the comment
> +  // on GlobalValue::isDefinitionExact for more details.
> +  if (!F.hasExactDefinition()) {
>      if (AliasAnalysis::onlyReadsMemory(MRB))
>        return MAK_ReadOnly;
>
> @@ -284,8 +285,7 @@ struct ArgumentUsesTracker : public Capt
>      }
>
>      Function *F = CS.getCalledFunction();
> -    if (!F || F->isDeclaration() || F->mayBeOverridden() ||
> -        !SCCNodes.count(F)) {
> +    if (!F || !F->hasExactDefinition() || !SCCNodes.count(F)) {
>        Captured = true;
>        return true;
>      }
> @@ -490,9 +490,10 @@ static bool addArgumentAttrs(const SCCNo
>    // Check each function in turn, determining which pointer arguments are not
>    // captured.
>    for (Function *F : SCCNodes) {
> -    // Definitions with weak linkage may be overridden at linktime with
> -    // something that captures pointers, so treat them like declarations.
> -    if (F->isDeclaration() || F->mayBeOverridden())
> +    // We can infer and propagate function attributes only when we know that the
> +    // definition we'll get at link time is *exactly* the definition we see now.
> +    // For more details, see GlobalValue::mayBeDerefined.
> +    if (!F->hasExactDefinition())
>        continue;
>
>      // Functions that are readonly (or readnone) and nounwind and don't return
> @@ -745,9 +746,10 @@ static bool addNoAliasAttrs(const SCCNod
>      if (F->doesNotAlias(0))
>        continue;
>
> -    // Definitions with weak linkage may be overridden at linktime, so
> -    // treat them like declarations.
> -    if (F->isDeclaration() || F->mayBeOverridden())
> +    // We can infer and propagate function attributes only when we know that the
> +    // definition we'll get at link time is *exactly* the definition we see now.
> +    // For more details, see GlobalValue::mayBeDerefined.
> +    if (!F->hasExactDefinition())
>        return false;
>
>      // We annotate noalias return values, which are only applicable to
> @@ -859,9 +861,10 @@ static bool addNonNullAttrs(const SCCNod
>                                          Attribute::NonNull))
>        continue;
>
> -    // Definitions with weak linkage may be overridden at linktime, so
> -    // treat them like declarations.
> -    if (F->isDeclaration() || F->mayBeOverridden())
> +    // We can infer and propagate function attributes only when we know that the
> +    // definition we'll get at link time is *exactly* the definition we see now.
> +    // For more details, see GlobalValue::mayBeDerefined.
> +    if (!F->hasExactDefinition())
>        return false;
>
>      // We annotate nonnull return values, which are only applicable to
>
> Modified: llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp (original)
> +++ llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp Thu Apr  7 19:48:30 2016
> @@ -2366,7 +2366,7 @@ bool GlobalOpt::OptimizeGlobalAliases(Mo
>      }
>
>      // If the aliasee may change at link time, nothing can be done - bail out.
> -    if (J->mayBeOverridden())
> +    if (J->isInterposable())
>        continue;
>
>      Constant *Aliasee = J->getAliasee();
>
> Modified: llvm/trunk/lib/Transforms/IPO/IPConstantPropagation.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/IPConstantPropagation.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/IPO/IPConstantPropagation.cpp (original)
> +++ llvm/trunk/lib/Transforms/IPO/IPConstantPropagation.cpp Thu Apr  7 19:48:30 2016
> @@ -161,9 +161,10 @@ bool IPCP::PropagateConstantReturn(Funct
>    if (F.getReturnType()->isVoidTy())
>      return false; // No return value.
>
> -  // If this function could be overridden later in the link stage, we can't
> -  // propagate information about its results into callers.
> -  if (F.mayBeOverridden())
> +  // We can infer and propagate the return value only when we know that the
> +  // definition we'll get at link time is *exactly* the definition we see now.
> +  // For more details, see GlobalValue::mayBeDerefined.
> +  if (!F.isDefinitionExact())
>      return false;
>
>    // Check to see if this function returns a constant.
>
> Modified: llvm/trunk/lib/Transforms/IPO/MergeFunctions.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/MergeFunctions.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/IPO/MergeFunctions.cpp (original)
> +++ llvm/trunk/lib/Transforms/IPO/MergeFunctions.cpp Thu Apr  7 19:48:30 2016
> @@ -1572,7 +1572,7 @@ bool MergeFunctions::runOnModule(Module
>        if (!*I) continue;
>        Function *F = cast<Function>(*I);
>        if (!F->isDeclaration() && !F->hasAvailableExternallyLinkage() &&
> -          !F->mayBeOverridden()) {
> +          !F->isInterposable()) {
>          Changed |= insert(F);
>        }
>      }
> @@ -1586,7 +1586,7 @@ bool MergeFunctions::runOnModule(Module
>        if (!*I) continue;
>        Function *F = cast<Function>(*I);
>        if (!F->isDeclaration() && !F->hasAvailableExternallyLinkage() &&
> -          F->mayBeOverridden()) {
> +          F->isInterposable()) {
>          Changed |= insert(F);
>        }
>      }
> @@ -1683,7 +1683,7 @@ static Value *createCast(IRBuilder<> &Bu
>  // Replace G with a simple tail call to bitcast(F). Also replace direct uses
>  // of G with bitcast(F). Deletes G.
>  void MergeFunctions::writeThunk(Function *F, Function *G) {
> -  if (!G->mayBeOverridden()) {
> +  if (!G->isInterposable()) {
>      // Redirect direct callers of G to F.
>      replaceDirectCallers(G, F);
>    }
> @@ -1744,8 +1744,8 @@ void MergeFunctions::writeAlias(Function
>
>  // Merge two equivalent functions. Upon completion, Function G is deleted.
>  void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
> -  if (F->mayBeOverridden()) {
> -    assert(G->mayBeOverridden());
> +  if (F->isInterposable()) {
> +    assert(G->isInterposable());
>
>      // Make them both thunks to the same internal function.
>      Function *H = Function::Create(F->getFunctionType(), F->getLinkage(), "",
> @@ -1828,8 +1828,8 @@ bool MergeFunctions::insert(Function *Ne
>    //
>    // When one function is weak and the other is strong there is an order imposed
>    // already. We process strong functions before weak functions.
> -  if ((OldF.getFunc()->mayBeOverridden() && NewFunction->mayBeOverridden()) ||
> -      (!OldF.getFunc()->mayBeOverridden() && !NewFunction->mayBeOverridden()))
> +  if ((OldF.getFunc()->isInterposable() && NewFunction->isInterposable()) ||
> +      (!OldF.getFunc()->isInterposable() && !NewFunction->isInterposable()))
>      if (OldF.getFunc()->getName() > NewFunction->getName()) {
>        // Swap the two functions.
>        Function *F = OldF.getFunc();
> @@ -1839,7 +1839,7 @@ bool MergeFunctions::insert(Function *Ne
>      }
>
>    // Never thunk a strong function to a weak function.
> -  assert(!OldF.getFunc()->mayBeOverridden() || NewFunction->mayBeOverridden());
> +  assert(!OldF.getFunc()->isInterposable() || NewFunction->isInterposable());
>
>    DEBUG(dbgs() << "  " << OldF.getFunc()->getName()
>                 << " == " << NewFunction->getName() << '\n');
>
> Modified: llvm/trunk/lib/Transforms/IPO/PruneEH.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/PruneEH.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/IPO/PruneEH.cpp (original)
> +++ llvm/trunk/lib/Transforms/IPO/PruneEH.cpp Thu Apr  7 19:48:30 2016
> @@ -93,7 +93,10 @@ bool PruneEH::runOnSCC(CallGraphSCC &SCC
>      if (!F) {
>        SCCMightUnwind = true;
>        SCCMightReturn = true;
> -    } else if (F->isDeclaration() || F->mayBeOverridden()) {
> +    } else if (F->isDeclaration() || F->isInterposable()) {
> +      // Note: isInterposable (as opposed to hasExactDefinition) is fine above,
> +      // since we're not inferring new attributes here, but only using existing,
> +      // assumed to be correct, function attributes.
>        SCCMightUnwind |= !F->doesNotThrow();
>        SCCMightReturn |= !F->doesNotReturn();
>      } else {
>
> Modified: llvm/trunk/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp (original)
> +++ llvm/trunk/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp Thu Apr  7 19:48:30 2016
> @@ -640,7 +640,7 @@ static bool isObjectSizeLessThanOrEq(Val
>      }
>
>      if (GlobalAlias *GA = dyn_cast<GlobalAlias>(P)) {
> -      if (GA->mayBeOverridden())
> +      if (!GA->isInterposable())
>          return false;
>        Worklist.push_back(GA->getAliasee());
>        continue;
>
> Modified: llvm/trunk/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp (original)
> +++ llvm/trunk/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp Thu Apr  7 19:48:30 2016
> @@ -70,7 +70,7 @@ void ObjCARCAPElim::getAnalysisUsage(Ana
>  /// possibly produce autoreleases.
>  bool ObjCARCAPElim::MayAutorelease(ImmutableCallSite CS, unsigned Depth) {
>    if (const Function *Callee = CS.getCalledFunction()) {
> -    if (Callee->isDeclaration() || Callee->mayBeOverridden())
> +    if (!Callee->hasExactDefinition())
>        return true;
>      for (const BasicBlock &BB : *Callee) {
>        for (const Instruction &I : BB)
>
> Modified: llvm/trunk/lib/Transforms/ObjCARC/ObjCARCContract.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/ObjCARC/ObjCARCContract.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/ObjCARC/ObjCARCContract.cpp (original)
> +++ llvm/trunk/lib/Transforms/ObjCARC/ObjCARCContract.cpp Thu Apr  7 19:48:30 2016
> @@ -605,7 +605,7 @@ bool ObjCARCContract::runOnFunction(Func
>                 cast<GEPOperator>(Arg)->hasAllZeroIndices())
>          Arg = cast<GEPOperator>(Arg)->getPointerOperand();
>        else if (isa<GlobalAlias>(Arg) &&
> -               !cast<GlobalAlias>(Arg)->mayBeOverridden())
> +               !cast<GlobalAlias>(Arg)->isInterposable())
>          Arg = cast<GlobalAlias>(Arg)->getAliasee();
>        else
>          break;
>
> Modified: llvm/trunk/lib/Transforms/Scalar/SCCP.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Scalar/SCCP.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/Scalar/SCCP.cpp (original)
> +++ llvm/trunk/lib/Transforms/Scalar/SCCP.cpp Thu Apr  7 19:48:30 2016
> @@ -1724,9 +1724,9 @@ bool IPSCCP::runOnModule(Module &M) {
>      if (F->isDeclaration())
>        continue;
>
> -    // 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())
> +    // If this is an exact definition of this function, then we can propagate
> +    // information about its result into callsites of it.
> +    if (F->hasExactDefinition())
>        Solver.AddTrackedFunction(&*F);
>
>      // If this function only has direct calls that we can see, we can track its
>
> Modified: llvm/trunk/lib/Transforms/Scalar/SROA.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Scalar/SROA.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/Scalar/SROA.cpp (original)
> +++ llvm/trunk/lib/Transforms/Scalar/SROA.cpp Thu Apr  7 19:48:30 2016
> @@ -1549,7 +1549,7 @@ static Value *getAdjustedPtr(IRBuilderTy
>      if (Operator::getOpcode(Ptr) == Instruction::BitCast) {
>        Ptr = cast<Operator>(Ptr)->getOperand(0);
>      } else if (GlobalAlias *GA = dyn_cast<GlobalAlias>(Ptr)) {
> -      if (GA->mayBeOverridden())
> +      if (GA->isInterposable())
>          break;
>        Ptr = GA->getAliasee();
>      } else {
>
> Modified: llvm/trunk/lib/Transforms/Utils/Evaluator.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/Evaluator.cpp?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/Utils/Evaluator.cpp (original)
> +++ llvm/trunk/lib/Transforms/Utils/Evaluator.cpp Thu Apr  7 19:48:30 2016
> @@ -427,7 +427,7 @@ bool Evaluator::EvaluateBlock(BasicBlock
>
>        // Resolve function pointers.
>        Function *Callee = dyn_cast<Function>(getVal(CS.getCalledValue()));
> -      if (!Callee || Callee->mayBeOverridden()) {
> +      if (!Callee || Callee->isInterposable()) {
>          DEBUG(dbgs() << "Can not resolve function pointer.\n");
>          return false;  // Cannot resolve.
>        }
>
> Added: llvm/trunk/test/Analysis/GlobalsModRef/comdat-ipo.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Analysis/GlobalsModRef/comdat-ipo.ll?rev=265762&view=auto
> ==============================================================================
> --- llvm/trunk/test/Analysis/GlobalsModRef/comdat-ipo.ll (added)
> +++ llvm/trunk/test/Analysis/GlobalsModRef/comdat-ipo.ll Thu Apr  7 19:48:30 2016
> @@ -0,0 +1,21 @@
> +; RUN: opt < %s -basicaa -globals-aa -gvn -S | FileCheck %s
> +
> +; See PR26774
> +
> + at X = internal global i32 4
> +
> +define i32 @test(i32* %P) {
> +; CHECK:      @test
> +; CHECK-NEXT: store i32 12, i32* @X
> +; CHECK-NEXT: call void @doesnotmodX()
> +; CHECK-NEXT:  %V = load i32, i32* @X
> +; CHECK-NEXT:  ret i32 %V
> +  store i32 12, i32* @X
> +  call void @doesnotmodX( )
> +  %V = load i32, i32* @X
> +  ret i32 %V
> +}
> +
> +define linkonce_odr void @doesnotmodX() {
> +  ret void
> +}
>
> Added: llvm/trunk/test/Transforms/FunctionAttrs/comdat-ipo.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/comdat-ipo.ll?rev=265762&view=auto
> ==============================================================================
> --- llvm/trunk/test/Transforms/FunctionAttrs/comdat-ipo.ll (added)
> +++ llvm/trunk/test/Transforms/FunctionAttrs/comdat-ipo.ll Thu Apr  7 19:48:30 2016
> @@ -0,0 +1,16 @@
> +; RUN: opt < %s -functionattrs -S | FileCheck %s
> +
> +; See PR26774
> +
> +; CHECK-LABEL: define void @bar(i8* readonly) {
> +define void @bar(i8* readonly) {
> +  call void @foo(i8* %0)
> +  ret void
> +}
> +
> +
> +; CHECK-LABEL: define linkonce_odr void @foo(i8* readonly) {
> +define linkonce_odr void @foo(i8* readonly) {
> +  call void @bar(i8* %0)
> +  ret void
> +}
>
> Added: llvm/trunk/test/Transforms/IPConstantProp/comdat-ipo.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/IPConstantProp/comdat-ipo.ll?rev=265762&view=auto
> ==============================================================================
> --- llvm/trunk/test/Transforms/IPConstantProp/comdat-ipo.ll (added)
> +++ llvm/trunk/test/Transforms/IPConstantProp/comdat-ipo.ll Thu Apr  7 19:48:30 2016
> @@ -0,0 +1,28 @@
> +; RUN: opt < %s -ipconstprop -S | FileCheck %s
> +
> +; See PR26774
> +
> +define i32 @baz() {
> +  ret i32 10
> +}
> +
> +; We can const-prop @baz's return value *into* @foo, but cannot
> +; constprop @foo's return value into bar.
> +
> +define linkonce_odr i32 @foo() {
> +; CHECK-LABEL: @foo(
> +; CHECK-NEXT:  %val = call i32 @baz()
> +; CHECK-NEXT:  ret i32 10
> +
> +  %val = call i32 @baz()
> +  ret i32 %val
> +}
> +
> +define i32 @bar() {
> +; CHECK-LABEL: @bar(
> +; CHECK-NEXT:  %val = call i32 @foo()
> +; CHECK-NEXT:  ret i32 %val
> +
> +  %val = call i32 @foo()
> +  ret i32 %val
> +}
>
> Added: llvm/trunk/test/Transforms/Inline/comdat-ipo.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Inline/comdat-ipo.ll?rev=265762&view=auto
> ==============================================================================
> --- llvm/trunk/test/Transforms/Inline/comdat-ipo.ll (added)
> +++ llvm/trunk/test/Transforms/Inline/comdat-ipo.ll Thu Apr  7 19:48:30 2016
> @@ -0,0 +1,19 @@
> +; RUN: opt -inline -S < %s | FileCheck %s
> +
> +define i32 @caller() {
> +; CHECK-LABEL: @caller(
> +; CHECK-NEXT:  %val2 = call i32 @linkonce_callee(i32 42)
> +; CHECK-NEXT:  ret i32 %val2
> +
> +  %val = call i32 @odr_callee()
> +  %val2 = call i32 @linkonce_callee(i32 %val);
> +  ret i32 %val2
> +}
> +
> +define linkonce_odr i32 @odr_callee() {
> +  ret i32 42
> +}
> +
> +define linkonce i32 @linkonce_callee(i32 %val) {
> +  ret i32 %val
> +}
>
> Added: llvm/trunk/test/Transforms/ObjCARC/comdat-ipo.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/ObjCARC/comdat-ipo.ll?rev=265762&view=auto
> ==============================================================================
> --- llvm/trunk/test/Transforms/ObjCARC/comdat-ipo.ll (added)
> +++ llvm/trunk/test/Transforms/ObjCARC/comdat-ipo.ll Thu Apr  7 19:48:30 2016
> @@ -0,0 +1,53 @@
> +; RUN: opt -S -objc-arc-apelim < %s | FileCheck %s
> +
> +; See PR26774
> +
> + at llvm.global_ctors = appending global [2 x { i32, void ()* }] [{ i32, void ()* } { i32 65535, void ()* @_GLOBAL__I_x }, { i32, void ()* } { i32 65535, void ()* @_GLOBAL__I_y }]
> +
> + at x = global i32 0
> +
> +declare i32 @bar() nounwind
> +
> +define linkonce_odr i32 @foo() nounwind {
> +entry:
> +  ret i32 5
> +}
> +
> +define internal void @__cxx_global_var_init() {
> +entry:
> +  %call = call i32 @foo()
> +  store i32 %call, i32* @x, align 4
> +  ret void
> +}
> +
> +define internal void @__dxx_global_var_init() {
> +entry:
> +  %call = call i32 @bar()
> +  store i32 %call, i32* @x, align 4
> +  ret void
> +}
> +
> +; CHECK-LABEL: define internal void @_GLOBAL__I_x() {
> +define internal void @_GLOBAL__I_x() {
> +entry:
> +; CHECK:  call i8* @objc_autoreleasePoolPush()
> +; CHECK-NEXT:  call void @__cxx_global_var_init()
> +; CHECK-NEXT:  call void @objc_autoreleasePoolPop(i8* %0)
> +; CHECK-NEXT:  ret void
> +
> +  %0 = call i8* @objc_autoreleasePoolPush() nounwind
> +  call void @__cxx_global_var_init()
> +  call void @objc_autoreleasePoolPop(i8* %0) nounwind
> +  ret void
> +}
> +
> +define internal void @_GLOBAL__I_y() {
> +entry:
> +  %0 = call i8* @objc_autoreleasePoolPush() nounwind
> +  call void @__dxx_global_var_init()
> +  call void @objc_autoreleasePoolPop(i8* %0) nounwind
> +  ret void
> +}
> +
> +declare i8* @objc_autoreleasePoolPush()
> +declare void @objc_autoreleasePoolPop(i8*)
>
> Added: llvm/trunk/test/Transforms/SCCP/comdat-ipo.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/SCCP/comdat-ipo.ll?rev=265762&view=auto
> ==============================================================================
> --- llvm/trunk/test/Transforms/SCCP/comdat-ipo.ll (added)
> +++ llvm/trunk/test/Transforms/SCCP/comdat-ipo.ll Thu Apr  7 19:48:30 2016
> @@ -0,0 +1,28 @@
> +; RUN: opt < %s -ipsccp -S | FileCheck %s
> +
> +; See PR26774
> +
> +define i32 @baz() {
> +  ret i32 10
> +}
> +
> +; We can const-prop @baz's return value *into* @foo, but cannot
> +; constprop @foo's return value into bar.
> +
> +define linkonce_odr i32 @foo() {
> +; CHECK-LABEL: @foo(
> +; CHECK-NEXT:  %val = call i32 @baz()
> +; CHECK-NEXT:  ret i32 10
> +
> +  %val = call i32 @baz()
> +  ret i32 %val
> +}
> +
> +define i32 @bar() {
> +; CHECK-LABEL: @bar(
> +; CHECK-NEXT:  %val = call i32 @foo()
> +; CHECK-NEXT:  ret i32 %val
> +
> +  %val = call i32 @foo()
> +  ret i32 %val
> +}
>
> Modified: llvm/trunk/test/Verifier/alias.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Verifier/alias.ll?rev=265762&r1=265761&r2=265762&view=diff
> ==============================================================================
> --- llvm/trunk/test/Verifier/alias.ll (original)
> +++ llvm/trunk/test/Verifier/alias.ll Thu Apr  7 19:48:30 2016
> @@ -29,5 +29,5 @@ define available_externally void @f2() {
>  @test3_a = global i32 42
>  @test3_b = weak alias i32, i32* @test3_a
>  @test3_c = alias i32, i32* @test3_b
> -; CHECK: Alias cannot point to a weak alias
> +; CHECK: Alias cannot point to an interposable alias
>  ; CHECK-NEXT: i32* @test3_c
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits


More information about the llvm-commits mailing list