Using Mu IR to give test inputs and read outputs: Bin Op tests work now.

parent c5863f83
# Copyright 2017 The Australian National University # Copyright 2017 The Australian National University
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
[package] [package]
name = "mu" name = "mu"
version = "0.0.1" version = "0.0.1"
...@@ -26,6 +26,7 @@ doctest = false ...@@ -26,6 +26,7 @@ doctest = false
default = ["aot"] default = ["aot"]
aot = [] aot = []
jit = [] jit = []
sel4-rumprun = []
[build-dependencies] [build-dependencies]
gcc = "0.3" gcc = "0.3"
......
...@@ -43,4 +43,23 @@ fn main() { ...@@ -43,4 +43,23 @@ fn main() {
gcc::Config::new().flag("-O3").flag("-c") gcc::Config::new().flag("-O3").flag("-c")
.file("src/runtime/swap_stack_aarch64_sysv.S") .file("src/runtime/swap_stack_aarch64_sysv.S")
.compile("libswap_stack.a"); .compile("libswap_stack.a");
}
#[cfg(feature = "sel4-rumprun")]
#[cfg(target_arch = "x86_64")]
fn main() {
let mut compiler_name = String::new();
compiler_name.push_str("x86_64-rumprun-netbsd-gcc");
gcc::Config::new().flag("-O3").flag("-c")
.compiler(Path::new(compiler_name.as_str()))
.file("src/runtime/runtime_x64_sel4_rumprun_sysv.c")
.compile("libruntime.a");
gcc::Config::new().flag("-O3").flag("-c")
.compiler(Path::new(compiler_name.as_str()))
.file("src/runtime/swap_stack_x64_sysv.S")
.compile("libswap_stack.a");
gcc::Config::new().flag("-O3").flag("-c")
.compiler(Path::new(compiler_name.as_str()))
.file("c_helpers.c")
.compile("libc_helpers.a");
} }
\ No newline at end of file
This diff is collapsed.
...@@ -142,7 +142,8 @@ impl Instruction { ...@@ -142,7 +142,8 @@ impl Instruction {
| CommonInst_Pin(_) | CommonInst_Pin(_)
| CommonInst_Unpin(_) | CommonInst_Unpin(_)
| Move(_) | Move(_)
| PrintHex(_) => None | PrintHex(_)
| SetRetval(_) => None
} }
} }
...@@ -357,7 +358,8 @@ pub enum Instruction_ { ...@@ -357,7 +358,8 @@ pub enum Instruction_ {
// internal use: mov from ops[0] to value // internal use: mov from ops[0] to value
Move(OpIndex), Move(OpIndex),
// internal use: print op as hex value // internal use: print op as hex value
PrintHex(OpIndex) PrintHex(OpIndex),
SetRetval(OpIndex)
} }
impl Instruction_ { impl Instruction_ {
...@@ -484,7 +486,9 @@ impl Instruction_ { ...@@ -484,7 +486,9 @@ impl Instruction_ {
// move // move
&Instruction_::Move(from) => format!("MOVE {}", ops[from]), &Instruction_::Move(from) => format!("MOVE {}", ops[from]),
// print hex // print hex
&Instruction_::PrintHex(i) => format!("PRINTHEX {}", ops[i]) &Instruction_::PrintHex(i) => format!("PRINTHEX {}", ops[i]),
// set retval
&Instruction_::SetRetval(val) => format!("SETRETVAL {}", ops[val])
} }
} }
} }
......
...@@ -47,7 +47,8 @@ pub fn is_terminal_inst(inst: &Instruction_) -> bool { ...@@ -47,7 +47,8 @@ pub fn is_terminal_inst(inst: &Instruction_) -> bool {
| &CommonInst_Pin(_) | &CommonInst_Pin(_)
| &CommonInst_Unpin(_) | &CommonInst_Unpin(_)
| &Move(_) | &Move(_)
| &PrintHex(_) => false, | &PrintHex(_)
| &SetRetval(_) => false,
&Return(_) &Return(_)
| &ThreadExit | &ThreadExit
| &Throw(_) | &Throw(_)
...@@ -114,7 +115,8 @@ pub fn has_side_effect(inst: &Instruction_) -> bool { ...@@ -114,7 +115,8 @@ pub fn has_side_effect(inst: &Instruction_) -> bool {
&CommonInst_Pin(_) => true, &CommonInst_Pin(_) => true,
&CommonInst_Unpin(_) => true, &CommonInst_Unpin(_) => true,
&Move(_) => false, &Move(_) => false,
&PrintHex(_) => true &PrintHex(_) => true,
&SetRetval(_) => true
} }
} }
...@@ -164,6 +166,7 @@ pub fn is_potentially_excepting_instruction(inst: &Instruction_) -> bool { ...@@ -164,6 +166,7 @@ pub fn is_potentially_excepting_instruction(inst: &Instruction_) -> bool {
| &CommonInst_Pin(_) | &CommonInst_Pin(_)
| &CommonInst_Unpin(_) | &CommonInst_Unpin(_)
| &Move(_) | &Move(_)
| &PrintHex(_) => false | &PrintHex(_)
| &SetRetval(_) => false
} }
} }
\ No newline at end of file
...@@ -80,7 +80,8 @@ pub enum OpCode { ...@@ -80,7 +80,8 @@ pub enum OpCode {
CommonInst_Unpin, CommonInst_Unpin,
Move, Move,
PrintHex PrintHex,
SetRetval
} }
pub fn pick_op_code_for_ssa(ty: &P<MuType>) -> OpCode { pub fn pick_op_code_for_ssa(ty: &P<MuType>) -> OpCode {
...@@ -392,5 +393,6 @@ pub fn pick_op_code_for_inst(inst: &Instruction) -> OpCode { ...@@ -392,5 +393,6 @@ pub fn pick_op_code_for_inst(inst: &Instruction) -> OpCode {
Instruction_::CommonInst_Unpin(_) => OpCode::CommonInst_Unpin, Instruction_::CommonInst_Unpin(_) => OpCode::CommonInst_Unpin,
Instruction_::Move(_) => OpCode::Move, Instruction_::Move(_) => OpCode::Move,
Instruction_::PrintHex(_) => OpCode::PrintHex, Instruction_::PrintHex(_) => OpCode::PrintHex,
Instruction_::SetRetval(_) => OpCode::SetRetval,
} }
} }
// Copyright 2017 The Australian National University // Copyright 2017 The Australian National University
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#![warn(unused_imports)] #![warn(unused_imports)]
#![warn(unreachable_code)] #![warn(unreachable_code)]
...@@ -1179,6 +1179,24 @@ impl <'a> InstructionSelection { ...@@ -1179,6 +1179,24 @@ impl <'a> InstructionSelection {
); );
} }
// Runtime Entry
Instruction_::SetRetval(index) => {
trace!("instsel on SETRETVAL");
let ref ops = inst.ops.read().unwrap();
let ref op = ops[index];
assert!(self.match_ireg(op));
let retval = self.emit_ireg(op, f_content, f_context, vm);
self.emit_runtime_entry(
&entrypoints::SET_RETVAL,
vec![op.clone_value()],
None,
Some(node), f_context, vm
);
}
_ => unimplemented!() _ => unimplemented!()
} // main switch } // main switch
}, },
......
...@@ -3028,7 +3028,25 @@ impl CodeGenerator for ASMCodeGen { ...@@ -3028,7 +3028,25 @@ impl CodeGenerator for ASMCodeGen {
ValueLocation::Relocatable(RegGroup::GPR, callsite) ValueLocation::Relocatable(RegGroup::GPR, callsite)
} }
#[cfg(feature = "sel4-rumprun")]
// exactly the same as Linux:
// generating Position-Independent Code using PLT
fn emit_call_near_rel32(&mut self, callsite: String, func: MuName, pe: Option<MuName>) -> ValueLocation {
trace!("emit: call {}", func);
let func = func + "@PLT";
let asm = format!("call {}", symbol(func));
self.add_asm_call(asm, pe);
let callsite_symbol = symbol(callsite.clone());
self.add_asm_symbolic(directive_globl(callsite_symbol.clone()));
self.add_asm_symbolic(format!("{}:", callsite_symbol.clone()));
ValueLocation::Relocatable(RegGroup::GPR, callsite)
}
fn emit_call_near_r64(&mut self, callsite: String, func: &P<Value>, pe: Option<MuName>) -> ValueLocation { fn emit_call_near_r64(&mut self, callsite: String, func: &P<Value>, pe: Option<MuName>) -> ValueLocation {
trace!("emit: call {}", func); trace!("emit: call {}", func);
...@@ -3437,6 +3455,13 @@ fn write_align(f: &mut File, align: ByteSize) { ...@@ -3437,6 +3455,13 @@ fn write_align(f: &mut File, align: ByteSize) {
f.write_fmt(format_args!("\t.align {}\n", n)).unwrap(); f.write_fmt(format_args!("\t.align {}\n", n)).unwrap();
} }
// The alignment is exactly the same as Linux
#[cfg(feature = "sel4-rumprun")]
fn write_align(f: &mut File, align: ByteSize) {
use std::io::Write;
f.write_fmt(format_args!("\t.align {}\n", check_min_align(align))).unwrap();
}
fn write_const(f: &mut File, constant: P<Value>, loc: P<Value>) { fn write_const(f: &mut File, constant: P<Value>, loc: P<Value>) {
use std::io::Write; use std::io::Write;
...@@ -3689,6 +3714,12 @@ pub fn symbol(name: String) -> String { ...@@ -3689,6 +3714,12 @@ pub fn symbol(name: String) -> String {
pub fn symbol(name: String) -> String { pub fn symbol(name: String) -> String {
format!("_{}", name) format!("_{}", name)
} }
// same as Linux
#[cfg(feature = "sel4-rumprun")]
pub fn symbol(name: String) -> String {
name
}
#[allow(dead_code)] #[allow(dead_code)]
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
...@@ -3700,6 +3731,12 @@ pub fn pic_symbol(name: String) -> String { ...@@ -3700,6 +3731,12 @@ pub fn pic_symbol(name: String) -> String {
pub fn pic_symbol(name: String) -> String { pub fn pic_symbol(name: String) -> String {
symbol(name) symbol(name)
} }
// same as Linux
#[allow(dead_code)]
#[cfg(feature = "sel4-rumprun")]
pub fn pic_symbol(name: String) -> String {
format!("{}@GOTPCREL", name)
}
use compiler::machine_code::CompiledFunction; use compiler::machine_code::CompiledFunction;
......
...@@ -1444,7 +1444,24 @@ impl <'a> InstructionSelection { ...@@ -1444,7 +1444,24 @@ impl <'a> InstructionSelection {
Some(node), f_content, f_context, vm Some(node), f_content, f_context, vm
); );
} }
Instruction_::SetRetval(index) => {
trace!("instsel on SETRETVAL");
let ref ops = inst.ops;
let ref op = ops[index];
assert!(self.match_ireg(op));
let retval = self.emit_ireg(op, f_content, f_context, vm);
self.emit_runtime_entry(
&entrypoints::SET_RETVAL,
vec![op.clone_value()],
None,
Some(node), f_content, f_context, vm
);
}
_ => unimplemented!() _ => unimplemented!()
} // main switch } // main switch
}, },
...@@ -3932,6 +3949,9 @@ impl <'a> InstructionSelection { ...@@ -3932,6 +3949,9 @@ impl <'a> InstructionSelection {
} else if cfg!(target_os = "linux") { } else if cfg!(target_os = "linux") {
let mem = self.get_mem_for_funcref(func_id, vm); let mem = self.get_mem_for_funcref(func_id, vm);
self.backend.emit_mov_r_mem(&tmp, &mem); self.backend.emit_mov_r_mem(&tmp, &mem);
} else if cfg!(feature = "sel4-rumprun") {
let mem = self.get_mem_for_funcref(func_id, vm);
self.backend.emit_mov_r_mem(&tmp, &mem);
} else { } else {
unimplemented!() unimplemented!()
} }
...@@ -4125,6 +4145,25 @@ impl <'a> InstructionSelection { ...@@ -4125,6 +4145,25 @@ impl <'a> InstructionSelection {
let actual_loc = self.make_temporary(f_context, pv.ty.clone(), vm); let actual_loc = self.make_temporary(f_context, pv.ty.clone(), vm);
self.emit_move_value_to_value(&actual_loc, &got_loc); self.emit_move_value_to_value(&actual_loc, &got_loc);
self.make_memory_op_base_offset(&actual_loc, 0, types::get_referent_ty(&pv.ty).unwrap(), vm)
} else if cfg!(feature = "sel4-rumprun") {
// for a(%RIP), we need to load its address from a@GOTPCREL(%RIP)
// then load from the address.
// asm_backend will emit a@GOTPCREL(%RIP) for a(%RIP)
let got_loc = P(Value {
hdr: MuEntityHeader::unnamed(vm.next_id()),
ty: pv.ty.clone(),
v: Value_::Memory(MemoryLocation::Symbolic {
base: Some(x86_64::RIP.clone()),
label: pv.name().unwrap(),
is_global: true
})
});
// mov (got_loc) -> actual_loc
let actual_loc = self.make_temporary(f_context, pv.ty.clone(), vm);
self.emit_move_value_to_value(&actual_loc, &got_loc);
self.make_memory_op_base_offset(&actual_loc, 0, types::get_referent_ty(&pv.ty).unwrap(), vm) self.make_memory_op_base_offset(&actual_loc, 0, types::get_referent_ty(&pv.ty).unwrap(), vm)
} else { } else {
unimplemented!() unimplemented!()
...@@ -4695,7 +4734,9 @@ impl <'a> InstructionSelection { ...@@ -4695,7 +4734,9 @@ impl <'a> InstructionSelection {
layout[index] as i32 layout[index] as i32
} }
// Primordial function name was added as part of the callsite label \
// because it avoids conflicts between labels of different tests
fn new_callsite_label(&mut self, cur_node: Option<&TreeNode>) -> String { fn new_callsite_label(&mut self, cur_node: Option<&TreeNode>) -> String {
let ret = { let ret = {
if cur_node.is_some() { if cur_node.is_some() {
......
...@@ -529,6 +529,7 @@ pub fn estimate_insts_for_ir(inst: &Instruction) -> usize { ...@@ -529,6 +529,7 @@ pub fn estimate_insts_for_ir(inst: &Instruction) -> usize {
// others // others
Move(_) => 0, Move(_) => 0,
PrintHex(_) => 10, PrintHex(_) => 10,
SetRetval(_) => 10,
ExnInstruction{ref inner, ..} => estimate_insts_for_ir(&inner) ExnInstruction{ref inner, ..} => estimate_insts_for_ir(&inner)
} }
} }
...@@ -32,3 +32,14 @@ fn main() { ...@@ -32,3 +32,14 @@ fn main() {
fn main() { fn main() {
gcc::compile_library("libgc_clib_aarch64.a", &["src/heap/gc/clib_aarch64.S"]); gcc::compile_library("libgc_clib_aarch64.a", &["src/heap/gc/clib_aarch64.S"]);
} }
#[cfg(feature = "sel4-rumprun")]
#[cfg(target_arch = "x86_64")]
fn main() {
let mut compiler_name = String::new();
compiler_name.push_str("x86_64-rumprun-netbsd-gcc");
gcc::Config::new().flag("-O3").flag("-c")
.compiler(Path::new(compiler_name.as_str()))
.file("src/heap/gc/clib_x64_sel4_rumprun.c")
.compile("libgc_clib_x64_sel4_rumprun.a");
}
\ No newline at end of file
// Copyright 2017 The Australian National University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
void* malloc_zero(size_t size) {
void* ret = malloc(size);
memset(ret, 0, size);
return ret;
}
uintptr_t immmix_get_stack_ptr() {
uintptr_t rsp;
// get current rsp, rbp (this C func frame)
__asm__(
"mov %%rsp, %0 \n"
: "=rm" (rsp)
);
return rsp;
}
int get_registers_count() {
return 16;
}
uintptr_t* get_registers () {
uintptr_t rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15;
__asm__(
"mov %%rax, %0 \n"
"mov %%rbx, %1 \n"
"mov %%rcx, %2 \n"
"mov %%rdx, %3 \n"
"mov %%rbp, %4 \n"
"mov %%rsp, %5 \n"
"mov %%rsi, %5 \n"
"mov %%rdi, %6 \n"
"mov %%r8, %7 \n"
"mov %%r9, %8 \n"
"mov %%r10, %10\n"
"mov %%r11, %11\n"
"mov %%r12, %12\n"
"mov %%r13, %13\n"
"mov %%r14, %14\n"
"mov %%r15, %15\n"
: "=m" (rax),
"=m" (rbx),
"=m" (rcx),
"=m" (rdx),
"=m" (rbp),
"=m" (rsp),
"=m" (rsi),
"=m" (rdi),
"=m" (r8),
"=m" (r9),
"=m" (r10),
"=m" (r11),
"=m" (r12),
"=m" (r13),
"=m" (r14),
"=m" (r15)
:
:
);
uintptr_t* ret = (uintptr_t*) malloc(sizeof(uintptr_t) * 16);
ret[0] = rax;
ret[1] = rbx;
ret[2] = rcx;
ret[3] = rdx;
ret[4] = rbp;
ret[5] = rsp;
ret[6] = rsi;
ret[7] = rdi;
ret[8] = r8;
ret[9] = r9;
ret[10] = r10;
ret[11] = r11;
ret[12] = r12;
ret[13] = r13;
ret[14] = r14;
ret[15] = r15;
return ret;
}
// __thread uintptr_t low_water_mark;
int lwm_initialized = 0;
static pthread_key_t low_water_mark;
void set_low_water_mark () {
uintptr_t rsp;
if(lwm_initialized == 0){
lwm_initialized = 1;
int result = pthread_key_create(&low_water_mark, NULL);
if(result != 0){
printf("set_low_water_mark(): PThread key create failed with error code = %d\n", result);
assert(0);
}
}
// get current rsp, rbp (this C func frame)
__asm__(
"mov %%rsp, %0 \n"
: "=rm" (rsp)
);
int result = pthread_setspecific(low_water_mark, (const void *) rsp);
if(result != 0){
printf("set_low_water_mark(): PThread set specific failed with error code = %d\n", result);
assert(0);
}
}
uintptr_t get_low_water_mark() {
if(lwm_initialized == 0){
printf("get_low_water_mark(): PThread key MUST be initialized before first use!!\n");
}
void * result = pthread_getspecific(low_water_mark);
if(result == NULL){
printf("get_low_water_mark(): NO pthread key found for current thread!!\n");
assert(0);
}
return (uintptr_t) result;
}
...@@ -87,6 +87,18 @@ extern "C" { ...@@ -87,6 +87,18 @@ extern "C" {
fn get_registers_count() -> i32; fn get_registers_count() -> i32;
} }
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "sel4-rumprun")]
#[link(name = "gc_clib_x64_sel4_rumprun")]
extern "C" {
pub fn malloc_zero(size: usize) -> *const c_void;
fn immmix_get_stack_ptr() -> Address;
pub fn set_low_water_mark();
fn get_low_water_mark() -> Address;
fn get_registers() -> *const Address;
fn get_registers_count() -> i32;
}
pub fn stack_scan() -> Vec<ObjectReference> { pub fn stack_scan() -> Vec<ObjectReference> {
trace!("stack scanning..."); trace!("stack scanning...");
let stack_ptr : Address = unsafe {immmix_get_stack_ptr()}; let stack_ptr : Address = unsafe {immmix_get_stack_ptr()};
......
# Copyright 2017 The Australian National University
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
// This file provides a template so that we can define functions in ASM and also
// let the symbols be recognised in both Linux and OSX
#define CNAME(n) n
.macro begin_func n
.text
.globl CNAME(\n)
.type CNAME(\n),@function
.balign 16
CNAME(\n):
.endm
.macro end_func n
.size CNAME(\n), .-CNAME(\n)
.endm
; vim: ft=asm
...@@ -42,7 +42,19 @@ lazy_static! { ...@@ -42,7 +42,19 @@ lazy_static! {
aot: ValueLocation::Relocatable(RegGroup::GPR, String::from("muentry_get_thread_local")), aot: ValueLocation::Relocatable(RegGroup::GPR, String::from("muentry_get_thread_local")),
jit: RwLock::new(None), jit: RwLock::new(None),
}; };
// impl: runtime_x64_sysv.c
// decl: thread.rs
pub static ref SET_RETVAL : RuntimeEntrypoint = RuntimeEntrypoint {
sig: P(MuFuncSig {
hdr: MuEntityHeader::unnamed(ir::new_internal_id()),
ret_tys: vec![],
arg_tys: vec![UINT32_TYPE.clone()]
}),
aot: ValueLocation::Relocatable(RegGroup::GPR, String::from("muentry_set_retval")),
jit: RwLock::new(None)
};
// impl: swap_stack_x64_macos.s // impl: swap_stack_x64_macos.s
// decl: thread.rs // decl: thread.rs
pub static ref SWAP_BACK_TO_NATIVE_STACK : RuntimeEntrypoint = RuntimeEntrypoint { pub static ref SWAP_BACK_TO_NATIVE_STACK : RuntimeEntrypoint = RuntimeEntrypoint {
......
// Copyright 2017 The Australian National University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
extern void* vm;
extern void mu_main(char*, int, char**);
extern int32_t c_check_result();
int main(int argc, char** argv) {
char* serialize_vm = (char*) &vm;
mu_main(serialize_vm, argc, argv);
assert(c_check_result() == 0);
}