Miscompile on arm64 at `-O`: AArch64 load/store optimizer renames a call's `swiftself` register (x20 → x22) · Issue #93360 · swiftlang/swift · GitHub
Skip to content

Miscompile on arm64 at -O: AArch64 load/store optimizer renames a call's swiftself register (x20 → x22) #93360

Open
llvm/llvm-project
#235446

Description

@eugenijusr

Description

A miscompile, not a compiler crash: the program built with -O crashes at runtime.

A call to Date.timeIntervalSince1970.getter (resilient struct, self passed by address in x20) is executed with x20 not holding self, so the getter dereferences garbage. Swift's IR is correct. The register is lost in LLVM's AArch64 load/store optimizer: to merge two stores into an stp, its register renaming rewrites the call's implicit $x20 argument to $x22. Disabling that renaming (-Xllvm -aarch64-load-store-renaming=false) fixes it.

Code shape that triggers it: We hit this in vital-ios 1.8.4 (HealthKitReads+WorkoutStream.swift).

Reproduction

import Foundation

final class Workout {
  let startDate: Date
  init(startDate: Date) { self.startDate = startDate }
}

final class Sample {
  let value: Double
  init(value: Double) { self.value = value }
}

typealias Bulk = (anchor: Date, value: [Double], startOffset: [Double])

enum Failure: Error { case invalid }

func group(_ samples: [Sample], type: String, anchor: Date) -> [Bulk] {
  let unit = NSObject()                                   // an object reference
  let groups = Dictionary(grouping: samples, by: { _ in Optional("x") })
  let anchorEpoch = anchor.timeIntervalSince1970          // <- crashes: x20 not set
  return groups.map { _, samples in                       // captures `anchor` together with `unit`
    (anchor: anchor,
     value: samples.map { $0.value + Double(unit.hash & 0) },
     startOffset: samples.map { $0.value - anchorEpoch })
  }
}

func compute(for workout: Workout) async throws -> [Bulk] {
  let sampleGroups = ["w": [Sample(value: 100)]]
  let anchor = workout.startDate
  var result: [Bulk] = []
  for (type, samples) in sampleGroups {
    guard type == "w" else {
      if #available(iOS 18, macOS 15, *) { throw Failure.invalid } else { throw Failure.invalid }
    }
    result.append(contentsOf: group(samples, type: type, anchor: anchor))
  }
  return result
}

@main struct Main {
  static func main() async throws {
    let result = try await compute(for: Workout(startDate: Date(timeIntervalSince1970: 0)))
    print(result.map(\.startOffset))  // expected: [[100.0]]
  }
}
$ swiftc -O -wmo -parse-as-library -target arm64-apple-macos14.0 -module-name repro repro.swift -o repro && ./repro
zsh: segmentation fault  ./repro

$ swiftc -O -Xllvm -aarch64-load-store-renaming=false -wmo -parse-as-library -target arm64-apple-macos14.0 -module-name repro repro.swift -o repro && ./repro
[[100.0]]

Stack dump

* thread #3, name = 'Task 1', queue = 'com.apple.root.default-qos.cooperative', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
   * frame #0: 0x000000019ec43e48 Foundation`Foundation.Date.timeIntervalSince1970.getter : Swift.Double + 16
     frame #1: 0x00000001000014f0 repro`compute(for:) + 380
     frame #2: 0x00000001000027c8 repro`specialized static Main.main()
     frame #3: 0x0000000100001aa0 repro`(1) await resume partial function for function signature specialization <Arg[1] = [Constant Propagated Function : async_Main]> of reabstraction thunk helper from @escaping @convention(thin) @async () -> () to @escaping @callee_guaranteed @async () -> (@out (), @error @owned Swift.Error)

Expected behavior

The program prints [[100.0]].

Environment

  • Xcode 27.0 / 27.1 RC: Apple Swift version 6.4 (swiftlang-6.4.0.34.1 clang-2100.3.34.1)
  • swift.org swift-6.4.1-RELEASE and swift-6.3.3-RELEASE
  • macOS 27.0.1, Apple M3 Pro. Same code generation for arm64-apple-ios15.0; originally hit on iOS 26.6.2.

Additional information

MIR around the call before and after aarch64-ldst-opt (the crashing build command plus -Xllvm -print-before=aarch64-ldst-opt -Xllvm -print-after=aarch64-ldst-opt, swift.org 6.4.1):

# before
renamable $x20 = LDRXui $sp, 2 :: (load (s64) from %stack.13)
BL @"$s10Foundation4DateV21timeIntervalSince1970Sdvg", <regmask $fp $lr $wzr $wzr_hi $xzr $b8 $b9 $b10 $b11 $b12 $b13 $b14 $b15 $d8 $d9 $d10 $d11 $d12 $d13 $d14 $d15 $h8 $h9 $h10 $h11 $h12 $h13 $h14 $h15 $s8 $s9 $s10 $s11 and 92 more...>, implicit-def dead $lr, implicit $sp, implicit $x20, implicit-def $sp, implicit-def $d0
renamable $x8 = LDRXui $sp, 3 :: (load (s64) from %stack.12)
STRXui killed renamable $x20, renamable $x8, 4 :: (store (s64) into %ir.153)
renamable $x20 = LDRXui $sp, 5 :: (load (s64) from %stack.10)
STRXui renamable $x20, renamable $x8, 5 :: (store (s64) into %ir.154)

# after
$x22 = LDRXui $sp, 2 :: (load (s64) from %stack.13)
BL @"$s10Foundation4DateV21timeIntervalSince1970Sdvg", <regmask $fp $lr $wzr $wzr_hi $xzr $b8 $b9 $b10 $b11 $b12 $b13 $b14 $b15 $d8 $d9 $d10 $d11 $d12 $d13 $d14 $d15 $h8 $h9 $h10 $h11 $h12 $h13 $h14 $h15 $s8 $s9 $s10 $s11 and 92 more...>, implicit-def dead $lr, implicit $sp, implicit $x22, implicit-def $sp, implicit-def $d0
renamable $x8 = LDRXui $sp, 3 :: (load (s64) from %stack.12)
renamable $x20 = LDRXui $sp, 5 :: (load (s64) from %stack.10)
STPXi killed $x22, renamable $x20, renamable $x8, 4 :: (store (s64) into %ir.153), (store (s64) into %ir.154)
STRDui killed renamable $d0, renamable $x8, 6 :: (store (s64) into %ir.155)

-opt-bisect-limit confirms this pass on compute(for:) is the first step that produces the crash. At the crash, x20 = 0x0 and x22 holds the correct Date address.

The shape is fragile. In the reproducer, each of these hides the crash: -Onone, -Osize, @inline(never) on group, looping over an array instead of a dictionary, removing the throwing exit, making compute synchronous, or capturing a Double or String instead of the object. Of the other types we tried in place of Date, Measurement also crashes; UUID, URL and Decimal don't, in this shape.

Activity

  1. added
    crashBug: A crash, i.e., an abnormal termination of software
    triage neededThis issue needs more specific labels
    on Oct 16, 2026
  2. added theissue type on Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    crashBug: A crash, i.e., an abnormal termination of softwaretriage neededThis issue needs more specific labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions