Merge pull request #68516 from eeckstein/fix-static-enum

IRGen: fix emission of constant single-case enums
This commit is contained in:
eeckstein
2023-09-14 18:43:48 +02:00
committed by GitHub
5 changed files with 99 additions and 3 deletions

View File

@@ -273,6 +273,11 @@ Explosion irgen::emitConstantValue(IRGenModule &IGM, SILValue operand,
irgen::getPhysicalTupleElementStructIndex,
flatten);
} else if (auto *ei = dyn_cast<EnumInst>(operand)) {
auto &strategy = getEnumImplStrategy(IGM, ei->getType());
if (strategy.emitPayloadDirectlyIntoConstant()) {
return emitConstantValue(IGM, ei->getOperand(), flatten);
}
Explosion data;
if (ei->hasOperand()) {
data = emitConstantValue(IGM, ei->getOperand(), /*flatten=*/ true);
@@ -282,10 +287,9 @@ Explosion irgen::emitConstantValue(IRGenModule &IGM, SILValue operand,
// arguments to the enum are constant, the builder never has to emit an
// instruction. Instead it can constant fold everything and just returns
// the final constant.
Explosion out;
IRBuilder builder(IGM.getLLVMContext(), false);
getEnumImplStrategy(IGM, ei->getType())
.emitValueInjection(IGM, builder, ei->getElement(), data, out);
Explosion out;
strategy.emitValueInjection(IGM, builder, ei->getElement(), data, out);
return replaceUnalignedIntegerValues(IGM, std::move(out));
} else if (auto *ILI = dyn_cast<IntegerLiteralInst>(operand)) {
return emitConstantInt(IGM, ILI);

View File

@@ -425,6 +425,8 @@ namespace {
getLoadableSingleton()->reexplode(params, out);
}
bool emitPayloadDirectlyIntoConstant() const override { return true; }
void destructiveProjectDataForLoad(IRGenFunction &IGF,
SILType T,
Address enumAddr) const override {

View File

@@ -334,6 +334,11 @@ public:
Explosion &params,
Explosion &out) const = 0;
/// Return true for single-case (singleton) enums.
/// The enum doesn't need any tag bits and the payload of the enum case can
/// (and needs!) to be emitted as-is into a constant.
virtual bool emitPayloadDirectlyIntoConstant() const { return false; }
/// Return an i1 value that indicates whether the specified loadable enum
/// value holds the specified case. This is a light-weight form of a switch.
virtual llvm::Value *emitValueCaseTest(IRGenFunction &IGF,

View File

@@ -218,6 +218,12 @@ func printFunctionEnum() {
}
}
enum SingleCaseEnum {
case a(b: Bool, i: Int)
static var x = Self.a(b:true, i: 42)
}
@main
struct Main {
static func main() {
@@ -293,6 +299,8 @@ struct Main {
print("optSalmon:", optSalmon as Any)
// CHECK-OUTPUT: test.C: 27
printFunctionEnum()
// CHECK-OUTPUT: SingleCaseEnum: a(b: true, i: 42)
print("SingleCaseEnum:", SingleCaseEnum.x)
}
}

View File

@@ -73,6 +73,15 @@ var typeDefinitions: String {
case X, Y, Z
}
public enum SPSCE<T> {
case A(T)
}
public enum SPE<T> {
case A(T)
case B
}
public enum MPE<T, V> {
case A(T)
case B(V)
@@ -285,6 +294,72 @@ struct Enum : Value {
}
}
struct SinglePayloadSingleCaseEnum : Value {
let payload: any Value
init(generator: inout RandomGenerator, depth: Int) {
self.payload = generator.createValue(depth: depth)
}
func getType() -> String {
"SPSCE<\(payload.getType())>"
}
func getInitValue() -> String {
return "SPSCE.A(\(payload.getInitValue()))"
}
func getExpectedOutput(topLevel: Bool) -> String {
let prefix = topLevel ? "" : "\(getRuntimeTypeName(topLevel: topLevel))."
return "\(prefix)A(\(payload.getExpectedOutput(topLevel: false)))"
}
func getRuntimeTypeName(topLevel: Bool) -> String {
let prefix = topLevel ? "" : "test."
return "\(prefix)SPSCE<\(payload.getRuntimeTypeName(topLevel: topLevel))>"
}
var containsEnum: Bool { true }
}
struct SinglePayloadEnum : Value {
let payload: any Value
let caseIdx: Int
init(generator: inout RandomGenerator, depth: Int) {
self.caseIdx = Int.random(in: 0..<2, using: &generator)
self.payload = generator.createValue(depth: depth)
}
func getType() -> String {
"SPE<\(payload.getType())>"
}
func getInitValue() -> String {
switch caseIdx {
case 0: return "SPE.A(\(payload.getInitValue()))"
case 1: return "SPE.B"
default: fatalError()
}
}
func getExpectedOutput(topLevel: Bool) -> String {
let prefix = topLevel ? "" : "\(getRuntimeTypeName(topLevel: topLevel))."
switch caseIdx {
case 0: return "\(prefix)A(\(payload.getExpectedOutput(topLevel: false)))"
case 1: return "\(prefix)B"
default: fatalError()
}
}
func getRuntimeTypeName(topLevel: Bool) -> String {
let prefix = topLevel ? "" : "test."
return "\(prefix)SPE<\(payload.getRuntimeTypeName(topLevel: topLevel))>"
}
var containsEnum: Bool { true }
}
struct MultiPayloadEnum : Value {
let payloadA: any Value
let payloadB: any Value
@@ -407,6 +482,8 @@ struct RandomGenerator : RandomNumberGenerator {
private static let allValueTypes: [any Value.Type] = allTerminalTypes + [
OptionalValue.self,
Struct.self,
SinglePayloadSingleCaseEnum.self,
SinglePayloadEnum.self,
MultiPayloadEnum.self
]