Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-13985] TempRValueOpt causes a failure when building swift-numerics #56380

Closed
compnerd opened this issue Dec 23, 2020 · 6 comments
Closed
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself

Comments

@compnerd
Copy link
Collaborator

Previous ID SR-13985
Radar rdar://problem/72614608
Original Reporter @compnerd
Type Bug
Status Resolved
Resolution Done
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee None
Priority Medium

md5: 9a03190e737cb12bc220336941a2ae02

Issue Description:

When building swift-numerics on Linux with WMO, there is a misoptimization with the TempRValueOpt pass:

SIL memory lifetime failure in @$s13ComplexModule0A0V3logyACyxGAEFZ: memory is not initialized, but should
memory location: %98 = alloc_stack $RealType, let, name "θ" // users: %392, %390, %234, %235, %406,. %247, %100
at instruction: copy_addr %98 to [initialization] %391 : %*RealType // id: %392

Happens with from 2020-11-26 at least to 2020-12-21.

@compnerd
Copy link
Collaborator Author

CC: @eeckstein @gottesmm @stephentyrone

@typesanitizer
Copy link

@swift-ci create

@compnerd
Copy link
Collaborator Author

Reduced this some:

// %swift_frontend_plain -c %s -target x86_64-unknown-linux-gnu -O -parse-as-library -o /dev/null -sil-verify-all

public struct Complex<RealType> where RealType: FloatingPoint {
  @usableFromInline
  internal var x: RealType

  @usableFromInline
  internal var y: RealType

  @_transparent
  public init(_ real: RealType, _ imaginary: RealType) {
    x = real
    y = imaginary
  }
}

extension Complex {
  public var phase: RealType { fatalError() }
}

extension Complex {
  public static func log(_ z: Complex, _ b: Bool) -> Complex {
    let θ = z.phase
    if b == true {
      return Complex(.infinity, θ)
    }
    return Complex(.infinity, θ)
  }
}

@compnerd
Copy link
Collaborator Author

Slightly lower form:

// RUN: %sil-opt --temp-rvalue-opt %s -o /dev/null 
sil_stage canonical

import Builtin
import Swift
import SwiftShims

public struct Complex<RealType> where RealType : FloatingPoint {
  @usableFromInline
  @_hasStorage internal var x: RealType { get set }
  @usableFromInline
  @_hasStorage internal var y: RealType { get set }
  init(_ real: RealType, _ imaginary: RealType)
}

extension Complex {
  var phase: RealType { get }
}

extension Complex {
  public static func log(_ z: Complex<RealType>, _ b: Bool) -> Complex<RealType>
}

// Complex.phase.getter
sil hidden [ossa] @$s7reduced7ComplexV5phasexvg : $@convention(method) <RealType where RealType : FloatingPoint> (@in_guaranteed Complex<RealType>) -> @out RealType {
// %0 "$return_value"
// %1 "self"                                      // user: %2
bb0(%0 : $*RealType, %1 : $*Complex<RealType>):
  debug_value_addr %1 : $*Complex<RealType>, let, name "self", argno 1 // id: %2
  unreachable                                     // id: %3
} // end sil function '$s7reduced7ComplexV5phasexvg'

// static Complex.log(_:_:)
sil [ossa] @$s7reduced7ComplexV3logyACyxGAE_SbtFZ : $@convention(method) <RealType where RealType : FloatingPoint> (@in_guaranteed Complex<RealType>, Bool, @thin Complex<RealType>.Type) -> @out Complex<RealType> {
// %0 "$return_value"                             // users: %35, %60
// %1 "z"                                         // users: %9, %4
// %2 "b"                                         // users: %15, %5
// %3 "self"                                      // user: %6
bb0(%0 : $*Complex<RealType>, %1 : $*Complex<RealType>, %2 : $Bool, %3 : $@thin Complex<RealType>.Type):
  debug_value_addr %1 : $*Complex<RealType>, let, name "z", argno 1 // id: %4
  debug_value %2 : $Bool, let, name "b", argno 2  // id: %5
  debug_value %3 : $@thin Complex<RealType>.Type, let, name "self", argno 3 // id: %6
  %7 = alloc_stack $RealType, let, name "θ"       // users: %66, %48, %41, %23, %11
  %8 = alloc_stack $Complex<RealType>             // users: %13, %12, %11, %9
  copy_addr %1 to [initialization] %8 : $*Complex<RealType> // id: %9
  // function_ref Complex.phase.getter
  %10 = function_ref @$s7reduced7ComplexV5phasexvg : $@convention(method) <τ_0_0 where τ_0_0 : FloatingPoint> (@in_guaranteed Complex<τ_0_0>) -> @out τ_0_0 // user: %11
  %11 = apply %10<RealType>(%7, %8) : $@convention(method) <τ_0_0 where τ_0_0 : FloatingPoint> (@in_guaranteed Complex<τ_0_0>) -> @out τ_0_0
  destroy_addr %8 : $*Complex<RealType>           // id: %12
  dealloc_stack %8 : $*Complex<RealType>          // id: %13
  %14 = integer_literal $Builtin.Int1, -1         // user: %16
  %15 = struct_extract %2 : $Bool, #Bool._value   // user: %16
  %16 = builtin "cmp_eq_Int1"(%15 : $Builtin.Int1, %14 : $Builtin.Int1) : $Builtin.Int1 // user: %17
  cond_br %16, bb1, bb2                           // id: %17

bb1:                                              // Preds: bb0
  %18 = metatype $@thick RealType.Type            // user: %21
  %19 = alloc_stack $RealType                     // users: %26, %40, %21
  %20 = witness_method $RealType, #FloatingPoint.infinity!getter : <Self where Self : FloatingPoint> (Self.Type) -> () -> Self : $@convention(witness_method: FloatingPoint) <τ_0_0 where τ_0_0 : FloatingPoint> (@thick τ_0_0.Type) -> @out τ_0_0 // user: %21
  %21 = apply %20<RealType>(%19, %18) : $@convention(witness_method: FloatingPoint) <τ_0_0 where τ_0_0 : FloatingPoint> (@thick τ_0_0.Type) -> @out τ_0_0
  %22 = alloc_stack $RealType                     // users: %31, %39, %23
  copy_addr [take] %7 to [initialization] %22 : $*RealType // id: %23
  %24 = alloc_stack $Complex<RealType>            // users: %27, %32, %37, %36, %35
  %25 = alloc_stack $RealType                     // users: %29, %28, %26
  copy_addr [take] %19 to [initialization] %25 : $*RealType // id: %26
  %27 = struct_element_addr %24 : $*Complex<RealType>, #Complex.x // user: %28
  copy_addr [take] %25 to [initialization] %27 : $*RealType // id: %28
  dealloc_stack %25 : $*RealType                  // id: %29
  %30 = alloc_stack $RealType                     // users: %34, %33, %31
  copy_addr [take] %22 to [initialization] %30 : $*RealType // id: %31
  %32 = struct_element_addr %24 : $*Complex<RealType>, #Complex.y // user: %33
  copy_addr [take] %30 to [initialization] %32 : $*RealType // id: %33
  dealloc_stack %30 : $*RealType                  // id: %34
  copy_addr %24 to [initialization] %0 : $*Complex<RealType> // id: %35
  destroy_addr %24 : $*Complex<RealType>          // id: %36
  dealloc_stack %24 : $*Complex<RealType>         // id: %37
  %38 = tuple ()
  dealloc_stack %22 : $*RealType                  // id: %39
  dealloc_stack %19 : $*RealType                  // id: %40
  dealloc_stack %7 : $*RealType                   // id: %41
  br bb3                                          // id: %42

bb2:                                              // Preds: bb0
  %43 = metatype $@thick RealType.Type            // user: %46
  %44 = alloc_stack $RealType                     // users: %51, %65, %46
  %45 = witness_method $RealType, #FloatingPoint.infinity!getter : <Self where Self : FloatingPoint> (Self.Type) -> () -> Self : $@convention(witness_method: FloatingPoint) <τ_0_0 where τ_0_0 : FloatingPoint> (@thick τ_0_0.Type) -> @out τ_0_0 // user: %46
  %46 = apply %45<RealType>(%44, %43) : $@convention(witness_method: FloatingPoint) <τ_0_0 where τ_0_0 : FloatingPoint> (@thick τ_0_0.Type) -> @out τ_0_0
  %47 = alloc_stack $RealType                     // users: %56, %64, %48
  copy_addr [take] %7 to [initialization] %47 : $*RealType // id: %48
  %49 = alloc_stack $Complex<RealType>            // users: %52, %57, %62, %61, %60
  %50 = alloc_stack $RealType                     // users: %54, %53, %51
  copy_addr [take] %44 to [initialization] %50 : $*RealType // id: %51
  %52 = struct_element_addr %49 : $*Complex<RealType>, #Complex.x // user: %53
  copy_addr [take] %50 to [initialization] %52 : $*RealType // id: %53
  dealloc_stack %50 : $*RealType                  // id: %54
  %55 = alloc_stack $RealType                     // users: %59, %58, %56
  copy_addr [take] %47 to [initialization] %55 : $*RealType // id: %56
  %57 = struct_element_addr %49 : $*Complex<RealType>, #Complex.y // user: %58
  copy_addr [take] %55 to [initialization] %57 : $*RealType // id: %58
  dealloc_stack %55 : $*RealType                  // id: %59
  copy_addr %49 to [initialization] %0 : $*Complex<RealType> // id: %60
  destroy_addr %49 : $*Complex<RealType>          // id: %61
  dealloc_stack %49 : $*Complex<RealType>         // id: %62
  %63 = tuple ()
  dealloc_stack %47 : $*RealType                  // id: %64
  dealloc_stack %44 : $*RealType                  // id: %65
  dealloc_stack %7 : $*RealType                   // id: %66
  br bb3                                          // id: %67

bb3:                                              // Preds: bb2 bb1
  %68 = tuple ()                                  // user: %69
  return %68 : $()                                // id: %69
} // end sil function '$s7reduced7ComplexV3logyACyxGAE_SbtFZ'

sil_property #Complex.x<τ_0_0 where τ_0_0 : FloatingPoint> ()
sil_property #Complex.y<τ_0_0 where τ_0_0 : FloatingPoint> ()

@compnerd
Copy link
Collaborator Author

From tracing, what appears to be happening is that the successive iteration for simplifying multiple redundant stores is causing an invalid case to form.

What roughly occurs is that a series of passes over:

...
copy_addr [take] %5 to [initialization] %37 : $*RealType
...
copy_addr [take] %34 to [initialization] %40 : $*RealType
...
copy_addr [take] %40 to [initialization] %42 : $*RealType
...
copy_addr [take] %37 to [initialization] %45 : $*RealType
...
copyaddr_ [take] %45 to [initialization] %47 : $*RealType
...

Replacing

Load: copy_addr [take] %37 to [initialization] %45 : $*RealType
Copy: copy_addr [take] %5 to [initialization] %47 : $*RealType 

Giving

...
copy_addr [take] %5 to [initialization] %5 : $*RealType
...
copy_addr [take] %34 to [initialization] %39 : $*RealType
...
copy_addr [take] %39 to [initialization] %41 : $*RealType
...
copy_addr %5 to [initialization] %44 : $*RealType
destroy_addr %5 : $*RealType
..
copy_addr [take] %44 to [initialization] %47 : $*RealType

Replacing

Load: copy_addr [take] %39 to [initialization] %41
Copy: copy_addr [take] %34 to [initialization] %39 

Giving

...
copy_addr [take] %5 to [initialization] %5 : $*RealType
...
copy_addr [take] %34 to [initialization] %34 : $*RealType
...
copy_addr [take] %34 to [initialization] %40 : $*RealType
destroy_addr %34
...
copy_addr %5 to [initialization] %43 : $*RealType
destroy_addr %5 : $*RealType
...
copy_addr [take] %43 to [initialization] %46 : $*RealType

And for the next (bad) transformation, as it ignores the destroy_addr %5 in between:

Load: copy_addr [take] %43 to [initialization] %46
Copy: copy_addr %5 to [initialization] %43 : $*RealType 

Giving:

...
copy_addr [take] %5 to [initialization] %5 : $*RealType
...
copy_addr [take] %34 to [initialization] %34 : $*RealType
...
copy_addr [take] %34 to [initialization] %40 : $*RealType
destroy_addr %34
...
copy_addr %5 to [initialization] %5 : $*RealType
destroy_addr %5 : $*RealType
...
copy_addr [take] %5 to [initialization] %45 : $*RealType

Note that %5 is destroyed prior to the initialization of %45, which then the memory verifier properly identifies as the value not being initialized when being used.

I cannot seem to create a synthetic further reduction as when fed the intermediate state.

The interaction seems to involve a conversion of a [take] initialization being optimized away (inserting a destroy_addr) and then a secondary one along the same path which does not add a destroy_addr as it is not a [take] copy being optimized the second time.

@eeckstein
Copy link
Member

Thanks for providing the reduced test case!
Should be fixed with: #35263

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself
Projects
None yet
Development

No branches or pull requests

3 participants