Files
swift-mirror/lib/IRGen/EnumPayload.h
Arnold Schwaighofer f538dd7075 Fix Enums with multiple payloads of < 32bit and empty payloads.
This used to crash because the code storing empty payload enum tag values would
use the bit width of the tag (32 bit) as the minimum unit to store to the
payload even if the actual bits required to store the biggest tag value in the
payload was much smaller.

With payload bit-widths < 32bit we would run out of space crashing looking for
new payload to store the value to ...

Instead pass the maximum size of the bits that need storing down.

rdar://26926035
2016-07-08 11:21:30 -07:00

189 lines
6.5 KiB
C++

//===--- EnumPayload.h - Payload management for 'enum' Types ----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_IRGEN_ENUMPAYLOAD_H
#define SWIFT_IRGEN_ENUMPAYLOAD_H
#include "IRGenModule.h"
#include "Explosion.h"
#include "TypeInfo.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/ADT/PointerEmbeddedInt.h"
#include "llvm/ADT/PointerUnion.h"
#include <utility>
namespace swift {
namespace irgen {
/// A description of how to represent an enum payload as a value.
/// A payload can either use a generic word-chunked representation, or attempt
/// to follow the explosion schema of one of its payload types.
class EnumPayloadSchema {
using BitSizeTy = llvm::PointerEmbeddedInt<unsigned, 31>;
const llvm::PointerUnion<ExplosionSchema *, BitSizeTy> Value;
public:
EnumPayloadSchema() : Value((ExplosionSchema *)nullptr) {}
explicit operator bool() {
return Value.getOpaqueValue() != nullptr;
}
explicit EnumPayloadSchema(unsigned bits)
: Value(BitSizeTy(bits)) {}
EnumPayloadSchema(ExplosionSchema &s)
: Value(&s) {}
static EnumPayloadSchema withBitSize(unsigned bits) {
return EnumPayloadSchema(bits);
}
ExplosionSchema *getSchema() const {
return Value.dyn_cast<ExplosionSchema*>();
}
/// Invoke a functor for each element type in the schema.
template<typename TypeFn /* void(llvm::Type *schemaType) */>
void forEachType(IRGenModule &IGM, TypeFn &&fn) const {
// Follow an explosion schema if we have one.
if (auto *explosion = Value.dyn_cast<ExplosionSchema *>()) {
for (auto &element : *explosion) {
auto type = element.getScalarType();
assert(IGM.DataLayout.getTypeSizeInBits(type)
== IGM.DataLayout.getTypeAllocSizeInBits(type)
&& "enum payload schema elements should use full alloc size");
(void) type;
fn(element.getScalarType());
}
return;
}
// Otherwise, chunk into pointer-sized integer values by default.
unsigned bitSize = Value.get<BitSizeTy>();
unsigned pointerSize = IGM.getPointerSize().getValueInBits();
while (bitSize >= pointerSize) {
fn(IGM.SizeTy);
bitSize -= pointerSize;
}
if (bitSize > 0)
fn(llvm::IntegerType::get(IGM.getLLVMContext(), bitSize));
}
};
/// Is a switch default destination unreachable?
enum IsUnreachable_t: bool {
IsNotUnreachable = false,
IsUnreachable = true,
};
using SwitchDefaultDest
= llvm::PointerIntPair<llvm::BasicBlock*, 1, IsUnreachable_t>;
/// An enum payload value. The payload is represented as an explosion of
/// integers and pointers that together represent the bit pattern of
/// the payload.
class EnumPayload {
public:
/// A value, or the type of a zero value in the payload.
using LazyValue = llvm::PointerUnion<llvm::Value *, llvm::Type *>;
mutable SmallVector<LazyValue, 2> PayloadValues;
mutable llvm::Type *StorageType = nullptr;
EnumPayload() = default;
/// Generate a "zero" enum payload.
static EnumPayload zero(IRGenModule &IGM,
EnumPayloadSchema schema);
/// Generate an enum payload containing the given bit pattern.
static EnumPayload fromBitPattern(IRGenModule &IGM,
APInt bitPattern,
EnumPayloadSchema schema);
/// Insert a value into the enum payload.
///
/// The current payload value at the given offset is assumed to be zero.
/// If \p numBitsUsedInValue is non-negative denotes the actual number of bits
/// that need storing in \p value otherwise the full bit-width of \p value
/// will be stored.
void insertValue(IRGenFunction &IGF,
llvm::Value *value, unsigned bitOffset,
int numBitsUsedInValue = -1);
/// Extract a value from the enum payload.
llvm::Value *extractValue(IRGenFunction &IGF,
llvm::Type *type, unsigned bitOffset) const;
/// Take an enum payload out of an explosion.
static EnumPayload fromExplosion(IRGenModule &IGM,
Explosion &in,
EnumPayloadSchema schema);
/// Add the payload to an explosion.
void explode(IRGenModule &IGM, Explosion &out) const;
/// Pack into another enum payload.
void packIntoEnumPayload(IRGenFunction &IGF,
EnumPayload &dest,
unsigned bitOffset) const;
/// Unpack from another enum payload.
static EnumPayload unpackFromEnumPayload(IRGenFunction &IGF,
const EnumPayload &src,
unsigned bitOffset,
EnumPayloadSchema schema);
/// Load an enum payload from memory.
static EnumPayload load(IRGenFunction &IGF, Address address,
EnumPayloadSchema schema);
/// Store an enum payload to memory.
void store(IRGenFunction &IGF, Address address) const;
/// Emit a switch over specific bit patterns for the payload.
/// The value will be tested as if AND-ed against the given mask.
void emitSwitch(IRGenFunction &IGF,
APInt mask,
ArrayRef<std::pair<APInt, llvm::BasicBlock*>> cases,
SwitchDefaultDest dflt) const;
/// Emit an equality comparison operation that payload & mask == value.
llvm::Value *emitCompare(IRGenFunction &IGF,
APInt mask,
APInt value) const;
/// Apply an AND mask to the payload.
void emitApplyAndMask(IRGenFunction &IGF, APInt mask);
/// Apply an OR mask to the payload.
void emitApplyOrMask(IRGenFunction &IGF, APInt mask);
/// Apply an OR mask to the payload.
void emitApplyOrMask(IRGenFunction &IGF, EnumPayload mask);
/// Gather bits from an enum payload based on a spare bit mask.
llvm::Value *emitGatherSpareBits(IRGenFunction &IGF,
const SpareBitVector &spareBits,
unsigned firstBitOffset,
unsigned bitWidth) const;
};
}
}
#endif