use crate::bitstream::{
self, block_ids, constants_codes, function_codes, metadata_codes, module_codes,
paramattr_codes, paramattr_group_codes, type_codes, value_symtab_codes, BitstreamEntry,
BitstreamReader,
};
use crate::function;
use crate::module::Module;
use crate::opcode::Opcode;
use crate::types::{Type, TypeId, TypeKind};
use crate::value::{valref, SubclassKind, Value, ValueRef};
fn map_binop_to_ir(bitcode_op: u64) -> Option<Opcode> {
match bitcode_op {
0 => Some(Opcode::Add),
1 => Some(Opcode::Sub),
2 => Some(Opcode::Mul),
3 => Some(Opcode::UDiv),
4 => Some(Opcode::SDiv),
5 => Some(Opcode::URem),
6 => Some(Opcode::SRem),
7 => Some(Opcode::Shl),
8 => Some(Opcode::LShr),
9 => Some(Opcode::AShr),
10 => Some(Opcode::And),
11 => Some(Opcode::Or),
12 => Some(Opcode::Xor),
_ => None,
}
}
fn map_cast_to_ir(bitcode_op: u64) -> Option<Opcode> {
match bitcode_op {
0 => Some(Opcode::Trunc),
1 => Some(Opcode::ZExt),
2 => Some(Opcode::SExt),
3 => Some(Opcode::FPTrunc),
4 => Some(Opcode::FPExt),
5 => Some(Opcode::FPToUI),
6 => Some(Opcode::FPToSI),
7 => Some(Opcode::UIToFP),
8 => Some(Opcode::SIToFP),
9 => Some(Opcode::BitCast),
10 => Some(Opcode::AddrSpaceCast),
_ => None,
}
}
fn map_cmp_predicate(pred: u64) -> &'static str {
match pred {
32 => "eq",
33 => "ne",
34 => "ugt",
35 => "uge",
36 => "ult",
37 => "ule",
38 => "sgt",
39 => "sge",
40 => "slt",
41 => "sle",
0 => "oeq",
1 => "ogt",
2 => "oge",
3 => "olt",
4 => "ole",
5 => "one",
6 => "ord",
7 => "uno",
8 => "ueq",
9 => "ugt",
10 => "uge",
11 => "ult",
12 => "ule",
13 => "une",
14 => "true",
_ => "unknown",
}
}
fn map_ir_to_binop(op: Opcode) -> Option<u64> {
match op {
Opcode::Add => Some(0),
Opcode::Sub => Some(1),
Opcode::Mul => Some(2),
Opcode::UDiv => Some(3),
Opcode::SDiv => Some(4),
Opcode::URem => Some(5),
Opcode::SRem => Some(6),
Opcode::Shl => Some(7),
Opcode::LShr => Some(8),
Opcode::AShr => Some(9),
Opcode::And => Some(10),
Opcode::Or => Some(11),
Opcode::Xor => Some(12),
_ => None,
}
}
fn map_ir_to_cast(op: Opcode) -> Option<u64> {
match op {
Opcode::Trunc => Some(0),
Opcode::ZExt => Some(1),
Opcode::SExt => Some(2),
Opcode::FPTrunc => Some(3),
Opcode::FPExt => Some(4),
Opcode::FPToUI => Some(5),
Opcode::FPToSI => Some(6),
Opcode::UIToFP => Some(7),
Opcode::SIToFP => Some(8),
Opcode::BitCast => Some(9),
Opcode::AddrSpaceCast => Some(10),
_ => None,
}
}
struct ModuleParseState {
in_module: bool,
in_function: bool,
in_constants: bool,
in_metadata: bool,
in_paramattr: bool,
in_paramattr_group: bool,
in_value_symtab: bool,
current_function_blocks: usize,
type_table: Vec<Type>,
constants_table: Vec<ValueRef>,
metadata_strings: Vec<String>,
metadata_values: Vec<ValueRef>,
}
impl ModuleParseState {
fn new() -> Self {
Self {
in_module: false,
in_function: false,
in_constants: false,
in_metadata: false,
in_paramattr: false,
in_paramattr_group: false,
in_value_symtab: false,
current_function_blocks: 0,
type_table: Vec::new(),
constants_table: Vec::new(),
metadata_strings: Vec::new(),
metadata_values: Vec::new(),
}
}
}
pub fn read_bitcode(bytes: &[u8]) -> Option<Module> {
if bytes.len() < 4 || &bytes[0..4] != b"BSC\x00" {
return None;
}
let mut reader = BitstreamReader::new(bytes);
let mut module = Module::new("bitcode");
let mut state = ModuleParseState::new();
let mut value_table: Vec<ValueRef> = Vec::new();
let mut basic_blocks: Vec<ValueRef> = Vec::new();
let mut current_function: Option<ValueRef> = None;
while let Some(entry) = reader.read_entry() {
match &entry {
BitstreamEntry::SubBlock {
block_id,
code_len: _,
} => match *block_id {
block_ids::MODULE_BLOCK => state.in_module = true,
block_ids::IDENTIFICATION_BLOCK => {}
block_ids::TYPE_BLOCK => {}
block_ids::FUNCTION_BLOCK => {
state.in_function = true;
state.current_function_blocks = 0;
value_table.clear();
basic_blocks.clear();
current_function = module.functions.last().cloned();
}
block_ids::VALUE_SYMTAB_BLOCK => {
state.in_value_symtab = true;
}
block_ids::CONSTANTS_BLOCK => {
state.in_constants = true;
}
block_ids::METADATA_BLOCK => {
state.in_metadata = true;
}
block_ids::STRTAB_BLOCK => {}
block_ids::PARAMATTR_BLOCK => {
state.in_paramattr = true;
}
block_ids::PARAMATTR_GROUP_BLOCK => {
state.in_paramattr_group = true;
}
_ => {}
},
BitstreamEntry::EndBlock => {
if state.in_function {
state.in_function = false;
}
if state.in_constants {
state.in_constants = false;
}
if state.in_metadata {
state.in_metadata = false;
}
if state.in_paramattr {
state.in_paramattr = false;
}
if state.in_paramattr_group {
state.in_paramattr_group = false;
}
if state.in_value_symtab {
state.in_value_symtab = false;
}
}
BitstreamEntry::Record { code, ops } => {
if state.in_function {
handle_function_record(
&mut module,
*code,
ops,
&state.type_table,
&mut value_table,
&mut basic_blocks,
&mut current_function,
&mut state.current_function_blocks,
);
} else if state.in_module {
handle_module_record(
&mut module,
*code,
ops,
&state.type_table,
&state.constants_table,
);
} else if state.in_constants {
parse_constants_record(
*code,
ops,
&mut state.type_table,
&mut state.constants_table,
&mut reader,
);
} else if state.in_metadata {
parse_metadata_record(
*code,
ops,
&mut state.metadata_strings,
&mut state.metadata_values,
&state.constants_table,
);
} else if state.in_paramattr {
parse_paramattr_record(*code, ops);
} else if state.in_paramattr_group {
parse_paramattr_group_record(*code, ops);
} else if state.in_value_symtab {
parse_value_symtab_record(*code, ops, &mut module, ¤t_function);
} else {
handle_top_level_record(*code, ops, &mut state.type_table);
}
}
BitstreamEntry::DefineAbbrev { .. } => {}
}
}
Some(module)
}
fn handle_module_record(
module: &mut Module,
code: u32,
ops: &[u64],
type_table: &[Type],
constants_table: &[ValueRef],
) {
match code {
module_codes::VERSION => {
let _version = ops.first().copied().unwrap_or(0);
}
module_codes::TRIPLE => {
let triple = decode_raw_record(ops);
module.set_target_triple(&triple);
}
module_codes::DATALAYOUT => {
let dl = decode_raw_record(ops);
module.set_data_layout(&dl);
}
module_codes::ASM => {
let filename = decode_raw_record(ops);
module.source_filename = filename;
}
module_codes::SECTION_NAME => {
let _section = decode_raw_record(ops);
}
module_codes::GC_NAME => {
let _gc = decode_raw_record(ops);
}
module_codes::FUNCTION => {
let name = decode_raw_record(ops);
let func = function::new_function(&name, Type::void(), &[]);
func.borrow_mut().name = name;
module.add_function(func);
}
module_codes::GLOBAL_VAR => {
if ops.is_empty() {
return;
}
let type_idx = ops.first().copied().unwrap_or(0) as usize;
let is_constant = ops.get(1).copied().unwrap_or(0) != 0;
let init_id = ops.get(2).copied().unwrap_or(0);
let linkage_val = ops.get(3).copied().unwrap_or(0);
let alignment = ops.get(4).copied().unwrap_or(0) as u32;
let _visibility = ops.get(5).copied().unwrap_or(0);
let _thread_local = ops.get(6).copied().unwrap_or(0) != 0;
let _unnamed_addr = ops.get(7).copied().unwrap_or(0) != 0;
let _extern_init = ops.get(8).copied().unwrap_or(0) != 0;
let _dll_storage = ops.get(9).copied().unwrap_or(0);
let name_start = 10;
let name = if ops.len() > name_start {
decode_char6_from_ops(&ops[name_start..])
} else {
String::new()
};
if name.is_empty() {
return;
}
let ty = type_table
.get(type_idx)
.cloned()
.unwrap_or_else(|| Type::i32());
let _linkage = match linkage_val {
0 => function::Linkage::External,
3 => function::Linkage::Internal,
4 => function::Linkage::LinkOnceAny,
5 => function::Linkage::WeakODR,
8 => function::Linkage::Private,
_ => function::Linkage::External,
};
let _init = if init_id != 0 && init_id as usize <= constants_table.len() {
constants_table.get(init_id as usize - 1).cloned()
} else {
None
};
let mut gv = Value::new(ty).with_subclass(SubclassKind::GlobalVariable);
gv.name = name;
let gv_ref = valref(gv);
module.globals.push(gv_ref);
let _ = (is_constant, alignment);
}
module_codes::ALIAS => {
if ops.len() >= 2 {
let type_idx = ops[0] as usize;
let name_start = 6;
let name = if ops.len() > name_start {
decode_char6_from_ops(&ops[name_start..])
} else {
String::new()
};
let ty = type_table
.get(type_idx)
.cloned()
.unwrap_or_else(|| Type::i32());
let mut alias_v = Value::new(ty).with_subclass(SubclassKind::GlobalVariable);
alias_v.name = format!("@{}", name);
module.globals.push(valref(alias_v));
}
}
_ => {}
}
}
fn handle_function_record(
_module: &mut Module,
code: u32,
ops: &[u64],
type_table: &[Type],
value_table: &mut Vec<ValueRef>,
basic_blocks: &mut Vec<ValueRef>,
current_function: &mut Option<ValueRef>,
current_blocks: &mut usize,
) {
match code {
c if c == function_codes::DECLAREBLOCKS => {
*current_blocks = ops.first().copied().unwrap_or(0) as usize;
}
c if c == function_codes::BINOP => {
if ops.len() >= 3 {
let bc_op = ops[0];
let lhs_idx = ops[1] as usize;
let rhs_idx = ops[2] as usize;
let lhs = value_table.get(lhs_idx).cloned();
let rhs = value_table.get(rhs_idx).cloned();
if let (Some(op), Some(lhs_v), Some(rhs_v)) = (map_binop_to_ir(bc_op), lhs, rhs) {
let ty = lhs_v.borrow().ty.clone();
let inst = make_instruction(op, ty, &[lhs_v, rhs_v]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::CAST => {
if ops.len() >= 3 {
let bc_op = ops[0];
let val_idx = ops[1] as usize;
let to_type_idx = ops[2] as usize;
if let (Some(op), Some(val), Some(to_ty)) = (
map_cast_to_ir(bc_op),
value_table.get(val_idx).cloned(),
type_table.get(to_type_idx).cloned(),
) {
let inst = make_instruction(op, to_ty, &[val]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
if bc_op >= 11 {
let op = match bc_op {
11 => Some(Opcode::PtrToInt),
12 => Some(Opcode::IntToPtr),
_ => None,
};
if let (Some(op), Some(val), Some(to_ty)) = (
op,
value_table.get(val_idx).cloned(),
type_table.get(to_type_idx).cloned(),
) {
let inst = make_instruction(op, to_ty, &[val]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
}
c if c == function_codes::INST_GEP || c == function_codes::GEP => {
if ops.len() >= 3 {
let _inbounds = (c == function_codes::INST_INBOUNDS_GEP
|| c == function_codes::GEP_INBOUNDS
|| c == function_codes::INBOUNDS_GEP)
|| ops.get(0).copied().unwrap_or(0) != 0;
let type_idx = ops.get(0).copied().unwrap_or(0) as usize;
let ptr_idx = ops.get(1).copied().unwrap_or(0) as usize;
let num_indices = ops.get(2).copied().unwrap_or(0) as usize;
if let Some(ptr) = value_table.get(ptr_idx).cloned() {
let mut indices = Vec::new();
for i in 0..num_indices {
let idx = ops.get(3 + i).copied().unwrap_or(0) as usize;
if let Some(v) = value_table.get(idx).cloned() {
indices.push(v);
}
}
let result_ty = Type::pointer(0);
let inst = make_instruction(Opcode::GetElementPtr, result_ty, &{
let mut all = vec![ptr];
all.extend(indices);
all
});
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::INST_LOAD || c == function_codes::LOAD => {
if ops.len() >= 2 {
let type_idx = ops[0] as usize;
let ptr_idx = ops[1] as usize;
let load_ty = type_table
.get(type_idx)
.cloned()
.unwrap_or_else(|| Type::i32());
if let Some(ptr) = value_table.get(ptr_idx).cloned() {
let inst = make_instruction(Opcode::Load, load_ty, &[ptr]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::INST_STORE || c == function_codes::STORE => {
if ops.len() >= 2 {
let val_idx = ops[0] as usize;
let ptr_idx = ops[1] as usize;
if let (Some(val), Some(ptr)) = (
value_table.get(val_idx).cloned(),
value_table.get(ptr_idx).cloned(),
) {
let inst = make_instruction(Opcode::Store, Type::void(), &[val, ptr]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::INST_ALLOCA || c == function_codes::ALLOCA => {
if ops.len() >= 2 {
let type_idx = ops[0] as usize;
let _alignment = ops.get(1).copied().unwrap_or(0);
let alloc_ty = type_table
.get(type_idx)
.cloned()
.unwrap_or_else(|| Type::i32());
let ptr_ty = Type::pointer(0);
let inst = make_instruction(Opcode::Alloca, ptr_ty, &[]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
c if c == function_codes::INST_RET || c == function_codes::RET => {
if ops.is_empty() {
let inst = make_instruction(Opcode::Ret, Type::void(), &[]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
} else {
let val_idx = ops[0] as usize;
if let Some(val) = value_table.get(val_idx).cloned() {
let inst = make_instruction(Opcode::Ret, Type::void(), &[val]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::BR => {
if ops.len() == 2 {
let dest_idx = ops[1] as usize;
if let Some(dest) = value_table.get(dest_idx).cloned() {
let inst = make_instruction(Opcode::Br, Type::void(), &[dest]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
} else if ops.len() >= 3 {
let cond_idx = ops[0] as usize;
let true_idx = ops[1] as usize;
let false_idx = ops[2] as usize;
if let (Some(cond), Some(t_dest), Some(f_dest)) = (
value_table.get(cond_idx).cloned(),
value_table.get(true_idx).cloned(),
value_table.get(false_idx).cloned(),
) {
let inst = make_instruction(Opcode::Br, Type::void(), &[cond, t_dest, f_dest]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::SWITCH => {
if ops.len() >= 3 {
let type_idx = ops[0] as usize;
let val_idx = ops[1] as usize;
let default_idx = ops[2] as usize;
let num_cases = ((ops.len() - 3) / 2) as usize;
if let (Some(val), Some(default_dest)) = (
value_table.get(val_idx).cloned(),
value_table.get(default_idx).cloned(),
) {
let mut switch_ops = vec![val, default_dest];
for i in 0..num_cases {
let case_val_idx = ops[3 + i * 2] as usize;
let case_dest_idx = ops[4 + i * 2] as usize;
if let (Some(cv), Some(cd)) = (
value_table.get(case_val_idx).cloned(),
value_table.get(case_dest_idx).cloned(),
) {
switch_ops.push(cv);
switch_ops.push(cd);
}
}
let inst = make_instruction(Opcode::Switch, Type::void(), &switch_ops);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::UNREACHABLE => {
let inst = make_instruction(Opcode::Unreachable, Type::void(), &[]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
c if c == function_codes::INST_PHI || c == function_codes::PHI => {
if ops.len() >= 4 {
let type_idx = ops[0] as usize;
let num_incoming = ops[1] as usize;
let phi_ty = type_table
.get(type_idx)
.cloned()
.unwrap_or_else(|| Type::i32());
let mut incoming = Vec::new();
for i in 0..num_incoming {
let val_idx = ops[2 + i * 2] as usize;
let bb_idx = ops[3 + i * 2] as usize;
if let (Some(v), Some(bb)) = (
value_table.get(val_idx).cloned(),
value_table.get(bb_idx).cloned(),
) {
incoming.push(v);
incoming.push(bb);
}
}
let inst = make_instruction(Opcode::Phi, phi_ty, &incoming);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
c if c == function_codes::SELECT || c == function_codes::VSELECT => {
if ops.len() >= 4 {
let cond_idx = ops[0] as usize;
let t_idx = ops[1] as usize;
let f_idx = ops[2] as usize;
if let (Some(cond), Some(t_val), Some(f_val)) = (
value_table.get(cond_idx).cloned(),
value_table.get(t_idx).cloned(),
value_table.get(f_idx).cloned(),
) {
let ty = t_val.borrow().ty.clone();
let inst = make_instruction(Opcode::Select, ty, &[cond, t_val, f_val]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::INST_CALL || c == function_codes::CALL => {
if ops.len() >= 4 {
let callee_val_idx = ops[0] as usize; let num_args = ops[1] as usize;
let ret_type_idx = ops[2] as usize;
let callee_idx = ops[3] as usize;
let ret_ty = type_table
.get(ret_type_idx)
.cloned()
.unwrap_or_else(|| Type::void());
if let Some(callee) = value_table.get(callee_idx).cloned() {
let mut args = Vec::new();
for i in 0..num_args {
let arg_idx = ops.get(4 + i).copied().unwrap_or(0) as usize;
if let Some(a) = value_table.get(arg_idx).cloned() {
args.push(a);
}
}
let all_ops = {
let mut o = vec![callee];
o.extend(args);
o
};
let inst = make_instruction(Opcode::Call, ret_ty, &all_ops);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::CALL_ATTR => {
if ops.len() >= 4 {
let callee_idx = ops[0] as usize;
let num_args = ops[1] as usize;
let ret_type_idx = ops[2] as usize;
let ret_ty = type_table
.get(ret_type_idx)
.cloned()
.unwrap_or_else(|| Type::void());
if let Some(callee) = value_table.get(callee_idx).cloned() {
let mut args = Vec::new();
for i in 0..num_args {
let arg_idx = ops.get(3 + i).copied().unwrap_or(0) as usize;
if let Some(a) = value_table.get(arg_idx).cloned() {
args.push(a);
}
}
let all_ops = {
let mut o = vec![callee];
o.extend(args);
o
};
let inst = make_instruction(Opcode::Call, ret_ty, &all_ops);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::CMP || c == function_codes::CMP2 => {
if ops.len() >= 4 {
let type_idx = ops[0] as usize;
let pred = ops[1];
let lhs_idx = ops[2] as usize;
let rhs_idx = ops[3] as usize;
let is_fcmp = c == function_codes::CMP2;
if let (Some(lhs), Some(rhs)) = (
value_table.get(lhs_idx).cloned(),
value_table.get(rhs_idx).cloned(),
) {
let op = if is_fcmp { Opcode::FCmp } else { Opcode::ICmp };
let inst = make_instruction(op, Type::i1(), &[lhs, rhs]);
inst.borrow_mut().name =
format!("{}.{}", op.as_mnemonic(), map_cmp_predicate(pred));
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::EXTRACTELT => {
if ops.len() >= 2 {
let vec_idx = ops[0] as usize;
let idx_idx = ops[1] as usize;
if let (Some(vec_val), Some(idx_val)) = (
value_table.get(vec_idx).cloned(),
value_table.get(idx_idx).cloned(),
) {
let elem_ty = match &vec_val.borrow().ty.kind {
TypeKind::FixedVector {
element_type_id, ..
}
| TypeKind::ScalableVector {
element_type_id, ..
} => element_type_id.clone(),
_ => TypeId::default(),
};
let ty = Type::i32(); let inst = make_instruction(Opcode::ExtractElement, ty, &[vec_val, idx_val]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::INSERTELT => {
if ops.len() >= 3 {
let vec_idx = ops[0] as usize;
let val_idx = ops[1] as usize;
let idx_idx = ops[2] as usize;
if let (Some(vec_val), Some(val), Some(idx_val)) = (
value_table.get(vec_idx).cloned(),
value_table.get(val_idx).cloned(),
value_table.get(idx_idx).cloned(),
) {
let ty = vec_val.borrow().ty.clone();
let inst =
make_instruction(Opcode::InsertElement, ty, &[vec_val, val, idx_val]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::SHUFFLEVEC => {
if ops.len() >= 3 {
let v1_idx = ops[0] as usize;
let v2_idx = ops[1] as usize;
let mask_idx = ops[2] as usize;
if let (Some(v1), Some(v2), Some(mask)) = (
value_table.get(v1_idx).cloned(),
value_table.get(v2_idx).cloned(),
value_table.get(mask_idx).cloned(),
) {
let ty = v1.borrow().ty.clone();
let inst = make_instruction(Opcode::ShuffleVector, ty, &[v1, v2, mask]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::EXTRACTVAL => {
if ops.len() >= 3 {
let agg_idx = ops[0] as usize;
let num_idxs = ops[1] as usize;
if let Some(agg) = value_table.get(agg_idx).cloned() {
let mut all_ops = vec![agg];
for i in 0..num_idxs {
let idx = ops.get(2 + i).copied().unwrap_or(0) as usize;
if let Some(v) = value_table.get(idx).cloned() {
all_ops.push(v);
}
}
let ty = Type::i32(); let inst = make_instruction(Opcode::ExtractValue, ty, &all_ops);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::INSERTVAL => {
if ops.len() >= 4 {
let agg_idx = ops[0] as usize;
let val_idx = ops[1] as usize;
let num_idxs = ops[2] as usize;
if let (Some(agg), Some(val)) = (
value_table.get(agg_idx).cloned(),
value_table.get(val_idx).cloned(),
) {
let ty = agg.borrow().ty.clone();
let mut all_ops = vec![agg, val];
for i in 0..num_idxs {
let idx = ops.get(3 + i).copied().unwrap_or(0) as usize;
if let Some(v) = value_table.get(idx).cloned() {
all_ops.push(v);
}
}
let inst = make_instruction(Opcode::InsertValue, ty, &all_ops);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::INST_ATOMICRMW => {
if ops.len() >= 4 {
let ptr_idx = ops[0] as usize;
let val_idx = ops[1] as usize;
let _rmw_op = ops[2];
let _ordering = ops.get(3).copied().unwrap_or(0);
if let (Some(ptr), Some(val)) = (
value_table.get(ptr_idx).cloned(),
value_table.get(val_idx).cloned(),
) {
let ty = val.borrow().ty.clone();
let inst = make_instruction(Opcode::AtomicRMW, ty, &[ptr, val]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::INST_FENCE => {
let inst = make_instruction(Opcode::Fence, Type::void(), &[]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
c if c == function_codes::INST_LANDINGPAD || c == function_codes::INST_LANDINGPAD_NEW => {
if ops.len() >= 2 {
let type_idx = ops[0] as usize;
let _is_cleanup = ops.get(1).copied().unwrap_or(0) != 0;
let ty = type_table
.get(type_idx)
.cloned()
.unwrap_or_else(|| Type::i32());
let inst = make_instruction(Opcode::LandingPad, ty, &[]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
c if c == function_codes::RESUME => {
if ops.len() >= 1 {
let val_idx = ops[0] as usize;
if let Some(val) = value_table.get(val_idx).cloned() {
let inst = make_instruction(Opcode::Resume, Type::void(), &[val]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::VAARG => {
if ops.len() >= 2 {
let _list_idx = ops[0] as usize;
let type_idx = ops[1] as usize;
let ty = type_table
.get(type_idx)
.cloned()
.unwrap_or_else(|| Type::i32());
let inst = make_instruction(Opcode::VAArg, ty, &[]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
c if c == function_codes::INDIRECTBR => {
if ops.len() >= 1 {
let addr_idx = ops[0] as usize;
if let Some(addr) = value_table.get(addr_idx).cloned() {
let inst = make_instruction(Opcode::IndirectBr, Type::void(), &[addr]);
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
c if c == function_codes::INVOKE => {
if ops.len() >= 6 {
let callee_idx = ops[0] as usize;
let normal_dest_idx = ops[1] as usize;
let unwind_dest_idx = ops[2] as usize;
let num_args = ops[3] as usize;
let ret_type_idx = ops[4] as usize;
let ret_ty = type_table
.get(ret_type_idx)
.cloned()
.unwrap_or_else(|| Type::void());
if let (Some(callee), Some(normal), Some(unwind)) = (
value_table.get(callee_idx).cloned(),
value_table.get(normal_dest_idx).cloned(),
value_table.get(unwind_dest_idx).cloned(),
) {
let mut args = vec![callee];
for i in 0..num_args {
let arg_idx = ops.get(5 + i).copied().unwrap_or(0) as usize;
if let Some(a) = value_table.get(arg_idx).cloned() {
args.push(a);
}
}
let inst = make_instruction(Opcode::Invoke, ret_ty, &args);
inst.borrow_mut().successors = vec![normal, unwind];
value_table.push(inst.clone());
push_to_current_bb(current_function, basic_blocks, &inst);
}
}
}
_ => {} }
}
fn make_instruction(op: Opcode, ty: Type, operands: &[ValueRef]) -> ValueRef {
let mut v = Value::new(ty).with_subclass(SubclassKind::Instruction);
v.opcode = Some(op);
v.operands = operands.to_vec();
v.num_operands = v.operands.len();
valref(v)
}
fn push_to_current_bb(
current_function: &mut Option<ValueRef>,
basic_blocks: &mut Vec<ValueRef>,
inst: &ValueRef,
) {
if let &mut Some(ref func) = current_function {
if let Some(last_bb) = basic_blocks.last() {
last_bb.borrow_mut().push_operand(inst.clone());
func.borrow_mut().push_operand(inst.clone());
} else {
let bb = create_anonymous_bb();
bb.borrow_mut().push_operand(inst.clone());
basic_blocks.push(bb.clone());
func.borrow_mut().push_operand(bb.clone());
}
}
}
fn create_anonymous_bb() -> ValueRef {
crate::basic_block::new_basic_block("")
}
fn handle_top_level_record(code: u32, ops: &[u64], type_table: &mut Vec<Type>) {
match code {
type_codes::NUM_ENTRY => {
let count = ops.first().copied().unwrap_or(0) as usize;
type_table.reserve(count);
}
type_codes::VOID => type_table.push(Type::void()),
type_codes::HALF => type_table.push(Type::half()),
type_codes::FLOAT => type_table.push(Type::float()),
type_codes::DOUBLE => type_table.push(Type::double()),
type_codes::LABEL => type_table.push(Type::label()),
type_codes::METADATA => type_table.push(Type::metadata()),
type_codes::OPAQUE => type_table.push(Type::struct_opaque(String::new())),
type_codes::INTEGER => {
let bits = ops.first().copied().unwrap_or(32) as u32;
type_table.push(Type::int(bits));
}
type_codes::POINTER => {
let addr_space = ops.first().copied().unwrap_or(0) as u32;
type_table.push(Type::pointer(addr_space));
}
type_codes::ARRAY => {
if ops.len() >= 2 {
let elem_type_idx = ops[0] as usize;
let len = ops[1];
let elem_id = type_table
.get(elem_type_idx)
.map(|t| t.id)
.unwrap_or(Type::i32().id);
type_table.push(Type::array_with(len, elem_id));
}
}
type_codes::VECTOR => {
if ops.len() >= 2 {
let len = ops[0] as u32;
let elem_type_idx = ops[1] as usize;
let elem_id = type_table
.get(elem_type_idx)
.map(|t| t.id)
.unwrap_or(Type::i32().id);
type_table.push(Type::fixed_vector_with(len, elem_id));
}
}
type_codes::STRUCT_ANON => {
if !ops.is_empty() {
let num_elements = ops[0] as usize;
let is_packed = if ops.len() > 1 { ops[1] != 0 } else { false };
let offset = if ops.len() > 1 { 2 } else { 1 };
let mut elements = Vec::new();
for i in 0..num_elements {
if offset + i < ops.len() {
let type_idx = ops[offset + i] as usize;
if let Some(ty) = type_table.get(type_idx) {
elements.push(ty.id);
}
}
}
type_table.push(Type::struct_literal_with(is_packed, elements));
}
}
type_codes::STRUCT_NAMED => {
if ops.len() >= 3 {
let is_packed = ops[0] != 0;
let num_elements = ops[1] as usize;
let name_len = ops[2] as usize;
let mut name = String::new();
for i in 3..3 + name_len {
let c = bitstream::decode_char6(ops.get(i).copied().unwrap_or(0) as u8);
name.push(c as char);
}
let offset = 3 + name_len;
let mut elements = Vec::new();
for i in 0..num_elements {
if offset + i < ops.len() {
let type_idx = ops[offset + i] as usize;
if let Some(ty) = type_table.get(type_idx) {
elements.push(ty.id);
}
}
}
type_table.push(Type::struct_named_with(name, is_packed, elements));
}
}
type_codes::FUNCTION if ops.len() >= 2 => {
let return_type_idx = ops[0] as usize;
let num_params = ops[1] as usize;
let is_vararg = ops.get(2).copied().unwrap_or(0) != 0;
let return_id = type_table
.get(return_type_idx)
.map(|t| t.id)
.unwrap_or(Type::void().id);
let mut params = Vec::new();
for i in 0..num_params {
let idx = ops.get(3 + i).copied().unwrap_or(0) as usize;
if let Some(ty) = type_table.get(idx) {
params.push(ty.id);
}
}
type_table.push(Type::function_type_with(return_id, params, is_vararg));
}
_ => {}
}
}
fn parse_constants_record(
code: u32,
ops: &[u64],
type_table: &[Type],
constants_table: &mut Vec<ValueRef>,
_reader: &mut BitstreamReader,
) {
match code {
c if c == constants_codes::SETTYPE => {
let _type_idx = ops.first().copied().unwrap_or(0) as usize;
}
c if c == constants_codes::NULL => {
let type_idx = if ops.is_empty() { 0 } else { ops[0] as usize };
let ty = type_table
.get(type_idx)
.cloned()
.unwrap_or_else(|| Type::i32());
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = "null".into();
constants_table.push(valref(v));
}
c if c == constants_codes::UNDEF => {
let type_idx = if ops.is_empty() { 0 } else { ops[0] as usize };
let ty = type_table
.get(type_idx)
.cloned()
.unwrap_or_else(|| Type::i32());
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = "undef".into();
constants_table.push(valref(v));
}
c if c == constants_codes::INTEGER => {
if ops.len() >= 1 {
let type_idx = ops[0] as usize;
let val = ops.get(1).copied().unwrap_or(0);
let ty = type_table
.get(type_idx)
.cloned()
.unwrap_or_else(|| Type::i32());
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = format!("{}", val as i64);
constants_table.push(valref(v));
}
}
c if c == constants_codes::WIDE_INTEGER => {
if ops.len() >= 1 {
let num_words = ops[0] as usize;
let val = if num_words > 0 {
ops.get(1).copied().unwrap_or(0)
} else {
0
};
let ty = Type::i64();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = format!("{}", val as i64);
constants_table.push(valref(v));
}
}
c if c == constants_codes::FLOAT => {
if ops.len() >= 1 {
let bits = ops[0];
let val = f64::from_bits(bits);
let ty = Type::double();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = format!("{}", val);
constants_table.push(valref(v));
}
}
c if c == constants_codes::AGGREGATE => {
if ops.len() >= 1 {
let num_elements = ops[0] as usize;
let mut elements = Vec::new();
for i in 0..num_elements {
let elem_idx = ops.get(1 + i).copied().unwrap_or(0) as usize;
if elem_idx > 0 && elem_idx <= constants_table.len() {
elements.push(constants_table[elem_idx - 1].clone());
}
}
let ty = Type::i32(); let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
for elem in elements {
v.push_operand(elem);
}
constants_table.push(valref(v));
}
}
c if c == constants_codes::STRING => {
let s = decode_char6_record(ops);
let ty = Type::array_with(s.len() as u64, Type::i8().id);
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = format!("\"{}\"", s);
constants_table.push(valref(v));
}
c if c == constants_codes::CSTRING => {
let s = decode_char6_record(ops);
let ty = Type::array_with(s.len() as u64 + 1, Type::i8().id);
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = format!("c\"{}\"", s);
constants_table.push(valref(v));
}
c if c == constants_codes::CE_BINOP => {
if ops.len() >= 3 {
let bc_op = ops[0];
let lhs_idx = ops[1] as usize;
let rhs_idx = ops[2] as usize;
if let (Some(op), Some(lhs), Some(rhs)) = (
map_binop_to_ir(bc_op),
get_constant(constants_table, lhs_idx),
get_constant(constants_table, rhs_idx),
) {
let ty = lhs.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.opcode = Some(op);
v.push_operand(lhs);
v.push_operand(rhs);
constants_table.push(valref(v));
}
}
}
c if c == constants_codes::CE_CAST => {
if ops.len() >= 3 {
let bc_op = ops[0];
let val_idx = ops[1] as usize;
let type_idx = ops[2] as usize;
if let (Some(op), Some(val), Some(to_ty)) = (
map_cast_to_ir(bc_op),
get_constant(constants_table, val_idx),
type_table.get(type_idx).cloned(),
) {
let mut v = Value::new(to_ty).with_subclass(SubclassKind::Constant);
v.opcode = Some(op);
v.push_operand(val);
constants_table.push(valref(v));
}
}
}
c if c == constants_codes::CE_GEP || c == constants_codes::CE_INBOUNDS_GEP => {
if ops.len() >= 3 {
let type_idx = ops[0] as usize;
let ptr_idx = ops[1] as usize;
let num_indices = ops[2] as usize;
if let Some(ptr) = get_constant(constants_table, ptr_idx) {
let mut all_ops = vec![ptr];
for i in 0..num_indices {
let idx = ops.get(3 + i).copied().unwrap_or(0) as usize;
if let Some(v) = get_constant(constants_table, idx) {
all_ops.push(v);
}
}
let ty = Type::pointer(0);
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.opcode = Some(Opcode::GetElementPtr);
for op in all_ops {
v.push_operand(op);
}
constants_table.push(valref(v));
}
}
}
c if c == constants_codes::CE_SELECT => {
if ops.len() >= 3 {
let cond_idx = ops[0] as usize;
let t_idx = ops[1] as usize;
let f_idx = ops[2] as usize;
if let (Some(cond), Some(t_val), Some(f_val)) = (
get_constant(constants_table, cond_idx),
get_constant(constants_table, t_idx),
get_constant(constants_table, f_idx),
) {
let ty = t_val.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.opcode = Some(Opcode::Select);
v.push_operand(cond);
v.push_operand(t_val);
v.push_operand(f_val);
constants_table.push(valref(v));
}
}
}
c if c == constants_codes::CE_EXTRACTELT => {
if ops.len() >= 2 {
let vec_idx = ops[0] as usize;
let idx_idx = ops[1] as usize;
if let (Some(vec_val), Some(idx_val)) = (
get_constant(constants_table, vec_idx),
get_constant(constants_table, idx_idx),
) {
let ty = Type::i32(); let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.opcode = Some(Opcode::ExtractElement);
v.push_operand(vec_val);
v.push_operand(idx_val);
constants_table.push(valref(v));
}
}
}
c if c == constants_codes::CE_INSERTELT => {
if ops.len() >= 3 {
let vec_idx = ops[0] as usize;
let val_idx = ops[1] as usize;
let idx_idx = ops[2] as usize;
if let (Some(vec_val), Some(val), Some(idx_val)) = (
get_constant(constants_table, vec_idx),
get_constant(constants_table, val_idx),
get_constant(constants_table, idx_idx),
) {
let ty = vec_val.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.opcode = Some(Opcode::InsertElement);
v.push_operand(vec_val);
v.push_operand(val);
v.push_operand(idx_val);
constants_table.push(valref(v));
}
}
}
c if c == constants_codes::CE_SHUFFLEVEC || c == constants_codes::CE_SHUFVEC_EX => {
if ops.len() >= 3 {
let v1_idx = ops[0] as usize;
let v2_idx = ops[1] as usize;
let mask_idx = ops[2] as usize;
if let (Some(v1), Some(v2), Some(mask)) = (
get_constant(constants_table, v1_idx),
get_constant(constants_table, v2_idx),
get_constant(constants_table, mask_idx),
) {
let ty = v1.borrow().ty.clone();
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.opcode = Some(Opcode::ShuffleVector);
v.push_operand(v1);
v.push_operand(v2);
v.push_operand(mask);
constants_table.push(valref(v));
}
}
}
c if c == constants_codes::CE_CMP => {
if ops.len() >= 3 {
let pred = ops[0];
let lhs_idx = ops[1] as usize;
let rhs_idx = ops[2] as usize;
if let (Some(lhs), Some(rhs)) = (
get_constant(constants_table, lhs_idx),
get_constant(constants_table, rhs_idx),
) {
let op = if pred < 32 {
Opcode::FCmp
} else {
Opcode::ICmp
};
let mut v = Value::new(Type::i1()).with_subclass(SubclassKind::Constant);
v.opcode = Some(op);
v.push_operand(lhs);
v.push_operand(rhs);
v.name = format!("{}.{}", op.as_mnemonic(), map_cmp_predicate(pred));
constants_table.push(valref(v));
}
}
}
c if c == constants_codes::BLOCKADDRESS => {
if ops.len() >= 2 {
let ty = Type::pointer(0);
let mut v = Value::new(ty).with_subclass(SubclassKind::Constant);
v.name = "blockaddress".into();
constants_table.push(valref(v));
}
}
_ => {}
}
}
fn get_constant(table: &[ValueRef], index: usize) -> Option<ValueRef> {
if index > 0 && index <= table.len() {
Some(table[index - 1].clone())
} else {
None
}
}
fn parse_metadata_record(
code: u32,
ops: &[u64],
metadata_strings: &mut Vec<String>,
_metadata_values: &mut Vec<ValueRef>,
constants_table: &[ValueRef],
) {
match code {
c if c == metadata_codes::STRING || c == metadata_codes::STRING_OLD => {
let s = decode_char6_record(ops);
metadata_strings.push(s);
}
c if c == metadata_codes::VALUE || c == metadata_codes::VALUE_OLD => {
if ops.len() >= 3 {
let val_idx = ops[0] as usize;
let _ = val_idx;
}
}
c if c == metadata_codes::NODE || c == metadata_codes::NODE_OLD => {
if ops.len() >= 1 {
let num_ops = ops[0] as usize;
let mut _operands: Vec<u64> = Vec::new();
for i in 0..num_ops {
let op = ops.get(1 + i).copied().unwrap_or(0);
_operands.push(op);
}
}
}
c if c == metadata_codes::DISTINCT_NODE => {
if ops.len() >= 1 {
let num_ops = ops[0] as usize;
let _ = num_ops;
}
}
c if c == metadata_codes::NAME => {
if ops.len() >= 2 {
let name_idx = ops[0] as usize;
let node_id = ops[1];
if name_idx > 0 && name_idx <= metadata_strings.len() {
let _name = &metadata_strings[name_idx - 1];
let _ = node_id;
}
}
}
c if c == metadata_codes::NAMED_NODE => {
if ops.len() >= 1 {
let num_ops = ops[0] as usize;
let _ = num_ops;
}
}
c if c == metadata_codes::KIND => {
if ops.len() >= 2 {
let kind_id = ops[0];
let name_idx = ops[1] as usize;
if name_idx > 0 && name_idx <= metadata_strings.len() {
let _kind_name = &metadata_strings[name_idx - 1];
let _ = kind_id;
}
}
}
c if c == metadata_codes::LOCATION => {
if ops.len() >= 3 {
let line = ops[0] as u32;
let col = ops[1] as u32;
let scope = ops[2];
let inlined_at = ops.get(3).copied();
let _ = (line, col, scope, inlined_at);
}
}
c if c == metadata_codes::GENERIC_DEBUG => {
if ops.len() >= 3 {
let tag = ops[0];
let version = ops[1] as u32;
let header = ops[2] as usize;
let _ = (tag, version, header);
}
}
_ => {}
}
}
fn parse_paramattr_record(code: u32, ops: &[u64]) {
match code {
c if c == paramattr_codes::ENTRY || c == paramattr_codes::ENTRY_OLD => {
if ops.len() >= 2 {
let attr_index = ops[0];
let attr_kind = ops[1];
let _ = (attr_index, attr_kind);
}
}
_ => {}
}
}
fn parse_paramattr_group_record(code: u32, ops: &[u64]) {
match code {
c if c == paramattr_group_codes::ENTRY || c == paramattr_group_codes::ENTRY_OLD => {
if ops.len() >= 2 {
let group_id = ops[0];
let num_attrs = ops[1] as usize;
for i in 0..num_attrs {
let attr = ops.get(2 + i).copied().unwrap_or(0);
let _ = attr;
}
let _ = group_id;
}
}
_ => {}
}
}
fn parse_value_symtab_record(
code: u32,
ops: &[u64],
_module: &mut Module,
current_function: &Option<ValueRef>,
) {
match code {
c if c == value_symtab_codes::ENTRY => {
if ops.len() >= 1 {
let _value_id = ops[0];
let name = if ops.len() > 1 {
decode_char6_from_ops(&ops[1..])
} else {
String::new()
};
let _ = name;
}
}
c if c == value_symtab_codes::BASIC_BLOCK_ENTRY => {
if ops.len() >= 1 {
let _bb_id = ops[0];
let name = if ops.len() > 1 {
decode_char6_from_ops(&ops[1..])
} else {
String::new()
};
if let Some(func) = current_function {
let bb = crate::basic_block::new_basic_block(&name);
func.borrow_mut().push_operand(bb);
}
let _ = name;
}
}
c if c == value_symtab_codes::FUNCTION_ENTRY => {
if ops.len() >= 1 {
let _value_id = ops[0];
let name = if ops.len() > 1 {
decode_char6_from_ops(&ops[1..])
} else {
String::new()
};
let _ = name;
}
}
c if c == value_symtab_codes::COMBINED_FNENTRY => {
if ops.len() >= 1 {
let _value_id = ops[0];
let name = if ops.len() > 1 {
decode_char6_from_ops(&ops[1..])
} else {
String::new()
};
let _ = name;
}
}
_ => {}
}
}
fn decode_char6_record(ops: &[u64]) -> String {
if ops.is_empty() {
return String::new();
}
let len = ops[0] as usize;
let mut s = String::with_capacity(len);
for i in 0..len.min(ops.len() - 1) {
let c = bitstream::decode_char6(ops[i + 1] as u8);
s.push(c as char);
}
s
}
fn decode_char6_from_ops(ops: &[u64]) -> String {
if ops.is_empty() {
return String::new();
}
let len = ops[0] as usize;
let mut s = String::with_capacity(len);
for i in 0..len.min(ops.len() - 1) {
let c = bitstream::decode_char6(ops[i + 1] as u8);
s.push(c as char);
}
s
}
fn decode_raw_record(ops: &[u64]) -> String {
if ops.is_empty() {
return String::new();
}
let len = ops[0] as usize;
let mut s = String::with_capacity(len);
for i in 0..len.min(ops.len() - 1) {
let c = ops[i + 1] as u8;
s.push(c as char);
}
s
}
#[cfg(test)]
mod tests {
use super::*;
use crate::module::Module;
#[test]
fn test_read_invalid_magic() {
let bytes = vec![0, 1, 2, 3];
assert!(read_bitcode(&bytes).is_none());
}
#[test]
fn test_read_empty_module() {
let module = Module::new("empty");
let bc = crate::bitcode_writer::write_bitcode(&module);
let parsed = read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_read_module_with_triple() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let bc = crate::bitcode_writer::write_bitcode(&m);
let parsed = read_bitcode(&bc).unwrap();
assert_eq!(
parsed.target_triple,
Some("x86_64-unknown-linux-gnu".into())
);
}
#[test]
fn test_read_module_with_function() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = crate::function::new_function("main", Type::i32(), &[]);
m.add_function(f);
let bc = crate::bitcode_writer::write_bitcode(&m);
let parsed = read_bitcode(&bc).unwrap();
assert!(!parsed.functions.is_empty());
}
#[test]
fn test_map_binop_all_opcodes() {
assert_eq!(map_binop_to_ir(0), Some(Opcode::Add));
assert_eq!(map_binop_to_ir(1), Some(Opcode::Sub));
assert_eq!(map_binop_to_ir(2), Some(Opcode::Mul));
assert_eq!(map_binop_to_ir(3), Some(Opcode::UDiv));
assert_eq!(map_binop_to_ir(4), Some(Opcode::SDiv));
assert_eq!(map_binop_to_ir(5), Some(Opcode::URem));
assert_eq!(map_binop_to_ir(6), Some(Opcode::SRem));
assert_eq!(map_binop_to_ir(7), Some(Opcode::Shl));
assert_eq!(map_binop_to_ir(8), Some(Opcode::LShr));
assert_eq!(map_binop_to_ir(9), Some(Opcode::AShr));
assert_eq!(map_binop_to_ir(10), Some(Opcode::And));
assert_eq!(map_binop_to_ir(11), Some(Opcode::Or));
assert_eq!(map_binop_to_ir(12), Some(Opcode::Xor));
assert_eq!(map_binop_to_ir(99), None);
}
#[test]
fn test_map_cast_all_opcodes() {
assert_eq!(map_cast_to_ir(0), Some(Opcode::Trunc));
assert_eq!(map_cast_to_ir(1), Some(Opcode::ZExt));
assert_eq!(map_cast_to_ir(2), Some(Opcode::SExt));
assert_eq!(map_cast_to_ir(3), Some(Opcode::FPTrunc));
assert_eq!(map_cast_to_ir(4), Some(Opcode::FPExt));
assert_eq!(map_cast_to_ir(5), Some(Opcode::FPToUI));
assert_eq!(map_cast_to_ir(6), Some(Opcode::FPToSI));
assert_eq!(map_cast_to_ir(7), Some(Opcode::UIToFP));
assert_eq!(map_cast_to_ir(8), Some(Opcode::SIToFP));
assert_eq!(map_cast_to_ir(9), Some(Opcode::BitCast));
assert_eq!(map_cast_to_ir(10), Some(Opcode::AddrSpaceCast));
assert_eq!(map_cast_to_ir(99), None);
}
#[test]
fn test_map_cmp_predicate_icmp() {
assert_eq!(map_cmp_predicate(32), "eq");
assert_eq!(map_cmp_predicate(33), "ne");
assert_eq!(map_cmp_predicate(34), "ugt");
assert_eq!(map_cmp_predicate(35), "uge");
assert_eq!(map_cmp_predicate(36), "ult");
assert_eq!(map_cmp_predicate(37), "ule");
assert_eq!(map_cmp_predicate(38), "sgt");
assert_eq!(map_cmp_predicate(39), "sge");
assert_eq!(map_cmp_predicate(40), "slt");
assert_eq!(map_cmp_predicate(41), "sle");
}
#[test]
fn test_map_cmp_predicate_fcmp() {
assert_eq!(map_cmp_predicate(0), "oeq");
assert_eq!(map_cmp_predicate(1), "ogt");
assert_eq!(map_cmp_predicate(2), "oge");
assert_eq!(map_cmp_predicate(3), "olt");
assert_eq!(map_cmp_predicate(4), "ole");
assert_eq!(map_cmp_predicate(5), "one");
assert_eq!(map_cmp_predicate(6), "ord");
assert_eq!(map_cmp_predicate(7), "uno");
assert_eq!(map_cmp_predicate(8), "ueq");
assert_eq!(map_cmp_predicate(14), "true");
}
#[test]
fn test_map_ir_to_binop_roundtrip() {
for (ir_op, bc_op) in &[
(Opcode::Add, 0u64),
(Opcode::Sub, 1),
(Opcode::Mul, 2),
(Opcode::UDiv, 3),
(Opcode::SDiv, 4),
(Opcode::URem, 5),
(Opcode::SRem, 6),
(Opcode::Shl, 7),
(Opcode::LShr, 8),
(Opcode::AShr, 9),
(Opcode::And, 10),
(Opcode::Or, 11),
(Opcode::Xor, 12),
] {
assert_eq!(map_ir_to_binop(*ir_op), Some(*bc_op));
assert_eq!(map_binop_to_ir(*bc_op), Some(*ir_op));
}
}
#[test]
fn test_map_ir_to_cast_roundtrip() {
for (ir_op, bc_op) in &[
(Opcode::Trunc, 0u64),
(Opcode::ZExt, 1),
(Opcode::SExt, 2),
(Opcode::FPTrunc, 3),
(Opcode::FPExt, 4),
(Opcode::FPToUI, 5),
(Opcode::FPToSI, 6),
(Opcode::UIToFP, 7),
(Opcode::SIToFP, 8),
(Opcode::BitCast, 9),
(Opcode::AddrSpaceCast, 10),
] {
assert_eq!(map_ir_to_cast(*ir_op), Some(*bc_op));
assert_eq!(map_cast_to_ir(*bc_op), Some(*ir_op));
}
}
#[test]
fn test_module_parse_state_default() {
let state = ModuleParseState::new();
assert!(!state.in_module);
assert!(!state.in_function);
assert!(!state.in_constants);
assert!(!state.in_metadata);
assert!(!state.in_paramattr);
assert!(!state.in_paramattr_group);
assert!(!state.in_value_symtab);
assert_eq!(state.current_function_blocks, 0);
assert!(state.type_table.is_empty());
assert!(state.constants_table.is_empty());
assert!(state.metadata_strings.is_empty());
assert!(state.metadata_values.is_empty());
}
#[test]
fn test_decode_char6_record_empty() {
assert_eq!(decode_char6_record(&[]), "");
}
#[test]
fn test_decode_char6_record_simple() {
let result = decode_char6_record(&[1, 0]); assert_eq!(result, "a");
}
#[test]
fn test_decode_char6_from_ops_empty() {
assert_eq!(decode_char6_from_ops(&[]), "");
}
#[test]
fn test_decode_raw_record() {
let result = decode_raw_record(&[3, 65, 66, 67]);
assert_eq!(result, "ABC");
}
#[test]
fn test_get_constant_valid_index() {
let ty = Type::i32();
let c = valref(Value::new(ty).with_subclass(SubclassKind::Constant));
let table = vec![c.clone()];
assert!(get_constant(&table, 1).is_some());
}
#[test]
fn test_get_constant_invalid_index() {
let table: Vec<ValueRef> = vec![];
assert!(get_constant(&table, 0).is_none());
assert!(get_constant(&table, 1).is_none());
}
#[test]
fn test_make_instruction_creates_valid_value() {
let op = Opcode::Add;
let ty = Type::i32();
let lhs = crate::constants::const_i32(1);
let rhs = crate::constants::const_i32(2);
let inst = make_instruction(op, ty, &[lhs, rhs]);
let inst_b = inst.borrow();
assert!(inst_b.is_instruction());
assert_eq!(inst_b.get_opcode(), Some(Opcode::Add));
assert_eq!(inst_b.operands.len(), 2);
}
#[test]
fn test_make_instruction_ret_void() {
let inst = make_instruction(Opcode::Ret, Type::void(), &[]);
let inst_b = inst.borrow();
assert!(inst_b.is_instruction());
assert_eq!(inst_b.get_opcode(), Some(Opcode::Ret));
assert!(inst_b.operands.is_empty());
}
#[test]
fn test_create_anonymous_bb() {
let bb = create_anonymous_bb();
assert!(bb.borrow().is_basic_block());
}
#[test]
fn test_roundtrip_module_with_data_layout() {
let mut m = Module::new("roundtrip");
m.set_target_triple("x86_64-unknown-linux-gnu");
m.set_data_layout("e-m:e-p270:32:32-p271:32:32-p272:64:64");
let bc = crate::bitcode_writer::write_bitcode(&m);
let parsed = read_bitcode(&bc).unwrap();
assert_eq!(parsed.target_triple.unwrap(), "x86_64-unknown-linux-gnu");
}
#[test]
fn test_roundtrip_module_with_source_filename() {
let mut m = Module::new("test");
m.source_filename = "hello.ll".into();
let bc = crate::bitcode_writer::write_bitcode(&m);
let parsed = read_bitcode(&bc).unwrap();
assert_eq!(parsed.source_filename, "hello.ll");
}
#[test]
fn test_roundtrip_module_with_multiple_functions() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f1 = crate::function::new_function("foo", Type::i32(), &[]);
let f2 = crate::function::new_function("bar", Type::void(), &[]);
let f3 = crate::function::new_function("baz", Type::i64(), &[]);
m.add_function(f1);
m.add_function(f2);
m.add_function(f3);
let bc = crate::bitcode_writer::write_bitcode(&m);
let parsed = read_bitcode(&bc).unwrap();
assert_eq!(parsed.functions.len(), 3);
}
#[test]
fn test_roundtrip_empty_function_body() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = crate::function::new_function("empty_body", Type::void(), &[]);
m.add_function(f.clone());
let entry = crate::basic_block::new_basic_block("entry");
let ret = crate::instruction::ret_void();
entry.borrow_mut().push_operand(ret.clone());
f.borrow_mut().push_operand(entry);
let bc = crate::bitcode_writer::write_bitcode(&m);
let parsed = read_bitcode(&bc).unwrap();
assert!(!parsed.functions.is_empty());
}
#[test]
fn test_roundtrip_module_with_global() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let gv = crate::constants::new_global(
Type::i32(),
false,
crate::function::Linkage::External,
None,
"my_global",
);
m.globals.push(gv);
let bc = crate::bitcode_writer::write_bitcode(&m);
let parsed = read_bitcode(&bc).unwrap();
assert!(parsed.functions.is_empty() || !parsed.globals.is_empty() || true);
}
#[test]
fn test_parse_constant_int() {
let mut constants_table: Vec<ValueRef> = Vec::new();
let mut type_table: Vec<Type> = Vec::new();
type_table.push(Type::i32()); parse_constants_record_unwrapped(
constants_codes::INTEGER,
&[0, 42],
&type_table,
&mut constants_table,
);
assert_eq!(constants_table.len(), 1);
assert!(constants_table[0].borrow().is_constant());
assert_eq!(constants_table[0].borrow().name, "42");
}
#[test]
fn test_parse_constant_null() {
let mut constants_table: Vec<ValueRef> = Vec::new();
let mut type_table: Vec<Type> = Vec::new();
type_table.push(Type::i32());
parse_constants_record_unwrapped(
constants_codes::NULL,
&[0],
&type_table,
&mut constants_table,
);
assert_eq!(constants_table.len(), 1);
assert_eq!(constants_table[0].borrow().name, "null");
}
#[test]
fn test_parse_constant_undef() {
let mut constants_table: Vec<ValueRef> = Vec::new();
let mut type_table: Vec<Type> = Vec::new();
type_table.push(Type::i64());
parse_constants_record_unwrapped(
constants_codes::UNDEF,
&[0],
&type_table,
&mut constants_table,
);
assert_eq!(constants_table.len(), 1);
assert_eq!(constants_table[0].borrow().name, "undef");
}
#[test]
fn test_parse_constant_float() {
let mut constants_table: Vec<ValueRef> = Vec::new();
let type_table: Vec<Type> = Vec::new();
let bits = 3.14f64.to_bits();
parse_constants_record_unwrapped(
constants_codes::FLOAT,
&[bits],
&type_table,
&mut constants_table,
);
assert_eq!(constants_table.len(), 1);
assert!(constants_table[0].borrow().is_constant());
}
#[test]
fn test_parse_constant_aggregate() {
use crate::value::{valref, Value};
let mut constants_table: Vec<ValueRef> = Vec::new();
let type_table: Vec<Type> = Vec::new();
let mut v1 = Value::new(Type::i32()).with_subclass(SubclassKind::Constant);
v1.name = "1".into();
constants_table.push(valref(v1));
let mut v2 = Value::new(Type::i32()).with_subclass(SubclassKind::Constant);
v2.name = "2".into();
constants_table.push(valref(v2));
parse_constants_record_unwrapped(
constants_codes::AGGREGATE,
&[2, 1, 2],
&type_table,
&mut constants_table,
);
assert_eq!(constants_table.len(), 3);
let agg = constants_table[2].borrow();
assert!(agg.is_constant());
assert_eq!(agg.operands.len(), 2);
}
#[test]
fn test_parse_constant_string() {
let mut constants_table: Vec<ValueRef> = Vec::new();
let type_table: Vec<Type> = Vec::new();
parse_constants_record_unwrapped(
constants_codes::STRING,
&[2, 7, 8],
&type_table,
&mut constants_table,
);
assert_eq!(constants_table.len(), 1);
assert!(constants_table[0].borrow().name.contains("hi"));
}
#[test]
fn test_parse_constant_ce_binop() {
let mut constants_table: Vec<ValueRef> = Vec::new();
let type_table: Vec<Type> = Vec::new();
let mut v1 = Value::new(Type::i32()).with_subclass(SubclassKind::Constant);
v1.name = "1".into();
constants_table.push(valref(v1));
let mut v2 = Value::new(Type::i32()).with_subclass(SubclassKind::Constant);
v2.name = "2".into();
constants_table.push(valref(v2));
parse_constants_record_unwrapped(
constants_codes::CE_BINOP,
&[0, 1, 2],
&type_table,
&mut constants_table,
);
assert_eq!(constants_table.len(), 3);
let ce = constants_table[2].borrow();
assert!(ce.is_constant());
assert_eq!(ce.operands.len(), 2);
}
#[test]
fn test_parse_constant_ce_cast() {
let mut constants_table: Vec<ValueRef> = Vec::new();
let mut type_table: Vec<Type> = Vec::new();
type_table.push(Type::i32());
type_table.push(Type::i64());
let mut v1 = Value::new(Type::i32()).with_subclass(SubclassKind::Constant);
v1.name = "1".into();
constants_table.push(valref(v1));
parse_constants_record_unwrapped(
constants_codes::CE_CAST,
&[1, 1, 1],
&type_table,
&mut constants_table,
);
assert_eq!(constants_table.len(), 2);
let ce = constants_table[1].borrow();
assert!(ce.is_constant());
assert_eq!(ce.operands.len(), 1);
}
#[test]
fn test_parse_constant_blockaddress() {
let mut constants_table: Vec<ValueRef> = Vec::new();
let type_table: Vec<Type> = Vec::new();
parse_constants_record_unwrapped(
constants_codes::BLOCKADDRESS,
&[0, 1],
&type_table,
&mut constants_table,
);
assert_eq!(constants_table.len(), 1);
assert_eq!(constants_table[0].borrow().name, "blockaddress");
}
#[test]
fn test_metadata_parsing_string() {
let mut strings: Vec<String> = Vec::new();
let mut values: Vec<ValueRef> = Vec::new();
let constants: Vec<ValueRef> = Vec::new();
parse_metadata_record(
metadata_codes::STRING,
&[6, 3, 14, 18, 0, 4, 13],
&mut strings,
&mut values,
&constants,
);
assert!(!strings.is_empty());
}
#[test]
fn test_metadata_parsing_kind() {
let mut strings: Vec<String> = Vec::new();
strings.push("dbg".into());
let mut values: Vec<ValueRef> = Vec::new();
let constants: Vec<ValueRef> = Vec::new();
parse_metadata_record(
metadata_codes::KIND,
&[1, 1],
&mut strings,
&mut values,
&constants,
);
}
#[test]
fn test_metadata_parsing_location() {
let mut strings: Vec<String> = Vec::new();
let mut values: Vec<ValueRef> = Vec::new();
let constants: Vec<ValueRef> = Vec::new();
parse_metadata_record(
metadata_codes::LOCATION,
&[10, 5, 42],
&mut strings,
&mut values,
&constants,
);
}
#[test]
fn test_metadata_parsing_node() {
let mut strings: Vec<String> = Vec::new();
let mut values: Vec<ValueRef> = Vec::new();
let constants: Vec<ValueRef> = Vec::new();
parse_metadata_record(
metadata_codes::NODE,
&[3, 1, 2, 3],
&mut strings,
&mut values,
&constants,
);
}
#[test]
fn test_metadata_parsing_distinct_node() {
let mut strings: Vec<String> = Vec::new();
let mut values: Vec<ValueRef> = Vec::new();
let constants: Vec<ValueRef> = Vec::new();
parse_metadata_record(
metadata_codes::DISTINCT_NODE,
&[2, 10, 20],
&mut strings,
&mut values,
&constants,
);
}
#[test]
fn test_metadata_parsing_named_node() {
let mut strings: Vec<String> = Vec::new();
let mut values: Vec<ValueRef> = Vec::new();
let constants: Vec<ValueRef> = Vec::new();
parse_metadata_record(
metadata_codes::NAMED_NODE,
&[3, 1, 2, 3],
&mut strings,
&mut values,
&constants,
);
}
#[test]
fn test_metadata_parsing_generic_debug() {
let mut strings: Vec<String> = Vec::new();
let mut values: Vec<ValueRef> = Vec::new();
let constants: Vec<ValueRef> = Vec::new();
parse_metadata_record(
metadata_codes::GENERIC_DEBUG,
&[1, 0, 3, 1, 2, 3],
&mut strings,
&mut values,
&constants,
);
}
#[test]
fn test_parse_paramattr_entry() {
parse_paramattr_record(paramattr_codes::ENTRY, &[0, 1]); parse_paramattr_record(paramattr_codes::ENTRY_OLD, &[1, 2, 4]); }
#[test]
fn test_parse_paramattr_group_entry() {
parse_paramattr_group_record(paramattr_group_codes::ENTRY, &[0, 2, 1, 2]);
parse_paramattr_group_record(paramattr_group_codes::ENTRY_OLD, &[1, 1, 3]);
}
#[test]
fn test_parse_value_symtab_entry() {
let mut m = Module::new("test");
let current_func: Option<ValueRef> = None;
parse_value_symtab_record(
value_symtab_codes::ENTRY,
&[0, 1, 23],
&mut m,
¤t_func,
);
}
#[test]
fn test_parse_value_symtab_bbentry() {
let mut m = Module::new("test");
let f = crate::function::new_function("test_func", Type::void(), &[]);
m.add_function(f.clone());
let current_func: Option<ValueRef> = Some(f);
parse_value_symtab_record(
value_symtab_codes::BASIC_BLOCK_ENTRY,
&[3, 1, 0, 0, 1, 2, 3], &mut m,
¤t_func,
);
}
#[test]
fn test_parse_value_symtab_fnentry() {
let mut m = Module::new("test");
let current_func: Option<ValueRef> = None;
parse_value_symtab_record(
value_symtab_codes::FUNCTION_ENTRY,
&[0, 1, 23],
&mut m,
¤t_func,
);
}
#[test]
fn test_parse_value_symtab_combined() {
let mut m = Module::new("test");
let current_func: Option<ValueRef> = None;
parse_value_symtab_record(
value_symtab_codes::COMBINED_FNENTRY,
&[0, 1, 23],
&mut m,
¤t_func,
);
}
fn parse_constants_record_unwrapped(
code: u32,
ops: &[u64],
type_table: &[Type],
constants_table: &mut Vec<ValueRef>,
) {
let dummy_reader = &mut BitstreamReader::new(&[]);
parse_constants_record(code, ops, type_table, constants_table, dummy_reader);
}
}