meijies opened issue #7937:
Hi everyone.
summary
This is asking for help/advices. I am trying generate a func named "call" and it will call a rust function named "add_wrapper". the signature of add_wrapper is
extern "C" fn add_wrapper(lhs: *mut dyn Datum, rhs: *mut dyn Datum) -> *const ArrayRef
. When I debug the test case, a "signal SIGSEGV" occurs at second println statement of add_wrapper function!test code.
use arrow::{ array::{Array, ArrayRef, Datum, Int32Array}, compute::kernels::numeric, }; use cranelift::{codegen::ir::UserFuncName, prelude::*}; use cranelift_jit::{JITBuilder, JITModule}; use cranelift_module::{DataDescription, Linkage, Module}; use std::mem; pub(crate) struct JIT { func_ctx: FunctionBuilderContext, ctx: codegen::Context, data_description: DataDescription, module: JITModule, } extern "C" fn add_wrapper(lhs: *mut dyn Datum, rhs: *mut dyn Datum) -> *const ArrayRef { let lhs = unsafe { Box::from_raw(lhs) }; let rhs = unsafe { Box::from_raw(rhs) }; let (l, l_scalar) = lhs.get(); let (r, r_scalar) = rhs.get(); println!("{:?}, {:?}", l.data_type(), l_scalar); println!("{:?}, {:?}", r.data_type(), r_scalar); let res = numeric::add(lhs.as_ref(), rhs.as_ref()).unwrap(); &res as *const ArrayRef } impl Default for JIT { fn default() -> Self { let mut flag_builder = settings::builder(); flag_builder.set("use_colocated_libcalls", "false").unwrap(); flag_builder.set("is_pic", "false").unwrap(); // build isa let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { panic!("host machine is not supported: {}", msg); }); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) .unwrap(); // build module let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); let addr: *const u8 = add_wrapper as *const u8; builder.symbol("add", addr); let module = JITModule::new(builder); Self { func_ctx: FunctionBuilderContext::new(), ctx: module.make_context(), data_description: DataDescription::new(), module, } } } impl JIT { pub fn compile(&mut self) { let mut sig_call = self.module.make_signature(); let pointer = self.module.target_config().pointer_type(); let mut sig_add = self.module.make_signature(); sig_add.params.push(AbiParam::new(pointer)); sig_add.params.push(AbiParam::new(pointer)); sig_add.returns.push(AbiParam::new(pointer)); let func_add = self .module .declare_function("add", Linkage::Import, &sig_add) .unwrap(); sig_call.params.push(AbiParam::new(pointer)); sig_call.params.push(AbiParam::new(pointer)); sig_call.returns.push(AbiParam::new(pointer)); let func_call = self .module .declare_function("call", Linkage::Local, &sig_call) .unwrap(); self.ctx.func.signature = sig_call; self.ctx.func.name = UserFuncName::user(0, func_call.as_u32()); { let mut func_builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.func_ctx); let ebb = func_builder.create_block(); func_builder.switch_to_block(ebb); func_builder.append_block_params_for_function_params(ebb); let func_add_local = self .module .declare_func_in_func(func_add, &mut func_builder.func); let lhs = func_builder.block_params(ebb)[0]; let rhs = func_builder.block_params(ebb)[1]; let call = func_builder.ins().call(func_add_local, &[lhs, rhs]); let value = { let result = func_builder.inst_results(call); assert_eq!(result.len(), 1); result[0].clone() }; func_builder.ins().return_(&[value]); func_builder.seal_all_blocks(); func_builder.finalize(); } self.module .define_function(func_call, &mut self.ctx) .unwrap(); self.module.clear_context(&mut self.ctx); self.module.finalize_definitions().unwrap(); let code_call = self.module.get_finalized_function(func_call); let call = unsafe { mem::transmute::<_, extern "C" fn(*mut dyn Datum, *mut dyn Datum) -> *const ArrayRef>( code_call, ) }; let mut a1 = Int32Array::from(vec![1, 2, 3, 4, 5]); let mut a2 = Int32Array::from(vec![1, 2, 3, 4, 5]); let result = call(&mut a1 as *mut dyn Datum, &mut a2 as *mut dyn Datum); println!("{:?}", result); } } #[cfg(test)] mod tests { use super::JIT; #[test] fn test_jit() { let mut jit = JIT::default(); jit.compile(); } }
debug info.
<img width="1652" alt="Screenshot 2024-02-14 at 21 23 54" src="https://github.com/bytecodealliance/wasmtime/assets/13784260/9807365d-63d0-421e-b403-1825b7cf2965">
bjorn3 commented on issue #7937:
Trait objects don't have a stable abi. Current rustc will pass it as two arguments rather than one. One fix here would be to do pass
*mut *mut dyn Datum
or*mut Box<dyn Datum>
instead. (double indirection such that the outer pointer is a thin pointer)
meijies closed issue #7937:
Hi everyone.
summary
This is asking for help/advices. I am trying generate a func named "call" and it will call a rust function named "add_wrapper". the signature of add_wrapper is
extern "C" fn add_wrapper(lhs: *mut dyn Datum, rhs: *mut dyn Datum) -> *const ArrayRef
. When I debug the test case, a "signal SIGSEGV" occurs at second println statement of add_wrapper function!test code.
use arrow::{ array::{Array, ArrayRef, Datum, Int32Array}, compute::kernels::numeric, }; use cranelift::{codegen::ir::UserFuncName, prelude::*}; use cranelift_jit::{JITBuilder, JITModule}; use cranelift_module::{DataDescription, Linkage, Module}; use std::mem; pub(crate) struct JIT { func_ctx: FunctionBuilderContext, ctx: codegen::Context, data_description: DataDescription, module: JITModule, } extern "C" fn add_wrapper(lhs: *mut dyn Datum, rhs: *mut dyn Datum) -> *const ArrayRef { let lhs = unsafe { Box::from_raw(lhs) }; let rhs = unsafe { Box::from_raw(rhs) }; let (l, l_scalar) = lhs.get(); let (r, r_scalar) = rhs.get(); println!("{:?}, {:?}", l.data_type(), l_scalar); println!("{:?}, {:?}", r.data_type(), r_scalar); let res = numeric::add(lhs.as_ref(), rhs.as_ref()).unwrap(); &res as *const ArrayRef } impl Default for JIT { fn default() -> Self { let mut flag_builder = settings::builder(); flag_builder.set("use_colocated_libcalls", "false").unwrap(); flag_builder.set("is_pic", "false").unwrap(); // build isa let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { panic!("host machine is not supported: {}", msg); }); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) .unwrap(); // build module let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); let addr: *const u8 = add_wrapper as *const u8; builder.symbol("add", addr); let module = JITModule::new(builder); Self { func_ctx: FunctionBuilderContext::new(), ctx: module.make_context(), data_description: DataDescription::new(), module, } } } impl JIT { pub fn compile(&mut self) { let mut sig_call = self.module.make_signature(); let pointer = self.module.target_config().pointer_type(); let mut sig_add = self.module.make_signature(); sig_add.params.push(AbiParam::new(pointer)); sig_add.params.push(AbiParam::new(pointer)); sig_add.returns.push(AbiParam::new(pointer)); let func_add = self .module .declare_function("add", Linkage::Import, &sig_add) .unwrap(); sig_call.params.push(AbiParam::new(pointer)); sig_call.params.push(AbiParam::new(pointer)); sig_call.returns.push(AbiParam::new(pointer)); let func_call = self .module .declare_function("call", Linkage::Local, &sig_call) .unwrap(); self.ctx.func.signature = sig_call; self.ctx.func.name = UserFuncName::user(0, func_call.as_u32()); { let mut func_builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.func_ctx); let ebb = func_builder.create_block(); func_builder.switch_to_block(ebb); func_builder.append_block_params_for_function_params(ebb); let func_add_local = self .module .declare_func_in_func(func_add, &mut func_builder.func); let lhs = func_builder.block_params(ebb)[0]; let rhs = func_builder.block_params(ebb)[1]; let call = func_builder.ins().call(func_add_local, &[lhs, rhs]); let value = { let result = func_builder.inst_results(call); assert_eq!(result.len(), 1); result[0].clone() }; func_builder.ins().return_(&[value]); func_builder.seal_all_blocks(); func_builder.finalize(); } self.module .define_function(func_call, &mut self.ctx) .unwrap(); self.module.clear_context(&mut self.ctx); self.module.finalize_definitions().unwrap(); let code_call = self.module.get_finalized_function(func_call); let call = unsafe { mem::transmute::<_, extern "C" fn(*mut dyn Datum, *mut dyn Datum) -> *const ArrayRef>( code_call, ) }; let mut a1 = Int32Array::from(vec![1, 2, 3, 4, 5]); let mut a2 = Int32Array::from(vec![1, 2, 3, 4, 5]); let result = call(&mut a1 as *mut dyn Datum, &mut a2 as *mut dyn Datum); println!("{:?}", result); } } #[cfg(test)] mod tests { use super::JIT; #[test] fn test_jit() { let mut jit = JIT::default(); jit.compile(); } }
debug info.
<img width="1652" alt="Screenshot 2024-02-14 at 21 23 54" src="https://github.com/bytecodealliance/wasmtime/assets/13784260/9807365d-63d0-421e-b403-1825b7cf2965">
meijies commented on issue #7937:
@bjorn3 Thanks, but I meet another issue, the return result of jit generated func is different from add_wrapper and it seems to point to an invalid address
{pointer:0x0000000000000070, vtable:(0) &[]}
.updated code.
use arrow::{ array::{Array, ArrayRef, Datum, Int32Array}, compute::kernels::numeric, }; use cranelift::{codegen::ir::UserFuncName, prelude::*}; use cranelift_jit::{JITBuilder, JITModule}; use cranelift_module::{DataDescription, Linkage, Module}; use std::{ mem::{self, forget}, sync::Arc, }; pub(crate) struct JIT { func_ctx: FunctionBuilderContext, ctx: codegen::Context, data_description: DataDescription, module: JITModule, } extern "C" fn add_wrapper( lhs: *mut Arc<dyn Datum>, rhs: *mut Arc<dyn Datum>, ) -> *mut Arc<dyn Array> { let lhs_ref = unsafe { lhs.read() }; let rhs_ref = unsafe { rhs.read() }; let mut res = numeric::add(lhs_ref.as_ref(), rhs_ref.as_ref()).unwrap(); println!("{:?}", res.clone()); &mut res as *mut ArrayRef } impl Default for JIT { fn default() -> Self { let mut flag_builder = settings::builder(); flag_builder.set("use_colocated_libcalls", "false").unwrap(); flag_builder.set("is_pic", "false").unwrap(); // build isa let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { panic!("host machine is not supported: {}", msg); }); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) .unwrap(); // build module let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); let addr: *const u8 = add_wrapper as *const u8; builder.symbol("add", addr); let module = JITModule::new(builder); Self { func_ctx: FunctionBuilderContext::new(), ctx: module.make_context(), data_description: DataDescription::new(), module, } } } impl JIT { pub fn compile(&mut self) { let mut sig_call = self.module.make_signature(); let pointer = self.module.target_config().pointer_type(); let mut sig_add = self.module.make_signature(); sig_add.params.push(AbiParam::new(pointer)); sig_add.params.push(AbiParam::new(pointer)); sig_add.returns.push(AbiParam::new(pointer)); let func_add = self .module .declare_function("add", Linkage::Import, &sig_add) .unwrap(); sig_call.params.push(AbiParam::new(pointer)); sig_call.params.push(AbiParam::new(pointer)); sig_call.returns.push(AbiParam::new(pointer)); let func_call = self .module .declare_function("call", Linkage::Local, &sig_call) .unwrap(); self.ctx.func.signature = sig_call; self.ctx.func.name = UserFuncName::user(0, func_call.as_u32()); { let mut func_builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.func_ctx); let ebb = func_builder.create_block(); func_builder.switch_to_block(ebb); func_builder.append_block_params_for_function_params(ebb); let func_add_local = self .module .declare_func_in_func(func_add, &mut func_builder.func); let lhs = func_builder.block_params(ebb)[0]; let rhs = func_builder.block_params(ebb)[1]; let call = func_builder.ins().call(func_add_local, &[lhs, rhs]); let value = { let result = func_builder.inst_results(call); assert_eq!(result.len(), 1); result[0].clone() }; func_builder.ins().return_(&[value]); func_builder.seal_all_blocks(); func_builder.finalize(); } self.module .define_function(func_call, &mut self.ctx) .unwrap(); self.module.clear_context(&mut self.ctx); self.module.finalize_definitions().unwrap(); let code_call = self.module.get_finalized_function(func_call); let call = unsafe { mem::transmute::< _, extern "C" fn(*mut Arc<dyn Datum>, *mut Arc<dyn Datum>) -> *mut Arc<dyn Array>, >(code_call) }; let a1: Arc<dyn Datum> = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5])); let a2: Arc<dyn Datum> = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5])); let result = call( &mut a1.clone() as *mut Arc<dyn Datum>, &mut a2.clone() as *mut Arc<dyn Datum>, ); let result = unsafe { result.read() }; println!("{:?}", result.clone()); } } #[cfg(test)] mod tests { use super::JIT; #[test] fn test_jit() { let mut jit = JIT::default(); jit.compile(); } }
meijies edited a comment on issue #7937:
@bjorn3 Thanks, but I meet another issue, the return result of jit generated func is different from add_wrapper and it seems to point to an invalid address
{pointer:0x0000000000000070, vtable:(0) &[]}
. error occurs at last line of compile function.updated code.
use arrow::{ array::{Array, ArrayRef, Datum, Int32Array}, compute::kernels::numeric, }; use cranelift::{codegen::ir::UserFuncName, prelude::*}; use cranelift_jit::{JITBuilder, JITModule}; use cranelift_module::{DataDescription, Linkage, Module}; use std::{ mem::{self, forget}, sync::Arc, }; pub(crate) struct JIT { func_ctx: FunctionBuilderContext, ctx: codegen::Context, data_description: DataDescription, module: JITModule, } extern "C" fn add_wrapper( lhs: *mut Arc<dyn Datum>, rhs: *mut Arc<dyn Datum>, ) -> *mut Arc<dyn Array> { let lhs_ref = unsafe { lhs.read() }; let rhs_ref = unsafe { rhs.read() }; let mut res = numeric::add(lhs_ref.as_ref(), rhs_ref.as_ref()).unwrap(); println!("{:?}", res.clone()); &mut res as *mut ArrayRef } impl Default for JIT { fn default() -> Self { let mut flag_builder = settings::builder(); flag_builder.set("use_colocated_libcalls", "false").unwrap(); flag_builder.set("is_pic", "false").unwrap(); // build isa let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { panic!("host machine is not supported: {}", msg); }); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) .unwrap(); // build module let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); let addr: *const u8 = add_wrapper as *const u8; builder.symbol("add", addr); let module = JITModule::new(builder); Self { func_ctx: FunctionBuilderContext::new(), ctx: module.make_context(), data_description: DataDescription::new(), module, } } } impl JIT { pub fn compile(&mut self) { let mut sig_call = self.module.make_signature(); let pointer = self.module.target_config().pointer_type(); let mut sig_add = self.module.make_signature(); sig_add.params.push(AbiParam::new(pointer)); sig_add.params.push(AbiParam::new(pointer)); sig_add.returns.push(AbiParam::new(pointer)); let func_add = self .module .declare_function("add", Linkage::Import, &sig_add) .unwrap(); sig_call.params.push(AbiParam::new(pointer)); sig_call.params.push(AbiParam::new(pointer)); sig_call.returns.push(AbiParam::new(pointer)); let func_call = self .module .declare_function("call", Linkage::Local, &sig_call) .unwrap(); self.ctx.func.signature = sig_call; self.ctx.func.name = UserFuncName::user(0, func_call.as_u32()); { let mut func_builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.func_ctx); let ebb = func_builder.create_block(); func_builder.switch_to_block(ebb); func_builder.append_block_params_for_function_params(ebb); let func_add_local = self .module .declare_func_in_func(func_add, &mut func_builder.func); let lhs = func_builder.block_params(ebb)[0]; let rhs = func_builder.block_params(ebb)[1]; let call = func_builder.ins().call(func_add_local, &[lhs, rhs]); let value = { let result = func_builder.inst_results(call); assert_eq!(result.len(), 1); result[0].clone() }; func_builder.ins().return_(&[value]); func_builder.seal_all_blocks(); func_builder.finalize(); } self.module .define_function(func_call, &mut self.ctx) .unwrap(); self.module.clear_context(&mut self.ctx); self.module.finalize_definitions().unwrap(); let code_call = self.module.get_finalized_function(func_call); let call = unsafe { mem::transmute::< _, extern "C" fn(*mut Arc<dyn Datum>, *mut Arc<dyn Datum>) -> *mut Arc<dyn Array>, >(code_call) }; let a1: Arc<dyn Datum> = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5])); let a2: Arc<dyn Datum> = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5])); let result = call( &mut a1.clone() as *mut Arc<dyn Datum>, &mut a2.clone() as *mut Arc<dyn Datum>, ); let result = unsafe { result.read() }; println!("{:?}", result.clone()); } } #[cfg(test)] mod tests { use super::JIT; #[test] fn test_jit() { let mut jit = JIT::default(); jit.compile(); } }
meijies edited a comment on issue #7937:
@bjorn3 Thanks, but I meet another issue, the return result of jit generated func is different from add_wrapper and it seems to point to an invalid address
{pointer:0x0000000000000070, vtable:(0) &[]}
. error occurs at last line of compile function.updated code.
use arrow::{ array::{Array, ArrayRef, Datum, Int32Array}, compute::kernels::numeric, }; use cranelift::{codegen::ir::UserFuncName, prelude::*}; use cranelift_jit::{JITBuilder, JITModule}; use cranelift_module::{DataDescription, Linkage, Module}; use std::{ mem::{self, forget}, sync::Arc, }; pub(crate) struct JIT { func_ctx: FunctionBuilderContext, ctx: codegen::Context, data_description: DataDescription, module: JITModule, } extern "C" fn add_wrapper( lhs: *mut Arc<dyn Datum>, rhs: *mut Arc<dyn Datum>, ) -> *mut Arc<dyn Array> { let lhs_ref = unsafe { lhs.read() }; let rhs_ref = unsafe { rhs.read() }; let mut res = numeric::add(lhs_ref.as_ref(), rhs_ref.as_ref()).unwrap(); println!("{:?}", res.clone()); &mut res as *mut ArrayRef } impl Default for JIT { fn default() -> Self { let mut flag_builder = settings::builder(); flag_builder.set("use_colocated_libcalls", "false").unwrap(); flag_builder.set("is_pic", "false").unwrap(); // build isa let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { panic!("host machine is not supported: {}", msg); }); let isa = isa_builder .finish(settings::Flags::new(flag_builder)) .unwrap(); // build module let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); let addr: *const u8 = add_wrapper as *const u8; builder.symbol("add", addr); let module = JITModule::new(builder); Self { func_ctx: FunctionBuilderContext::new(), ctx: module.make_context(), data_description: DataDescription::new(), module, } } } impl JIT { pub fn compile(&mut self) { let mut sig_call = self.module.make_signature(); let pointer = self.module.target_config().pointer_type(); let mut sig_add = self.module.make_signature(); sig_add.params.push(AbiParam::new(pointer)); sig_add.params.push(AbiParam::new(pointer)); sig_add.returns.push(AbiParam::new(pointer)); let func_add = self .module .declare_function("add", Linkage::Import, &sig_add) .unwrap(); sig_call.params.push(AbiParam::new(pointer)); sig_call.params.push(AbiParam::new(pointer)); sig_call.returns.push(AbiParam::new(pointer)); let func_call = self .module .declare_function("call", Linkage::Local, &sig_call) .unwrap(); self.ctx.func.signature = sig_call; self.ctx.func.name = UserFuncName::user(0, func_call.as_u32()); { let mut func_builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.func_ctx); let ebb = func_builder.create_block(); func_builder.switch_to_block(ebb); func_builder.append_block_params_for_function_params(ebb); let func_add_local = self .module .declare_func_in_func(func_add, &mut func_builder.func); let lhs = func_builder.block_params(ebb)[0]; let rhs = func_builder.block_params(ebb)[1]; let call = func_builder.ins().call(func_add_local, &[lhs, rhs]); let value = { let result = func_builder.inst_results(call); assert_eq!(result.len(), 1); result[0].clone() }; func_builder.ins().return_(&[value]); func_builder.seal_all_blocks(); func_builder.finalize(); } self.module .define_function(func_call, &mut self.ctx) .unwrap(); self.module.clear_context(&mut self.ctx); self.module.finalize_definitions().unwrap(); let code_call = self.module.get_finalized_function(func_call); let call = unsafe { mem::transmute::< _, extern "C" fn(*mut Arc<dyn Datum>, *mut Arc<dyn Datum>) -> *mut Arc<dyn Array>, >(code_call) }; let a1: Arc<dyn Datum> = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5])); let a2: Arc<dyn Datum> = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5])); let result = call( &mut a1.clone() as *mut Arc<dyn Datum>, &mut a2.clone() as *mut Arc<dyn Datum>, ); let result = unsafe { result.read() }; println!("{:?}", result.clone()); } } #[cfg(test)] mod tests { use super::JIT; #[test] fn test_jit() { let mut jit = JIT::default(); jit.compile(); } }
debug info
<img width="1627" alt="Screenshot 2024-02-15 at 01 22 44" src="https://github.com/bytecodealliance/wasmtime/assets/13784260/867f3eba-efb8-4764-8591-df5eb6af2b1b">
bjorn3 commented on issue #7937:
At least one issue I see is that you are having a double free.
&mut a1.clone() as *mut Arc<dyn Datum>
would increment the refcount in the clone and decrement it again after it gets dropped. At the same time howeverunsafe { lhs.read() }
insideadd_wrapper
constructs anArc
without incrementing the refcount, but then decrements it at the end. Furthermore the&mut res as *mut ArrayRef
insideadd_wrapper
will drop theres
at the end of the function and thus cause the value to be freed, but then after the call,unsafe { result.read() };
will try to get anArc
again. In addition it will not even keep theArrayRef
value itself alive, so you are returning a dangling pointer to a danglingArc
. To avoid the refcount issues you could useArc::into_raw()
on one side andArc::from_raw()
on the other side. For the issue with&mut res as *mut ArrayRef
, the easiest solution is likely to changeadd_wrapper
to instead take as argument the location where theArrayRef
(or rather*mut dyn Array
after callingArc::into_raw()
) should be written and pass this intocall
.
Last updated: Dec 23 2024 at 12:05 UTC