use crate::opcode::Opcode;
use crate::types::{Type, TypeKind};
use crate::value::ValueRef;
use std::collections::{HashMap, HashSet};
use std::fmt;
#[derive(Debug, Clone)]
pub struct VerifierResult {
pub is_valid: bool,
pub errors: Vec<VerifierError>,
pub warnings: Vec<VerifierWarning>,
}
#[derive(Debug, Clone)]
pub struct VerifierError {
pub code: String,
pub message: String,
pub context: String,
pub severity: VerifierSeverity,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum VerifierSeverity {
Fatal,
Error,
Warning,
Note,
}
#[derive(Debug, Clone)]
pub struct VerifierWarning {
pub message: String,
}
impl VerifierResult {
pub fn success() -> Self {
VerifierResult {
is_valid: true,
errors: Vec::new(),
warnings: Vec::new(),
}
}
pub fn failure(errors: Vec<VerifierError>) -> Self {
VerifierResult {
is_valid: false,
errors,
warnings: Vec::new(),
}
}
pub fn add_error(&mut self, code: &str, msg: &str, ctx: &str) {
self.is_valid = false;
self.errors.push(VerifierError {
code: code.to_string(),
message: msg.to_string(),
context: ctx.to_string(),
severity: VerifierSeverity::Error,
});
}
pub fn add_warning(&mut self, msg: &str) {
self.warnings.push(VerifierWarning {
message: msg.to_string(),
});
}
}
pub struct FunctionVerifier {
pub result: VerifierResult,
}
impl FunctionVerifier {
pub fn new() -> Self {
FunctionVerifier {
result: VerifierResult::success(),
}
}
pub fn verify_ssa(
&mut self,
block_names: &[String],
_instructions: &HashMap<String, Vec<(String, Opcode)>>,
phi_nodes: &HashMap<String, Vec<(String, String)>>, ) {
for (block, phis) in phi_nodes {
for (_, from_block) in phis {
if !block_names.contains(from_block) {
self.result.add_error(
"PHI-REFERENCE",
&format!(
"PHI node in '{}' references non-existent predecessor '{}'",
block, from_block
),
block,
);
}
}
}
}
pub fn verify_terminators(
&mut self,
block_terminators: &HashMap<String, Opcode>,
blocks_without_terminators: &HashSet<String>,
) {
for block in blocks_without_terminators {
self.result.add_error(
"NO-TERMINATOR",
&format!("Basic block '{}' has no terminator instruction", block),
block,
);
}
for (block, term) in block_terminators {
match term {
Opcode::Switch => {
}
Opcode::Ret => {
}
Opcode::Unreachable => {
}
_ => {}
}
}
}
pub fn verify_entry_block(&mut self, entry: &str, predecessors: &HashMap<String, Vec<String>>) {
if let Some(preds) = predecessors.get(entry) {
if !preds.is_empty() {
self.result.add_error(
"ENTRY-BLOCK-PRED",
&format!("Entry block '{}' has {} predecessor(s)", entry, preds.len()),
entry,
);
}
}
}
pub fn verify_types(&mut self, _operand_types: &HashMap<String, Vec<TypeKind>>) {
}
pub fn verify_dominance(
&mut self,
_def_blocks: &HashMap<String, String>, _use_blocks: &HashMap<String, Vec<String>>, _dominates: &dyn Fn(&str, &str) -> bool,
) {
}
pub fn verify_no_self_references(&mut self, _def_use_map: &HashMap<String, HashSet<String>>) {
}
}
impl Default for FunctionVerifier {
fn default() -> Self {
FunctionVerifier::new()
}
}
pub struct ModuleVerifier {
pub result: VerifierResult,
}
impl ModuleVerifier {
pub fn new() -> Self {
ModuleVerifier {
result: VerifierResult::success(),
}
}
pub fn verify_type_consistency(
&mut self,
_function_types: &HashMap<String, (TypeKind, Vec<TypeKind>)>,
) {
}
pub fn verify_symbol_uniqueness(
&mut self,
function_names: &[String],
global_names: &[String],
alias_names: &[String],
) {
let mut seen: HashSet<&str> = HashSet::new();
let all_names: Vec<&str> = function_names
.iter()
.chain(global_names.iter())
.chain(alias_names.iter())
.map(|s| s.as_str())
.collect();
for name in &all_names {
if !seen.insert(name) {
self.result.add_error(
"DUPLICATE-SYMBOL",
&format!("Duplicate symbol '{}'", name),
name,
);
}
}
}
pub fn verify_linkage(&mut self, _function_linkage: &HashMap<String, &str>) {
}
pub fn verify_target_consistency(
&mut self,
_target_triple: Option<&str>,
_data_layout: Option<&str>,
) {
}
}
impl Default for ModuleVerifier {
fn default() -> Self {
ModuleVerifier::new()
}
}
pub struct MachineVerifier {
pub errors: Vec<String>,
}
impl MachineVerifier {
pub fn new() -> Self {
MachineVerifier { errors: Vec::new() }
}
pub fn verify_liveness(
&mut self,
_live_ins: &HashMap<String, Vec<u32>>,
_live_outs: &HashMap<String, Vec<u32>>,
_defs: &HashMap<String, Vec<u32>>,
_uses: &HashMap<String, Vec<u32>>,
) {
}
pub fn verify_instruction_constraints(&mut self, _opcode: &str, _operands: &[u32]) {
}
}
impl Default for MachineVerifier {
fn default() -> Self {
MachineVerifier::new()
}
}
pub struct MetadataVerifier;
impl MetadataVerifier {
pub fn verify_debug_info(_compile_units: &[u64], _subprograms: &[u64]) {
}
pub fn verify_tbaa(_tbaa_nodes: &[u64]) {
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_verifier_result_success() {
let result = VerifierResult::success();
assert!(result.is_valid);
assert!(result.errors.is_empty());
}
#[test]
fn test_verifier_result_add_error() {
let mut result = VerifierResult::success();
result.add_error("TEST", "test error", "ctx");
assert!(!result.is_valid);
assert_eq!(result.errors.len(), 1);
}
#[test]
fn test_function_verifier_no_terminator() {
let mut fv = FunctionVerifier::new();
let mut blocks_without = HashSet::new();
blocks_without.insert("block1".to_string());
fv.verify_terminators(&HashMap::new(), &blocks_without);
assert!(!fv.result.is_valid);
}
#[test]
fn test_module_verifier_duplicate_symbols() {
let mut mv = ModuleVerifier::new();
let funcs = vec!["foo".to_string(), "foo".to_string()];
mv.verify_symbol_uniqueness(&funcs, &[], &[]);
assert!(!mv.result.is_valid);
}
#[test]
fn test_entry_block_verification() {
let mut fv = FunctionVerifier::new();
let mut preds: HashMap<String, Vec<String>> = HashMap::new();
preds.insert("entry".to_string(), vec!["loop".to_string()]);
fv.verify_entry_block("entry", &preds);
assert!(!fv.result.is_valid);
}
}