blob: cbd1611ad03c42f92db121de11059c711bece612 [file] [log] [blame]
// Copyright 2023 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_LANG_CORE_IR_VALIDATOR_H_
#define SRC_TINT_LANG_CORE_IR_VALIDATOR_H_
#include <string>
#include "src/tint/lang/core/ir/disassembler.h"
#include "src/tint/lang/core/ir/module.h"
#include "src/tint/utils/diagnostic/diagnostic.h"
#include "src/tint/utils/result/result.h"
// Forward declarations
namespace tint::ir {
class Access;
class ExitIf;
class ExitLoop;
class ExitSwitch;
class Let;
class LoadVectorElement;
class Return;
class StoreVectorElement;
class Var;
} // namespace tint::ir
namespace tint::ir {
/// Signifies the validation completed successfully
struct Success {};
/// Validates that a given IR module is correctly formed
/// @param mod the module to validate
/// @returns true on success, an error result otherwise
utils::Result<Success, diag::List> Validate(Module& mod);
/// The core IR validator.
class Validator {
public:
/// Create a core validator
/// @param mod the module to be validated
explicit Validator(Module& mod);
/// Destructor
~Validator();
/// Runs the validator over the module provided during construction
/// @returns the results of validation, either a success result object or the diagnostics of
/// validation failures.
utils::Result<Success, diag::List> IsValid();
protected:
/// @param inst the instruction
/// @param err the error message
/// @returns a string with the instruction name name and error message formatted
std::string InstError(Instruction* inst, std::string err);
/// Adds an error for the @p inst and highlights the instruction in the disassembly
/// @param inst the instruction
/// @param err the error string
void AddError(Instruction* inst, std::string err);
/// Adds an error for the @p inst operand at @p idx and highlights the operand in the
/// disassembly
/// @param inst the instaruction
/// @param idx the operand index
/// @param err the error string
void AddError(Instruction* inst, size_t idx, std::string err);
/// Adds an error for the @p inst result at @p idx and highlgihts the result in the disassembly
/// @param inst the instruction
/// @param idx the result index
/// @param err the error string
void AddResultError(Instruction* inst, size_t idx, std::string err);
/// Adds an error the @p block and highlights the block header in the disassembly
/// @param blk the block
/// @param err the error string
void AddError(Block* blk, std::string err);
/// Adds a note to @p inst and highlights the instruction in the disassembly
/// @param inst the instruction
/// @param err the message to emit
void AddNote(Instruction* inst, std::string err);
/// Adds a note to @p inst for operand @p idx and highlights the operand in the
/// disassembly
/// @param inst the instruction
/// @param idx the operand index
/// @param err the message string
void AddNote(Instruction* inst, size_t idx, std::string err);
/// Adds a note to @p blk and highlights the block in the disassembly
/// @param blk the block
/// @param err the message to emit
void AddNote(Block* blk, std::string err);
/// Adds an error to the diagnostics
/// @param err the message to emit
/// @param src the source lines to highlight
void AddError(std::string err, Source src = {});
/// Adds a note to the diagnostics
/// @param note the note to emit
/// @param src the source lines to highlight
void AddNote(std::string note, Source src = {});
/// @param v the value to get the name for
/// @returns the name for the given value
std::string Name(Value* v);
/// Checks the given operand is not null
/// @param inst the instruciton
/// @param operand the operand
/// @param idx the operand index
void CheckOperandNotNull(ir::Instruction* inst, ir::Value* operand, size_t idx);
/// Checks all operands in the given range (inclusive) for @p inst are not null
/// @param inst the instruction
/// @param start_operand the first operand to check
/// @param end_operand the last operand to check
void CheckOperandsNotNull(ir::Instruction* inst, size_t start_operand, size_t end_operand);
/// Validates the root block
/// @param blk the block
void CheckRootBlock(Block* blk);
/// Validates the given function
/// @param func the function validate
void CheckFunction(Function* func);
/// Validates the given block
/// @param blk the block to validate
void CheckBlock(Block* blk);
/// Validates the given instruction
/// @param inst the instruction to validate
void CheckInstruction(Instruction* inst);
/// Validates the given var
/// @param var the var to validate
void CheckVar(Var* var);
/// Validates the given let
/// @param let the let to validate
void CheckLet(Let* let);
/// Validates the given call
/// @param call the call to validate
void CheckCall(Call* call);
/// Validates the given access
/// @param a the access to validate
void CheckAccess(ir::Access* a);
/// Validates the given binary
/// @param b the binary to validate
void CheckBinary(ir::Binary* b);
/// Validates the given unary
/// @param u the unary to validate
void CheckUnary(ir::Unary* u);
/// Validates the given if
/// @param if_ the if to validate
void CheckIf(If* if_);
/// Validates the given loop
/// @param l the loop to validate
void CheckLoop(Loop* l);
/// Validates the given switch
/// @param s the switch to validate
void CheckSwitch(Switch* s);
/// Validates the given terminator
/// @param b the terminator to validate
void CheckTerminator(ir::Terminator* b);
/// Validates the given exit
/// @param e the exit to validate
void CheckExit(ir::Exit* e);
/// Validates the given exit if
/// @param e the exit if to validate
void CheckExitIf(ExitIf* e);
/// Validates the given return
/// @param r the return to validate
void CheckReturn(Return* r);
/// Validates the @p exit targets a valid @p control instruction where the instruction may jump
/// over if control instructions.
/// @param exit the exit to validate
/// @param control the control instruction targeted
void CheckControlsAllowingIf(Exit* exit, Instruction* control);
/// Validates the given exit switch
/// @param s the exit switch to validate
void CheckExitSwitch(ExitSwitch* s);
/// Validates the given exit loop
/// @param l the exit loop to validate
void CheckExitLoop(ExitLoop* l);
/// Validates the given load vector element
/// @param l the load vector element to validate
void CheckLoadVectorElement(LoadVectorElement* l);
/// Validates the given store vector element
/// @param s the store vector element to validate
void CheckStoreVectorElement(StoreVectorElement* s);
/// @param inst the instruction
/// @param idx the operand index
/// @returns the vector pointer type for the given instruction operand
const type::Type* GetVectorPtrElementType(Instruction* inst, size_t idx);
private:
Module& mod_;
diag::List diagnostics_;
Disassembler dis_{mod_};
Block* current_block_ = nullptr;
utils::Hashset<Function*, 4> seen_functions_;
utils::Vector<ControlInstruction*, 8> control_stack_;
void DisassembleIfNeeded();
};
} // namespace tint::ir
#endif // SRC_TINT_LANG_CORE_IR_VALIDATOR_H_