use std::{collections::BTreeSet, path::PathBuf};
use serde::{Deserialize, Serialize};
use rllvm_core::catalog::{CatalogOrigin, CatalogScope, DigestOrigin};
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct FunctionId {
pub module_id: String,
pub symbol: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct CallSiteId {
pub function: FunctionId,
pub block_index: u32,
pub instruction_index: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SourceStatus {
Current,
Modified,
Missing,
Unknown,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SourceLocation {
pub file: PathBuf,
pub directory: Option<PathBuf>,
pub line: u32,
pub column: u32,
pub source_status: SourceStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status_basis: Option<DigestOrigin>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub inlined_at: Vec<SourceLocation>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Linkage {
External,
Internal,
Odr,
Weak,
AvailableExternally,
Other,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FunctionFact {
pub id: FunctionId,
pub is_definition: bool,
pub linkage: Linkage,
pub signature: String,
pub location: Option<SourceLocation>,
pub mapped_lines: BTreeSet<(PathBuf, u32)>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum CallTarget {
Direct {
callee: FunctionId,
},
Indirect {
signature: String,
#[serde(skip_serializing_if = "Option::is_none")]
llvm_target_bound: Option<Vec<FunctionId>>,
},
Intrinsic {
name: String,
},
InlineAsm,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CallSiteFact {
pub id: CallSiteId,
pub location: Option<SourceLocation>,
pub target: CallTarget,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UseKind {
StoredToMemory,
PassedAsArgument,
GlobalInitializer,
ReturnedValue,
Other,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UseFact {
pub used: FunctionId,
pub in_function: Option<FunctionId>,
pub location: Option<SourceLocation>,
pub kind: UseKind,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ModuleAnalysis {
Verified,
Analyzed,
Changed,
Missing,
Failed,
Unsupported,
NotBuilt,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ModuleReport {
pub id: String,
pub status: ModuleAnalysis,
pub ir_stage: Option<String>,
pub debug_info: Option<bool>,
pub compiler: Option<String>,
pub configuration_id: Option<String>,
pub content_sha256: Option<String>,
pub target_triple: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub diagnostic: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ProgramFacts {
pub functions: Vec<FunctionFact>,
pub call_sites: Vec<CallSiteFact>,
pub uses: Vec<UseFact>,
pub scope: CatalogScope,
pub origin: CatalogOrigin,
pub modules: Vec<ModuleReport>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn call_sites_on_one_line_have_distinct_identities() {
let function = FunctionId {
module_id: "m".into(),
symbol: "caller".into(),
};
let first = CallSiteId {
function: function.clone(),
block_index: 0,
instruction_index: 3,
};
let second = CallSiteId {
function,
block_index: 0,
instruction_index: 7,
};
assert_ne!(first, second);
}
#[test]
fn a_bare_symbol_is_not_a_function_identity() {
let left = FunctionId {
module_id: "a".into(),
symbol: "f".into(),
};
let right = FunctionId {
module_id: "b".into(),
symbol: "f".into(),
};
assert_ne!(left, right, "same symbol in two modules must not collide");
}
}