Skip to content

Commit ca402f1

Browse files
committed
Give multi-payload enums extra inhabitants.
Previously, they would forward their unused spare bits to be used by other multi-payload enums, but did not implement anything for single-payload extra inhabitants.
1 parent c266662 commit ca402f1

File tree

16 files changed

+485
-115
lines changed

16 files changed

+485
-115
lines changed

include/swift/ABI/Enum.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//===--- Enum.h - Enum implementation runtime declarations ------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Enum implementation details declarations relevant to the ABI.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_ABI_ENUM_H
18+
#define SWIFT_ABI_ENUM_H
19+
20+
#include <stdlib.h>
21+
22+
namespace swift {
23+
24+
struct EnumTagCounts {
25+
unsigned numTags, numTagBytes;
26+
};
27+
28+
inline EnumTagCounts
29+
getEnumTagCounts(size_t size, unsigned emptyCases, unsigned payloadCases) {
30+
// We can use the payload area with a tag bit set somewhere outside of the
31+
// payload area to represent cases. See how many bytes we need to cover
32+
// all the empty cases.
33+
unsigned numTags = payloadCases;
34+
if (emptyCases > 0) {
35+
if (size >= 4)
36+
// Assume that one tag bit is enough if the precise calculation overflows
37+
// an int32.
38+
numTags += 1;
39+
else {
40+
unsigned bits = size * 8U;
41+
unsigned casesPerTagBitValue = 1U << bits;
42+
numTags += ((emptyCases + (casesPerTagBitValue-1U)) >> bits);
43+
}
44+
}
45+
unsigned numTagBytes = (numTags <= 1 ? 0 :
46+
numTags < 256 ? 1 :
47+
numTags < 65536 ? 2 : 4);
48+
return {numTags, numTagBytes};
49+
}
50+
51+
} // namespace swift
52+
53+
#endif // SWIFT_ABI_ENUM_H

lib/IRGen/GenEnum.cpp

Lines changed: 130 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,7 @@ namespace {
11811181
// discriminate enum cases.
11821182
unsigned ExtraTagBitCount = ~0u;
11831183
// The number of possible values for the extra tag bits that are used.
1184-
// Log2(NumExtraTagValues - 1) + 1 == ExtraTagBitCount
1184+
// Log2(NumExtraTagValues - 1) + 1 <= ExtraTagBitCount
11851185
unsigned NumExtraTagValues = ~0u;
11861186

11871187
APInt getExtraTagBitConstant(uint64_t value) const {
@@ -4820,41 +4820,161 @@ namespace {
48204820

48214821
/// \group Extra inhabitants
48224822

4823-
// TODO
4823+
// If we didn't use all of the available tag bit representations, offer
4824+
// the remaining ones as extra inhabitants.
48244825

4825-
bool mayHaveExtraInhabitants(IRGenModule &) const override { return false; }
4826+
bool mayHaveExtraInhabitants(IRGenModule &IGM) const override {
4827+
if (TIK >= Fixed)
4828+
return getFixedExtraInhabitantCount(IGM) > 0;
4829+
return true;
4830+
}
4831+
4832+
/// Rounds the extra tag bit count up to the next byte size.
4833+
unsigned getExtraTagBitCountForExtraInhabitants() const {
4834+
if (!ExtraTagTy)
4835+
return 0;
4836+
return (ExtraTagTy->getBitWidth() + 7) & ~7;
4837+
}
4838+
4839+
Address projectExtraTagBitsForExtraInhabitants(IRGenFunction &IGF,
4840+
Address base) const {
4841+
auto addr = projectExtraTagBits(IGF, base);
4842+
if (ExtraTagTy->getBitWidth() != getExtraTagBitCountForExtraInhabitants()) {
4843+
addr = IGF.Builder.CreateBitCast(addr,
4844+
llvm::IntegerType::get(IGF.IGM.getLLVMContext(),
4845+
getExtraTagBitCountForExtraInhabitants())
4846+
->getPointerTo());
4847+
}
4848+
return addr;
4849+
}
48264850

48274851
llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF,
48284852
Address src,
48294853
SILType T,
48304854
bool isOutlined) const override {
4831-
llvm_unreachable("extra inhabitants for multi-payload enums not implemented");
4855+
// For dynamic layouts, the runtime provides a value witness to do this.
4856+
if (TIK < Fixed) {
4857+
return emitGetExtraInhabitantIndexCall(IGF, T, src);
4858+
}
4859+
4860+
llvm::Value *tag;
4861+
if (CommonSpareBits.count()) {
4862+
auto payload = EnumPayload::load(IGF, projectPayload(IGF, src),
4863+
PayloadSchema);
4864+
tag = payload.emitGatherSpareBits(IGF, CommonSpareBits, 0, 32);
4865+
if (getExtraTagBitCountForExtraInhabitants()) {
4866+
auto extraTagAddr = projectExtraTagBitsForExtraInhabitants(IGF, src);
4867+
auto extraTag = IGF.Builder.CreateLoad(extraTagAddr);
4868+
auto extraTagBits =
4869+
IGF.Builder.CreateZExtOrTrunc(extraTag, IGF.IGM.Int32Ty);
4870+
extraTagBits =
4871+
IGF.Builder.CreateShl(extraTagBits, CommonSpareBits.count());
4872+
tag = IGF.Builder.CreateOr(tag, extraTagBits);
4873+
}
4874+
} else {
4875+
auto extraTagAddr = projectExtraTagBitsForExtraInhabitants(IGF, src);
4876+
auto extraTag = IGF.Builder.CreateLoad(extraTagAddr);
4877+
tag = IGF.Builder.CreateZExtOrTrunc(extraTag, IGF.IGM.Int32Ty);
4878+
}
4879+
4880+
// Check whether it really is an extra inhabitant.
4881+
auto tagBits = CommonSpareBits.count() + getExtraTagBitCountForExtraInhabitants();
4882+
auto maxTag = tagBits >= 32 ? ~0u : (1 << tagBits) - 1;
4883+
auto index = IGF.Builder.CreateSub(
4884+
llvm::ConstantInt::get(IGF.IGM.Int32Ty, maxTag),
4885+
tag);
4886+
auto isExtraInhabitant = IGF.Builder.CreateICmpULT(index,
4887+
llvm::ConstantInt::get(IGF.IGM.Int32Ty,
4888+
getFixedExtraInhabitantCount(IGF.IGM)));
4889+
return IGF.Builder.CreateSelect(isExtraInhabitant,
4890+
index, llvm::ConstantInt::get(IGF.IGM.Int32Ty, -1));
48324891
}
48334892

48344893
void storeExtraInhabitant(IRGenFunction &IGF,
48354894
llvm::Value *index,
48364895
Address dest,
48374896
SILType T,
48384897
bool isOutlined) const override {
4839-
llvm_unreachable("extra inhabitants for multi-payload enums not implemented");
4898+
// For dynamic layouts, the runtime provides a value witness to do this.
4899+
if (TIK < Fixed) {
4900+
emitStoreExtraInhabitantCall(IGF, T, index, dest);
4901+
return;
4902+
}
4903+
4904+
auto indexValue = IGF.Builder.CreateNot(index);
4905+
if (CommonSpareBits.count()) {
4906+
// Factor the index value into parts to scatter into the payload and
4907+
// to store in the extra tag bits, if any.
4908+
EnumPayload payload =
4909+
interleaveSpareBits(IGF, PayloadSchema, CommonSpareBits, indexValue);
4910+
payload.store(IGF, projectPayload(IGF, dest));
4911+
if (getExtraTagBitCountForExtraInhabitants() > 0) {
4912+
auto tagBits = IGF.Builder.CreateLShr(indexValue,
4913+
llvm::ConstantInt::get(IGF.IGM.Int32Ty, CommonSpareBits.count()));
4914+
auto tagAddr = projectExtraTagBitsForExtraInhabitants(IGF, dest);
4915+
tagBits = IGF.Builder.CreateZExtOrTrunc(tagBits,
4916+
tagAddr.getAddress()->getType()->getPointerElementType());
4917+
IGF.Builder.CreateStore(tagBits, tagAddr);
4918+
}
4919+
} else {
4920+
// Only need to store the tag value.
4921+
auto tagAddr = projectExtraTagBitsForExtraInhabitants(IGF, dest);
4922+
indexValue = IGF.Builder.CreateZExtOrTrunc(indexValue,
4923+
tagAddr.getAddress()->getType()->getPointerElementType());
4924+
IGF.Builder.CreateStore(indexValue, tagAddr);
4925+
}
48404926
}
48414927

48424928
APInt
48434929
getFixedExtraInhabitantMask(IRGenModule &IGM) const override {
4844-
// TODO may not always be all-ones
4845-
return APInt::getAllOnesValue(
4846-
cast<FixedTypeInfo>(TI)->getFixedSize().getValueInBits());
4930+
// The extra inhabitant goes into the tag bits.
4931+
auto tagBits = CommonSpareBits.asAPInt();
4932+
auto fixedTI = cast<FixedTypeInfo>(TI);
4933+
if (getExtraTagBitCountForExtraInhabitants() > 0) {
4934+
auto bitSize = fixedTI->getFixedSize().getValueInBits();
4935+
tagBits = tagBits.zext(bitSize);
4936+
auto extraTagMask = APInt::getAllOnesValue(bitSize)
4937+
.shl(CommonSpareBits.size());
4938+
tagBits |= extraTagMask;
4939+
}
4940+
return tagBits;
48474941
}
48484942

48494943
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override {
4850-
return 0;
4944+
unsigned totalTagBits = CommonSpareBits.count() + getExtraTagBitCountForExtraInhabitants();
4945+
if (totalTagBits >= 32)
4946+
return INT_MAX;
4947+
unsigned totalTags = 1u << totalTagBits;
4948+
return totalTags - ElementsWithPayload.size() - NumEmptyElementTags;
48514949
}
48524950

48534951
APInt
48544952
getFixedExtraInhabitantValue(IRGenModule &IGM,
48554953
unsigned bits,
48564954
unsigned index) const override {
4857-
llvm_unreachable("extra inhabitants for multi-payload enums not implemented");
4955+
// Count down from all-ones since a small negative number constant is
4956+
// likely to be easier to reify.
4957+
auto mask = ~index;
4958+
auto extraTagMask = getExtraTagBitCountForExtraInhabitants() >= 32
4959+
? ~0u : (1 << getExtraTagBitCountForExtraInhabitants()) - 1;
4960+
4961+
if (auto payloadBitCount = CommonSpareBits.count()) {
4962+
auto payloadTagMask = payloadBitCount >= 32
4963+
? ~0u : (1 << payloadBitCount) - 1;
4964+
auto payloadPart = mask & payloadTagMask;
4965+
auto payloadBits = interleaveSpareBits(IGM, CommonSpareBits,
4966+
bits, payloadPart, 0);
4967+
if (getExtraTagBitCountForExtraInhabitants() > 0) {
4968+
auto extraBits = APInt(bits,
4969+
(mask >> payloadBitCount) & extraTagMask)
4970+
.shl(CommonSpareBits.size());
4971+
payloadBits |= extraBits;
4972+
}
4973+
return payloadBits;
4974+
} else {
4975+
auto value = APInt(bits, mask & extraTagMask);
4976+
return value.shl(CommonSpareBits.size());
4977+
}
48584978
}
48594979

48604980
ClusteredBitVector

stdlib/public/Reflection/TypeLowering.cpp

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
//
1919
//===----------------------------------------------------------------------===//
2020

21+
#include "swift/ABI/Enum.h"
2122
#include "swift/Reflection/TypeLowering.h"
2223
#include "swift/Reflection/TypeRef.h"
2324
#include "swift/Reflection/TypeRefBuilder.h"
@@ -948,31 +949,6 @@ class HasSingletonMetatype
948949
}
949950
};
950951

951-
// Copy-and-pasted from stdlib/public/runtime/Enum.cpp -- should probably go
952-
// in a header somewhere, since the formula is part of the ABI.
953-
static unsigned getNumTagBytes(size_t size, unsigned emptyCases,
954-
unsigned payloadCases) {
955-
// We can use the payload area with a tag bit set somewhere outside of the
956-
// payload area to represent cases. See how many bytes we need to cover
957-
// all the empty cases.
958-
959-
unsigned numTags = payloadCases;
960-
if (emptyCases > 0) {
961-
if (size >= 4)
962-
// Assume that one tag bit is enough if the precise calculation overflows
963-
// an int32.
964-
numTags += 1;
965-
else {
966-
unsigned bits = size * 8U;
967-
unsigned casesPerTagBitValue = 1U << bits;
968-
numTags += ((emptyCases + (casesPerTagBitValue-1U)) >> bits);
969-
}
970-
}
971-
return (numTags <= 1 ? 0 :
972-
numTags < 256 ? 1 :
973-
numTags < 65536 ? 2 : 4);
974-
}
975-
976952
class EnumTypeInfoBuilder {
977953
TypeConverter &TC;
978954
unsigned Size, Alignment, NumExtraInhabitants;
@@ -1035,9 +1011,9 @@ class EnumTypeInfoBuilder {
10351011
// NoPayloadEnumImplStrategy
10361012
if (PayloadCases.empty()) {
10371013
Kind = RecordKind::NoPayloadEnum;
1038-
Size += getNumTagBytes(/*size=*/0,
1039-
NoPayloadCases,
1040-
/*payloadCases=*/0);
1014+
Size += getEnumTagCounts(/*size=*/0,
1015+
NoPayloadCases,
1016+
/*payloadCases=*/0).numTagBytes;
10411017

10421018
// SinglePayloadEnumImplStrategy
10431019
} else if (PayloadCases.size() == 1) {
@@ -1065,9 +1041,9 @@ class EnumTypeInfoBuilder {
10651041
// Not enough extra inhabitants for all cases. We have to add an
10661042
// extra tag field.
10671043
NumExtraInhabitants = 0;
1068-
Size += getNumTagBytes(Size,
1069-
NoPayloadCases - NumExtraInhabitants,
1070-
/*payloadCases=*/1);
1044+
Size += getEnumTagCounts(Size,
1045+
NoPayloadCases - NumExtraInhabitants,
1046+
/*payloadCases=*/1).numTagBytes;
10711047
}
10721048
}
10731049

@@ -1091,14 +1067,20 @@ class EnumTypeInfoBuilder {
10911067
NumExtraInhabitants = FixedDescriptor->NumExtraInhabitants;
10921068
BitwiseTakable = FixedDescriptor->isBitwiseTakable();
10931069
} else {
1094-
// Dynamic multi-payload enums do not have extra inhabitants
1095-
NumExtraInhabitants = 0;
1096-
10971070
// Dynamic multi-payload enums always use an extra tag to differentiate
10981071
// between cases
1099-
Size += getNumTagBytes(Size,
1100-
NoPayloadCases,
1101-
PayloadCases.size());
1072+
auto tagCounts = getEnumTagCounts(Size, NoPayloadCases,
1073+
PayloadCases.size());
1074+
1075+
Size += tagCounts.numTagBytes;
1076+
// Dynamic multi-payload enums use the tag representations not assigned
1077+
// to cases for extra inhabitants.
1078+
if (tagCounts.numTagBytes >= 32) {
1079+
NumExtraInhabitants = INT_MAX;
1080+
} else {
1081+
NumExtraInhabitants =
1082+
(1 << (tagCounts.numTagBytes * 8)) - tagCounts.numTags;
1083+
}
11021084
}
11031085
}
11041086

0 commit comments

Comments
 (0)