SILGen: revise emission of UnreachableExpr

The prior emission strategy produced some scary SIL where
we have a copy of uninitialized memory:

```
  ignored_use %14
  %16 = alloc_stack $T
  unreachable

bb1:
  copy_addr [take] %16 to [init] %0
  dealloc_stack %16
```

I assume this was done to dodge the SILVerifier, which
will catch a take of an uninitialized address, had the
alloc_stack been in any reachable predecessor.

We already have a representation for an undefined value
in SIL, so I'd rather use that than try to sneak one
past the verifier.

This adjusted emission also is opaque values friendly,
as it doesn't assume the result value is an address.

resolves rdar://162239557
This commit is contained in:
Kavon Farvardin
2025-10-08 17:34:09 -07:00
parent cc8f060555
commit 1e28b0a312
3 changed files with 51 additions and 9 deletions

View File

@@ -2606,19 +2606,31 @@ RValue RValueEmitter::visitUnderlyingToOpaqueExpr(UnderlyingToOpaqueExpr *E,
}
RValue RValueEmitter::visitUnreachableExpr(UnreachableExpr *E, SGFContext C) {
// Emit the expression, followed by an unreachable. To produce a value of
// arbitrary type, we emit a temporary allocation, with the use of the
// allocation in the unreachable block. The SILOptimizer will eliminate both
// the unreachable block and unused allocation.
// Emit the expression, followed by an unreachable.
SGF.emitIgnoredExpr(E->getSubExpr());
auto &lowering = SGF.getTypeLowering(E->getType());
auto resultAddr = SGF.emitTemporaryAllocation(E, lowering.getLoweredType());
SGF.B.createUnreachable(E);
// Continue code generation in a block with no predecessors.
// Whatever code is emitted here is guaranteed to be removed by SIL passes.
SGF.B.emitBlock(SGF.createBasicBlock());
return RValue(SGF, E, SGF.emitManagedRValueWithCleanup(resultAddr));
// Since the type is uninhabited, use a SILUndef of so that we can return
// some sort of RValue from this API.
auto &lowering = SGF.getTypeLowering(E->getType());
auto loweredTy = lowering.getLoweredType();
auto undef = SILUndef::get(SGF.F, loweredTy);
// Create an alloc initialized with contents from the undefined addr type.
// It seems pack addresses do not satisfy isPlusOneOrTrivial, so we need an
// actual allocation.
if (loweredTy.isAddress()) {
auto resultAddr = SGF.emitTemporaryAllocation(E, loweredTy);
SGF.emitSemanticStore(E, undef, resultAddr, lowering, IsInitialization);
return RValue(SGF, E, SGF.emitManagedRValueWithCleanup(resultAddr));
}
// Otherwise, if it's not an address, just emit the undef value itself.
return RValue(SGF, E, ManagedValue::forRValueWithoutOwnership(undef));
}
VarargsInfo Lowering::emitBeginVarargs(SILGenFunction &SGF, SILLocation loc,

View File

@@ -0,0 +1,20 @@
// RUN: %target-swift-emit-silgen %s -verify -sil-verify-all | %FileCheck %s --check-prefixes CHECK,REG
// RUN: %target-swift-emit-silgen %s -verify -sil-verify-all -enable-sil-opaque-values | %FileCheck %s --check-prefixes CHECK,OV
// CHECK-LABEL: sil{{.*}} [ossa] @{{.*}}uninhabited_generic{{.*}}
// CHECK: [[NEVER:%[^,]+]] = apply {{.*}} -> Never
// CHECK-NEXT: ignored_use [[NEVER]]
// CHECK-NEXT: unreachable
//
// CHECK: bb1:
// CHECK-NOT: Preds
// Without opaque values, take from an undef address,
// through a temporary alloc, to eventually the out-parameter %0
// REG-NEXT: [[BOGUS_ALLOC:%.*]] = alloc_stack $T
// REG-NEXT: copy_addr [take] undef to [init] [[BOGUS_ALLOC]]
// REG-NEXT: copy_addr [take] [[BOGUS_ALLOC]] to [init] %0
// With opaque values, simply return the undef value
// OV-NEXT: return undef : $T
func uninhabited_generic<T>() -> T { fatalError("todo") }

View File

@@ -430,6 +430,16 @@ func convertVoidPayloads() {
convertPayloads(as: Void.self)
}
// CHECK-LABEL: sil{{.*}} [ossa] @{{.*}}convertPayloads{{.*}} :
// CHECK: ignored_use {{.*}}
// CHECK-NEXT: unreachable
//
// CHECK: bb1:
// CHECK-NOT: Preds
// CHECK: [[BOGUS_ALLOC:%.*]] = alloc_stack $(repeat each Value)
// CHECK-NEXT: copy_addr [take] undef to [init] [[BOGUS_ALLOC]]
//
// CHECK: = tuple_pack_element_addr {{.*}} of [[BOGUS_ALLOC]]
func convertPayloads<each Value>(as valueTypes: repeat (each Value).Type) -> (repeat each Value) {
fatalError()
}