Skip to content

Commit 0797d66

Browse files
authored
Merge pull request #68516 from eeckstein/fix-static-enum
IRGen: fix emission of constant single-case enums
2 parents 1c0ccf6 + 9cf0229 commit 0797d66

File tree

5 files changed

+99
-3
lines changed

5 files changed

+99
-3
lines changed

lib/IRGen/GenConstant.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,11 @@ Explosion irgen::emitConstantValue(IRGenModule &IGM, SILValue operand,
273273
irgen::getPhysicalTupleElementStructIndex,
274274
flatten);
275275
} else if (auto *ei = dyn_cast<EnumInst>(operand)) {
276+
auto &strategy = getEnumImplStrategy(IGM, ei->getType());
277+
if (strategy.emitPayloadDirectlyIntoConstant()) {
278+
return emitConstantValue(IGM, ei->getOperand(), flatten);
279+
}
280+
276281
Explosion data;
277282
if (ei->hasOperand()) {
278283
data = emitConstantValue(IGM, ei->getOperand(), /*flatten=*/ true);
@@ -282,10 +287,9 @@ Explosion irgen::emitConstantValue(IRGenModule &IGM, SILValue operand,
282287
// arguments to the enum are constant, the builder never has to emit an
283288
// instruction. Instead it can constant fold everything and just returns
284289
// the final constant.
285-
Explosion out;
286290
IRBuilder builder(IGM.getLLVMContext(), false);
287-
getEnumImplStrategy(IGM, ei->getType())
288-
.emitValueInjection(IGM, builder, ei->getElement(), data, out);
291+
Explosion out;
292+
strategy.emitValueInjection(IGM, builder, ei->getElement(), data, out);
289293
return replaceUnalignedIntegerValues(IGM, std::move(out));
290294
} else if (auto *ILI = dyn_cast<IntegerLiteralInst>(operand)) {
291295
return emitConstantInt(IGM, ILI);

lib/IRGen/GenEnum.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ namespace {
425425
getLoadableSingleton()->reexplode(params, out);
426426
}
427427

428+
bool emitPayloadDirectlyIntoConstant() const override { return true; }
429+
428430
void destructiveProjectDataForLoad(IRGenFunction &IGF,
429431
SILType T,
430432
Address enumAddr) const override {

lib/IRGen/GenEnum.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,11 @@ class EnumImplStrategy {
334334
Explosion &params,
335335
Explosion &out) const = 0;
336336

337+
/// Return true for single-case (singleton) enums.
338+
/// The enum doesn't need any tag bits and the payload of the enum case can
339+
/// (and needs!) to be emitted as-is into a constant.
340+
virtual bool emitPayloadDirectlyIntoConstant() const { return false; }
341+
337342
/// Return an i1 value that indicates whether the specified loadable enum
338343
/// value holds the specified case. This is a light-weight form of a switch.
339344
virtual llvm::Value *emitValueCaseTest(IRGenFunction &IGF,

test/SILOptimizer/static_enums.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,12 @@ func printFunctionEnum() {
218218
}
219219
}
220220

221+
enum SingleCaseEnum {
222+
case a(b: Bool, i: Int)
223+
224+
static var x = Self.a(b:true, i: 42)
225+
}
226+
221227
@main
222228
struct Main {
223229
static func main() {
@@ -293,6 +299,8 @@ struct Main {
293299
print("optSalmon:", optSalmon as Any)
294300
// CHECK-OUTPUT: test.C: 27
295301
printFunctionEnum()
302+
// CHECK-OUTPUT: SingleCaseEnum: a(b: true, i: 42)
303+
print("SingleCaseEnum:", SingleCaseEnum.x)
296304
}
297305
}
298306

validation-test/SILOptimizer/static_enums_fuzzing.swift

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ var typeDefinitions: String {
7373
case X, Y, Z
7474
}
7575
76+
public enum SPSCE<T> {
77+
case A(T)
78+
}
79+
80+
public enum SPE<T> {
81+
case A(T)
82+
case B
83+
}
84+
7685
public enum MPE<T, V> {
7786
case A(T)
7887
case B(V)
@@ -285,6 +294,72 @@ struct Enum : Value {
285294
}
286295
}
287296

297+
struct SinglePayloadSingleCaseEnum : Value {
298+
let payload: any Value
299+
300+
init(generator: inout RandomGenerator, depth: Int) {
301+
self.payload = generator.createValue(depth: depth)
302+
}
303+
304+
func getType() -> String {
305+
"SPSCE<\(payload.getType())>"
306+
}
307+
308+
func getInitValue() -> String {
309+
return "SPSCE.A(\(payload.getInitValue()))"
310+
}
311+
312+
func getExpectedOutput(topLevel: Bool) -> String {
313+
let prefix = topLevel ? "" : "\(getRuntimeTypeName(topLevel: topLevel))."
314+
return "\(prefix)A(\(payload.getExpectedOutput(topLevel: false)))"
315+
}
316+
317+
func getRuntimeTypeName(topLevel: Bool) -> String {
318+
let prefix = topLevel ? "" : "test."
319+
return "\(prefix)SPSCE<\(payload.getRuntimeTypeName(topLevel: topLevel))>"
320+
}
321+
322+
var containsEnum: Bool { true }
323+
}
324+
325+
struct SinglePayloadEnum : Value {
326+
let payload: any Value
327+
let caseIdx: Int
328+
329+
init(generator: inout RandomGenerator, depth: Int) {
330+
self.caseIdx = Int.random(in: 0..<2, using: &generator)
331+
self.payload = generator.createValue(depth: depth)
332+
}
333+
334+
func getType() -> String {
335+
"SPE<\(payload.getType())>"
336+
}
337+
338+
func getInitValue() -> String {
339+
switch caseIdx {
340+
case 0: return "SPE.A(\(payload.getInitValue()))"
341+
case 1: return "SPE.B"
342+
default: fatalError()
343+
}
344+
}
345+
346+
func getExpectedOutput(topLevel: Bool) -> String {
347+
let prefix = topLevel ? "" : "\(getRuntimeTypeName(topLevel: topLevel))."
348+
switch caseIdx {
349+
case 0: return "\(prefix)A(\(payload.getExpectedOutput(topLevel: false)))"
350+
case 1: return "\(prefix)B"
351+
default: fatalError()
352+
}
353+
}
354+
355+
func getRuntimeTypeName(topLevel: Bool) -> String {
356+
let prefix = topLevel ? "" : "test."
357+
return "\(prefix)SPE<\(payload.getRuntimeTypeName(topLevel: topLevel))>"
358+
}
359+
360+
var containsEnum: Bool { true }
361+
}
362+
288363
struct MultiPayloadEnum : Value {
289364
let payloadA: any Value
290365
let payloadB: any Value
@@ -407,6 +482,8 @@ struct RandomGenerator : RandomNumberGenerator {
407482
private static let allValueTypes: [any Value.Type] = allTerminalTypes + [
408483
OptionalValue.self,
409484
Struct.self,
485+
SinglePayloadSingleCaseEnum.self,
486+
SinglePayloadEnum.self,
410487
MultiPayloadEnum.self
411488
]
412489

0 commit comments

Comments
 (0)