mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
657 lines
22 KiB
C++
657 lines
22 KiB
C++
//===--- EnumPayload.cpp - Payload management for 'enum' Types ------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "BitPatternReader.h"
|
|
#include "EnumPayload.h"
|
|
#include "Explosion.h"
|
|
#include "GenEnum.h"
|
|
#include "IRGenModule.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
|
|
#include <algorithm>
|
|
#include <map>
|
|
|
|
using namespace swift;
|
|
using namespace irgen;
|
|
|
|
// FIXME: Everything here brazenly assumes little-endian-ness.
|
|
|
|
static llvm::Value *forcePayloadValue(EnumPayload::LazyValue &value) {
|
|
if (auto v = value.dyn_cast<llvm::Value *>())
|
|
return v;
|
|
|
|
auto null = llvm::Constant::getNullValue(value.dyn_cast<llvm::Type*>());
|
|
value = null;
|
|
return null;
|
|
}
|
|
|
|
static llvm::Type *getPayloadType(EnumPayload::LazyValue value) {
|
|
if (auto t = value.dyn_cast<llvm::Type *>())
|
|
return t;
|
|
|
|
return value.dyn_cast<llvm::Value *>()->getType();
|
|
}
|
|
|
|
// Clear bits starting from the most significant until the number
|
|
// of set bits in the value is less than or equal to numSetBits.
|
|
//
|
|
// For example: getLowestNSetBits(0x11111100, 2) = 0x00001100
|
|
static llvm::APInt getLowestNSetBits(llvm::APInt value,
|
|
unsigned numSetBits) {
|
|
// TODO: optimize
|
|
for (unsigned i = 0; i < value.getBitWidth(); ++i) {
|
|
if (numSetBits == 0) {
|
|
value.clearBit(i);
|
|
} else if (value[i]) {
|
|
numSetBits -= 1;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
EnumPayload EnumPayload::zero(IRGenModule &IGM, EnumPayloadSchema schema) {
|
|
// We don't need to create any values yet; they can be filled in when
|
|
// real values are inserted.
|
|
EnumPayload result;
|
|
schema.forEachType(IGM, [&](llvm::Type *type) {
|
|
result.PayloadValues.push_back(type);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
EnumPayload EnumPayload::fromBitPattern(IRGenModule &IGM,
|
|
const APInt &bitPattern,
|
|
EnumPayloadSchema schema) {
|
|
auto maskReader = BitPatternReader(bitPattern, IGM.Triple.isLittleEndian());
|
|
|
|
EnumPayload result;
|
|
schema.forEachType(IGM, [&](llvm::Type *type) {
|
|
unsigned bitSize = IGM.DataLayout.getTypeSizeInBits(type);
|
|
|
|
llvm::IntegerType *intTy
|
|
= llvm::IntegerType::get(IGM.getLLVMContext(), bitSize);
|
|
|
|
// Take some bits off of the bottom of the pattern.
|
|
auto bits = maskReader.read(bitSize);
|
|
auto val = llvm::ConstantInt::get(intTy, bits);
|
|
if (val->getType() != type) {
|
|
if (type->isPointerTy())
|
|
val = llvm::ConstantExpr::getIntToPtr(val, type);
|
|
else
|
|
val = llvm::ConstantExpr::getBitCast(val, type);
|
|
}
|
|
|
|
result.PayloadValues.push_back(val);
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Create a mask for an element with the given type at the provided
|
|
/// offset into the payload. The offset and size are in bits but
|
|
/// must be a multiple of 8 (i.e. byte aligned).
|
|
static APInt createElementMask(const llvm::DataLayout &DL,
|
|
llvm::Type *type,
|
|
unsigned payloadOffset,
|
|
unsigned payloadSizeInBits) {
|
|
// Create a mask for the bytes that make up the stored element
|
|
// by zero extending the element's mask to its storage size.
|
|
// This makes the mask valid regardless of endianness.
|
|
auto elSize = DL.getTypeSizeInBits(type);
|
|
auto elStoreSize = DL.getTypeStoreSizeInBits(type);
|
|
auto elMask = APInt::getLowBitsSet(elStoreSize, elSize);
|
|
|
|
// Pad the valueMask so that it can be applied to the entire
|
|
// payload.
|
|
auto mask = APInt::getZero(payloadSizeInBits);
|
|
auto offset = payloadOffset;
|
|
if (DL.isBigEndian()) {
|
|
offset = payloadSizeInBits - payloadOffset - elStoreSize;
|
|
}
|
|
mask.insertBits(elMask, offset);
|
|
return mask;
|
|
}
|
|
|
|
void EnumPayload::insertValue(IRGenModule &IGM,
|
|
IRBuilder &builder, llvm::Value *value,
|
|
unsigned payloadOffset) {
|
|
auto &DL = IGM.DataLayout;
|
|
|
|
// Create a mask for the value we are going to insert.
|
|
auto type = value->getType();
|
|
auto payloadSize = getAllocSizeInBits(DL);
|
|
auto mask = createElementMask(DL, type, payloadOffset, payloadSize);
|
|
|
|
// Scatter the value into the payload.
|
|
emitScatterBits(IGM, builder, mask, value);
|
|
}
|
|
|
|
llvm::Value *EnumPayload::extractValue(IRGenFunction &IGF, llvm::Type *type,
|
|
unsigned payloadOffset) const {
|
|
auto &DL = IGF.IGM.DataLayout;
|
|
|
|
// Create a mask for the value we are going to extract.
|
|
auto payloadSize = getAllocSizeInBits(DL);
|
|
auto mask = createElementMask(DL, type, payloadOffset, payloadSize);
|
|
|
|
// Convert the payload mask into a SpareBitVector.
|
|
// TODO: make emitGatherSpareBits take an APInt and delete.
|
|
auto bits = SpareBitVector::fromAPInt(std::move(mask));
|
|
|
|
// Gather the value from the payload.
|
|
auto valueSize = DL.getTypeSizeInBits(type);
|
|
auto value = emitGatherSpareBits(IGF, bits, 0, valueSize);
|
|
|
|
// Convert the integer value to the required type.
|
|
if (value->getType() != type) {
|
|
value = IGF.Builder.CreateBitOrPointerCast(value, type);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
EnumPayload EnumPayload::fromExplosion(IRGenModule &IGM,
|
|
Explosion &in, EnumPayloadSchema schema){
|
|
EnumPayload result;
|
|
|
|
schema.forEachType(IGM, [&](llvm::Type *type) {
|
|
auto next = in.claimNext();
|
|
if (next->getType() == type) {
|
|
result.PayloadValues.push_back(next);
|
|
} else {
|
|
// The original value had an unaligned integer size and was replaced by
|
|
// byte values in `replaceUnalignedIntegerValues`.
|
|
// This is done for enums in statically initialized global variables.
|
|
unsigned bitSize = cast<llvm::IntegerType>(type)->getBitWidth();
|
|
assert(bitSize % 8 == 0);
|
|
assert(cast<llvm::ConstantInt>(next)->getBitWidth() == 8);
|
|
result.PayloadValues.push_back(next);
|
|
for (unsigned byte = 1; byte < bitSize / 8; ++byte) {
|
|
auto nextByte = in.claimNext();
|
|
assert(cast<llvm::ConstantInt>(nextByte)->getBitWidth() == 8);
|
|
result.PayloadValues.push_back(nextByte);
|
|
}
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
void EnumPayload::explode(IRGenModule &IGM, Explosion &out) const {
|
|
for (LazyValue &value : PayloadValues) {
|
|
out.add(forcePayloadValue(value));
|
|
}
|
|
}
|
|
|
|
void EnumPayload::packIntoEnumPayload(IRGenModule &IGM,
|
|
IRBuilder &builder,
|
|
EnumPayload &outerPayload,
|
|
unsigned bitOffset) const {
|
|
auto &DL = IGM.DataLayout;
|
|
for (auto &value : PayloadValues) {
|
|
auto v = forcePayloadValue(value);
|
|
outerPayload.insertValue(IGM, builder, v, bitOffset);
|
|
bitOffset += DL.getTypeSizeInBits(v->getType());
|
|
}
|
|
}
|
|
|
|
EnumPayload EnumPayload::unpackFromEnumPayload(IRGenFunction &IGF,
|
|
const EnumPayload &outerPayload,
|
|
unsigned bitOffset,
|
|
EnumPayloadSchema schema) {
|
|
EnumPayload result;
|
|
auto &DL = IGF.IGM.DataLayout;
|
|
schema.forEachType(IGF.IGM, [&](llvm::Type *type) {
|
|
auto v = outerPayload.extractValue(IGF, type, bitOffset);
|
|
result.PayloadValues.push_back(v);
|
|
bitOffset += DL.getTypeSizeInBits(type);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
static llvm::Type *getPayloadStorageType(IRGenModule &IGM,
|
|
const EnumPayload &payload) {
|
|
if (payload.StorageType)
|
|
return payload.StorageType;
|
|
|
|
if (payload.PayloadValues.size() == 1) {
|
|
payload.StorageType = getPayloadType(payload.PayloadValues.front());
|
|
return payload.StorageType;
|
|
}
|
|
|
|
SmallVector<llvm::Type *, 2> elementTypes;
|
|
for (auto value : payload.PayloadValues) {
|
|
elementTypes.push_back(getPayloadType(value));
|
|
}
|
|
|
|
payload.StorageType = llvm::StructType::get(IGM.getLLVMContext(),
|
|
elementTypes);
|
|
return payload.StorageType;
|
|
}
|
|
|
|
EnumPayload EnumPayload::load(IRGenFunction &IGF, Address address,
|
|
EnumPayloadSchema schema) {
|
|
EnumPayload result = EnumPayload::zero(IGF.IGM, schema);
|
|
if (result.PayloadValues.empty())
|
|
return result;
|
|
|
|
auto storageTy = getPayloadStorageType(IGF.IGM, result);
|
|
address = IGF.Builder.CreateElementBitCast(address, storageTy);
|
|
|
|
if (result.PayloadValues.size() == 1) {
|
|
result.PayloadValues.front() = IGF.Builder.CreateLoad(address);
|
|
} else {
|
|
Size offset(0);
|
|
for (unsigned i : indices(result.PayloadValues)) {
|
|
auto &value = result.PayloadValues[i];
|
|
auto member = IGF.Builder.CreateStructGEP(address, i, offset);
|
|
auto loadedValue = IGF.Builder.CreateLoad(member);
|
|
value = loadedValue;
|
|
offset += Size(IGF.IGM.DataLayout.getTypeAllocSize(loadedValue->getType()));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void EnumPayload::store(IRGenFunction &IGF, Address address) const {
|
|
if (PayloadValues.empty())
|
|
return;
|
|
|
|
auto storageTy = getPayloadStorageType(IGF.IGM, *this);
|
|
address = IGF.Builder.CreateElementBitCast(address, storageTy);
|
|
|
|
if (PayloadValues.size() == 1) {
|
|
IGF.Builder.CreateStore(forcePayloadValue(PayloadValues.front()), address);
|
|
return;
|
|
} else {
|
|
Size offset(0);
|
|
for (unsigned i : indices(PayloadValues)) {
|
|
auto &value = PayloadValues[i];
|
|
auto member = IGF.Builder.CreateStructGEP(address, i, offset);
|
|
auto valueToStore = forcePayloadValue(value);
|
|
IGF.Builder.CreateStore(valueToStore, member);
|
|
offset += Size(IGF.IGM.DataLayout
|
|
.getTypeAllocSize(valueToStore->getType()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnumPayload::emitSwitch(IRGenFunction &IGF,
|
|
const APInt &mask,
|
|
ArrayRef<std::pair<APInt, llvm::BasicBlock *>> cases,
|
|
SwitchDefaultDest dflt) const {
|
|
// If there's only one case to test, do a simple compare and branch.
|
|
if (cases.size() == 1) {
|
|
// If the default case is unreachable, don't bother branching at all.
|
|
if (dflt.getInt()) {
|
|
IGF.Builder.CreateBr(cases[0].second);
|
|
return;
|
|
}
|
|
|
|
auto *cmp = emitCompare(IGF, mask, cases[0].first);
|
|
IGF.Builder.CreateCondBr(cmp, cases[0].second, dflt.getPointer());
|
|
return;
|
|
}
|
|
|
|
if (mask.getBitWidth() > IGF.IGM.getPointerSize().getValueInBits() * 2) {
|
|
for (int index = 0, size = cases.size(); index < size; ++index) {
|
|
auto &c = cases[index];
|
|
auto *cmp = emitCompare(IGF, mask, c.first);
|
|
llvm::BasicBlock *elseBlock;
|
|
if (index < size - 1) {
|
|
elseBlock = IGF.createBasicBlock("");
|
|
} else {
|
|
elseBlock = dflt.getPointer();
|
|
}
|
|
IGF.Builder.CreateCondBr(cmp, c.second, elseBlock);
|
|
if (index < size - 1) {
|
|
IGF.Builder.emitBlock(elseBlock);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Otherwise emit a switch statement.
|
|
auto &C = IGF.IGM.getLLVMContext();
|
|
unsigned numBits = mask.popcount();
|
|
auto target = emitGatherSpareBits(IGF, SpareBitVector::fromAPInt(mask),
|
|
0, numBits);
|
|
auto swi = IGF.Builder.CreateSwitch(target, dflt.getPointer(), cases.size());
|
|
for (auto &c : cases) {
|
|
auto value = llvm::ConstantInt::get(C, gatherBits(mask, c.first));
|
|
swi->addCase(value, c.second);
|
|
}
|
|
assert(IGF.Builder.hasPostTerminatorIP());
|
|
}
|
|
|
|
llvm::Value *
|
|
EnumPayload::emitCompare(IRGenFunction &IGF,
|
|
const APInt &mask,
|
|
const APInt &value) const {
|
|
// Succeed trivially for an empty payload, or if the payload is masked
|
|
// out completely.
|
|
if (PayloadValues.empty() || mask == 0)
|
|
return llvm::ConstantInt::get(IGF.IGM.Int1Ty, 1U);
|
|
|
|
assert((~mask & value) == 0
|
|
&& "value has masked out bits set?!");
|
|
|
|
auto &DL = IGF.IGM.DataLayout;
|
|
auto valueReader = BitPatternReader(value, DL.isLittleEndian());
|
|
auto maskReader = BitPatternReader(mask, DL.isLittleEndian());
|
|
|
|
llvm::Value *condition = nullptr;
|
|
for (auto &pv : PayloadValues) {
|
|
auto v = forcePayloadValue(pv);
|
|
unsigned size = DL.getTypeSizeInBits(v->getType());
|
|
|
|
// Break off a piece of the mask and value.
|
|
auto maskPiece = maskReader.read(size);
|
|
auto valuePiece = valueReader.read(size);
|
|
|
|
// If this piece is zero, it doesn't affect the comparison.
|
|
if (maskPiece == 0)
|
|
continue;
|
|
|
|
// Apply the mask and test.
|
|
bool isMasked = !maskPiece.isAllOnes();
|
|
auto intTy = llvm::IntegerType::get(IGF.IGM.getLLVMContext(), size);
|
|
// Need to bitcast to an integer in order to use 'icmp eq' if the piece
|
|
// isn't already an int or pointer, or in order to apply a mask.
|
|
if (isMasked
|
|
|| (!isa<llvm::IntegerType>(v->getType())
|
|
&& !isa<llvm::PointerType>(v->getType())))
|
|
v = IGF.Builder.CreateBitOrPointerCast(v, intTy);
|
|
|
|
if (isMasked) {
|
|
auto maskConstant = llvm::ConstantInt::get(intTy, maskPiece);
|
|
v = IGF.Builder.CreateAnd(v, maskConstant);
|
|
}
|
|
|
|
llvm::Value *valueConstant = llvm::ConstantInt::get(intTy, valuePiece);
|
|
valueConstant = IGF.Builder.CreateBitOrPointerCast(valueConstant,
|
|
v->getType());
|
|
auto cmp = IGF.Builder.CreateICmpEQ(v, valueConstant);
|
|
if (!condition)
|
|
condition = cmp;
|
|
else
|
|
condition = IGF.Builder.CreateAnd(condition, cmp);
|
|
}
|
|
|
|
// We should have handled the cases where there are no significant conditions
|
|
// in the early exit.
|
|
assert(condition && "no significant condition?!");
|
|
return condition;
|
|
}
|
|
|
|
void
|
|
EnumPayload::emitApplyAndMask(IRGenFunction &IGF, const APInt &mask) {
|
|
// Early exit if the mask has no effect.
|
|
if (mask.isAllOnes())
|
|
return;
|
|
|
|
auto &DL = IGF.IGM.DataLayout;
|
|
auto maskReader = BitPatternReader(mask, DL.isLittleEndian());
|
|
|
|
for (auto &pv : PayloadValues) {
|
|
auto payloadTy = getPayloadType(pv);
|
|
unsigned size = DL.getTypeSizeInBits(payloadTy);
|
|
|
|
// Read a chunk of the mask.
|
|
auto maskPiece = maskReader.read(size);
|
|
|
|
// If this piece is all ones, it has no effect.
|
|
if (maskPiece.isAllOnes())
|
|
continue;
|
|
|
|
// If the payload value is vacant, the mask can't change it.
|
|
if (isa<llvm::Type *>(pv))
|
|
continue;
|
|
|
|
// If this piece is zero, it wipes out the chunk entirely, and we can
|
|
// drop it.
|
|
if (maskPiece == 0) {
|
|
pv = payloadTy;
|
|
continue;
|
|
}
|
|
|
|
// Otherwise, apply the mask to the existing value.
|
|
auto v = cast<llvm::Value *>(pv);
|
|
auto payloadIntTy = llvm::IntegerType::get(IGF.IGM.getLLVMContext(), size);
|
|
auto maskConstant = llvm::ConstantInt::get(payloadIntTy, maskPiece);
|
|
v = IGF.Builder.CreateBitOrPointerCast(v, payloadIntTy);
|
|
v = IGF.Builder.CreateAnd(v, maskConstant);
|
|
v = IGF.Builder.CreateBitOrPointerCast(v, payloadTy);
|
|
pv = v;
|
|
}
|
|
}
|
|
|
|
void
|
|
EnumPayload::emitApplyOrMask(IRGenModule &IGM,
|
|
IRBuilder &builder, const APInt &mask) {
|
|
// Early exit if the mask has no effect.
|
|
if (mask == 0)
|
|
return;
|
|
|
|
auto &DL = IGM.DataLayout;
|
|
auto maskReader = BitPatternReader(mask, DL.isLittleEndian());
|
|
|
|
for (auto &pv : PayloadValues) {
|
|
auto payloadTy = getPayloadType(pv);
|
|
unsigned size = DL.getTypeSizeInBits(payloadTy);
|
|
|
|
// Read a chunk of the mask.
|
|
auto maskPiece = maskReader.read(size);
|
|
|
|
// If this piece is zero, it has no effect.
|
|
if (maskPiece == 0)
|
|
continue;
|
|
|
|
auto payloadIntTy = llvm::IntegerType::get(IGM.getLLVMContext(), size);
|
|
auto maskConstant = llvm::ConstantInt::get(payloadIntTy, maskPiece);
|
|
|
|
// If the payload value is vacant, or the mask is all ones,
|
|
// we can adopt the mask value directly.
|
|
if (isa<llvm::Type *>(pv) || maskPiece.isAllOnes()) {
|
|
pv = builder.CreateBitOrPointerCast(maskConstant, payloadTy);
|
|
continue;
|
|
}
|
|
|
|
// Otherwise, apply the mask to the existing value.
|
|
auto v = cast<llvm::Value *>(pv);
|
|
v = builder.CreateBitOrPointerCast(v, payloadIntTy);
|
|
v = builder.CreateOr(v, maskConstant);
|
|
v = builder.CreateBitOrPointerCast(v, payloadTy);
|
|
pv = v;
|
|
}
|
|
}
|
|
|
|
void
|
|
EnumPayload::emitApplyOrMask(IRGenFunction &IGF,
|
|
EnumPayload mask) {
|
|
unsigned count = PayloadValues.size();
|
|
assert(count == mask.PayloadValues.size());
|
|
|
|
auto &DL = IGF.IGM.DataLayout;
|
|
for (unsigned i = 0; i < count; ++i) {
|
|
auto payloadTy = getPayloadType(PayloadValues[i]);
|
|
unsigned size = DL.getTypeSizeInBits(payloadTy);
|
|
|
|
auto payloadIntTy = llvm::IntegerType::get(IGF.IGM.getLLVMContext(), size);
|
|
|
|
if (isa<llvm::Type *>(mask.PayloadValues[i])) {
|
|
// We're ORing with zero, do nothing
|
|
} else if (isa<llvm::Type *>(PayloadValues[i])) {
|
|
PayloadValues[i] = mask.PayloadValues[i];
|
|
} else {
|
|
auto v1 = IGF.Builder.CreateBitOrPointerCast(
|
|
cast<llvm::Value *>(PayloadValues[i]), payloadIntTy);
|
|
|
|
auto v2 = IGF.Builder.CreateBitOrPointerCast(
|
|
cast<llvm::Value *>(mask.PayloadValues[i]), payloadIntTy);
|
|
|
|
PayloadValues[i] = IGF.Builder.CreateBitOrPointerCast(
|
|
IGF.Builder.CreateOr(v1, v2),
|
|
payloadTy);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnumPayload::emitScatterBits(IRGenModule &IGM,
|
|
IRBuilder &builder,
|
|
const APInt &mask,
|
|
llvm::Value *value) {
|
|
auto &DL = IGM.DataLayout;
|
|
|
|
unsigned valueBits = DL.getTypeSizeInBits(value->getType());
|
|
auto totalBits = std::min(valueBits, mask.popcount());
|
|
auto maskReader = BitPatternReader(getLowestNSetBits(mask, totalBits),
|
|
DL.isLittleEndian());
|
|
auto usedBits = 0u;
|
|
for (auto &pv : PayloadValues) {
|
|
auto partType = getPayloadType(pv);
|
|
auto partSize = DL.getTypeSizeInBits(partType);
|
|
auto partMask = maskReader.read(partSize);
|
|
|
|
// Skip this element if there are no set bits in the mask.
|
|
if (partMask == 0) {
|
|
continue;
|
|
}
|
|
|
|
// Calculate the number of bits we are going to scatter.
|
|
auto partCount = partMask.popcount();
|
|
|
|
// Scatter bits from the source into the bits specified by the mask.
|
|
auto offset = usedBits;
|
|
if (DL.isBigEndian()) {
|
|
offset = totalBits - partCount - usedBits;
|
|
}
|
|
auto partValue = irgen::emitScatterBits(IGM, builder, partMask, value, offset);
|
|
|
|
// If necessary OR with the existing value.
|
|
if (auto existingValue = pv.dyn_cast<llvm::Value*>()) {
|
|
if (partType != partValue->getType()) {
|
|
existingValue = builder.CreateBitOrPointerCast(existingValue,
|
|
partValue->getType());
|
|
}
|
|
partValue = builder.CreateOr(partValue, existingValue);
|
|
}
|
|
|
|
// Convert the integer result to the target type.
|
|
if (partType != partValue->getType()) {
|
|
partValue = builder.CreateBitOrPointerCast(partValue, partType);
|
|
}
|
|
|
|
// Update this payload element.
|
|
pv = partValue;
|
|
|
|
// Update our position in the source integer.
|
|
usedBits += partCount;
|
|
if (usedBits >= totalBits) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
llvm::Value *
|
|
EnumPayload::emitGatherSpareBits(IRGenFunction &IGF,
|
|
const SpareBitVector &spareBits,
|
|
unsigned firstBitOffset,
|
|
unsigned resultBitWidth) const {
|
|
auto &DL = IGF.IGM.DataLayout;
|
|
auto &C = IGF.IGM.getLLVMContext();
|
|
|
|
auto mask = getLowestNSetBits(spareBits.asAPInt(),
|
|
resultBitWidth - firstBitOffset);
|
|
auto bitWidth = mask.popcount();
|
|
auto spareBitReader = BitPatternReader(std::move(mask),
|
|
DL.isLittleEndian());
|
|
auto usedBits = firstBitOffset;
|
|
|
|
llvm::Value *spareBitValue = nullptr;
|
|
for (auto &pv : PayloadValues) {
|
|
// If this value is zero, it has nothing to add to the spare bits.
|
|
auto v = pv.dyn_cast<llvm::Value*>();
|
|
if (!v) {
|
|
spareBitReader.skip(DL.getTypeSizeInBits(cast<llvm::Type *>(pv)));
|
|
continue;
|
|
}
|
|
|
|
// Slice the spare bit vector.
|
|
unsigned size = DL.getTypeSizeInBits(v->getType());
|
|
auto spareBitsPart = spareBitReader.read(size);
|
|
unsigned numBitsInPart = spareBitsPart.popcount();
|
|
|
|
// If there were no spare bits in this part, it has nothing to add.
|
|
if (numBitsInPart == 0)
|
|
continue;
|
|
|
|
if (usedBits >= bitWidth)
|
|
break;
|
|
|
|
unsigned offset = usedBits;
|
|
if (DL.isBigEndian()) {
|
|
offset = bitWidth - usedBits - numBitsInPart;
|
|
}
|
|
// Get the spare bits from this part.
|
|
auto bits = irgen::emitGatherBits(IGF, spareBitsPart,
|
|
v, offset, resultBitWidth);
|
|
usedBits += numBitsInPart;
|
|
|
|
// Accumulate it into the full set.
|
|
if (spareBitValue) {
|
|
bits = IGF.Builder.CreateOr(spareBitValue, bits);
|
|
}
|
|
spareBitValue = bits;
|
|
}
|
|
auto destTy = llvm::IntegerType::get(C, resultBitWidth);
|
|
if (spareBitValue) {
|
|
assert(spareBitValue->getType() == destTy);
|
|
return spareBitValue;
|
|
}
|
|
return llvm::ConstantInt::get(destTy, 0);
|
|
}
|
|
|
|
unsigned EnumPayload::getAllocSizeInBits(const llvm::DataLayout &DL) const {
|
|
unsigned size = 0u;
|
|
for (const auto &pv : PayloadValues) {
|
|
size += DL.getTypeAllocSizeInBits(getPayloadType(pv));
|
|
assert(size % 8 == 0 && "allocation size must be a multiple of bytes");
|
|
}
|
|
return size;
|
|
}
|
|
|
|
void EnumPayload::print(llvm::raw_ostream &OS) {
|
|
if (StorageType) {
|
|
OS << "storage-type: ";
|
|
StorageType->print(OS);
|
|
OS << '\n';
|
|
}
|
|
for (LazyValue pv : PayloadValues) {
|
|
if (auto *v = pv.dyn_cast<llvm::Value*>()) {
|
|
OS << "value: ";
|
|
v->print(OS);
|
|
OS << '\n';
|
|
} else {
|
|
auto *t = cast<llvm::Type *>(pv);
|
|
OS << "type: ";
|
|
t->print(OS);
|
|
OS << '\n';
|
|
}
|
|
}
|
|
}
|
|
|
|
void EnumPayload::dump() {
|
|
print(llvm::errs());
|
|
}
|
|
|