From 32aa4d9aed3a2709d55b58ed3d227965cf304bcc Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 15 Nov 2025 12:12:46 +0000 Subject: [PATCH 1/3] sil-opt: Add flag to disable aggressive reg2mem mode --- lib/DriverTool/sil_opt_main.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/DriverTool/sil_opt_main.cpp b/lib/DriverTool/sil_opt_main.cpp index 5b04453e83701..530c85a35a216 100644 --- a/lib/DriverTool/sil_opt_main.cpp +++ b/lib/DriverTool/sil_opt_main.cpp @@ -593,6 +593,11 @@ struct SILOptOptions { "enable-address-dependencies", llvm::cl::desc("Enable enforcement of lifetime dependencies on addressable values.")); + llvm::cl::opt DisaleAggressiveReg2Mem = llvm::cl::opt( + "disable-aggressive-reg2mem", + llvm::cl::desc("Disable aggressive reg2mem optimizations."), + llvm::cl::init(false)); + llvm::cl::opt EnableCalleeAllocatedCoroAbi = llvm::cl::opt( "enable-callee-allocated-coro-abi", llvm::cl::desc("Override per-platform settings and use yield_once_2."), @@ -921,6 +926,8 @@ int sil_opt_main(ArrayRef argv, void *MainAddr) { options.EnablePackMetadataStackPromotion; SILOpts.EnableAddressDependencies = options.EnableAddressDependencies; + if (options.DisaleAggressiveReg2Mem) + SILOpts.UseAggressiveReg2MemForCodeSize = false; if (options.EnableCalleeAllocatedCoroAbi) SILOpts.CoroutineAccessorsUseYieldOnce2 = true; if (options.DisableCalleeAllocatedCoroAbi) From e259a257e69b944e6de70cc1952c57765714cd2b Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 15 Nov 2025 12:13:34 +0000 Subject: [PATCH 2/3] RedundantLoadElim: Fix invalid `destructure_struct` emission without aggressive reg2mem The `shouldExpand` in `OptUtils.swift` was incorrectly returning `true` unconditionally when `useAggressiveReg2MemForCodeSize` was disabled. The expansion might be invalid for types with addr-only types and structs with deinit, but we didn't check them before. This could lead to invalid `destructure_struct` instructions without `drop_deinit` being emitted. --- .../Optimizer/Utilities/OptUtils.swift | 3 -- lib/SIL/Utils/InstructionUtils.cpp | 8 ++++ .../redundant_load_elim_struct_deinit.sil | 44 +++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 test/SILOptimizer/redundant_load_elim_struct_deinit.sil diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index c5d256f221aa2..eb852a6969904 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -1100,9 +1100,6 @@ extension Type { /// False if expanding a type is invalid. For example, expanding a /// struct-with-deinit drops the deinit. func shouldExpand(_ context: some Context) -> Bool { - if !context.options.useAggressiveReg2MemForCodeSize { - return true - } return context.bridgedPassContext.shouldExpand(self.bridged) } } diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 73bb7d3a4ceb4..095442b58252a 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -1486,10 +1486,18 @@ bool swift::shouldExpand(SILModule &module, SILType ty) { if (nominalTy->getValueTypeDestructor()) return false; } + + // At this point we know it's valid to expand the type, next decide if we + // "should" expand it. + if (EnableExpandAll) { return true; } + if (!module.getOptions().UseAggressiveReg2MemForCodeSize) { + return true; + } + unsigned numFields = module.Types.countNumberOfFields(ty, expansion); return (numFields <= 6); } diff --git a/test/SILOptimizer/redundant_load_elim_struct_deinit.sil b/test/SILOptimizer/redundant_load_elim_struct_deinit.sil new file mode 100644 index 0000000000000..6e78ac991ada6 --- /dev/null +++ b/test/SILOptimizer/redundant_load_elim_struct_deinit.sil @@ -0,0 +1,44 @@ +// RUN: %target-sil-opt -module-name Swift -sil-print-types -enforce-exclusivity=none -enable-sil-verify-all %s -redundant-load-elimination | %FileCheck %s +// RUN: %target-sil-opt -module-name Swift -sil-print-types -enforce-exclusivity=none -enable-sil-verify-all %s -redundant-load-elimination -disable-aggressive-reg2mem | %FileCheck %s + +// REQUIRES: swift_in_compiler + +import Builtin + +@_marker protocol Copyable {} + +final class C1 {} + +struct S1 {} + +struct StructWithDeinit : ~Copyable { + // stored class-instance to ensure this struct has a non-trivial + var f1: C1 + var f2: S1 + deinit +} + +// CHECK-LABEL: sil [ossa] @test : $@convention(thin) (@owned StructWithDeinit, @owned S1) -> () +// CHECK: %[[STACK:.*]] = alloc_stack $StructWithDeinit +// CHECK: load [take] %[[STACK]] : $*StructWithDeinit +// CHECK: } // end sil function 'test' +sil [ossa] @test : $@convention(thin) (@owned StructWithDeinit, @owned S1) -> () { +bb0(%0 : @owned $StructWithDeinit, %1 : $S1): + %2 = alloc_stack $StructWithDeinit + store %0 to [init] %2 + // partial store into f2 + %3 = struct_element_addr %2, #StructWithDeinit.f2 + store %1 to [trivial] %3 + + // redundant load of the whole struct + %4 = load [take] %2 + destroy_value %4 + + dealloc_stack %2 + %5 = tuple () + return %5 +} + +sil @s1_deinit : $@convention(method) (@owned StructWithDeinit) -> () +sil_moveonlydeinit StructWithDeinit { @s1_deinit } + From fba5c9d1a32df83d4518f0f84e2b79f8073acbce Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 15 Nov 2025 19:29:56 +0000 Subject: [PATCH 3/3] test: Skip test/IRGen/moveonly_value_functions.swift for Wasm archs --- test/IRGen/moveonly_value_functions.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/IRGen/moveonly_value_functions.swift b/test/IRGen/moveonly_value_functions.swift index b0c01a96cca6c..ef7237ca8a1d5 100644 --- a/test/IRGen/moveonly_value_functions.swift +++ b/test/IRGen/moveonly_value_functions.swift @@ -5,6 +5,9 @@ // RUN: | \ // RUN: %IRGenFileCheck %s +// FIXME: This test currently requires aggressive reg2mem to be enabled +// UNSUPPORTED: CPU=wasm32 + @_silgen_name("external_symbol") func external_symbol()