mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #68516 from eeckstein/fix-static-enum
IRGen: fix emission of constant single-case enums
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -334,6 +334,11 @@ public:
|
||||
Explosion ¶ms,
|
||||
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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user