Repository: apple/swift
File: SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift (Swift)

Code before:
  return changed
}

/// Either a `load` or a `copy_addr` (which is equivalent to a load+store).
private protocol LoadingInstruction: Instruction {
  var address: Value { get }
  var type: Type { get }
  var ownership: Ownership { get }
  var loadOwnership: LoadInst.LoadOwnership { get }
  var canLoadValue: Bool { get }
  func trySplit(_ context: FunctionPassContext) -> Bool
  func materializeLoadForReplacement(_ context: FunctionPassContext) -> LoadInst
}

extension LoadInst : LoadingInstruction {
  // We know that the type is loadable because - well - this is a load.
  var canLoadValue: Bool { true }

  // Nothing to materialize, because this is already a `load`.
  func materializeLoadForReplacement(_ context: FunctionPassContext) -> LoadInst { return self }

  func replaceEfficiently(with newValue: Value, _ context: FunctionPassContext) {
    if let existingLoad = newValue as? LoadInst {
      // As we are processing the loads in reverse control flow order, the replaced loads might accumulate
      // quite a lot of users. This happens if the are many loads from the same location in a row.
      // To void quadratic complexity in `uses.replaceAll`, we swap both load instructions and move the uses
      // from the `existingLoad` (which usually has a small number of uses) to this load - and delete the
      // `existingLoad`.
      existingLoad.uses.replaceAll(with: self, context)
      self.operand.set(to: existingLoad.address, context)
      self.set(ownership: existingLoad.loadOwnership, context)
      self.set(location: existingLoad.location, context)
      self.move(before: existingLoad, context)
      context.erase(instruction: existingLoad)
    } else {
      replace(with: newValue, context)
    }
  }
}

extension CopyAddrInst : LoadingInstruction {
  var address: Value { source }
  var type: Type { address.type.objectType }
  var typeIsLoadable: Bool { type.isLoadable(in: parentFunction) }

  var ownership: Ownership {
    if !parentFunction.hasOwnership || type.isTrivial(in: parentFunction) {
      return .none
    }
    // Regardless of if the copy is taking or copying, the loaded value is an owned value.
    return .owned

Review comment (performance):
```suggestion
      // To avoid quadratic complexity in `uses.replaceAll`, we swap both load instructions and move the uses
```

Code after:
  return changed
}

/// Either a `load` or a `copy_addr` (which is equivalent to a load+store).
private protocol LoadingInstruction: Instruction {
  var address: Value { get }
  var type: Type { get }
  var ownership: Ownership { get }
  var loadOwnership: LoadInst.LoadOwnership { get }
  var canLoadValue: Bool { get }
  func trySplit(_ context: FunctionPassContext) -> Bool
  func materializeLoadForReplacement(_ context: FunctionPassContext) -> LoadInst
}

extension LoadInst : LoadingInstruction {
  // We know that the type is loadable because - well - this is a load.
  var canLoadValue: Bool { true }

  // Nothing to materialize, because this is already a `load`.
  func materializeLoadForReplacement(_ context: FunctionPassContext) -> LoadInst { return self }

  func replaceEfficiently(with newValue: Value, _ context: FunctionPassContext) {
    if let existingLoad = newValue as? LoadInst {
      // As we are processing the loads in reverse control flow order, the replaced loads might accumulate
      // quite a lot of users. This happens if the are many loads from the same location in a row.
      // To avoid quadratic complexity in `uses.replaceAll`, we swap both load instructions and move the uses
      // from the `existingLoad` (which usually has a small number of uses) to this load - and delete the
      // `existingLoad`.
      existingLoad.uses.replaceAll(with: self, context)
      self.operand.set(to: existingLoad.address, context)
      self.set(ownership: existingLoad.loadOwnership, context)
      self.set(location: existingLoad.location, context)
      self.move(before: existingLoad, context)
      context.erase(instruction: existingLoad)
    } else {
      replace(with: newValue, context)
    }
  }
}

extension CopyAddrInst : LoadingInstruction {
  var address: Value { source }
  var type: Type { address.type.objectType }
  var typeIsLoadable: Bool { type.isLoadable(in: parentFunction) }

  var ownership: Ownership {
    if !parentFunction.hasOwnership || type.isTrivial(in: parentFunction) {
      return .none
    }
    // Regardless of if the copy is taking or copying, the loaded value is an owned value.
    return .owned