lib/IR/IRVerifier.cpp (667 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "hermes/IR/IRVerifier.h"
#include "hermes/IR/CFG.h"
#include "hermes/IR/IRVisitor.h"
#include "hermes/IR/Instrs.h"
#include "llvh/ADT/DenseMap.h"
#include "llvh/Support/Casting.h"
using llvh::dyn_cast;
using llvh::isa;
using llvh::raw_null_ostream;
using namespace hermes;
#ifdef HERMES_SLOW_DEBUG
namespace {
#define INCLUDE_ALL_INSTRS
/// IR Verifier - the verifier checks some basic properties of the IR to make
/// sure that it is not incorrect. For example, it checks that instructions are
/// dominated by their oparands and that the entry block has no predecessors.
///
/// The verifier also checks if the IR is in optimized form that allows bytecode
/// generation. For example, it checks that there are no unreachable basic
/// blocks.
class Verifier : public InstructionVisitor<Verifier, void> {
class FunctionState; // Forward declaration of inner class.
bool valid{true};
Context *Ctx{nullptr};
raw_ostream &OS;
VerificationMode verificationMode;
/// State for the function currently being verified.
FunctionState *functionState{};
/// Cached users of each value, lazily computed.
llvh::DenseMap<const Value *, llvh::DenseSet<const Value *>> cachedUsers{};
/// Verifier state per-function is kept here.
class FunctionState {
Verifier *verifier;
/// Save the previous state here so we can restore it in our destructor.
FunctionState *oldState;
public:
const Function &function;
bool createArgumentsEncountered = false;
FunctionState(Verifier *verifier, const Function &function)
: verifier(verifier),
oldState(verifier->functionState),
function(function) {
verifier->functionState = this;
}
~FunctionState() {
verifier->functionState = oldState;
}
};
bool isVerifyingOptimalIR() {
return verificationMode == VerificationMode::IR_OPTIMIZED;
}
public:
explicit Verifier(raw_ostream &OS, VerificationMode mode)
: OS(OS), verificationMode(mode) {}
bool verify(const Module &M) {
valid = true;
Ctx = &M.getContext();
visitModule(M);
return valid;
};
/// Reimplement in Verifier class for custom behavior.
void beforeVisitInstruction(const Instruction &I);
void visitModule(const Module &M);
void visitFunction(const Function &F);
void visitBasicBlock(const BasicBlock &BB);
#define DEF_VALUE(XX, PARENT) void visit##XX(const XX &Inst);
#include "hermes/IR/Instrs.def"
private:
/// Implement verification for a switch-like instruction.
template <class T>
void visitSwitchLikeInst(const T &Inst);
/// Implement verification for a conditional branch-like instruction.
template <class T>
void visitCondBranchLikeInst(const T &Inst);
/// Implement verification for a binaryoperator-like instruction.
template <class T>
void visitBinaryOperatorLikeInst(const T &Inst);
/// \return a DenseSet of users for a Value, computed lazily.
const llvh::DenseSet<const Value *> &getUsersSetForValue(const Value *V) {
auto &users = cachedUsers[V];
if (users.empty()) {
users.insert(V->users_begin(), V->users_end());
}
return users;
}
};
// TODO: Need to make this accept format strings
#define Assert(C, ...) \
do { \
if (!(C)) { \
valid = false; \
if (functionState) \
OS << (__VA_ARGS__) << " in function " \
<< functionState->function.getInternalNameStr() << '\n'; \
else \
OS << (__VA_ARGS__) << '\n'; \
return; \
} \
} while (0)
void Verifier::visitModule(const Module &M) {
// Verify all functions are valid
for (Module::const_iterator I = M.begin(); I != M.end(); I++) {
Assert(I->getParent() == &M, "Function's parent does not match module");
visitFunction(*I);
}
}
void Verifier::visitFunction(const Function &F) {
Assert(&F.getContext() == Ctx, "Function has wrong context");
if (F.isLazy())
return;
FunctionState newFunctionState(this, F);
// Verify all basic blocks are valid
for (auto I = F.begin(); I != F.end(); I++) {
Assert(
I->getParent() == &F, "Basic Block's parent does not match functiion");
visitBasicBlock(*I);
}
// Verify all parameters are valid
for (auto I = F.arg_begin(); I != F.arg_end(); I++) {
Assert(
(*I)->getParent() == &F, "Parameter's parent does not match function");
}
// Verify Dominance Tree is valid
DominanceInfo D(const_cast<Function *>(&F));
Assert(
D.getRoot() == &*F.begin(),
"Root node in dominance tree should be the entry basic block");
for (const auto &I : F) {
if (isVerifyingOptimalIR()) {
// Check for unreachable blocks.
Assert(
D.isReachableFromEntry(&I),
"Basic Block unreachable from entry in the Dominance Tree");
}
// Domination checks within a block are linear, so for huge blocks we get
// quadratic runtime. To avoid this, we store a set of instructions we've
// seen so far in the current block.
llvh::SmallPtrSet<const Instruction *, 32> seen;
// Instruction dominance check
for (BasicBlock::const_iterator II = I.begin(); II != I.end(); II++) {
// Check that incoming phi node values are dominated in their incoming
// blocks.
if (auto *Phi = llvh::dyn_cast<PhiInst>(&*II)) {
for (int i = 0, e = Phi->getNumEntries(); i < e; ++i) {
auto pair = Phi->getEntry(i);
BasicBlock *block = pair.second;
auto *inst = llvh::dyn_cast<Instruction>(pair.first);
// Non-instructions always dominate everything. Move on.
if (!inst) {
continue;
}
// Make sure that the incoming value dominates the incoming block.
Assert(
D.dominates(inst->getParent(), block),
"Incoming PHI value must dominate incoming basic block");
}
continue;
}
// Check that all instructions are dominated by their operands.
for (unsigned i = 0; i < II->getNumOperands(); i++) {
auto Operand = II->getOperand(i);
if (auto *InstOp = llvh::dyn_cast<Instruction>(Operand)) {
Assert(
seen.count(InstOp) || D.properlyDominates(InstOp, &*II),
"Operand must dominates the Instruction");
}
}
seen.insert(&*II);
}
}
}
void Verifier::visitBasicBlock(const BasicBlock &BB) {
Assert(&BB.getContext() == Ctx, "Basic Block has wrong context");
Assert(BB.getTerminator(), "Basic block must have a terminator.");
// Verify the mutual predecessor/successor relationship
for (auto I = succ_begin(&BB), E = succ_end(&BB); I != E; ++I) {
Assert(
pred_contains(*I, &BB),
"Cannot find self in the predecessors of a successor");
}
for (auto I = pred_begin(&BB), E = pred_end(&BB); I != E; ++I) {
Assert(
succ_contains(*I, &BB),
"Cannot find self in the successors of a predecessor");
}
// Verify each instruction
for (BasicBlock::const_iterator I = BB.begin(); I != BB.end(); I++) {
Assert(
I->getParent() == &BB,
"Instruction's parent does not match basic block");
// Use the instruction using the InstructionVisitor::visit();
visit(*I);
}
}
void Verifier::beforeVisitInstruction(const Instruction &Inst) {
// TODO: Verify the instruction is valid, need switch/case on each
// actual Instruction type
Assert(&Inst.getContext() == Ctx, "Instruction has wrong context");
// Verify all operands are valid
for (unsigned i = 0; i < Inst.getNumOperands(); i++) {
auto Operand = Inst.getOperand(i);
Assert(Operand != nullptr, "Invalid operand");
Assert(
getUsersSetForValue(Operand).count(&Inst) == 1,
"This instruction is not in the User list of the operand");
if (llvh::isa<Variable>(Operand)) {
Assert(
llvh::isa<LoadFrameInst>(Inst) || llvh::isa<StoreFrameInst>(Inst) ||
llvh::isa<HBCLoadFromEnvironmentInst>(Inst) ||
llvh::isa<HBCStoreToEnvironmentInst>(Inst),
"Variable can only be accessed in "
"LoadFrame/StoreFrame/HBCLoadFromEnvironmentInst/HBCStoreToEnvironmentInst Inst.");
}
if (llvh::isa<AllocStackInst>(Operand)) {
Assert(
llvh::isa<LoadStackInst>(Inst) || llvh::isa<StoreStackInst>(Inst) ||
llvh::isa<CatchInst>(Inst) || llvh::isa<GetPNamesInst>(Inst) ||
llvh::isa<CheckHasInstanceInst>(Inst) ||
llvh::isa<GetNextPNameInst>(Inst) ||
llvh::isa<ResumeGeneratorInst>(Inst) ||
llvh::isa<IteratorBeginInst>(Inst) ||
llvh::isa<IteratorNextInst>(Inst) ||
llvh::isa<IteratorCloseInst>(Inst) ||
llvh::isa<HBCGetArgumentsPropByValInst>(Inst) ||
llvh::isa<HBCGetArgumentsLengthInst>(Inst) ||
llvh::isa<HBCReifyArgumentsInst>(Inst),
"Stack variable can only be accessed in certain instructions.");
}
}
// Verify that terminator instructions never need to return a value.
if (llvh::isa<TerminatorInst>(Inst)) {
Assert(Inst.getNumUsers() == 0, "Terminator Inst cannot return value.");
}
}
static bool isTerminator(const Instruction *Inst) {
return &*Inst->getParent()->rbegin() == Inst;
}
void Verifier::visitSingleOperandInst(const SingleOperandInst &Inst) {}
void Verifier::visitReturnInst(const ReturnInst &Inst) {
Assert(
isTerminator(&Inst),
"Return Instruction must be the last instruction of a basic block");
Assert(Inst.getNumSuccessors() == 0, "ReturnInst should not have successors");
}
void Verifier::visitSaveAndYieldInst(const SaveAndYieldInst &Inst) {
Assert(isTerminator(&Inst), "SaveAndYield must be a terminator");
Assert(
Inst.getParent()->getTerminator() == &Inst,
"SaveAndYield must be the terminator");
}
void Verifier::visitBranchInst(const BranchInst &Inst) {
Assert(
isTerminator(&Inst),
"Branch Instruction must be the last instruction of a basic block");
Assert(
Inst.getNumSuccessors() == 1,
"Branch Instruction should have only 1 successor");
Assert(
succ_contains(Inst.getParent(), Inst.getBranchDest()),
"Branch Dest Basic Block does not match with successor pointer");
Assert(
pred_contains(Inst.getBranchDest(), Inst.getParent()),
"BranchInst Basic Block not in the predecessor list of target block");
}
void Verifier::visitHBCStoreToEnvironmentInst(
const HBCStoreToEnvironmentInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCLoadFromEnvironmentInst(
const HBCLoadFromEnvironmentInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCResolveEnvironment(const HBCResolveEnvironment &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitAsNumberInst(const AsNumberInst &Inst) {
Assert(
!isTerminator(&Inst),
"Non-terminator cannot be the last instruction of a basic block");
Assert(
Inst.getType() == Type::createNumber(),
"AsNumberInst must return a number type");
}
void Verifier::visitAsInt32Inst(const AsInt32Inst &Inst) {
Assert(
!isTerminator(&Inst),
"Non-terminator cannot be the last instruction of a basic block");
Assert(Inst.getType().isInt32Type(), "AsInt32Inst must return an Int32 type");
}
void Verifier::visitAddEmptyStringInst(const AddEmptyStringInst &Inst) {
Assert(
!isTerminator(&Inst),
"Non-terminator cannot be the last instruction of a basic block");
Assert(
Inst.getType() == Type::createString(),
"AddEmptyStringInst must return a string type");
}
void Verifier::visitAllocStackInst(const AllocStackInst &Inst) {
Assert(
&(Inst.getParent()->back()) != &Inst,
"Alloca Instruction cannot be the last instruction of a basic block");
}
template <class T>
void Verifier::visitCondBranchLikeInst(const T &Inst) {
Assert(
isTerminator(&Inst),
"CondBranchInst must be the last instruction of a basic block");
Assert(
Inst.getNumSuccessors() == 2, "CondBranchInst should have 2 successors");
Assert(
succ_contains(Inst.getParent(), Inst.getTrueDest()),
"True dest should be a successor of CondBranchInst block");
Assert(
pred_contains(Inst.getTrueDest(), Inst.getParent()),
"CondBranchInst block should be a predecessor of true dest");
Assert(
succ_contains(Inst.getParent(), Inst.getFalseDest()),
"False dest should be a successor of CondBranchInst block");
Assert(
pred_contains(Inst.getFalseDest(), Inst.getParent()),
"CondBranchInst block should be a predecessor of false dest");
}
void Verifier::visitCondBranchInst(const CondBranchInst &Inst) {
visitCondBranchLikeInst(Inst);
}
void Verifier::visitUnaryOperatorInst(const UnaryOperatorInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitTryStartInst(const TryStartInst &Inst) {
Assert(isTerminator(&Inst), "TryStartInst must be a TermInst");
Assert(Inst.getNumSuccessors() == 2, "TryStartInst must have 2 successors");
Assert(
Inst.getCatchTarget()->front().getKind() == ValueKind::CatchInstKind,
"Catch Target of TryStartInst must begin with a CatchInst");
}
void Verifier::visitTryEndInst(const TryEndInst &Inst) {
Assert(
&Inst == &Inst.getParent()->front(),
"TryEndInst must be the first instruction of a block");
Assert(
pred_count(Inst.getParent()) == 1,
"TryEndInst must have only one predecessor.");
}
void Verifier::visitStoreStackInst(const StoreStackInst &Inst) {
Assert(
!llvh::isa<AllocStackInst>(Inst.getValue()),
"Storing the address of stack location");
Assert(!Inst.hasUsers(), "Store Instructions must not have users");
}
void Verifier::visitStoreFrameInst(const StoreFrameInst &Inst) {
Assert(!Inst.hasUsers(), "Store Instructions must not have users");
}
void Verifier::visitLoadFrameInst(const LoadFrameInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitLoadStackInst(const LoadStackInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitCreateFunctionInst(const CreateFunctionInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitCallInst(const CallInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCCallNInst(const HBCCallNInst &Inst) {
Assert(
HBCCallNInst::kMinArgs <= Inst.getNumArguments() &&
Inst.getNumArguments() <= HBCCallNInst::kMaxArgs,
"CallNInst has too many args");
}
void Verifier::visitCallBuiltinInst(CallBuiltinInst const &Inst) {
assert(
isNativeBuiltin(Inst.getBuiltinIndex()) &&
"CallBuiltin must take a native builtin.");
visitCallInst(Inst);
}
void Verifier::visitGetBuiltinClosureInst(GetBuiltinClosureInst const &Inst) {
assert(
Inst.getBuiltinIndex() < BuiltinMethod::_count &&
"Out of bound BuiltinMethod index.");
}
#ifdef HERMES_RUN_WASM
void Verifier::visitCallIntrinsicInst(CallIntrinsicInst const &Inst) {
assert(
Inst.getIntrinsicsIndex() < WasmIntrinsics::_count &&
"Out of bound Unsafe Compiler Intrinsics Index.");
}
#endif
void Verifier::visitHBCCallDirectInst(HBCCallDirectInst const &Inst) {
Assert(
llvh::isa<Function>(Inst.getCallee()),
"HBCCallDirect callee must be a Function");
Assert(
Inst.getNumArguments() <= HBCCallDirectInst::MAX_ARGUMENTS,
"CallBuiltin too many arguments");
visitCallInst(Inst);
}
void Verifier::visitLoadPropertyInst(const LoadPropertyInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitTryLoadGlobalPropertyInst(
const TryLoadGlobalPropertyInst &Inst) {
visitLoadPropertyInst(Inst);
}
void Verifier::visitDeletePropertyInst(const DeletePropertyInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitStorePropertyInst(const StorePropertyInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitTryStoreGlobalPropertyInst(
const TryStoreGlobalPropertyInst &Inst) {
visitStorePropertyInst(Inst);
}
void Verifier::visitStoreOwnPropertyInst(const StoreOwnPropertyInst &Inst) {
Assert(
llvh::isa<LiteralBool>(
Inst.getOperand(StoreOwnPropertyInst::IsEnumerableIdx)),
"StoreOwnPropertyInst::IsEnumerable must be a boolean literal");
}
void Verifier::visitStoreNewOwnPropertyInst(
const StoreNewOwnPropertyInst &Inst) {
visitStoreOwnPropertyInst(Inst);
Assert(
llvh::isa<LiteralString>(
Inst.getOperand(StoreOwnPropertyInst::PropertyIdx)),
"StoreNewOwnPropertyInst::Property must be a string literal");
Assert(
Inst.getObject()->getType().isObjectType(),
"StoreNewOwnPropertyInst::Object must be known to be an object");
}
void Verifier::visitStoreGetterSetterInst(const StoreGetterSetterInst &Inst) {
Assert(
llvh::isa<LiteralBool>(
Inst.getOperand(StoreGetterSetterInst::IsEnumerableIdx)),
"StoreGetterSetterInsr::IsEnumerable must be a boolean constant");
}
void Verifier::visitAllocObjectInst(const hermes::AllocObjectInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitAllocArrayInst(const hermes::AllocArrayInst &Inst) {
LiteralNumber *size = Inst.getSizeHint();
Assert(size->isUInt32Representible(), "Invalid AllocArrayInst size hint");
if (!Ctx->getCodeGenerationSettings().unlimitedRegisters) {
Assert(
Inst.isLiteralArray(),
"Array elements must be literal when registers are limited");
}
}
void Verifier::visitCreateArgumentsInst(const CreateArgumentsInst &Inst) {
Assert(functionState, "functionState cannot be null");
Assert(
!functionState->createArgumentsEncountered,
"There should be only one CreateArgumentsInst in a function");
functionState->createArgumentsEncountered = true;
BasicBlock *BB = Inst.getParent();
Function *F = BB->getParent();
if (llvh::isa<GeneratorInnerFunction>(F)) {
auto secondBB = F->begin();
++secondBB;
Assert(
BB == &*secondBB,
"CreateArgumentsInst must be in the second basic block in generators");
} else {
Assert(
BB == &*F->begin(),
"CreateArgumentsInst must be in the first basic block");
}
}
void Verifier::visitCreateRegExpInst(CreateRegExpInst const &Inst) {
// Nothing to verify at this point.
}
template <class T>
void Verifier::visitBinaryOperatorLikeInst(const T &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitBinaryOperatorInst(const hermes::BinaryOperatorInst &Inst) {
visitBinaryOperatorLikeInst(Inst);
}
void Verifier::visitCatchInst(const CatchInst &Inst) {
Assert(
&Inst.getParent()->front() == &Inst,
"Catch instruction must be the first in a basic block");
Assert(
pred_count(Inst.getParent()) == 1,
"CatchInst must have only 1 predecessor.");
}
void Verifier::visitThrowInst(const ThrowInst &Inst) {
Assert(isTerminator(&Inst), "ThrowInst must be a terminator");
Assert(
Inst.getNumSuccessors() == 0,
"visitThrowInst should not have successors");
}
void Verifier::visitConstructInst(const ConstructInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitGetNextPNameInst(const GetNextPNameInst &Inst) {
Assert(isTerminator(&Inst), "GetNextPNameInst must terminate the block");
Assert(
Inst.getNumSuccessors() == 2,
"GetNextPNameInst should have 2 successors");
Assert(
succ_contains(Inst.getParent(), Inst.getOnSomeDest()),
"OnSome dest should be a successor of GetNextPNameInst block");
Assert(
pred_contains(Inst.getOnSomeDest(), Inst.getParent()),
"GetNextPNameInst block should be a predecessor of OnSome dest");
Assert(
succ_contains(Inst.getParent(), Inst.getOnLastDest()),
"OnLast dest should be a successor of GetNextPNameInst block");
Assert(
pred_contains(Inst.getOnLastDest(), Inst.getParent()),
"GetNextPNameInst block should be a predecessor of OnLast dest");
}
void Verifier::visitGetPNamesInst(const GetPNamesInst &Inst) {
Assert(isTerminator(&Inst), "GetPNamesInst must terminate the block");
Assert(
Inst.getNumSuccessors() == 2, "GetPNamesInst should have 2 successors");
Assert(
succ_contains(Inst.getParent(), Inst.getOnSomeDest()),
"OnSome dest should be a successor of GetPNamesInst block");
Assert(
pred_contains(Inst.getOnSomeDest(), Inst.getParent()),
"GetPNamesInst block should be a predecessor of OnSome dest");
Assert(
succ_contains(Inst.getParent(), Inst.getOnEmptyDest()),
"OnEmpty dest should be a successor of GetPNamesInst block");
Assert(
pred_contains(Inst.getOnEmptyDest(), Inst.getParent()),
"GetPNamesInst block should be a predecessor of OnEmpty dest");
}
void Verifier::visitMovInst(const hermes::MovInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitImplicitMovInst(const hermes::ImplicitMovInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitCoerceThisNSInst(CoerceThisNSInst const &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitPhiInst(const hermes::PhiInst &Inst) {
// We verify the dominance property when we scan the whole function. In here
// we only verify local properties.
llvh::DenseMap<BasicBlock *, Value *> entries(8);
// Check that every input block enters only once:
for (int i = 0, e = Inst.getNumEntries(); i < e; ++i) {
auto pair = Inst.getEntry(i);
BasicBlock *block = pair.second;
Assert(
pred_contains(Inst.getParent(), block),
"Predecessor not covered by phi node!");
Assert(
succ_contains(block, Inst.getParent()),
"Phi node should be Successor!");
Value *value = pair.first;
auto result = entries.find(block);
if (result == entries.end()) {
entries[block] = value;
} else {
Assert(
value == result->second,
"Phi node has different inputs for the same block.");
}
}
Assert(
entries.size() == pred_count_unique(Inst.getParent()),
"number of predecessors does not match phi inputs");
}
template <class T>
void Verifier::visitSwitchLikeInst(const T &Inst) {
Assert(isTerminator(&Inst), "SwitchInst must be a terminator");
Assert(
Inst.getNumCasePair() > 0, "SwitchInst must have some case destinations");
Assert(Inst.getDefaultDestination(), "Invalid destination block");
Assert(Inst.getInputValue(), "Invalid input value");
Assert(
Inst.getNumSuccessors() == Inst.getNumCasePair() + 1,
"Number of successors of SwitchInst does not match.");
Assert(
succ_contains(Inst.getParent(), Inst.getDefaultDestination()),
"Default destination must be a successor of SwitchInst block");
Assert(
pred_contains(Inst.getDefaultDestination(), Inst.getParent()),
"SwitchInst block must be a predecessor of default destination.");
for (unsigned idx = 0, e = Inst.getNumCasePair(); idx < e; ++idx) {
Assert(
succ_contains(Inst.getParent(), Inst.getCasePair(idx).second),
"Case target must be a successor of SwitchInst block");
Assert(
pred_contains(Inst.getCasePair(idx).second, Inst.getParent()),
"SwitchInst block must be a predecessor of case target");
}
// Verify that each case is unique.
llvh::SmallPtrSet<Literal *, 8> values;
for (unsigned idx = 0, e = Inst.getNumCasePair(); idx < e; ++idx) {
Assert(
values.insert(Inst.getCasePair(idx).first).second,
"switch values must be unique");
}
}
void Verifier::visitSwitchInst(const hermes::SwitchInst &Inst) {
visitSwitchLikeInst(Inst);
}
void Verifier::visitSwitchImmInst(const hermes::SwitchImmInst &Inst) {
visitSwitchLikeInst(Inst);
for (unsigned idx = 0, e = Inst.getNumCasePair(); idx < e; ++idx) {
Assert(
Inst.getCasePair(idx).first->isInt32Representible(),
"case value must be a int32");
}
}
void Verifier::visitCheckHasInstanceInst(const CheckHasInstanceInst &Inst) {
Assert(isTerminator(&Inst), "CheckHasInstanceInst must be a terminator");
Assert(
Inst.getNumSuccessors() == 2,
"CheckHasInstanceInst should have 2 successors");
}
void Verifier::visitDebuggerInst(DebuggerInst const &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitTerminatorInst(const TerminatorInst &Inst) {
Assert(false, "TerminatorInst is a virtual class.");
}
void Verifier::visitDirectEvalInst(DirectEvalInst const &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCCreateEnvironmentInst(
const HBCCreateEnvironmentInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCProfilePointInst(const HBCProfilePointInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCAllocObjectFromBufferInst(
const hermes::HBCAllocObjectFromBufferInst &Inst) {
LiteralNumber *size = Inst.getSizeHint();
Assert(
size->isUInt32Representible(),
"Invalid HBCAllocObjectFromBufferInst size hint");
Assert(
Inst.getKeyValuePairCount() > 0,
"Cannot allocate an empty HBCAllocObjectFromBufferInst");
}
void Verifier::visitAllocObjectLiteralInst(
const hermes::AllocObjectLiteralInst &Inst) {
Assert(
Inst.getKeyValuePairCount() > 0,
"Cannot allocate an empty AllocObjectLiteralInst");
}
void Verifier::visitHBCGetGlobalObjectInst(const HBCGetGlobalObjectInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCLoadConstInst(hermes::HBCLoadConstInst const &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCLoadParamInst(hermes::HBCLoadParamInst const &Inst) {
Assert(
Inst.getIndex()->isUInt32Representible(),
"HBCLoadParamInst's LiteralNumber is not a uint32.");
}
void Verifier::visitCompareBranchInst(const CompareBranchInst &Inst) {
visitCondBranchLikeInst(Inst);
visitBinaryOperatorLikeInst(Inst);
}
void Verifier::visitCreateGeneratorInst(const CreateGeneratorInst &Inst) {}
void Verifier::visitStartGeneratorInst(const StartGeneratorInst &Inst) {
Assert(
&Inst == &Inst.getParent()->front() &&
Inst.getParent() == &Inst.getParent()->getParent()->front(),
"StartGeneratorInst must be the first instruction of a function");
}
void Verifier::visitResumeGeneratorInst(const ResumeGeneratorInst &Inst) {}
void Verifier::visitHBCCreateGeneratorInst(const HBCCreateGeneratorInst &Inst) {
visitCreateGeneratorInst(Inst);
}
void Verifier::visitHBCGetThisNSInst(const HBCGetThisNSInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCGetArgumentsPropByValInst(
const HBCGetArgumentsPropByValInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCGetArgumentsLengthInst(
const HBCGetArgumentsLengthInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCReifyArgumentsInst(const HBCReifyArgumentsInst &Inst) {
// Nothing to verify at this point.
}
void Verifier::visitHBCConstructInst(const HBCConstructInst &Inst) {}
void Verifier::visitHBCCreateThisInst(const HBCCreateThisInst &Inst) {}
void Verifier::visitHBCGetConstructedObjectInst(
const HBCGetConstructedObjectInst &Inst) {}
void Verifier::visitHBCCreateFunctionInst(const HBCCreateFunctionInst &Inst) {
visitCreateFunctionInst(Inst);
}
void Verifier::visitHBCSpillMovInst(const HBCSpillMovInst &Inst) {}
void Verifier::visitUnreachableInst(const UnreachableInst &Inst) {}
void Verifier::visitIteratorBeginInst(const IteratorBeginInst &Inst) {
Assert(
llvh::isa<AllocStackInst>(Inst.getSourceOrNext()),
"SourceOrNext must be an AllocStackInst");
}
void Verifier::visitIteratorNextInst(const IteratorNextInst &Inst) {
Assert(
llvh::isa<AllocStackInst>(Inst.getSourceOrNext()),
"SourceOrNext must be an AllocStackInst");
}
void Verifier::visitIteratorCloseInst(const IteratorCloseInst &Inst) {
Assert(
llvh::isa<LiteralBool>(
Inst.getOperand(IteratorCloseInst::IgnoreInnerExceptionIdx)),
"IgnoreInnerException must be a LiteralBool in IteratorCloseInst");
}
void Verifier::visitGetNewTargetInst(GetNewTargetInst const &Inst) {
auto definitionKind = Inst.getParent()->getParent()->getDefinitionKind();
Assert(
definitionKind == Function::DefinitionKind::ES5Function ||
definitionKind == Function::DefinitionKind::ES6Constructor,
"GetNewTargetInst can only be used in ES6 constructors and ES5 functions");
}
void Verifier::visitThrowIfEmptyInst(const ThrowIfEmptyInst &Inst) {}
} // namespace
#endif
bool hermes::verifyModule(
const Module &M,
raw_ostream *OS,
VerificationMode mode) {
#ifdef HERMES_SLOW_DEBUG
raw_null_ostream NullStr;
NullStr.SetUnbuffered();
Verifier V(OS ? *OS : NullStr, mode);
return !V.verify(M);
#else
return false;
#endif
}