//===--- Exclusivity.swift -------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// A set of tests for measuring the enforcement overhead of memory access
// exclusivity rules.
//
//===----------------------------------------------------------------------===//

import TestsUtils

public let benchmarks = [
  // At -Onone
  // 25% swift_beginAccess
  // 15% tlv_get_addr
  // 15% swift_endAccess
  BenchmarkInfo(
    name: "ExclusivityGlobal",
    runFunction: run_accessGlobal,
    tags: [.runtime, .cpubench]
  ),
  // At -Onone
  // 23% swift_retain
  // 22% swift_release
  //  9% swift_beginAccess
  //  3% swift_endAccess
  BenchmarkInfo(
    name: "ExclusivityInMatSet",
    runFunction: run_accessInMatSet,
    tags: [.runtime, .cpubench, .unstable]
  ),
  // At -Onone
  // 25% swift_release
  // 23% swift_retain
  // 16% swift_beginAccess
  //  8% swift_endAccess
  BenchmarkInfo(
    name: "ExclusivityIndependent",
    runFunction: run_accessIndependent,
    tags: [.runtime, .cpubench]
  ),
]

// Initially these benchmarks only measure access checks at -Onone. In
// the future, access checks will also be emitted at -O.

// Measure memory access checks on a trivial global.
// ---

public var globalCounter: Int = 0

// TODO:
// - Merge begin/endAccess when no calls intervene (~2x speedup).
// - Move Swift runtime into the OS  (~2x speedup).
// - Whole module analysis can remove exclusivity checks (> 10x speedup now, 4x speedup with runtime in OS).
//   (The global's "public" qualifier should make the benchmark immune to this optimization.)
@inline(never)
public func run_accessGlobal(_ n: Int) {
  globalCounter = 0
  for _ in 1...10000*n {
    globalCounter += 1
  }
  check(globalCounter == 10000*n)
}

// Measure memory access checks on a class property.
//
// Note: The end_unpaired_access forces a callback on the property's
// materializeForSet!
// ---

// Hopefully the optimizer will not see this as "final" and optimize away the
// materializeForSet.
public class C {
  public var counter = 0

  func inc() {
    counter += 1
  }
}

// Thunk 
@inline(never)
func updateClass(_ c: C) {
  c.inc()
}

// TODO: Replacing materializeForSet accessors with yield-once
// accessors should make the callback overhead go away.
@inline(never)
public func run_accessInMatSet(_ n: Int) {
  let c = C()
  for _ in 1...10000*n {
    updateClass(c)
  }
  check(c.counter == 10000*n)
}

// Measure nested access to independent objects.
//
// A single access set is still faster than hashing for up to four accesses.
// ---

struct Var {
  var val = 0
}

@inline(never)
func update(a: inout Var, b: inout Var, c: inout Var, d: inout Var) {
  a.val += 1
  b.val += 1
  c.val += 1
  d.val += 1
}

@inline(never)
public func run_accessIndependent(_ n: Int) {
  var a = Var()
  var b = Var()
  var c = Var()
  var d = Var()
  let updateVars = {
    update(a: &a, b: &b, c: &c, d: &d)
  }
  for _ in 1...1000*n {
    updateVars()
  }
  check(a.val == 1000*n && b.val == 1000*n && c.val == 1000*n
    && d.val == 1000*n)
}
