Skip to content

Commit 36cdd57

Browse files
committed
Optimizer/IRGen: allow enums in static initializers of globals
The main change here is in IRGen which needs to be able to emit constant enum values. Use `emitValueInjection` to create the enum constant. Usually this method creates code in the current function. But if all arguments to the enum are constant, the builder never has to emit an instruction. Instead it can constant fold everything and just returns the final constant. Also, create statically initialized let-globals as constant global (`constant` instead of `global`).
1 parent 8a0c287 commit 36cdd57

File tree

11 files changed

+801
-125
lines changed

11 files changed

+801
-125
lines changed

SwiftCompilerSources/Sources/SIL/GlobalVariable.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ extension Instruction {
123123
return !fri.referencedFunction.isAsync
124124
case is StructInst,
125125
is TupleInst,
126+
is EnumInst,
126127
is IntegerLiteralInst,
127128
is FloatLiteralInst,
128129
is ObjectInst,

lib/IRGen/Explosion.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ class Explosion {
6060
return *this;
6161
}
6262

63+
Explosion(llvm::Value *singleValue) : NextValue(0) {
64+
add(singleValue);
65+
}
66+
6367
~Explosion() {
6468
assert(empty() && "explosion had values remaining when destroyed!");
6569
}
@@ -133,6 +137,10 @@ class Explosion {
133137
return Values[NextValue++];
134138
}
135139

140+
llvm::Constant *claimNextConstant() {
141+
return cast<llvm::Constant>(claimNext());
142+
}
143+
136144
/// Claim and return the next N values in this explosion.
137145
ArrayRef<llvm::Value*> claim(unsigned n) {
138146
assert(NextValue + n <= Values.size());

lib/IRGen/GenConstant.cpp

Lines changed: 133 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616

1717
#include "llvm/IR/Constants.h"
1818

19+
#include "BitPatternReader.h"
20+
#include "Explosion.h"
1921
#include "GenConstant.h"
22+
#include "GenEnum.h"
2023
#include "GenIntegerLiteral.h"
2124
#include "GenStruct.h"
2225
#include "GenTuple.h"
@@ -115,43 +118,83 @@ llvm::Constant *irgen::emitAddrOfConstantString(IRGenModule &IGM,
115118
namespace {
116119

117120
/// Fill in the missing values for padding.
118-
void insertPadding(SmallVectorImpl<llvm::Constant *> &Elements,
121+
void insertPadding(SmallVectorImpl<Explosion> &elements,
119122
llvm::StructType *sTy) {
120123
// fill in any gaps, which are the explicit padding that swiftc inserts.
121-
for (unsigned i = 0, e = Elements.size(); i != e; ++i) {
122-
auto &elt = Elements[i];
123-
if (elt == nullptr) {
124+
for (unsigned i = 0, e = elements.size(); i != e; ++i) {
125+
if (elements[i].empty()) {
124126
auto *eltTy = sTy->getElementType(i);
125127
assert(eltTy->isArrayTy() &&
126128
eltTy->getArrayElementType()->isIntegerTy(8) &&
127129
"Unexpected non-byte-array type for constant struct padding");
128-
elt = llvm::UndefValue::get(eltTy);
130+
elements[i].add(llvm::UndefValue::get(eltTy));
129131
}
130132
}
131133
}
132134

135+
/// Creates a struct which contains all values of `explosions`.
136+
///
137+
/// If all explosions have a single element and those elements match the
138+
/// elements of `structTy`, it uses this type as result type.
139+
/// Otherwise, it creates an anonymous struct. This can be the case for enums.
140+
llvm::Constant *createStructFromExplosion(SmallVectorImpl<Explosion> &explosions,
141+
llvm::StructType *structTy) {
142+
assert(explosions.size() == structTy->getNumElements());
143+
bool canUseStructType = true;
144+
llvm::SmallVector<llvm::Constant *, 32> values;
145+
unsigned idx = 0;
146+
for (auto &elmt : explosions) {
147+
if (elmt.size() != 1)
148+
canUseStructType = false;
149+
for (llvm::Value *v : elmt.claimAll()) {
150+
if (v->getType() != structTy->getElementType(idx))
151+
canUseStructType = false;
152+
values.push_back(cast<llvm::Constant>(v));
153+
}
154+
idx++;
155+
}
156+
if (canUseStructType) {
157+
return llvm::ConstantStruct::get(structTy, values);
158+
} else {
159+
return llvm::ConstantStruct::getAnon(values, /*Packed=*/ true);
160+
}
161+
}
162+
163+
void initWithEmptyExplosions(SmallVectorImpl<Explosion> &explosions,
164+
unsigned count) {
165+
for (unsigned i = 0; i < count; i++) {
166+
explosions.push_back(Explosion());
167+
}
168+
}
169+
133170
template <typename InstTy, typename NextIndexFunc>
134-
llvm::Constant *emitConstantStructOrTuple(IRGenModule &IGM, InstTy inst,
135-
NextIndexFunc nextIndex) {
171+
Explosion emitConstantStructOrTuple(IRGenModule &IGM, InstTy inst,
172+
NextIndexFunc nextIndex, bool flatten) {
136173
auto type = inst->getType();
137174
auto *sTy = cast<llvm::StructType>(IGM.getTypeInfo(type).getStorageType());
138175

139-
SmallVector<llvm::Constant *, 32> elts(sTy->getNumElements(), nullptr);
176+
SmallVector<Explosion, 32> elements;
177+
initWithEmptyExplosions(elements, sTy->getNumElements());
140178

141-
// run over the Swift initializers, putting them into the struct as
142-
// appropriate.
143179
for (unsigned i = 0, e = inst->getElements().size(); i != e; ++i) {
144180
auto operand = inst->getOperand(i);
145181
Optional<unsigned> index = nextIndex(IGM, type, i);
146182
if (index.has_value()) {
147-
assert(elts[index.value()] == nullptr &&
183+
unsigned idx = index.value();
184+
assert(elements[idx].empty() &&
148185
"Unexpected constant struct field overlap");
149-
150-
elts[index.value()] = emitConstantValue(IGM, operand);
186+
elements[idx] = emitConstantValue(IGM, operand, flatten);
187+
}
188+
}
189+
if (flatten) {
190+
Explosion out;
191+
for (auto &elmt : elements) {
192+
out.add(elmt.claimAll());
151193
}
194+
return out;
152195
}
153-
insertPadding(elts, sTy);
154-
return llvm::ConstantStruct::get(sTy, elts);
196+
insertPadding(elements, sTy);
197+
return createStructFromExplosion(elements, sTy);
155198
}
156199
} // end anonymous namespace
157200

@@ -181,7 +224,36 @@ static BuiltinInst *getOffsetSubtract(const TupleExtractInst *TE, SILModule &M)
181224
return BI;
182225
}
183226

184-
llvm::Constant *irgen::emitConstantValue(IRGenModule &IGM, SILValue operand) {
227+
static bool isPowerOfTwo(unsigned x) {
228+
return (x & -x) == x;
229+
}
230+
231+
/// Replace i24, i40, i48 and i56 constants in `e` with the corresponding byte values.
232+
/// Such unaligned integer constants are not correctly layed out in the data section.
233+
static Explosion replaceUnalignedIntegerValues(IRGenModule &IGM, Explosion e) {
234+
Explosion out;
235+
while (!e.empty()) {
236+
llvm::Value *v = e.claimNext();
237+
238+
if (auto *constInt = dyn_cast<llvm::ConstantInt>(v)) {
239+
unsigned size = constInt->getBitWidth();
240+
if (size % 8 == 0 && !isPowerOfTwo(size)) {
241+
BitPatternReader reader(constInt->getValue(), IGM.Triple.isLittleEndian());
242+
while (size > 0) {
243+
APInt byte = reader.read(8);
244+
out.add(llvm::ConstantInt::get(IGM.getLLVMContext(), byte));
245+
size -= 8;
246+
}
247+
continue;
248+
}
249+
}
250+
out.add(v);
251+
}
252+
return out;
253+
}
254+
255+
Explosion irgen::emitConstantValue(IRGenModule &IGM, SILValue operand,
256+
bool flatten) {
185257
if (auto *SI = dyn_cast<StructInst>(operand)) {
186258
// The only way to get a struct's stored properties (which we need to map to
187259
// their physical/LLVM index) is to iterate over the properties
@@ -195,64 +267,83 @@ llvm::Constant *irgen::emitConstantValue(IRGenModule &IGM, SILValue operand) {
195267
(void)_i;
196268
auto *FD = *Iter++;
197269
return irgen::getPhysicalStructFieldIndex(IGM, Type, FD);
198-
});
270+
}, flatten);
199271
} else if (auto *TI = dyn_cast<TupleInst>(operand)) {
200272
return emitConstantStructOrTuple(IGM, TI,
201-
irgen::getPhysicalTupleElementStructIndex);
273+
irgen::getPhysicalTupleElementStructIndex,
274+
flatten);
275+
} else if (auto *ei = dyn_cast<EnumInst>(operand)) {
276+
Explosion data;
277+
if (ei->hasOperand()) {
278+
data = emitConstantValue(IGM, ei->getOperand(), /*flatten=*/ true);
279+
}
280+
// Use `emitValueInjection` to create the enum constant.
281+
// Usually this method creates code in the current function. But if all
282+
// arguments to the enum are constant, the builder never has to emit an
283+
// instruction. Instead it can constant fold everything and just returns
284+
// the final constant.
285+
Explosion out;
286+
IRBuilder builder(IGM.getLLVMContext(), false);
287+
getEnumImplStrategy(IGM, ei->getType())
288+
.emitValueInjection(IGM, builder, ei->getElement(), data, out);
289+
return replaceUnalignedIntegerValues(IGM, std::move(out));
202290
} else if (auto *ILI = dyn_cast<IntegerLiteralInst>(operand)) {
203291
return emitConstantInt(IGM, ILI);
204292
} else if (auto *FLI = dyn_cast<FloatLiteralInst>(operand)) {
205293
return emitConstantFP(IGM, FLI);
206294
} else if (auto *SLI = dyn_cast<StringLiteralInst>(operand)) {
207295
return emitAddrOfConstantString(IGM, SLI);
208296
} else if (auto *BI = dyn_cast<BuiltinInst>(operand)) {
297+
auto args = BI->getArguments();
209298
switch (IGM.getSILModule().getBuiltinInfo(BI->getName()).ID) {
210299
case BuiltinValueKind::ZeroInitializer:
211300
return emitConstantZero(IGM, BI);
212301
case BuiltinValueKind::PtrToInt: {
213-
llvm::Constant *ptr = emitConstantValue(IGM, BI->getArguments()[0]);
302+
auto *ptr = emitConstantValue(IGM, args[0]).claimNextConstant();
214303
return llvm::ConstantExpr::getPtrToInt(ptr, IGM.IntPtrTy);
215304
}
216305
case BuiltinValueKind::ZExtOrBitCast: {
217-
llvm::Constant *value = emitConstantValue(IGM, BI->getArguments()[0]);
218-
return llvm::ConstantExpr::getZExtOrBitCast(value, IGM.getStorageType(BI->getType()));
306+
auto *val = emitConstantValue(IGM, args[0]).claimNextConstant();
307+
return llvm::ConstantExpr::getZExtOrBitCast(val,
308+
IGM.getStorageType(BI->getType()));
219309
}
220310
case BuiltinValueKind::StringObjectOr: {
221311
// It is a requirement that the or'd bits in the left argument are
222312
// initialized with 0. Therefore the or-operation is equivalent to an
223313
// addition. We need an addition to generate a valid relocation.
224-
llvm::Constant *rhs = emitConstantValue(IGM, BI->getArguments()[1]);
225-
if (auto *TE = dyn_cast<TupleExtractInst>(BI->getArguments()[0])) {
314+
auto *rhs = emitConstantValue(IGM, args[1]).claimNextConstant();
315+
if (auto *TE = dyn_cast<TupleExtractInst>(args[0])) {
226316
// Handle StringObjectOr(tuple_extract(usub_with_overflow(x, offset)), bits)
227317
// This pattern appears in UTF8 String literal construction.
228318
// Generate the equivalent: add(x, sub(bits - offset)
229319
BuiltinInst *SubtrBI = getOffsetSubtract(TE, IGM.getSILModule());
230320
assert(SubtrBI && "unsupported argument of StringObjectOr");
231-
auto *ptr = emitConstantValue(IGM, SubtrBI->getArguments()[0]);
232-
auto *offset = emitConstantValue(IGM, SubtrBI->getArguments()[1]);
321+
auto subArgs = SubtrBI->getArguments();
322+
auto *ptr = emitConstantValue(IGM, subArgs[0]).claimNextConstant();
323+
auto *offset = emitConstantValue(IGM, subArgs[1]).claimNextConstant();
233324
auto *totalOffset = llvm::ConstantExpr::getSub(rhs, offset);
234325
return llvm::ConstantExpr::getAdd(ptr, totalOffset);
235326
}
236-
llvm::Constant *lhs = emitConstantValue(IGM, BI->getArguments()[0]);
327+
auto *lhs = emitConstantValue(IGM, args[0]).claimNextConstant();
237328
return llvm::ConstantExpr::getAdd(lhs, rhs);
238329
}
239330
default:
240331
llvm_unreachable("unsupported builtin for constant expression");
241332
}
242333
} else if (auto *VTBI = dyn_cast<ValueToBridgeObjectInst>(operand)) {
243-
auto *val = emitConstantValue(IGM, VTBI->getOperand());
334+
auto *val = emitConstantValue(IGM, VTBI->getOperand()).claimNextConstant();
244335
auto *sTy = IGM.getTypeInfo(VTBI->getType()).getStorageType();
245336
return llvm::ConstantExpr::getIntToPtr(val, sTy);
246337

247338
} else if (auto *CFI = dyn_cast<ConvertFunctionInst>(operand)) {
248-
return emitConstantValue(IGM, CFI->getOperand());
339+
return emitConstantValue(IGM, CFI->getOperand()).claimNextConstant();
249340

250341
} else if (auto *T2TFI = dyn_cast<ThinToThickFunctionInst>(operand)) {
251342
SILType type = operand->getType();
252343
auto *sTy = cast<llvm::StructType>(IGM.getTypeInfo(type).getStorageType());
253344

254345
auto *function = llvm::ConstantExpr::getBitCast(
255-
emitConstantValue(IGM, T2TFI->getCallee()),
346+
emitConstantValue(IGM, T2TFI->getCallee()).claimNextConstant(),
256347
sTy->getTypeAtIndex((unsigned)0));
257348

258349
auto *context = llvm::ConstantExpr::getBitCast(
@@ -287,7 +378,9 @@ llvm::Constant *irgen::emitConstantValue(IRGenModule &IGM, SILValue operand) {
287378
llvm::Constant *irgen::emitConstantObject(IRGenModule &IGM, ObjectInst *OI,
288379
StructLayout *ClassLayout) {
289380
auto *sTy = cast<llvm::StructType>(ClassLayout->getType());
290-
SmallVector<llvm::Constant *, 32> elts(sTy->getNumElements(), nullptr);
381+
382+
SmallVector<Explosion, 32> elements;
383+
initWithEmptyExplosions(elements, sTy->getNumElements());
291384

292385
unsigned NumElems = OI->getAllElements().size();
293386
assert(NumElems == ClassLayout->getElements().size());
@@ -297,9 +390,11 @@ llvm::Constant *irgen::emitConstantObject(IRGenModule &IGM, ObjectInst *OI,
297390
SILValue Val = OI->getAllElements()[i];
298391
const ElementLayout &EL = ClassLayout->getElements()[i];
299392
if (!EL.isEmpty()) {
300-
unsigned EltIdx = EL.getStructIndex();
301-
assert(EltIdx != 0 && "the first element is the object header");
302-
elts[EltIdx] = emitConstantValue(IGM, Val);
393+
unsigned idx = EL.getStructIndex();
394+
assert(idx != 0 && "the first element is the object header");
395+
assert(elements[idx].empty() &&
396+
"Unexpected constant struct field overlap");
397+
elements[idx] = emitConstantValue(IGM, Val);
303398
}
304399
}
305400
// Construct the object header.
@@ -322,14 +417,14 @@ llvm::Constant *irgen::emitConstantObject(IRGenModule &IGM, ObjectInst *OI,
322417
/*initializer*/ nullptr, "$ss19__EmptyArrayStorageCN");
323418
IGM.swiftStaticArrayMetadata = var;
324419
}
325-
elts[0] = llvm::ConstantStruct::get(ObjectHeaderTy, {
420+
elements[0].add(llvm::ConstantStruct::get(ObjectHeaderTy, {
326421
IGM.swiftStaticArrayMetadata,
327-
llvm::ConstantExpr::getPtrToInt(IGM.swiftImmortalRefCount, IGM.IntPtrTy)});
422+
llvm::ConstantExpr::getPtrToInt(IGM.swiftImmortalRefCount, IGM.IntPtrTy)}));
328423
} else {
329-
elts[0] = llvm::Constant::getNullValue(ObjectHeaderTy);
424+
elements[0].add(llvm::Constant::getNullValue(ObjectHeaderTy));
330425
}
331-
insertPadding(elts, sTy);
332-
return llvm::ConstantStruct::get(sTy, elts);
426+
insertPadding(elements, sTy);
427+
return createStructFromExplosion(elements, sTy);
333428
}
334429

335430
void ConstantAggregateBuilderBase::addUniqueHash(StringRef data) {

lib/IRGen/GenConstant.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ llvm::Constant *emitAddrOfConstantString(IRGenModule &IGM,
3838
StringLiteralInst *SLI);
3939

4040
/// Construct a constant from a SILValue containing constant values.
41-
llvm::Constant *emitConstantValue(IRGenModule &IGM, SILValue value);
41+
Explosion emitConstantValue(IRGenModule &IGM, SILValue value,
42+
bool flatten = false);
4243

4344
/// Construct an object (with a HeapObject header) from an ObjectInst
4445
/// containing constant values.

0 commit comments

Comments
 (0)