Skip to content
Snippets Groups Projects
  • Alessandro Di Federico's avatar
    f8199625
    Switch to new assertion system globally · f8199625
    Alessandro Di Federico authored
    This commit enforces on the whole project the usage of our own assertion
    system. This means all calls to `abort`, `assert` and `llvm_unreachable`
    have been replaced with calls to `revng_abort`, `revng_assert` and
    `revng_unreachable`, respectively.
    
    The error messages, usually expressed as `assert(Condition &&
    "Message")` have now been replaced using the second (optional) argument
    of `revng_assert`.
    
    Additionally, all the `assert(false)` statements have been replaced with
    calls to `revng_abort`. Apart from readibility, this ensures the
    compilers is aware of the fact that call will never return.
    
    This change will enable us to drop many statements whose sole purpose
    was marking a variable employed in an assertion as used in release
    mode. In fact, with the new assertion system this is no longer
    necessary.
    f8199625
    History
    Switch to new assertion system globally
    Alessandro Di Federico authored
    This commit enforces on the whole project the usage of our own assertion
    system. This means all calls to `abort`, `assert` and `llvm_unreachable`
    have been replaced with calls to `revng_abort`, `revng_assert` and
    `revng_unreachable`, respectively.
    
    The error messages, usually expressed as `assert(Condition &&
    "Message")` have now been replaced using the second (optional) argument
    of `revng_assert`.
    
    Additionally, all the `assert(false)` statements have been replaced with
    calls to `revng_abort`. Apart from readibility, this ensures the
    compilers is aware of the fact that call will never return.
    
    This change will enable us to drop many statements whose sole purpose
    was marking a variable employed in an assertion as used in release
    mode. In fact, with the new assertion system this is no longer
    necessary.
noreturnanalysis.h 3.96 KiB
#ifndef NORETURNANALYSIS_H
#define NORETURNANALYSIS_H

//
// This file is distributed under the MIT License. See LICENSE.md for details.
//

// Standard includes
#include <cstdint>
#include <map>
#include <vector>

// LLVM includes
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"

// Local includes
#include "reachingdefinitions.h"
#include "revamb.h"

namespace llvm {
class BasicBlock;
class CallInst;
class Instruction;
class LoadInst;
class StoreInst;
class TerminatorInst;
} // namespace llvm

class NoReturnAnalysis {
public:
  NoReturnAnalysis(Architecture TheArchitecture) :
    SourceArchitecture(TheArchitecture),
    NoDCE(nullptr) {}

  /// Records all the calls to the syscall helper and inject a sentinel load
  /// from the syscall number register
  void registerSyscalls(llvm::Function *F);

  /// \brief Use \p CRL to collect all the definitions reaching the sentinel
  ///        load
  void collectDefinitions(ConditionalReachedLoadsPass &CRL);

  /// \brief Return true if \p Store is ever used to write the syscall number
  bool setsSyscallNumber(llvm::StoreInst *Store);

  /// \brief If appropriate, register \p Setter's basic block as a killer
  ///
  /// \param StoredValue the value stored by \p Definition starting from \p
  ///        Setter
  /// \param Setter the instruction responsible for making \p Definition store
  ///        \p StoredValue
  /// \param Definition the definition that will end up in the syscall register
  // TODO: check from Setter you have to get to Definition
  void registerKiller(uint64_t StoredValue,
                      llvm::Instruction *Setter,
                      llvm::Instruction *Definition);

  using PredecessorsMap = std::map<llvm::BasicBlock *,
                                   std::vector<llvm::BasicBlock *>>;
  /// Add to the set of killer basic blocks all the basic blocks who can only
  /// end in one of those already registered.
  void computeKillerSet(PredecessorsMap &CallPredecessors);

  void setDispatcher(llvm::BasicBlock *BB) { Dispatcher = BB; }

  /// \brief Check if the given basic block as has been registered as a killer
  bool isNoreturnBasicBlock(llvm::BasicBlock *BB) {
    for (llvm::BasicBlock *Successor : successors(BB))
      if (!isKiller(Successor))
        return false;
    return true;
  }

  void cleanup() {
    // Cleanup all the calls to "nodce"
    if (NoDCE != nullptr) {
      for (llvm::User *NoDCEUser : NoDCE->users())
        llvm::cast<llvm::CallInst>(NoDCEUser)->eraseFromParent();

      NoDCE->eraseFromParent();
    }
  }

private:
  bool isKiller(llvm::BasicBlock *BB) const {
    return KillerBBs.count(BB) != 0;
  };

  /// \brief Register BB as killer and associate a noreturn metadata to it
  void registerKiller(llvm::BasicBlock *BB, KillReason::Values Reason) {
    KillerBBs.insert(BB);

    if (!BB->empty()) {
      llvm::TerminatorInst *Terminator = BB->getTerminator();
      revng_assert(Terminator != nullptr);
      if (Terminator->getMetadata("noreturn") == nullptr) {
        QuickMetadata QMD(getContext(BB));
        Terminator->setMetadata("noreturn",
                                QMD.tuple(KillReason::getName(Reason)));
      }
    }
  }

  bool endsUpIn(llvm::Instruction *I, llvm::BasicBlock *Target);

  bool checkKiller(llvm::BasicBlock *BB) const {
    if (BB == Dispatcher)
      return false;

    for (llvm::BasicBlock *Successor : successors(BB))
      if (BB != Dispatcher && KillerBBs.count(Successor) == 0)
        return false;
    return true;
  }

  bool hasSyscalls() const { return NoDCE != nullptr; }

  /// \brief Register as killer basic blocks those parts of infinite loops
  void findInfinteLoops();

private:
  Architecture SourceArchitecture;
  std::set<llvm::CallInst *> RegisteredSyscalls;
  std::vector<llvm::LoadInst *> SyscallRegisterReads;
  std::set<llvm::StoreInst *> SyscallRegisterDefinitions;
  std::set<llvm::BasicBlock *> KillerBBs;
  llvm::BasicBlock *Dispatcher;
  llvm::Function *NoDCE;
};

#endif // NORETURNANALYSIS_H