Skip to content

Commit 32749f3

Browse files
authored
Merge pull request #20561 from jckarter/multi-payload-xi
Give multi-payload enums extra inhabitants.
2 parents a1a3c5f + ca402f1 commit 32749f3

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 {
@@ -4817,41 +4817,161 @@ namespace {
48174817

48184818
/// \group Extra inhabitants
48194819

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

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

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

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

48394925
APInt
48404926
getFixedExtraInhabitantMask(IRGenModule &IGM) const override {
4841-
// TODO may not always be all-ones
4842-
return APInt::getAllOnesValue(
4843-
cast<FixedTypeInfo>(TI)->getFixedSize().getValueInBits());
4927+
// The extra inhabitant goes into the tag bits.
4928+
auto tagBits = CommonSpareBits.asAPInt();
4929+
auto fixedTI = cast<FixedTypeInfo>(TI);
4930+
if (getExtraTagBitCountForExtraInhabitants() > 0) {
4931+
auto bitSize = fixedTI->getFixedSize().getValueInBits();
4932+
tagBits = tagBits.zext(bitSize);
4933+
auto extraTagMask = APInt::getAllOnesValue(bitSize)
4934+
.shl(CommonSpareBits.size());
4935+
tagBits |= extraTagMask;
4936+
}
4937+
return tagBits;
48444938
}
48454939

48464940
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override {
4847-
return 0;
4941+
unsigned totalTagBits = CommonSpareBits.count() + getExtraTagBitCountForExtraInhabitants();
4942+
if (totalTagBits >= 32)
4943+
return INT_MAX;
4944+
unsigned totalTags = 1u << totalTagBits;
4945+
return totalTags - ElementsWithPayload.size() - NumEmptyElementTags;
48484946
}
48494947

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

48574977
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)