Remove interprocedural-basic-aa and associated code. The AliasAnalysis

interface needs implementations to be consistent, so any code which
wants to support different semantics must use a different interface.
It's not currently worthwhile to add a new interface for this new
concept.

Document that AliasAnalysis doesn't support cross-function queries.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@107776 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Dan Gohman 2010-07-07 14:27:09 +00:00
parent 46151f1764
commit 9e86f4364b
8 changed files with 338 additions and 1959 deletions

View File

@ -117,6 +117,11 @@ as the actual <tt>call</tt> or <tt>invoke</tt> instructions that performs the
call. The <tt>AliasAnalysis</tt> interface also exposes some helper methods
which allow you to get mod/ref information for arbitrary instructions.</p>
<p>All <tt>AliasAnalysis</tt> interfaces require that in queries involving
multiple values, values which are not
<a href="LangRef.html#constants">constants</a> are all defined within the
same function.</p>
</div>
<!-- ======================================================================= -->
@ -181,9 +186,13 @@ that the accesses alias.</p>
</div>
<div class="doc_text">
The <tt>alias</tt> method is the primary interface used to determine whether or
not two memory objects alias each other. It takes two memory objects as input
and returns MustAlias, MayAlias, or NoAlias as appropriate.
<p>The <tt>alias</tt> method is the primary interface used to determine whether
or not two memory objects alias each other. It takes two memory objects as
input and returns MustAlias, MayAlias, or NoAlias as appropriate.</p>
<p>Like all <tt>AliasAnalysis</tt> interfaces, the <tt>alias</tt> method requires
that either the two pointer values be defined within the same function, or at
least one of the values is a <a href="LangRef.html#constants">constant</a>.</p>
</div>
<!-- _______________________________________________________________________ -->
@ -202,10 +211,8 @@ the other -- in this case, there is a dependence, but it's mediated by the free
and reallocation.</p>
<p>As an exception to this is with the
<a href="LangRef.html#noalias"><tt>noalias</tt></a> keyword. AliasAnalysis
implementations may choose to respect the <tt>noalias</tt> keyword and ignore
the "irrelevant" dependencies, provided their clients do not need to be aware
of these dependencies for correctness.</p>
<a href="LangRef.html#noalias"><tt>noalias</tt></a> keyword; the "irrelevant"
dependencies are ignored.</p>
<p>The MayAlias response is used whenever the two pointers might refer to the
same object. If the two memory objects overlap, but do not start at the same

View File

@ -323,10 +323,10 @@ bool isNoAliasCall(const Value *V);
/// identifiable object. This returns true for:
/// Global Variables and Functions (but not Global Aliases)
/// Allocas and Mallocs
/// ByVal and NoAlias Arguments, if Interprocedural is false
/// NoAlias returns, if Interprocedural is false
/// ByVal and NoAlias Arguments
/// NoAlias returns
///
bool isIdentifiedObject(const Value *V, bool Interprocedural = false);
bool isIdentifiedObject(const Value *V);
} // End llvm namespace

View File

@ -51,13 +51,6 @@ namespace llvm {
//
FunctionPass *createAAEvalPass();
//===--------------------------------------------------------------------===//
//
// createInterproceduralAAEvalPass - This pass implements a simple
// N^2 interprocedural alias analysis accuracy evaluator.
//
Pass *createInterproceduralAAEvalPass();
//===--------------------------------------------------------------------===//
//
// createNoAAPass - This pass implements a "I don't know" alias analysis.
@ -67,21 +60,10 @@ namespace llvm {
//===--------------------------------------------------------------------===//
//
// createBasicAliasAnalysisPass - This pass implements the default alias
// analysis. This analysis respects the noalias attribute, so it is not
// suitable for some interprocedural uses (see the discussion of noalias
// in AliasAnalysis.html for details).
// analysis.
//
ImmutablePass *createBasicAliasAnalysisPass();
//===--------------------------------------------------------------------===//
//
// createInterproceduralBasicAliasAnalysisPass - This pass is similar to
// baiscaa, except that it properly supports queries to values which live
// in different functions. Unlike the regular BasicAliasAnalysis, this
// implementation does not respect the noalias attribute.
//
ImmutablePass *createInterproceduralBasicAliasAnalysisPass();
//===--------------------------------------------------------------------===//
//
/// createLibCallAliasAnalysisPass - Create an alias analysis pass that knows

View File

@ -229,20 +229,18 @@ bool llvm::isNoAliasCall(const Value *V) {
/// identifiable object. This returns true for:
/// Global Variables and Functions (but not Global Aliases)
/// Allocas and Mallocs
/// ByVal and NoAlias Arguments, if Interprocedural is false
/// NoAlias returns, if Interprocedural is false
/// ByVal and NoAlias Arguments
/// NoAlias returns
///
bool llvm::isIdentifiedObject(const Value *V, bool Interprocedural) {
bool llvm::isIdentifiedObject(const Value *V) {
if (isa<AllocaInst>(V))
return true;
if (isa<GlobalValue>(V) && !isa<GlobalAlias>(V))
return true;
if (!Interprocedural) {
if (isNoAliasCall(V))
return true;
if (const Argument *A = dyn_cast<Argument>(V))
return A->hasNoAliasAttr() || A->hasByValAttr();
}
if (isNoAliasCall(V))
return true;
if (const Argument *A = dyn_cast<Argument>(V))
return A->hasNoAliasAttr() || A->hasByValAttr();
return false;
}

View File

@ -21,7 +21,6 @@
#include "llvm/DerivedTypes.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/Module.h"
#include "llvm/Pass.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/Analysis/AliasAnalysis.h"
@ -45,21 +44,20 @@ static cl::opt<bool> PrintRef("print-ref", cl::ReallyHidden);
static cl::opt<bool> PrintModRef("print-modref", cl::ReallyHidden);
namespace {
/// AAEval - Base class for exhaustive alias analysis evaluators.
class AAEval {
protected:
class AAEval : public FunctionPass {
unsigned NoAlias, MayAlias, MustAlias;
unsigned NoModRef, Mod, Ref, ModRef;
SetVector<Value *> Pointers;
SetVector<CallSite> CallSites;
public:
static char ID; // Pass identification, replacement for typeid
AAEval() : FunctionPass(&ID) {}
void getAnalysisUsage(AnalysisUsage &AU) const {
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<AliasAnalysis>();
AU.setPreservesAll();
}
void doInitialization(Module &M) {
bool doInitialization(Module &M) {
NoAlias = MayAlias = MustAlias = 0;
NoModRef = Mod = Ref = ModRef = 0;
@ -67,85 +65,19 @@ namespace {
PrintNoAlias = PrintMayAlias = PrintMustAlias = true;
PrintNoModRef = PrintMod = PrintRef = PrintModRef = true;
}
}
void runOnFunction(Function &F);
void evaluate(AliasAnalysis *AA, Module *M);
void doFinalization(Module &M);
};
class FunctionAAEval : public FunctionPass, AAEval {
public:
static char ID; // Pass identification, replacement for typeid
FunctionAAEval() : FunctionPass(&ID) {}
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
return AAEval::getAnalysisUsage(AU);
}
virtual bool doInitialization(Module &M) {
AAEval::doInitialization(M);
return false;
}
virtual bool runOnFunction(Function &F) {
AAEval::runOnFunction(F);
if (PrintNoAlias || PrintMayAlias || PrintMustAlias ||
PrintNoModRef || PrintMod || PrintRef || PrintModRef)
errs() << "Function: " << F.getName() << ": " << Pointers.size()
<< " pointers, " << CallSites.size() << " call sites\n";
AAEval::evaluate(&getAnalysis<AliasAnalysis>(), F.getParent());
return false;
}
virtual bool doFinalization(Module &M) {
AAEval::doFinalization(M);
return false;
}
};
class InterproceduralAAEval : public ModulePass, AAEval {
public:
static char ID; // Pass identification, replacement for typeid
InterproceduralAAEval() : ModulePass(&ID) {}
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
return AAEval::getAnalysisUsage(AU);
}
virtual bool runOnModule(Module &M) {
AAEval::doInitialization(M);
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
AAEval::runOnFunction(*I);
if (PrintNoAlias || PrintMayAlias || PrintMustAlias ||
PrintNoModRef || PrintMod || PrintRef || PrintModRef)
errs() << "Module: " << Pointers.size()
<< " pointers, " << CallSites.size() << " call sites\n";
AAEval::evaluate(&getAnalysis<AliasAnalysis>(), &M);
AAEval::doFinalization(M);
return false;
}
bool runOnFunction(Function &F);
bool doFinalization(Module &M);
};
}
char FunctionAAEval::ID = 0;
static RegisterPass<FunctionAAEval>
char AAEval::ID = 0;
static RegisterPass<AAEval>
X("aa-eval", "Exhaustive Alias Analysis Precision Evaluator", false, true);
FunctionPass *llvm::createAAEvalPass() { return new FunctionAAEval(); }
char InterproceduralAAEval::ID = 0;
static RegisterPass<InterproceduralAAEval>
Y("interprocedural-aa-eval",
"Exhaustive Interprocedural Alias Analysis Precision Evaluator", false, true);
Pass *llvm::createInterproceduralAAEvalPass() {
return new InterproceduralAAEval();
}
FunctionPass *llvm::createAAEvalPass() { return new AAEval(); }
static void PrintResults(const char *Msg, bool P, const Value *V1,
const Value *V2, const Module *M) {
@ -180,7 +112,12 @@ static inline bool isInterestingPointer(Value *V) {
&& !isa<ConstantPointerNull>(V);
}
void AAEval::runOnFunction(Function &F) {
bool AAEval::runOnFunction(Function &F) {
AliasAnalysis &AA = getAnalysis<AliasAnalysis>();
SetVector<Value *> Pointers;
SetVector<CallSite> CallSites;
for (Function::arg_iterator I = F.arg_begin(), E = F.arg_end(); I != E; ++I)
if (I->getType()->isPointerTy()) // Add all pointer arguments.
Pointers.insert(I);
@ -210,31 +147,33 @@ void AAEval::runOnFunction(Function &F) {
if (CS.getInstruction()) CallSites.insert(CS);
}
}
void AAEval::evaluate(AliasAnalysis *AA, Module *M) {
if (PrintNoAlias || PrintMayAlias || PrintMustAlias ||
PrintNoModRef || PrintMod || PrintRef || PrintModRef)
errs() << "Function: " << F.getName() << ": " << Pointers.size()
<< " pointers, " << CallSites.size() << " call sites\n";
// iterate over the worklist, and run the full (n^2)/2 disambiguations
for (SetVector<Value *>::iterator I1 = Pointers.begin(), E = Pointers.end();
I1 != E; ++I1) {
unsigned I1Size = ~0u;
const Type *I1ElTy = cast<PointerType>((*I1)->getType())->getElementType();
if (I1ElTy->isSized()) I1Size = AA->getTypeStoreSize(I1ElTy);
if (I1ElTy->isSized()) I1Size = AA.getTypeStoreSize(I1ElTy);
for (SetVector<Value *>::iterator I2 = Pointers.begin(); I2 != I1; ++I2) {
unsigned I2Size = ~0u;
const Type *I2ElTy =cast<PointerType>((*I2)->getType())->getElementType();
if (I2ElTy->isSized()) I2Size = AA->getTypeStoreSize(I2ElTy);
if (I2ElTy->isSized()) I2Size = AA.getTypeStoreSize(I2ElTy);
switch (AA->alias(*I1, I1Size, *I2, I2Size)) {
switch (AA.alias(*I1, I1Size, *I2, I2Size)) {
case AliasAnalysis::NoAlias:
PrintResults("NoAlias", PrintNoAlias, *I1, *I2, M);
PrintResults("NoAlias", PrintNoAlias, *I1, *I2, F.getParent());
++NoAlias; break;
case AliasAnalysis::MayAlias:
PrintResults("MayAlias", PrintMayAlias, *I1, *I2, M);
PrintResults("MayAlias", PrintMayAlias, *I1, *I2, F.getParent());
++MayAlias; break;
case AliasAnalysis::MustAlias:
PrintResults("MustAlias", PrintMustAlias, *I1, *I2, M);
PrintResults("MustAlias", PrintMustAlias, *I1, *I2, F.getParent());
++MustAlias; break;
default:
errs() << "Unknown alias query result!\n";
@ -251,20 +190,20 @@ void AAEval::evaluate(AliasAnalysis *AA, Module *M) {
V != Ve; ++V) {
unsigned Size = ~0u;
const Type *ElTy = cast<PointerType>((*V)->getType())->getElementType();
if (ElTy->isSized()) Size = AA->getTypeStoreSize(ElTy);
if (ElTy->isSized()) Size = AA.getTypeStoreSize(ElTy);
switch (AA->getModRefInfo(*C, *V, Size)) {
switch (AA.getModRefInfo(*C, *V, Size)) {
case AliasAnalysis::NoModRef:
PrintModRefResults("NoModRef", PrintNoModRef, I, *V, M);
PrintModRefResults("NoModRef", PrintNoModRef, I, *V, F.getParent());
++NoModRef; break;
case AliasAnalysis::Mod:
PrintModRefResults(" Mod", PrintMod, I, *V, M);
PrintModRefResults(" Mod", PrintMod, I, *V, F.getParent());
++Mod; break;
case AliasAnalysis::Ref:
PrintModRefResults(" Ref", PrintRef, I, *V, M);
PrintModRefResults(" Ref", PrintRef, I, *V, F.getParent());
++Ref; break;
case AliasAnalysis::ModRef:
PrintModRefResults(" ModRef", PrintModRef, I, *V, M);
PrintModRefResults(" ModRef", PrintModRef, I, *V, F.getParent());
++ModRef; break;
default:
errs() << "Unknown alias query result!\n";
@ -272,8 +211,7 @@ void AAEval::evaluate(AliasAnalysis *AA, Module *M) {
}
}
Pointers.clear();
CallSites.clear();
return false;
}
static void PrintPercent(unsigned Num, unsigned Sum) {
@ -281,7 +219,7 @@ static void PrintPercent(unsigned Num, unsigned Sum) {
<< ((Num*1000ULL/Sum) % 10) << "%)\n";
}
void AAEval::doFinalization(Module &M) {
bool AAEval::doFinalization(Module &M) {
unsigned AliasSum = NoAlias + MayAlias + MustAlias;
errs() << "===== Alias Analysis Evaluator Report =====\n";
if (AliasSum == 0) {
@ -317,4 +255,6 @@ void AAEval::doFinalization(Module &M) {
<< NoModRef*100/ModRefSum << "%/" << Mod*100/ModRefSum << "%/"
<< Ref*100/ModRefSum << "%/" << ModRef*100/ModRefSum << "%\n";
}
return false;
}

View File

@ -194,6 +194,7 @@ ImmutablePass *llvm::createNoAAPass() { return new NoAA(); }
// BasicAliasAnalysis Pass
//===----------------------------------------------------------------------===//
#ifndef NDEBUG
static const Function *getParent(const Value *V) {
if (const Instruction *inst = dyn_cast<Instruction>(V))
return inst->getParent()->getParent();
@ -204,15 +205,6 @@ static const Function *getParent(const Value *V) {
return NULL;
}
static bool sameParent(const Value *O1, const Value *O2) {
const Function *F1 = getParent(O1);
const Function *F2 = getParent(O2);
return F1 && F1 == F2;
}
#ifdef XDEBUG
static bool notDifferentParent(const Value *O1, const Value *O2) {
const Function *F1 = getParent(O1);
@ -227,25 +219,14 @@ namespace {
/// Because it doesn't chain to a previous alias analysis (like -no-aa), it
/// derives from the NoAA class.
struct BasicAliasAnalysis : public NoAA {
/// Interprocedural - Flag for "interprocedural" mode, where we must
/// support queries of values which live in different functions.
bool Interprocedural;
static char ID; // Class identification, replacement for typeinfo
BasicAliasAnalysis()
: NoAA(&ID), Interprocedural(false) {}
BasicAliasAnalysis(void *PID, bool interprocedural)
: NoAA(PID), Interprocedural(interprocedural) {}
BasicAliasAnalysis() : NoAA(&ID) {}
AliasResult alias(const Value *V1, unsigned V1Size,
const Value *V2, unsigned V2Size) {
assert(Visited.empty() && "Visited must be cleared after use!");
#ifdef XDEBUG
assert((Interprocedural || notDifferentParent(V1, V2)) &&
"BasicAliasAnalysis (-basicaa) doesn't support interprocedural "
"queries; use InterproceduralAliasAnalysis "
"(-interprocedural-basic-aa) instead.");
#endif
assert(notDifferentParent(V1, V2) &&
"BasicAliasAnalysis doesn't support interprocedural queries.");
AliasResult Alias = aliasCheck(V1, V1Size, V2, V2Size);
Visited.clear();
return Alias;
@ -324,6 +305,9 @@ bool BasicAliasAnalysis::pointsToConstantMemory(const Value *P) {
/// simple "address taken" analysis on local objects.
AliasAnalysis::ModRefResult
BasicAliasAnalysis::getModRefInfo(CallSite CS, Value *P, unsigned Size) {
assert(notDifferentParent(CS.getInstruction(), P) &&
"AliasAnalysis query involving multiple functions!");
const Value *Object = P->getUnderlyingObject();
// If this is a tail call and P points to a stack location, we know that
@ -336,16 +320,10 @@ BasicAliasAnalysis::getModRefInfo(CallSite CS, Value *P, unsigned Size) {
if (CI->isTailCall())
return NoModRef;
// If we can identify an object and it's known to be within the
// same function as the call, we can ignore interprocedural concerns.
bool EffectivelyInterprocedural =
Interprocedural && !sameParent(Object, CS.getInstruction());
// If the pointer is to a locally allocated object that does not escape,
// then the call can not mod/ref the pointer unless the call takes the pointer
// as an argument, and itself doesn't capture it.
if (!isa<Constant>(Object) && CS.getInstruction() != Object &&
!EffectivelyInterprocedural &&
isNonEscapingLocalObject(Object)) {
bool PassedAsArg = false;
unsigned ArgNo = 0;
@ -765,36 +743,25 @@ BasicAliasAnalysis::aliasCheck(const Value *V1, unsigned V1Size,
if (CPN->getType()->getAddressSpace() == 0)
return NoAlias;
// If we can identify two objects and they're known to be within the
// same function, we can ignore interprocedural concerns.
bool EffectivelyInterprocedural =
Interprocedural && !sameParent(O1, O2);
if (O1 != O2) {
// If V1/V2 point to two different objects we know that we have no alias.
if (isIdentifiedObject(O1, EffectivelyInterprocedural) &&
isIdentifiedObject(O2, EffectivelyInterprocedural))
if (isIdentifiedObject(O1) && isIdentifiedObject(O2))
return NoAlias;
// Constant pointers can't alias with non-const isIdentifiedObject objects.
if ((isa<Constant>(O1) &&
isIdentifiedObject(O2, EffectivelyInterprocedural) &&
!isa<Constant>(O2)) ||
(isa<Constant>(O2) &&
isIdentifiedObject(O1, EffectivelyInterprocedural) &&
!isa<Constant>(O1)))
if ((isa<Constant>(O1) && isIdentifiedObject(O2) && !isa<Constant>(O2)) ||
(isa<Constant>(O2) && isIdentifiedObject(O1) && !isa<Constant>(O1)))
return NoAlias;
// Arguments can't alias with local allocations or noalias calls
// in the same function.
if (!EffectivelyInterprocedural &&
((isa<Argument>(O1) && (isa<AllocaInst>(O2) || isNoAliasCall(O2))) ||
if (((isa<Argument>(O1) && (isa<AllocaInst>(O2) || isNoAliasCall(O2))) ||
(isa<Argument>(O2) && (isa<AllocaInst>(O1) || isNoAliasCall(O1)))))
return NoAlias;
// Most objects can't alias null.
if ((isa<ConstantPointerNull>(V2) && isKnownNonNull(O1)) ||
(isa<ConstantPointerNull>(V1) && isKnownNonNull(O2)))
if ((isa<ConstantPointerNull>(O2) && isKnownNonNull(O1)) ||
(isa<ConstantPointerNull>(O1) && isKnownNonNull(O2)))
return NoAlias;
}
@ -814,7 +781,7 @@ BasicAliasAnalysis::aliasCheck(const Value *V1, unsigned V1Size,
// temporary store the nocapture argument's value in a temporary memory
// location if that memory location doesn't escape. Or it may pass a
// nocapture value to other functions as long as they don't capture it.
if (O1 != O2 && !EffectivelyInterprocedural) {
if (O1 != O2) {
if (isEscapeSource(O1) && isNonEscapingLocalObject(O2))
return NoAlias;
if (isEscapeSource(O2) && isNonEscapingLocalObject(O1))
@ -850,33 +817,3 @@ BasicAliasAnalysis::aliasCheck(const Value *V1, unsigned V1Size,
// Make sure that anything that uses AliasAnalysis pulls in this file.
DEFINING_FILE_FOR(BasicAliasAnalysis)
//===----------------------------------------------------------------------===//
// InterproceduralBasicAliasAnalysis Pass
//===----------------------------------------------------------------------===//
namespace {
/// InterproceduralBasicAliasAnalysis - This is similar to basicaa, except
/// that it properly supports queries to values which live in different
/// functions.
///
/// Note that we don't currently take this to the extreme, analyzing all
/// call sites of a function to answer a query about an Argument.
///
struct InterproceduralBasicAliasAnalysis : public BasicAliasAnalysis {
static char ID; // Class identification, replacement for typeinfo
InterproceduralBasicAliasAnalysis() : BasicAliasAnalysis(&ID, true) {}
};
}
// Register this pass...
char InterproceduralBasicAliasAnalysis::ID = 0;
static RegisterPass<InterproceduralBasicAliasAnalysis>
W("interprocedural-basic-aa", "Interprocedural Basic Alias Analysis", false, true);
// Declare that we implement the AliasAnalysis interface
static RegisterAnalysisGroup<AliasAnalysis> Z(W);
ImmutablePass *llvm::createInterproceduralBasicAliasAnalysisPass() {
return new InterproceduralBasicAliasAnalysis();
}

File diff suppressed because it is too large Load Diff

View File

@ -1,73 +0,0 @@
; RUN: opt -interprocedural-basic-aa -interprocedural-aa-eval -print-all-alias-modref-info -disable-output < %s |& FileCheck %s
; The noalias attribute is not safe in an interprocedural context.
; CHECK: MayAlias: i8* %p, i8* %q
define void @t0(i8* noalias %p) {
store i8 0, i8* %p
ret void
}
define void @t1(i8* noalias %q) {
store i8 0, i8* %q
ret void
}
; An alloca can alias an argument in a different function.
; CHECK: MayAlias: i32* %r, i32* %s
define void @s0(i32* %r) {
store i32 0, i32* %r
ret void
}
define void @s1() {
%s = alloca i32, i32 10
store i32 0, i32* %s
call void @s0(i32* %s)
ret void
}
; An alloca does not alias an argument in the same function.
; CHECK: NoAlias: i64* %t, i64* %u
; CHECK: NoAlias: i64* %a, i64* %u
; CHECK: NoAlias: i64* %a, i64* %t
; CHECK: MayAlias: i64* %u, i64* %v
; CHECK: MayAlias: i64* %t, i64* %v
; CHECK: NoAlias: i64* %a, i64* %v
; CHECK: MayAlias: i64* %b, i64* %u
; CHECK: MayAlias: i64* %b, i64* %t
; CHECK: MayAlias: i64* %b, i64* %v
declare i64* @r0_callee(i64*)
define i64* @r0(i64* %u) {
%t = alloca i64, i32 10
%a = alloca i64, i32 10
%v = call i64* @r0_callee(i64* %t)
%b = call i64* @r0_callee(i64* %t)
store i64 0, i64* %t
store i64 0, i64* %u
store i64 0, i64* %v
store i64 0, i64* %a
store i64 0, i64* %b
ret i64* %t
}
; The noalias attribute is safe when both arguments belong to the same function
; even in an interprocedural context.
; CHECK: NoAlias: i8* %w, i8* %x
define void @q0(i8* noalias %w, i8* noalias %x) {
store i8 0, i8* %w
store i8 0, i8* %x
call void @q0(i8* noalias %x, i8* noalias %w)
unreachable
}
; The noalias attribute is not necessarily safe in an interprocedural context.
; CHECK: MayAlias: double* %y, double* @G
@G = external global double
define void @p0(double* noalias %y) {
store double 0.0, double* %y
store double 0.0, double* @G
unreachable
}