Skip to content

Let the stride of a type be at least one and remove the strideof_nonzero builtin #4774

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions include/swift/AST/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -358,11 +358,6 @@ BUILTIN_MISC_OPERATION(IsPOD, "ispod", "n", Special)
/// Alignof has type T.Type -> Int
BUILTIN_MISC_OPERATION(Alignof, "alignof", "n", Special)

/// Strideof has type T.Type -> Int.
/// Note that this version will never return 0. It instead always returns
/// at least 1
BUILTIN_MISC_OPERATION(StrideofNonZero, "strideof_nonzero", "n", Special)

/// AllocRaw has type (Int, Int) -> Builtin.RawPointer
BUILTIN_MISC_OPERATION(AllocRaw, "allocRaw", "", Special)

Expand Down
1 change: 0 additions & 1 deletion lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1559,7 +1559,6 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
case BuiltinValueKind::Sizeof:
case BuiltinValueKind::Strideof:
case BuiltinValueKind::Alignof:
case BuiltinValueKind::StrideofNonZero:
return getSizeOrAlignOfOperation(Context, Id);

case BuiltinValueKind::IsPOD:
Expand Down
7 changes: 6 additions & 1 deletion lib/IRGen/FixedTypeInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,13 @@ class FixedTypeInfo : public TypeInfo {
/// object. The stride is the storage size rounded up to the
/// alignment; its practical use is that, in an array, it is the
/// offset from the size of one element to the offset of the next.
/// The stride is at least one, even for zero-sized types, like the empty
/// tuple.
Size getFixedStride() const {
return StorageSize.roundUpToAlignment(getFixedAlignment());
Size s = StorageSize.roundUpToAlignment(getFixedAlignment());
if (s.isZero())
s = Size(1);
return s;
}

/// Returns the fixed number of "extra inhabitants" (that is, bit
Expand Down
16 changes: 0 additions & 16 deletions lib/IRGen/GenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,22 +171,6 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, Identifier FnId,
return;
}

if (Builtin.ID == BuiltinValueKind::StrideofNonZero) {
// Note this case must never return 0.
// It is implemented as max(strideof, 1)
args.claimAll();
auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM,
substitutions[0].getReplacement());
// Strideof should never return 0, so return 1 if the type has a 0 stride.
llvm::Value *StrideOf = valueTy.second.getStride(IGF, valueTy.first);
llvm::IntegerType *IntTy = cast<llvm::IntegerType>(StrideOf->getType());
auto *Zero = llvm::ConstantInt::get(IntTy, 0);
auto *One = llvm::ConstantInt::get(IntTy, 1);
llvm::Value *Cmp = IGF.Builder.CreateICmpEQ(StrideOf, Zero);
out.add(IGF.Builder.CreateSelect(Cmp, One, StrideOf));
return;
}

if (Builtin.ID == BuiltinValueKind::IsPOD) {
args.claimAll();
auto valueTy = getLoweredTypeAndTypeInfo(IGF.IGM,
Expand Down
3 changes: 2 additions & 1 deletion lib/IRGen/ValueWitness.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,8 @@ enum class ValueWitness : unsigned {

/// size_t stride;
///
/// The required size per element of an array of this type.
/// The required size per element of an array of this type. It is at least
/// one, even for zero-sized types, like the empty tuple.
Stride,

Last_RequiredValueWitness = Stride,
Expand Down
3 changes: 0 additions & 3 deletions lib/SILOptimizer/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,6 @@ Optional<bool> swift::computeSignBit(SILValue V) {
// Strideof always returns non-negative results.
case BuiltinValueKind::Strideof:
return false;
// StrideofNonZero always returns positive results.
case BuiltinValueKind::StrideofNonZero:
return false;
// Alignof always returns non-negative results.
case BuiltinValueKind::Alignof:
return false;
Expand Down
7 changes: 2 additions & 5 deletions lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ matchSizeOfMultiplication(SILValue I, MetatypeInst *RequiredType,
m_ApplyInst(
BuiltinValueKind::SMulOver, m_SILValue(Dist),
m_ApplyInst(BuiltinValueKind::ZExtOrBitCast,
m_ApplyInst(BuiltinValueKind::StrideofNonZero,
m_ApplyInst(BuiltinValueKind::Strideof,
m_MetatypeInst(StrideType)))),
0))) ||
match(
Expand All @@ -195,7 +195,7 @@ matchSizeOfMultiplication(SILValue I, MetatypeInst *RequiredType,
m_ApplyInst(
BuiltinValueKind::SMulOver,
m_ApplyInst(BuiltinValueKind::ZExtOrBitCast,
m_ApplyInst(BuiltinValueKind::StrideofNonZero,
m_ApplyInst(BuiltinValueKind::Strideof,
m_MetatypeInst(StrideType))),
m_SILValue(Dist)),
0)))) {
Expand Down Expand Up @@ -464,9 +464,6 @@ SILInstruction *SILCombiner::visitBuiltinInst(BuiltinInst *I) {

if (match(I, m_ApplyInst(BuiltinValueKind::SMulOver,
m_ApplyInst(BuiltinValueKind::Strideof),
m_ValueBase(), m_IntegerLiteralInst())) ||
match(I, m_ApplyInst(BuiltinValueKind::SMulOver,
m_ApplyInst(BuiltinValueKind::StrideofNonZero),
m_ValueBase(), m_IntegerLiteralInst()))) {
I->swapOperands(0, 1);
return I;
Expand Down
10 changes: 3 additions & 7 deletions lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ visitPointerToAddressInst(PointerToAddressInst *PTAI) {
// Turn this also into an index_addr. We generate this pattern after switching
// the Word type to an explicit Int32 or Int64 in the stdlib.
//
// %101 = builtin "strideof_nonzero"<Int>(%84 : $@thick Int.Type) :
// %101 = builtin "strideof"<Int>(%84 : $@thick Int.Type) :
// $Builtin.Word
// %102 = builtin "zextOrBitCast_Word_Int64"(%101 : $Builtin.Word) :
// $Builtin.Int64
Expand Down Expand Up @@ -119,13 +119,13 @@ visitPointerToAddressInst(PointerToAddressInst *PTAI) {
m_ApplyInst(
BuiltinValueKind::SMulOver, m_SILValue(Distance),
m_ApplyInst(BuiltinValueKind::ZExtOrBitCast,
m_ApplyInst(BuiltinValueKind::StrideofNonZero,
m_ApplyInst(BuiltinValueKind::Strideof,
m_MetatypeInst(Metatype))))) ||
match(StrideMul,
m_ApplyInst(
BuiltinValueKind::SMulOver,
m_ApplyInst(BuiltinValueKind::ZExtOrBitCast,
m_ApplyInst(BuiltinValueKind::StrideofNonZero,
m_ApplyInst(BuiltinValueKind::Strideof,
m_MetatypeInst(Metatype))),
m_SILValue(Distance)))) {
SILType InstanceType =
Expand Down Expand Up @@ -167,10 +167,6 @@ visitPointerToAddressInst(PointerToAddressInst *PTAI) {
if (match(Bytes, m_ApplyInst(BuiltinValueKind::SMulOver, m_ValueBase(),
m_ApplyInst(BuiltinValueKind::Strideof,
m_MetatypeInst(Metatype)),
m_ValueBase())) ||
match(Bytes, m_ApplyInst(BuiltinValueKind::SMulOver, m_ValueBase(),
m_ApplyInst(BuiltinValueKind::StrideofNonZero,
m_MetatypeInst(Metatype)),
m_ValueBase()))) {
SILType InstanceType =
Metatype->getType().getMetatypeInstanceType(PTAI->getModule());
Expand Down
6 changes: 5 additions & 1 deletion stdlib/public/Reflection/TypeLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,8 @@ const RecordTypeInfo *RecordTypeInfoBuilder::build() {

// Calculate the stride
unsigned Stride = ((Size + Alignment - 1) & ~(Alignment - 1));
if (Stride == 0)
Stride = 1;

return TC.makeTypeInfo<RecordTypeInfo>(
Size, Alignment, Stride,
Expand Down Expand Up @@ -528,7 +530,7 @@ const TypeInfo *TypeConverter::getEmptyTypeInfo() {
if (EmptyTI != nullptr)
return EmptyTI;

EmptyTI = makeTypeInfo<TypeInfo>(TypeInfoKind::Builtin, 0, 1, 0, 0);
EmptyTI = makeTypeInfo<TypeInfo>(TypeInfoKind::Builtin, 0, 1, 1, 0);
return EmptyTI;
}

Expand Down Expand Up @@ -962,6 +964,8 @@ class EnumTypeInfoBuilder {

// Calculate the stride
unsigned Stride = ((Size + Alignment - 1) & ~(Alignment - 1));
if (Stride == 0)
Stride = 1;

return TC.makeTypeInfo<RecordTypeInfo>(
Size, Alignment, Stride,
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/MemoryLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public enum MemoryLayout<T> {
/// performance for space efficiency. The result is always positive.
@_transparent
public static var stride: Int {
return Int(Builtin.strideof_nonzero(T.self))
return Int(Builtin.strideof(T.self))
}

/// The default memory alignment of `T`.
Expand Down
6 changes: 4 additions & 2 deletions stdlib/public/runtime/Enum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ swift::swift_initEnumValueWitnessTableSinglePayload(ValueWitnessTable *vwtable,
.withExtraInhabitants(unusedExtraInhabitants > 0)
.withEnumWitnesses(true)
.withInlineStorage(ValueWitnessTable::isValueInline(size, align));
vwtable->stride = llvm::alignTo(size, align);
auto rawStride = llvm::alignTo(size, align);
vwtable->stride = rawStride == 0 ? 1 : rawStride;

// Substitute in better common value witnesses if we have them.
// If the payload type is a single-refcounted pointer, and the enum has
Expand Down Expand Up @@ -310,7 +311,8 @@ swift::swift_initEnumMetadataMultiPayload(ValueWitnessTable *vwtable,
.withEnumWitnesses(true)
.withInlineStorage(ValueWitnessTable::isValueInline(totalSize, alignMask+1))
;
vwtable->stride = (totalSize + alignMask) & ~alignMask;
auto rawStride = (totalSize + alignMask) & ~alignMask;
vwtable->stride = rawStride == 0 ? 1 : rawStride;

installCommonValueWitnesses(vwtable);
}
Expand Down
15 changes: 9 additions & 6 deletions stdlib/public/runtime/ExistentialMetadataImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,9 @@ struct LLVM_LIBRARY_VISIBILITY NonFixedOpaqueExistentialBox
return std::max(alignof(void*), alignof(ValueBuffer));
}
static size_t getSize(unsigned numWitnessTables) {
return sizeof(OpaqueExistentialContainer)
+ numWitnessTables * sizeof(void*);
constexpr size_t base = sizeof(OpaqueExistentialContainer);
static_assert(base > 0, "stride needs base size > 0");
return base + numWitnessTables * sizeof(void*);
}
static size_t getStride(unsigned numWitnessTables) {
return getSize(numWitnessTables);
Expand Down Expand Up @@ -348,8 +349,9 @@ struct LLVM_LIBRARY_VISIBILITY NonFixedClassExistentialBox
return alignof(void*);
}
static size_t getSize(unsigned numWitnessTables) {
return sizeof(ClassExistentialContainer)
+ numWitnessTables * sizeof(void*);
constexpr size_t base = sizeof(ClassExistentialContainer);
static_assert(base > 0, "stride needs base size > 0");
return base + numWitnessTables * sizeof(void*);
}
static size_t getStride(unsigned numWitnessTables) {
return getSize(numWitnessTables);
Expand Down Expand Up @@ -469,8 +471,9 @@ struct LLVM_LIBRARY_VISIBILITY NonFixedExistentialMetatypeBox
return alignof(void*);
}
static size_t getSize(unsigned numWitnessTables) {
return sizeof(ExistentialMetatypeContainer)
+ numWitnessTables * sizeof(void*);
constexpr size_t base = sizeof(ExistentialMetatypeContainer);
static_assert(base > 0, "stride needs base size > 0");
return base + numWitnessTables * sizeof(void*);
}
static size_t getStride(unsigned numWitnessTables) {
return getSize(numWitnessTables);
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@ void performBasicLayout(BasicLayout &layout,
.withPOD(isPOD)
.withBitwiseTakable(isBitwiseTakable)
.withInlineStorage(isInline);
layout.stride = roundUpToAlignMask(size, alignMask);
layout.stride = std::max(size_t(1), roundUpToAlignMask(size, alignMask));
}
} // end anonymous namespace

Expand Down
4 changes: 3 additions & 1 deletion stdlib/public/runtime/MetadataImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,9 @@ struct AggregateBox {
using Helper = AggregateBoxHelper<0, EltBoxes...>;
static constexpr size_t size = Helper::endOffset;
static constexpr size_t alignment = Helper::alignment;
static constexpr size_t stride = roundUpToAlignment(size, alignment);
static constexpr size_t rawStride = roundUpToAlignment(size, alignment);
static constexpr size_t stride = rawStride == 0 ? 1 : rawStride;

static constexpr bool isPOD = Helper::isPOD;
static constexpr bool isBitwiseTakable = Helper::isBitwiseTakable;

Expand Down
11 changes: 0 additions & 11 deletions test/IRGen/builtins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,17 +245,6 @@ func generic_strideof_test<T>(_: T) {
var s = Builtin.strideof(T.self)
}

// CHECK: define hidden void @_TF8builtins29generic_strideof_nonzero_testurFxT_(
func generic_strideof_nonzero_test<T>(_: T) {
// CHECK: [[T0:%.*]] = getelementptr inbounds i8*, i8** [[T:%.*]], i32 19
// CHECK-NEXT: [[T1:%.*]] = load i8*, i8** [[T0]]
// CHECK-NEXT: [[STRIDE:%.*]] = ptrtoint i8* [[T1]] to i64
// CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[STRIDE]], 0
// CHECK-NEXT: [[SELECT:%.*]] = select i1 [[CMP]], i64 1, i64 [[STRIDE]]
// CHECK-NEXT: store i64 [[SELECT]], i64* [[S:%.*]]
var s = Builtin.strideof_nonzero(T.self)
}

class X {}

class Y {}
Expand Down
10 changes: 10 additions & 0 deletions test/IRGen/indexing.sil
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ entry(%p : $*DifferentSizeStride, %i: $Builtin.Word):
return undef : $()
}

// CHECK: define{{( protected)?}} void @zero_size(%swift.opaque* noalias nocapture, i64) {{.*}} {
// CHECK: %2 = bitcast %swift.opaque* %0 to i8*
// CHECK-NEXT: %3 = mul nsw i64 %1, 1
// CHECK-NEXT: %4 = getelementptr inbounds i8, i8* %2, i64 %3
sil @zero_size : $@convention(thin) (@in (), Builtin.Word) -> () {
entry(%p : $*(), %i: $Builtin.Word):
%q = index_addr %p : $*(), %i : $Builtin.Word
return undef : $()
}

// CHECK: define{{( protected)?}} void @dynamic_size(%swift.opaque* noalias nocapture, i64, %swift.type* %T) {{.*}} {
// CHECK: %3 = bitcast %swift.type* %T to i8***
// CHECK-NEXT: %4 = getelementptr inbounds i8**, i8*** %3, i64 -1
Expand Down
69 changes: 69 additions & 0 deletions test/IRGen/zero_size_types.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// RUN: rm -rf %t && mkdir -p %t
// RUN: %target-build-swift -Xllvm -sil-disable-pass="Generic Specializer" %s -o %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s
// REQUIRES: executable_test

public struct EmptyStruct {
}

public enum SingleCaseEnum {
case A
}

public struct GenStruct<T> {
var t: T
}

@inline(never)
func fail() {
fatalError("size/stride mismatch")
}

@inline(never)
public func testit<T>(_ t: T) {
if MemoryLayout<T>.size != 0 {
fail()
}
if MemoryLayout<T>.stride != 1 {
fail()
}
if MemoryLayout<GenStruct<T>>.size != 0 {
fail()
}
if MemoryLayout<GenStruct<T>>.stride != 1 {
fail()
}
}

// Test size and stride which are loaded from the value witness tables.

testit(EmptyStruct())
testit(())
testit(SingleCaseEnum.A)

// Test size and stride which are computed as constants in IRGen.

if MemoryLayout<()>.size != 0 {
fail()
}
if MemoryLayout<()>.stride != 1 {
fail()
}

if MemoryLayout<EmptyStruct>.size != 0 {
fail()
}
if MemoryLayout<EmptyStruct>.stride != 1 {
fail()
}

if MemoryLayout<SingleCaseEnum>.size != 0 {
fail()
}
if MemoryLayout<SingleCaseEnum>.stride != 1 {
fail()
}

// CHECK: success
print("success")

10 changes: 5 additions & 5 deletions test/Reflection/typeref_lowering.swift
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ V12TypeLowering14MetatypeStruct
// CHECK-64-NEXT: (field name=wtable offset=16
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=1))))))
// CHECK-64-NEXT: (field name=structMetatype offset=112
// CHECK-64-NEXT: (builtin size=0 alignment=1 stride=0 num_extra_inhabitants=0))
// CHECK-64-NEXT: (builtin size=0 alignment=1 stride=1 num_extra_inhabitants=0))
// CHECK-64-NEXT: (field name=optionalStructMetatype offset=112
// CHECK-64-NEXT: (single_payload_enum size=8 alignment=8 stride=8 num_extra_inhabitants=[[PTR_XI_SUB_1]]
// CHECK-64-NEXT: (field name=some offset=0
Expand Down Expand Up @@ -667,7 +667,7 @@ V12TypeLowering14MetatypeStruct
// CHECK-32-NEXT: (field name=wtable offset=8
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=1))))))
// CHECK-32-NEXT: (field name=structMetatype offset=56
// CHECK-32-NEXT: (builtin size=0 alignment=1 stride=0 num_extra_inhabitants=0))
// CHECK-32-NEXT: (builtin size=0 alignment=1 stride=1 num_extra_inhabitants=0))
// CHECK-32-NEXT: (field name=optionalStructMetatype offset=56
// CHECK-32-NEXT: (single_payload_enum size=4 alignment=4 stride=4 num_extra_inhabitants=4095
// CHECK-32-NEXT: (field name=some offset=0
Expand All @@ -689,11 +689,11 @@ V12TypeLowering10EnumStruct
// CHECK-64: (struct TypeLowering.EnumStruct)
// CHECK-64-NEXT: (struct size=81 alignment=8 stride=88 num_extra_inhabitants=0
// CHECK-64-NEXT: (field name=empty offset=0
// CHECK-64-NEXT: (no_payload_enum size=0 alignment=0 stride=0 num_extra_inhabitants=0))
// CHECK-64-NEXT: (no_payload_enum size=0 alignment=0 stride=1 num_extra_inhabitants=0))
// CHECK-64-NEXT: (field name=noPayload offset=0
// CHECK-64-NEXT: (no_payload_enum size=1 alignment=0 stride=0 num_extra_inhabitants=0))
// CHECK-64-NEXT: (no_payload_enum size=1 alignment=0 stride=1 num_extra_inhabitants=0))
// CHECK-64-NEXT: (field name=sillyNoPayload offset=0
// CHECK-64-NEXT: (no_payload_enum size=1 alignment=0 stride=0 num_extra_inhabitants=0))
// CHECK-64-NEXT: (no_payload_enum size=1 alignment=0 stride=1 num_extra_inhabitants=0))
// CHECK-64-NEXT: (field name=singleton offset=8
// CHECK-64-NEXT: (reference kind=strong refcounting=native))
// CHECK-64-NEXT: (field name=singlePayload offset=16
Expand Down
Loading