use llvm_native_core::attributes::{AttributeKind, AttributeList};
use llvm_native_core::bitstream::{
self, block_ids, constants_codes, function_codes, metadata_codes, module_codes,
paramattr_codes, paramattr_group_codes, strtab_codes, type_codes, value_symtab_codes,
BitstreamWriter,
};
use llvm_native_core::module::Module;
use llvm_native_core::opcode::Opcode;
use llvm_native_core::types::{Type, TypeId, TypeKind};
use llvm_native_core::value::{SubclassKind, ValueRef};
use std::collections::HashMap;
struct TypeTable {
id_to_index: HashMap<u64, u64>,
next_index: u64,
types: Vec<Type>,
}
impl TypeTable {
fn new() -> Self {
let mut tt = Self {
id_to_index: HashMap::new(),
next_index: 0,
types: Vec::new(),
};
tt.types.push(Type::void());
tt.types.push(Type::label());
tt.types.push(Type::metadata());
tt.types.push(Type::struct_opaque(String::new())); tt.next_index = 4;
tt
}
fn ensure_type(&mut self, ty: &Type) -> u64 {
let id = ty.id.as_u64();
if let Some(idx) = self.id_to_index.get(&id) {
return *idx;
}
match &ty.kind {
TypeKind::Array {
element_type_id, ..
} => {
let _ = element_type_id;
}
TypeKind::FixedVector {
element_type_id, ..
}
| TypeKind::ScalableVector {
element_type_id, ..
} => {
let _ = element_type_id;
}
TypeKind::Struct {
element_type_ids, ..
} => {
for eid in element_type_ids {
let _ = eid;
}
}
TypeKind::Function {
return_type_id,
param_type_ids,
..
} => {
let _ = return_type_id;
for pid in param_type_ids {
let _ = pid;
}
}
_ => {}
}
let index = self.next_index;
self.id_to_index.insert(id, index);
self.types.push(ty.clone());
self.next_index += 1;
index
}
fn get_index(&self, ty: &Type) -> Option<u64> {
self.id_to_index.get(&ty.id.as_u64()).copied()
}
fn len(&self) -> usize {
self.types.len()
}
}
struct ValueTable {
vid_to_idx: HashMap<u64, u64>,
next_idx: u64,
}
impl ValueTable {
fn new() -> Self {
Self {
vid_to_idx: HashMap::new(),
next_idx: 0,
}
}
fn assign(&mut self, val: &ValueRef) -> u64 {
let vid = val.borrow().vid;
if let Some(idx) = self.vid_to_idx.get(&vid) {
return *idx;
}
let idx = self.next_idx;
self.vid_to_idx.insert(vid, idx);
self.next_idx += 1;
idx
}
fn get(&self, val: &ValueRef) -> Option<u64> {
self.vid_to_idx.get(&val.borrow().vid).copied()
}
}
pub fn write_bitcode(module: &Module) -> Vec<u8> {
let mut writer = BitstreamWriter::new();
let mut type_table = TypeTable::new();
writer.emit_magic();
writer.enter_subblock(block_ids::IDENTIFICATION_BLOCK, 2);
writer.emit_record(bitstream::identification_codes::EPOCH, &[0]);
writer.end_block();
collect_module_types(module, &mut type_table);
writer.enter_subblock(block_ids::MODULE_BLOCK, 3);
writer.emit_record(module_codes::VERSION, &[1]);
if let Some(ref triple) = module.target_triple {
write_string_record(&mut writer, module_codes::TRIPLE, triple);
}
if let Some(ref dl) = module.data_layout {
write_string_record(&mut writer, module_codes::DATALAYOUT, dl);
}
if !module.source_filename.is_empty() {
write_string_record(&mut writer, module_codes::ASM, &module.source_filename);
}
if type_table.len() > 4 {
writer.enter_subblock(block_ids::TYPE_BLOCK, 2);
let num_emit = type_table.len() - 4;
writer.emit_record(type_codes::NUM_ENTRY, &[num_emit as u64]);
emit_type_table(&mut writer, &type_table);
writer.end_block();
}
for gv in &module.globals {
let gv_b = gv.borrow();
let ty_idx = type_table.get_index(&gv_b.ty).unwrap_or(0);
let mut ops = vec![ty_idx]; ops.push(0); ops.push(0); ops.push(0); ops.push(0); ops.push(0); ops.push(0); ops.push(0); ops.push(0); ops.push(0); let name_ops = encode_char6_string_ops(&gv_b.name);
ops.extend(name_ops);
writer.emit_record(module_codes::GLOBAL_VAR, &ops);
}
for func in &module.functions {
let f = func.borrow();
if f.is_function() {
if f.num_operands == 0 {
write_string_record(&mut writer, module_codes::FUNCTION, &f.name);
} else {
write_string_record(&mut writer, module_codes::FUNCTION, &f.name);
writer.enter_subblock(block_ids::FUNCTION_BLOCK, 3);
let num_blocks = f
.operands
.iter()
.filter(|op| op.borrow().is_basic_block())
.count();
writer.emit_record(function_codes::DECLAREBLOCKS, &[num_blocks as u64]);
emit_value_symtab(&mut writer, &f.operands);
let mut value_table = ValueTable::new();
for op in &f.operands {
let bb = op.borrow();
if bb.is_basic_block() {
value_table.assign(op);
for inst_val in &bb.operands {
let inst = inst_val.borrow();
if inst.is_instruction() {
emit_instruction(
&mut writer,
inst_val,
&type_table,
&mut value_table,
);
}
}
}
}
writer.end_block(); }
}
}
writer.end_block();
writer.finish()
}
fn collect_module_types(module: &Module, tt: &mut TypeTable) {
for func in &module.functions {
let f = func.borrow();
collect_type(&f.ty, tt);
for op in &f.operands {
let bb = op.borrow();
if bb.is_basic_block() {
for inst_val in &bb.operands {
let inst = inst_val.borrow();
collect_type(&inst.ty, tt);
for operand in &inst.operands {
let op_val = operand.borrow();
collect_type(&op_val.ty, tt);
}
}
}
}
}
for gv in &module.globals {
collect_type(&gv.borrow().ty, tt);
}
}
fn collect_type(ty: &Type, tt: &mut TypeTable) {
tt.ensure_type(ty);
}
fn emit_type_table(writer: &mut BitstreamWriter, tt: &TypeTable) {
for i in 4..tt.types.len() {
let ty = &tt.types[i];
emit_single_type(writer, ty, tt, i as u64);
}
}
fn emit_single_type(writer: &mut BitstreamWriter, ty: &Type, tt: &TypeTable, _our_idx: u64) {
match &ty.kind {
TypeKind::Void => {} TypeKind::Half => {
writer.emit_record(type_codes::HALF, &[]);
}
TypeKind::Float => {
writer.emit_record(type_codes::FLOAT, &[]);
}
TypeKind::Double => {
writer.emit_record(type_codes::DOUBLE, &[]);
}
TypeKind::Integer { bits } => {
writer.emit_record(type_codes::INTEGER, &[*bits as u64]);
}
TypeKind::Pointer { addr_space } => {
writer.emit_record(type_codes::POINTER, &[*addr_space as u64]);
}
TypeKind::Label => {
writer.emit_record(type_codes::LABEL, &[]);
}
TypeKind::Metadata => {
writer.emit_record(type_codes::METADATA, &[]);
}
TypeKind::Array {
len,
element_type_id,
} => {
let elem_idx = tt.get_index_by_id(&element_type_id).unwrap_or(0);
writer.emit_record(type_codes::ARRAY, &[elem_idx, *len]);
}
TypeKind::FixedVector {
len,
element_type_id,
} => {
let elem_idx = tt.get_index_by_id(&element_type_id).unwrap_or(0);
writer.emit_record(type_codes::VECTOR, &[*len as u64, elem_idx]);
}
TypeKind::ScalableVector {
min_elems,
element_type_id,
} => {
let elem_idx = tt.get_index_by_id(&element_type_id).unwrap_or(0);
writer.emit_record(type_codes::VECTOR, &[*min_elems as u64, elem_idx]);
}
TypeKind::Struct {
name,
is_packed,
element_type_ids,
is_opaque,
} => {
if *is_opaque {
writer.emit_record(type_codes::OPAQUE, &[]);
} else if let Some(n) = name {
let is_packed_val: u64 = if *is_packed { 1 } else { 0 };
let name_ops = encode_char6_string_ops(n);
let mut ops = vec![is_packed_val, element_type_ids.len() as u64];
ops.extend(name_ops);
for eid in element_type_ids {
let idx = tt.get_index_by_id(eid).unwrap_or(0);
ops.push(idx);
}
writer.emit_record(type_codes::STRUCT_NAMED, &ops);
} else {
let is_packed_val: u64 = if *is_packed { 1 } else { 0 };
let mut ops = vec![is_packed_val, element_type_ids.len() as u64];
for eid in element_type_ids {
let idx = tt.get_index_by_id(eid).unwrap_or(0);
ops.push(idx);
}
writer.emit_record(type_codes::STRUCT_ANON, &ops);
}
}
TypeKind::Function {
return_type_id,
param_type_ids,
is_vararg,
} => {
let ret_idx = tt.get_index_by_id(return_type_id).unwrap_or(0);
let vararg_val: u64 = if *is_vararg { 1 } else { 0 };
let mut ops = vec![ret_idx, param_type_ids.len() as u64, vararg_val];
for pid in param_type_ids {
let idx = tt.get_index_by_id(pid).unwrap_or(0);
ops.push(idx);
}
writer.emit_record(type_codes::FUNCTION, &ops);
}
_ => {
writer.emit_record(type_codes::OPAQUE, &[]);
}
}
}
fn emit_instruction(
writer: &mut BitstreamWriter,
inst_val: &ValueRef,
type_table: &TypeTable,
value_table: &mut ValueTable,
) {
let inst = inst_val.borrow();
let op = inst.get_opcode();
match op {
Some(Opcode::Ret) => {
if inst.operands.is_empty() {
writer.emit_record(function_codes::INST_RET, &[]);
} else {
let val_idx = value_table.assign(&inst.operands[0]);
writer.emit_record(function_codes::INST_RET, &[val_idx]);
}
value_table.assign(inst_val);
}
Some(Opcode::Br) => {
if inst.operands.len() == 1 {
let dest_idx = value_table.assign(&inst.operands[0]);
writer.emit_record(function_codes::BR, &[0, dest_idx]);
} else if inst.operands.len() >= 3 {
let cond_idx = value_table.assign(&inst.operands[0]);
let true_idx = value_table.assign(&inst.operands[1]);
let false_idx = value_table.assign(&inst.operands[2]);
writer.emit_record(function_codes::BR, &[cond_idx, true_idx, false_idx]);
}
value_table.assign(inst_val);
}
Some(Opcode::Switch) => {
if inst.operands.len() >= 2 {
let type_idx = type_table
.get_index(&inst.operands[0].borrow().ty)
.unwrap_or(0);
let val_idx = value_table.assign(&inst.operands[0]);
let default_idx = value_table.assign(&inst.operands[1]);
let mut ops = vec![type_idx, val_idx, default_idx];
let mut i = 2;
while i + 1 < inst.operands.len() {
let case_val_idx = value_table.assign(&inst.operands[i]);
let case_dest_idx = value_table.assign(&inst.operands[i + 1]);
ops.push(case_val_idx);
ops.push(case_dest_idx);
i += 2;
}
writer.emit_record(function_codes::SWITCH, &ops);
}
value_table.assign(inst_val);
}
Some(Opcode::Unreachable) => {
writer.emit_record(function_codes::UNREACHABLE, &[]);
value_table.assign(inst_val);
}
Some(opcode) if opcode.is_binary_op() => {
if let Some(bc_op) = map_ir_to_bc_binop(opcode) {
if inst.operands.len() >= 2 {
let lhs_idx = value_table.assign(&inst.operands[0]);
let rhs_idx = value_table.assign(&inst.operands[1]);
writer.emit_record(function_codes::BINOP, &[bc_op, lhs_idx, rhs_idx]);
}
}
value_table.assign(inst_val);
}
Some(opcode) if opcode.is_cast() => {
if let Some(bc_op) = map_ir_to_bc_cast(opcode) {
if inst.operands.len() >= 1 {
let val_idx = value_table.assign(&inst.operands[0]);
let to_type_idx = type_table.get_index(&inst.ty).unwrap_or(0);
writer.emit_record(function_codes::CAST, &[bc_op, val_idx, to_type_idx]);
}
}
value_table.assign(inst_val);
}
Some(Opcode::Alloca) => {
let type_idx = type_table.get_index(&inst.ty).unwrap_or(0);
writer.emit_record(function_codes::INST_ALLOCA, &[type_idx, 0]);
value_table.assign(inst_val);
}
Some(Opcode::Load) => {
if inst.operands.len() >= 1 {
let type_idx = type_table.get_index(&inst.ty).unwrap_or(0);
let ptr_idx = value_table.assign(&inst.operands[0]);
writer.emit_record(function_codes::INST_LOAD, &[type_idx, ptr_idx]);
}
value_table.assign(inst_val);
}
Some(Opcode::Store) => {
if inst.operands.len() >= 2 {
let val_idx = value_table.assign(&inst.operands[0]);
let ptr_idx = value_table.assign(&inst.operands[1]);
writer.emit_record(function_codes::INST_STORE, &[val_idx, ptr_idx]);
}
value_table.assign(inst_val);
}
Some(Opcode::GetElementPtr) => {
if inst.operands.len() >= 1 {
let type_idx = type_table
.get_index(&inst.operands[0].borrow().ty)
.unwrap_or(0);
let ptr_idx = value_table.assign(&inst.operands[0]);
let num_indices = inst.operands.len() - 1;
let mut ops = vec![type_idx, ptr_idx, num_indices as u64];
for i in 1..inst.operands.len() {
let idx = value_table.assign(&inst.operands[i]);
ops.push(idx);
}
writer.emit_record(function_codes::INST_GEP, &ops);
}
value_table.assign(inst_val);
}
Some(Opcode::Call) => {
if inst.operands.len() >= 1 {
let callee_idx = value_table.assign(&inst.operands[0]);
let num_args = inst.operands.len() - 1;
let ret_type_idx = type_table.get_index(&inst.ty).unwrap_or(0);
let mut ops = vec![callee_idx, num_args as u64, ret_type_idx];
for i in 1..inst.operands.len() {
let arg_idx = value_table.assign(&inst.operands[i]);
ops.push(arg_idx);
}
writer.emit_record(function_codes::INST_CALL, &ops);
}
value_table.assign(inst_val);
}
Some(Opcode::ICmp) | Some(Opcode::FCmp) => {
if inst.operands.len() >= 2 {
let type_idx = type_table.get_index(&inst.ty).unwrap_or(0);
let pred = infer_cmp_predicate(&inst.name, op == Some(Opcode::FCmp));
let lhs_idx = value_table.assign(&inst.operands[0]);
let rhs_idx = value_table.assign(&inst.operands[1]);
let code = if op == Some(Opcode::FCmp) {
function_codes::CMP2
} else {
function_codes::CMP
};
writer.emit_record(code, &[type_idx, pred, lhs_idx, rhs_idx]);
}
value_table.assign(inst_val);
}
Some(Opcode::Phi) => {
let type_idx = type_table.get_index(&inst.ty).unwrap_or(0);
let num_incoming = inst.operands.len() / 2;
let mut ops = vec![type_idx, num_incoming as u64];
let mut i = 0;
while i + 1 < inst.operands.len() {
let val_idx = value_table.assign(&inst.operands[i]);
let bb_idx = value_table.assign(&inst.operands[i + 1]);
ops.push(val_idx);
ops.push(bb_idx);
i += 2;
}
writer.emit_record(function_codes::INST_PHI, &ops);
value_table.assign(inst_val);
}
Some(Opcode::Select) => {
if inst.operands.len() >= 3 {
let cond_idx = value_table.assign(&inst.operands[0]);
let t_idx = value_table.assign(&inst.operands[1]);
let f_idx = value_table.assign(&inst.operands[2]);
writer.emit_record(function_codes::SELECT, &[cond_idx, t_idx, f_idx, 0]);
}
value_table.assign(inst_val);
}
Some(Opcode::ExtractElement) => {
if inst.operands.len() >= 2 {
let vec_idx = value_table.assign(&inst.operands[0]);
let idx_idx = value_table.assign(&inst.operands[1]);
writer.emit_record(function_codes::EXTRACTELT, &[vec_idx, idx_idx]);
}
value_table.assign(inst_val);
}
Some(Opcode::InsertElement) => {
if inst.operands.len() >= 3 {
let vec_idx = value_table.assign(&inst.operands[0]);
let val_idx = value_table.assign(&inst.operands[1]);
let idx_idx = value_table.assign(&inst.operands[2]);
writer.emit_record(function_codes::INSERTELT, &[vec_idx, val_idx, idx_idx]);
}
value_table.assign(inst_val);
}
Some(Opcode::ShuffleVector) => {
if inst.operands.len() >= 3 {
let v1_idx = value_table.assign(&inst.operands[0]);
let v2_idx = value_table.assign(&inst.operands[1]);
let mask_idx = value_table.assign(&inst.operands[2]);
writer.emit_record(function_codes::SHUFFLEVEC, &[v1_idx, v2_idx, mask_idx]);
}
value_table.assign(inst_val);
}
Some(Opcode::ExtractValue) => {
if inst.operands.len() >= 1 {
let agg_idx = value_table.assign(&inst.operands[0]);
let num_idxs = inst.operands.len() - 1;
let mut ops = vec![agg_idx, num_idxs as u64];
for i in 1..inst.operands.len() {
let idx = value_table.assign(&inst.operands[i]);
ops.push(idx);
}
writer.emit_record(function_codes::EXTRACTVAL, &ops);
}
value_table.assign(inst_val);
}
Some(Opcode::InsertValue) => {
if inst.operands.len() >= 2 {
let agg_idx = value_table.assign(&inst.operands[0]);
let val_idx = value_table.assign(&inst.operands[1]);
let num_idxs = inst.operands.len() - 2;
let mut ops = vec![agg_idx, val_idx, num_idxs as u64];
for i in 2..inst.operands.len() {
let idx = value_table.assign(&inst.operands[i]);
ops.push(idx);
}
writer.emit_record(function_codes::INSERTVAL, &ops);
}
value_table.assign(inst_val);
}
Some(Opcode::AtomicRMW) => {
if inst.operands.len() >= 2 {
let ptr_idx = value_table.assign(&inst.operands[0]);
let val_idx = value_table.assign(&inst.operands[1]);
writer.emit_record(function_codes::INST_ATOMICRMW, &[ptr_idx, val_idx, 0, 0]);
}
value_table.assign(inst_val);
}
Some(Opcode::Fence) => {
writer.emit_record(function_codes::INST_FENCE, &[0, 0]);
value_table.assign(inst_val);
}
Some(Opcode::LandingPad) => {
let type_idx = type_table.get_index(&inst.ty).unwrap_or(0);
writer.emit_record(function_codes::INST_LANDINGPAD_NEW, &[type_idx, 0]);
value_table.assign(inst_val);
}
Some(Opcode::Resume) => {
if inst.operands.len() >= 1 {
let val_idx = value_table.assign(&inst.operands[0]);
writer.emit_record(function_codes::RESUME, &[val_idx]);
}
value_table.assign(inst_val);
}
Some(Opcode::VAArg) => {
if inst.operands.len() >= 1 {
let list_idx = value_table.assign(&inst.operands[0]);
let type_idx = type_table.get_index(&inst.ty).unwrap_or(0);
writer.emit_record(function_codes::VAARG, &[list_idx, type_idx]);
}
value_table.assign(inst_val);
}
Some(Opcode::IndirectBr) => {
if inst.operands.len() >= 1 {
let addr_idx = value_table.assign(&inst.operands[0]);
writer.emit_record(function_codes::INDIRECTBR, &[addr_idx]);
}
value_table.assign(inst_val);
}
Some(Opcode::Invoke) => {
if inst.operands.len() >= 1 && inst.successors.len() >= 2 {
let callee_idx = value_table.assign(&inst.operands[0]);
let normal_idx = value_table.assign(&inst.successors[0]);
let unwind_idx = value_table.assign(&inst.successors[1]);
let num_args = inst.operands.len() - 1;
let ret_type_idx = type_table.get_index(&inst.ty).unwrap_or(0);
let mut ops = vec![
callee_idx,
normal_idx,
unwind_idx,
num_args as u64,
ret_type_idx,
];
for i in 1..inst.operands.len() {
let arg_idx = value_table.assign(&inst.operands[i]);
ops.push(arg_idx);
}
writer.emit_record(function_codes::INVOKE, &ops);
}
value_table.assign(inst_val);
}
_ => {
value_table.assign(inst_val);
}
}
}
fn map_ir_to_bc_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),
Opcode::FAdd => Some(0),
Opcode::FSub => Some(1),
Opcode::FMul => Some(2),
Opcode::FDiv => Some(3),
Opcode::FRem => Some(5),
_ => None,
}
}
fn map_ir_to_bc_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),
Opcode::PtrToInt => Some(11),
Opcode::IntToPtr => Some(12),
_ => None,
}
}
fn infer_cmp_predicate(name: &str, is_fcmp: bool) -> u64 {
if is_fcmp {
match name {
n if n.contains("false") => 0,
n if n.contains("oeq") => 1,
n if n.contains("ogt") => 2,
n if n.contains("oge") => 3,
n if n.contains("olt") => 4,
n if n.contains("ole") => 5,
n if n.contains("one") => 6,
n if n.contains("ord") => 7,
n if n.contains("uno") => 8,
n if n.contains("ueq") => 9,
n if n.contains("ugt") => 10,
n if n.contains("uge") => 11,
n if n.contains("ult") => 12,
n if n.contains("ule") => 13,
n if n.contains("une") => 14,
n if n.contains("true") => 15,
_ => 1, }
} else {
match name {
n if n.contains("eq") => 32,
n if n.contains("ne") => 33,
n if n.contains("ugt") => 34,
n if n.contains("uge") => 35,
n if n.contains("ult") => 36,
n if n.contains("ule") => 37,
n if n.contains("sgt") => 38,
n if n.contains("sge") => 39,
n if n.contains("slt") => 40,
n if n.contains("sle") => 41,
_ => 32, }
}
}
fn emit_value_symtab(writer: &mut BitstreamWriter, operands: &[ValueRef]) {
writer.enter_subblock(block_ids::VALUE_SYMTAB_BLOCK, 2);
for (i, op) in operands.iter().enumerate() {
let bb = op.borrow();
if bb.is_basic_block() && !bb.name.is_empty() {
writer.emit_record(
value_symtab_codes::BASIC_BLOCK_ENTRY,
&[i as u64, bb.name.len() as u64],
);
writer.emit_char6_string(&bb.name);
} else if bb.is_instruction() && !bb.name.is_empty() {
writer.emit_record(value_symtab_codes::ENTRY, &[i as u64, bb.name.len() as u64]);
writer.emit_char6_string(&bb.name);
}
}
writer.end_block();
}
fn write_string_record(writer: &mut BitstreamWriter, code: u32, s: &str) {
let len = s.len() as u64;
let mut ops = vec![len];
for c in s.as_bytes() {
ops.push(*c as u64);
}
writer.emit_record(code, &ops);
}
fn encode_char6_string_ops(s: &str) -> Vec<u64> {
let mut ops = vec![s.len() as u64];
for c in s.as_bytes() {
ops.push(bitstream::encode_char6(*c) as u64);
}
ops
}
impl TypeTable {
fn get_index_by_id(&self, id: &TypeId) -> Option<u64> {
self.id_to_index.get(&id.as_u64()).copied()
}
}
#[derive(Debug, Clone)]
pub struct BitcodeWriterConfig {
pub version: u32,
pub emit_metadata: bool,
pub emit_attributes: bool,
pub emit_value_symtab: bool,
pub emit_string_table: bool,
pub emit_constants: bool,
pub use_abbrevs: bool,
pub abbrev_id_width: u32,
pub emit_module_asm: bool,
pub emit_globals: bool,
pub emit_aliases: bool,
pub emit_gc: bool,
pub emit_section_names: bool,
pub emit_deplibs: bool,
}
impl Default for BitcodeWriterConfig {
fn default() -> Self {
Self {
version: 1,
emit_metadata: true,
emit_attributes: true,
emit_value_symtab: true,
emit_string_table: false,
emit_constants: true,
use_abbrevs: true,
abbrev_id_width: 2,
emit_module_asm: true,
emit_globals: true,
emit_aliases: true,
emit_gc: false,
emit_section_names: false,
emit_deplibs: false,
}
}
}
impl BitcodeWriterConfig {
pub fn minimal() -> Self {
Self {
emit_metadata: false,
emit_attributes: false,
emit_value_symtab: false,
emit_string_table: false,
emit_constants: false,
use_abbrevs: false,
emit_module_asm: false,
emit_globals: true,
emit_aliases: false,
emit_gc: false,
emit_section_names: false,
emit_deplibs: false,
..Default::default()
}
}
pub fn verbose() -> Self {
Self {
emit_metadata: true,
emit_attributes: true,
emit_value_symtab: true,
emit_string_table: true,
emit_constants: true,
use_abbrevs: true,
emit_module_asm: true,
emit_globals: true,
emit_aliases: true,
emit_gc: true,
emit_section_names: true,
emit_deplibs: true,
..Default::default()
}
}
}
#[allow(dead_code)]
pub fn emit_module_block(
writer: &mut BitstreamWriter,
module: &Module,
config: &BitcodeWriterConfig,
type_table: &TypeTable,
value_table: &mut ValueTable,
) {
writer.enter_subblock(block_ids::MODULE_BLOCK, 3);
writer.emit_record(module_codes::VERSION, &[config.version as u64]);
if let Some(ref triple) = module.target_triple {
write_string_record(writer, module_codes::TRIPLE, triple);
}
if let Some(ref dl) = module.data_layout {
write_string_record(writer, module_codes::DATALAYOUT, dl);
}
if !module.source_filename.is_empty() {
write_string_record(writer, module_codes::ASM, &module.source_filename);
}
if config.emit_section_names {
for func in &module.functions {
let f = func.borrow();
if f.is_function() && !f.name.is_empty() {
write_string_record(writer, module_codes::SECTION_NAME, &f.name);
}
}
}
if config.emit_gc {
for func in &module.functions {
let f = func.borrow();
if f.is_function() && f.subclass_data != 0 {
write_string_record(
writer,
module_codes::GC_NAME,
&format!("gc_{}", f.subclass_data),
);
}
}
}
if config.emit_deplibs {
}
emit_type_block_full(writer, config, type_table);
if config.emit_attributes {
emit_attribute_blocks(writer, module, config);
}
if config.emit_constants {
emit_constants_block(writer, module, type_table, value_table, config);
}
if config.emit_globals {
emit_global_var_records(writer, module, type_table, config);
}
emit_function_records(writer, module, type_table, config);
if config.emit_aliases {
emit_alias_records(writer, module, type_table, config);
}
if config.emit_metadata {
emit_metadata_block(writer, module, config);
}
if config.emit_string_table {
emit_string_table_block(writer, module, config);
}
writer.end_block(); }
#[allow(dead_code)]
pub fn emit_type_block_full(
writer: &mut BitstreamWriter,
_config: &BitcodeWriterConfig,
type_table: &TypeTable,
) {
if type_table.len() <= 4 {
return;
}
writer.enter_subblock(block_ids::TYPE_BLOCK, 2);
let num_emit = type_table.len() - 4;
writer.emit_record(type_codes::NUM_ENTRY, &[num_emit as u64]);
emit_type_table(writer, type_table);
writer.end_block();
}
#[allow(dead_code)]
pub fn emit_attribute_blocks(
writer: &mut BitstreamWriter,
module: &Module,
_config: &BitcodeWriterConfig,
) {
if module.attr_groups.is_empty() {
return;
}
writer.enter_subblock(block_ids::PARAMATTR_GROUP_BLOCK, 2);
let mut ids: Vec<u32> = module.attr_groups.keys().copied().collect();
ids.sort();
for &gid in &ids {
if let Some(_attrs) = module.attr_groups.get(&gid) {
let ops = serialize_attribute_group(_attrs);
writer.emit_record(paramattr_group_codes::ENTRY, &ops);
}
}
writer.end_block();
writer.enter_subblock(block_ids::PARAMATTR_BLOCK, 2);
for &gid in &ids {
if let Some(_attrs) = module.attr_groups.get(&gid) {
let ops = vec![gid as u64];
writer.emit_record(paramattr_codes::ENTRY, &ops);
}
}
writer.end_block();
}
#[allow(dead_code)]
pub fn emit_constants_block(
writer: &mut BitstreamWriter,
module: &Module,
type_table: &TypeTable,
_value_table: &mut ValueTable,
_config: &BitcodeWriterConfig,
) {
writer.enter_subblock(block_ids::CONSTANTS_BLOCK, 3);
for gv in &module.globals {
let gv_b = gv.borrow();
let ty_idx = type_table.get_index(&gv_b.ty).unwrap_or(0);
writer.emit_record(constants_codes::SETTYPE, &[ty_idx]);
if let Some(init) = gv_b.operands.first() {
serialize_constant(writer, init, type_table);
}
}
for func in &module.functions {
let f = func.borrow();
if f.is_function() && f.num_operands > 0 {
for op in &f.operands {
let bb = op.borrow();
if bb.is_basic_block() {
for inst_val in &bb.operands {
let inst = inst_val.borrow();
if inst.is_instruction() {
for operand in &inst.operands {
let op_val = operand.borrow();
if op_val.subclass.is_constant() {
let ty_idx = type_table.get_index(&op_val.ty).unwrap_or(0);
writer.emit_record(constants_codes::SETTYPE, &[ty_idx]);
serialize_constant(writer, operand, type_table);
}
}
}
}
}
}
}
}
writer.end_block();
}
#[allow(dead_code)]
pub fn emit_global_var_records(
writer: &mut BitstreamWriter,
module: &Module,
type_table: &TypeTable,
_config: &BitcodeWriterConfig,
) {
for gv in &module.globals {
let gv_b = gv.borrow();
let ty_idx = type_table.get_index(&gv_b.ty).unwrap_or(0);
let mut ops = vec![ty_idx];
ops.push(0); ops.push(0); ops.push(0); ops.push(0); ops.push(0); ops.push(0); ops.push(0); ops.push(0); ops.push(0); let name_ops = encode_char6_string_ops(&gv_b.name);
ops.extend(name_ops);
writer.emit_record(module_codes::GLOBAL_VAR, &ops);
}
}
#[allow(dead_code)]
pub fn emit_function_records(
writer: &mut BitstreamWriter,
module: &Module,
type_table: &TypeTable,
config: &BitcodeWriterConfig,
) {
for func in &module.functions {
let f = func.borrow();
if !f.is_function() {
continue;
}
if f.num_operands == 0 {
write_string_record(writer, module_codes::FUNCTION, &f.name);
} else {
write_string_record(writer, module_codes::FUNCTION, &f.name);
writer.enter_subblock(block_ids::FUNCTION_BLOCK, 3);
let num_blocks = f
.operands
.iter()
.filter(|op| op.borrow().is_basic_block())
.count();
writer.emit_record(function_codes::DECLAREBLOCKS, &[num_blocks as u64]);
if config.emit_value_symtab {
emit_value_symtab(writer, &f.operands);
}
let mut value_table = ValueTable::new();
for op in &f.operands {
let bb = op.borrow();
if bb.is_basic_block() {
value_table.assign(op);
for inst_val in &bb.operands {
let inst = inst_val.borrow();
if inst.is_instruction() {
serialize_instruction(writer, inst_val, type_table, &mut value_table);
}
}
}
}
writer.end_block(); }
}
}
#[allow(dead_code)]
pub fn emit_alias_records(
writer: &mut BitstreamWriter,
module: &Module,
type_table: &TypeTable,
_config: &BitcodeWriterConfig,
) {
for alias in &module.aliases {
let a = alias.borrow();
let ty_idx = type_table.get_index(&a.ty).unwrap_or(0);
let mut ops = vec![ty_idx];
ops.push(0); ops.push(0); ops.push(0); let name_ops = encode_char6_string_ops(&a.name);
ops.extend(name_ops);
writer.emit_record(module_codes::ALIAS, &ops);
}
}
#[allow(dead_code)]
pub fn emit_metadata_block(
writer: &mut BitstreamWriter,
module: &Module,
_config: &BitcodeWriterConfig,
) {
if module.named_metadata.is_empty() && module.flags.is_empty() {
return;
}
writer.enter_subblock(block_ids::METADATA_BLOCK, 3);
for (name, node_ids) in &module.named_metadata {
let name_ops = encode_char6_string_ops(name);
let mut ops = name_ops;
ops.push(node_ids.len() as u64);
for &nid in node_ids {
ops.push(nid as u64);
}
writer.emit_record(metadata_codes::NAMED_NODE, &ops);
}
writer.end_block();
}
#[allow(dead_code)]
pub fn emit_string_table_block(
writer: &mut BitstreamWriter,
module: &Module,
_config: &BitcodeWriterConfig,
) {
writer.enter_subblock(block_ids::STRTAB_BLOCK, 2);
let mut strings: Vec<String> = Vec::new();
if !module.source_filename.is_empty() {
strings.push(module.source_filename.clone());
}
if let Some(ref t) = module.target_triple {
strings.push(t.clone());
}
if let Some(ref dl) = module.data_layout {
strings.push(dl.clone());
}
for func in &module.functions {
let f = func.borrow();
if f.is_function() && !f.name.is_empty() {
strings.push(f.name.clone());
}
}
for gv in &module.globals {
let g = gv.borrow();
if !g.name.is_empty() {
strings.push(g.name.clone());
}
}
let mut blob = Vec::new();
for s in &strings {
blob.extend_from_slice(s.as_bytes());
blob.push(0);
}
writer.emit_record(strtab_codes::BLOB, &[]);
let _ = blob;
writer.end_block();
}
#[allow(dead_code)]
pub fn serialize_type(writer: &mut BitstreamWriter, ty: &Type, type_table: &TypeTable) {
match &ty.kind {
TypeKind::Void => {
writer.emit_record(type_codes::VOID, &[]);
}
TypeKind::Half => {
writer.emit_record(type_codes::HALF, &[]);
}
TypeKind::BFloat => {
writer.emit_record(type_codes::OPAQUE, &[]);
}
TypeKind::Float => {
writer.emit_record(type_codes::FLOAT, &[]);
}
TypeKind::Double => {
writer.emit_record(type_codes::DOUBLE, &[]);
}
TypeKind::FP128 => {
writer.emit_record(type_codes::OPAQUE, &[]);
}
TypeKind::X86FP80 => {
writer.emit_record(type_codes::OPAQUE, &[]);
}
TypeKind::PPCFP128 => {
writer.emit_record(type_codes::OPAQUE, &[]);
}
TypeKind::Label => {
writer.emit_record(type_codes::LABEL, &[]);
}
TypeKind::Metadata => {
writer.emit_record(type_codes::METADATA, &[]);
}
TypeKind::X86AMX => {
writer.emit_record(type_codes::OPAQUE, &[]);
}
TypeKind::X86MMX => {
writer.emit_record(type_codes::X86_MMX, &[]);
}
TypeKind::Token => {
writer.emit_record(type_codes::OPAQUE, &[]);
}
TypeKind::Integer { bits } => {
writer.emit_record(type_codes::INTEGER, &[*bits as u64]);
}
TypeKind::Pointer { addr_space } => {
writer.emit_record(type_codes::POINTER, &[*addr_space as u64]);
}
TypeKind::Array {
len,
element_type_id,
} => {
let elem_idx = type_table.get_index_by_id(element_type_id).unwrap_or(0);
writer.emit_record(type_codes::ARRAY, &[elem_idx, *len]);
}
TypeKind::Struct {
name,
is_packed,
is_opaque,
element_type_ids,
} => {
if *is_opaque {
writer.emit_record(type_codes::OPAQUE, &[]);
} else if let Some(n) = name {
let is_packed_val: u64 = if *is_packed { 1 } else { 0 };
let name_ops = encode_char6_string_ops(n);
let mut ops = vec![is_packed_val, element_type_ids.len() as u64];
ops.extend(name_ops);
for eid in element_type_ids {
let idx = type_table.get_index_by_id(eid).unwrap_or(0);
ops.push(idx);
}
writer.emit_record(type_codes::STRUCT_NAMED, &ops);
} else {
let is_packed_val: u64 = if *is_packed { 1 } else { 0 };
let mut ops = vec![is_packed_val, element_type_ids.len() as u64];
for eid in element_type_ids {
let idx = type_table.get_index_by_id(eid).unwrap_or(0);
ops.push(idx);
}
writer.emit_record(type_codes::STRUCT_ANON, &ops);
}
}
TypeKind::FixedVector {
len,
element_type_id,
} => {
let elem_idx = type_table.get_index_by_id(element_type_id).unwrap_or(0);
writer.emit_record(type_codes::VECTOR, &[*len as u64, elem_idx]);
}
TypeKind::ScalableVector {
min_elems,
element_type_id,
} => {
let elem_idx = type_table.get_index_by_id(element_type_id).unwrap_or(0);
writer.emit_record(type_codes::VECTOR, &[*min_elems as u64, elem_idx]);
}
TypeKind::Function {
return_type_id,
param_type_ids,
is_vararg,
} => {
let ret_idx = type_table.get_index_by_id(return_type_id).unwrap_or(0);
let vararg_val: u64 = if *is_vararg { 1 } else { 0 };
let mut ops = vec![ret_idx, param_type_ids.len() as u64, vararg_val];
for pid in param_type_ids {
let idx = type_table.get_index_by_id(pid).unwrap_or(0);
ops.push(idx);
}
writer.emit_record(type_codes::FUNCTION, &ops);
}
}
}
#[allow(dead_code)]
pub fn serialize_constant(writer: &mut BitstreamWriter, val: &ValueRef, type_table: &TypeTable) {
let v = val.borrow();
match v.subclass {
SubclassKind::ConstantInt => {
let bits = v.ty.integer_bit_width();
if bits <= 64 {
let int_val: i64 = if let Ok(n) = v.name.parse::<i64>() {
n
} else {
0
};
let unsigned_val = int_val as u64;
writer.emit_record(constants_codes::INTEGER, &[unsigned_val]);
} else {
writer.emit_record(constants_codes::WIDE_INTEGER, &[bits as u64, 0]);
}
}
SubclassKind::ConstantFP => {
let ty_str = if v.ty.is_float() { "float" } else { "double" };
let val: f64 = if let Ok(n) = v.name.parse::<f64>() {
n
} else {
0.0
};
let bits = val.to_bits();
writer.emit_record(
constants_codes::FLOAT,
&[bits, v.ty.integer_bit_width() as u64],
);
let _ = ty_str;
}
SubclassKind::ConstantAggregate => {
let mut ops = vec![v.operands.len() as u64];
for operand in &v.operands {
let op_val = operand.borrow();
ops.push(op_val.vid);
}
writer.emit_record(constants_codes::AGGREGATE, &ops);
}
SubclassKind::ConstantData => {
if v.name == "zeroinitializer" {
writer.emit_record(constants_codes::NULL, &[]);
} else {
let string_ops = v.name.bytes().map(|b| b as u64).collect::<Vec<u64>>();
writer.emit_record(constants_codes::CSTRING, &string_ops);
}
}
SubclassKind::Constant => {
if v.name == "null" || v.name == "zeroinitializer" {
writer.emit_record(constants_codes::NULL, &[]);
} else if v.name == "undef" {
writer.emit_record(constants_codes::UNDEF, &[]);
} else if v.name == "poison" {
writer.emit_record(constants_codes::UNDEF, &[]);
} else if v.ty.is_integer() {
if let Ok(n) = v.name.parse::<i64>() {
writer.emit_record(constants_codes::INTEGER, &[n as u64]);
}
} else {
writer.emit_record(constants_codes::NULL, &[]);
}
}
_ => {
writer.emit_record(constants_codes::NULL, &[]);
}
}
}
#[allow(dead_code)]
pub fn serialize_constant_gep(
writer: &mut BitstreamWriter,
ops: &[ValueRef],
type_table: &TypeTable,
inbounds: bool,
) {
let code = if inbounds {
constants_codes::CE_INBOUNDS_GEP
} else {
constants_codes::CE_GEP
};
let mut record_ops = vec![ops.len() as u64];
for op in ops {
let ty_idx = type_table.get_index(&op.borrow().ty).unwrap_or(0);
record_ops.push(ty_idx);
record_ops.push(op.borrow().vid);
}
writer.emit_record(code, &record_ops);
}
#[allow(dead_code)]
pub fn serialize_constant_bitcast(
writer: &mut BitstreamWriter,
_from_ty: &Type,
_to_ty: &Type,
_op: &ValueRef,
type_table: &TypeTable,
) {
let from_idx = type_table.get_index(_from_ty).unwrap_or(0);
let to_idx = type_table.get_index(_to_ty).unwrap_or(0);
let op_vid = _op.borrow().vid;
writer.emit_record(constants_codes::CE_CAST, &[from_idx, op_vid, to_idx]);
}
#[allow(dead_code)]
pub fn serialize_constant_cmp(
writer: &mut BitstreamWriter,
pred: u64,
lhs: &ValueRef,
rhs: &ValueRef,
type_table: &TypeTable,
) {
let lhs_ty_idx = type_table.get_index(&lhs.borrow().ty).unwrap_or(0);
writer.emit_record(
constants_codes::CE_CMP,
&[pred, lhs_ty_idx, lhs.borrow().vid, rhs.borrow().vid],
);
}
#[allow(dead_code)]
pub fn serialize_constant_select(
writer: &mut BitstreamWriter,
cond: &ValueRef,
true_val: &ValueRef,
false_val: &ValueRef,
type_table: &TypeTable,
) {
writer.emit_record(
constants_codes::CE_SELECT,
&[
cond.borrow().vid,
true_val.borrow().vid,
false_val.borrow().vid,
],
);
let _ = type_table;
}
#[allow(dead_code)]
pub fn serialize_instruction(
writer: &mut BitstreamWriter,
inst_val: &ValueRef,
type_table: &TypeTable,
value_table: &mut ValueTable,
) {
let i = inst_val.borrow();
value_table.assign(inst_val);
let vid = value_table.get(inst_val).unwrap_or(0);
if let Some(opcode) = i.get_opcode() {
match opcode {
Opcode::Ret => {
if i.operands.is_empty() {
writer.emit_record(function_codes::RET, &[]);
} else {
let op_id = value_id_of(&i.operands[0], value_table);
let ty_idx = type_table
.get_index(&i.operands[0].borrow().ty)
.unwrap_or(0);
writer.emit_record(function_codes::INST_RET, &[op_id, ty_idx]);
}
}
Opcode::Br => {
if i.operands.len() == 1 {
let bb_id = value_id_of(&i.operands[0], value_table);
writer.emit_record(function_codes::BR, &[1, bb_id]);
} else if i.operands.len() >= 3 {
let cond_id = value_id_of(&i.operands[0], value_table);
let true_id = value_id_of(&i.operands[1], value_table);
let false_id = value_id_of(&i.operands[2], value_table);
writer.emit_record(function_codes::BR, &[3, cond_id, true_id, false_id]);
}
}
Opcode::Switch => {
let mut ops = Vec::new();
for operand in &i.operands {
ops.push(value_id_of(operand, value_table));
}
writer.emit_record(function_codes::SWITCH, &ops);
}
Opcode::IndirectBr => {
let addr_id = value_id_of(&i.operands[0], value_table);
let mut ops = vec![addr_id];
for bb in &i.operands[1..] {
ops.push(value_id_of(bb, value_table));
}
writer.emit_record(function_codes::INDIRECTBR, &ops);
}
Opcode::Invoke => {
let callee_id = value_id_of(&i.operands[0], value_table);
let mut ops = vec![callee_id];
for op in &i.operands[1..] {
ops.push(value_id_of(op, value_table));
}
writer.emit_record(function_codes::INVOKE, &ops);
}
Opcode::CallBr => {
let callee_id = value_id_of(&i.operands[0], value_table);
let mut ops = vec![callee_id];
for op in &i.operands[1..] {
ops.push(value_id_of(op, value_table));
}
writer.emit_record(function_codes::INST_CALLBR, &ops);
}
Opcode::Resume => {
let val_id = value_id_of(&i.operands[0], value_table);
writer.emit_record(function_codes::RESUME, &[val_id]);
}
Opcode::CatchSwitch => {
let mut ops = vec![0u64]; for succ in &i.successors {
ops.push(value_id_of(succ, value_table));
}
writer.emit_record(function_codes::INST_LANDINGPAD, &ops);
}
Opcode::CatchRet => {
if !i.operands.is_empty() && !i.successors.is_empty() {
let pad_id = value_id_of(&i.operands[0], value_table);
let succ_id = value_id_of(&i.successors[0], value_table);
writer.emit_record(function_codes::INST_LANDINGPAD, &[pad_id, succ_id]);
}
}
Opcode::CleanupRet => {
if !i.operands.is_empty() {
let pad_id = value_id_of(&i.operands[0], value_table);
writer.emit_record(function_codes::INST_LANDINGPAD, &[pad_id]);
}
}
Opcode::Unreachable => {
writer.emit_record(function_codes::UNREACHABLE, &[]);
}
Opcode::Add
| Opcode::FAdd
| Opcode::Sub
| Opcode::FSub
| Opcode::Mul
| Opcode::FMul
| Opcode::UDiv
| Opcode::SDiv
| Opcode::FDiv
| Opcode::URem
| Opcode::SRem
| Opcode::FRem
| Opcode::Shl
| Opcode::LShr
| Opcode::AShr
| Opcode::And
| Opcode::Or
| Opcode::Xor => {
if i.operands.len() >= 2 {
let binop_code = map_ir_to_bc_binop(opcode).unwrap_or(0);
let lhs_id = value_id_of(&i.operands[0], value_table);
let rhs_id = value_id_of(&i.operands[1], value_table);
let ty_idx = type_table
.get_index(&i.operands[0].borrow().ty)
.unwrap_or(0);
writer
.emit_record(function_codes::BINOP, &[binop_code, lhs_id, rhs_id, ty_idx]);
}
}
Opcode::Alloca => {
let ty_idx = type_table.get_index(&i.ty).unwrap_or(0);
let mut ops = vec![ty_idx];
if i.operands.len() >= 1 {
ops.push(value_id_of(&i.operands[0], value_table));
}
ops.push(i.subclass_data as u64); writer.emit_record(function_codes::INST_ALLOCA, &ops);
}
Opcode::Load => {
let ptr_id = value_id_of(&i.operands[0], value_table);
let ty_idx = type_table.get_index(&i.ty).unwrap_or(0);
let align = i.subclass_data as u64;
writer.emit_record(function_codes::INST_LOAD, &[ptr_id, ty_idx, align]);
}
Opcode::Store => {
if i.operands.len() >= 2 {
let val_id = value_id_of(&i.operands[0], value_table);
let ptr_id = value_id_of(&i.operands[1], value_table);
let ty_idx = type_table
.get_index(&i.operands[0].borrow().ty)
.unwrap_or(0);
let align = i.subclass_data as u64;
writer
.emit_record(function_codes::INST_STORE, &[val_id, ptr_id, ty_idx, align]);
}
}
Opcode::GetElementPtr => {
let ty_idx = type_table
.get_index(&i.operands[0].borrow().ty)
.unwrap_or(0);
let mut ops = vec![ty_idx];
for op in &i.operands {
ops.push(value_id_of(op, value_table));
}
let code = if i.subclass_data & 1 != 0 {
function_codes::INST_INBOUNDS_GEP
} else {
function_codes::INST_GEP
};
writer.emit_record(code, &ops);
}
Opcode::Fence => {
let ordering = fence_ordering_val(i.subclass_data);
writer.emit_record(function_codes::INST_FENCE, &[ordering]);
}
Opcode::CmpXchg => {
if i.operands.len() >= 3 {
let ptr_id = value_id_of(&i.operands[0], value_table);
let old_id = value_id_of(&i.operands[1], value_table);
let new_id = value_id_of(&i.operands[2], value_table);
writer.emit_record(function_codes::INST_CMPXCHG, &[ptr_id, old_id, new_id]);
}
}
Opcode::AtomicRMW => {
if i.operands.len() >= 2 {
let binop = atomicrmw_binop_val(i.subclass_data);
let ptr_id = value_id_of(&i.operands[0], value_table);
let val_id = value_id_of(&i.operands[1], value_table);
writer.emit_record(function_codes::INST_ATOMICRMW, &[binop, ptr_id, val_id]);
}
}
Opcode::Trunc
| Opcode::ZExt
| Opcode::SExt
| Opcode::FPTrunc
| Opcode::FPExt
| Opcode::FPToUI
| Opcode::FPToSI
| Opcode::UIToFP
| Opcode::SIToFP
| Opcode::PtrToInt
| Opcode::IntToPtr
| Opcode::BitCast
| Opcode::AddrSpaceCast => {
if !i.operands.is_empty() {
let cast_code = map_ir_to_bc_cast(opcode).unwrap_or(0);
let val_id = value_id_of(&i.operands[0], value_table);
let from_ty_idx = type_table
.get_index(&i.operands[0].borrow().ty)
.unwrap_or(0);
let to_ty_idx = type_table.get_index(&i.ty).unwrap_or(0);
writer.emit_record(
function_codes::CAST,
&[cast_code, val_id, from_ty_idx, to_ty_idx],
);
}
}
Opcode::ICmp => {
if i.operands.len() >= 2 {
let pred = icmp_pred_val(i.subclass_data);
let lhs_id = value_id_of(&i.operands[0], value_table);
let rhs_id = value_id_of(&i.operands[1], value_table);
let ty_idx = type_table
.get_index(&i.operands[0].borrow().ty)
.unwrap_or(0);
writer.emit_record(function_codes::CMP2, &[pred, lhs_id, rhs_id, ty_idx]);
}
}
Opcode::FCmp => {
if i.operands.len() >= 2 {
let pred = fcmp_pred_val(i.subclass_data);
let lhs_id = value_id_of(&i.operands[0], value_table);
let rhs_id = value_id_of(&i.operands[1], value_table);
let ty_idx = type_table
.get_index(&i.operands[0].borrow().ty)
.unwrap_or(0);
writer.emit_record(function_codes::CMP2, &[pred, lhs_id, rhs_id, ty_idx]);
}
}
Opcode::Phi => {
let ty_idx = type_table.get_index(&i.ty).unwrap_or(0);
let mut ops = vec![ty_idx];
for chunk in i.operands.chunks(2) {
if chunk.len() == 2 {
ops.push(value_id_of(&chunk[0], value_table));
ops.push(value_id_of(&chunk[1], value_table));
}
}
writer.emit_record(function_codes::INST_PHI, &ops);
}
Opcode::Call => {
let callee_id = value_id_of(&i.operands[0], value_table);
let mut ops = vec![callee_id];
for op in &i.operands[1..] {
ops.push(value_id_of(op, value_table));
}
writer.emit_record(function_codes::INST_CALL, &ops);
}
Opcode::Select => {
if i.operands.len() >= 3 {
let cond_id = value_id_of(&i.operands[0], value_table);
let true_id = value_id_of(&i.operands[1], value_table);
let false_id = value_id_of(&i.operands[2], value_table);
writer.emit_record(function_codes::VSELECT, &[cond_id, true_id, false_id]);
}
}
Opcode::Freeze => {
if !i.operands.is_empty() {
let val_id = value_id_of(&i.operands[0], value_table);
writer.emit_record(function_codes::INST_LANDINGPAD, &[val_id]);
}
}
Opcode::VAArg => {
if !i.operands.is_empty() {
let va_id = value_id_of(&i.operands[0], value_table);
let ty_idx = type_table.get_index(&i.ty).unwrap_or(0);
writer.emit_record(function_codes::VAARG, &[va_id, ty_idx]);
}
}
Opcode::ExtractElement => {
if i.operands.len() >= 2 {
let vec_id = value_id_of(&i.operands[0], value_table);
let idx_id = value_id_of(&i.operands[1], value_table);
writer.emit_record(function_codes::EXTRACTELT, &[vec_id, idx_id]);
}
}
Opcode::InsertElement => {
if i.operands.len() >= 3 {
let vec_id = value_id_of(&i.operands[0], value_table);
let elem_id = value_id_of(&i.operands[1], value_table);
let idx_id = value_id_of(&i.operands[2], value_table);
writer.emit_record(function_codes::INSERTELT, &[vec_id, elem_id, idx_id]);
}
}
Opcode::ShuffleVector => {
if i.operands.len() >= 2 {
let a_id = value_id_of(&i.operands[0], value_table);
let b_id = value_id_of(&i.operands[1], value_table);
let mask_id = if i.operands.len() >= 3 {
value_id_of(&i.operands[2], value_table)
} else {
0
};
writer.emit_record(function_codes::SHUFFLEVEC, &[a_id, b_id, mask_id]);
}
}
Opcode::ExtractValue => {
if !i.operands.is_empty() {
let agg_id = value_id_of(&i.operands[0], value_table);
let mut ops = vec![agg_id];
for idx in &i.operands[1..] {
if let Ok(n) = idx.borrow().name.parse::<u64>() {
ops.push(n);
}
}
writer.emit_record(function_codes::EXTRACTVAL, &ops);
}
}
Opcode::InsertValue => {
if i.operands.len() >= 2 {
let agg_id = value_id_of(&i.operands[0], value_table);
let elem_id = value_id_of(&i.operands[1], value_table);
let mut ops = vec![agg_id, elem_id];
for idx in &i.operands[2..] {
if let Ok(n) = idx.borrow().name.parse::<u64>() {
ops.push(n);
}
}
writer.emit_record(function_codes::INSERTVAL, &ops);
}
}
Opcode::LandingPad => {
let ty_idx = type_table.get_index(&i.ty).unwrap_or(0);
writer.emit_record(function_codes::INST_LANDINGPAD_NEW, &[ty_idx]);
}
Opcode::CatchPad | Opcode::CleanupPad => {
writer.emit_record(function_codes::INST_LANDINGPAD, &[]);
}
_ => {
writer.emit_record(function_codes::UNREACHABLE, &[]);
}
}
}
let _ = vid;
}
fn value_id_of(val: &ValueRef, value_table: &ValueTable) -> u64 {
value_table.get(val).unwrap_or(0)
}
fn fence_ordering_val(data: u32) -> u64 {
match data {
0 => 0, 1 => 1, 2 => 2, 3 => 3, _ => 3,
}
}
fn atomicrmw_binop_val(data: u32) -> u64 {
data as u64
}
fn icmp_pred_val(data: u32) -> u64 {
(data + 32) as u64
}
fn fcmp_pred_val(data: u32) -> u64 {
data as u64
}
#[allow(dead_code)]
pub fn assign_value_id(val: &ValueRef, value_table: &mut ValueTable) -> u64 {
value_table.assign(val)
}
#[allow(dead_code)]
pub fn get_value_id(val: &ValueRef, value_table: &ValueTable) -> Option<u64> {
value_table.get(val)
}
#[allow(dead_code)]
pub fn enumerate_module_values(module: &Module, value_table: &mut ValueTable) {
for gv in &module.globals {
value_table.assign(gv);
}
for func in &module.functions {
let f = func.borrow();
if f.is_function() {
value_table.assign(func);
if f.num_operands > 0 {
for op in &f.operands {
let bb = op.borrow();
if bb.is_basic_block() {
value_table.assign(op);
for inst_val in &bb.operands {
let inst = inst_val.borrow();
if inst.is_instruction() {
value_table.assign(inst_val);
}
}
}
}
}
}
}
for alias in &module.aliases {
value_table.assign(alias);
}
for ifunc in &module.ifuncs {
value_table.assign(ifunc);
}
}
#[allow(dead_code)]
pub fn serialize_metadata(writer: &mut BitstreamWriter, md_id: u32, _kind: u32, operands: &[u64]) {
let mut ops = vec![md_id as u64, operands.len() as u64];
ops.extend_from_slice(operands);
writer.emit_record(metadata_codes::VALUE, &ops);
}
#[allow(dead_code)]
pub fn serialize_md_node(writer: &mut BitstreamWriter, node_id: u32, operands: &[u64]) {
let mut ops = vec![node_id as u64, operands.len() as u64];
ops.extend_from_slice(operands);
writer.emit_record(metadata_codes::NODE, &ops);
}
#[allow(dead_code)]
pub fn serialize_named_metadata(writer: &mut BitstreamWriter, name: &str, node_ids: &[u32]) {
let name_ops = encode_char6_string_ops(name);
let mut ops = name_ops;
ops.push(node_ids.len() as u64);
for &nid in node_ids {
ops.push(nid as u64);
}
writer.emit_record(metadata_codes::NAMED_NODE, &ops);
}
#[allow(dead_code)]
pub fn serialize_attribute_list(attrs: &AttributeList) -> Vec<u64> {
let mut ops = Vec::new();
ops.push(attrs.fn_attrs.len() as u64);
for attr in &attrs.fn_attrs {
ops.push(attribute_encoded_val(attr));
}
ops.push(attrs.ret_attrs.len() as u64);
for _attr in &attrs.ret_attrs {
ops.push(0); }
ops.push(attrs.param_attrs.len() as u64);
for param_attr_list in &attrs.param_attrs {
ops.push(param_attr_list.len() as u64);
for _attr in param_attr_list {
ops.push(0); }
}
ops
}
#[allow(dead_code)]
pub fn serialize_attribute_group(attrs: &AttributeList) -> Vec<u64> {
let mut ops = Vec::new();
ops.push(attrs.fn_attrs.len() as u64);
for attr in &attrs.fn_attrs {
ops.push(attribute_encoded_val(attr));
}
ops
}
fn attribute_encoded_val(attr: &AttributeKind) -> u64 {
match attr {
AttributeKind::NoInline => 1,
AttributeKind::AlwaysInline => 2,
AttributeKind::OptimizeNone => 3,
AttributeKind::OptimizeForSize => 4,
AttributeKind::OptimizeForSpeed => 5,
AttributeKind::ReadNone => 6,
AttributeKind::ReadOnly => 7,
AttributeKind::WriteOnly => 8,
AttributeKind::ArgMemOnly => 9,
AttributeKind::InaccessibleMemOnly => 10,
AttributeKind::InaccessibleMemOrArgMemOnly => 11,
AttributeKind::NoUnwind => 12,
AttributeKind::NoReturn => 13,
AttributeKind::WillReturn => 14,
AttributeKind::Ssp => 15,
AttributeKind::SspReq => 16,
AttributeKind::SspStrong => 17,
AttributeKind::SanitizeAddress => 18,
AttributeKind::SanitizeThread => 19,
AttributeKind::SanitizeMemory => 20,
AttributeKind::SanitizeHwAddress => 21,
AttributeKind::NoSanitize => 22,
AttributeKind::SanitizeCoverage => 23,
AttributeKind::ShadowCallStack => 24,
AttributeKind::SpeculativeLoadHardening => 25,
AttributeKind::NoCfCheck => 26,
AttributeKind::InlineHint => 27,
AttributeKind::NoMerge => 28,
AttributeKind::ReturnsTwice => 29,
AttributeKind::JumpTable => 30,
AttributeKind::MustProgress => 31,
AttributeKind::Convergent => 32,
AttributeKind::NoDivergenceSource => 33,
AttributeKind::NoProfile => 34,
AttributeKind::SkipProfile => 35,
AttributeKind::OptDebug => 36,
AttributeKind::OptForFuzzing => 37,
AttributeKind::AllocPtr => 38,
AttributeKind::NoBuiltin => 39,
AttributeKind::NoCallback => 40,
AttributeKind::NonLazyBind => 41,
AttributeKind::Naked => 42,
AttributeKind::NoImplicitFloat => 43,
AttributeKind::NoRedZone => 44,
AttributeKind::UWTable => 45,
AttributeKind::SafeStack => 46,
AttributeKind::SanitizeMemTag => 47,
AttributeKind::DisableSanitizerInstrumentation => 48,
AttributeKind::Hotpatch => 49,
AttributeKind::FnRetThunkExtern => 50,
AttributeKind::StrictFP => 51,
AttributeKind::Speculatable => 52,
AttributeKind::NoDuplicate => 53,
AttributeKind::NoRecurse => 54,
AttributeKind::NoSync => 55,
AttributeKind::NoFree => 56,
AttributeKind::Cold => 57,
AttributeKind::Hot => 58,
AttributeKind::Minsize => 59,
AttributeKind::NullPointerIsValid => 60,
AttributeKind::UseSampleProfile => 61,
AttributeKind::AllocSize(_, _) => 100,
AttributeKind::AlignStack(_) => 101,
AttributeKind::FramePointer(_) => 102,
AttributeKind::NoFPClass(_) => 103,
AttributeKind::VScaleRange(_, _) => 104,
AttributeKind::StringAttribute(_, _) => 105,
}
}
#[derive(Debug, Clone)]
pub struct InstructionRecord {
pub opcode: Opcode,
pub value_id: u64,
pub type_index: u64,
pub operands: Vec<u64>,
pub flags: u64,
pub metadata: Vec<(u32, u32)>,
}
impl InstructionRecord {
pub fn new(opcode: Opcode, value_id: u64, type_index: u64) -> Self {
Self {
opcode,
value_id,
type_index,
operands: Vec::new(),
flags: 0,
metadata: Vec::new(),
}
}
pub fn with_operands(mut self, operands: Vec<u64>) -> Self {
self.operands = operands;
self
}
pub fn with_flags(mut self, flags: u64) -> Self {
self.flags = flags;
self
}
pub fn with_metadata(mut self, metadata: Vec<(u32, u32)>) -> Self {
self.metadata = metadata;
self
}
}
#[derive(Debug, Clone)]
pub struct BasicBlockRecord {
pub value_id: u64,
pub name: String,
pub instructions: Vec<InstructionRecord>,
}
impl BasicBlockRecord {
pub fn new(value_id: u64, name: &str) -> Self {
Self {
value_id,
name: name.to_string(),
instructions: Vec::new(),
}
}
pub fn add_instruction(&mut self, inst: InstructionRecord) {
self.instructions.push(inst);
}
}
#[derive(Debug, Clone)]
pub struct FunctionRecord {
pub value_id: u64,
pub name: String,
pub type_index: u64,
pub linkage: u32,
pub blocks: Vec<BasicBlockRecord>,
pub symtab_entries: Vec<(u64, String)>,
}
impl FunctionRecord {
pub fn new(value_id: u64, name: &str, type_index: u64) -> Self {
Self {
value_id,
name: name.to_string(),
type_index,
linkage: 0,
blocks: Vec::new(),
symtab_entries: Vec::new(),
}
}
pub fn add_block(&mut self, block: BasicBlockRecord) {
self.blocks.push(block);
}
pub fn add_symtab_entry(&mut self, val_id: u64, name: &str) {
self.symtab_entries.push((val_id, name.to_string()));
}
}
pub struct BitcodeEmitter<'a> {
pub writer: &'a mut BitstreamWriter,
pub type_table: &'a mut TypeTable,
pub config: BitcodeWriterConfig,
}
impl<'a> BitcodeEmitter<'a> {
pub fn new(writer: &'a mut BitstreamWriter, type_table: &'a mut TypeTable) -> Self {
Self {
writer,
type_table,
config: BitcodeWriterConfig::default(),
}
}
pub fn emit_function(&mut self, func: &FunctionRecord) {
self.writer.enter_subblock(block_ids::FUNCTION_BLOCK, 3);
self.writer
.emit_record(function_codes::DECLAREBLOCKS, &[func.blocks.len() as u64]);
if self.config.emit_value_symtab && !func.symtab_entries.is_empty() {
self.writer.enter_subblock(block_ids::VALUE_SYMTAB_BLOCK, 2);
for (i, (vid, name)) in func.symtab_entries.iter().enumerate() {
self.writer
.emit_record(value_symtab_codes::ENTRY, &[*vid, name.len() as u64]);
self.writer.emit_char6_string(name);
}
self.writer.end_block();
}
for block in &func.blocks {
for inst in &block.instructions {
self.emit_instruction_record(inst);
}
}
self.writer.end_block();
}
fn emit_instruction_record(&mut self, inst: &InstructionRecord) {
match inst.opcode {
Opcode::Ret => {
self.writer.emit_record(function_codes::RET, &inst.operands);
}
Opcode::Br => {
self.writer.emit_record(function_codes::BR, &inst.operands);
}
Opcode::Unreachable => {
self.writer.emit_record(function_codes::UNREACHABLE, &[]);
}
_ => {
let mut ops = inst.operands.clone();
ops.push(inst.type_index);
let code = match inst.opcode {
Opcode::Alloca => function_codes::INST_ALLOCA,
Opcode::Load => function_codes::INST_LOAD,
Opcode::Store => function_codes::INST_STORE,
Opcode::GetElementPtr => function_codes::INST_GEP,
Opcode::Call => function_codes::INST_CALL,
Opcode::Ret => function_codes::INST_RET,
Opcode::Phi => function_codes::INST_PHI,
Opcode::CmpXchg => function_codes::INST_CMPXCHG,
Opcode::AtomicRMW => function_codes::INST_ATOMICRMW,
Opcode::Fence => function_codes::INST_FENCE,
Opcode::LandingPad => function_codes::INST_LANDINGPAD,
_ => function_codes::BINOP,
};
self.writer.emit_record(code, &ops);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use llvm_native_core::context::LLVMContext;
use llvm_native_core::function;
use llvm_native_core::instruction;
use llvm_native_core::module::Module;
use llvm_native_core::types::Type;
use llvm_native_core::value::{valref, SubclassKind, Value, ValueRef};
#[test]
fn test_write_empty_module() {
let mut ctx = LLVMContext::new();
let module = ctx.create_module("empty");
module.set_target_triple("x86_64-unknown-linux-gnu");
let bytes = write_bitcode(module);
assert!(!bytes.is_empty());
assert_eq!(&bytes[..4], &[0x42, 0x53, 0x43, 0x00]);
}
#[test]
fn test_write_module_with_function() {
let mut ctx = LLVMContext::new();
let i32_ty = ctx.i32();
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("main", i32_ty, &[]);
m.add_function(f);
let bytes = write_bitcode(&m);
assert!(bytes.len() > 4);
}
#[test]
fn test_bitcode_roundtrip_header() {
let mut ctx = LLVMContext::new();
let module = ctx.create_module("roundtrip");
module.set_target_triple("x86_64-unknown-linux-gnu");
module.set_data_layout("e-m:e-p270:32:32");
let bytes = write_bitcode(module);
assert_eq!(&bytes[0..4], b"BSC\x00");
}
#[test]
fn test_write_function_with_body() {
let mut ctx = LLVMContext::new();
let i32_ty = ctx.i32();
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let func = function::new_function("main", i32_ty, &[]);
m.add_function(func.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let zero = llvm_native_core::constants::const_i32(0);
let ret = instruction::ret_val(zero);
func.borrow_mut().push_operand(entry.clone());
entry.borrow_mut().push_operand(ret);
let bytes = write_bitcode(&m);
assert!(bytes.len() > 10);
assert_eq!(&bytes[0..4], b"BSC\x00");
}
#[test]
fn test_write_and_roundtrip() {
let mut ctx = LLVMContext::new();
let i32_ty = ctx.i32();
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("foo", i32_ty, &[]);
m.add_function(f);
let bytes = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bytes);
assert!(parsed.is_some(), "Roundtrip should succeed");
let parsed_mod = parsed.unwrap();
assert!(!parsed_mod.functions.is_empty());
}
#[test]
fn test_type_table_new() {
let tt = TypeTable::new();
assert_eq!(tt.len(), 4); assert_eq!(tt.next_index, 4);
}
#[test]
fn test_type_table_ensure_type() {
let mut tt = TypeTable::new();
let i32_ty = Type::i32();
let idx = tt.ensure_type(&i32_ty);
assert_eq!(idx, 4); let idx2 = tt.ensure_type(&i32_ty);
assert_eq!(idx, idx2);
assert_eq!(tt.len(), 5);
}
#[test]
fn test_type_table_get_index() {
let mut tt = TypeTable::new();
let i64_ty = Type::i64();
tt.ensure_type(&i64_ty);
assert_eq!(tt.get_index(&i64_ty), Some(4));
assert_eq!(tt.get_index(&Type::float()), None);
}
#[test]
fn test_type_table_multiple_types() {
let mut tt = TypeTable::new();
let t1 = Type::i32();
let t2 = Type::i64();
let t3 = Type::float();
let t4 = Type::double();
tt.ensure_type(&t1);
tt.ensure_type(&t2);
tt.ensure_type(&t3);
tt.ensure_type(&t4);
assert_eq!(tt.len(), 8); assert_eq!(tt.get_index(&t1), Some(4));
assert_eq!(tt.get_index(&t2), Some(5));
assert_eq!(tt.get_index(&t3), Some(6));
assert_eq!(tt.get_index(&t4), Some(7));
}
#[test]
fn test_type_table_compound_types() {
let mut tt = TypeTable::new();
let i32_ty = Type::i32();
let ptr_ty = Type::pointer(0);
let arr_ty = Type::array_with(10, i32_ty.id);
tt.ensure_type(&i32_ty);
tt.ensure_type(&ptr_ty);
tt.ensure_type(&arr_ty);
assert!(tt.get_index(&arr_ty).is_some());
}
#[test]
fn test_value_table_assign() {
let mut vt = ValueTable::new();
let v1 = valref(Value::new(Type::i32()));
let idx1 = vt.assign(&v1);
let idx2 = vt.assign(&v1); assert_eq!(idx1, idx2);
let v2 = valref(Value::new(Type::i64()));
let idx3 = vt.assign(&v2);
assert_eq!(idx3, idx1 + 1);
}
#[test]
fn test_value_table_get() {
let mut vt = ValueTable::new();
let v1 = valref(Value::new(Type::i32()));
vt.assign(&v1);
assert_eq!(vt.get(&v1), Some(0));
}
#[test]
fn test_map_ir_to_bc_binop_all() {
assert_eq!(map_ir_to_bc_binop(Opcode::Add), Some(0));
assert_eq!(map_ir_to_bc_binop(Opcode::Sub), Some(1));
assert_eq!(map_ir_to_bc_binop(Opcode::Mul), Some(2));
assert_eq!(map_ir_to_bc_binop(Opcode::And), Some(10));
assert_eq!(map_ir_to_bc_binop(Opcode::Or), Some(11));
assert_eq!(map_ir_to_bc_binop(Opcode::Xor), Some(12));
assert_eq!(map_ir_to_bc_binop(Opcode::Ret), None);
}
#[test]
fn test_map_ir_to_bc_cast_all() {
assert_eq!(map_ir_to_bc_cast(Opcode::Trunc), Some(0));
assert_eq!(map_ir_to_bc_cast(Opcode::ZExt), Some(1));
assert_eq!(map_ir_to_bc_cast(Opcode::SExt), Some(2));
assert_eq!(map_ir_to_bc_cast(Opcode::BitCast), Some(9));
assert_eq!(map_ir_to_bc_cast(Opcode::AddrSpaceCast), Some(10));
assert_eq!(map_ir_to_bc_cast(Opcode::PtrToInt), Some(11));
assert_eq!(map_ir_to_bc_cast(Opcode::IntToPtr), Some(12));
assert_eq!(map_ir_to_bc_cast(Opcode::Add), None);
}
#[test]
fn test_infer_cmp_predicate_icmp() {
assert_eq!(infer_cmp_predicate("icmp.eq", false), 32);
assert_eq!(infer_cmp_predicate("icmp.ne", false), 33);
assert_eq!(infer_cmp_predicate("icmp.sgt", false), 38);
assert_eq!(infer_cmp_predicate("icmp.slt", false), 40);
assert_eq!(infer_cmp_predicate("unknown", false), 32);
}
#[test]
fn test_infer_cmp_predicate_fcmp() {
assert_eq!(infer_cmp_predicate("fcmp.oeq", true), 1);
assert_eq!(infer_cmp_predicate("fcmp.ogt", true), 2);
assert_eq!(infer_cmp_predicate("fcmp.une", true), 14);
assert_eq!(infer_cmp_predicate("fcmp.true", true), 15);
assert_eq!(infer_cmp_predicate("unknown", true), 1);
}
#[test]
fn test_encode_char6_string_ops() {
let ops = encode_char6_string_ops("ab");
assert_eq!(ops.len(), 3); assert_eq!(ops[0], 2); }
#[test]
fn test_write_string_record() {
let mut writer = BitstreamWriter::new();
write_string_record(&mut writer, 1, "hello");
let bytes = writer.finish();
assert!(!bytes.is_empty());
}
#[test]
fn test_roundtrip_empty_module_with_source() {
let mut m = Module::new("test");
m.source_filename = "test.ll".into();
m.set_target_triple("x86_64-unknown-linux-gnu");
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc).unwrap();
assert_eq!(parsed.source_filename, "test.ll");
}
#[test]
fn test_roundtrip_multiple_functions() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f1 = function::new_function("f1", Type::i32(), &[]);
let f2 = function::new_function("f2", Type::void(), &[]);
let f3 = function::new_function("f3", Type::i64(), &[]);
m.add_function(f1);
m.add_function(f2);
m.add_function(f3);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc).unwrap();
assert_eq!(parsed.functions.len(), 3);
}
#[test]
fn test_roundtrip_global_variable() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let gv = llvm_native_core::constants::new_global(
Type::i32(),
false,
function::Linkage::External,
None,
"my_gv",
);
m.globals.push(gv);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc).unwrap();
let _ = parsed;
}
#[test]
fn test_roundtrip_function_with_ret() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("do_ret", Type::void(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let ret = instruction::ret_void();
entry.borrow_mut().push_operand(ret);
f.borrow_mut().push_operand(entry);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_function_with_binop() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("add_two", Type::i32(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let a = llvm_native_core::constants::const_i32(10);
let b = llvm_native_core::constants::const_i32(20);
let sum = instruction::add(a, b);
let ret = instruction::ret_val(sum.clone());
entry.borrow_mut().push_operand(sum);
entry.borrow_mut().push_operand(ret);
f.borrow_mut().push_operand(entry);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_function_with_br() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("branch_test", Type::void(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let target = llvm_native_core::basic_block::new_basic_block("target");
let br = instruction::br(target.clone());
let ret = instruction::ret_void();
entry.borrow_mut().push_operand(br);
target.borrow_mut().push_operand(ret);
f.borrow_mut().push_operand(entry.clone());
f.borrow_mut().push_operand(target);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_function_with_icmp() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("cmp_test", Type::i1(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let a = llvm_native_core::constants::const_i32(1);
let b = llvm_native_core::constants::const_i32(2);
let cmp = instruction::icmp(llvm_native_core::opcode::ICmpPred::Eq, a, b);
let ret = instruction::ret_val(cmp.clone());
entry.borrow_mut().push_operand(cmp);
entry.borrow_mut().push_operand(ret);
f.borrow_mut().push_operand(entry);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_function_with_alloca_and_store() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("alloc_test", Type::void(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let alloc = instruction::alloca(Type::i32());
let val = llvm_native_core::constants::const_i32(42);
let st = instruction::store(val, alloc.clone());
let ret = instruction::ret_void();
entry.borrow_mut().push_operand(alloc);
entry.borrow_mut().push_operand(st);
entry.borrow_mut().push_operand(ret);
f.borrow_mut().push_operand(entry);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_function_with_phi() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("phi_test", Type::i32(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let bb1 = llvm_native_core::basic_block::new_basic_block("bb1");
let bb2 = llvm_native_core::basic_block::new_basic_block("bb2");
let merge = llvm_native_core::basic_block::new_basic_block("merge");
let v1 = llvm_native_core::constants::const_i32(1);
let v2 = llvm_native_core::constants::const_i32(2);
let phi = instruction::phi(Type::i32(), vec![(v1, bb1.clone()), (v2, bb2.clone())]);
let ret = instruction::ret_val(phi.clone());
merge.borrow_mut().push_operand(phi);
merge.borrow_mut().push_operand(ret);
f.borrow_mut().push_operand(entry);
f.borrow_mut().push_operand(bb1);
f.borrow_mut().push_operand(bb2);
f.borrow_mut().push_operand(merge);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_function_with_unreachable() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("unreach_test", Type::void(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let u = instruction::unreachable();
entry.borrow_mut().push_operand(u);
f.borrow_mut().push_operand(entry);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_function_with_select() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("sel_test", Type::i32(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let cond = llvm_native_core::constants::const_bool(true);
let a = llvm_native_core::constants::const_i32(1);
let b = llvm_native_core::constants::const_i32(2);
let sel = instruction::select(cond, a, b);
let ret = instruction::ret_val(sel.clone());
entry.borrow_mut().push_operand(sel);
entry.borrow_mut().push_operand(ret);
f.borrow_mut().push_operand(entry);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_function_with_cast() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("cast_test", Type::i64(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let val = llvm_native_core::constants::const_i32(42);
let zext = instruction::zext(val, Type::i64());
let ret = instruction::ret_val(zext.clone());
entry.borrow_mut().push_operand(zext);
entry.borrow_mut().push_operand(ret);
f.borrow_mut().push_operand(entry);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_complex_module() {
let mut m = Module::new("complex");
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 gv = llvm_native_core::constants::new_global(
Type::i32(),
true,
function::Linkage::Internal,
Some(llvm_native_core::constants::const_i32(0)),
"counter",
);
m.globals.push(gv);
let f = function::new_function("compute", Type::i32(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let v = llvm_native_core::constants::const_i32(5);
let ret = instruction::ret_val(v);
entry.borrow_mut().push_operand(ret);
f.borrow_mut().push_operand(entry);
let bc = write_bitcode(&m);
assert!(bc.len() > 20);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_empty_functions_no_panic() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("external_func", Type::void(), &[]);
m.add_function(f);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_function_with_call() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let callee = function::new_function("helper", Type::i32(), &[Type::i32()]);
m.add_function(callee.clone());
let caller = function::new_function("caller", Type::i32(), &[]);
m.add_function(caller.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let arg = llvm_native_core::constants::const_i32(10);
let call = instruction::call(Type::i32(), callee, vec![arg]);
let ret = instruction::ret_val(call.clone());
entry.borrow_mut().push_operand(call);
entry.borrow_mut().push_operand(ret);
caller.borrow_mut().push_operand(entry);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_function_with_multiple_ops() {
let mut m = Module::new("test");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("multi_op", Type::i32(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let a = llvm_native_core::constants::const_i32(7);
let b = llvm_native_core::constants::const_i32(3);
let add = instruction::add(a, b);
let c = llvm_native_core::constants::const_i32(2);
let mul = instruction::mul(add.clone(), c);
let ret = instruction::ret_val(mul.clone());
entry.borrow_mut().push_operand(add);
entry.borrow_mut().push_operand(mul);
entry.borrow_mut().push_operand(ret);
f.borrow_mut().push_operand(entry);
let bc = write_bitcode(&m);
let parsed = llvm_native_core::bitcode_reader::read_bitcode(&bc);
assert!(parsed.is_some());
}
#[test]
fn test_roundtrip_coverage_all_stored_types() {
let mut m = Module::new("types");
m.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("type_test", Type::i64(), &[]);
m.add_function(f.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let v = llvm_native_core::constants::const_i64(100);
let ret = instruction::ret_val(v);
entry.borrow_mut().push_operand(ret);
f.borrow_mut().push_operand(entry);
let bc = write_bitcode(&m);
assert!(!bc.is_empty());
}
#[test]
fn test_bitcode_starts_with_magic() {
let mut m = Module::new("magic");
let bc = write_bitcode(&m);
assert_eq!(&bc[0..4], b"BSC\x00");
}
#[test]
fn test_write_module_with_data_layout() {
let mut m = Module::new("dl");
m.set_target_triple("x86_64-unknown-linux-gnu");
m.set_data_layout("e-m:e-i64:64-f80:128-n8:16:32:64-S128");
let bc = write_bitcode(&m);
assert!(bc.len() > 10);
}
#[test]
fn test_type_table_get_index_by_id() {
let mut tt = TypeTable::new();
let i32_ty = Type::i32();
tt.ensure_type(&i32_ty);
assert_eq!(tt.get_index_by_id(&i32_ty.id), Some(4));
assert_eq!(tt.get_index_by_id(&TypeId::default()), None);
}
}