Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 45 additions & 22 deletions src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,30 +72,46 @@ impl Module {

/// Get the `GlobalAlias` having the given `Name` (if any).
pub fn get_global_alias_by_name(&self, name: &Name) -> Option<&GlobalAlias> {
self.global_aliases.iter().find(|global| global.name == *name)
self.global_aliases
.iter()
.find(|global| global.name == *name)
}

/// Get the `GlobalIFunc` having the given `Name` (if any).
pub fn get_global_ifunc_by_name(&self, name: &Name) -> Option<&GlobalIFunc> {
self.global_ifuncs.iter().find(|global| global.name == *name)
self.global_ifuncs
.iter()
.find(|global| global.name == *name)
}

/// Parse the LLVM bitcode (.bc) file at the given path to create a `Module`
pub fn from_bc_path(path: impl AsRef<Path>) -> Result<Self, String> {
unsafe fn parse_bc(
context_ref: LLVMContextRef,
mem_buf: LLVMMemoryBufferRef,
out_module: *mut LLVMModuleRef,
) -> Result<(), String> {
let result =
llvm_sys::bit_reader::LLVMParseBitcodeInContext2(context_ref, mem_buf, out_module);
LLVMDisposeMemoryBuffer(mem_buf);
match result {
0 => Ok(()),
_ => Err("Failed to parse bitcode".to_owned())
}
Self::from_path(path, Self::parse_bc)
}

pub fn from_bc_bytes(bytes: &[u8]) -> Result<Self, String> {
let memory_buffer = unsafe {
LLVMCreateMemoryBufferWithMemoryRangeCopy(
bytes.as_ptr() as *const _,
bytes.len(),
std::ffi::CString::default().as_ptr(),
)
};
Self::from_buffer(memory_buffer, Self::parse_bc)
}

unsafe fn parse_bc(
context_ref: LLVMContextRef,
mem_buf: LLVMMemoryBufferRef,
out_module: *mut LLVMModuleRef,
) -> Result<(), String> {
let result =
llvm_sys::bit_reader::LLVMParseBitcodeInContext2(context_ref, mem_buf, out_module);
LLVMDisposeMemoryBuffer(mem_buf);
match result {
0 => Ok(()),
_ => Err("Failed to parse bitcode".to_owned()),
}
Self::from_path(path, parse_bc)
}

/// Parse the LLVM text IR (.ll) file at the given path to create a `Module`
Expand Down Expand Up @@ -123,10 +139,19 @@ impl Module {
use std::ffi::CStr;
let mut err_string = std::mem::zeroed();
// This call takes ownership of the buffer, so we don't free it.
match llvm_sys::ir_reader::LLVMParseIRInContext(context_ref, mem_buf, out_module, &mut err_string) {
match llvm_sys::ir_reader::LLVMParseIRInContext(
context_ref,
mem_buf,
out_module,
&mut err_string,
) {
0 => Ok(()),
_ => Err(format!("Failed to parse IR: {}",
CStr::from_ptr(err_string).to_str().expect("Failed to convert CStr")))
_ => Err(format!(
"Failed to parse IR: {}",
CStr::from_ptr(err_string)
.to_str()
.expect("Failed to convert CStr")
)),
}
}

Expand Down Expand Up @@ -1059,8 +1084,7 @@ impl DataLayout {
independent: true,
abi,
};
data_layout.alignments.fptr_alignment_as_alignment =
Alignment { abi, pref: abi };
data_layout.alignments.fptr_alignment_as_alignment = Alignment { abi, pref: abi };
} else if let Some(stripped) = spec.strip_prefix("Fn") {
let abi: u32 = stripped
.parse()
Expand All @@ -1069,8 +1093,7 @@ impl DataLayout {
independent: false,
abi,
};
data_layout.alignments.fptr_alignment_as_alignment =
Alignment { abi, pref: abi };
data_layout.alignments.fptr_alignment_as_alignment = Alignment { abi, pref: abi };
} else if spec.starts_with('m') {
let mut chunks = spec.split(':');
let first_chunk = chunks.next().unwrap();
Expand Down
51 changes: 51 additions & 0 deletions tests/basic_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3151,3 +3151,54 @@ fn parseir() -> Result<(), Box<dyn std::error::Error>> {
assert_eq!(ret.debugloc, None);
Ok(())
}

#[test]
fn from_bc_bytes_test() {
init_logging();
let path = llvm_bc_dir().join("hello.bc");

// Read the bitcode file into bytes
let bytes = std::fs::read(&path).expect("Failed to read bitcode file");

// Parse using from_bc_bytes
let module = Module::from_bc_bytes(&bytes).expect("Failed to parse module from bytes");

// Verify the parsed module has the same content as from_bc_path
let module_from_path = Module::from_bc_path(&path).expect("Failed to parse module from path");

// Check that key properties match
assert_eq!(module.source_file_name, module_from_path.source_file_name);
assert_eq!(module.target_triple, module_from_path.target_triple);
assert_eq!(module.functions.len(), module_from_path.functions.len());
assert_eq!(module.global_vars.len(), module_from_path.global_vars.len());

// Check the main function exists and has the same properties
let func = &module.functions[0];
let func_from_path = &module_from_path.functions[0];
assert_eq!(func.name, func_from_path.name);
assert_eq!(func.parameters.len(), func_from_path.parameters.len());
assert_eq!(func.is_var_arg, func_from_path.is_var_arg);
assert_eq!(func.return_type, func_from_path.return_type);
assert_eq!(func.basic_blocks.len(), func_from_path.basic_blocks.len());

// Verify the basic block structure
let bb = &func.basic_blocks[0];
let bb_from_path = &func_from_path.basic_blocks[0];
assert_eq!(bb.name, bb_from_path.name);
assert_eq!(bb.instrs.len(), bb_from_path.instrs.len());

// Check the terminator instruction
let ret: &terminator::Ret = &bb
.term
.clone()
.try_into()
.unwrap_or_else(|_| panic!("Terminator should be a Ret but is {:?}", &bb.term));
assert_eq!(
ret.return_operand,
Some(Operand::ConstantOperand(ConstantRef::new(Constant::Int {
bits: 32,
value: 0
})))
);
assert_eq!(&ret.to_string(), "ret i32 0");
}