Skip to content

Commit bbeaf2a

Browse files
committed
[GlobalOpt][Evaluator] Rewrite global ctor evaluation (fixes PR51879)
Global ctor evaluation currently models memory as a map from Constant* to Constant*. For this to be correct, it is required that there is only a single Constant* referencing a given memory location. The Evaluator tries to ensure this by imposing certain limitations that could result in ambiguities (by limiting types, casts and GEP formats), but ultimately still fails, as can be seen in PR51879. The approach is fundamentally fragile and will get more so with opaque pointers. My original thought was to instead store memory for each global as an offset => value representation. However, we also need to make sure that we can actually rematerialize the modified global initializer into a Constant in the end, which may not be possible if we allow arbitrary writes. What this patch does instead is to represent globals as a MutableValue, which is either a Constant* or a MutableAggregate*. The mutable aggregate exists to allow efficient mutation of individual aggregate elements, as mutating an element on a Constant would require interning a new constant. When a write to the Constant* is made, it is converted into a MutableAggregate* as needed. I believe this should make the evaluator more robust, compatible with opaque pointers, and a bit simpler as well. Fixes llvm/llvm-project#51221. Differential Revision: https://reviews.llvm.org/D115530
1 parent e2b6e21 commit bbeaf2a

File tree

4 files changed

+158
-354
lines changed

4 files changed

+158
-354
lines changed

llvm/include/llvm/Transforms/Utils/Evaluator.h

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,49 @@ class TargetLibraryInfo;
3636
/// be iterated over after the evaluation is complete. Once an evaluation call
3737
/// fails, the evaluation object should not be reused.
3838
class Evaluator {
39+
class MutableAggregate;
40+
41+
/// The evaluator represents values either as a Constant*, or as a
42+
/// MutableAggregate, which allows changing individual aggregate elements
43+
/// without creating a new interned Constant.
44+
class MutableValue {
45+
PointerUnion<Constant *, MutableAggregate *> Val;
46+
void clear();
47+
bool makeMutable();
48+
49+
public:
50+
MutableValue(Constant *C) { Val = C; }
51+
MutableValue(const MutableValue &) = delete;
52+
MutableValue(MutableValue &&Other) {
53+
Val = Other.Val;
54+
Other.Val = nullptr;
55+
}
56+
~MutableValue() { clear(); }
57+
58+
Type *getType() const {
59+
if (auto *C = Val.dyn_cast<Constant *>())
60+
return C->getType();
61+
return Val.get<MutableAggregate *>()->Ty;
62+
}
63+
64+
Constant *toConstant() const {
65+
if (auto *C = Val.dyn_cast<Constant *>())
66+
return C;
67+
return Val.get<MutableAggregate *>()->toConstant();
68+
}
69+
70+
Constant *read(Type *Ty, APInt Offset, const DataLayout &DL) const;
71+
bool write(Constant *V, APInt Offset, const DataLayout &DL);
72+
};
73+
74+
struct MutableAggregate {
75+
Type *Ty;
76+
SmallVector<MutableValue> Elements;
77+
78+
MutableAggregate(Type *Ty) : Ty(Ty) {}
79+
Constant *toConstant() const;
80+
};
81+
3982
public:
4083
Evaluator(const DataLayout &DL, const TargetLibraryInfo *TLI)
4184
: DL(DL), TLI(TLI) {
@@ -57,8 +100,11 @@ class Evaluator {
57100
bool EvaluateFunction(Function *F, Constant *&RetVal,
58101
const SmallVectorImpl<Constant*> &ActualArgs);
59102

60-
const DenseMap<Constant *, Constant *> &getMutatedMemory() const {
61-
return MutatedMemory;
103+
DenseMap<GlobalVariable *, Constant *> getMutatedInitializers() const {
104+
DenseMap<GlobalVariable *, Constant *> Result;
105+
for (auto &Pair : MutatedMemory)
106+
Result[Pair.first] = Pair.second.toConstant();
107+
return Result;
62108
}
63109

64110
const SmallPtrSetImpl<GlobalVariable *> &getInvariants() const {
@@ -106,7 +152,7 @@ class Evaluator {
106152
/// For each store we execute, we update this map. Loads check this to get
107153
/// the most up-to-date value. If evaluation is successful, this state is
108154
/// committed to the process.
109-
DenseMap<Constant*, Constant*> MutatedMemory;
155+
DenseMap<GlobalVariable *, MutableValue> MutatedMemory;
110156

111157
/// To 'execute' an alloca, we create a temporary global variable to represent
112158
/// its body. This vector is needed so we can delete the temporary globals

llvm/lib/Transforms/IPO/GlobalOpt.cpp

Lines changed: 5 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -2066,194 +2066,6 @@ OptimizeGlobalVars(Module &M,
20662066
return Changed;
20672067
}
20682068

2069-
/// Evaluate a piece of a constantexpr store into a global initializer. This
2070-
/// returns 'Init' modified to reflect 'Val' stored into it. At this point, the
2071-
/// GEP operands of Addr [0, OpNo) have been stepped into.
2072-
static Constant *EvaluateStoreInto(Constant *Init, Constant *Val,
2073-
ConstantExpr *Addr, unsigned OpNo) {
2074-
// Base case of the recursion.
2075-
if (OpNo == Addr->getNumOperands()) {
2076-
assert(Val->getType() == Init->getType() && "Type mismatch!");
2077-
return Val;
2078-
}
2079-
2080-
SmallVector<Constant*, 32> Elts;
2081-
if (StructType *STy = dyn_cast<StructType>(Init->getType())) {
2082-
// Break up the constant into its elements.
2083-
for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i)
2084-
Elts.push_back(Init->getAggregateElement(i));
2085-
2086-
// Replace the element that we are supposed to.
2087-
ConstantInt *CU = cast<ConstantInt>(Addr->getOperand(OpNo));
2088-
unsigned Idx = CU->getZExtValue();
2089-
assert(Idx < STy->getNumElements() && "Struct index out of range!");
2090-
Elts[Idx] = EvaluateStoreInto(Elts[Idx], Val, Addr, OpNo+1);
2091-
2092-
// Return the modified struct.
2093-
return ConstantStruct::get(STy, Elts);
2094-
}
2095-
2096-
ConstantInt *CI = cast<ConstantInt>(Addr->getOperand(OpNo));
2097-
uint64_t NumElts;
2098-
if (ArrayType *ATy = dyn_cast<ArrayType>(Init->getType()))
2099-
NumElts = ATy->getNumElements();
2100-
else
2101-
NumElts = cast<FixedVectorType>(Init->getType())->getNumElements();
2102-
2103-
// Break up the array into elements.
2104-
for (uint64_t i = 0, e = NumElts; i != e; ++i)
2105-
Elts.push_back(Init->getAggregateElement(i));
2106-
2107-
assert(CI->getZExtValue() < NumElts);
2108-
Elts[CI->getZExtValue()] =
2109-
EvaluateStoreInto(Elts[CI->getZExtValue()], Val, Addr, OpNo+1);
2110-
2111-
if (Init->getType()->isArrayTy())
2112-
return ConstantArray::get(cast<ArrayType>(Init->getType()), Elts);
2113-
return ConstantVector::get(Elts);
2114-
}
2115-
2116-
/// We have decided that Addr (which satisfies the predicate
2117-
/// isSimpleEnoughPointerToCommit) should get Val as its value. Make it happen.
2118-
static void CommitValueTo(Constant *Val, Constant *Addr) {
2119-
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Addr)) {
2120-
assert(GV->hasInitializer());
2121-
GV->setInitializer(Val);
2122-
return;
2123-
}
2124-
2125-
ConstantExpr *CE = cast<ConstantExpr>(Addr);
2126-
GlobalVariable *GV = cast<GlobalVariable>(CE->getOperand(0));
2127-
GV->setInitializer(EvaluateStoreInto(GV->getInitializer(), Val, CE, 2));
2128-
}
2129-
2130-
/// Given a map of address -> value, where addresses are expected to be some form
2131-
/// of either a global or a constant GEP, set the initializer for the address to
2132-
/// be the value. This performs mostly the same function as CommitValueTo()
2133-
/// and EvaluateStoreInto() but is optimized to be more efficient for the common
2134-
/// case where the set of addresses are GEPs sharing the same underlying global,
2135-
/// processing the GEPs in batches rather than individually.
2136-
///
2137-
/// To give an example, consider the following C++ code adapted from the clang
2138-
/// regression tests:
2139-
/// struct S {
2140-
/// int n = 10;
2141-
/// int m = 2 * n;
2142-
/// S(int a) : n(a) {}
2143-
/// };
2144-
///
2145-
/// template<typename T>
2146-
/// struct U {
2147-
/// T *r = &q;
2148-
/// T q = 42;
2149-
/// U *p = this;
2150-
/// };
2151-
///
2152-
/// U<S> e;
2153-
///
2154-
/// The global static constructor for 'e' will need to initialize 'r' and 'p' of
2155-
/// the outer struct, while also initializing the inner 'q' structs 'n' and 'm'
2156-
/// members. This batch algorithm will simply use general CommitValueTo() method
2157-
/// to handle the complex nested S struct initialization of 'q', before
2158-
/// processing the outermost members in a single batch. Using CommitValueTo() to
2159-
/// handle member in the outer struct is inefficient when the struct/array is
2160-
/// very large as we end up creating and destroy constant arrays for each
2161-
/// initialization.
2162-
/// For the above case, we expect the following IR to be generated:
2163-
///
2164-
/// %struct.U = type { %struct.S*, %struct.S, %struct.U* }
2165-
/// %struct.S = type { i32, i32 }
2166-
/// @e = global %struct.U { %struct.S* gep inbounds (%struct.U, %struct.U* @e,
2167-
/// i64 0, i32 1),
2168-
/// %struct.S { i32 42, i32 84 }, %struct.U* @e }
2169-
/// The %struct.S { i32 42, i32 84 } inner initializer is treated as a complex
2170-
/// constant expression, while the other two elements of @e are "simple".
2171-
static void BatchCommitValueTo(const DenseMap<Constant*, Constant*> &Mem) {
2172-
SmallVector<std::pair<GlobalVariable*, Constant*>, 32> GVs;
2173-
SmallVector<std::pair<ConstantExpr*, Constant*>, 32> ComplexCEs;
2174-
SmallVector<std::pair<ConstantExpr*, Constant*>, 32> SimpleCEs;
2175-
SimpleCEs.reserve(Mem.size());
2176-
2177-
for (const auto &I : Mem) {
2178-
if (auto *GV = dyn_cast<GlobalVariable>(I.first)) {
2179-
GVs.push_back(std::make_pair(GV, I.second));
2180-
} else {
2181-
ConstantExpr *GEP = cast<ConstantExpr>(I.first);
2182-
// We don't handle the deeply recursive case using the batch method.
2183-
if (GEP->getNumOperands() > 3)
2184-
ComplexCEs.push_back(std::make_pair(GEP, I.second));
2185-
else
2186-
SimpleCEs.push_back(std::make_pair(GEP, I.second));
2187-
}
2188-
}
2189-
2190-
// The algorithm below doesn't handle cases like nested structs, so use the
2191-
// slower fully general method if we have to.
2192-
for (auto ComplexCE : ComplexCEs)
2193-
CommitValueTo(ComplexCE.second, ComplexCE.first);
2194-
2195-
for (auto GVPair : GVs) {
2196-
assert(GVPair.first->hasInitializer());
2197-
GVPair.first->setInitializer(GVPair.second);
2198-
}
2199-
2200-
if (SimpleCEs.empty())
2201-
return;
2202-
2203-
// We cache a single global's initializer elements in the case where the
2204-
// subsequent address/val pair uses the same one. This avoids throwing away and
2205-
// rebuilding the constant struct/vector/array just because one element is
2206-
// modified at a time.
2207-
SmallVector<Constant *, 32> Elts;
2208-
Elts.reserve(SimpleCEs.size());
2209-
GlobalVariable *CurrentGV = nullptr;
2210-
2211-
auto commitAndSetupCache = [&](GlobalVariable *GV, bool Update) {
2212-
Constant *Init = GV->getInitializer();
2213-
Type *Ty = Init->getType();
2214-
if (Update) {
2215-
if (CurrentGV) {
2216-
assert(CurrentGV && "Expected a GV to commit to!");
2217-
Type *CurrentInitTy = CurrentGV->getInitializer()->getType();
2218-
// We have a valid cache that needs to be committed.
2219-
if (StructType *STy = dyn_cast<StructType>(CurrentInitTy))
2220-
CurrentGV->setInitializer(ConstantStruct::get(STy, Elts));
2221-
else if (ArrayType *ArrTy = dyn_cast<ArrayType>(CurrentInitTy))
2222-
CurrentGV->setInitializer(ConstantArray::get(ArrTy, Elts));
2223-
else
2224-
CurrentGV->setInitializer(ConstantVector::get(Elts));
2225-
}
2226-
if (CurrentGV == GV)
2227-
return;
2228-
// Need to clear and set up cache for new initializer.
2229-
CurrentGV = GV;
2230-
Elts.clear();
2231-
unsigned NumElts;
2232-
if (auto *STy = dyn_cast<StructType>(Ty))
2233-
NumElts = STy->getNumElements();
2234-
else if (auto *ATy = dyn_cast<ArrayType>(Ty))
2235-
NumElts = ATy->getNumElements();
2236-
else
2237-
NumElts = cast<FixedVectorType>(Ty)->getNumElements();
2238-
for (unsigned i = 0, e = NumElts; i != e; ++i)
2239-
Elts.push_back(Init->getAggregateElement(i));
2240-
}
2241-
};
2242-
2243-
for (auto CEPair : SimpleCEs) {
2244-
ConstantExpr *GEP = CEPair.first;
2245-
Constant *Val = CEPair.second;
2246-
2247-
GlobalVariable *GV = cast<GlobalVariable>(GEP->getOperand(0));
2248-
commitAndSetupCache(GV, GV != CurrentGV);
2249-
ConstantInt *CI = cast<ConstantInt>(GEP->getOperand(2));
2250-
Elts[CI->getZExtValue()] = Val;
2251-
}
2252-
// The last initializer in the list needs to be committed, others
2253-
// will be committed on a new initializer being processed.
2254-
commitAndSetupCache(CurrentGV, true);
2255-
}
2256-
22572069
/// Evaluate static constructors in the function, if we can. Return true if we
22582070
/// can, false otherwise.
22592071
static bool EvaluateStaticConstructor(Function *F, const DataLayout &DL,
@@ -2268,10 +2080,12 @@ static bool EvaluateStaticConstructor(Function *F, const DataLayout &DL,
22682080
++NumCtorsEvaluated;
22692081

22702082
// We succeeded at evaluation: commit the result.
2083+
auto NewInitializers = Eval.getMutatedInitializers();
22712084
LLVM_DEBUG(dbgs() << "FULLY EVALUATED GLOBAL CTOR FUNCTION '"
2272-
<< F->getName() << "' to "
2273-
<< Eval.getMutatedMemory().size() << " stores.\n");
2274-
BatchCommitValueTo(Eval.getMutatedMemory());
2085+
<< F->getName() << "' to " << NewInitializers.size()
2086+
<< " stores.\n");
2087+
for (const auto &Pair : NewInitializers)
2088+
Pair.first->setInitializer(Pair.second);
22752089
for (GlobalVariable *GV : Eval.getInvariants())
22762090
GV->setConstant(true);
22772091
}

0 commit comments

Comments
 (0)