Status: Root cause identified via direct live backtrace evidence. Not fixable
from a swift-corelibs-foundation, swift-corelibs-libdispatch, or ports-level
patch — the actual gap is in Swift LLVM's PowerPC target backend (llvm/lib/Target/PowerPC/).
Target: swift-6.3.2, powerpc64-unknown-freebsd14.4. Discovered while
building sourcekit-lsp (Stage 5), specifically its DocumentationLanguageService
module. Confirmed to reproduce with a 20-line, fully isolated, dependency-free
minimal repro (included below) — not specific to sourcekit-lsp's actual code in any way.
1. Symptom
Two distinct symptoms of the same underlying cause, depending on
optimization level and exact code shape:
- In
sourcekit-lsp's real build: an LLVM IR verifier failure —
error: fatal error encountered during compilation; please submit a bug report / note: Broken module found, compilation aborted! — with
visibly inconsistent operand types in the dumped IR (a value of the
Swift enum's opaque type fed directly into icmp/ptrtoint, which
require raw pointer/integer operands, alongside a phi node elsewhere
expecting a plain ptr).
- In the isolated minimal repro (see §3): rather than failing fast,
swift-frontend instead hangs indefinitely, consuming 95-99% CPU
continuously for 30+ minutes on a 20-line source file with no sign of
terminating. (Both are almost certainly the same underlying LLVM
backend defect; which one manifests likely depends on which specific
code path gets hit for a given IR shape — see §6.)
2. Minimal reproduction
enum MyError: Error {
case wrapped(any Error)
case simple
case cancelled
}
func doThing(_ fail: Bool) async throws(MyError) -> Int {
if fail {
throw MyError.simple
}
return 42
}
func caller() async {
do {
let result = try await doThing(true)
print(result)
} catch {
print("caught: \(error)")
}
}
Compiled with: swiftc -c minimal_repro.swift -o minimal_repro.o (any
optimization level; reproduces at both default and -Onone).
Precisely isolated via controlled variation — each of the following
compiles instantly and correctly on their own:
- The enum alone, with a plain synchronous, non-throwing function using it.
throws(MyError) on a synchronous function (no async).
async throws (untyped) using the same enum as a thrown value.
Only the combination of async + typed throws
(throws(ConcreteErrorType)) together triggers the bug. Neither feature
alone is sufficient.
3. Investigation summary — layers ruled out, with direct evidence
This was traced through several layers, each with a specific,
falsifiable hypothesis tested and ruled out with hard evidence rather than
assumption, before reaching the actual root cause:
swift-corelibs-foundation/general enum extra-inhabitant
representation — considered given this session's history of
similar big-endian struct-layout bugs (_CFInfo, see the companion
report). Ruled out: ErrorExistentialTypeInfo (the type info for
any Error, the enum's payload) correctly derives its spare-bits from
the same generic HeapObjectSpareBits mechanism already fixed and
confirmed correct elsewhere this session.
- SIL generation — ruled out directly:
swiftc -emit-sil on the
minimal repro completes instantly, producing entirely ordinary,
platform-generic SIL (a standard alloc_stack/store/apply
sequence for the generic swift_willThrowTyped runtime hook). No
anomaly visible at this level at all.
NativeConventionSchema::shouldReturnTypedErrorIndirectly() (the
function deciding whether the typed error is returned directly in
registers or indirectly via memory) — the leading hypothesis for some
time, given 17 separate call sites across four IRGen files. Ruled
out via direct instrumentation and a full targeted compiler rebuild:
all 14 invocations during compilation of the minimal repro return the
identical, consistent answer (false — "direct"). No disagreement
between call sites.
combineResultAndTypedErrorType and buildDirectError (the
actual "direct" codegen path, in swift/lib/IRGen/GenCall.cpp) — read
in full and confirmed to be provably bounded for this input (every
loop advances a fixed-size iterator with no possibility of
non-termination for a two-element aggregate). Ruled out by direct
source inspection.
- The actual location — confirmed via live
gdb backtrace while the
hung process was still running:
#0 llvm::DAGTypeLegalizer::getTableId(llvm::SDValue)
#1 llvm::DAGTypeLegalizer::AnalyzeNewNode(llvm::SDNode*)
#2 llvm::DAGTypeLegalizer::run()
#3 llvm::SelectionDAG::LegalizeTypes()
#4 llvm::SelectionDAGISel::CodeGenAndEmitDAG()
...
#8 (anonymous namespace)::PPCDAGToDAGISel::runOnMachineFunction(llvm::MachineFunction&)
This is entirely inside LLVM's own generic backend — specifically
its target-independent SelectionDAG type-legalization framework,
running within PowerPC's own instruction selector. Not Swift's IRGen,
not Clang's Swift-ABI machinery — LLVM's generic code generation
proper.
4. Root cause
grep -c "CallingConv::Swift\|swiftcc\|SwiftError" \
llvm/lib/Target/PowerPC/PPCISelLowering.cpp # → 0
grep -c "CallingConv::Swift\|swiftcc\|SwiftError" \
llvm/lib/Target/AArch64/AArch64ISelLowering.cpp # → 11
PowerPC's LLVM backend contains zero Swift-calling-convention-specific
handling anywhere in its main instruction-selection lowering file.
AArch64 — a genuinely well-supported Swift target — has eleven. This is
not a subtle bug in an otherwise-complete implementation; it is the
complete absence of a subsystem other supported targets have.
When Swift's compiler generates a function using the Swift calling
convention (every Swift function does, always) with the swiftasync
parameter attribute and a combined result+error aggregate type (produced
specifically by the async + typed-throws codegen path, see the companion
GenCall.cpp trace in §3.3-3.4), LLVM's generic, target-independent type
legalization framework has no PowerPC-specific guidance for how to
correctly legalize this. It falls through to generic fallback logic that
was evidently never validated against this exact combination on this
target, and enters what appears to be a non-terminating (or
extremely-long-running) type-promotion cycle.
Status: Root cause identified via direct live backtrace evidence. Not fixable
from a
swift-corelibs-foundation,swift-corelibs-libdispatch, or ports-levelpatch — the actual gap is in Swift LLVM's PowerPC target backend (
llvm/lib/Target/PowerPC/).Target:
swift-6.3.2,powerpc64-unknown-freebsd14.4. Discovered whilebuilding
sourcekit-lsp(Stage 5), specifically itsDocumentationLanguageServicemodule. Confirmed to reproduce with a 20-line, fully isolated, dependency-free
minimal repro (included below) — not specific to
sourcekit-lsp's actual code in any way.1. Symptom
Two distinct symptoms of the same underlying cause, depending on
optimization level and exact code shape:
sourcekit-lsp's real build: an LLVM IR verifier failure —error: fatal error encountered during compilation; please submit a bug report/note: Broken module found, compilation aborted!— withvisibly inconsistent operand types in the dumped IR (a value of the
Swift enum's opaque type fed directly into
icmp/ptrtoint, whichrequire raw pointer/integer operands, alongside a
phinode elsewhereexpecting a plain
ptr).swift-frontendinstead hangs indefinitely, consuming 95-99% CPUcontinuously for 30+ minutes on a 20-line source file with no sign of
terminating. (Both are almost certainly the same underlying LLVM
backend defect; which one manifests likely depends on which specific
code path gets hit for a given IR shape — see §6.)
2. Minimal reproduction
Compiled with:
swiftc -c minimal_repro.swift -o minimal_repro.o(anyoptimization level; reproduces at both default and
-Onone).Precisely isolated via controlled variation — each of the following
compiles instantly and correctly on their own:
throws(MyError)on a synchronous function (noasync).async throws(untyped) using the same enum as a thrown value.Only the combination of
async+ typed throws(
throws(ConcreteErrorType)) together triggers the bug. Neither featurealone is sufficient.
3. Investigation summary — layers ruled out, with direct evidence
This was traced through several layers, each with a specific,
falsifiable hypothesis tested and ruled out with hard evidence rather than
assumption, before reaching the actual root cause:
swift-corelibs-foundation/general enum extra-inhabitantrepresentation — considered given this session's history of
similar big-endian struct-layout bugs (
_CFInfo, see the companionreport). Ruled out:
ErrorExistentialTypeInfo(the type info forany Error, the enum's payload) correctly derives its spare-bits fromthe same generic
HeapObjectSpareBitsmechanism already fixed andconfirmed correct elsewhere this session.
swiftc -emit-silon theminimal repro completes instantly, producing entirely ordinary,
platform-generic SIL (a standard
alloc_stack/store/applysequence for the generic
swift_willThrowTypedruntime hook). Noanomaly visible at this level at all.
NativeConventionSchema::shouldReturnTypedErrorIndirectly()(thefunction deciding whether the typed error is returned directly in
registers or indirectly via memory) — the leading hypothesis for some
time, given 17 separate call sites across four IRGen files. Ruled
out via direct instrumentation and a full targeted compiler rebuild:
all 14 invocations during compilation of the minimal repro return the
identical, consistent answer (
false— "direct"). No disagreementbetween call sites.
combineResultAndTypedErrorTypeandbuildDirectError(theactual "direct" codegen path, in
swift/lib/IRGen/GenCall.cpp) — readin full and confirmed to be provably bounded for this input (every
loop advances a fixed-size iterator with no possibility of
non-termination for a two-element aggregate). Ruled out by direct
source inspection.
gdbbacktrace while thehung process was still running:
its target-independent SelectionDAG type-legalization framework,
running within PowerPC's own instruction selector. Not Swift's IRGen,
not Clang's Swift-ABI machinery — LLVM's generic code generation
proper.
4. Root cause
PowerPC's LLVM backend contains zero Swift-calling-convention-specific
handling anywhere in its main instruction-selection lowering file.
AArch64 — a genuinely well-supported Swift target — has eleven. This is
not a subtle bug in an otherwise-complete implementation; it is the
complete absence of a subsystem other supported targets have.
When Swift's compiler generates a function using the Swift calling
convention (every Swift function does, always) with the
swiftasyncparameter attribute and a combined result+error aggregate type (produced
specifically by the
async+ typed-throws codegen path, see the companionGenCall.cpptrace in §3.3-3.4), LLVM's generic, target-independent typelegalization framework has no PowerPC-specific guidance for how to
correctly legalize this. It falls through to generic fallback logic that
was evidently never validated against this exact combination on this
target, and enters what appears to be a non-terminating (or
extremely-long-running) type-promotion cycle.