Stream: git-wasmtime

Topic: wasmtime / Issue #2316 Cranelift:


view this post on Zulip Wasmtime GitHub notifications bot (Oct 24 2020 at 18:23):

chrisvittal opened Issue #2316:

Thanks for opening a bug report! Please answer the questions below
if they're relevant and delete this text before submitting.

function u0:0(i64, i64, i64) -> i64, i64, i64 system_v {
    gv0 = symbol colocated u1:0
    gv1 = symbol colocated u1:1
    gv2 = symbol colocated u1:2
    sig0 = (i64, i64) -> i32 system_v
    fn0 = u0:0 sig0

block0(v0: i64, v1: i64, v2: i64):
    v3 = symbol_value.i64 gv0
    v4 = call fn0(v3, v0)
    v5 = symbol_value.i64 gv1
    v6 = call fn0(v5, v1)
    v7 = symbol_value.i64 gv2
    v8 = call fn0(v7, v2)
    jump block1(v2, v0, v1)

block1(v9: i64, v10: i64, v11: i64):
    return v9, v10, v11
}

I build the code with a FunctionBuilder much like the toy example. Then I call it from rust as follows, printing out diagnostic information along the way.

    unsafe {
        let mut zero = 0usize;
        let mut one = 1usize;
        let mut two = 2usize;

        let zero_r = &mut zero;
        let one_r = &mut one;
        let two_r = &mut two;

        println!("from rust:");
        println!("0: {:p}", zero_r);
        println!("1: {:p}", one_r);
        println!("2: {:p}", two_r);

        #[repr(C)]
        struct Retval(*const u8, *const u8, *const u8);
        let func = std::mem::transmute::<
            _,
            unsafe extern "C" fn(*mut usize, *mut usize, *mut usize) -> Retval,
        >(func);
        println!("from compiled:");
        let Retval(_2, _0, _1) = func(zero_r, one_r, two_r);

        println!("from returned:");
        println!("0: {:p}", _0);
        println!("1: {:p}", _1);
        println!("2: {:p}", _2);
    }

The output I then see (in debug mode), (with variation on exact addresses but the offsets are usually similar) is the following:

from rust:
0: 0x7ffe818bf6c0
1: 0x7ffe818bf6c8
2: 0x7ffe818bf6d0
from compiled:
0: 0x7ffe818bf840
1: 0x7ffe818bf6c0
2: 0x7ffe818bf6c8
from returned:
0: 0x7ffe818bfa88
1: 0x7ffe818bf880
2: 0x557e358e62a0

I expect the block parameters to have the same values that were passed into the function. I expect the return values to be what the function should return.

Thank you for your assistance.

Full code here:

# Cargo.toml
[package]
name = "cranelift-repro"
version = "0.1.0"
authors = ["Chris Vittal <chris@vittal.dev>"]
edition = "2018"

[dependencies]
cranelift = "0.67.0"
cranelift-module = "0.67.0"
cranelift-simplejit = "0.67.0"
// main.rs
use cranelift::prelude::*;
use cranelift_module::{DataContext, Linkage, Module};
use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};

use std::ffi::CString;

#[repr(C)]
struct Retval(*const u8, *const u8, *const u8);

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let builder = SimpleJITBuilder::new(cranelift_module::default_libcall_names());
    let mut builder_context = FunctionBuilderContext::new();
    let mut data_ctx = DataContext::new();
    let mut module: Module<SimpleJITBackend> = Module::new(builder);
    let mut ctx = module.make_context();
    let ptr_ty = module.target_config().pointer_type();
    let mut fn_sig = module.make_signature();
    fn_sig.params.extend(&[AbiParam::new(ptr_ty); 3]);
    fn_sig.returns.extend(&[AbiParam::new(ptr_ty); 3]);
    ctx.func.signature = fn_sig;

    // this is hacky
    let mut printf_sig = module.make_signature();
    printf_sig.params.extend(&[AbiParam::new(ptr_ty); 2]);
    printf_sig.returns.push(AbiParam::new(types::I32));

    let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_context);
    let printf = module.declare_function("printf", Linkage::Import, &printf_sig)?;
    let printf = module.declare_func_in_func(printf, &mut builder.func);

    let entry_block = builder.create_block();
    builder.append_block_params_for_function_params(entry_block);
    builder.switch_to_block(entry_block);
    builder.seal_block(entry_block);
    let params: Vec<Value> = Vec::from(builder.block_params(entry_block));
    for (i, param) in params.iter().enumerate() {
        let s = CString::new(format!("{}: %p\n", i)).expect("no nulls");
        data_ctx.define(s.into_bytes_with_nul().into_boxed_slice());
        let name = format!("data_{}", i);
        let id = module.declare_data(&name, Linkage::Local, false, false, None)?;
        module.define_data(id, &data_ctx)?;
        data_ctx.clear();
        let id = module.declare_data_in_func(id, &mut builder.func);
        let ptr = builder.ins().symbol_value(ptr_ty, id);
        builder.ins().call(printf, &[ptr, *param]);
    }

    let exit_block = builder.create_block();
    builder.append_block_params_for_function_returns(exit_block);
    let ret_params = [params[2], params[0], params[1]];
    builder.ins().jump(exit_block, &ret_params);
    builder.switch_to_block(exit_block);
    builder.seal_block(exit_block);

    let params = Vec::from(builder.block_params(exit_block));
    builder.ins().return_(&params);

    println!("{}", builder.func.display(None));
    builder.finalize();
    let id = module.declare_function("test_fn", Linkage::Export, &ctx.func.signature)?;
    module.define_function(id, &mut ctx, &mut codegen::binemit::NullTrapSink {})?;
    module.finalize_definitions();
    let func: *const u8 = module.get_finalized_function(id);

    let mut zero = 0usize;
    let mut one = 1usize;
    let mut two = 2usize;

    let zero_r = &mut zero;
    let one_r = &mut one;
    let two_r = &mut two;

    unsafe {
        let func = std::mem::transmute::<
            _,
            unsafe extern "C" fn(*mut usize, *mut usize, *mut usize) -> Retval,
        >(func);
        println!("from rust:");
        println!("0: {:p}", zero_r);
        println!("1: {:p}", one_r);
        println!("2: {:p}", two_r);
        println!("from compiled:");
        let Retval(_2, _0, _1) = func(zero_r, one_r, two_r);
        println!("from returned:");
        println!("0: {:p}", _0);
        println!("1: {:p}", _1);
        println!("2: {:p}", _2);
    }

    Ok(())
}

view this post on Zulip Wasmtime GitHub notifications bot (Oct 24 2020 at 18:23):

chrisvittal labeled Issue #2316:

Thanks for opening a bug report! Please answer the questions below
if they're relevant and delete this text before submitting.

function u0:0(i64, i64, i64) -> i64, i64, i64 system_v {
    gv0 = symbol colocated u1:0
    gv1 = symbol colocated u1:1
    gv2 = symbol colocated u1:2
    sig0 = (i64, i64) -> i32 system_v
    fn0 = u0:0 sig0

block0(v0: i64, v1: i64, v2: i64):
    v3 = symbol_value.i64 gv0
    v4 = call fn0(v3, v0)
    v5 = symbol_value.i64 gv1
    v6 = call fn0(v5, v1)
    v7 = symbol_value.i64 gv2
    v8 = call fn0(v7, v2)
    jump block1(v2, v0, v1)

block1(v9: i64, v10: i64, v11: i64):
    return v9, v10, v11
}

I build the code with a FunctionBuilder much like the toy example. Then I call it from rust as follows, printing out diagnostic information along the way.

    unsafe {
        let mut zero = 0usize;
        let mut one = 1usize;
        let mut two = 2usize;

        let zero_r = &mut zero;
        let one_r = &mut one;
        let two_r = &mut two;

        println!("from rust:");
        println!("0: {:p}", zero_r);
        println!("1: {:p}", one_r);
        println!("2: {:p}", two_r);

        #[repr(C)]
        struct Retval(*const u8, *const u8, *const u8);
        let func = std::mem::transmute::<
            _,
            unsafe extern "C" fn(*mut usize, *mut usize, *mut usize) -> Retval,
        >(func);
        println!("from compiled:");
        let Retval(_2, _0, _1) = func(zero_r, one_r, two_r);

        println!("from returned:");
        println!("0: {:p}", _0);
        println!("1: {:p}", _1);
        println!("2: {:p}", _2);
    }

The output I then see (in debug mode), (with variation on exact addresses but the offsets are usually similar) is the following:

from rust:
0: 0x7ffe818bf6c0
1: 0x7ffe818bf6c8
2: 0x7ffe818bf6d0
from compiled:
0: 0x7ffe818bf840
1: 0x7ffe818bf6c0
2: 0x7ffe818bf6c8
from returned:
0: 0x7ffe818bfa88
1: 0x7ffe818bf880
2: 0x557e358e62a0

I expect the block parameters to have the same values that were passed into the function. I expect the return values to be what the function should return.

Thank you for your assistance.

Full code here:

# Cargo.toml
[package]
name = "cranelift-repro"
version = "0.1.0"
authors = ["Chris Vittal <chris@vittal.dev>"]
edition = "2018"

[dependencies]
cranelift = "0.67.0"
cranelift-module = "0.67.0"
cranelift-simplejit = "0.67.0"
// main.rs
use cranelift::prelude::*;
use cranelift_module::{DataContext, Linkage, Module};
use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};

use std::ffi::CString;

#[repr(C)]
struct Retval(*const u8, *const u8, *const u8);

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let builder = SimpleJITBuilder::new(cranelift_module::default_libcall_names());
    let mut builder_context = FunctionBuilderContext::new();
    let mut data_ctx = DataContext::new();
    let mut module: Module<SimpleJITBackend> = Module::new(builder);
    let mut ctx = module.make_context();
    let ptr_ty = module.target_config().pointer_type();
    let mut fn_sig = module.make_signature();
    fn_sig.params.extend(&[AbiParam::new(ptr_ty); 3]);
    fn_sig.returns.extend(&[AbiParam::new(ptr_ty); 3]);
    ctx.func.signature = fn_sig;

    // this is hacky
    let mut printf_sig = module.make_signature();
    printf_sig.params.extend(&[AbiParam::new(ptr_ty); 2]);
    printf_sig.returns.push(AbiParam::new(types::I32));

    let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_context);
    let printf = module.declare_function("printf", Linkage::Import, &printf_sig)?;
    let printf = module.declare_func_in_func(printf, &mut builder.func);

    let entry_block = builder.create_block();
    builder.append_block_params_for_function_params(entry_block);
    builder.switch_to_block(entry_block);
    builder.seal_block(entry_block);
    let params: Vec<Value> = Vec::from(builder.block_params(entry_block));
    for (i, param) in params.iter().enumerate() {
        let s = CString::new(format!("{}: %p\n", i)).expect("no nulls");
        data_ctx.define(s.into_bytes_with_nul().into_boxed_slice());
        let name = format!("data_{}", i);
        let id = module.declare_data(&name, Linkage::Local, false, false, None)?;
        module.define_data(id, &data_ctx)?;
        data_ctx.clear();
        let id = module.declare_data_in_func(id, &mut builder.func);
        let ptr = builder.ins().symbol_value(ptr_ty, id);
        builder.ins().call(printf, &[ptr, *param]);
    }

    let exit_block = builder.create_block();
    builder.append_block_params_for_function_returns(exit_block);
    let ret_params = [params[2], params[0], params[1]];
    builder.ins().jump(exit_block, &ret_params);
    builder.switch_to_block(exit_block);
    builder.seal_block(exit_block);

    let params = Vec::from(builder.block_params(exit_block));
    builder.ins().return_(&params);

    println!("{}", builder.func.display(None));
    builder.finalize();
    let id = module.declare_function("test_fn", Linkage::Export, &ctx.func.signature)?;
    module.define_function(id, &mut ctx, &mut codegen::binemit::NullTrapSink {})?;
    module.finalize_definitions();
    let func: *const u8 = module.get_finalized_function(id);

    let mut zero = 0usize;
    let mut one = 1usize;
    let mut two = 2usize;

    let zero_r = &mut zero;
    let one_r = &mut one;
    let two_r = &mut two;

    unsafe {
        let func = std::mem::transmute::<
            _,
            unsafe extern "C" fn(*mut usize, *mut usize, *mut usize) -> Retval,
        >(func);
        println!("from rust:");
        println!("0: {:p}", zero_r);
        println!("1: {:p}", one_r);
        println!("2: {:p}", two_r);
        println!("from compiled:");
        let Retval(_2, _0, _1) = func(zero_r, one_r, two_r);
        println!("from returned:");
        println!("0: {:p}", _0);
        println!("1: {:p}", _1);
        println!("2: {:p}", _2);
    }

    Ok(())
}

view this post on Zulip Wasmtime GitHub notifications bot (Oct 24 2020 at 18:23):

chrisvittal labeled Issue #2316:

Thanks for opening a bug report! Please answer the questions below
if they're relevant and delete this text before submitting.

function u0:0(i64, i64, i64) -> i64, i64, i64 system_v {
    gv0 = symbol colocated u1:0
    gv1 = symbol colocated u1:1
    gv2 = symbol colocated u1:2
    sig0 = (i64, i64) -> i32 system_v
    fn0 = u0:0 sig0

block0(v0: i64, v1: i64, v2: i64):
    v3 = symbol_value.i64 gv0
    v4 = call fn0(v3, v0)
    v5 = symbol_value.i64 gv1
    v6 = call fn0(v5, v1)
    v7 = symbol_value.i64 gv2
    v8 = call fn0(v7, v2)
    jump block1(v2, v0, v1)

block1(v9: i64, v10: i64, v11: i64):
    return v9, v10, v11
}

I build the code with a FunctionBuilder much like the toy example. Then I call it from rust as follows, printing out diagnostic information along the way.

    unsafe {
        let mut zero = 0usize;
        let mut one = 1usize;
        let mut two = 2usize;

        let zero_r = &mut zero;
        let one_r = &mut one;
        let two_r = &mut two;

        println!("from rust:");
        println!("0: {:p}", zero_r);
        println!("1: {:p}", one_r);
        println!("2: {:p}", two_r);

        #[repr(C)]
        struct Retval(*const u8, *const u8, *const u8);
        let func = std::mem::transmute::<
            _,
            unsafe extern "C" fn(*mut usize, *mut usize, *mut usize) -> Retval,
        >(func);
        println!("from compiled:");
        let Retval(_2, _0, _1) = func(zero_r, one_r, two_r);

        println!("from returned:");
        println!("0: {:p}", _0);
        println!("1: {:p}", _1);
        println!("2: {:p}", _2);
    }

The output I then see (in debug mode), (with variation on exact addresses but the offsets are usually similar) is the following:

from rust:
0: 0x7ffe818bf6c0
1: 0x7ffe818bf6c8
2: 0x7ffe818bf6d0
from compiled:
0: 0x7ffe818bf840
1: 0x7ffe818bf6c0
2: 0x7ffe818bf6c8
from returned:
0: 0x7ffe818bfa88
1: 0x7ffe818bf880
2: 0x557e358e62a0

I expect the block parameters to have the same values that were passed into the function. I expect the return values to be what the function should return.

Thank you for your assistance.

Full code here:

# Cargo.toml
[package]
name = "cranelift-repro"
version = "0.1.0"
authors = ["Chris Vittal <chris@vittal.dev>"]
edition = "2018"

[dependencies]
cranelift = "0.67.0"
cranelift-module = "0.67.0"
cranelift-simplejit = "0.67.0"
// main.rs
use cranelift::prelude::*;
use cranelift_module::{DataContext, Linkage, Module};
use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};

use std::ffi::CString;

#[repr(C)]
struct Retval(*const u8, *const u8, *const u8);

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let builder = SimpleJITBuilder::new(cranelift_module::default_libcall_names());
    let mut builder_context = FunctionBuilderContext::new();
    let mut data_ctx = DataContext::new();
    let mut module: Module<SimpleJITBackend> = Module::new(builder);
    let mut ctx = module.make_context();
    let ptr_ty = module.target_config().pointer_type();
    let mut fn_sig = module.make_signature();
    fn_sig.params.extend(&[AbiParam::new(ptr_ty); 3]);
    fn_sig.returns.extend(&[AbiParam::new(ptr_ty); 3]);
    ctx.func.signature = fn_sig;

    // this is hacky
    let mut printf_sig = module.make_signature();
    printf_sig.params.extend(&[AbiParam::new(ptr_ty); 2]);
    printf_sig.returns.push(AbiParam::new(types::I32));

    let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_context);
    let printf = module.declare_function("printf", Linkage::Import, &printf_sig)?;
    let printf = module.declare_func_in_func(printf, &mut builder.func);

    let entry_block = builder.create_block();
    builder.append_block_params_for_function_params(entry_block);
    builder.switch_to_block(entry_block);
    builder.seal_block(entry_block);
    let params: Vec<Value> = Vec::from(builder.block_params(entry_block));
    for (i, param) in params.iter().enumerate() {
        let s = CString::new(format!("{}: %p\n", i)).expect("no nulls");
        data_ctx.define(s.into_bytes_with_nul().into_boxed_slice());
        let name = format!("data_{}", i);
        let id = module.declare_data(&name, Linkage::Local, false, false, None)?;
        module.define_data(id, &data_ctx)?;
        data_ctx.clear();
        let id = module.declare_data_in_func(id, &mut builder.func);
        let ptr = builder.ins().symbol_value(ptr_ty, id);
        builder.ins().call(printf, &[ptr, *param]);
    }

    let exit_block = builder.create_block();
    builder.append_block_params_for_function_returns(exit_block);
    let ret_params = [params[2], params[0], params[1]];
    builder.ins().jump(exit_block, &ret_params);
    builder.switch_to_block(exit_block);
    builder.seal_block(exit_block);

    let params = Vec::from(builder.block_params(exit_block));
    builder.ins().return_(&params);

    println!("{}", builder.func.display(None));
    builder.finalize();
    let id = module.declare_function("test_fn", Linkage::Export, &ctx.func.signature)?;
    module.define_function(id, &mut ctx, &mut codegen::binemit::NullTrapSink {})?;
    module.finalize_definitions();
    let func: *const u8 = module.get_finalized_function(id);

    let mut zero = 0usize;
    let mut one = 1usize;
    let mut two = 2usize;

    let zero_r = &mut zero;
    let one_r = &mut one;
    let two_r = &mut two;

    unsafe {
        let func = std::mem::transmute::<
            _,
            unsafe extern "C" fn(*mut usize, *mut usize, *mut usize) -> Retval,
        >(func);
        println!("from rust:");
        println!("0: {:p}", zero_r);
        println!("1: {:p}", one_r);
        println!("2: {:p}", two_r);
        println!("from compiled:");
        let Retval(_2, _0, _1) = func(zero_r, one_r, two_r);
        println!("from returned:");
        println!("0: {:p}", _0);
        println!("1: {:p}", _1);
        println!("2: {:p}", _2);
    }

    Ok(())
}

Last updated: Dec 23 2024 at 12:05 UTC