use cranelift::prelude::*;
use super::MirToIR;
const STRING_OBJ_DATA_OFFSET: i32 = 8;
const STRING_OBJ_LEN_OFFSET: i32 = 16;
impl<'a, 'b> MirToIR<'a, 'b> {
pub(crate) fn compile_string_len(&mut self, str_ptr: Value) -> Value {
let len = self
.builder
.ins()
.load(types::I32, MemFlags::new(), str_ptr, STRING_OBJ_LEN_OFFSET);
self.builder.ins().uextend(types::I64, len)
}
pub(crate) fn compile_string_data_ptr(&mut self, str_ptr: Value) -> Value {
self.builder
.ins()
.load(types::I64, MemFlags::new(), str_ptr, STRING_OBJ_DATA_OFFSET)
}
}
#[cfg(test)]
mod tests {
use cranelift::prelude::*;
use cranelift_jit::{JITBuilder, JITModule};
use cranelift_module::Module;
#[repr(C)]
struct FakeStringObj {
_header: [u8; 8],
data: *const u8,
len: u32,
_pad: u32,
}
fn jit_string_len(ptr_val: u64) -> i64 {
let mut flag_builder = settings::builder();
flag_builder.set("opt_level", "speed_and_size").unwrap();
let isa_builder = cranelift_native::builder().unwrap();
let isa = isa_builder
.finish(settings::Flags::new(flag_builder))
.unwrap();
let builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
let mut module = JITModule::new(builder);
let mut ctx = module.make_context();
let mut sig = module.make_signature();
sig.params.push(AbiParam::new(types::I64));
sig.returns.push(AbiParam::new(types::I64));
let func_id = module
.declare_function("string_len", cranelift_module::Linkage::Local, &sig)
.unwrap();
ctx.func.signature = sig;
let mut fn_builder_ctx = FunctionBuilderContext::new();
let mut fbuilder = FunctionBuilder::new(&mut ctx.func, &mut fn_builder_ctx);
let block = fbuilder.create_block();
fbuilder.append_block_params_for_function_params(block);
fbuilder.switch_to_block(block);
fbuilder.seal_block(block);
let str_ptr = fbuilder.block_params(block)[0];
let len = fbuilder
.ins()
.load(types::I32, MemFlags::new(), str_ptr, 16); let len_i64 = fbuilder.ins().uextend(types::I64, len);
fbuilder.ins().return_(&[len_i64]);
fbuilder.finalize();
module.define_function(func_id, &mut ctx).unwrap();
module.clear_context(&mut ctx);
module.finalize_definitions().unwrap();
let code_ptr = module.get_finalized_function(func_id);
let func: fn(u64) -> i64 = unsafe { std::mem::transmute(code_ptr) };
func(ptr_val)
}
fn jit_string_data_ptr(ptr_val: u64) -> i64 {
let mut flag_builder = settings::builder();
flag_builder.set("opt_level", "speed_and_size").unwrap();
let isa_builder = cranelift_native::builder().unwrap();
let isa = isa_builder
.finish(settings::Flags::new(flag_builder))
.unwrap();
let builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
let mut module = JITModule::new(builder);
let mut ctx = module.make_context();
let mut sig = module.make_signature();
sig.params.push(AbiParam::new(types::I64));
sig.returns.push(AbiParam::new(types::I64));
let func_id = module
.declare_function("string_data_ptr", cranelift_module::Linkage::Local, &sig)
.unwrap();
ctx.func.signature = sig;
let mut fn_builder_ctx = FunctionBuilderContext::new();
let mut fbuilder = FunctionBuilder::new(&mut ctx.func, &mut fn_builder_ctx);
let block = fbuilder.create_block();
fbuilder.append_block_params_for_function_params(block);
fbuilder.switch_to_block(block);
fbuilder.seal_block(block);
let str_ptr = fbuilder.block_params(block)[0];
let data = fbuilder
.ins()
.load(types::I64, MemFlags::new(), str_ptr, 8); fbuilder.ins().return_(&[data]);
fbuilder.finalize();
module.define_function(func_id, &mut ctx).unwrap();
module.clear_context(&mut ctx);
module.finalize_definitions().unwrap();
let code_ptr = module.get_finalized_function(func_id);
let func: fn(u64) -> i64 = unsafe { std::mem::transmute(code_ptr) };
func(ptr_val)
}
#[test]
fn test_string_len_codegen() {
let data = b"hello";
let s = FakeStringObj {
_header: [0; 8],
data: data.as_ptr(),
len: 5,
_pad: 0,
};
let ptr = &s as *const _ as u64;
assert_eq!(jit_string_len(ptr), 5);
}
#[test]
fn test_string_len_zero() {
let s = FakeStringObj {
_header: [0; 8],
data: std::ptr::null(),
len: 0,
_pad: 0,
};
let ptr = &s as *const _ as u64;
assert_eq!(jit_string_len(ptr), 0);
}
#[test]
fn test_string_data_ptr_codegen() {
let data = b"test";
let s = FakeStringObj {
_header: [0; 8],
data: data.as_ptr(),
len: 4,
_pad: 0,
};
let ptr = &s as *const _ as u64;
let result = jit_string_data_ptr(ptr);
assert_eq!(result as u64, data.as_ptr() as u64);
}
#[test]
fn test_string_len_large() {
let s = FakeStringObj {
_header: [0; 8],
data: std::ptr::null(),
len: 1_000_000,
_pad: 0,
};
let ptr = &s as *const _ as u64;
assert_eq!(jit_string_len(ptr), 1_000_000);
}
}