vm.rs 65.8 KB
Newer Older
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
1
// Copyright 2017 The Australian National University
2
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
3 4 5
// 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
6
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
9 10 11 12 13 14
// 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.

qinsoon's avatar
qinsoon committed
15 16
use std::collections::HashMap;

17
use rodal;
qinsoon's avatar
qinsoon committed
18
use ast::ptr::*;
qinsoon's avatar
qinsoon committed
19
use ast::ir::*;
20
use ast::inst::*;
qinsoon's avatar
qinsoon committed
21
use ast::types;
qinsoon's avatar
qinsoon committed
22
use ast::types::*;
qinsoon's avatar
qinsoon committed
23
use compiler::{Compiler, CompilerPolicy};
qinsoon's avatar
qinsoon committed
24
use compiler::backend;
25
use compiler::backend::BackendType;
26
use compiler::machine_code::{CompiledFunction, CompiledCallsite};
27

28
use runtime::thread::*;
29
use runtime::*;
qinsoon's avatar
qinsoon committed
30
use utils::ByteSize;
31
use utils::BitSize;
32
use utils::Address;
33
use runtime::mm as gc;
34
use vm::handle::*;
qinsoon's avatar
qinsoon committed
35 36
use vm::vm_options::VMOptions;
use vm::vm_options::MuLogLevel;
37

qinsoon's avatar
qinsoon committed
38
use log::LogLevel;
39
use std::sync::Arc;
qinsoon's avatar
qinsoon committed
40
use std::sync::RwLock;
41
use std::sync::RwLockWriteGuard;
42
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
43

44 45
use std;
use utils::bit_utils::{bits_ones, u64_asr};
qinsoon's avatar
qinsoon committed
46

qinsoon's avatar
qinsoon committed
47 48 49
/// The VM struct. This stores metadata for the currently running Zebu instance.
/// This struct gets persisted in the boot image, and when the boot image is loaded,
/// everything should be back to the same status as before persisting.
qinsoon's avatar
[wip]  
qinsoon committed
50
///
qinsoon's avatar
qinsoon committed
51 52 53
/// This struct is usually used as Arc<VM> so it can be shared among threads. The
/// Arc<VM> is stored in every thread local of a Mu thread, so that they can refer
/// to the VM easily.
qinsoon's avatar
[wip]  
qinsoon committed
54
///
qinsoon's avatar
qinsoon committed
55 56 57 58
/// We are using fine-grained lock on VM to allow mutability on different fields in VM.
/// Also we use two-level locks for some data structures such as MuFunction/
/// MuFunctionVersion/CompiledFunction so that we can mutate on two
/// different functions/funcvers/etc at the same time.
qinsoon's avatar
[wip]  
qinsoon committed
59 60 61 62

//  FIXME: However, there are problems with fine-grained lock design,
//  and we will need to rethink. See Issue #2.
//  TODO: besides fields in VM, there are some 'globals' we need to persist
qinsoon's avatar
qinsoon committed
63 64 65 66
//  such as STRUCT_TAG_MAP, INTERNAL_ID and internal types from ir crate. The point is
//  ir crate should be independent and self-contained. But when persisting the 'world',
//  besides persisting VM struct (containing most of the 'world'), we also need to
//  specifically persist those globals.
67 68
pub struct VM {
    // The comments are the offset into the struct
69
    // ---serialize---
qinsoon's avatar
[wip]  
qinsoon committed
70
    /// next MuID to assign
71
    next_id: AtomicUsize, // +0
qinsoon's avatar
[wip]  
qinsoon committed
72
    /// a map from MuID to MuName (for client to query)
73
    id_name_map: RwLock<HashMap<MuID, MuName>>, // +8
qinsoon's avatar
[wip]  
qinsoon committed
74
    /// a map from MuName to ID (for client to query)
75
    name_id_map: RwLock<HashMap<MuName, MuID>>, // +64
qinsoon's avatar
[wip]  
qinsoon committed
76
    /// types declared to the VM
77
    types: RwLock<HashMap<MuID, P<MuType>>>, // +120
qinsoon's avatar
[wip]  
qinsoon committed
78 79 80
    /// types that are resolved as BackendType
    backend_type_info: RwLock<HashMap<MuID, Box<BackendType>>>, // +176
    /// constants declared to the VM
81
    constants: RwLock<HashMap<MuID, P<Value>>>, // +232
qinsoon's avatar
[wip]  
qinsoon committed
82
    /// globals declared to the VM
83
    globals: RwLock<HashMap<MuID, P<Value>>>, // +288
qinsoon's avatar
[wip]  
qinsoon committed
84
    /// function signatures declared
85
    func_sigs: RwLock<HashMap<MuID, P<MuFuncSig>>>, // +400
qinsoon's avatar
[wip]  
qinsoon committed
86
    /// functions declared to the VM
87
    funcs: RwLock<HashMap<MuID, RwLock<MuFunction>>>, // +456
qinsoon's avatar
[wip]  
qinsoon committed
88
    /// primordial function that is set to make boot image
89
    primordial: RwLock<Option<PrimordialThreadInfo>>, // +568
90

qinsoon's avatar
[wip]  
qinsoon committed
91
    /// current options for this VM
92
    pub vm_options: VMOptions, // +624
93

94
    // ---partially serialize---
qinsoon's avatar
[wip]  
qinsoon committed
95 96
    /// compiled functions
    /// (we are not persisting generated code with compiled function)
97
    compiled_funcs: RwLock<HashMap<MuID, RwLock<CompiledFunction>>>, // +728
98

99 100
    /// match each functions version to a map, mapping each of it's containing callsites
    /// to the name of the catch block
101
    callsite_table: RwLock<HashMap<MuID, Vec<Callsite>>>, // +784
102

103
    // ---do not serialize---
qinsoon's avatar
vm.rs  
qinsoon committed
104 105 106
    /// global cell locations. We use this map to create handles for global cells,
    /// or dump globals into boot image. (this map does not get persisted because
    /// the location is changed in different runs)
107
    global_locations: RwLock<HashMap<MuID, ValueLocation>>,
108
    func_vers: RwLock<HashMap<MuID, RwLock<MuFunctionVersion>>>,
109

qinsoon's avatar
[wip]  
qinsoon committed
110 111 112 113
    /// all the funcref that clients want to store for AOT which are pending stores
    /// For AOT scenario, when client tries to store funcref to the heap, the store
    /// happens before we have an actual address for the function so we store a fake
    /// funcref and when generating boot image, we fix the funcref with a relocatable symbol
114 115
    aot_pending_funcref_store: RwLock<HashMap<Address, ValueLocation>>,

116 117
    /// runtime callsite table for exception handling
    /// a map from callsite address to CompiledCallsite
118
    compiled_callsite_table: RwLock<HashMap<Address, CompiledCallsite>>, // 896
119 120

    /// Nnmber of callsites in the callsite tables
121
    callsite_count: AtomicUsize
122
}
qinsoon's avatar
vm.rs  
qinsoon committed
123

124
unsafe impl rodal::Dump for VM {
125
    fn dump<D: ?Sized + rodal::Dumper>(&self, dumper: &mut D) {
126 127 128 129 130 131 132 133 134 135 136 137 138 139
        dumper.debug_record("VM", "dump");

        dumper.dump_object(&self.next_id);
        dumper.dump_object(&self.id_name_map);
        dumper.dump_object(&self.name_id_map);
        dumper.dump_object(&self.types);
        dumper.dump_object(&self.backend_type_info);
        dumper.dump_object(&self.constants);
        dumper.dump_object(&self.globals);
        dumper.dump_object(&self.func_sigs);
        dumper.dump_object(&self.funcs);
        dumper.dump_object(&self.primordial);
        dumper.dump_object(&self.vm_options);
        dumper.dump_object(&self.compiled_funcs);
140
        dumper.dump_object(&self.callsite_table);
141

142 143
        // Dump empty maps so that we can safely read and modify them once loaded
        dumper.dump_padding(&self.global_locations);
144
        dumper.dump_object_here(&RwLock::new(
145
            rodal::EmptyHashMap::<MuID, ValueLocation>::new()
146
        ));
147 148

        dumper.dump_padding(&self.func_vers);
149
        dumper.dump_object_here(&RwLock::new(
150
            rodal::EmptyHashMap::<MuID, RwLock<MuFunctionVersion>>::new()
151
        ));
152 153

        dumper.dump_padding(&self.aot_pending_funcref_store);
154
        dumper.dump_object_here(&RwLock::new(
155
            rodal::EmptyHashMap::<Address, ValueLocation>::new()
156
        ));
157 158

        // Dump an emepty hashmap for the other hashmaps
159
        dumper.dump_padding(&self.compiled_callsite_table);
160
        dumper.dump_object_here(&RwLock::new(
161
            rodal::EmptyHashMap::<Address, CompiledCallsite>::new()
162
        ));
163
        dumper.dump_object(&self.callsite_count);
164
    }
165
}
166

qinsoon's avatar
[wip]  
qinsoon committed
167 168 169 170
/// a fake funcref to store for AOT when client tries to store a funcref via API
//  For AOT scenario, when client tries to store funcref to the heap, the store
//  happens before we have an actual address for the function so we store a fake
//  funcref and when generating boot image, we fix the funcref with a relocatable symbol
171
const PENDING_FUNCREF: u64 = {
qinsoon's avatar
[wip]  
qinsoon committed
172 173 174
    use std::u64;
    u64::MAX
};
175

qinsoon's avatar
[wip]  
qinsoon committed
176
/// a macro to generate int8/16/32/64 from/to API calls
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
macro_rules! gen_handle_int {
    ($fn_from: ident, $fn_to: ident, $int_ty: ty) => {
        pub fn $fn_from (&self, num: $int_ty, len: BitSize) -> APIHandleResult {
            let handle_id = self.next_id();
            self.new_handle (APIHandle {
                id: handle_id,
                v: APIHandleValue::Int(num as u64, len)
            })
        }

        pub fn $fn_to (&self, handle: APIHandleArg) -> $int_ty {
            handle.v.as_int() as $int_ty
        }
    }
}

193
impl<'a> VM {
qinsoon's avatar
[wip]  
qinsoon committed
194
    /// creates a VM with default options
qinsoon's avatar
qinsoon committed
195
    pub fn new() -> VM {
qinsoon's avatar
qinsoon committed
196 197 198
        VM::new_internal(VMOptions::default())
    }

qinsoon's avatar
[wip]  
qinsoon committed
199
    /// creates a VM with specified options
qinsoon's avatar
qinsoon committed
200 201 202 203
    pub fn new_with_opts(str: &str) -> VM {
        VM::new_internal(VMOptions::init(str))
    }

qinsoon's avatar
[wip]  
qinsoon committed
204
    /// internal function to create a VM with options
205
    #[cfg(not(feature = "sel4-rumprun"))]
qinsoon's avatar
qinsoon committed
206 207
    fn new_internal(options: VMOptions) -> VM {
        VM::start_logging(options.flag_log_level);
qinsoon's avatar
qinsoon committed
208

qinsoon's avatar
qinsoon committed
209
        let ret = VM {
210
            next_id: ATOMIC_USIZE_INIT,
qinsoon's avatar
qinsoon committed
211
            vm_options: options,
qinsoon's avatar
qinsoon committed
212 213
            id_name_map: RwLock::new(HashMap::new()),
            name_id_map: RwLock::new(HashMap::new()),
qinsoon's avatar
qinsoon committed
214 215
            constants: RwLock::new(HashMap::new()),
            types: RwLock::new(HashMap::new()),
qinsoon's avatar
qinsoon committed
216
            backend_type_info: RwLock::new(HashMap::new()),
qinsoon's avatar
qinsoon committed
217
            globals: RwLock::new(HashMap::new()),
218
            global_locations: RwLock::new(hashmap!{}),
qinsoon's avatar
qinsoon committed
219
            func_sigs: RwLock::new(HashMap::new()),
220
            func_vers: RwLock::new(HashMap::new()),
qinsoon's avatar
qinsoon committed
221
            funcs: RwLock::new(HashMap::new()),
qinsoon's avatar
qinsoon committed
222
            compiled_funcs: RwLock::new(HashMap::new()),
223
            callsite_table: RwLock::new(HashMap::new()),
224
            primordial: RwLock::new(None),
225
            aot_pending_funcref_store: RwLock::new(HashMap::new()),
226
            compiled_callsite_table: RwLock::new(HashMap::new()),
227
            callsite_count: ATOMIC_USIZE_INIT
228
        };
qinsoon's avatar
qinsoon committed
229

230
        // insert all internal types
qinsoon's avatar
qinsoon committed
231 232 233 234 235 236
        {
            let mut types = ret.types.write().unwrap();
            for ty in INTERNAL_TYPES.iter() {
                types.insert(ty.id(), ty.clone());
            }
        }
qinsoon's avatar
qinsoon committed
237

qinsoon's avatar
[wip]  
qinsoon committed
238
        // starts allocating ID from USER_ID_START
239
        ret.next_id.store(USER_ID_START, Ordering::Relaxed);
qinsoon's avatar
qinsoon committed
240

241 242 243
        // init types
        types::init_types();

qinsoon's avatar
[wip]  
qinsoon committed
244
        // init runtime
245
        ret.init_runtime();
qinsoon's avatar
qinsoon committed
246

247 248
        ret
    }
249

250 251 252 253 254
    /// internal function to create a VM with options for sel4-rumprun
    /// default memory sizes are different from other platforms
    #[cfg(feature = "sel4-rumprun")]
    fn new_internal(options: VMOptions) -> VM {
        VM::start_logging(options.flag_log_level);
255

256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
        let mut ret = VM {
            next_id: ATOMIC_USIZE_INIT,
            vm_options: options,
            id_name_map: RwLock::new(HashMap::new()),
            name_id_map: RwLock::new(HashMap::new()),
            constants: RwLock::new(HashMap::new()),
            types: RwLock::new(HashMap::new()),
            backend_type_info: RwLock::new(HashMap::new()),
            globals: RwLock::new(HashMap::new()),
            global_locations: RwLock::new(hashmap!{}),
            func_sigs: RwLock::new(HashMap::new()),
            func_vers: RwLock::new(HashMap::new()),
            funcs: RwLock::new(HashMap::new()),
            compiled_funcs: RwLock::new(HashMap::new()),
            callsite_table: RwLock::new(HashMap::new()),
            primordial: RwLock::new(None),
            aot_pending_funcref_store: RwLock::new(HashMap::new()),
            compiled_callsite_table: RwLock::new(HashMap::new()),
            callsite_count: ATOMIC_USIZE_INIT
        };
276

277 278
        // currently, the default sizes don't work on sel4-rumprun platform
        // this is due to memory allocation size limitations
279 280 281
        ret.vm_options.flag_gc_immixspace_size = 1 << 19;
        ret.vm_options.flag_gc_lospace_size = 1 << 19;

282 283 284 285 286 287 288
        // insert all internal types
        {
            let mut types = ret.types.write().unwrap();
            for ty in INTERNAL_TYPES.iter() {
                types.insert(ty.id(), ty.clone());
            }
        }
289

290 291
        // starts allocating ID from USER_ID_START
        ret.next_id.store(USER_ID_START, Ordering::Relaxed);
292

293 294
        // init types
        types::init_types();
295

296 297
        // init runtime
        ret.init_runtime();
298

299 300
        ret
    }
qinsoon's avatar
qinsoon committed
301

qinsoon's avatar
[wip]  
qinsoon committed
302
    /// initializes runtime
303
    fn init_runtime(&self) {
qinsoon's avatar
qinsoon committed
304 305 306
        // init gc
        {
            let ref options = self.vm_options;
307 308 309 310
            gc::gc_init(
                options.flag_gc_immixspace_size,
                options.flag_gc_lospace_size,
                options.flag_gc_nthreads,
311
                !options.flag_gc_disable_collection
312
            );
qinsoon's avatar
qinsoon committed
313 314 315
        }
    }

qinsoon's avatar
[wip]  
qinsoon committed
316
    /// starts logging based on MuLogLevel flag
qinsoon's avatar
qinsoon committed
317
    fn start_logging(level: MuLogLevel) {
318
        use std::env;
qinsoon's avatar
qinsoon committed
319
        match level {
320
            MuLogLevel::None => {}
qinsoon's avatar
qinsoon committed
321
            MuLogLevel::Error => VM::start_logging_internal(LogLevel::Error),
322 323
            MuLogLevel::Warn => VM::start_logging_internal(LogLevel::Warn),
            MuLogLevel::Info => VM::start_logging_internal(LogLevel::Info),
qinsoon's avatar
qinsoon committed
324 325
            MuLogLevel::Debug => VM::start_logging_internal(LogLevel::Debug),
            MuLogLevel::Trace => VM::start_logging_internal(LogLevel::Trace),
326 327 328 329 330
            MuLogLevel::Env => {
                match env::var("MU_LOG_LEVEL") {
                    Ok(s) => VM::start_logging(MuLogLevel::from_string(s)),
                    _ => {} // Don't log
                }
331
            }
qinsoon's avatar
qinsoon committed
332
        }
qinsoon's avatar
qinsoon committed
333 334
    }

qinsoon's avatar
[wip]  
qinsoon committed
335
    /// starts trace-level logging
qinsoon's avatar
qinsoon committed
336
    pub fn start_logging_trace() {
qinsoon's avatar
qinsoon committed
337 338
        VM::start_logging_internal(LogLevel::Trace)
    }
qinsoon's avatar
[wip]  
qinsoon committed
339 340

    /// starts logging based on MU_LOG_LEVEL environment variable
341 342 343
    pub fn start_logging_env() {
        VM::start_logging(MuLogLevel::Env)
    }
qinsoon's avatar
qinsoon committed
344

qinsoon's avatar
[wip]  
qinsoon committed
345 346
    /// starts logging based on Rust's LogLevel
    /// (this function actually initializes logger and deals with error)
qinsoon's avatar
qinsoon committed
347
    fn start_logging_internal(level: LogLevel) {
348 349 350 351
        use stderrlog;

        let verbose = match level {
            LogLevel::Error => 0,
352 353
            LogLevel::Warn => 1,
            LogLevel::Info => 2,
354
            LogLevel::Debug => 3,
355
            LogLevel::Trace => 4
356
        };
qinsoon's avatar
qinsoon committed
357

358
        match stderrlog::new().verbosity(verbose).init() {
359
            Ok(()) => { info!("logger initialized") }
360 361 362 363 364 365
            Err(e) => {
                error!(
                    "failed to init logger, probably already initialized: {:?}",
                    e
                )
            }
qinsoon's avatar
qinsoon committed
366 367
        }
    }
368

qinsoon's avatar
[wip]  
qinsoon committed
369 370
    /// adds an exception callsite and catch block
    /// (later we will use this info to build an exception table for unwinding use)
371 372
    pub fn add_exception_callsite(&self, callsite: Callsite, fv: MuID) {
        let mut table = self.callsite_table.write().unwrap();
373 374

        if table.contains_key(&fv) {
375
            table.get_mut(&fv).unwrap().push(callsite);
376
        } else {
377
            table.insert(fv, vec![callsite]);
378
        };
379 380
        // TODO: do wee need a stronger ordering??
        self.callsite_count.fetch_add(1, Ordering::Relaxed);
381 382
    }

qinsoon's avatar
[wip]  
qinsoon committed
383 384
    /// resumes persisted VM. Ideally the VM should be back to the status when we start
    /// persisting it except a few fields that we do not want to persist.
385
    pub fn resume_vm(dumped_vm: *mut Arc<VM>) -> Arc<VM> {
qinsoon's avatar
[wip]  
qinsoon committed
386
        // load the vm back
387
        let vm = unsafe { rodal::load_asm_pointer_move(dumped_vm) };
qinsoon's avatar
[wip]  
qinsoon committed
388 389

        // initialize runtime
390
        vm.init_runtime();
391

qinsoon's avatar
[wip]  
qinsoon committed
392
        // construct exception table
393
        vm.build_callsite_table();
qinsoon's avatar
[wip]  
qinsoon committed
394

395 396 397
        // restore gc types
        {
            let type_info_guard = vm.backend_type_info.read().unwrap();
398 399
            let mut type_info_vec: Vec<Box<BackendType>> =
                type_info_guard.values().map(|x| x.clone()).collect();
400 401 402 403 404 405 406 407 408 409 410 411 412 413
            type_info_vec.sort_by(|a, b| a.gc_type.id.cmp(&b.gc_type.id));

            let mut expect_id = 0;
            for ty_info in type_info_vec.iter() {
                use runtime::mm;

                let ref gc_type = ty_info.gc_type;

                if gc_type.id != expect_id {
                    debug_assert!(expect_id < gc_type.id);

                    while expect_id < gc_type.id {
                        use runtime::mm::common::gctype::GCType;

qinsoon's avatar
qinsoon committed
414
                        mm::add_gc_type(GCType::new_noreftype(0, 8));
415 416 417 418 419 420 421 422 423 424 425
                        expect_id += 1;
                    }
                }

                // now expect_id == gc_type.id
                debug_assert!(expect_id == gc_type.id);

                mm::add_gc_type(gc_type.as_ref().clone());
                expect_id += 1;
            }
        }
426
        // construct exception table
427
        vm.build_callsite_table();
428 429
        vm
    }
430

qinsoon's avatar
[wip]  
qinsoon committed
431 432 433 434 435
    /// builds a succinct exception table for fast query during exception unwinding
    /// We need this step because for AOT compilation, we do not know symbol address at compile,
    /// and resolving symbol address during exception handling is expensive. Thus when boot image
    /// gets executed, we first resolve symbols and store the results in another table for fast
    /// query.
436 437
    pub fn build_callsite_table(&self) {
        let callsite_table = self.callsite_table.read().unwrap();
438
        let compiled_funcs = self.compiled_funcs.read().unwrap();
439
        let mut compiled_callsite_table = self.compiled_callsite_table.write().unwrap();
440 441
        // TODO: Use a different ordering?
        compiled_callsite_table.reserve(self.callsite_count.load(Ordering::Relaxed));
442 443 444 445
        for (fv, callsite_list) in callsite_table.iter() {
            let compiled_func = compiled_funcs.get(fv).unwrap().read().unwrap();
            let callee_saved_table = Arc::new(compiled_func.frame.callee_saved.clone());
            for callsite in callsite_list.iter() {
446 447 448 449 450
                compiled_callsite_table.insert(
                    resolve_symbol(callsite.name.clone()),
                    CompiledCallsite::new(
                        &callsite,
                        compiled_func.func_ver_id,
451 452
                        callee_saved_table.clone()
                    )
453
                );
454 455
            }
        }
456
    }
qinsoon's avatar
[wip]  
qinsoon committed
457 458

    /// returns a valid ID for use next
459
    pub fn next_id(&self) -> MuID {
460 461 462
        // This only needs to be atomic, and does not need to be a synchronisation operation. The
        // only requirement for IDs is that all IDs obtained from `next_id()` are different. So
        // `Ordering::Relaxed` is sufficient.
463
        self.next_id.fetch_add(1, Ordering::Relaxed)
464
    }
qinsoon's avatar
[wip]  
qinsoon committed
465

466 467
    /// are we doing AOT compilation? (feature = aot when building Zebu)
    pub fn is_doing_aot(&self) -> bool {
468
        return cfg!(feature = "aot");
469
    }
470 471 472

    /// are we doing JIT compilation? (feature = jit when building Zebu)
    pub fn is_doing_jit(&self) -> bool {
473
        return cfg!(feature = "jit");
qinsoon's avatar
qinsoon committed
474
    }
475 476

    /// informs VM about a client-supplied name
477
    pub fn set_name(&self, entity: &MuEntity) {
qinsoon's avatar
qinsoon committed
478
        let id = entity.id();
479
        let name = entity.name();
480

qinsoon's avatar
qinsoon committed
481
        let mut map = self.id_name_map.write().unwrap();
482
        map.insert(id, name.clone());
483

qinsoon's avatar
qinsoon committed
484 485 486
        let mut map2 = self.name_id_map.write().unwrap();
        map2.insert(name, id);
    }
qinsoon's avatar
vm.rs  
qinsoon committed
487 488 489 490 491

    /// returns Mu ID for a client-supplied name
    /// This function should only used by client, 'name' used internally may be slightly different
    /// due to removal of some special symbols in the MuName. See name_check() in ir.rs
    pub fn id_of(&self, name: &str) -> MuID {
qinsoon's avatar
qinsoon committed
492
        let map = self.name_id_map.read().unwrap();
493 494
        match map.get(name) {
            Some(id) => *id,
495
            None => panic!("cannot find id for name: {}", name)
496
        }
497
    }
498

qinsoon's avatar
vm.rs  
qinsoon committed
499 500 501
    /// returns the client-supplied Mu name for Mu ID
    /// This function should only used by client, 'name' used internally may be slightly different
    /// due to removal of some special symbols in the MuName. See name_check() in ir.rs
qinsoon's avatar
qinsoon committed
502 503
    pub fn name_of(&self, id: MuID) -> MuName {
        let map = self.id_name_map.read().unwrap();
504
        map.get(&id).unwrap().clone()
qinsoon's avatar
qinsoon committed
505
    }
qinsoon's avatar
vm.rs  
qinsoon committed
506 507

    /// declares a constant
508
    pub fn declare_const(&self, entity: MuEntityHeader, ty: P<MuType>, val: Constant) -> P<Value> {
509 510 511
        let ret = P(Value {
            hdr: entity,
            ty: ty,
512
            v: Value_::Constant(val)
513
        });
514

qinsoon's avatar
vm.rs  
qinsoon committed
515
        let mut constants = self.constants.write().unwrap();
516
        self.declare_const_internal(&mut constants, ret.id(), ret.clone());
517

qinsoon's avatar
qinsoon committed
518 519
        ret
    }
520

qinsoon's avatar
vm.rs  
qinsoon committed
521
    /// adds a constant to the map (already acquired lock)
522 523 524 525
    fn declare_const_internal(
        &self,
        map: &mut RwLockWriteGuard<HashMap<MuID, P<Value>>>,
        id: MuID,
526
        val: P<Value>
527
    ) {
528 529
        debug_assert!(!map.contains_key(&id));

qinsoon's avatar
vm.rs  
qinsoon committed
530
        info!("declare const #{} = {}", id, val);
531 532
        map.insert(id, val);
    }
qinsoon's avatar
vm.rs  
qinsoon committed
533 534

    /// gets the constant P<Value> for a given Mu ID, panics if there is no type with the ID
535 536 537 538
    pub fn get_const(&self, id: MuID) -> P<Value> {
        let const_lock = self.constants.read().unwrap();
        match const_lock.get(&id) {
            Some(ret) => ret.clone(),
539
            None => panic!("cannot find const #{}", id)
540 541
        }
    }
542

qinsoon's avatar
vm.rs  
qinsoon committed
543 544
    /// allocates memory for a constant that needs to be put in memory
    /// For AOT, we simply create a label for it, and let code emitter allocate the memory
545 546 547
    #[cfg(feature = "aot")]
    pub fn allocate_const(&self, val: P<Value>) -> ValueLocation {
        let id = val.id();
548
        let name = format!("CONST_{}_{}", id, val.name());
549 550 551

        ValueLocation::Relocatable(backend::RegGroup::GPR, name)
    }
qinsoon's avatar
vm.rs  
qinsoon committed
552 553

    /// declares a global
554
    pub fn declare_global(&self, entity: MuEntityHeader, ty: P<MuType>) -> P<Value> {
qinsoon's avatar
vm.rs  
qinsoon committed
555
        // create iref value for the global
556
        let global = P(Value {
557
            hdr: entity,
558 559
            ty: self.declare_type(
                MuEntityHeader::unnamed(self.next_id()),
560
                MuType_::iref(ty.clone())
561
            ),
562
            v: Value_::Global(ty)
qinsoon's avatar
qinsoon committed
563
        });
qinsoon's avatar
vm.rs  
qinsoon committed
564

qinsoon's avatar
qinsoon committed
565
        let mut globals = self.globals.write().unwrap();
566
        let mut global_locs = self.global_locations.write().unwrap();
567
        self.declare_global_internal(&mut globals, &mut global_locs, global.id(), global.clone());
568

qinsoon's avatar
qinsoon committed
569
        global
qinsoon's avatar
qinsoon committed
570
    }
571

qinsoon's avatar
vm.rs  
qinsoon committed
572
    /// adds the global to the map (already acquired lock), and allocates memory for it
573 574 575 576
    fn declare_global_internal(
        &self,
        globals: &mut RwLockWriteGuard<HashMap<MuID, P<Value>>>,
        global_locs: &mut RwLockWriteGuard<HashMap<MuID, ValueLocation>>,
577
        id: MuID,
578
        val: P<Value>
579 580 581 582 583
    ) {
        self.declare_global_internal_no_alloc(globals, id, val.clone());
        self.alloc_global(global_locs, id, val);
    }

qinsoon's avatar
vm.rs  
qinsoon committed
584
    /// adds the global to the map (already acquired lock)
qinsoon's avatar
qinsoon committed
585 586
    /// when bulk declaring, we hold locks for everything, we cannot resolve backend type
    /// and do alloc so we add globals to the map, and then allocate them later
587 588 589
    fn declare_global_internal_no_alloc(
        &self,
        globals: &mut RwLockWriteGuard<HashMap<MuID, P<Value>>>,
590
        id: MuID,
591
        val: P<Value>
592 593 594 595 596 597 598
    ) {
        debug_assert!(!globals.contains_key(&id));

        info!("declare global #{} = {}", id, val);
        globals.insert(id, val.clone());
    }

qinsoon's avatar
vm.rs  
qinsoon committed
599
    /// allocates memory for a global cell
600 601 602
    fn alloc_global(
        &self,
        global_locs: &mut RwLockWriteGuard<HashMap<MuID, ValueLocation>>,
603
        id: MuID,
604
        val: P<Value>
605
    ) {
606
        let backend_ty = self.get_backend_type_info(val.ty.get_referent_ty().unwrap().id());
607
        let loc = gc::allocate_global(val, backend_ty);
qinsoon's avatar
vm.rs  
qinsoon committed
608
        trace!("allocate global #{} as {}", id, loc);
609 610
        global_locs.insert(id, loc);
    }
qinsoon's avatar
vm.rs  
qinsoon committed
611 612

    /// declares a type
613
    pub fn declare_type(&self, entity: MuEntityHeader, ty: MuType_) -> P<MuType> {
614
        let ty = P(MuType { hdr: entity, v: ty });
615

qinsoon's avatar
vm.rs  
qinsoon committed
616
        let mut types = self.types.write().unwrap();
617
        self.declare_type_internal(&mut types, ty.id(), ty.clone());
618

qinsoon's avatar
qinsoon committed
619 620
        ty
    }
621

qinsoon's avatar
vm.rs  
qinsoon committed
622
    /// adds the type to the map (already acquired lock)
623 624 625 626
    fn declare_type_internal(
        &self,
        types: &mut RwLockWriteGuard<HashMap<MuID, P<MuType>>>,
        id: MuID,
627
        ty: P<MuType>
628
    ) {
629 630 631
        debug_assert!(!types.contains_key(&id));

        types.insert(id, ty.clone());
qinsoon's avatar
vm.rs  
qinsoon committed
632
        info!("declare type #{} = {}", id, ty);
qinsoon's avatar
qinsoon committed
633

qinsoon's avatar
vm.rs  
qinsoon committed
634
        // for struct/hybrid, also adds to struct/hybrid tag map
qinsoon's avatar
qinsoon committed
635 636 637 638
        if ty.is_struct() {
            let tag = ty.get_struct_hybrid_tag().unwrap();
            let struct_map_guard = STRUCT_TAG_MAP.read().unwrap();
            let struct_inner = struct_map_guard.get(&tag).unwrap();
639
            trace!("  {}", struct_inner);
qinsoon's avatar
qinsoon committed
640 641 642 643
        } else if ty.is_hybrid() {
            let tag = ty.get_struct_hybrid_tag().unwrap();
            let hybrid_map_guard = HYBRID_TAG_MAP.read().unwrap();
            let hybrid_inner = hybrid_map_guard.get(&tag).unwrap();
644
            trace!("  {}", hybrid_inner);
qinsoon's avatar
qinsoon committed
645
        }
646
    }
qinsoon's avatar
vm.rs  
qinsoon committed
647 648

    /// gets the type for a given Mu ID, panics if there is no type with the ID
649 650 651 652
    pub fn get_type(&self, id: MuID) -> P<MuType> {
        let type_lock = self.types.read().unwrap();
        match type_lock.get(&id) {
            Some(ret) => ret.clone(),
653
            None => panic!("cannot find type #{}", id)
654
        }
655
    }
qinsoon's avatar
vm.rs  
qinsoon committed
656 657

    /// declares a function signature
658 659 660 661
    pub fn declare_func_sig(
        &self,
        entity: MuEntityHeader,
        ret_tys: Vec<P<MuType>>,
662
        arg_tys: Vec<P<MuType>>
663 664 665 666
    ) -> P<MuFuncSig> {
        let ret = P(MuFuncSig {
            hdr: entity,
            ret_tys: ret_tys,
667
            arg_tys: arg_tys
668
        });
669 670

        let mut func_sigs = self.func_sigs.write().unwrap();
671
        self.declare_func_sig_internal(&mut func_sigs, ret.id(), ret.clone());
672

qinsoon's avatar
qinsoon committed
673 674
        ret
    }
675

qinsoon's avatar
vm.rs  
qinsoon committed
676
    /// adds a function signature to the map (already acquired lock)
677 678 679 680
    fn declare_func_sig_internal(
        &self,
        sigs: &mut RwLockWriteGuard<HashMap<MuID, P<MuFuncSig>>>,
        id: MuID,
681
        sig: P<MuFuncSig>
682
    ) {
683 684 685 686 687
        debug_assert!(!sigs.contains_key(&id));

        info!("declare func sig #{} = {}", id, sig);
        sigs.insert(id, sig);
    }
qinsoon's avatar
vm.rs  
qinsoon committed
688 689

    /// gets the function signature for a given ID, panics if there is no func sig with the ID
690 691 692 693
    pub fn get_func_sig(&self, id: MuID) -> P<MuFuncSig> {
        let func_sig_lock = self.func_sigs.read().unwrap();
        match func_sig_lock.get(&id) {
            Some(ret) => ret.clone(),
694
            None => panic!("cannot find func sig #{}", id)
695 696
        }
    }
qinsoon's avatar
vm.rs  
qinsoon committed
697 698

    /// declares a Mu function
699
    pub fn declare_func(&self, func: MuFunction) {
qinsoon's avatar
qinsoon committed
700
        let mut funcs = self.funcs.write().unwrap();
701 702 703 704

        self.declare_func_internal(&mut funcs, func.id(), func);
    }

qinsoon's avatar
vm.rs  
qinsoon committed
705
    /// adds a Mu function to the map (already acquired lock)
706 707 708 709
    fn declare_func_internal(
        &self,
        funcs: &mut RwLockWriteGuard<HashMap<MuID, RwLock<MuFunction>>>,
        id: MuID,
710
        func: MuFunction
711
    ) {
712 713 714 715
        debug_assert!(!funcs.contains_key(&id));

        info!("declare func #{} = {}", id, func);
        funcs.insert(id, RwLock::new(func));
716
    }
717

qinsoon's avatar
vm.rs  
qinsoon committed
718 719 720 721
    /// gets the function name for a function (by ID), panics if there is no function with the ID
    /// Note this name is the internal name, which is different than
    /// the client-supplied name from vm.name_of()
    pub fn get_name_for_func(&self, id: MuID) -> MuName {
722 723
        let funcs_lock = self.funcs.read().unwrap();
        match funcs_lock.get(&id) {
724
            Some(func) => func.read().unwrap().name(),
725
            None => panic!("cannot find name for Mu function #{}")
726 727
        }
    }
qinsoon's avatar
vm.rs  
qinsoon committed
728

qinsoon's avatar
qinsoon committed
729 730
    /// gets the function signature for a function (by ID),
    /// panics if there is no function with the ID
qinsoon's avatar
vm.rs  
qinsoon committed
731
    pub fn get_sig_for_func(&self, id: MuID) -> P<MuFuncSig> {
732 733 734
        let funcs_lock = self.funcs.read().unwrap();
        match funcs_lock.get(&id) {
            Some(func) => func.read().unwrap().sig.clone(),
735
            None => panic!("cannot find Mu function #{}", id)
736
        }
qinsoon's avatar
vm.rs  
qinsoon committed
737 738 739 740 741 742 743 744 745 746
    }

    /// gets the current function version for a Mu function (by ID)
    /// returns None if the function does not exist, or no version is defined for the function
    pub fn get_cur_version_for_func(&self, fid: MuID) -> Option<MuID> {
        let funcs_guard = self.funcs.read().unwrap();
        match funcs_guard.get(&fid) {
            Some(rwlock_func) => {
                let func_guard = rwlock_func.read().unwrap();
                func_guard.cur_ver
747
            }
748
            None => None
qinsoon's avatar
vm.rs  
qinsoon committed
749 750 751 752 753 754
        }
    }

    /// gets the address as ValueLocation of a Mu function (by ID)
    pub fn get_address_for_func(&self, func_id: MuID) -> ValueLocation {
        let funcs = self.funcs.read().unwrap();
755
        let func: &MuFunction = &funcs.get(&func_id).unwrap().read().unwrap();
qinsoon's avatar
vm.rs  
qinsoon committed
756 757 758 759

        if self.is_doing_jit() {
            unimplemented!()
        } else {
760
            ValueLocation::Relocatable(backend::RegGroup::GPR, func.name())
qinsoon's avatar
vm.rs  
qinsoon committed
761 762 763 764
        }
    }

    /// defines a function version
765
    pub fn define_func_version(&self, func_ver: MuFunctionVersion) {
qinsoon's avatar
qinsoon committed
766
        info!("define function version {}", func_ver);
qinsoon's avatar
vm.rs  
qinsoon committed
767
        // add this funcver to map
qinsoon's avatar
qinsoon committed
768
        let func_ver_id = func_ver.id();
769 770
        {
            let mut func_vers = self.func_vers.write().unwrap();
qinsoon's avatar
qinsoon committed
771
            func_vers.insert(func_ver_id, RwLock::new(func_ver));
772
        }
773

774 775
        // acquire a reference to the func_ver
        let func_vers = self.func_vers.read().unwrap();
qinsoon's avatar
qinsoon committed
776
        let func_ver = func_vers.get(&func_ver_id).unwrap().write().unwrap();
777

qinsoon's avatar
vm.rs  
qinsoon committed
778
        // change current version of the function to new version (obsolete old versions)
779
        let funcs = self.funcs.read().unwrap();
qinsoon's avatar
qinsoon committed
780 781
        // it should be declared before defining
        debug_assert!(funcs.contains_key(&func_ver.func_id));
qinsoon's avatar
qinsoon committed
782
        let mut func = funcs.get(&func_ver.func_id).unwrap().write().unwrap();
783

784
        func.new_version(func_ver.id());
qinsoon's avatar
vm.rs  
qinsoon committed
785 786 787 788 789

        if self.is_doing_jit() {
            // redefinition may happen, we need to check
            unimplemented!()
        }
790
    }
791

qinsoon's avatar
vm.rs  
qinsoon committed
792 793 794 795
    /// adds a new bundle into VM.
    /// This function will drain the contents of all arguments. Ideally, this function should
    /// happen atomically. e.g. The client should not see a new type added without also seeing
    /// a new function added.
796 797 798 799 800 801 802 803 804
    pub fn declare_many(
        &self,
        new_id_name_map: &mut HashMap<MuID, MuName>,
        new_types: &mut HashMap<MuID, P<MuType>>,
        new_func_sigs: &mut HashMap<MuID, P<MuFuncSig>>,
        new_constants: &mut HashMap<MuID, P<Value>>,
        new_globals: &mut HashMap<MuID, P<Value>>,
        new_funcs: &mut HashMap<MuID, Box<MuFunction>>,
        new_func_vers: &mut HashMap<MuID, Box<MuFunctionVersion>>,
805
        arc_vm: Arc<VM>
806
    ) {
807 808
        // Make sure other components, if ever acquiring multiple locks at the same time, acquire
        // them in this order, to prevent deadlock.
809 810 811
        {
            let mut id_name_map = self.id_name_map.write().unwrap();
            let mut name_id_map = self.name_id_map.write().unwrap();
812 813 814 815 816 817
            let mut types = self.types.write().unwrap();
            let mut constants = self.constants.write().unwrap();
            let mut globals = self.globals.write().unwrap();
            let mut func_sigs = self.func_sigs.write().unwrap();
            let mut funcs = self.funcs.write().unwrap();
            let mut func_vers = self.func_vers.write().unwrap();
818

819 820 821 822
            for (id, name) in new_id_name_map.drain() {
                id_name_map.insert(id, name.clone());
                name_id_map.insert(name, id);
            }
823

824 825 826
            for (id, obj) in new_types.drain() {
                self.declare_type_internal(&mut types, id, obj);
            }
827

828 829 830
            for (id, obj) in new_constants.drain() {
                self.declare_const_internal(&mut constants, id, obj);
            }
831

832
            for (id, obj) in new_globals.drain() {
qinsoon's avatar
qinsoon committed
833 834
                // we bulk allocate later
                // (since we are holding all the locks, we cannot find ty info)
835 836
                self.declare_global_internal_no_alloc(&mut globals, id, obj);
            }
837

838 839 840
            for (id, obj) in new_func_sigs.drain() {
                self.declare_func_sig_internal(&mut func_sigs, id, obj);
            }
841

842 843 844
            for (id, obj) in new_funcs.drain() {
                self.declare_func_internal(&mut funcs, id, *obj);
            }
845

846 847 848
            for (id, obj) in new_func_vers.drain() {
                let func_id = obj.func_id;
                func_vers.insert(id, RwLock::new(*obj));
849

850 851 852 853
                {
                    trace!("Adding funcver {} as a version of {}...", id, func_id);
                    let func = funcs.get_mut(&func_id).unwrap();
                    func.write().unwrap().new_version(id);
854 855 856 857 858 859
                    trace!(
                        "Added funcver {} as a version of {} {:?}.",
                        id,
                        func_id,
                        func
                    );
860
                }
861 862 863
            }
        }
        // Locks released here
864 865 866 867 868 869 870

        // allocate all the globals defined
        {
            let globals = self.globals.read().unwrap();
            let mut global_locs = self.global_locations.write().unwrap();

            // make sure current thread has allocator
871 872
            let created =
                unsafe { MuThread::current_thread_as_mu_thread(Address::zero(), arc_vm.clone()) };
873 874 875 876

            for (id, global) in globals.iter() {
                self.alloc_global(&mut global_locs, *id, global.clone());
            }
877 878

            if created {
879
                unsafe { MuThread::cleanup_current_mu_thread() };
880
            }
881
        }
882
    }
qinsoon's avatar
vm.rs  
qinsoon committed
883

qinsoon's avatar
qinsoon committed
884 885
    /// informs the VM of a newly compiled function
    /// (the function and funcver should already be declared before this call)
886
    pub fn add_compiled_func(&self, func: CompiledFunction) {
qinsoon's avatar
qinsoon committed
887
        debug_assert!(self.funcs.read().unwrap().contains_key(&func.func_id));
888 889 890 891 892 893
        debug_assert!(
            self.func_vers
                .read()
                .unwrap()
                .contains_key(&func.func_ver_id)
        );
qinsoon's avatar
qinsoon committed
894

895 896 897 898
        self.compiled_funcs
            .write()
            .unwrap()
            .insert(func.func_ver_id, RwLock::new(func));
qinsoon's avatar
qinsoon committed
899
    }
qinsoon's avatar
vm.rs  
qinsoon committed
900 901

    /// gets the backend/storage type for a given Mu type (by ID)
902
    pub fn get_backend_type_info(&self, tyid: MuID) -> Box<BackendType> {
qinsoon's avatar
vm.rs  
qinsoon committed
903
        // if we already resolved this type, return the BackendType