use llvm_native_core::types::Type;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ConstantValue {
pub ty: Type,
pub repr: ConstantRepr,
}
#[derive(Debug, Clone)]
pub enum ConstantRepr {
Int(i64),
Float(f64),
Null,
Undef,
Poison,
String(String),
ZeroInitializer,
}
impl ConstantValue {
pub fn new_int(ty: Type, val: i64) -> Self {
Self {
ty,
repr: ConstantRepr::Int(val),
}
}
pub fn new_float(ty: Type, val: f64) -> Self {
Self {
ty,
repr: ConstantRepr::Float(val),
}
}
pub fn null(ty: Type) -> Self {
Self {
ty,
repr: ConstantRepr::Null,
}
}
pub fn to_string(&self) -> String {
match &self.repr {
ConstantRepr::Int(v) => format!("i{} {}", self.ty.integer_bit_width(), v),
ConstantRepr::Float(v) => format!("{} {}", self.ty, v),
ConstantRepr::Null => "null".to_string(),
ConstantRepr::Undef => "undef".to_string(),
ConstantRepr::Poison => "poison".to_string(),
ConstantRepr::String(s) => format!("\"{}\"", s),
ConstantRepr::ZeroInitializer => "zeroinitializer".to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct MetadataValue {
pub id: u32,
pub kind: MetadataKind,
}
#[derive(Debug, Clone)]
pub enum MetadataKind {
MDString(String),
Constant(ConstantValue),
Value(usize),
MDNode(Vec<MetadataOperand>),
DistinctMDNode(Vec<MetadataOperand>),
MDTuple(Vec<MetadataOperand>),
NamedNode(String, Vec<u32>),
}
#[derive(Debug, Clone)]
pub enum MetadataOperand {
Metadata(u32),
Value(usize),
MDString(String),
Constant(ConstantValue),
Null,
}
impl MetadataOperand {
pub fn is_metadata_ref(&self) -> bool {
matches!(self, MetadataOperand::Metadata(_))
}
pub fn as_metadata_id(&self) -> Option<u32> {
match self {
MetadataOperand::Metadata(id) => Some(*id),
_ => None,
}
}
pub fn to_debug_string(&self) -> String {
match self {
MetadataOperand::Metadata(id) => format!("!{}", id),
MetadataOperand::Value(v) => format!("value(%{})", v),
MetadataOperand::MDString(s) => format!("!\"{}\"", s),
MetadataOperand::Constant(c) => c.to_string(),
MetadataOperand::Null => "null".to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct DILocation {
pub line: u32,
pub column: u32,
pub scope: u32,
pub inlined_at: Option<u32>,
}
impl DILocation {
pub fn new(line: u32, column: u32, scope: u32) -> Self {
Self {
line,
column,
scope,
inlined_at: None,
}
}
pub fn with_inlined_at(mut self, inlined_at: u32) -> Self {
self.inlined_at = Some(inlined_at);
self
}
}
#[derive(Debug, Clone)]
pub struct DIFile {
pub filename: String,
pub directory: String,
pub checksum_kind: Option<String>,
pub checksum_value: Option<String>,
pub source: Option<String>,
}
impl DIFile {
pub fn new(filename: String, directory: String) -> Self {
Self {
filename,
directory,
checksum_kind: None,
checksum_value: None,
source: None,
}
}
pub fn full_path(&self) -> String {
if self.directory.is_empty() {
self.filename.clone()
} else {
format!("{}/{}", self.directory, self.filename)
}
}
}
#[derive(Debug, Clone)]
pub struct DISubprogram {
pub name: String,
pub linkage_name: Option<String>,
pub file: u32,
pub line: u32,
pub unit: u32,
pub ty: Option<u32>,
pub scope: Option<u32>,
pub is_definition: bool,
pub is_local: bool,
pub is_optimized: bool,
pub virtual_index: Option<u32>,
pub flags: u32,
}
impl DISubprogram {
pub fn new(name: String, file: u32, line: u32, unit: u32) -> Self {
Self {
name,
linkage_name: None,
file,
line,
unit,
ty: None,
scope: None,
is_definition: true,
is_local: false,
is_optimized: false,
virtual_index: None,
flags: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct DIType {
pub name: String,
pub size_in_bits: u64,
pub align_in_bits: u32,
pub offset_in_bits: u64,
pub encoding: u32,
pub base_type: Option<u32>,
pub file: Option<u32>,
pub line: Option<u32>,
pub tag: u32,
pub flags: u32,
}
impl DIType {
pub fn new_basic(name: String, size_in_bits: u64, align_in_bits: u32, encoding: u32) -> Self {
Self {
name,
size_in_bits,
align_in_bits,
offset_in_bits: 0,
encoding,
base_type: None,
file: None,
line: None,
tag: 36, flags: 0,
}
}
pub fn size_in_bytes(&self) -> u64 {
(self.size_in_bits + 7) / 8
}
}
#[derive(Debug, Clone)]
pub struct DILocalVariable {
pub name: String,
pub scope: u32,
pub file: u32,
pub line: u32,
pub var_type: u32,
pub arg: Option<u32>,
pub flags: u32,
pub align_in_bits: u32,
}
impl DILocalVariable {
pub fn new(name: String, scope: u32, file: u32, line: u32, var_type: u32) -> Self {
Self {
name,
scope,
file,
line,
var_type,
arg: None,
flags: 0,
align_in_bits: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct DIGlobalVariable {
pub name: String,
pub scope: u32,
pub file: u32,
pub line: u32,
pub var_type: u32,
pub is_local_to_unit: bool,
pub is_definition: bool,
pub declaration: Option<u32>,
pub align_in_bits: u32,
}
impl DIGlobalVariable {
pub fn new(
name: String,
scope: u32,
file: u32,
line: u32,
var_type: u32,
is_local_to_unit: bool,
is_definition: bool,
) -> Self {
Self {
name,
scope,
file,
line,
var_type,
is_local_to_unit,
is_definition,
declaration: None,
align_in_bits: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct DICompileUnit {
pub language: u32,
pub file: u32,
pub producer: String,
pub is_optimized: bool,
pub flags: String,
pub runtime_version: u32,
pub split_debug_filename: Option<String>,
pub emission_kind: u32,
pub retained_types: Vec<u32>,
pub globals: Vec<u32>,
pub imports: Vec<u32>,
}
impl DICompileUnit {
pub fn new(language: u32, file: u32, producer: String) -> Self {
Self {
language,
file,
producer,
is_optimized: false,
flags: String::new(),
runtime_version: 0,
split_debug_filename: None,
emission_kind: 0, retained_types: Vec::new(),
globals: Vec::new(),
imports: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct TemporaryNodeState {
pub operands: Vec<MetadataOperand>,
pub resolved: bool,
pub replacement: Option<u32>,
}
impl TemporaryNodeState {
pub fn new(operands: Vec<MetadataOperand>) -> Self {
Self {
operands,
resolved: false,
replacement: None,
}
}
}
#[derive(Debug, Clone)]
pub struct MetadataStore {
pub values: Vec<MetadataValue>,
pub named_nodes: HashMap<String, Vec<u32>>,
pub debug_loc: Vec<Option<DILocation>>,
pub debug_files: Vec<Option<DIFile>>,
pub debug_subprograms: Vec<Option<DISubprogram>>,
pub debug_types: Vec<Option<DIType>>,
pub debug_local_vars: Vec<Option<DILocalVariable>>,
pub debug_global_vars: Vec<Option<DIGlobalVariable>>,
pub debug_compile_units: Vec<Option<DICompileUnit>>,
pub metadata_attachments: HashMap<usize, Vec<(String, u32)>>,
pub temporary_nodes: HashMap<u32, TemporaryNodeState>,
}
impl MetadataStore {
pub fn new() -> Self {
Self {
values: Vec::new(),
named_nodes: HashMap::new(),
debug_loc: Vec::new(),
debug_files: Vec::new(),
debug_subprograms: Vec::new(),
debug_types: Vec::new(),
debug_local_vars: Vec::new(),
debug_global_vars: Vec::new(),
debug_compile_units: Vec::new(),
metadata_attachments: HashMap::new(),
temporary_nodes: HashMap::new(),
}
}
pub fn add_metadata(&mut self, kind: MetadataKind) -> u32 {
let id = self.values.len() as u32;
self.values.push(MetadataValue { id, kind });
id
}
pub fn get_metadata(&self, id: u32) -> Option<&MetadataValue> {
self.values.get(id as usize)
}
pub fn get_metadata_mut(&mut self, id: u32) -> Option<&mut MetadataValue> {
self.values.get_mut(id as usize)
}
pub fn add_named_node(&mut self, name: &str, operands: Vec<u32>) {
self.named_nodes.insert(name.to_string(), operands);
}
pub fn get_named_node(&self, name: &str) -> Option<&Vec<u32>> {
self.named_nodes.get(name)
}
pub fn remove_named_node(&mut self, name: &str) {
self.named_nodes.remove(name);
}
pub fn has_named_node(&self, name: &str) -> bool {
self.named_nodes.contains_key(name)
}
pub fn count(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty() && self.named_nodes.is_empty()
}
pub fn add_debug_location(&mut self, loc: DILocation) -> u32 {
let id = self.debug_loc.len() as u32;
self.debug_loc.push(Some(loc));
id
}
pub fn get_debug_location(&self, id: u32) -> Option<&DILocation> {
self.debug_loc.get(id as usize).and_then(|opt| opt.as_ref())
}
pub fn set_debug_location(&mut self, id: u32, loc: DILocation) {
let idx = id as usize;
while self.debug_loc.len() <= idx {
self.debug_loc.push(None);
}
self.debug_loc[idx] = Some(loc);
}
pub fn add_debug_file(&mut self, file: DIFile) -> u32 {
let id = self.debug_files.len() as u32;
self.debug_files.push(Some(file));
id
}
pub fn get_debug_file(&self, id: u32) -> Option<&DIFile> {
self.debug_files
.get(id as usize)
.and_then(|opt| opt.as_ref())
}
pub fn find_debug_file(&self, filename: &str, directory: &str) -> Option<u32> {
self.debug_files
.iter()
.position(|opt| {
opt.as_ref().map_or(false, |f| {
f.filename == filename && f.directory == directory
})
})
.map(|idx| idx as u32)
}
pub fn add_debug_subprogram(&mut self, sp: DISubprogram) -> u32 {
let id = self.debug_subprograms.len() as u32;
self.debug_subprograms.push(Some(sp));
id
}
pub fn get_debug_subprogram(&self, id: u32) -> Option<&DISubprogram> {
self.debug_subprograms
.get(id as usize)
.and_then(|opt| opt.as_ref())
}
pub fn find_debug_subprogram(&self, name: &str) -> Option<u32> {
self.debug_subprograms
.iter()
.position(|opt| opt.as_ref().map_or(false, |sp| sp.name == name))
.map(|idx| idx as u32)
}
pub fn add_debug_type(&mut self, ty: DIType) -> u32 {
let id = self.debug_types.len() as u32;
self.debug_types.push(Some(ty));
id
}
pub fn get_debug_type(&self, id: u32) -> Option<&DIType> {
self.debug_types
.get(id as usize)
.and_then(|opt| opt.as_ref())
}
pub fn find_debug_type(&self, name: &str) -> Option<u32> {
self.debug_types
.iter()
.position(|opt| opt.as_ref().map_or(false, |ty| ty.name == name))
.map(|idx| idx as u32)
}
pub fn add_debug_local_var(&mut self, var: DILocalVariable) -> u32 {
let id = self.debug_local_vars.len() as u32;
self.debug_local_vars.push(Some(var));
id
}
pub fn get_debug_local_var(&self, id: u32) -> Option<&DILocalVariable> {
self.debug_local_vars
.get(id as usize)
.and_then(|opt| opt.as_ref())
}
pub fn add_debug_global_var(&mut self, var: DIGlobalVariable) -> u32 {
let id = self.debug_global_vars.len() as u32;
self.debug_global_vars.push(Some(var));
id
}
pub fn get_debug_global_var(&self, id: u32) -> Option<&DIGlobalVariable> {
self.debug_global_vars
.get(id as usize)
.and_then(|opt| opt.as_ref())
}
pub fn add_debug_compile_unit(&mut self, cu: DICompileUnit) -> u32 {
let id = self.debug_compile_units.len() as u32;
self.debug_compile_units.push(Some(cu));
id
}
pub fn get_debug_compile_unit(&self, id: u32) -> Option<&DICompileUnit> {
self.debug_compile_units
.get(id as usize)
.and_then(|opt| opt.as_ref())
}
pub fn create_md_node(&mut self, operands: Vec<MetadataOperand>) -> u32 {
self.add_metadata(MetadataKind::MDNode(operands))
}
pub fn create_distinct_md_node(&mut self, operands: Vec<MetadataOperand>) -> u32 {
self.add_metadata(MetadataKind::DistinctMDNode(operands))
}
pub fn create_md_tuple(&mut self, operands: Vec<MetadataOperand>) -> u32 {
self.add_metadata(MetadataKind::MDTuple(operands))
}
pub fn create_md_string(&mut self, s: &str) -> u32 {
self.add_metadata(MetadataKind::MDString(s.to_string()))
}
pub fn md_ref(id: u32) -> MetadataOperand {
MetadataOperand::Metadata(id)
}
pub fn md_string_op(s: &str) -> MetadataOperand {
MetadataOperand::MDString(s.to_string())
}
pub fn md_null() -> MetadataOperand {
MetadataOperand::Null
}
pub fn emit_assembly(&self) -> String {
let mut lines = Vec::new();
for mv in &self.values {
let line = self.emit_metadata_value(mv);
if !line.is_empty() {
lines.push(line);
}
}
for (name, operands) in &self.named_nodes {
let ops_str = operands
.iter()
.map(|id| format!("!{}", id))
.collect::<Vec<_>>()
.join(", ");
lines.push(format!("!{} = !{{{}}}", name, ops_str));
}
lines.join("\n")
}
fn emit_metadata_value(&self, mv: &MetadataValue) -> String {
match &mv.kind {
MetadataKind::MDString(s) => {
format!("!{} = !\"{}\"", mv.id, s)
}
MetadataKind::Constant(c) => {
format!("!{} = !{{{}}}", mv.id, c.to_string())
}
MetadataKind::Value(v) => {
format!("!{} = !{{{}}}", mv.id, v)
}
MetadataKind::MDNode(operands)
| MetadataKind::DistinctMDNode(operands)
| MetadataKind::MDTuple(operands) => {
let distinct = matches!(mv.kind, MetadataKind::DistinctMDNode(_));
let prefix = if distinct { "distinct " } else { "" };
let ops_str = operands
.iter()
.map(|op| op.to_debug_string())
.collect::<Vec<_>>()
.join(", ");
format!("!{} = {}!{{{}}}", mv.id, prefix, ops_str)
}
MetadataKind::NamedNode(name, _) => {
format!("; named node: {}", name)
}
}
}
}
impl Default for MetadataStore {
fn default() -> Self {
Self::new()
}
}
pub mod md_kind {
pub const DBG: &str = "dbg";
pub const TBAA: &str = "tbaa";
pub const TBAA_STRUCT: &str = "tbaa.struct";
pub const ALIAS_SCOPE: &str = "alias.scope";
pub const NOALIAS: &str = "noalias";
pub const RANGE: &str = "range";
pub const FPMATH: &str = "fpmath";
pub const PROF: &str = "prof";
pub const LLVM_LOOP: &str = "llvm.loop";
pub const MAKE_IMPLICIT: &str = "make.implicit";
pub const ABSOLUTE_SYMBOL: &str = "absolute_symbol";
pub const INACCESSIBLEMEMONLY: &str = "inaccessiblememonly";
pub const INVARIANT_LOAD: &str = "invariant.load";
pub const INVARIANT_GROUP: &str = "invariant.group";
pub const DEREFERENCEABLE: &str = "dereferenceable";
pub const DEREFERENCEABLE_OR_NULL: &str = "dereferenceable_or_null";
pub const NONNULL: &str = "nonnull";
pub const ALIGN: &str = "align";
pub const TYPE: &str = "type";
pub const SANITIZE: &str = "sanitize";
pub const LLVM_MODULE_FLAGS: &str = "llvm.module.flags";
pub const LLVM_IDENT: &str = "llvm.ident";
pub const LLVM_DBG_CU: &str = "llvm.dbg.cu";
pub const LLVM_USED: &str = "llvm.used";
pub const LLVM_COMPILER_USED: &str = "llvm.compiler.used";
pub const NAMED: &[&str] = &[
LLVM_MODULE_FLAGS,
LLVM_IDENT,
LLVM_DBG_CU,
LLVM_USED,
LLVM_COMPILER_USED,
];
pub fn is_named(kind: &str) -> bool {
NAMED.iter().any(|&n| n == kind)
}
}
pub mod dwarf_tag {
pub const ARRAY_TYPE: u32 = 1;
pub const CLASS_TYPE: u32 = 2;
pub const ENTRY_POINT: u32 = 3;
pub const ENUMERATION_TYPE: u32 = 4;
pub const FORMAL_PARAMETER: u32 = 5;
pub const IMPORTED_DECLARATION: u32 = 8;
pub const LABEL: u32 = 10;
pub const LEXICAL_BLOCK: u32 = 11;
pub const MEMBER: u32 = 13;
pub const POINTER_TYPE: u32 = 15;
pub const REFERENCE_TYPE: u32 = 16;
pub const COMPILE_UNIT: u32 = 17;
pub const STRUCTURE_TYPE: u32 = 19;
pub const SUBROUTINE_TYPE: u32 = 21;
pub const TYPEDEF: u32 = 22;
pub const UNION_TYPE: u32 = 23;
pub const INHERITANCE: u32 = 28;
pub const PTR_TO_MEMBER_TYPE: u32 = 31;
pub const ENUMERATOR: u32 = 40;
pub const SUBRANGE_TYPE: u32 = 33;
pub const CONST_TYPE: u32 = 38;
pub const VOLATILE_TYPE: u32 = 53;
pub const RESTRICT_TYPE: u32 = 55;
pub const IMPORTED_MODULE: u32 = 58;
pub const TEMPLATE_TYPE_PARAMETER: u32 = 47;
pub const TEMPLATE_VALUE_PARAMETER: u32 = 48;
pub const NAMESPACE: u32 = 57;
pub const LEXICAL_BLOCK_FILE: u32 = 58;
pub const GLOBAL_VARIABLE: u32 = 52;
pub const LOCAL_VARIABLE: u32 = 33;
pub const AUTO_VARIABLE: u32 = 256;
pub const ARG_VARIABLE: u32 = 257;
pub const RETURN_VARIABLE: u32 = 258;
pub const STRING_TYPE: u32 = 67;
}
pub mod dwarf_encoding {
pub const ADDRESS: u32 = 1;
pub const BOOLEAN: u32 = 2;
pub const COMPLEX_FLOAT: u32 = 3;
pub const FLOAT: u32 = 4;
pub const SIGNED: u32 = 5;
pub const SIGNED_CHAR: u32 = 6;
pub const UNSIGNED: u32 = 7;
pub const UNSIGNED_CHAR: u32 = 8;
pub const IMAGINARY_FLOAT: u32 = 9;
pub const PACKED_DECIMAL: u32 = 10;
pub const NUMERIC_STRING: u32 = 11;
pub const UTF: u32 = 16;
pub const UCS: u32 = 17;
pub const ASCII: u32 = 18;
}
pub mod dwarf_lang {
pub const C89: u32 = 1;
pub const C: u32 = 2;
pub const ADA83: u32 = 3;
pub const C_PLUS_PLUS: u32 = 4;
pub const COBOL74: u32 = 5;
pub const COBOL85: u32 = 6;
pub const FORTRAN77: u32 = 7;
pub const FORTRAN90: u32 = 8;
pub const PASCAL83: u32 = 9;
pub const MODULA2: u32 = 10;
pub const JAVA: u32 = 11;
pub const C99: u32 = 12;
pub const ADA95: u32 = 13;
pub const FORTRAN95: u32 = 14;
pub const PL1: u32 = 15;
pub const OBJC: u32 = 16;
pub const OBJC_PLUS_PLUS: u32 = 17;
pub const D: u32 = 19;
pub const PYTHON: u32 = 20;
pub const RUST: u32 = 28;
pub const C11: u32 = 29;
pub const SWIFT: u32 = 31;
pub const C_PLUS_PLUS_14: u32 = 33;
pub const FORTRAN03: u32 = 34;
pub const FORTRAN08: u32 = 35;
pub const RENDERSCRIPT: u32 = 36;
pub const C17: u32 = 38;
pub const C_PLUS_PLUS_20: u32 = 41;
pub const MOJO: u32 = 43;
}
pub mod dwarf_op {
pub const ADDR: u64 = 0x03;
pub const DEREF: u64 = 0x06;
pub const CONST1U: u64 = 0x08;
pub const CONST1S: u64 = 0x09;
pub const CONST2U: u64 = 0x0a;
pub const CONST2S: u64 = 0x0b;
pub const CONST4U: u64 = 0x0c;
pub const CONST4S: u64 = 0x0d;
pub const CONST8U: u64 = 0x0e;
pub const CONST8S: u64 = 0x0f;
pub const CONSTU: u64 = 0x10;
pub const CONSTS: u64 = 0x11;
pub const DUP: u64 = 0x12;
pub const DROP: u64 = 0x13;
pub const OVER: u64 = 0x14;
pub const PICK: u64 = 0x15;
pub const SWAP: u64 = 0x16;
pub const ROT: u64 = 0x17;
pub const XDEREF: u64 = 0x18;
pub const ABS: u64 = 0x19;
pub const AND: u64 = 0x1a;
pub const DIV: u64 = 0x1b;
pub const MINUS: u64 = 0x1c;
pub const MOD: u64 = 0x1d;
pub const MUL: u64 = 0x1e;
pub const NEG: u64 = 0x1f;
pub const NOT: u64 = 0x20;
pub const OR: u64 = 0x21;
pub const PLUS: u64 = 0x22;
pub const PLUS_UCONST: u64 = 0x23;
pub const SHL: u64 = 0x24;
pub const SHR: u64 = 0x25;
pub const SHRA: u64 = 0x26;
pub const XOR: u64 = 0x27;
pub const BRA: u64 = 0x28;
pub const EQ: u64 = 0x29;
pub const GE: u64 = 0x2a;
pub const GT: u64 = 0x2b;
pub const LE: u64 = 0x2c;
pub const LT: u64 = 0x2d;
pub const NE: u64 = 0x2e;
pub const SKIP: u64 = 0x2f;
pub const LIT0: u64 = 0x30;
pub const LIT1: u64 = 0x31;
pub const LIT2: u64 = 0x32;
pub const LIT3: u64 = 0x33;
pub const LIT4: u64 = 0x34;
pub const LIT5: u64 = 0x35;
pub const LIT6: u64 = 0x36;
pub const LIT7: u64 = 0x37;
pub const LIT8: u64 = 0x38;
pub const LIT9: u64 = 0x39;
pub const LIT10: u64 = 0x3a;
pub const LIT11: u64 = 0x3b;
pub const LIT12: u64 = 0x3c;
pub const LIT13: u64 = 0x3d;
pub const LIT14: u64 = 0x3e;
pub const LIT15: u64 = 0x3f;
pub const LIT16: u64 = 0x40;
pub const LIT17: u64 = 0x41;
pub const LIT18: u64 = 0x42;
pub const LIT19: u64 = 0x43;
pub const LIT20: u64 = 0x44;
pub const LIT21: u64 = 0x45;
pub const LIT22: u64 = 0x46;
pub const LIT23: u64 = 0x47;
pub const LIT24: u64 = 0x48;
pub const LIT25: u64 = 0x49;
pub const LIT26: u64 = 0x4a;
pub const LIT27: u64 = 0x4b;
pub const LIT28: u64 = 0x4c;
pub const LIT29: u64 = 0x4d;
pub const LIT30: u64 = 0x4e;
pub const LIT31: u64 = 0x4f;
pub const REG0: u64 = 0x50;
pub const REG1: u64 = 0x51;
pub const REG2: u64 = 0x52;
pub const REG3: u64 = 0x53;
pub const REG4: u64 = 0x54;
pub const REG5: u64 = 0x55;
pub const REG6: u64 = 0x56;
pub const REG7: u64 = 0x57;
pub const REG8: u64 = 0x58;
pub const REG9: u64 = 0x59;
pub const REG10: u64 = 0x5a;
pub const REG11: u64 = 0x5b;
pub const REG12: u64 = 0x5c;
pub const REG13: u64 = 0x5d;
pub const REG14: u64 = 0x5e;
pub const REG15: u64 = 0x5f;
pub const REG16: u64 = 0x60;
pub const REG17: u64 = 0x61;
pub const REG18: u64 = 0x62;
pub const REG19: u64 = 0x63;
pub const REG20: u64 = 0x64;
pub const REG21: u64 = 0x65;
pub const REG22: u64 = 0x66;
pub const REG23: u64 = 0x67;
pub const REG24: u64 = 0x68;
pub const REG25: u64 = 0x69;
pub const REG26: u64 = 0x6a;
pub const REG27: u64 = 0x6b;
pub const REG28: u64 = 0x6c;
pub const REG29: u64 = 0x6d;
pub const REG30: u64 = 0x6e;
pub const REG31: u64 = 0x6f;
pub const BREG0: u64 = 0x70;
pub const BREG1: u64 = 0x71;
pub const BREG2: u64 = 0x72;
pub const BREG3: u64 = 0x73;
pub const BREG4: u64 = 0x74;
pub const BREG5: u64 = 0x75;
pub const BREG6: u64 = 0x76;
pub const BREG7: u64 = 0x77;
pub const BREG8: u64 = 0x78;
pub const BREG9: u64 = 0x79;
pub const BREG10: u64 = 0x7a;
pub const BREG11: u64 = 0x7b;
pub const BREG12: u64 = 0x7c;
pub const BREG13: u64 = 0x7d;
pub const BREG14: u64 = 0x7e;
pub const BREG15: u64 = 0x7f;
pub const BREG16: u64 = 0x80;
pub const BREG17: u64 = 0x81;
pub const BREG18: u64 = 0x82;
pub const BREG19: u64 = 0x83;
pub const BREG20: u64 = 0x84;
pub const BREG21: u64 = 0x85;
pub const BREG22: u64 = 0x86;
pub const BREG23: u64 = 0x87;
pub const BREG24: u64 = 0x88;
pub const BREG25: u64 = 0x89;
pub const BREG26: u64 = 0x8a;
pub const BREG27: u64 = 0x8b;
pub const BREG28: u64 = 0x8c;
pub const BREG29: u64 = 0x8d;
pub const BREG30: u64 = 0x8e;
pub const BREG31: u64 = 0x8f;
pub const REGX: u64 = 0x90;
pub const FBREG: u64 = 0x91;
pub const BREGX: u64 = 0x92;
pub const PIECE: u64 = 0x93;
pub const DEREF_SIZE: u64 = 0x94;
pub const XDEREF_SIZE: u64 = 0x95;
pub const NOP: u64 = 0x96;
pub const PUSH_OBJECT_ADDRESS: u64 = 0x97;
pub const CALL2: u64 = 0x98;
pub const CALL4: u64 = 0x99;
pub const CALL_REF: u64 = 0x9a;
pub const FORM_TLS_ADDRESS: u64 = 0x9b;
pub const CALL_FRAME_CFA: u64 = 0x9c;
pub const BIT_PIECE: u64 = 0x9d;
pub const IMPLICIT_VALUE: u64 = 0x9e;
pub const STACK_VALUE: u64 = 0x9f;
pub const IMPLICIT_POINTER: u64 = 0xa0;
pub const ADDR_OF: u64 = 0xa1;
pub const ENTRY_VALUE: u64 = 0xa3;
pub const CONST_TYPE: u64 = 0xa4;
pub const REGVAL_TYPE: u64 = 0xa5;
pub const DEREF_TYPE: u64 = 0xa6;
pub const XDEREF_TYPE: u64 = 0xa7;
pub const CONVERT: u64 = 0xa8;
pub const REINTERPRET: u64 = 0xa9;
pub const LLVM_FRAGMENT: u64 = 0x1000;
pub const LLVM_ARG: u64 = 0x1000 + 1;
pub const LLVM_ENTRY_VALUE: u64 = 0x1000 + 3;
}
pub trait DIScope {
fn get_scope_name(&self) -> &str;
fn get_scope_file(&self) -> Option<u32>;
fn get_scope_line(&self) -> Option<u32>;
}
impl DIScope for DISubprogram {
fn get_scope_name(&self) -> &str {
&self.name
}
fn get_scope_file(&self) -> Option<u32> {
Some(self.file)
}
fn get_scope_line(&self) -> Option<u32> {
Some(self.line)
}
}
impl DIScope for DICompileUnit {
fn get_scope_name(&self) -> &str {
&self.producer
}
fn get_scope_file(&self) -> Option<u32> {
Some(self.file)
}
fn get_scope_line(&self) -> Option<u32> {
None
}
}
#[derive(Debug, Clone)]
pub struct DIBasicType {
pub name: String,
pub size_in_bits: u64,
pub align_in_bits: u32,
pub encoding: u32,
pub flags: u32,
}
impl DIBasicType {
pub fn new(name: String, size_in_bits: u64, align_in_bits: u32, encoding: u32) -> Self {
Self {
name,
size_in_bits,
align_in_bits,
encoding,
flags: 0,
}
}
pub fn signed_int(name: String, size_in_bits: u64, align_in_bits: u32) -> Self {
Self::new(name, size_in_bits, align_in_bits, dwarf_encoding::SIGNED)
}
pub fn unsigned_int(name: String, size_in_bits: u64, align_in_bits: u32) -> Self {
Self::new(name, size_in_bits, align_in_bits, dwarf_encoding::UNSIGNED)
}
pub fn float_type(name: String, size_in_bits: u64, align_in_bits: u32) -> Self {
Self::new(name, size_in_bits, align_in_bits, dwarf_encoding::FLOAT)
}
pub fn boolean(name: String, size_in_bits: u64, align_in_bits: u32) -> Self {
Self::new(name, size_in_bits, align_in_bits, dwarf_encoding::BOOLEAN)
}
pub fn character(name: String, size_in_bits: u64, align_in_bits: u32, signed: bool) -> Self {
let enc = if signed {
dwarf_encoding::SIGNED_CHAR
} else {
dwarf_encoding::UNSIGNED_CHAR
};
Self::new(name, size_in_bits, align_in_bits, enc)
}
pub fn size_in_bytes(&self) -> u64 {
(self.size_in_bits + 7) / 8
}
pub fn is_signed(&self) -> bool {
self.encoding == dwarf_encoding::SIGNED || self.encoding == dwarf_encoding::SIGNED_CHAR
}
pub fn is_unsigned(&self) -> bool {
self.encoding == dwarf_encoding::UNSIGNED || self.encoding == dwarf_encoding::UNSIGNED_CHAR
}
pub fn is_float(&self) -> bool {
self.encoding == dwarf_encoding::FLOAT
|| self.encoding == dwarf_encoding::COMPLEX_FLOAT
|| self.encoding == dwarf_encoding::IMAGINARY_FLOAT
}
pub fn is_boolean(&self) -> bool {
self.encoding == dwarf_encoding::BOOLEAN
}
pub fn with_flags(mut self, flags: u32) -> Self {
self.flags = flags;
self
}
}
#[derive(Debug, Clone)]
pub struct DIDerivedType {
pub tag: u32,
pub base_type: u32,
pub name: Option<String>,
pub size: u64,
pub align: u32,
pub offset: u64,
pub dw_flags: u32,
pub scope: Option<u32>,
pub file: Option<u32>,
pub line: Option<u32>,
pub extra_data: Option<u32>,
pub annotations: Option<u32>,
}
impl DIDerivedType {
pub fn new(tag: u32, base_type: u32, size: u64, align: u32, offset: u64) -> Self {
Self {
tag,
base_type,
name: None,
size,
align,
offset,
dw_flags: 0,
scope: None,
file: None,
line: None,
extra_data: None,
annotations: None,
}
}
pub fn pointer_type(base_type: u32, size: u64, align: u32) -> Self {
Self::new(dwarf_tag::POINTER_TYPE, base_type, size, align, 0)
}
pub fn reference_type(base_type: u32, size: u64, align: u32) -> Self {
Self::new(dwarf_tag::REFERENCE_TYPE, base_type, size, align, 0)
}
pub fn typedef_type(name: String, base_type: u32, file: u32, line: u32) -> Self {
let mut ty = Self::new(dwarf_tag::TYPEDEF, base_type, 0, 0, 0);
ty.name = Some(name);
ty.file = Some(file);
ty.line = Some(line);
ty
}
pub fn const_type(base_type: u32) -> Self {
Self::new(dwarf_tag::CONST_TYPE, base_type, 0, 0, 0)
}
pub fn volatile_type(base_type: u32) -> Self {
Self::new(dwarf_tag::VOLATILE_TYPE, base_type, 0, 0, 0)
}
pub fn restrict_type(base_type: u32) -> Self {
Self::new(dwarf_tag::RESTRICT_TYPE, base_type, 0, 0, 0)
}
pub fn ptr_to_member_type(base_type: u32, class_type: u32, size: u64, align: u32) -> Self {
let mut ty = Self::new(dwarf_tag::PTR_TO_MEMBER_TYPE, base_type, size, align, 0);
ty.extra_data = Some(class_type);
ty
}
pub fn member_type(
name: String,
base_type: u32,
size: u64,
align: u32,
offset: u64,
file: u32,
line: u32,
) -> Self {
let mut ty = Self::new(dwarf_tag::MEMBER, base_type, size, align, offset);
ty.name = Some(name);
ty.file = Some(file);
ty.line = Some(line);
ty
}
pub fn inheritance(base_type: u32, offset: u64, dw_flags: u32) -> Self {
Self::new(dwarf_tag::INHERITANCE, base_type, 0, 0, offset).with_dw_flags(dw_flags)
}
pub fn is_pointer(&self) -> bool {
self.tag == dwarf_tag::POINTER_TYPE
}
pub fn is_reference(&self) -> bool {
self.tag == dwarf_tag::REFERENCE_TYPE
}
pub fn is_typedef(&self) -> bool {
self.tag == dwarf_tag::TYPEDEF
}
pub fn is_const(&self) -> bool {
self.tag == dwarf_tag::CONST_TYPE
}
pub fn is_volatile(&self) -> bool {
self.tag == dwarf_tag::VOLATILE_TYPE
}
pub fn is_restrict(&self) -> bool {
self.tag == dwarf_tag::RESTRICT_TYPE
}
pub fn is_member(&self) -> bool {
self.tag == dwarf_tag::MEMBER
}
pub fn is_inheritance(&self) -> bool {
self.tag == dwarf_tag::INHERITANCE
}
pub fn with_dw_flags(mut self, flags: u32) -> Self {
self.dw_flags = flags;
self
}
pub fn with_scope(mut self, scope: u32) -> Self {
self.scope = Some(scope);
self
}
pub fn with_name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn with_file_line(mut self, file: u32, line: u32) -> Self {
self.file = Some(file);
self.line = Some(line);
self
}
}
impl DIScope for DIDerivedType {
fn get_scope_name(&self) -> &str {
self.name.as_deref().unwrap_or("<derived>")
}
fn get_scope_file(&self) -> Option<u32> {
self.file
}
fn get_scope_line(&self) -> Option<u32> {
self.line
}
}
#[derive(Debug, Clone)]
pub struct DICompositeType {
pub tag: u32,
pub name: Option<String>,
pub file: Option<u32>,
pub line: Option<u32>,
pub scope: Option<u32>,
pub base_type: Option<u32>,
pub size: u64,
pub align: u32,
pub offset: u64,
pub flags: u32,
pub elements: Vec<u32>,
pub vtable_holder: Option<u32>,
pub template_params: Vec<u32>,
pub identifier: Option<String>,
pub runtime_lang: u32,
pub discriminator: Option<u32>,
pub data_location: Option<u32>,
pub associated: Option<u32>,
pub allocated: Option<u32>,
pub rank: Option<u32>,
}
impl DICompositeType {
pub fn new(tag: u32, name: Option<String>, size: u64, align: u32, offset: u64) -> Self {
Self {
tag,
name,
file: None,
line: None,
scope: None,
base_type: None,
size,
align,
offset,
flags: 0,
elements: Vec::new(),
vtable_holder: None,
template_params: Vec::new(),
identifier: None,
runtime_lang: 0,
discriminator: None,
data_location: None,
associated: None,
allocated: None,
rank: None,
}
}
pub fn struct_type(name: String, size: u64, align: u32, file: u32, line: u32) -> Self {
let mut ty = Self::new(dwarf_tag::STRUCTURE_TYPE, Some(name), size, align, 0);
ty.file = Some(file);
ty.line = Some(line);
ty
}
pub fn class_type(name: String, size: u64, align: u32, file: u32, line: u32) -> Self {
let mut ty = Self::new(dwarf_tag::CLASS_TYPE, Some(name), size, align, 0);
ty.file = Some(file);
ty.line = Some(line);
ty
}
pub fn union_type(name: String, size: u64, align: u32, file: u32, line: u32) -> Self {
let mut ty = Self::new(dwarf_tag::UNION_TYPE, Some(name), size, align, 0);
ty.file = Some(file);
ty.line = Some(line);
ty
}
pub fn array_type(
name: String,
element_type: u32,
size: u64,
align: u32,
subscripts: Vec<u32>,
) -> Self {
let mut ty = Self::new(dwarf_tag::ARRAY_TYPE, Some(name), size, align, 0);
ty.base_type = Some(element_type);
ty.elements = subscripts;
ty
}
pub fn enum_type(
name: String,
underlying_type: u32,
size: u64,
align: u32,
file: u32,
line: u32,
enumerators: Vec<u32>,
) -> Self {
let mut ty = Self::new(dwarf_tag::ENUMERATION_TYPE, Some(name), size, align, 0);
ty.base_type = Some(underlying_type);
ty.file = Some(file);
ty.line = Some(line);
ty.elements = enumerators;
ty
}
pub fn is_struct_or_class(&self) -> bool {
self.tag == dwarf_tag::STRUCTURE_TYPE || self.tag == dwarf_tag::CLASS_TYPE
}
pub fn is_union(&self) -> bool {
self.tag == dwarf_tag::UNION_TYPE
}
pub fn is_array(&self) -> bool {
self.tag == dwarf_tag::ARRAY_TYPE
}
pub fn is_enum(&self) -> bool {
self.tag == dwarf_tag::ENUMERATION_TYPE
}
pub fn is_forward_declaration(&self) -> bool {
self.size == 0 && self.elements.is_empty()
}
pub fn is_complete_definition(&self) -> bool {
!self.is_forward_declaration()
}
pub fn add_element(&mut self, element_id: u32) {
self.elements.push(element_id);
}
pub fn add_template_param(&mut self, param_id: u32) {
self.template_params.push(param_id);
}
pub fn with_identifier(mut self, identifier: String) -> Self {
self.identifier = Some(identifier);
self
}
pub fn with_vtable_holder(mut self, vtable_holder: u32) -> Self {
self.vtable_holder = Some(vtable_holder);
self
}
pub fn with_scope(mut self, scope: u32) -> Self {
self.scope = Some(scope);
self
}
pub fn with_flags(mut self, flags: u32) -> Self {
self.flags = flags;
self
}
pub fn with_discriminator(mut self, disc: u32) -> Self {
self.discriminator = Some(disc);
self
}
pub fn with_data_location(mut self, dl: u32) -> Self {
self.data_location = Some(dl);
self
}
pub fn with_rank(mut self, rank: u32) -> Self {
self.rank = Some(rank);
self
}
}
impl DIScope for DICompositeType {
fn get_scope_name(&self) -> &str {
self.name.as_deref().unwrap_or("<unnamed>")
}
fn get_scope_file(&self) -> Option<u32> {
self.file
}
fn get_scope_line(&self) -> Option<u32> {
self.line
}
}
#[derive(Debug, Clone)]
pub struct DISubroutineType {
pub flags: u32,
pub types: Vec<u32>,
pub cc: Option<u32>,
}
impl DISubroutineType {
pub fn new(flags: u32, types: Vec<u32>) -> Self {
Self {
flags,
types,
cc: None,
}
}
pub fn with_signature(return_type: u32, param_types: Vec<u32>) -> Self {
let mut types = vec![return_type];
types.extend(param_types);
Self::new(0, types)
}
pub fn void_returning(param_types: Vec<u32>) -> Self {
let mut types = Vec::with_capacity(param_types.len() + 1);
types.push(0); types.extend(param_types);
Self::new(0, types)
}
pub fn return_type(&self) -> Option<u32> {
self.types.first().copied()
}
pub fn param_types(&self) -> &[u32] {
if self.types.is_empty() {
&[]
} else {
&self.types[1..]
}
}
pub fn num_types(&self) -> usize {
self.types.len()
}
pub fn num_params(&self) -> usize {
if self.types.is_empty() {
0
} else {
self.types.len() - 1
}
}
pub fn add_param_type(&mut self, ty: u32) {
self.types.push(ty);
}
pub fn with_cc(mut self, cc: u32) -> Self {
self.cc = Some(cc);
self
}
pub fn with_flags(mut self, flags: u32) -> Self {
self.flags = flags;
self
}
}
impl DIScope for DISubroutineType {
fn get_scope_name(&self) -> &str {
"<subroutine>"
}
fn get_scope_file(&self) -> Option<u32> {
None
}
fn get_scope_line(&self) -> Option<u32> {
None
}
}
#[derive(Debug, Clone)]
pub struct DIEnumerator {
pub name: String,
pub value: i64,
pub is_unsigned: bool,
}
impl DIEnumerator {
pub fn new(name: String, value: i64, is_unsigned: bool) -> Self {
Self {
name,
value,
is_unsigned,
}
}
pub fn signed(name: String, value: i64) -> Self {
Self::new(name, value, false)
}
pub fn unsigned(name: String, value: u64) -> Self {
Self::new(name, value as i64, true)
}
pub fn value_as_u64(&self) -> u64 {
if self.is_unsigned {
self.value as u64
} else {
assert!(
self.value >= 0,
"Cannot convert negative signed enumerator value to u64"
);
self.value as u64
}
}
pub fn value_as_i64(&self) -> i64 {
self.value
}
pub fn value_display(&self) -> String {
if self.is_unsigned {
format!("{}", self.value as u64)
} else {
format!("{}", self.value)
}
}
}
#[derive(Debug, Clone)]
pub struct DINamespace {
pub name: String,
pub scope: u32,
pub export_symbols: bool,
}
impl DINamespace {
pub fn new(name: String, scope: u32, export_symbols: bool) -> Self {
Self {
name,
scope,
export_symbols,
}
}
pub fn regular(name: String, scope: u32) -> Self {
Self::new(name, scope, false)
}
pub fn exporting(name: String, scope: u32) -> Self {
Self::new(name, scope, true)
}
pub fn is_inline(&self) -> bool {
self.export_symbols
}
}
impl DIScope for DINamespace {
fn get_scope_name(&self) -> &str {
&self.name
}
fn get_scope_file(&self) -> Option<u32> {
None
}
fn get_scope_line(&self) -> Option<u32> {
None
}
}
#[derive(Debug, Clone)]
pub struct DITemplateTypeParameter {
pub name: String,
pub ty: u32,
pub is_default: bool,
}
impl DITemplateTypeParameter {
pub fn new(name: String, ty: u32) -> Self {
Self {
name,
ty,
is_default: false,
}
}
pub fn with_default(mut self) -> Self {
self.is_default = true;
self
}
}
#[derive(Debug, Clone)]
pub struct DITemplateValueParameter {
pub name: String,
pub ty: u32,
pub value: MetadataOperand,
pub is_default: bool,
}
impl DITemplateValueParameter {
pub fn new(name: String, ty: u32, value: MetadataOperand) -> Self {
Self {
name,
ty,
value,
is_default: false,
}
}
pub fn integer(name: String, ty: u32, val: i64) -> Self {
let cv = ConstantValue::new_int(Type::i32(), val);
Self::new(name, ty, MetadataOperand::Constant(cv))
}
pub fn with_default(mut self) -> Self {
self.is_default = true;
self
}
}
#[derive(Debug, Clone)]
pub struct DIObjCProperty {
pub name: String,
pub file: u32,
pub line: u32,
pub getter_name: String,
pub setter_name: String,
pub attributes: u32,
pub ty: u32,
}
pub mod objc_property_attribute {
pub const READONLY: u32 = 1 << 0;
pub const READWRITE: u32 = 1 << 1;
pub const ASSIGN: u32 = 1 << 2;
pub const COPY: u32 = 1 << 3;
pub const RETAIN: u32 = 1 << 4;
pub const NONATOMIC: u32 = 1 << 5;
pub const ATOMIC: u32 = 1 << 6;
pub const WEAK: u32 = 1 << 7;
pub const STRONG: u32 = 1 << 8;
pub const UNSAFE_UNRETAINED: u32 = 1 << 9;
pub const NULLABILITY: u32 = 1 << 10;
pub const NULL_RESETTABLE: u32 = 1 << 11;
pub const CLASS: u32 = 1 << 12;
pub const DIRECT: u32 = 1 << 13;
}
impl DIObjCProperty {
pub fn new(
name: String,
file: u32,
line: u32,
getter_name: String,
setter_name: String,
attributes: u32,
ty: u32,
) -> Self {
Self {
name,
file,
line,
getter_name,
setter_name,
attributes,
ty,
}
}
pub fn is_readonly(&self) -> bool {
self.attributes & objc_property_attribute::READONLY != 0
}
pub fn is_readwrite(&self) -> bool {
self.attributes & objc_property_attribute::READWRITE != 0
}
pub fn is_retain(&self) -> bool {
self.attributes & objc_property_attribute::RETAIN != 0
}
pub fn is_copy(&self) -> bool {
self.attributes & objc_property_attribute::COPY != 0
}
pub fn is_nonatomic(&self) -> bool {
self.attributes & objc_property_attribute::NONATOMIC != 0
}
pub fn is_weak(&self) -> bool {
self.attributes & objc_property_attribute::WEAK != 0
}
pub fn is_strong(&self) -> bool {
self.attributes & objc_property_attribute::STRONG != 0
}
}
#[derive(Debug, Clone)]
pub struct DIImportedEntity {
pub tag: u32,
pub scope: u32,
pub entity: u32,
pub file: Option<u32>,
pub line: Option<u32>,
pub name: Option<String>,
}
impl DIImportedEntity {
pub fn new(tag: u32, scope: u32, entity: u32) -> Self {
Self {
tag,
scope,
entity,
file: None,
line: None,
name: None,
}
}
pub fn imported_module(scope: u32, entity: u32) -> Self {
Self::new(dwarf_tag::IMPORTED_MODULE, scope, entity)
}
pub fn imported_declaration(
scope: u32,
entity: u32,
name: String,
file: u32,
line: u32,
) -> Self {
Self {
tag: dwarf_tag::IMPORTED_DECLARATION,
scope,
entity,
file: Some(file),
line: Some(line),
name: Some(name),
}
}
pub fn is_imported_module(&self) -> bool {
self.tag == dwarf_tag::IMPORTED_MODULE
}
pub fn is_imported_declaration(&self) -> bool {
self.tag == dwarf_tag::IMPORTED_DECLARATION
}
pub fn with_file_line(mut self, file: u32, line: u32) -> Self {
self.file = Some(file);
self.line = Some(line);
self
}
pub fn with_name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
}
impl DIScope for DIImportedEntity {
fn get_scope_name(&self) -> &str {
self.name.as_deref().unwrap_or("<imported>")
}
fn get_scope_file(&self) -> Option<u32> {
self.file
}
fn get_scope_line(&self) -> Option<u32> {
self.line
}
}
impl DIFile {
pub fn set_checksum_kind(&mut self, kind: String) {
self.checksum_kind = Some(kind);
}
pub fn set_checksum_value(&mut self, value: String) {
self.checksum_value = Some(value);
}
pub fn set_source(&mut self, source: String) {
self.source = Some(source);
}
pub fn get_checksum_kind(&self) -> Option<&str> {
self.checksum_kind.as_deref()
}
pub fn get_checksum_value(&self) -> Option<&str> {
self.checksum_value.as_deref()
}
pub fn get_source(&self) -> Option<&str> {
self.source.as_deref()
}
pub fn has_checksum(&self) -> bool {
self.checksum_kind.is_some() && self.checksum_value.is_some()
}
pub fn has_source(&self) -> bool {
self.source.is_some()
}
pub fn with_checksum_kind(mut self, kind: String) -> Self {
self.checksum_kind = Some(kind);
self
}
pub fn with_checksum_value(mut self, value: String) -> Self {
self.checksum_value = Some(value);
self
}
pub fn with_source(mut self, source: String) -> Self {
self.source = Some(source);
self
}
pub fn with_md5_checksum(mut self, value: String) -> Self {
self.checksum_kind = Some("CSK_MD5".to_string());
self.checksum_value = Some(value);
self
}
pub fn with_sha1_checksum(mut self, value: String) -> Self {
self.checksum_kind = Some("CSK_SHA1".to_string());
self.checksum_value = Some(value);
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DIExpression {
elements: Vec<u64>,
fragment: Option<(u64, u64)>,
}
impl DIExpression {
pub fn new(elements: Vec<u64>) -> Self {
let mut expr = Self {
elements,
fragment: None,
};
expr.extract_fragment();
expr
}
pub fn empty() -> Self {
Self {
elements: Vec::new(),
fragment: None,
}
}
pub fn create_fragment_expression(offset_in_bits: u64, size_in_bits: u64) -> Self {
Self {
elements: vec![dwarf_op::LLVM_FRAGMENT, offset_in_bits, size_in_bits],
fragment: Some((offset_in_bits, size_in_bits)),
}
}
fn extract_fragment(&mut self) {
let len = self.elements.len();
if len >= 3 && self.elements[len - 3] == dwarf_op::LLVM_FRAGMENT {
self.fragment = Some((self.elements[len - 2], self.elements[len - 1]));
}
}
pub fn is_valid(&self) -> bool {
!self.elements.is_empty() || self.fragment.is_some()
}
pub fn get_fragment_info(&self) -> Option<(u64, u64)> {
self.fragment
}
pub fn get_element(&self, index: usize) -> Option<u64> {
self.elements.get(index).copied()
}
pub fn get_num_elements(&self) -> usize {
self.elements.len()
}
pub fn elements(&self) -> &[u64] {
&self.elements
}
pub fn is_complex(&self) -> bool {
let non_fragment_len = if self.fragment.is_some() {
self.elements.len().saturating_sub(3)
} else {
self.elements.len()
};
if non_fragment_len > 2 {
return true;
}
if non_fragment_len == 1 {
let op = self.elements[0];
let is_simple_register = (dwarf_op::REG0..=dwarf_op::REG31).contains(&op);
let is_simple_fbreg = op == dwarf_op::FBREG;
if is_simple_register || is_simple_fbreg {
return false;
}
}
non_fragment_len > 0
}
pub fn is_implicit(&self) -> bool {
self.elements
.last()
.map_or(false, |&e| e == dwarf_op::STACK_VALUE)
}
pub fn is_constant(&self) -> bool {
if self.elements.is_empty() {
return false;
}
let first = self.elements[0];
first == dwarf_op::CONSTU || first == dwarf_op::CONSTS
}
pub fn append(&mut self, element: u64) {
if let Some(_) = self.fragment {
let insert_pos = self.elements.len() - 3;
self.elements.insert(insert_pos, element);
} else {
self.elements.push(element);
}
}
pub fn prepend(&mut self, element: u64) {
self.elements.insert(0, element);
}
pub fn get_expr_operand(&self, index: usize) -> Option<u64> {
if index + 1 < self.elements.len() {
Some(self.elements[index + 1])
} else {
None
}
}
pub fn reg(register: u64) -> Self {
assert!(register < 32, "Use reg_x for register numbers >= 32");
Self {
elements: vec![dwarf_op::REG0 + register],
fragment: None,
}
}
pub fn reg_x(register: u64) -> Self {
Self {
elements: vec![dwarf_op::REGX, register],
fragment: None,
}
}
pub fn fb_reg(offset: i64) -> Self {
Self {
elements: vec![dwarf_op::FBREG, offset as u64],
fragment: None,
}
}
pub fn deref() -> Self {
Self {
elements: vec![dwarf_op::DEREF],
fragment: None,
}
}
pub fn plus() -> Self {
Self {
elements: vec![dwarf_op::PLUS],
fragment: None,
}
}
pub fn const_u(val: u64) -> Self {
Self {
elements: vec![dwarf_op::CONSTU, val],
fragment: None,
}
}
pub fn const_s(val: i64) -> Self {
Self {
elements: vec![dwarf_op::CONSTS, val as u64],
fragment: None,
}
}
pub fn stack_value() -> Self {
Self {
elements: vec![dwarf_op::STACK_VALUE],
fragment: None,
}
}
pub fn and() -> Self {
Self {
elements: vec![dwarf_op::AND],
fragment: None,
}
}
pub fn or() -> Self {
Self {
elements: vec![dwarf_op::OR],
fragment: None,
}
}
pub fn xor() -> Self {
Self {
elements: vec![dwarf_op::XOR],
fragment: None,
}
}
pub fn not() -> Self {
Self {
elements: vec![dwarf_op::NOT],
fragment: None,
}
}
pub fn neg() -> Self {
Self {
elements: vec![dwarf_op::NEG],
fragment: None,
}
}
pub fn addr(address: u64) -> Self {
Self {
elements: vec![dwarf_op::ADDR, address],
fragment: None,
}
}
pub fn entry_value(expr: &DIExpression) -> Self {
let mut elements = vec![dwarf_op::LLVM_ENTRY_VALUE];
elements.push(expr.elements.len() as u64);
elements.extend_from_slice(&expr.elements);
Self {
elements,
fragment: None,
}
}
pub fn bit_piece(size_in_bits: u64, offset_in_bits: u64) -> Self {
Self {
elements: vec![dwarf_op::BIT_PIECE, size_in_bits, offset_in_bits],
fragment: None,
}
}
pub fn with_fragment(mut self, offset_in_bits: u64, size_in_bits: u64) -> Self {
if self.fragment.is_some() {
let len = self.elements.len();
if len >= 3 && self.elements[len - 3] == dwarf_op::LLVM_FRAGMENT {
self.elements.truncate(len - 3);
}
}
self.elements.push(dwarf_op::LLVM_FRAGMENT);
self.elements.push(offset_in_bits);
self.elements.push(size_in_bits);
self.fragment = Some((offset_in_bits, size_in_bits));
self
}
pub fn implicit_pointer(deref_id: u64, offset: i64) -> Self {
Self {
elements: vec![dwarf_op::IMPLICIT_POINTER, deref_id, offset as u64],
fragment: None,
}
}
pub fn to_debug_string(&self) -> String {
if self.elements.is_empty() {
return "<empty>".to_string();
}
let parts: Vec<String> = self.elements.iter().map(|e| format!("0x{:x}", e)).collect();
parts.join(", ")
}
pub fn is_constant_expression(&self) -> bool {
if self.elements.is_empty() {
return false;
}
let first = self.elements[0];
let last = *self.elements.last().unwrap();
matches!(first, dwarf_op::CONSTU | dwarf_op::CONSTS) && last == dwarf_op::STACK_VALUE
}
pub fn combine(&self, other: &DIExpression) -> Self {
let mut elements = Vec::with_capacity(
self.non_fragment_elements().len() + other.non_fragment_elements().len(),
);
elements.extend_from_slice(self.non_fragment_elements());
elements.extend_from_slice(other.non_fragment_elements());
Self::new(elements)
}
fn non_fragment_elements(&self) -> &[u64] {
if self.fragment.is_some() && self.elements.len() >= 3 {
&self.elements[..self.elements.len() - 3]
} else {
&self.elements
}
}
}
impl MetadataStore {
pub fn attach_metadata(&mut self, value_index: usize, kind: &str, metadata_id: u32) {
let attachments = self.metadata_attachments.entry(value_index).or_default();
attachments.push((kind.to_string(), metadata_id));
}
pub fn get_metadata_attachment(&self, value_index: usize, kind: &str) -> Option<u32> {
self.metadata_attachments
.get(&value_index)
.and_then(|attachments| {
attachments
.iter()
.find(|(k, _)| k == kind)
.map(|(_, id)| *id)
})
}
pub fn erase_metadata_attachment(&mut self, value_index: usize, kind: &str) {
if let Some(attachments) = self.metadata_attachments.get_mut(&value_index) {
attachments.retain(|(k, _)| k != kind);
}
}
pub fn get_all_metadata_attachments(&self, value_index: usize) -> &[(String, u32)] {
match self.metadata_attachments.get(&value_index) {
Some(attachments) => attachments.as_slice(),
None => &[],
}
}
pub fn set_metadata_attachment(&mut self, value_index: usize, kind: &str, metadata_id: u32) {
let attachments = self.metadata_attachments.entry(value_index).or_default();
if let Some(existing) = attachments.iter_mut().find(|(k, _)| k == kind) {
*existing = (kind.to_string(), metadata_id);
} else {
attachments.push((kind.to_string(), metadata_id));
}
}
pub fn has_metadata_attachments(&self, value_index: usize) -> bool {
self.metadata_attachments
.get(&value_index)
.map_or(false, |v| !v.is_empty())
}
pub fn erase_all_metadata_attachments(&mut self, value_index: usize) {
self.metadata_attachments.remove(&value_index);
}
pub fn num_metadata_attachments(&self, value_index: usize) -> usize {
self.metadata_attachments
.get(&value_index)
.map_or(0, |v| v.len())
}
pub fn iter_metadata_attachments(&self) -> impl Iterator<Item = (usize, &[(String, u32)])> {
self.metadata_attachments
.iter()
.map(|(&idx, attachments)| (idx, attachments.as_slice()))
}
pub fn copy_metadata_attachments(&mut self, from: usize, to: usize) {
if let Some(attachments) = self.metadata_attachments.get(&from) {
let cloned: Vec<(String, u32)> = attachments.clone();
self.metadata_attachments.insert(to, cloned);
}
}
}
impl MetadataStore {
pub fn get_md_string(&self, id: u32) -> Option<&str> {
self.get_metadata(id).and_then(|mv| match &mv.kind {
MetadataKind::MDString(s) => Some(s.as_str()),
_ => None,
})
}
pub fn create_temporary_md_node(&mut self, operands: Vec<MetadataOperand>) -> u32 {
let id = self.values.len() as u32;
self.values.push(MetadataValue {
id,
kind: MetadataKind::MDNode(operands.clone()),
});
self.temporary_nodes
.insert(id, TemporaryNodeState::new(operands));
id
}
pub fn replace_temporary_md_node(&mut self, temp_id: u32, replacement_id: u32) {
if let Some(temp) = self.temporary_nodes.get_mut(&temp_id) {
temp.resolved = true;
temp.replacement = Some(replacement_id);
}
if let Some(mv) = self.values.get_mut(temp_id as usize) {
mv.kind = MetadataKind::MDNode(vec![MetadataOperand::Metadata(replacement_id)]);
}
}
pub fn resolve_temporary_md_nodes(&mut self) {
let replacements: Vec<(u32, u32)> = self
.temporary_nodes
.iter()
.filter_map(|(&temp_id, state)| {
if state.resolved {
state.replacement.map(|rep_id| (temp_id, rep_id))
} else {
None
}
})
.collect();
if replacements.is_empty() {
return;
}
for mv in &mut self.values {
match &mut mv.kind {
MetadataKind::MDNode(ops)
| MetadataKind::DistinctMDNode(ops)
| MetadataKind::MDTuple(ops) => {
for op in ops {
if let MetadataOperand::Metadata(id) = op {
for (temp_id, rep_id) in &replacements {
if *id == *temp_id {
*id = *rep_id;
}
}
}
}
}
_ => {}
}
}
for (_name, operands) in &mut self.named_nodes {
for op_id in operands {
for (temp_id, rep_id) in &replacements {
if *op_id == *temp_id {
*op_id = *rep_id;
}
}
}
}
for (_value_idx, attachments) in &mut self.metadata_attachments {
for (_kind, md_id) in attachments {
for (temp_id, rep_id) in &replacements {
if *md_id == *temp_id {
*md_id = *rep_id;
}
}
}
}
}
pub fn is_distinct(&self, id: u32) -> bool {
self.get_metadata(id).map_or(false, |mv| {
matches!(mv.kind, MetadataKind::DistinctMDNode(_))
})
}
pub fn is_temporary(&self, id: u32) -> bool {
self.temporary_nodes.contains_key(&id)
}
pub fn is_resolved(&self, id: u32) -> bool {
self.temporary_nodes
.get(&id)
.map_or(false, |state| state.resolved)
}
pub fn is_uniqued(&self, id: u32) -> bool {
self.get_metadata(id).map_or(false, |mv| {
matches!(mv.kind, MetadataKind::MDNode(_) | MetadataKind::MDTuple(_))
&& !self.temporary_nodes.contains_key(&id)
})
}
pub fn create_uniqued_md_node(&mut self, operands: Vec<MetadataOperand>) -> u32 {
for mv in &self.values {
if let MetadataKind::MDNode(existing_ops) = &mv.kind {
if self.temporary_nodes.contains_key(&mv.id) {
continue;
}
if operands_match(existing_ops, &operands) {
return mv.id;
}
}
}
self.create_md_node(operands)
}
pub fn add_named_metadata(&mut self, name: &str, operands: Vec<u32>) {
self.add_named_node(name, operands);
}
pub fn get_named_metadata(&self, name: &str) -> Option<&Vec<u32>> {
self.get_named_node(name)
}
pub fn get_or_create_named_metadata(&mut self, name: &str) -> &Vec<u32> {
if !self.named_nodes.contains_key(name) {
self.named_nodes.insert(name.to_string(), Vec::new());
}
self.named_nodes.get(name).unwrap()
}
pub fn get_or_create_named_metadata_mut(&mut self, name: &str) -> &mut Vec<u32> {
self.named_nodes
.entry(name.to_string())
.or_insert_with(Vec::new)
}
pub fn erase_named_metadata(&mut self, name: &str) {
self.remove_named_node(name);
}
pub fn is_distinct_node(&self, id: u32) -> bool {
self.is_distinct(id)
}
pub fn is_unresolved_temporary(&self, id: u32) -> bool {
self.is_temporary(id) && !self.is_resolved(id)
}
pub fn get_temporary_replacement(&self, id: u32) -> Option<u32> {
self.temporary_nodes
.get(&id)
.and_then(|state| state.replacement)
}
pub fn num_temporaries(&self) -> usize {
self.temporary_nodes.len()
}
pub fn num_unresolved_temporaries(&self) -> usize {
self.temporary_nodes
.values()
.filter(|state| !state.resolved)
.count()
}
pub fn add_di_basic_type(&mut self, basic: DIBasicType) -> u32 {
let id = self.debug_types.len() as u32;
let ty = DIType {
name: basic.name,
size_in_bits: basic.size_in_bits,
align_in_bits: basic.align_in_bits,
offset_in_bits: 0,
encoding: basic.encoding,
base_type: None,
file: None,
line: None,
tag: 36, flags: basic.flags,
};
self.debug_types.push(Some(ty));
id
}
pub fn iter_metadata(&self) -> impl Iterator<Item = &MetadataValue> {
self.values.iter()
}
pub fn iter_named_metadata(&self) -> impl Iterator<Item = (&String, &Vec<u32>)> {
self.named_nodes.iter()
}
pub fn dump_debug(&self) -> String {
let mut lines = Vec::new();
lines.push(format!(
"MetadataStore: {} values, {} named nodes",
self.values.len(),
self.named_nodes.len()
));
for mv in &self.values {
let kind_str = match &mv.kind {
MetadataKind::MDString(s) => format!("MDString(\"{}\")", s),
MetadataKind::Constant(c) => format!("Constant({})", c.to_string()),
MetadataKind::Value(v) => format!("Value(%{})", v),
MetadataKind::MDNode(ops) => {
format!("MDNode([{} ops])", ops.len())
}
MetadataKind::DistinctMDNode(ops) => {
format!("DistinctMDNode([{} ops])", ops.len())
}
MetadataKind::MDTuple(ops) => {
format!("MDTuple([{} ops])", ops.len())
}
MetadataKind::NamedNode(name, _) => format!("NamedNode({})", name),
};
lines.push(format!(" !{} = {}", mv.id, kind_str));
}
for (name, ops) in &self.named_nodes {
lines.push(format!(" !{} -> [{} ops]", name, ops.len()));
}
lines.join("\n")
}
}
fn operands_match(a: &[MetadataOperand], b: &[MetadataOperand]) -> bool {
if a.len() != b.len() {
return false;
}
a.iter().zip(b.iter()).all(|(a, b)| match (a, b) {
(MetadataOperand::Metadata(id_a), MetadataOperand::Metadata(id_b)) => id_a == id_b,
(MetadataOperand::MDString(s_a), MetadataOperand::MDString(s_b)) => s_a == s_b,
(MetadataOperand::Null, MetadataOperand::Null) => true,
(MetadataOperand::Value(v_a), MetadataOperand::Value(v_b)) => v_a == v_b,
(MetadataOperand::Constant(c_a), MetadataOperand::Constant(c_b)) => {
c_a.to_string() == c_b.to_string()
}
_ => false,
})
}
pub mod checksum_kind {
pub const MD5: &str = "CSK_MD5";
pub const SHA1: &str = "CSK_SHA1";
pub const SHA256: &str = "CSK_SHA256";
}
pub mod emission_kind {
pub const FULL_DEBUG: u32 = 0;
pub const LINE_TABLES_ONLY: u32 = 1;
pub const NO_DEBUG: u32 = 2;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metadata_operand_is_metadata_ref() {
let op_ref = MetadataOperand::Metadata(42);
assert!(op_ref.is_metadata_ref());
assert_eq!(op_ref.as_metadata_id(), Some(42));
let op_str = MetadataOperand::MDString("hello".to_string());
assert!(!op_str.is_metadata_ref());
assert_eq!(op_str.as_metadata_id(), None);
let op_null = MetadataOperand::Null;
assert!(!op_null.is_metadata_ref());
assert_eq!(op_null.as_metadata_id(), None);
}
#[test]
fn test_metadata_operand_debug_string() {
assert_eq!(MetadataOperand::Metadata(7).to_debug_string(), "!7");
assert_eq!(
MetadataOperand::MDString("test".to_string()).to_debug_string(),
"!\"test\""
);
assert_eq!(MetadataOperand::Null.to_debug_string(), "null");
}
#[test]
fn test_constant_value_to_string() {
let ci = ConstantValue::new_int(llvm_native_core::types::Type::i32(), 42);
assert_eq!(ci.to_string(), "i32 42");
let cf = ConstantValue::new_float(llvm_native_core::types::Type::double(), 3.14);
assert!(cf.to_string().contains("3.14"));
let cn = ConstantValue::null(llvm_native_core::types::Type::pointer(0));
assert_eq!(cn.to_string(), "null");
}
#[test]
fn test_metadata_store_new_empty() {
let store = MetadataStore::new();
assert!(store.is_empty());
assert_eq!(store.count(), 0);
}
#[test]
fn test_metadata_store_add_and_get() {
let mut store = MetadataStore::new();
let id1 = store.add_metadata(MetadataKind::MDString("hello".to_string()));
let id2 = store.add_metadata(MetadataKind::MDString("world".to_string()));
assert_eq!(id1, 0);
assert_eq!(id2, 1);
assert_eq!(store.count(), 2);
let mv1 = store.get_metadata(0);
assert!(mv1.is_some());
if let MetadataKind::MDString(s) = &mv1.unwrap().kind {
assert_eq!(s, "hello");
} else {
panic!("Expected MDString");
}
assert!(store.get_metadata(999).is_none());
}
#[test]
fn test_metadata_store_mdnode() {
let mut store = MetadataStore::new();
let str_id = store.create_md_string("my_string");
let node_id = store.create_md_node(vec![
MetadataOperand::MDString("key".to_string()),
MetadataOperand::Metadata(str_id),
MetadataOperand::Null,
]);
assert_eq!(str_id, 0);
assert_eq!(node_id, 1);
assert_eq!(store.count(), 2);
let node = store.get_metadata(node_id).unwrap();
match &node.kind {
MetadataKind::MDNode(ops) => {
assert_eq!(ops.len(), 3);
}
_ => panic!("Expected MDNode"),
}
}
#[test]
fn test_metadata_store_named_nodes() {
let mut store = MetadataStore::new();
let id0 = store.add_metadata(MetadataKind::MDString("flag1".to_string()));
let id1 = store.add_metadata(MetadataKind::MDString("flag2".to_string()));
store.add_named_node("llvm.module.flags", vec![id0, id1]);
assert!(store.has_named_node("llvm.module.flags"));
assert!(!store.has_named_node("nonexistent"));
let ops = store.get_named_node("llvm.module.flags").unwrap();
assert_eq!(ops.len(), 2);
assert_eq!(ops[0], id0);
assert_eq!(ops[1], id1);
store.remove_named_node("llvm.module.flags");
assert!(!store.has_named_node("llvm.module.flags"));
}
#[test]
fn test_di_location() {
let loc = DILocation::new(42, 10, 1);
assert_eq!(loc.line, 42);
assert_eq!(loc.column, 10);
assert_eq!(loc.scope, 1);
assert!(loc.inlined_at.is_none());
let loc2 = DILocation::new(100, 5, 2).with_inlined_at(1);
assert_eq!(loc2.inlined_at, Some(1));
}
#[test]
fn test_di_file() {
let file = DIFile::new("main.c".to_string(), "/home/user/src".to_string());
assert_eq!(file.filename, "main.c");
assert_eq!(file.full_path(), "/home/user/src/main.c");
let file2 = DIFile::new("standalone.s".to_string(), String::new());
assert_eq!(file2.full_path(), "standalone.s");
}
#[test]
fn test_di_subprogram() {
let sp = DISubprogram::new("my_func".to_string(), 0, 42, 1);
assert_eq!(sp.name, "my_func");
assert_eq!(sp.file, 0);
assert_eq!(sp.line, 42);
assert_eq!(sp.unit, 1);
assert!(sp.is_definition);
assert!(!sp.is_local);
}
#[test]
fn test_di_type() {
let ty = DIType::new_basic("int".to_string(), 32, 32, 5); assert_eq!(ty.name, "int");
assert_eq!(ty.size_in_bits, 32);
assert_eq!(ty.align_in_bits, 32);
assert_eq!(ty.size_in_bytes(), 4);
assert_eq!(ty.encoding, 5);
}
#[test]
fn test_di_local_variable() {
let var = DILocalVariable::new("x".to_string(), 0, 1, 10, 2);
assert_eq!(var.name, "x");
assert_eq!(var.scope, 0);
assert_eq!(var.line, 10);
assert_eq!(var.var_type, 2);
assert!(var.arg.is_none());
}
#[test]
fn test_di_global_variable() {
let var = DIGlobalVariable::new("g_counter".to_string(), 0, 1, 5, 2, false, true);
assert_eq!(var.name, "g_counter");
assert!(var.is_definition);
assert!(!var.is_local_to_unit);
}
#[test]
fn test_di_compile_unit() {
let cu = DICompileUnit::new(12, 0, "clang 18.0".to_string()); assert_eq!(cu.language, 12);
assert_eq!(cu.file, 0);
assert_eq!(cu.producer, "clang 18.0");
assert!(!cu.is_optimized);
assert_eq!(cu.emission_kind, 0);
}
#[test]
fn test_metadata_store_debug_location() {
let mut store = MetadataStore::new();
let loc = DILocation::new(10, 5, 0);
let id = store.add_debug_location(loc);
let retrieved = store.get_debug_location(id);
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().line, 10);
assert_eq!(retrieved.unwrap().column, 5);
}
#[test]
fn test_metadata_store_debug_file() {
let mut store = MetadataStore::new();
let file = DIFile::new("test.rs".to_string(), "/src".to_string());
let id = store.add_debug_file(file);
let retrieved = store.get_debug_file(id);
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().filename, "test.rs");
let found = store.find_debug_file("test.rs", "/src");
assert_eq!(found, Some(id));
let not_found = store.find_debug_file("nonexistent.rs", "/src");
assert_eq!(not_found, None);
}
#[test]
fn test_metadata_store_debug_subprogram() {
let mut store = MetadataStore::new();
let sp = DISubprogram::new("main".to_string(), 0, 1, 2);
let id = store.add_debug_subprogram(sp);
let retrieved = store.get_debug_subprogram(id);
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().name, "main");
let found = store.find_debug_subprogram("main");
assert_eq!(found, Some(id));
assert_eq!(store.find_debug_subprogram("nonexistent"), None);
}
#[test]
fn test_metadata_store_debug_type() {
let mut store = MetadataStore::new();
let ty = DIType::new_basic("i32".to_string(), 32, 32, 5);
let id = store.add_debug_type(ty);
let retrieved = store.get_debug_type(id);
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().name, "i32");
}
#[test]
fn test_metadata_store_debug_var() {
let mut store = MetadataStore::new();
let local = DILocalVariable::new("x".to_string(), 0, 1, 5, 2);
let local_id = store.add_debug_local_var(local);
let global = DIGlobalVariable::new("g".to_string(), 0, 1, 3, 4, true, true);
let global_id = store.add_debug_global_var(global);
assert!(store.get_debug_local_var(local_id).is_some());
assert!(store.get_debug_global_var(global_id).is_some());
assert!(store.get_debug_local_var(999).is_none());
}
#[test]
fn test_metadata_store_debug_compile_unit() {
let mut store = MetadataStore::new();
let cu = DICompileUnit::new(12, 0, "rustc".to_string());
let id = store.add_debug_compile_unit(cu);
let retrieved = store.get_debug_compile_unit(id);
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().producer, "rustc");
}
#[test]
fn test_metadata_store_emit_assembly() {
let mut store = MetadataStore::new();
store.add_metadata(MetadataKind::MDString("test".to_string()));
store.create_md_node(vec![MetadataOperand::Metadata(0)]);
store.add_named_node("llvm.ident", vec![0]);
let asm = store.emit_assembly();
assert!(asm.contains("test"));
assert!(asm.contains("llvm.ident"));
}
#[test]
fn test_metadata_store_default() {
let store = MetadataStore::default();
assert!(store.is_empty());
assert_eq!(store.count(), 0);
}
#[test]
fn test_metadata_store_set_debug_location() {
let mut store = MetadataStore::new();
store.set_debug_location(5, DILocation::new(50, 5, 1));
let loc = store.get_debug_location(5);
assert!(loc.is_some());
assert_eq!(loc.unwrap().line, 50);
}
#[test]
fn test_constant_value_zeroinit() {
let cv = ConstantValue {
ty: llvm_native_core::types::Type::i32(),
repr: ConstantRepr::ZeroInitializer,
};
assert_eq!(cv.to_string(), "zeroinitializer");
}
#[test]
fn test_metadata_value_id_consistency() {
let mut store = MetadataStore::new();
let ids: Vec<u32> = (0..10)
.map(|i| store.add_metadata(MetadataKind::MDString(format!("v{}", i))))
.collect();
for (i, id) in ids.iter().enumerate() {
assert_eq!(*id, i as u32);
let mv = store.get_metadata(*id).unwrap();
assert_eq!(mv.id, *id);
}
}
#[test]
fn test_metadata_store_mdref_helper() {
let op = MetadataStore::md_ref(42);
assert_eq!(op.as_metadata_id(), Some(42));
let op_str = MetadataStore::md_string_op("hello");
match op_str {
MetadataOperand::MDString(s) => assert_eq!(s, "hello"),
_ => panic!("Expected MDString"),
}
}
#[test]
fn test_dilocation_with_inlined_at() {
let loc = DILocation::new(10, 1, 0).with_inlined_at(5);
assert_eq!(loc.line, 10);
assert_eq!(loc.column, 1);
assert_eq!(loc.scope, 0);
assert_eq!(loc.inlined_at, Some(5));
}
}