//===- Evaluator.h - LLVM IR evaluator --------------------------*- 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
//
//===----------------------------------------------------------------------===//
//
// Function evaluator for LLVM IR.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_TRANSFORMS_UTILS_EVALUATOR_H
#define LLVM_TRANSFORMS_UTILS_EVALUATOR_H

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/Support/Casting.h"
#include <cassert>
#include <deque>
#include <memory>

namespace llvm {

class CallBase;
class DataLayout;
class Function;
class TargetLibraryInfo;

/// This class evaluates LLVM IR, producing the Constant representing each SSA
/// instruction.  Changes to global variables are stored in a mapping that can
/// be iterated over after the evaluation is complete.  Once an evaluation call
/// fails, the evaluation object should not be reused.
class Evaluator {
  struct MutableAggregate;

  /// The evaluator represents values either as a Constant*, or as a
  /// MutableAggregate, which allows changing individual aggregate elements
  /// without creating a new interned Constant.
  class MutableValue {
    PointerUnion<Constant *, MutableAggregate *> Val;
    void clear();
    bool makeMutable();

  public:
    MutableValue(Constant *C) { Val = C; }
    MutableValue(const MutableValue &) = delete;
    MutableValue(MutableValue &&Other) {
      Val = Other.Val;
      Other.Val = nullptr;
    }
    ~MutableValue() { clear(); }

    Type *getType() const {
      if (auto *C = Val.dyn_cast<Constant *>())
        return C->getType();
      return Val.get<MutableAggregate *>()->Ty;
    }

    Constant *toConstant() const {
      if (auto *C = Val.dyn_cast<Constant *>())
        return C;
      return Val.get<MutableAggregate *>()->toConstant();
    }

    Constant *read(Type *Ty, APInt Offset, const DataLayout &DL) const;
    bool write(Constant *V, APInt Offset, const DataLayout &DL);
  };

  struct MutableAggregate {
    Type *Ty;
    SmallVector<MutableValue> Elements;

    MutableAggregate(Type *Ty) : Ty(Ty) {}
    Constant *toConstant() const;
  };

public:
  Evaluator(const DataLayout &DL, const TargetLibraryInfo *TLI)
      : DL(DL), TLI(TLI) {
    ValueStack.emplace_back();
  }

  ~Evaluator() {
    for (auto &Tmp : AllocaTmps)
      // If there are still users of the alloca, the program is doing something
      // silly, e.g. storing the address of the alloca somewhere and using it
      // later.  Since this is undefined, we'll just make it be null.
      if (!Tmp->use_empty())
        Tmp->replaceAllUsesWith(Constant::getNullValue(Tmp->getType()));
  }

  /// Evaluate a call to function F, returning true if successful, false if we
  /// can't evaluate it.  ActualArgs contains the formal arguments for the
  /// function.
  bool EvaluateFunction(Function *F, Constant *&RetVal,
                        const SmallVectorImpl<Constant*> &ActualArgs);

  DenseMap<GlobalVariable *, Constant *> getMutatedInitializers() const {
    DenseMap<GlobalVariable *, Constant *> Result;
    for (auto &Pair : MutatedMemory)
      Result[Pair.first] = Pair.second.toConstant();
    return Result;
  }

  const SmallPtrSetImpl<GlobalVariable *> &getInvariants() const {
    return Invariants;
  }

private:
  bool EvaluateBlock(BasicBlock::iterator CurInst, BasicBlock *&NextBB,
                     bool &StrippedPointerCastsForAliasAnalysis);

  Constant *getVal(Value *V) {
    if (Constant *CV = dyn_cast<Constant>(V)) return CV;
    Constant *R = ValueStack.back().lookup(V);
    assert(R && "Reference to an uncomputed value!");
    return R;
  }

  void setVal(Value *V, Constant *C) {
    ValueStack.back()[V] = C;
  }

  /// Casts call result to a type of bitcast call expression
  Constant *castCallResultIfNeeded(Type *ReturnType, Constant *RV);

  /// Given call site return callee and list of its formal arguments
  Function *getCalleeWithFormalArgs(CallBase &CB,
                                    SmallVectorImpl<Constant *> &Formals);

  /// Given call site and callee returns list of callee formal argument
  /// values converting them when necessary
  bool getFormalParams(CallBase &CB, Function *F,
                       SmallVectorImpl<Constant *> &Formals);

  Constant *ComputeLoadResult(Constant *P, Type *Ty);
  Constant *ComputeLoadResult(GlobalVariable *GV, Type *Ty,
                              const APInt &Offset);

  /// As we compute SSA register values, we store their contents here. The back
  /// of the deque contains the current function and the stack contains the
  /// values in the calling frames.
  std::deque<DenseMap<Value*, Constant*>> ValueStack;

  /// This is used to detect recursion.  In pathological situations we could hit
  /// exponential behavior, but at least there is nothing unbounded.
  SmallVector<Function*, 4> CallStack;

  /// For each store we execute, we update this map.  Loads check this to get
  /// the most up-to-date value.  If evaluation is successful, this state is
  /// committed to the process.
  DenseMap<GlobalVariable *, MutableValue> MutatedMemory;

  /// To 'execute' an alloca, we create a temporary global variable to represent
  /// its body.  This vector is needed so we can delete the temporary globals
  /// when we are done.
  SmallVector<std::unique_ptr<GlobalVariable>, 32> AllocaTmps;

  /// These global variables have been marked invariant by the static
  /// constructor.
  SmallPtrSet<GlobalVariable*, 8> Invariants;

  /// These are constants we have checked and know to be simple enough to live
  /// in a static initializer of a global.
  SmallPtrSet<Constant*, 8> SimpleConstants;

  const DataLayout &DL;
  const TargetLibraryInfo *TLI;
};

} // end namespace llvm

#endif // LLVM_TRANSFORMS_UTILS_EVALUATOR_H
