Skip to content

Commit 38b34b7

Browse files
committed
Pass #1 at localizing assumptions about fixed layout and
handling non-fixed layouts. This uncovered a bug where we weren't rounding up the header size to the element alignment when allocating an array of archetypes. Writing up a detailed test case for *that* revealed that we were never initializing the length field of heap arrays. Fixing that caused a bunch of tests to crash trying to release stuff. So... I've left this in a workaround state right now because I have to catch a plane. Swift SVN r4804
1 parent 4d617d8 commit 38b34b7

20 files changed

+658
-232
lines changed

lib/IRGen/FixedTypeInfo.h

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,23 @@ namespace irgen {
2727
/// FixedTypeInfo - An abstract class designed for use when
2828
/// implementing a type that has a statically known layout.
2929
class FixedTypeInfo : public TypeInfo {
30+
private:
31+
/// The storage size of this type in bytes. This may be zero even
32+
/// for well-formed and complete types, such as a trivial oneof or
33+
/// tuple.
34+
Size StorageSize;
35+
3036
protected:
3137
FixedTypeInfo(llvm::Type *type, Size size, Alignment align, IsPOD_t pod)
32-
: TypeInfo(type, size, align, pod) {}
38+
: TypeInfo(type, align, pod, IsFixedSize), StorageSize(size) {}
3339

3440
public:
41+
// This is useful for metaprogramming.
42+
static bool isFixed() { return true; }
43+
44+
/// Whether this type is known to be empty.
45+
bool isKnownEmpty() const { return StorageSize.isZero(); }
46+
3547
OwnedAddress allocate(IRGenFunction &IGF, Initialization &init,
3648
InitializedObject object,
3749
OnHeap_t onHeap,
@@ -55,7 +67,30 @@ class FixedTypeInfo : public TypeInfo {
5567
llvm::Constant *getStaticAlignment(IRGenModule &IGM) const;
5668
llvm::Constant *getStaticStride(IRGenModule &IGM) const;
5769

58-
// TODO: move the StorageSize etc. members here.
70+
void completeFixed(Size size, Alignment alignment) {
71+
StorageSize = size;
72+
setStorageAlignment(alignment);
73+
}
74+
75+
/// Returns the known, fixed size required to store a value of this type.
76+
Alignment getFixedAlignment() const {
77+
return getBestKnownAlignment();
78+
}
79+
80+
/// Returns the known, fixed alignment of a stored value of this type.
81+
Size getFixedSize() const {
82+
return StorageSize;
83+
}
84+
85+
/// Returns the (assumed fixed) stride of the storage for this
86+
/// object. The stride is the storage size rounded up to the
87+
/// alignment; its practical use is that, in an array, it is the
88+
/// offset from the size of one element to the offset of the next.
89+
Size getFixedStride() const {
90+
return StorageSize.roundUpToAlignment(getFixedAlignment());
91+
}
92+
93+
static bool classof(const TypeInfo *type) { return type->isFixedSize(); }
5994
};
6095

6196
}

lib/IRGen/GenHeap.cpp

Lines changed: 124 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@ static llvm::ConstantInt *getMetadataKind(IRGenModule &IGM,
4343
return llvm::ConstantInt::get(IGM.MetadataKindTy, uint8_t(kind));
4444
}
4545

46-
static llvm::ConstantInt *getSize(IRGenFunction &IGF, Size value) {
47-
return llvm::ConstantInt::get(IGF.IGM.SizeTy, value.getValue());
48-
}
49-
5046
static llvm::ConstantInt *getSize(IRGenFunction &IGF,
5147
const llvm::APInt &value) {
5248
return cast<llvm::ConstantInt>(llvm::ConstantInt::get(IGF.IGM.SizeTy, value));
@@ -169,35 +165,69 @@ static void bindNecessaryBindings(IRGenFunction &IGF,
169165
bindings.restore(IGF, bindingsBuffer);
170166
}
171167

172-
/// Lay out an array on the heap.
173-
ArrayHeapLayout::ArrayHeapLayout(IRGenFunction &IGF, CanType T)
174-
: ElementTI(IGF.getFragileTypeInfo(T)), Bindings(IGF.IGM, T) {
168+
/// Compute the basic information for how to lay out a heap array.
169+
HeapArrayInfo::HeapArrayInfo(IRGenFunction &IGF, CanType T)
170+
: ElementTI(IGF.getFragileTypeInfo(T)), Bindings(IGF.IGM, T) {}
175171

176-
// Add the heap header.
177-
Size size(0);
178-
Alignment align(1);
172+
/// Lay out the allocation in this IGF.
173+
HeapArrayInfo::Layout HeapArrayInfo::getLayout(IRGenFunction &IGF) const {
174+
// Start with the heap header.
175+
Size headerSize(0);
176+
Alignment headerAlign(1);
179177
SmallVector<llvm::Type*, 4> fields;
180-
addHeapHeaderToLayout(IGF.IGM, size, align, fields);
181-
assert((size % align).isZero());
182-
assert(align >= IGF.IGM.getPointerAlignment());
178+
addHeapHeaderToLayout(IGF.IGM, headerSize, headerAlign, fields);
179+
assert((headerSize % headerAlign).isZero());
180+
assert(headerAlign >= IGF.IGM.getPointerAlignment());
183181

184182
// Add the length field.
185-
size += IGF.IGM.getPointerSize();
186-
assert(size == getArrayHeapHeaderSize(IGF.IGM));
183+
headerSize += IGF.IGM.getPointerSize();
184+
assert(headerSize == getArrayHeapHeaderSize(IGF.IGM));
187185

188186
// Add the necessary bindings size.
189-
size += Bindings.getBufferSize(IGF.IGM);
187+
headerSize += Bindings.getBufferSize(IGF.IGM);
188+
189+
// The easy case is when we know the layout of the element.
190+
if (auto fixedElementTI = dyn_cast<FixedTypeInfo>(&ElementTI)) {
191+
// Update the required alignment.
192+
if (fixedElementTI->getFixedAlignment() > headerAlign)
193+
headerAlign = fixedElementTI->getFixedAlignment();
194+
195+
// Round the size up to the alignment of the element type.
196+
// FIXME: resilient types.
197+
headerSize = headerSize.roundUpToAlignment(
198+
fixedElementTI->getFixedAlignment());
199+
200+
return {
201+
IGF.IGM.getSize(headerSize),
202+
IGF.IGM.getSize(headerAlign.asSize()),
203+
headerAlign
204+
};
205+
}
206+
207+
// Otherwise, we need to do this computation at runtime.
208+
209+
// Read the alignment of the element type.
210+
llvm::Value *eltAlign = ElementTI.getAlignment(IGF);
211+
212+
// Round the header size up to the element alignment.
213+
llvm::Value *headerSizeV = IGF.IGM.getSize(headerSize);
190214

191-
// Update the required alignment.
192-
if (ElementTI.getFixedAlignment() > align)
193-
align = ElementTI.getFixedAlignment();
215+
// mask = alignment - 1
216+
// headerSize = (headerSize + mask) & ~mask
217+
auto eltAlignMask = IGF.Builder.CreateSub(eltAlign, IGF.IGM.getSize(Size(1)));
218+
headerSizeV = IGF.Builder.CreateAdd(headerSizeV, eltAlignMask);
219+
llvm::Value *eltAlignMaskInverted = IGF.Builder.CreateNot(eltAlignMask);
220+
headerSizeV = IGF.Builder.CreateAnd(headerSizeV, eltAlignMaskInverted,
221+
"array-header-size");
194222

195-
// Round the size up to the alignment of the element type.
196-
// FIXME: resilient types.
197-
size = size.roundUpToAlignment(ElementTI.getFixedAlignment());
223+
// allocAlign = max(headerAlign, alignment)
224+
llvm::Value *headerAlignV = IGF.IGM.getSize(headerAlign.asSize());
225+
llvm::Value *overaligned =
226+
IGF.Builder.CreateICmpUGT(eltAlign, headerAlignV, "overaligned");
227+
llvm::Value *allocAlign =
228+
IGF.Builder.CreateSelect(overaligned, eltAlign, headerAlignV);
198229

199-
HeaderSize = size;
200-
Align = align;
230+
return { headerSizeV, allocAlign, headerAlign };
201231
}
202232

203233
/// Destroy all the elements of an array.
@@ -252,7 +282,7 @@ static void emitArrayDestroy(IRGenFunction &IGF,
252282
/// TODO: give this some reasonable name and possibly linkage.
253283
static llvm::Constant *
254284
createArrayDtorFn(IRGenModule &IGM,
255-
const ArrayHeapLayout &layout,
285+
const HeapArrayInfo &arrayInfo,
256286
const NecessaryBindings &bindings) {
257287
llvm::Function *fn =
258288
llvm::Function::Create(IGM.DeallocatingDtorTy,
@@ -262,20 +292,26 @@ createArrayDtorFn(IRGenModule &IGM,
262292
IRGenFunction IGF(IGM, CanType(), llvm::ArrayRef<Pattern*>(),
263293
ExplosionKind::Minimal, 0, fn, Prologue::Bare);
264294

295+
// Bind the necessary archetypes. This is required before we can
296+
// lay out the array in this IGF.
265297
llvm::Value *header = fn->arg_begin();
266-
Address lengthPtr = layout.getLengthPointer(IGF, header);
298+
bindNecessaryBindings(IGF, bindings,
299+
Address(header, IGM.getPointerAlignment()));
300+
301+
auto layout = arrayInfo.getLayout(IGF);
302+
303+
Address lengthPtr = arrayInfo.getLengthPointer(IGF, layout, header);
267304
llvm::Value *length = IGF.Builder.CreateLoad(lengthPtr, "length");
268305

269-
// Bind the necessary archetypes.
270-
bindNecessaryBindings(IGF, bindings, Address(header, layout.getAlignment()));
306+
auto &eltTI = arrayInfo.getElementTypeInfo();
271307

272308
// If the layout isn't known to be POD, we actually have to do work here.
273-
if (!layout.getElementTypeInfo().isPOD(ResilienceScope::Local)) {
274-
llvm::Value *elementSize = layout.getElementTypeInfo().getStride(IGF);
309+
if (!eltTI.isPOD(ResilienceScope::Local)) {
310+
llvm::Value *elementSize = eltTI.getStride(IGF);
275311

276-
llvm::Value *begin = layout.getBeginPointer(IGF, header);
312+
llvm::Value *begin = arrayInfo.getBeginPointer(IGF, layout, header);
277313
llvm::Value *end;
278-
if (layout.getElementTypeInfo().StorageType->isSized()) {
314+
if (isa<FixedTypeInfo>(eltTI)) {
279315
end = IGF.Builder.CreateInBoundsGEP(begin, length, "end");
280316
} else {
281317
end = IGF.Builder.CreateBitCast(begin, IGF.IGM.Int8PtrTy);
@@ -284,16 +320,17 @@ createArrayDtorFn(IRGenModule &IGM,
284320
end = IGF.Builder.CreateBitCast(end, begin->getType());
285321
}
286322

287-
emitArrayDestroy(IGF, begin, end, layout.getElementTypeInfo(), elementSize);
323+
emitArrayDestroy(IGF, begin, end, eltTI, elementSize);
288324
}
289325

290-
llvm::Value *size = layout.getAllocationSize(IGF, length, false, false);
326+
llvm::Value *size =
327+
arrayInfo.getAllocationSize(IGF, layout, length, false, false);
291328
IGF.Builder.CreateRet(size);
292329

293330
return fn;
294331
}
295332

296-
llvm::Constant *ArrayHeapLayout::getPrivateMetadata(IRGenModule &IGM) const {
333+
llvm::Constant *HeapArrayInfo::getPrivateMetadata(IRGenModule &IGM) const {
297334
return buildPrivateMetadata(IGM, createArrayDtorFn(IGM, *this, Bindings),
298335
MetadataKind::HeapArray);
299336
}
@@ -327,15 +364,17 @@ static llvm::Value *checkOverflow(IRGenFunction &IGF,
327364
/// this is false for computations involving a known-good length
328365
/// \param updateLength - whether to update the 'length' parameter
329366
/// with the proper length, i.e. the length as a size_t
330-
llvm::Value *ArrayHeapLayout::getAllocationSize(IRGenFunction &IGF,
331-
llvm::Value *&length,
332-
bool canOverflow,
333-
bool updateLength) const {
367+
llvm::Value *HeapArrayInfo::getAllocationSize(IRGenFunction &IGF,
368+
const Layout &layout,
369+
llvm::Value *&length,
370+
bool canOverflow,
371+
bool updateLength) const {
334372
// We're computing HeaderSize + length * sizeof(element).
335373

336374
// Easy case: the length is a static constant.
337375
llvm::ConstantInt *clength = dyn_cast<llvm::ConstantInt>(length);
338-
if (clength && ElementTI.StorageType->isSized()) {
376+
if (clength && ElementTI.isFixedSize()) {
377+
auto &fixedElementTI = cast<FixedTypeInfo>(ElementTI);
339378
unsigned sizeWidth = IGF.IGM.SizeTy->getBitWidth();
340379

341380
// Get the length to size_t, making sure it isn't too large.
@@ -352,13 +391,16 @@ llvm::Value *ArrayHeapLayout::getAllocationSize(IRGenFunction &IGF,
352391
bool overflow = false;
353392

354393
// Scale the length by the element stride.
355-
llvm::APInt elementStride(sizeWidth, ElementTI.getFixedStride().getValue());
394+
llvm::APInt elementStride(sizeWidth,
395+
fixedElementTI.getFixedStride().getValue());
356396
assert(elementStride);
357397
auto scaledLength = lenval.umul_ov(elementStride, overflow);
358398
if (overflow) return getSizeMax(IGF);
359399

360400
// Add the header size in.
361-
llvm::APInt headerSize(sizeWidth, HeaderSize.getValue());
401+
assert(isa<llvm::ConstantInt>(layout.HeaderSize) &&
402+
"fixed-size array element type without constant header size?");
403+
auto &headerSize = cast<llvm::ConstantInt>(layout.HeaderSize)->getValue();
362404
auto lengthWithHeader = scaledLength.uadd_ov(headerSize, overflow);
363405
if (overflow) return getSizeMax(IGF);
364406

@@ -396,9 +438,8 @@ llvm::Value *ArrayHeapLayout::getAllocationSize(IRGenFunction &IGF,
396438

397439
// If the element size is known to be zero, we don't need to do
398440
// anything further.
399-
llvm::Value *headerSize = getSize(IGF, HeaderSize);
400-
if (ElementTI.isEmpty(ResilienceScope::Local))
401-
return headerSize;
441+
if (ElementTI.isKnownEmpty())
442+
return layout.HeaderSize;
402443

403444
llvm::Value *size = properLength;
404445

@@ -414,68 +455,81 @@ llvm::Value *ArrayHeapLayout::getAllocationSize(IRGenFunction &IGF,
414455
// Increase that by the header size, saturating at SIZE_MAX.
415456
if (canOverflow) {
416457
size = checkOverflow(IGF, llvm::Intrinsic::uadd_with_overflow,
417-
size, headerSize);
458+
size, layout.HeaderSize);
418459
} else {
419-
size = IGF.Builder.CreateAdd(size, headerSize);
460+
size = IGF.Builder.CreateAdd(size, layout.HeaderSize);
420461
}
421462

422463
return size;
423464
}
424465

425466
/// Returns a pointer to the 'length' field of an array allocation.
426-
Address ArrayHeapLayout::getLengthPointer(IRGenFunction &IGF,
427-
llvm::Value *alloc) const {
467+
Address HeapArrayInfo::getLengthPointer(IRGenFunction &IGF,
468+
const Layout &layout,
469+
llvm::Value *alloc) const {
428470
assert(alloc->getType() == IGF.IGM.RefCountedPtrTy);
429471
llvm::Value *addr = IGF.Builder.CreateConstInBoundsGEP1_32(alloc, 1);
430472
addr = IGF.Builder.CreateBitCast(addr, IGF.IGM.SizeTy->getPointerTo());
431473

432474
return Address(addr, IGF.IGM.getPointerAlignment());
433475
}
434476

435-
llvm::Value *ArrayHeapLayout::getBeginPointer(IRGenFunction &IGF,
436-
llvm::Value *alloc) const {
477+
llvm::Value *HeapArrayInfo::getBeginPointer(IRGenFunction &IGF,
478+
const Layout &layout,
479+
llvm::Value *alloc) const {
437480
assert(alloc->getType() == IGF.IGM.RefCountedPtrTy);
438481
alloc = IGF.Builder.CreateBitCast(alloc, IGF.IGM.Int8PtrTy);
439-
llvm::Value *begin =
440-
IGF.Builder.CreateConstInBoundsGEP1_32(alloc, HeaderSize.getValue());
482+
llvm::Value *begin = IGF.Builder.CreateInBoundsGEP(alloc, layout.HeaderSize);
441483
return IGF.Builder.CreateBitCast(begin,
442484
ElementTI.getStorageType()->getPointerTo());
443485
}
444486

445-
llvm::Value *ArrayHeapLayout::emitUnmanagedAlloc(IRGenFunction &IGF,
446-
llvm::Value *length,
447-
Address &begin,
448-
Expr *init,
449-
const llvm::Twine &name) const
487+
llvm::Value *HeapArrayInfo::emitUnmanagedAlloc(IRGenFunction &IGF,
488+
llvm::Value *length,
489+
Address &begin,
490+
Expr *init,
491+
const llvm::Twine &name) const
450492
{
493+
Layout layout = getLayout(IGF);
494+
451495
llvm::Constant *metadata = getPrivateMetadata(IGF.IGM);
452-
llvm::Value *size = getAllocationSize(IGF, length, true, true);
453-
llvm::Value *align = getSize(IGF, Size(Align.getValue()));
496+
llvm::Value *size = getAllocationSize(IGF, layout, length, true, true);
497+
llvm::Value *align = layout.AllocAlign;
454498

455499
// Perform the allocation.
456500
llvm::Value *alloc =
457501
IGF.emitAllocObjectCall(metadata, size, align, "array.alloc");
458502

459503
if (!Bindings.empty()) {
460504
Address bindingsBuffer =
461-
projectBindingsBuffer(IGF, Address(alloc, getAlignment()));
505+
projectBindingsBuffer(IGF, Address(alloc, layout.BestStaticAlignment));
462506
Bindings.save(IGF, bindingsBuffer);
463507
}
464508

509+
// Store the length pointer to the array.
510+
Address lengthPtr = getLengthPointer(IGF, layout, alloc);
511+
// FIXME: storing the actual length here doesn't seem to work.
512+
IGF.Builder.CreateStore(IGF.IGM.getSize(Size(0)), lengthPtr);
513+
465514
// Find the begin pointer.
466-
llvm::Value *beginPtr = getBeginPointer(IGF, alloc);
515+
llvm::Value *beginPtr = getBeginPointer(IGF, layout, alloc);
467516
begin = ElementTI.getAddressForPointer(beginPtr);
468517

469518
// If we don't have an initializer, just zero-initialize and
470519
// immediately enter a release cleanup.
471520
if (!init) {
472-
llvm::Value *sizeToMemset =
473-
IGF.Builder.CreateSub(size, getSize(IGF, HeaderSize));
521+
llvm::Value *sizeToMemset = IGF.Builder.CreateSub(size, layout.HeaderSize);
522+
523+
Alignment arrayAlignment = layout.BestStaticAlignment;
524+
if (auto offset = dyn_cast<llvm::ConstantInt>(layout.HeaderSize))
525+
arrayAlignment =
526+
arrayAlignment.alignmentAtOffset(Size(offset->getZExtValue()));
527+
474528
IGF.Builder.CreateMemSet(
475529
IGF.Builder.CreateBitCast(beginPtr, IGF.IGM.Int8PtrTy),
476530
llvm::ConstantInt::get(IGF.IGM.Int8Ty, 0),
477531
sizeToMemset,
478-
Align.alignmentAtOffset(HeaderSize).getValue(),
532+
arrayAlignment.getValue(),
479533
/*volatile*/ false);
480534

481535
// Otherwise, repeatedly evaluate the initializer into successive
@@ -487,11 +541,11 @@ llvm::Value *ArrayHeapLayout::emitUnmanagedAlloc(IRGenFunction &IGF,
487541
return alloc;
488542
}
489543

490-
ManagedValue ArrayHeapLayout::emitAlloc(IRGenFunction &IGF,
491-
llvm::Value *length,
492-
Address &begin,
493-
Expr *init,
494-
const llvm::Twine &name) const {
544+
ManagedValue HeapArrayInfo::emitAlloc(IRGenFunction &IGF,
545+
llvm::Value *length,
546+
Address &begin,
547+
Expr *init,
548+
const llvm::Twine &name) const {
495549
llvm::Value *alloc = emitUnmanagedAlloc(IGF, length, begin, init, name);
496550
return IGF.enterReleaseCleanup(alloc);
497551
}

0 commit comments

Comments
 (0)