main.rs 6.9 KB
Newer Older
1
use std::convert::TryInto;
Guy Watson's avatar
Guy Watson committed
2
use std::fs;
3
use structopt::StructOpt;
4
use wasmer::{imports, Instance, Module, NativeFunc, Store, Function};
5
use wasmer_compiler_singlepass::Singlepass;
6
7
use wasmer_compiler_cranelift::{Cranelift, CraneliftOptLevel};
use wasmer_compiler_llvm::{LLVM, LLVMOptLevel};
8
9
10
11
use wasmer_engine_universal::Universal;
use wasmer_engine_dylib::Dylib;
use wasmer_engine_staticlib::Staticlib;
use wasmer::CompilerConfig;
12
use crc::{Crc, CRC_32_ISO_HDLC};
13
use std::sync::{Arc, Mutex};
14

15

16
17
18
19
20
21
22
23
/// Run a wasm program using Wasmer
#[derive(StructOpt)]
#[structopt(name = "load-wasmer", about = "Uses Wasmer to load and execute a WebAssembly program.")]
struct Cli {
    /// Compiler. Options are "singlepass", "cranelift", or "llvm".
    #[structopt(long, required(true))]
    compiler: String,

Guy Watson's avatar
Guy Watson committed
24
    /// Engine. Options are "universal" or "dylib".
25
26
27
28
29
30
    #[structopt(long, required(true))]
    engine: String,

    /// Select if the compiler should apply optimizations.
    #[structopt(long = "optimize")]
    should_optimize: bool,
31
32
33
34

    /// The path to the wasm program (.wasm).
    #[structopt(required(true))]
    program: std::path::PathBuf,
35
36
}

37
static CRC_ALGORITHM: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
38

Guy Watson's avatar
Guy Watson committed
39
fn main() -> anyhow::Result<()> {
40
41
    // Get command line arguments
    let args = Cli::from_args();
42
    if !(args.compiler == "singlepass"
43
44
45
46
         || args.compiler == "cranelift"
         || args.compiler == "llvm") {
        panic!("Provided compiler option must be [\"singlepass\"|\"cranelift\"|\"llvm\"]");
    }
47
48
49
50
    if !(args.engine == "universal"
         || args.engine == "dylib"
         || args.engine == "staticlib") {
        panic!("Provided engine option must be [\"universal\"|\"dylib\"|\"staticlib\"]");
51
    }
Guy Watson's avatar
Guy Watson committed
52

53
  
54
    // Instantiate the compiler and engine into a store
55
    let config_options = (&args.compiler[..], &args.engine[..]);
56
    let store = match config_options {
57
58
59
        ("singlepass", engine) => {
            let mut compiler = Singlepass::new();
            compiler
60
                .enable_stack_check(args.should_optimize)
61
62
                .canonicalize_nans(args.should_optimize);

63
            match engine {
64
65
66
                "universal" => Store::new(&Universal::new(compiler).engine()),
                "dylib"     => Store::new(&Dylib::new(compiler).engine()),
                "staticlib" => Store::new(&Staticlib::new(compiler).engine()),
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
                _ => panic!("engine did not match any known option"), 
            }
        },
        
        ("cranelift", engine) => {
            let mut compiler = Cranelift::new();
            compiler
                .canonicalize_nans(args.should_optimize)
                .opt_level(
                    if args.should_optimize {
                        CraneliftOptLevel::SpeedAndSize
                    } else {
                        CraneliftOptLevel::None
                    });
            
            match engine {
83
84
85
                "universal" => Store::new(&Universal::new(compiler).engine()),
                "dylib"     => Store::new(&Dylib::new(compiler).engine()),
                "staticlib" => Store::new(&Staticlib::new(compiler).engine()),
86
87
88
89
90
91
92
93
94
95
96
97
                _ => panic!("engine did not match any known option"), 
            }
        },

        ("llvm", engine) => {
            let mut compiler = LLVM::new();
            compiler
                .opt_level(
                    if args.should_optimize {
                        LLVMOptLevel::Aggressive
                    } else {
                        LLVMOptLevel::None
98
99
100
                    })
                .canonicalize_nans(args.should_optimize);
                    
101
            match engine {
102
103
104
                "universal" => Store::new(&Universal::new(compiler).engine()),
                "dylib"     => Store::new(&Dylib::new(compiler).engine()),
                "staticlib" => Store::new(&Staticlib::new(compiler).engine()),
105
106
                _ => panic!("engine did not match any known option"), 
            }
107
108
        },

109
110
        (_, _) => panic!("compiler did not match any known option"),
    };
111

112
    let wasm_bytes = fs::read(args.program)?;
113
  
114
    // Compile the Wasm module.
Guy Watson's avatar
Guy Watson committed
115
116
    let module = Module::new(&store, wasm_bytes)?;

117
118
119
120
121
122
123
    // Wasmer interoperability:
    //   Arc<...>   -   In order to allow the wasm code to interact with a data-structure on the rist side of things, we have to be able
    //                  to give it a pointer to the data, while still allowing for rust to be able to still do the same thing. Multiple
    //                  references = Arc.
    //   Mutex <...>  - The crc digest is mutable. Mutex allows mutability by locking the data even with multiple references.
    //   Option <...> - The crc digest is moved (not borrowed) when finalize() is called. Option allows us to 'take' the value

124
    // Make a crc digest
125
    let shared_digest: Arc<Mutex<Option<crc::Digest<u32>>>> = Arc::new(Mutex::new(Some(CRC_ALGORITHM.digest())));
126

127
128
129
130
131
132
133
134
135
136
137
138
139
    #[derive(wasmer::WasmerEnv, Clone)]
    struct Env<'a> {
        digest: Arc<Mutex<Option<crc::Digest<'a, u32>>>>,
    }

    fn add_to_crc(env: &Env, val: u32) {
        env.digest.lock().unwrap().as_mut().unwrap().update(&val.to_le_bytes());
    }

    fn get_crc(env: &Env) -> u32 {
        let owned_digest = env.digest.lock().unwrap().take().unwrap();
        owned_digest.finalize()
    }
140

141
    // Create a function to pass to wasm
142
    let add_to_crc_func = Function::new_native_with_env(
143
        &store,
144
        Env { digest: shared_digest.clone() }, // These clones clone the Arc, not the underlying data
145
146
147
        add_to_crc
    );

148
149
150
151
152
153
    // Create an import object with the crc function.
    let import_object = imports! {
        "env" => {
            "addToCrc" => add_to_crc_func
        },
    };
Guy Watson's avatar
Guy Watson committed
154

155
    // Instantiate the Wasm module.
Guy Watson's avatar
Guy Watson committed
156
157
    let instance = Instance::new(&module, &import_object)?;

158
159
160
161
162
163
    // Get handles for the exports
    let main_func: NativeFunc<(), i32> = instance.exports.get_native_function("_main")?;
    let crc_globals_func: NativeFunc<(), ()> = instance.exports.get_native_function("_crc_globals")?;
    let memory = instance.exports.get_memory("_memory")?;

    // Call main and add the result to the crc
164
    add_to_crc(&Env{ digest: shared_digest.clone() },main_func.call()?.try_into()?);
165
166
167
168
169
170
171
172
173
174

    // Call the crc globals function to have wasm add all of it's globals to the crc
    crc_globals_func.call()?;

    // Add the contents of memory to the crc
    // Get the pointer and size in bytes
    let mem_ptr: *mut i32 = memory.data_ptr() as *mut i32;
    let mem_size = (memory.data_size() / 4) as isize; // bytes to i32s
    
    for address in 0..mem_size {
175
        let mem_value : u32;
176
        unsafe { // raw memory: can't be typed by the compiler
177
            mem_value = *mem_ptr.offset(address) as u32;
178
        }
179
        add_to_crc(&Env { digest: shared_digest.clone() }, mem_value);
180
181
182
    }

    // Print the crc
183
    println!("{:x}", get_crc(&Env { digest: shared_digest.clone() }));
Guy Watson's avatar
Guy Watson committed
184

185
    // Signal to the shell that everything went according to plan
Guy Watson's avatar
Guy Watson committed
186
187
    Ok(())
}