Skip to content

Commit ea0ca88

Browse files
authored
Add Module::from_bc_bytes method with test, extract parse_bc (#75)
1 parent 5cf225c commit ea0ca88

2 files changed

Lines changed: 96 additions & 22 deletions

File tree

src/module.rs

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -72,30 +72,46 @@ impl Module {
7272

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

7880
/// Get the `GlobalIFunc` having the given `Name` (if any).
7981
pub fn get_global_ifunc_by_name(&self, name: &Name) -> Option<&GlobalIFunc> {
80-
self.global_ifuncs.iter().find(|global| global.name == *name)
82+
self.global_ifuncs
83+
.iter()
84+
.find(|global| global.name == *name)
8185
}
8286

8387
/// Parse the LLVM bitcode (.bc) file at the given path to create a `Module`
8488
pub fn from_bc_path(path: impl AsRef<Path>) -> Result<Self, String> {
85-
unsafe fn parse_bc(
86-
context_ref: LLVMContextRef,
87-
mem_buf: LLVMMemoryBufferRef,
88-
out_module: *mut LLVMModuleRef,
89-
) -> Result<(), String> {
90-
let result =
91-
llvm_sys::bit_reader::LLVMParseBitcodeInContext2(context_ref, mem_buf, out_module);
92-
LLVMDisposeMemoryBuffer(mem_buf);
93-
match result {
94-
0 => Ok(()),
95-
_ => Err("Failed to parse bitcode".to_owned())
96-
}
89+
Self::from_path(path, Self::parse_bc)
90+
}
91+
92+
pub fn from_bc_bytes(bytes: &[u8]) -> Result<Self, String> {
93+
let memory_buffer = unsafe {
94+
LLVMCreateMemoryBufferWithMemoryRangeCopy(
95+
bytes.as_ptr() as *const _,
96+
bytes.len(),
97+
std::ffi::CString::default().as_ptr(),
98+
)
99+
};
100+
Self::from_buffer(memory_buffer, Self::parse_bc)
101+
}
102+
103+
unsafe fn parse_bc(
104+
context_ref: LLVMContextRef,
105+
mem_buf: LLVMMemoryBufferRef,
106+
out_module: *mut LLVMModuleRef,
107+
) -> Result<(), String> {
108+
let result =
109+
llvm_sys::bit_reader::LLVMParseBitcodeInContext2(context_ref, mem_buf, out_module);
110+
LLVMDisposeMemoryBuffer(mem_buf);
111+
match result {
112+
0 => Ok(()),
113+
_ => Err("Failed to parse bitcode".to_owned()),
97114
}
98-
Self::from_path(path, parse_bc)
99115
}
100116

101117
/// Parse the LLVM text IR (.ll) file at the given path to create a `Module`
@@ -123,10 +139,19 @@ impl Module {
123139
use std::ffi::CStr;
124140
let mut err_string = std::mem::zeroed();
125141
// This call takes ownership of the buffer, so we don't free it.
126-
match llvm_sys::ir_reader::LLVMParseIRInContext(context_ref, mem_buf, out_module, &mut err_string) {
142+
match llvm_sys::ir_reader::LLVMParseIRInContext(
143+
context_ref,
144+
mem_buf,
145+
out_module,
146+
&mut err_string,
147+
) {
127148
0 => Ok(()),
128-
_ => Err(format!("Failed to parse IR: {}",
129-
CStr::from_ptr(err_string).to_str().expect("Failed to convert CStr")))
149+
_ => Err(format!(
150+
"Failed to parse IR: {}",
151+
CStr::from_ptr(err_string)
152+
.to_str()
153+
.expect("Failed to convert CStr")
154+
)),
130155
}
131156
}
132157

@@ -1059,8 +1084,7 @@ impl DataLayout {
10591084
independent: true,
10601085
abi,
10611086
};
1062-
data_layout.alignments.fptr_alignment_as_alignment =
1063-
Alignment { abi, pref: abi };
1087+
data_layout.alignments.fptr_alignment_as_alignment = Alignment { abi, pref: abi };
10641088
} else if let Some(stripped) = spec.strip_prefix("Fn") {
10651089
let abi: u32 = stripped
10661090
.parse()
@@ -1069,8 +1093,7 @@ impl DataLayout {
10691093
independent: false,
10701094
abi,
10711095
};
1072-
data_layout.alignments.fptr_alignment_as_alignment =
1073-
Alignment { abi, pref: abi };
1096+
data_layout.alignments.fptr_alignment_as_alignment = Alignment { abi, pref: abi };
10741097
} else if spec.starts_with('m') {
10751098
let mut chunks = spec.split(':');
10761099
let first_chunk = chunks.next().unwrap();

tests/basic_tests.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3151,3 +3151,54 @@ fn parseir() -> Result<(), Box<dyn std::error::Error>> {
31513151
assert_eq!(ret.debugloc, None);
31523152
Ok(())
31533153
}
3154+
3155+
#[test]
3156+
fn from_bc_bytes_test() {
3157+
init_logging();
3158+
let path = llvm_bc_dir().join("hello.bc");
3159+
3160+
// Read the bitcode file into bytes
3161+
let bytes = std::fs::read(&path).expect("Failed to read bitcode file");
3162+
3163+
// Parse using from_bc_bytes
3164+
let module = Module::from_bc_bytes(&bytes).expect("Failed to parse module from bytes");
3165+
3166+
// Verify the parsed module has the same content as from_bc_path
3167+
let module_from_path = Module::from_bc_path(&path).expect("Failed to parse module from path");
3168+
3169+
// Check that key properties match
3170+
assert_eq!(module.source_file_name, module_from_path.source_file_name);
3171+
assert_eq!(module.target_triple, module_from_path.target_triple);
3172+
assert_eq!(module.functions.len(), module_from_path.functions.len());
3173+
assert_eq!(module.global_vars.len(), module_from_path.global_vars.len());
3174+
3175+
// Check the main function exists and has the same properties
3176+
let func = &module.functions[0];
3177+
let func_from_path = &module_from_path.functions[0];
3178+
assert_eq!(func.name, func_from_path.name);
3179+
assert_eq!(func.parameters.len(), func_from_path.parameters.len());
3180+
assert_eq!(func.is_var_arg, func_from_path.is_var_arg);
3181+
assert_eq!(func.return_type, func_from_path.return_type);
3182+
assert_eq!(func.basic_blocks.len(), func_from_path.basic_blocks.len());
3183+
3184+
// Verify the basic block structure
3185+
let bb = &func.basic_blocks[0];
3186+
let bb_from_path = &func_from_path.basic_blocks[0];
3187+
assert_eq!(bb.name, bb_from_path.name);
3188+
assert_eq!(bb.instrs.len(), bb_from_path.instrs.len());
3189+
3190+
// Check the terminator instruction
3191+
let ret: &terminator::Ret = &bb
3192+
.term
3193+
.clone()
3194+
.try_into()
3195+
.unwrap_or_else(|_| panic!("Terminator should be a Ret but is {:?}", &bb.term));
3196+
assert_eq!(
3197+
ret.return_operand,
3198+
Some(Operand::ConstantOperand(ConstantRef::new(Constant::Int {
3199+
bits: 32,
3200+
value: 0
3201+
})))
3202+
);
3203+
assert_eq!(&ret.to_string(), "ret i32 0");
3204+
}

0 commit comments

Comments
 (0)