From 254e4f041f1e0e46eaa1ff50d8420cb3d0a2036b Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 29 Feb 2024 15:47:41 -0800 Subject: [PATCH] Handle atomic instructions in escape analysis Split atomic loads/stores no more guard invert condition --- .../Optimizer/Utilities/EscapeUtils.swift | 35 +++++ test/SILOptimizer/mem-behavior.sil | 125 ++++++++++++++++++ test/SILOptimizer/templvalueopt.sil | 10 ++ 3 files changed, 170 insertions(+) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift index a9d3e954aecc6..4818a5b7e0473 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift @@ -420,6 +420,41 @@ fileprivate struct EscapeWalker : ValueDefUseWalker, return .continueWalk } return isEscaping + + case .AtomicLoad: + // Treat atomic loads as regular loads and just walk down their uses. + if !followLoads(at: path) { + return .continueWalk + } + + // Even when analyzing atomics, a loaded trivial value can be ignored. + if hasRelevantType(bi, at: path.projectionPath) { + return .continueWalk + } + + return walkDownUses(ofValue: bi, path: path.with(knownType: nil)) + + case .AtomicStore, .AtomicRMW: + // If we shouldn't follow the store, then we can keep walking. + if !path.followStores { + return .continueWalk + } + + // Be conservative and just say the store is escaping. + return isEscaping + + case .CmpXChg: + // If we have to follow loads or stores of a cmpxchg, then just bail. + if followLoads(at: path) || path.followStores { + return isEscaping + } + + return .continueWalk + + case .Fence: + // Fences do not affect escape analysis. + return .continueWalk + default: return isEscaping } diff --git a/test/SILOptimizer/mem-behavior.sil b/test/SILOptimizer/mem-behavior.sil index a50ec47e50c91..3438d181ce43a 100644 --- a/test/SILOptimizer/mem-behavior.sil +++ b/test/SILOptimizer/mem-behavior.sil @@ -1100,6 +1100,131 @@ bb0: return %4 : $() } +// CHECK-LABEL: @test_builtin_zeroInitializer_atomicload +// CHECK: PAIR #0. +// CHECK-NEXT: %2 = builtin "zeroInitializer"(%1 : $*C) +// CHECK-NEXT: %0 = alloc_stack +// CHECK-NEXT: r=0,w=0 +// CHECK: PAIR #1. +// CHECK-NEXT: %2 = builtin "zeroInitializer"(%1 : $*C) +// CHECK-NEXT: %1 = alloc_stack +// CHECK-NEXT: r=0,w=1 +sil @test_builtin_zeroInitializer_atomicload : $@convention(thin) () -> Builtin.Int64 { +bb0: + %0 = alloc_stack $C + %1 = alloc_stack $C + %2 = builtin "zeroInitializer"(%1 : $*C) : $() + %3 = apply undef(%1) : $@convention(thin) () -> @out C + copy_addr [take] %1 to [init] %0 : $*C + dealloc_stack %1 : $*C + %4 = address_to_pointer %0 : $*C to $Builtin.RawPointer + %5 = builtin "atomicload_monotonic_Int64"(%4 : $Builtin.RawPointer) : $Builtin.Int64 + destroy_addr %0 : $*C + dealloc_stack %0 : $*C + return %5 : $Builtin.Int64 +} + +// CHECK-LABEL: @test_builtin_zeroInitializer_atomicstore +// CHECK: PAIR #0. +// CHECK-NEXT: %2 = builtin "zeroInitializer"(%1 : $*C) +// CHECK-NEXT: %0 = alloc_stack +// CHECK-NEXT: r=0,w=0 +// CHECK: PAIR #1. +// CHECK-NEXT: %2 = builtin "zeroInitializer"(%1 : $*C) +// CHECK-NEXT: %1 = alloc_stack +// CHECK-NEXT: r=0,w=1 +sil @test_builtin_zeroInitializer_atomicstore : $@convention(thin) () -> () { +bb0: + %0 = alloc_stack $C + %1 = alloc_stack $C + %2 = builtin "zeroInitializer"(%1 : $*C) : $() + %3 = apply undef(%1) : $@convention(thin) () -> @out C + copy_addr [take] %1 to [init] %0 : $*C + dealloc_stack %1 : $*C + %4 = address_to_pointer %0 : $*C to $Builtin.RawPointer + %5 = integer_literal $Builtin.Int64, 1 + %6 = builtin "atomicstore_monotonic_Int64"(%4 : $Builtin.RawPointer, %5 : $Builtin.Int64) : $() + destroy_addr %0 : $*C + dealloc_stack %0 : $*C + %7 = tuple () + return %7 : $() +} + +// CHECK-LABEL: @test_builtin_zeroInitializer_atomicrmw +// CHECK: PAIR #0. +// CHECK-NEXT: %2 = builtin "zeroInitializer"(%1 : $*C) +// CHECK-NEXT: %0 = alloc_stack +// CHECK-NEXT: r=0,w=0 +// CHECK: PAIR #1. +// CHECK-NEXT: %2 = builtin "zeroInitializer"(%1 : $*C) +// CHECK-NEXT: %1 = alloc_stack +// CHECK-NEXT: r=0,w=1 +sil @test_builtin_zeroInitializer_atomicrmw : $@convention(thin) () -> (Builtin.Int64, Builtin.Int64) { +bb0: + %0 = alloc_stack $C + %1 = alloc_stack $C + %2 = builtin "zeroInitializer"(%1 : $*C) : $() + %3 = apply undef(%1) : $@convention(thin) () -> @out C + copy_addr [take] %1 to [init] %0 : $*C + dealloc_stack %1 : $*C + %4 = address_to_pointer %0 : $*C to $Builtin.RawPointer + %5 = integer_literal $Builtin.Int64, 1 + %6 = builtin "atomicrmw_xchg_monotonic_Int64"(%4 : $Builtin.RawPointer, %5 : $Builtin.Int64) : $Builtin.Int64 + %7 = builtin "atomicrmw_add_monotonic_Int64"(%4 : $Builtin.RawPointer, %5 : $Builtin.Int64) : $Builtin.Int64 + destroy_addr %0 : $*C + dealloc_stack %0 : $*C + %8 = tuple (%6 : $Builtin.Int64, %7 : $Builtin.Int64) + return %8 : $(Builtin.Int64, Builtin.Int64) +} + +// CHECK-LABEL: @test_builtin_zeroInitializer_cmpxchg +// CHECK: PAIR #0. +// CHECK-NEXT: %2 = builtin "zeroInitializer"(%1 : $*C) +// CHECK-NEXT: %0 = alloc_stack +// CHECK-NEXT: r=0,w=0 +// CHECK: PAIR #1. +// CHECK-NEXT: %2 = builtin "zeroInitializer"(%1 : $*C) +// CHECK-NEXT: %1 = alloc_stack +// CHECK-NEXT: r=0,w=1 +sil @test_builtin_zeroInitializer_cmpxchg : $@convention(thin) () -> (Builtin.Int64, Builtin.Int1) { +bb0: + %0 = alloc_stack $C + %1 = alloc_stack $C + %2 = builtin "zeroInitializer"(%1 : $*C) : $() + %3 = apply undef(%1) : $@convention(thin) () -> @out C + copy_addr [take] %1 to [init] %0 : $*C + dealloc_stack %1 : $*C + %4 = address_to_pointer %0 : $*C to $Builtin.RawPointer + %5 = integer_literal $Builtin.Int64, 1 + %6 = builtin "cmpxchg_monotonic_monotonic_Int64"(%4 : $Builtin.RawPointer, %5 : $Builtin.Int64) : $(Builtin.Int64, Builtin.Int1) + destroy_addr %0 : $*C + dealloc_stack %0 : $*C + return %6 : $(Builtin.Int64, Builtin.Int1) +} + +// CHECK-LABEL: @test_builtin_zeroInitializer_fence +// CHECK: PAIR #0. +// CHECK-NEXT: %2 = builtin "zeroInitializer"(%1 : $*C) +// CHECK-NEXT: %0 = alloc_stack +// CHECK-NEXT: r=0,w=0 +// CHECK: PAIR #1. +// CHECK-NEXT: %2 = builtin "zeroInitializer"(%1 : $*C) +// CHECK-NEXT: %1 = alloc_stack +// CHECK-NEXT: r=0,w=1 +sil @test_builtin_zeroInitializer_fence : $@convention(thin) () -> () { +bb0: + %0 = alloc_stack $C + %1 = alloc_stack $C + %2 = builtin "zeroInitializer"(%1 : $*C) : $() + %3 = apply undef(%1) : $@convention(thin) () -> @out C + copy_addr [take] %1 to [init] %0 : $*C + dealloc_stack %1 : $*C + %5 = builtin "fence_release"() : $() + destroy_addr %0 : $*C + dealloc_stack %0 : $*C + return %5 : $() +} + // CHECK-LABEL: @test_stored_pointer // CHECK: PAIR #3. // CHECK-NEXT: %5 = apply %4(%2) : $@convention(thin) (@in Builtin.RawPointer) -> () diff --git a/test/SILOptimizer/templvalueopt.sil b/test/SILOptimizer/templvalueopt.sil index e122ffe77162b..eeda820573b9b 100644 --- a/test/SILOptimizer/templvalueopt.sil +++ b/test/SILOptimizer/templvalueopt.sil @@ -264,6 +264,16 @@ bb0(%ret_addr : $*T): apply undef(%temporary) : $@convention(thin) () -> @out T copy_addr [take] %temporary to [init] %ret_addr : $*T dealloc_stack %temporary : $*T + + // Ensure that the following builtin instructions don't interfere with + // temp l value from getting rid of the temporary. + %empty = builtin "fence_release"() : $() + %ptr = address_to_pointer %ret_addr : $*T to $Builtin.RawPointer + %load = builtin "atomicload_monotonic_Int64"(%ptr : $Builtin.RawPointer) : $Builtin.Int64 + %onetwoeight = integer_literal $Builtin.Int64, 128 + %empty2 = builtin "atomicstore_monotonic_Int64"(%ptr : $Builtin.RawPointer, %onetwoeight : $Builtin.Int64) : $() + %add = builtin "atomicrmw_add_monotonic_Int64"(%ptr : $Builtin.RawPointer, %onetwoeight : $Builtin.Int64) : $Builtin.Int64 + %cmpxchg = builtin "cmpxchg_monotonic_monotonic_Int64"(%ptr : $Builtin.RawPointer, %onetwoeight : $Builtin.Int64, %onetwoeight : $Builtin.Int64) : $(Builtin.Int64, Builtin.Int1) %15 = tuple () return %15 : $() }