//===- bolt/Passes/ValidateInternalCalls.h ----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef BOLT_PASSES_VALIDATEINTERNALCALLS_H
#define BOLT_PASSES_VALIDATEINTERNALCALLS_H

#include "bolt/Passes/BinaryPasses.h"

namespace llvm {
namespace bolt {

/// Post-processing for internal calls. What are those? They are call
/// instructions that do not transfer control to another function, but
/// rather branch to a basic block inside the caller function itself.
/// This pass checks that the internal calls observed in a function are
/// manageable. We support two types:
///
///   1. Position Independent Code (PIC) tricks: in this type of internal
///      call, we don't really have a call because the return address is
///      not utilized for branching to, but only as a base address to
///      reference other objects. We call it a "trick" because this is not
///      the standard way a compiler would do this and this will often come
///      from awkwardly written assembly code.
///
///   2. Real internal calls: in this case, a function was inlined inside
///      a caller, but the CALL instruction wasn't removed. This pair of
///      caller-callee is treated as a single function and is analyzed
///      here.
///
/// In general, the rest of the BOLT pipeline (other optimizations, including
/// code reordering) will not support neither of these cases. In this pass,
/// we just identify them, verify they are safe (do not reference objects
/// that will be moved after reordering) and freeze these functions in the
/// way they were read. We do this by marking them as non-simple.
///
/// Why do we freeze them?
///
/// Type 1 is not safe to optimize because any changed offsets will break the
/// PIC references made in this code. Type 2 is not safe to optimize because
/// it requires BOLT to understand a new CFG format where internal calls are
/// broken into two BBs (calling block and returning block), and we currently do
/// not support this  elsewhere. Only this pass is able to make sense of these
/// non-canonical CFGs (specifically, fixBranches does not support them).
///
class ValidateInternalCalls : public BinaryFunctionPass {
public:
  explicit ValidateInternalCalls(const cl::opt<bool> &PrintPass)
      : BinaryFunctionPass(PrintPass) {}

  const char *getName() const override { return "validate-internal-calls"; }

  void runOnFunctions(BinaryContext &BC) override;

private:
  /// Fix the CFG to take into consideration internal calls that do not
  /// return, but are only used as a trick to perform Position Independent
  /// Code (PIC) computations. This will change internal calls to be treated
  /// as unconditional jumps.
  bool fixCFGForPIC(BinaryFunction &Function) const;

  /// Fix the CFG to take into consideration real internal calls (whole
  /// functions that got inlined inside its caller, but the CALL instruction
  /// wasn't removed).
  bool fixCFGForIC(BinaryFunction &Function) const;

  /// Detect tail calls in the range of the PIC access and fail to validate if
  /// one is detected. Tail calls are dangerous because they may be emitted
  /// with a different size in comparison with the original code.
  /// FIXME: shortenInstructions and NOP sizes can impact offsets too
  bool hasTailCallsInRange(BinaryFunction &Function) const;

  /// Check that the PIC computations performed by Type 1 internal calls are
  /// safe
  bool analyzeFunction(BinaryFunction &Function) const;

  /// The annotation tag we use to keep track of internal calls we already
  /// processed.
  StringRef getProcessedICTag() const { return "ProcessedInternalCall"; }

  void clearAnnotations(BinaryFunction &Function) const {
    const BinaryContext &BC = Function.getBinaryContext();
    for (BinaryBasicBlock &BB : Function)
      for (MCInst &Inst : BB)
        BC.MIB->removeAnnotation(Inst, getProcessedICTag());
  }
};

} // namespace bolt
} // namespace llvm

#endif
