use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use crate::types::TypeKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BitcodeBlockID {
ModuleBlock = 8,
ParamAttrBlock = 9,
ParamAttrGroupBlock = 10,
ConstantBlock = 11,
FunctionBlock = 12,
TypeBlockN2_DEPRECATED = 120, ValueSymtabBlock = 14,
MetadataBlock = 15,
MetadataAttachmentBlock = 16,
TypeBlock = 17,
UseListBlock = 18,
ModuleStrtabBlock = 19,
GlobalValSummaryFunctionIndexBlock = 20,
OperandBundleTagsBlock = 21,
MetadataKindBlock = 22,
StrtabBlock = 23,
FullLtoGlobalValSummaryBlock = 24,
SymtabBlock = 25,
SymtabBlockV2 = 26,
IdentificationBlock = 27,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BitcodeRecordCode {
Version = 1,
Triple = 2,
DataLayout = 3,
Asm = 4,
SectionName = 5,
DepLib = 6,
GlobalVar = 7,
Function = 8,
AliasOld = 9,
PurgeVals = 10,
GCCExcept = 11,
Comdat = 12,
VstOffset = 13,
Alias = 14,
SourceFilename = 15,
Hash = 16,
FDeclareBlocks = 101,
FBinop = 102,
FCast = 103,
FGepOld = 104,
FSelect = 105,
FExtractElt = 106,
FInsertElt = 107,
FShuffleVec = 108,
FCmp = 109,
FRet = 110,
FBr = 111,
FSwitch = 112,
FInvoke = 113,
FUnreachable = 115,
FPhi = 116,
FAlloca = 119,
FLoad = 120,
FStoreOld = 121,
FCall = 123,
FVAArg = 125,
FStore = 126,
TNumEntry = 201,
TVoid = 202,
TFloat = 203,
TDouble = 204,
TLabel = 205,
TOpaque = 206,
TInteger = 207,
TPointer = 208,
TFunctionOld = 209,
THalf = 210,
TArray = 211,
TVector = 212,
TX86FP80 = 213,
TFP128 = 214,
TPPCFP128 = 215,
TMetadata = 216,
TX86MMX = 217,
TStructAnon = 218,
TStructName = 219,
TStructNamed = 220,
TFunction = 221,
TToken = 222,
TBFloat = 223,
TX86AMX = 224,
TOpaquePointer = 225,
TScalableVector = 226,
TTargetExtType = 227,
}
#[derive(Debug, Clone)]
pub struct BitstreamRecord {
pub code: u32,
pub values: Vec<u64>,
pub block_id: Option<BitcodeBlockID>,
}
#[derive(Debug, Clone)]
pub struct Abbreviation {
pub id: u32,
pub operands: Vec<AbbrevOperand>,
}
#[derive(Debug, Clone)]
pub enum AbbrevOperand {
Literal(u64),
Fixed(u32),
VBR(u32), Char6,
Blob,
Array,
}
#[derive(Debug)]
pub struct BitstreamReader {
pub data: Vec<u8>,
pub pos: usize, pub bit_pos: u32, pub records: Vec<BitstreamRecord>,
pub abbreviations: Vec<Abbreviation>,
pub block_stack: Vec<BitcodeBlockID>,
pub current_block: Option<BitcodeBlockID>,
}
impl BitstreamReader {
pub fn new(data: Vec<u8>) -> Self {
BitstreamReader {
data,
pos: 0,
bit_pos: 0,
records: Vec::new(),
abbreviations: Vec::new(),
block_stack: Vec::new(),
current_block: None,
}
}
pub fn read_bits(&mut self, num_bits: u32) -> Option<u32> {
if num_bits > 32 {
return None;
}
let byte_offset = self.pos;
let total_bits_needed = self.bit_pos + num_bits;
let bytes_needed = ((total_bits_needed + 7) / 8) as usize;
if byte_offset + bytes_needed > self.data.len() {
return None;
}
let mut result: u32 = 0;
let mut bits_read: u32 = 0;
let mut byte_idx = byte_offset;
let mut bit_offset = self.bit_pos;
while bits_read < num_bits {
let byte = self.data[byte_idx] as u32;
let bits_available = 8 - bit_offset;
let bits_to_take = (num_bits - bits_read).min(bits_available);
result |= ((byte >> bit_offset) & ((1u32 << bits_to_take) - 1)) << bits_read;
bits_read += bits_to_take;
bit_offset += bits_to_take;
if bit_offset >= 8 {
bit_offset = 0;
byte_idx += 1;
}
}
self.pos = byte_idx;
self.bit_pos = bit_offset;
Some(result)
}
pub fn read_vbr(&mut self, width: u32) -> Option<u64> {
let mut result: u64 = 0;
let mut shift: u32 = 0;
loop {
let chunk = self.read_bits(width)? as u64;
result |= (chunk & ((1u64 << (width - 1)) - 1)) << shift;
if chunk & (1u64 << (width - 1)) == 0 {
break;
}
shift += width - 1;
}
Some(result)
}
pub fn read_abbrev_op(&mut self, _op: &AbbrevOperand) -> Option<u64> {
match _op {
AbbrevOperand::Literal(v) => Some(*v),
AbbrevOperand::Fixed(width) => self.read_bits(*width).map(|v| v as u64),
AbbrevOperand::VBR(width) => self.read_vbr(*width),
AbbrevOperand::Char6 => self.read_bits(6).map(|v| v as u64),
AbbrevOperand::Blob | AbbrevOperand::Array => None, }
}
pub fn align32(&mut self) {
let total_bits = (self.pos * 8) as u32 + self.bit_pos;
let remainder = total_bits % 32;
if remainder != 0 {
let _ = self.read_bits(32 - remainder);
}
}
pub fn is_eof(&self) -> bool {
self.pos >= self.data.len()
}
}
#[derive(Debug)]
pub struct BitstreamWriter {
pub data: Vec<u8>,
pub bit_buffer: u32,
pub bits_in_buffer: u32,
pub block_info: Vec<BlockInfo>,
pub current_block: Option<BitcodeBlockID>,
}
#[derive(Debug, Clone)]
pub struct BlockInfo {
pub block_id: BitcodeBlockID,
pub start_offset: usize,
pub abbreviation_len_width: u32,
}
impl BitstreamWriter {
pub fn new() -> Self {
BitstreamWriter {
data: Vec::new(),
bit_buffer: 0,
bits_in_buffer: 0,
block_info: Vec::new(),
current_block: None,
}
}
pub fn emit_bits(&mut self, value: u32, num_bits: u32) {
let mut remaining = num_bits;
let mut val = value;
while remaining > 0 {
let space = 32 - self.bits_in_buffer;
let to_write = remaining.min(space);
self.bit_buffer |= (val & ((1u32 << to_write) - 1)) << self.bits_in_buffer;
self.bits_in_buffer += to_write;
val >>= to_write;
remaining -= to_write;
if self.bits_in_buffer == 32 {
self.data.extend_from_slice(&self.bit_buffer.to_le_bytes());
self.bit_buffer = 0;
self.bits_in_buffer = 0;
}
}
}
pub fn emit_vbr(&mut self, value: u64, width: u32) {
let mut val = value;
loop {
let chunk = (val & ((1u64 << (width - 1)) - 1)) as u32;
val >>= width - 1;
if val != 0 {
self.emit_bits(chunk | (1u32 << (width - 1)), width);
} else {
self.emit_bits(chunk, width);
break;
}
}
}
pub fn emit_record(&mut self, code: u32, values: &[u64], abbrev: u32) {
self.emit_vbr(code as u64, 6); self.emit_vbr(values.len() as u64, 6); for v in values {
self.emit_vbr(*v, 6);
}
}
pub fn enter_subblock(&mut self, block_id: BitcodeBlockID, abbrev_len_width: u32) {
self.align32();
let offset = self.data.len();
self.emit_bits(block_id as u32, 8);
self.emit_vbr(0, 4); self.block_info.push(BlockInfo {
block_id,
start_offset: offset,
abbreviation_len_width: abbrev_len_width,
});
self.current_block = Some(block_id);
}
pub fn exit_subblock(&mut self) {
if let Some(info) = self.block_info.pop() {
self.align32();
let current_offset = self.data.len();
let block_size = current_offset - info.start_offset - 4; }
self.current_block = self.block_info.last().map(|b| b.block_id);
}
pub fn align32(&mut self) {
if self.bits_in_buffer > 0 {
self.data.extend_from_slice(
&self.bit_buffer.to_le_bytes()[..((self.bits_in_buffer as usize + 7) / 8)],
);
self.bit_buffer = 0;
self.bits_in_buffer = 0;
}
while self.data.len() % 4 != 0 {
self.data.push(0);
}
}
pub fn finish(mut self) -> Vec<u8> {
self.align32();
self.data
}
}
impl Default for BitstreamWriter {
fn default() -> Self {
BitstreamWriter::new()
}
}
pub struct BitcodeModuleWriter {
pub writer: BitstreamWriter,
pub type_table: Vec<u32>, pub value_table: Vec<String>, pub metadata_table: Vec<u64>, }
impl BitcodeModuleWriter {
pub fn new() -> Self {
BitcodeModuleWriter {
writer: BitstreamWriter::new(),
type_table: Vec::new(),
value_table: Vec::new(),
metadata_table: Vec::new(),
}
}
pub fn write_identification(&mut self) {
self.writer
.enter_subblock(BitcodeBlockID::IdentificationBlock, 2);
self.writer.emit_record(1, &[1], 0); self.writer.exit_subblock();
}
pub fn write_module_info(&mut self, triple: &str, data_layout: &str, source_filename: &str) {
self.writer.enter_subblock(BitcodeBlockID::ModuleBlock, 2);
self.writer
.emit_record(BitcodeRecordCode::Version as u32, &[3], 0);
if !triple.is_empty() {
}
if !data_layout.is_empty() {
}
if !source_filename.is_empty() {
}
self.writer.exit_subblock();
}
pub fn write_type_table(&mut self, types: &[TypeKind]) {
self.writer.enter_subblock(BitcodeBlockID::TypeBlock, 2);
self.writer.emit_record(
BitcodeRecordCode::TNumEntry as u32,
&[types.len() as u64],
0,
);
for ty in types {
let code = match ty {
TypeKind::Void => BitcodeRecordCode::TVoid as u32,
TypeKind::Half => BitcodeRecordCode::THalf as u32,
TypeKind::Float => BitcodeRecordCode::TFloat as u32,
TypeKind::Double => BitcodeRecordCode::TDouble as u32,
TypeKind::Integer { bits } => {
self.writer
.emit_record(BitcodeRecordCode::TInteger as u32, &[*bits as u64], 0);
continue;
}
_ => 0, };
self.writer.emit_record(code, &[], 0);
}
self.writer.exit_subblock();
}
pub fn finish(self) -> Vec<u8> {
self.writer.finish()
}
}
impl Default for BitcodeModuleWriter {
fn default() -> Self {
BitcodeModuleWriter::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bitstream_writer_basic() {
let mut writer = BitstreamWriter::new();
writer.emit_bits(0b1010, 4);
writer.emit_bits(0b1100, 4);
writer.align32();
let data = writer.finish();
assert!(!data.is_empty());
}
#[test]
fn test_bitstream_vbr() {
let mut writer = BitstreamWriter::new();
writer.emit_vbr(42, 6);
writer.align32();
let data = writer.finish();
assert!(!data.is_empty());
}
#[test]
fn test_bitstream_read_bits() {
let mut writer = BitstreamWriter::new();
writer.emit_bits(0b10101010, 8);
writer.align32();
let data = writer.finish();
let mut reader = BitstreamReader::new(data);
let val = reader.read_bits(8);
assert_eq!(val, Some(0b10101010));
}
#[test]
fn test_bitstream_read_vbr() {
let mut writer = BitstreamWriter::new();
writer.emit_vbr(42, 6);
writer.align32();
let data = writer.finish();
let mut reader = BitstreamReader::new(data);
let val = reader.read_vbr(6);
assert_eq!(val, Some(42));
}
#[test]
fn test_bitcode_block_enter_exit() {
let mut writer = BitstreamWriter::new();
writer.enter_subblock(BitcodeBlockID::ModuleBlock, 2);
writer.emit_record(1, &[3], 0);
writer.exit_subblock();
let data = writer.finish();
assert!(!data.is_empty());
}
#[test]
fn test_bitcode_module_writer() {
let mut bmw = BitcodeModuleWriter::new();
bmw.write_identification();
let data = bmw.finish();
assert!(!data.is_empty());
}
}