From b0f2f07c978f64ce8e2f09e694e77a9b50f45e9e Mon Sep 17 00:00:00 2001 From: masnagam Date: Thu, 7 Nov 2024 20:42:51 +0900 Subject: [PATCH 1/5] refactor(jsruntime): add the types module --- Cargo.lock | 23 +- libs/jsruntime/Cargo.toml | 1 + libs/jsruntime/src/lib.rs | 20 +- libs/jsruntime/src/llvmir/bridge.hh | 75 +--- libs/jsruntime/src/llvmir/bridge.rs | 377 +++++------------- libs/jsruntime/src/llvmir/compiler.hh | 27 +- libs/jsruntime/src/llvmir/compiler/mod.rs | 5 +- libs/jsruntime/src/llvmir/mod.rs | 6 +- libs/jsruntime/src/tasklet.rs | 14 +- libs/jsruntime/src/types.rs | 274 +++++++++++++ .../jsruntime/tests/collect_evaluate_tests.js | 26 +- .../tests/modules/await_in_arguments.mjs | 4 +- .../tests/scripts/let_declaration.js | 2 +- libs/jsruntime/tests/scripts/null.js | 2 +- libs/jsruntime/tests/scripts/number.js | 4 +- libs/jsruntime/tests/scripts/ternary.js | 4 +- .../tests/scripts/ternary_mixed_types.js | 2 +- libs/jsruntime/tests/scripts/throw_null.js | 2 +- .../tests/scripts/throw_undefined.js | 2 +- libs/jsruntime/tests/scripts/to_numeric.js | 2 +- libs/jsruntime/tests/scripts/unary_minus.js | 6 +- libs/jsruntime/tests/scripts/unary_plus.js | 6 +- libs/jsruntime/tests/scripts/undefined.js | 2 +- libs/jsruntime/tests/scripts/void.js | 18 +- 24 files changed, 478 insertions(+), 426 deletions(-) create mode 100644 libs/jsruntime/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 9a1c38ad..2a1c09a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,9 +49,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.17" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -64,9 +64,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" @@ -599,9 +599,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "flagset" @@ -702,9 +702,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "heck" @@ -890,7 +890,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "serde", ] @@ -1016,6 +1016,7 @@ dependencies = [ "jsparser", "logging", "rustc-hash 2.0.0", + "static_assertions", "thiserror", ] @@ -1074,9 +1075,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libloading" diff --git a/libs/jsruntime/Cargo.toml b/libs/jsruntime/Cargo.toml index 52e94141..51ef7452 100644 --- a/libs/jsruntime/Cargo.toml +++ b/libs/jsruntime/Cargo.toml @@ -14,6 +14,7 @@ indexmap = "2.6.0" jsparser = { path = "../jsparser", features = ["location"] } logging = { path = "../logging" } rustc-hash = "2.0.0" +static_assertions = "1.1.0" thiserror = "2.0.1" [build-dependencies] diff --git a/libs/jsruntime/src/lib.rs b/libs/jsruntime/src/lib.rs index 666367d2..6394da7c 100644 --- a/libs/jsruntime/src/lib.rs +++ b/libs/jsruntime/src/lib.rs @@ -3,18 +3,19 @@ mod llvmir; mod logger; mod semantics; mod tasklet; +mod types; use jsparser::SymbolRegistry; use function::FunctionId; use function::FunctionRegistry; use llvmir::Executor; -use llvmir::ReturnValue; +use types::ReturnValue; use llvmir::Status; pub use llvmir::CompileError; pub use llvmir::Module; -pub use llvmir::Value; +pub use types::Value; pub use semantics::Program; type VoidPtr = *mut std::ffi::c_void; @@ -105,7 +106,7 @@ impl Runtime { pub fn evaluate(&mut self, module: Module) -> Result { logger::debug!(event = "evaluate"); self.executor.register_module(module); - let mut retv = Value::UNDEFINED; + let mut retv = Value::Undefined; let status = match self.executor.get_native_function(FunctionId::MAIN) { Some(main) => unsafe { main( @@ -118,7 +119,8 @@ impl Runtime { // argv std::ptr::null_mut(), // retv - &mut retv as *mut Value, + // TODO: remove type cast + &mut retv as *mut Value as *mut llvmir::bridge::Value, ) }, None => unreachable!(), @@ -146,7 +148,8 @@ where // See https://www.reddit.com/r/rust/comments/ksfk4j/comment/gifzlhg/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button -type HostLambda = unsafe extern "C" fn(VoidPtr, VoidPtr, usize, *mut Value, *mut Value) -> Status; +// TODO: remove llvmir::bridge::Value +type HostLambda = unsafe extern "C" fn(VoidPtr, VoidPtr, usize, *mut llvmir::bridge::Value, *mut llvmir::bridge::Value) -> Status; // This function generates a wrapper function for each `host_func` at compile time. #[inline(always)] @@ -164,8 +167,9 @@ unsafe extern "C" fn host_fn_wrapper( runtime: VoidPtr, _context: VoidPtr, argc: usize, - argv: *mut Value, - retv: *mut Value, + // TODO: remove llvmir::bridge::Value + argv: *mut llvmir::bridge::Value, + retv: *mut llvmir::bridge::Value, ) -> Status where F: Fn(&mut Runtime, &[Value]) -> R + Send + Sync + 'static, @@ -177,6 +181,8 @@ where let args = std::slice::from_raw_parts(argv as *const Value, argc); // TODO: The return value is copied twice. That's inefficient. let result = host_fn(runtime, args); + // TODO: remove type cast + let retv = &mut *(retv as *mut Value); *retv = result.value(); result.status() } diff --git a/libs/jsruntime/src/llvmir/bridge.hh b/libs/jsruntime/src/llvmir/bridge.hh index 2001cded..72100280 100644 --- a/libs/jsruntime/src/llvmir/bridge.hh +++ b/libs/jsruntime/src/llvmir/bridge.hh @@ -3,7 +3,10 @@ #include #include +struct Capture; struct Closure; +struct Coroutine; +struct Value; #define STATUS_UNSET_BIT 0x10 #define STATUS_MASK 0x0F @@ -20,6 +23,7 @@ enum class Status : uint32_t { static_assert(sizeof(Status) == sizeof(uint32_t), "size mismatched"); +// TODO: generate from the Rust type. enum class ValueKind : uint8_t { // DO NOT CHANGE THE ORDER OF THE FOLLOWING ENUM VARIANTS. // Some operations heavily rely on the order. @@ -34,83 +38,14 @@ enum class ValueKind : uint8_t { static_assert(sizeof(ValueKind) == sizeof(uint8_t), "size mismatched"); -union ValueHolder { - uintptr_t opaque; - bool boolean; - double number; - // TODO(issue#237): GcCellRef - Closure* closure; - uint32_t promise; -}; - -static_assert(sizeof(ValueHolder) == sizeof(uint64_t), "size mismatched"); - -// Can be copied as Value. -struct Value { - ValueKind kind; - ValueHolder holder; -}; - -static_assert(sizeof(Value) == sizeof(uint64_t) * 2, "size mismatched"); - // The actual type of `context` varies depending on usage of the lambda function: // // Regular functions: Capture** // Coroutine functions: Coroutine* // +// TODO: move to types.rs typedef Status (*Lambda)(void* runtime, void* context, size_t argc, Value* argv, Value* ret); -// TODO(issue#237): GcCell -struct Capture { - // NOTE: The `target` may point to the `escaped`. In this case, the `target` must be updated if - // the capture is moved during GC, so that the `target` points to the `escaped` correctly. - Value* target; - Value escaped; -}; - -static_assert(sizeof(Capture) == sizeof(uint64_t) * 3, "size mismatched"); - -// TODO(issue#237): GcCell -struct Closure { - // A pointer to a function compiled from a JavaScript function. - Lambda lambda; - - // The number of captures. - // - // Usually, this field does not used in the compiled function, but we add this field here for - // debugging purposes. If we need to reduce the heap memory usage and `Closure`s dominant, we - // can remove this field. - uint16_t num_captures; - - // A variable-length list of captures used in the lambda function. - // TODO(issue#237): GcCellRef - Capture* captures[32]; -}; - -// TODO(issue#237): GcCell -struct Coroutine { - // The closure of the coroutine. - // TODO(issue#237): GcCellRef - Closure* closure; - - // The state of the coroutine. - uint32_t state; - - // The number of local variables. - uint16_t num_locals; - - // The current scope id used by the scope cleanup checker. - uint16_t scope_id; - - // The size of the scratch buffer in bytes. - uint16_t scratch_buffer_len; - - // A variable-length list of local variables used in the coroutine. - Value locals[32]; - - // The scratch_buffer starts from &locals[num_locals]. -}; - #include "runtime.hh" void llvmir_initialize(); diff --git a/libs/jsruntime/src/llvmir/bridge.rs b/libs/jsruntime/src/llvmir/bridge.rs index dcb5ffaa..6cc5b2a0 100644 --- a/libs/jsruntime/src/llvmir/bridge.rs +++ b/libs/jsruntime/src/llvmir/bridge.rs @@ -3,7 +3,7 @@ #![allow(non_snake_case)] #![allow(non_upper_case_globals)] -use crate::tasklet::Promise; +use crate::types; use crate::VoidPtr; include!(concat!(env!("OUT_DIR"), "/bridge.rs")); @@ -14,213 +14,11 @@ macro_rules! into_runtime { }; } -impl Value { - pub const NONE: Self = Self { - kind: ValueKind_None, - holder: ValueHolder { opaque: 0 }, +macro_rules! into_value { + ($value:expr) => { + // TODO: remove type cast + &*($value as *const crate::types::Value) }; - - pub const UNDEFINED: Self = Self { - kind: ValueKind_Undefined, - holder: ValueHolder { opaque: 0 }, - }; - - pub const NULL: Self = Self { - kind: ValueKind_Null, - holder: ValueHolder { opaque: 0 }, - }; - - pub const TRUE: Self = Self::boolean(true); - pub const FALSE: Self = Self::boolean(false); - - pub const fn boolean(boolean: bool) -> Self { - Self { - kind: ValueKind_Boolean, - holder: ValueHolder { boolean }, - } - } - - pub const fn number(number: f64) -> Self { - Self { - kind: ValueKind_Number, - holder: ValueHolder { number }, - } - } - - pub const fn promise(promise: u32) -> Self { - Self { - kind: ValueKind_Promise, - holder: ValueHolder { promise }, - } - } - - pub fn into_result(self, status: Status) -> Result { - match status { - Status_Normal => Ok(self), - Status_Exception => Err(self), - _ => unreachable!(), - } - } -} - -impl From<()> for Value { - fn from(_: ()) -> Self { - Self::UNDEFINED - } -} - -impl From for Value { - fn from(value: bool) -> Self { - Self::boolean(value) - } -} - -impl From for Value { - fn from(value: f64) -> Self { - Self::number(value) - } -} - -impl From for Value { - fn from(value: i32) -> Self { - Self::from(value as f64) - } -} - -impl From for Value { - fn from(value: u32) -> Self { - Self::from(value as f64) - } -} - -impl From for Value { - fn from(value: Promise) -> Self { - Self::promise(value.into()) - } -} - -impl std::fmt::Debug for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // `unsafe` is needed for accessing the `holder` field. - unsafe { - match self.kind { - ValueKind_None => write!(f, "none"), - ValueKind_Undefined => write!(f, "undefined"), - ValueKind_Null => write!(f, "null"), - ValueKind_Boolean if self.holder.boolean => write!(f, "true"), - ValueKind_Boolean => write!(f, "false"), - ValueKind_Number => write!(f, "{}", self.holder.number), - ValueKind_Closure => { - let lambda = (*self.holder.closure).lambda.unwrap(); - write!(f, "closure({lambda:?}, [")?; - let len = (*self.holder.closure).num_captures as usize; - let data = (*self.holder.closure).captures.as_ptr(); - let mut captures = std::slice::from_raw_parts(data, len) - .iter() - .map(|capture| capture.as_ref().unwrap()); - if let Some(capture) = captures.next() { - write!(f, "{capture:?}")?; - for capture in captures { - write!(f, ", {capture:?}")?; - } - } - write!(f, "])") - } - ValueKind_Promise => write!(f, "promise({:04X})", self.holder.promise), - _ => unreachable!("invalid kind: {:?}", self.kind), - } - } - } -} - -impl Capture { - fn is_escaped(&self) -> bool { - self.target as *const Value == &self.escaped - } -} - -impl std::fmt::Debug for Capture { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.is_escaped() { - write!(f, "capture(escaped: {:?})", self.target) - } else { - write!(f, "capture(onstack: {:?})", self.target) - } - } -} - -pub trait ReturnValue { - fn status(&self) -> Status; - fn value(&self) -> Value; -} - -impl ReturnValue for T -where - T: Clone + Into, -{ - fn status(&self) -> Status { - Status_Normal - } - - fn value(&self) -> Value { - self.clone().into() - } -} - -impl ReturnValue for Result -where - T: Clone + Into, - E: Clone + Into, -{ - fn status(&self) -> Status { - if self.is_ok() { - Status_Normal - } else { - Status_Exception - } - } - - fn value(&self) -> Value { - match self { - Ok(v) => v.clone().into(), - Err(err) => err.clone().into(), - } - } -} - -impl Coroutine { - pub fn resume( - runtime: VoidPtr, - coroutine: *mut Coroutine, - promise: Promise, - result: &Value, - error: &Value, - ) -> CoroutineStatus { - unsafe { - let lambda = (*(*coroutine).closure).lambda.unwrap(); - let mut args = [promise.into(), *result, *error]; - let mut retv = Value::NONE; - let status = lambda( - runtime, - coroutine as VoidPtr, - args.len(), - args.as_mut_ptr(), - &mut retv as *mut Value, - ); - match status { - STATUS_NORMAL => CoroutineStatus::Done(retv), - STATUS_EXCEPTION => CoroutineStatus::Error(retv), - STATUS_SUSPEND => CoroutineStatus::Suspend, - _ => unreachable!(), - } - } - } -} - -pub enum CoroutineStatus { - Done(Value), - Error(Value), - Suspend, } pub fn runtime_bridge() -> Runtime { @@ -248,33 +46,33 @@ pub fn runtime_bridge() -> Runtime { // 7.1.2 ToBoolean ( argument ) unsafe extern "C" fn runtime_to_boolean(_runtime: VoidPtr, value: *const Value) -> bool { - let value = &*value; - match value.kind { - ValueKind_Undefined => false, - ValueKind_Null => false, - ValueKind_Boolean => value.holder.boolean, - ValueKind_Number if value.holder.number == 0.0 => false, - ValueKind_Number if value.holder.number.is_nan() => false, - ValueKind_Number => true, - ValueKind_Closure => true, - ValueKind_Promise => true, - _ => unreachable!("invalid value: {value:?}"), + let value = into_value!(value); + match value { + types::Value::None => unreachable!("Value::None"), + types::Value::Undefined => false, + types::Value::Null => false, + types::Value::Boolean(value) => *value, + types::Value::Number(value) if *value == 0.0 => false, + types::Value::Number(value) if value.is_nan() => false, + types::Value::Number(_) => true, + types::Value::Closure(_) => true, + types::Value::Promise(_) => true, } } // 7.1.3 ToNumeric ( value ) // 7.1.4 ToNumber ( argument ) unsafe extern "C" fn runtime_to_numeric(_runtime: VoidPtr, value: *const Value) -> f64 { - let value = &*value; - match value.kind { - ValueKind_Undefined => f64::NAN, - ValueKind_Null => 0.0, - ValueKind_Boolean if value.holder.boolean => 1.0, - ValueKind_Boolean => 0.0, - ValueKind_Number => value.holder.number, - ValueKind_Closure => f64::NAN, - ValueKind_Promise => f64::NAN, - _ => unreachable!("invalid value: {value:?}"), + let value = into_value!(value); + match value { + types::Value::None => unreachable!("Value::None"), + types::Value::Undefined => f64::NAN, + types::Value::Null => 0.0, + types::Value::Boolean(value) if *value => 1.0, + types::Value::Boolean(_) => 0.0, + types::Value::Number(value) => *value, + types::Value::Closure(_) => f64::NAN, + types::Value::Promise(_) => f64::NAN, } } @@ -334,35 +132,43 @@ unsafe extern "C" fn runtime_is_loosely_equal( a: *const Value, b: *const Value, ) -> bool { - let x = &*a; - let y = &*b; + let x = into_value!(a); + debug_assert!(!matches!(x, types::Value::None)); + + let y = into_value!(b); + debug_assert!(!matches!(y, types::Value::None)); + + let x_kind = std::mem::discriminant(x); + let y_kind = std::mem::discriminant(y); + // 1. If Type(x) is Type(y) - if x.kind == y.kind { + if x_kind == y_kind { // a. Return IsStrictlyEqual(x, y). return runtime_is_strictly_equal(runtime, a, b); } - // 2. If x is null and y is undefined, return true. - if x.kind == ValueKind_Null && y.kind == ValueKind_Undefined { - return true; - } - // 3. If x is undefined and y is null, return true. - if x.kind == ValueKind_Undefined && y.kind == ValueKind_Null { - return true; - } - // TODO: 4. NOTE: This step is replaced in section B.3.6.2. - // TODO: 5. If x is a Number and y is a String, return ! IsLooselyEqual(x, ! ToNumber(y)). - // TODO: 6. If x is a String and y is a Number, return ! IsLooselyEqual(! ToNumber(x), y). - // TODO: 7. If x is a BigInt and y is a String, then - // TODO: 8. If x is a String and y is a BigInt, return ! IsLooselyEqual(y, x). - // TODO: 9. If x is a Boolean, return ! IsLooselyEqual(! ToNumber(x), y). - // TODO: 10. If y is a Boolean, return ! IsLooselyEqual(x, ! ToNumber(y)). - // ... - let xnum = runtime_to_numeric(runtime, x); - let ynum = runtime_to_numeric(runtime, y); - if xnum.is_nan() || ynum.is_nan() { - return false; + + match (x, y) { + // 2. If x is null and y is undefined, return true. + (types::Value::Null, types::Value::Undefined) => true, + // 3. If x is undefined and y is null, return true. + (types::Value::Undefined, types::Value::Null) => true, + // TODO: 4. NOTE: This step is replaced in section B.3.6.2. + // TODO: 5. If x is a Number and y is a String, return ! IsLooselyEqual(x, ! ToNumber(y)). + // TODO: 6. If x is a String and y is a Number, return ! IsLooselyEqual(! ToNumber(x), y). + // TODO: 7. If x is a BigInt and y is a String, then + // TODO: 8. If x is a String and y is a BigInt, return ! IsLooselyEqual(y, x). + // TODO: 9. If x is a Boolean, return ! IsLooselyEqual(! ToNumber(x), y). + // TODO: 10. If y is a Boolean, return ! IsLooselyEqual(x, ! ToNumber(y)). + // ... + _ => { + let xnum = runtime_to_numeric(runtime, a); + let ynum = runtime_to_numeric(runtime, b); + if xnum.is_nan() || ynum.is_nan() { + return false; + } + xnum == ynum + } } - xnum == ynum } // 7.2.14 IsStrictlyEqual ( x, y ) @@ -371,20 +177,13 @@ unsafe extern "C" fn runtime_is_strictly_equal( a: *const Value, b: *const Value, ) -> bool { - let x = &*a; - let y = &*b; - if x.kind != y.kind { - return false; - } - match x.kind { - ValueKind_Undefined => true, - ValueKind_Null => true, - ValueKind_Boolean => x.holder.boolean == y.holder.boolean, - ValueKind_Number => x.holder.number == y.holder.number, - ValueKind_Closure => x.holder.closure == y.holder.closure, - ValueKind_Promise => x.holder.promise == y.holder.promise, - _ => unreachable!("invalid value: {x:?}"), - } + let x = into_value!(a); + debug_assert!(!matches!(x, types::Value::None)); + + let y = into_value!(b); + debug_assert!(!matches!(y, types::Value::None)); + + x == y } unsafe extern "C" fn runtime_create_capture( @@ -393,8 +192,8 @@ unsafe extern "C" fn runtime_create_capture( ) -> *mut Capture { const LAYOUT: std::alloc::Layout = unsafe { std::alloc::Layout::from_size_align_unchecked( - std::mem::size_of::(), - std::mem::align_of::(), + std::mem::size_of::(), + std::mem::align_of::(), ) }; @@ -404,12 +203,14 @@ unsafe extern "C" fn runtime_create_capture( // TODO: GC let ptr = allocator.alloc_layout(LAYOUT); - let capture = ptr.cast::().as_ptr(); - (*capture).target = target; + let capture = ptr.cast::().as_ptr(); + // TODO: remove type cast + (*capture).target = target as *mut types::Value; // `capture.escaped` will be filled with an actual value. - capture + // TODO: remove type cast + capture as *mut types::Capture as *mut Capture } unsafe extern "C" fn runtime_create_closure( @@ -419,12 +220,12 @@ unsafe extern "C" fn runtime_create_closure( ) -> *mut Closure { const BASE_LAYOUT: std::alloc::Layout = unsafe { std::alloc::Layout::from_size_align_unchecked( - std::mem::offset_of!(Closure, captures), - std::mem::align_of::(), + std::mem::offset_of!(types::Closure, captures), + std::mem::align_of::(), ) }; - let storage_layout = std::alloc::Layout::array::<*mut Capture>(num_captures as usize).unwrap(); + let storage_layout = std::alloc::Layout::array::<*mut types::Capture>(num_captures as usize).unwrap(); let (layout, _) = BASE_LAYOUT.extend(storage_layout).unwrap(); let runtime = into_runtime!(runtime, X); @@ -433,12 +234,13 @@ unsafe extern "C" fn runtime_create_closure( // TODO: GC let ptr = allocator.alloc_layout(layout); - let closure = ptr.cast::().as_ptr(); + let closure = ptr.cast::().as_ptr(); (*closure).lambda = lambda; (*closure).num_captures = num_captures; // `(*closure).captures[]` will be filled with actual pointers to `Captures`. - closure + // TODO: remove type cast + closure as *mut types::Closure as *mut Closure } unsafe extern "C" fn runtime_create_coroutine( @@ -449,13 +251,13 @@ unsafe extern "C" fn runtime_create_coroutine( ) -> *mut Coroutine { const BASE_LAYOUT: std::alloc::Layout = unsafe { std::alloc::Layout::from_size_align_unchecked( - std::mem::offset_of!(Coroutine, locals), - std::mem::align_of::(), + std::mem::offset_of!(types::Coroutine, locals), + std::mem::align_of::(), ) }; // num_locals may be 0. - let locals_layout = std::alloc::Layout::array::(num_locals as usize).unwrap(); + let locals_layout = std::alloc::Layout::array::(num_locals as usize).unwrap(); let (layout, _) = BASE_LAYOUT.extend(locals_layout).unwrap(); // scratch_buffer_len may be 0. @@ -470,15 +272,17 @@ unsafe extern "C" fn runtime_create_coroutine( // TODO: GC let ptr = allocator.alloc_layout(layout); - let coroutine = ptr.cast::().as_ptr(); - (*coroutine).closure = closure; + let coroutine = ptr.cast::().as_ptr(); + // TODO: remove type cast + (*coroutine).closure = closure as *mut types::Closure; (*coroutine).state = 0; (*coroutine).num_locals = num_locals; (*coroutine).scope_id = 0; (*coroutine).scratch_buffer_len = scratch_buffer_len; // `(*coroutine).locals[]` will be initialized in the coroutine. - coroutine + // TODO: remove type cast + coroutine as *mut types::Coroutine as *mut Coroutine } unsafe extern "C" fn runtime_register_promise( @@ -486,12 +290,13 @@ unsafe extern "C" fn runtime_register_promise( coroutine: *mut Coroutine, ) -> u32 { let runtime = into_runtime!(runtime, X); - runtime.register_promise(coroutine).into() + // TODO: remove type cast + runtime.register_promise(coroutine as *mut types::Coroutine).into() } unsafe extern "C" fn runtime_resume(runtime: VoidPtr, promise: u32) { let runtime = into_runtime!(runtime, X); - runtime.process_promise(promise.into(), &Value::NONE, &Value::NONE); + runtime.process_promise(promise.into(), &types::Value::None, &types::Value::None); } unsafe extern "C" fn runtime_await_promise(runtime: VoidPtr, promise: u32, awaiting: u32) { @@ -505,7 +310,7 @@ unsafe extern "C" fn runtime_emit_promise_resolved( result: *const Value, ) { let runtime = into_runtime!(runtime, X); - let cloned = *result; + let cloned = into_value!(result).clone(); runtime.emit_promise_resolved(promise.into(), cloned); } @@ -551,7 +356,7 @@ unsafe extern "C" fn runtime_print_value( value: *const Value, msg: *const std::os::raw::c_char, ) { - let value = &*value; + let value = into_value!(value); let msg = std::ffi::CStr::from_ptr(msg); if msg.is_empty() { crate::logger::debug!("runtime_print_value: {value:?}"); diff --git a/libs/jsruntime/src/llvmir/compiler.hh b/libs/jsruntime/src/llvmir/compiler.hh index 54254cc9..1e460cd5 100644 --- a/libs/jsruntime/src/llvmir/compiler.hh +++ b/libs/jsruntime/src/llvmir/compiler.hh @@ -780,8 +780,7 @@ class Compiler { void CreateEscapeValue(llvm::Value* capture, llvm::Value* value) { auto* escaped_ptr = CreateGetEscapedPtrOfCapture(capture); CreateStoreTargetToCapture(escaped_ptr, capture); - auto align = llvm::Align(sizeof(double)); - builder_->CreateMemCpy(escaped_ptr, align, value, align, types_->GetWord(sizeof(Value))); + CreateMemCpyValue(escaped_ptr, value); } llvm::Value* CreateGetCaptureValuePtr(uint16_t index) { @@ -1103,6 +1102,20 @@ class Compiler { builder_->CreateStore(holder, ptr); } + void CreateMemCpyValue(llvm::Value* dst, llvm::Value* src) { + auto* layout = module_->getDataLayout().getStructLayout(types_->CreateValueType()); + // TODO: can computed at compile-time + auto align = layout->getAlignment(); + auto* size = types_->GetWord(layout->getSizeInBytes()); + builder_->CreateMemCpy(dst, align, src, align, size); + } + + llvm::Value* GetSizeofValue() { + auto* layout = module_->getDataLayout().getStructLayout(types_->CreateValueType()); + // TODO: can computed at compile-time + return types_->GetWord(layout->getSizeInBytes()); + } + // closure llvm::Value* CreateGetLambdaPtrOfClosure(llvm::Value* closure_ptr) { @@ -1154,8 +1167,7 @@ class Compiler { void CreateStoreEscapedToCapture(llvm::Value* value_ptr, llvm::Value* capture_ptr) { auto* ptr = CreateGetEscapedPtrOfCapture(capture_ptr); - auto align = llvm::Align(sizeof(double)); - builder_->CreateMemCpy(ptr, align, value_ptr, align, types_->GetWord(sizeof(Value))); + CreateMemCpyValue(ptr, value_ptr); } // captures @@ -1235,9 +1247,10 @@ class Compiler { auto* num_locals = CreateLoadNumLocalsFromCoroutine(); auto* num_locals_usize = builder_->CreateSExt(num_locals, types_->GetWordType(), REG_NAME("co.num_locals.usize")); - auto* sizeof_locals = builder_->CreateMul(types_->GetWord(sizeof(Value)), num_locals_usize, - REG_NAME("co.locals.sizeof")); - auto* offsetof_locals = types_->GetWord(offsetof(Coroutine, locals)); + auto* sizeof_locals = builder_->CreateMul(GetSizeofValue(), num_locals_usize, REG_NAME("co.locals.sizeof")); + auto* layout = module_->getDataLayout().getStructLayout(types_->CreateCoroutineType()); + // TODO: can computed at compile-time + auto* offsetof_locals = types_->GetWord(layout->getElementOffset(5)); auto* offset = builder_->CreateAdd(offsetof_locals, sizeof_locals, REG_NAME("co.scratch_buffer.offsetof")); return builder_->CreateInBoundsPtrAdd(context_, offset, REG_NAME("co.scratch_buffer.ptr")); diff --git a/libs/jsruntime/src/llvmir/compiler/mod.rs b/libs/jsruntime/src/llvmir/compiler/mod.rs index f1df4635..4bc25abf 100644 --- a/libs/jsruntime/src/llvmir/compiler/mod.rs +++ b/libs/jsruntime/src/llvmir/compiler/mod.rs @@ -20,10 +20,9 @@ use crate::semantics::ScopeRef; use crate::semantics::ScopeTree; use crate::Program; use crate::Runtime; +use crate::Value; use super::bridge; -use super::bridge::Value; -use super::bridge::ValueHolder; use super::Module; use control_flow::ControlFlowStack; @@ -40,7 +39,7 @@ use peer::SwitchIr; use peer::ValueIr; const VALUE_SIZE: u32 = size_of::() as u32; -const VALUE_HOLDER_SIZE: u32 = size_of::() as u32; +const VALUE_HOLDER_SIZE: u32 = size_of::() as u32; impl Runtime { pub fn compile(&mut self, program: &Program, optimize: bool) -> Result { diff --git a/libs/jsruntime/src/llvmir/mod.rs b/libs/jsruntime/src/llvmir/mod.rs index b04ad540..ba43a85f 100644 --- a/libs/jsruntime/src/llvmir/mod.rs +++ b/libs/jsruntime/src/llvmir/mod.rs @@ -1,13 +1,9 @@ -mod bridge; +pub mod bridge; mod compiler; mod executor; pub use bridge::runtime_bridge; -pub use bridge::Coroutine; -pub use bridge::CoroutineStatus; -pub use bridge::ReturnValue; pub use bridge::Status; -pub use bridge::Value; pub use compiler::CompileError; pub use executor::Executor; diff --git a/libs/jsruntime/src/tasklet.rs b/libs/jsruntime/src/tasklet.rs index 72d02a08..f648dd58 100644 --- a/libs/jsruntime/src/tasklet.rs +++ b/libs/jsruntime/src/tasklet.rs @@ -2,8 +2,8 @@ use std::collections::VecDeque; use rustc_hash::FxHashMap; -use crate::llvmir::Coroutine; -use crate::llvmir::CoroutineStatus; +use crate::types::Coroutine; +use crate::types::CoroutineStatus; use crate::Runtime; use crate::Value; @@ -21,9 +21,9 @@ impl Runtime { Message::PromiseResolved { promise, ref result, - } => self.process_promise(promise, result, &Value::NONE), + } => self.process_promise(promise, result, &Value::None), Message::PromiseRejected { promise, ref error } => { - self.process_promise(promise, &Value::NONE, error) + self.process_promise(promise, &Value::None, error) } } } @@ -112,11 +112,13 @@ impl System { debug_assert!(driver.awaiting.is_none()); match driver.state { PromiseState::Pending => driver.awaiting = Some(awaiting), - PromiseState::Resolved(result) => { + PromiseState::Resolved(ref result) => { + let result = result.clone(); self.emit_promise_resolved(awaiting, result); self.promises.remove(&promise); } - PromiseState::Rejected(error) => { + PromiseState::Rejected(ref error) => { + let error = error.clone(); self.emit_promise_rejected(awaiting, error); self.promises.remove(&promise); } diff --git a/libs/jsruntime/src/types.rs b/libs/jsruntime/src/types.rs new file mode 100644 index 00000000..3e2ea879 --- /dev/null +++ b/libs/jsruntime/src/types.rs @@ -0,0 +1,274 @@ +use std::ffi::c_void; +use std::marker::PhantomPinned; +use std::mem::offset_of; +use std::ptr::addr_eq; + +use crate::llvmir::bridge::Lambda; +use crate::llvmir::bridge::Status; +use crate::llvmir::bridge::STATUS_NORMAL; +use crate::llvmir::bridge::STATUS_EXCEPTION; +use crate::llvmir::bridge::STATUS_SUSPEND; +use crate::tasklet::Promise; + +// CAUTION: This module contains types used in JIT-generated code. Please carefully check the +// memory layout of a type you want to change. It's recommended to use compile-time assertions +// that ensure the memory layout of the type. + +/// A data type to hold a JavaScript value. +// +// DO NOT CHANGE THE ORDER OF THE VARIANTS. +// Some operations heavily rely on the order. +#[repr(C, u8)] +#[derive(Clone, PartialEq)] +pub enum Value { + None = 0, + Undefined, + Null, + Boolean(bool), + Number(f64), + // TODO(issue#237): GcCellRef + Closure(*mut Closure), + Promise(Promise), +} + +static_assertions::const_assert_eq!(size_of::(), 16); +static_assertions::const_assert_eq!(align_of::(), 8); + +impl Value { + pub fn into_result(self, status: Status) -> Result { + match status { + STATUS_NORMAL => Ok(self), + STATUS_EXCEPTION => Err(self), + _ => unreachable!(), + } + } +} + +impl From<()> for Value { + fn from(_: ()) -> Self { + Self::Undefined + } +} + +impl From for Value { + fn from(value: bool) -> Self { + Self::Boolean(value) + } +} + +impl From for Value { + fn from(value: f64) -> Self { + Self::Number(value) + } +} + +impl From for Value { + fn from(value: i32) -> Self { + Self::from(value as f64) + } +} + +impl From for Value { + fn from(value: u32) -> Self { + Self::from(value as f64) + } +} + +impl From for Value { + fn from(value: Promise) -> Self { + Self::Promise(value.into()) + } +} + +impl std::fmt::Debug for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::None => write!(f, "none"), + Self::Undefined => write!(f, "undefined"), + Self::Null => write!(f, "null"), + Self::Boolean(value) => write!(f, "{value}"), + Self::Number(value) => write!(f, "{value}"), + Self::Closure(value) => write!(f, "{:?}", &*value), + Self::Promise(value) => write!(f, "{value:?}"), + } + } +} + +/// A data type to represent a closure. +// +// TODO(issue#237): GcCell +#[repr(C)] +pub struct Closure { + /// A pointer to a lambda function compiled from a JavaScript function definition. + pub lambda: Lambda, + + /// The number of captures. + /// + /// Usually, this field does not used in the compiled function, but we add this field here for + /// debugging purposes. If we need to reduce the heap memory usage and `Closure`s dominant, we + /// can remove this field. + pub num_captures: u16, + + /// A variable-length list of captures used in the lambda function. + // + // TODO(issue#237): GcCellRef + pub captures: [Capture; 32], +} + +static_assertions::const_assert_eq!(align_of::(), 8); + +impl std::fmt::Debug for Closure { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let lambda = self.lambda.unwrap(); + write!(f, "closure({lambda:?}, [")?; + let len = self.num_captures as usize; + let data = self.captures.as_ptr(); + let mut captures = unsafe { std::slice::from_raw_parts(data, len).iter() }; + if let Some(capture) = captures.next() { + write!(f, "{capture:?}")?; + for capture in captures { + write!(f, ", {capture:?}")?; + } + } + write!(f, "])") + } +} + +/// A data type to track a captured value. +// +// NOTE: The `target` may point to the `escaped`. In this case, the `target` must be updated if +// the capture is moved during GC, so that the `target` points to the `escaped` correctly. +// +// TODO(issue#237): GcCell +#[repr(C)] +pub struct Capture { + pub target: *mut Value, + pub escaped: Value, + _pinned: PhantomPinned, +} + +static_assertions::const_assert_eq!(size_of::(), 24); +static_assertions::const_assert_eq!(align_of::(), 8); +static_assertions::const_assert_eq!(offset_of!(Capture, escaped), 8); + +impl Capture { + fn is_escaped(&self) -> bool { + debug_assert!(!self.target.is_null()); + addr_eq(self.target, &self.escaped) + } +} + +impl std::fmt::Debug for Capture { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.is_escaped() { + write!(f, "capture(escaped: {:?})", self.target) + } else { + write!(f, "capture(onstack: {:?})", self.target) + } + } +} + +/// A data type to represent a coroutine. +// +// TODO(issue#237): GcCell +#[repr(C)] +pub struct Coroutine { + /// The closure of the coroutine. + // + // TODO(issue#237): GcCellRef + pub closure: *mut Closure, + + /// The state of the coroutine. + pub state: u32, + + /// The number of the local variables used in the coroutine. + pub num_locals: u16, + + /// The current scope ID used by the scope cleanup checker. + pub scope_id: u16, + + /// The size of the scratch buffer in bytes. + pub scratch_buffer_len: u16, + + /// A variable-length list of local variables used in the coroutine. + pub locals: [Value; 32], + + // The scratch_buffer starts from &locals[num_locals]. +} + +static_assertions::const_assert_eq!(align_of::(), 8); + +impl Coroutine { + pub fn resume( + runtime: *mut c_void, + coroutine: *mut Coroutine, + promise: Promise, + result: &Value, + error: &Value, + ) -> CoroutineStatus { + unsafe { + let lambda = (&*(*coroutine).closure).lambda.unwrap(); + let mut args = [promise.into(), result.clone(), error.clone()]; + let mut retv = Value::None; + let status = lambda( + runtime, + coroutine as *mut c_void, + args.len(), + args.as_mut_ptr() as *mut Value as *mut crate::llvmir::bridge::Value, + &mut retv as *mut Value as *mut crate::llvmir::bridge::Value, + ); + match status { + STATUS_NORMAL => CoroutineStatus::Done(retv), + STATUS_EXCEPTION => CoroutineStatus::Error(retv), + STATUS_SUSPEND => CoroutineStatus::Suspend, + _ => unreachable!(), + } + } + } +} + +/// The return value type of `Coroutine::resume()`. +pub enum CoroutineStatus { + Done(Value), + Error(Value), + Suspend, +} + +pub trait ReturnValue { + fn status(&self) -> Status; + fn value(&self) -> Value; +} + +impl ReturnValue for T +where + T: Clone + Into, +{ + fn status(&self) -> Status { + STATUS_NORMAL + } + + fn value(&self) -> Value { + self.clone().into() + } +} + +impl ReturnValue for Result +where + T: Clone + Into, + E: Clone + Into, +{ + fn status(&self) -> Status { + if self.is_ok() { + STATUS_NORMAL + } else { + STATUS_EXCEPTION + } + } + + fn value(&self) -> Value { + match self { + Ok(v) => v.clone().into(), + Err(err) => err.clone().into(), + } + } +} diff --git a/libs/jsruntime/tests/collect_evaluate_tests.js b/libs/jsruntime/tests/collect_evaluate_tests.js index 818b38d9..9b362851 100644 --- a/libs/jsruntime/tests/collect_evaluate_tests.js +++ b/libs/jsruntime/tests/collect_evaluate_tests.js @@ -30,18 +30,38 @@ if (options.debug) { Deno.exit(await main(args, options)); async function main(args, options) { + function mapValue(value) { + switch (value) { + case 'undefined': + return 'Value::Undefined'; + case 'null': + return 'Value::Null'; + case 'NaN': + return 'f64::NAN'; + case 'Infinity': + return 'f64::INFINITY'; + case '-Infinity': + return '-f64::INFINITY'; + default: + return value; + } + } + const tests = []; for (const test of args.tests) { log.debug(`Reading ${test}...`); const script = await Deno.readTextFile(test); const lines = script.split('\n').map((line) => line.trim()); - const sequencedValues = lines.filter((line) => line.includes('///=')) - .map((line) => line.split('///=')[1].trim()); + const sequencedValues = lines + .filter((line) => line.includes('///=')) + .map((line) => line.split('///=')[1].trim()) + .map(mapValue); const orderedValues = lines .filter((line) => line.includes('///#')) .map((line) => line.split('///#')[1].trim().split('=')) + .map(([i, v]) => [i, mapValue(v)]) .reduce((acc, [i, v]) => { acc[i] = v; return acc; }, []); - const throws = lines.find((line) => line.includes('///!'))?.split('///!')[1].trim(); + const throws = mapValue(lines.find((line) => line.includes('///!'))?.split('///!')[1].trim()); const name = path.basename(test).replace('.', '_'); const module = test.endsWith('.js') ? false : true; tests.push({ diff --git a/libs/jsruntime/tests/modules/await_in_arguments.mjs b/libs/jsruntime/tests/modules/await_in_arguments.mjs index 91eecd52..fc60042d 100644 --- a/libs/jsruntime/tests/modules/await_in_arguments.mjs +++ b/libs/jsruntime/tests/modules/await_in_arguments.mjs @@ -1,8 +1,8 @@ test(await undefined, await null, await true, await 1, await x, y(), await 300); async function test(undef, nul, bool, number, closure, promise, last) { - print(undef); ///=Value::UNDEFINED - print(nul); ///=Value::NULL + print(undef); ///=undefined + print(nul); ///=null print(bool); ///=true print(number); ///=1 closure(); ///=100 diff --git a/libs/jsruntime/tests/scripts/let_declaration.js b/libs/jsruntime/tests/scripts/let_declaration.js index 3abb5aed..616f340c 100644 --- a/libs/jsruntime/tests/scripts/let_declaration.js +++ b/libs/jsruntime/tests/scripts/let_declaration.js @@ -1,5 +1,5 @@ let a, b = 2; -print(a); ///=Value::UNDEFINED +print(a); ///=undefined print(b); ///=2 a = 1; diff --git a/libs/jsruntime/tests/scripts/null.js b/libs/jsruntime/tests/scripts/null.js index 93c79e90..1b504708 100644 --- a/libs/jsruntime/tests/scripts/null.js +++ b/libs/jsruntime/tests/scripts/null.js @@ -1 +1 @@ -print(null); ///=Value::NULL +print(null); ///=null diff --git a/libs/jsruntime/tests/scripts/number.js b/libs/jsruntime/tests/scripts/number.js index d091659b..feb2ac5d 100644 --- a/libs/jsruntime/tests/scripts/number.js +++ b/libs/jsruntime/tests/scripts/number.js @@ -1,3 +1,3 @@ print(1); ///=1 -print(NaN); ///=f64::NAN -print(Infinity); ///= f64::INFINITY +print(NaN); ///=NaN +print(Infinity); ///=Infinity diff --git a/libs/jsruntime/tests/scripts/ternary.js b/libs/jsruntime/tests/scripts/ternary.js index e176d449..cf53a205 100644 --- a/libs/jsruntime/tests/scripts/ternary.js +++ b/libs/jsruntime/tests/scripts/ternary.js @@ -2,5 +2,5 @@ print(1 > 0 ? 2 : 3); ///=2 print(1 < 0 ? 2 : 3); ///=3 print(1 > 0 ? true : false); ///=true print(1 < 0 ? true : false); ///=false -print(1 > 0 ? undefined : undefined); ///=Value::UNDEFINED -print(1 < 0 ? undefined : undefined); ///=Value::UNDEFINED +print(1 > 0 ? undefined : undefined); ///=undefined +print(1 < 0 ? undefined : undefined); ///=undefined diff --git a/libs/jsruntime/tests/scripts/ternary_mixed_types.js b/libs/jsruntime/tests/scripts/ternary_mixed_types.js index 49057a07..6d2cc9d1 100644 --- a/libs/jsruntime/tests/scripts/ternary_mixed_types.js +++ b/libs/jsruntime/tests/scripts/ternary_mixed_types.js @@ -1,4 +1,4 @@ print(true ? 2 : false); ///=2 print(false ? 2 : false); ///=false print(true ? 2 : undefined); ///=2 -print(false ? 2 : undefined); ///=Value::UNDEFINED +print(false ? 2 : undefined); ///=undefined diff --git a/libs/jsruntime/tests/scripts/throw_null.js b/libs/jsruntime/tests/scripts/throw_null.js index f42f9288..9323e6cd 100644 --- a/libs/jsruntime/tests/scripts/throw_null.js +++ b/libs/jsruntime/tests/scripts/throw_null.js @@ -1 +1 @@ -throw null; ///!Value::NULL +throw null; ///!null diff --git a/libs/jsruntime/tests/scripts/throw_undefined.js b/libs/jsruntime/tests/scripts/throw_undefined.js index 183f028e..829f52a2 100644 --- a/libs/jsruntime/tests/scripts/throw_undefined.js +++ b/libs/jsruntime/tests/scripts/throw_undefined.js @@ -1 +1 @@ -throw undefined; ///!Value::UNDEFINED +throw undefined; ///!undefined diff --git a/libs/jsruntime/tests/scripts/to_numeric.js b/libs/jsruntime/tests/scripts/to_numeric.js index a27fa88c..2ebe5a20 100644 --- a/libs/jsruntime/tests/scripts/to_numeric.js +++ b/libs/jsruntime/tests/scripts/to_numeric.js @@ -1,4 +1,4 @@ -print(undefined + 0); ///=f64::NAN +print(undefined + 0); ///=NaN print(null + 0); ///=0 print(false + 0); ///=0 print(true + 0); ///=1 diff --git a/libs/jsruntime/tests/scripts/unary_minus.js b/libs/jsruntime/tests/scripts/unary_minus.js index 1c7be336..5c03c448 100644 --- a/libs/jsruntime/tests/scripts/unary_minus.js +++ b/libs/jsruntime/tests/scripts/unary_minus.js @@ -1,9 +1,9 @@ -print(-undefined); ///=f64::NAN +print(-undefined); ///=NaN print(-null); ///=-0. print(-true); ///=-1 print(-false); ///=-0. print(-0); ///=-0. print(-1); ///=-1 print(-(-1)); ///=1 -print(-NaN); ///=f64::NAN -print(-Infinity); ///=-f64::INFINITY +print(-NaN); ///=NaN +print(-Infinity); ///=-Infinity diff --git a/libs/jsruntime/tests/scripts/unary_plus.js b/libs/jsruntime/tests/scripts/unary_plus.js index 7771dcbb..a42c227f 100644 --- a/libs/jsruntime/tests/scripts/unary_plus.js +++ b/libs/jsruntime/tests/scripts/unary_plus.js @@ -1,9 +1,9 @@ -print(+undefined); ///=f64::NAN +print(+undefined); ///=NaN print(+null); ///=0 print(+true); ///=1 print(+false); ///=0 print(+0); ///=0 print(+1); ///=1 print(+(+1)); ///=1 -print(+NaN); ///=f64::NAN -print(+Infinity); ///=f64::INFINITY +print(+NaN); ///=NaN +print(+Infinity); ///=Infinity diff --git a/libs/jsruntime/tests/scripts/undefined.js b/libs/jsruntime/tests/scripts/undefined.js index b1eaa482..cd65a630 100644 --- a/libs/jsruntime/tests/scripts/undefined.js +++ b/libs/jsruntime/tests/scripts/undefined.js @@ -1 +1 @@ -print(undefined); ///=Value::UNDEFINED +print(undefined); ///=undefined diff --git a/libs/jsruntime/tests/scripts/void.js b/libs/jsruntime/tests/scripts/void.js index 8b631102..da28310b 100644 --- a/libs/jsruntime/tests/scripts/void.js +++ b/libs/jsruntime/tests/scripts/void.js @@ -1,11 +1,11 @@ -print(void undefined); ///=Value::UNDEFINED -print(void null); ///=Value::UNDEFINED -print(void true); ///=Value::UNDEFINED -print(void false); ///=Value::UNDEFINED -print(void 0); ///=Value::UNDEFINED -print(void NaN); ///=Value::UNDEFINED -print(void Infinity); ///=Value::UNDEFINED -print(void void 0); ///=Value::UNDEFINED +print(void undefined); ///=undefined +print(void null); ///=undefined +print(void true); ///=undefined +print(void false); ///=undefined +print(void 0); ///=undefined +print(void NaN); ///=undefined +print(void Infinity); ///=undefined +print(void void 0); ///=undefined const a = 1; -print(void a); ///=Value::UNDEFINED +print(void a); ///=undefined From 278f534976c5472a3479fb0ac603d9862958d752 Mon Sep 17 00:00:00 2001 From: masnagam Date: Sat, 9 Nov 2024 20:05:30 +0900 Subject: [PATCH 2/5] refactor(jsruntime): use cbindgen --- Cargo.lock | 172 +- libs/jsruntime/.gitignore | 8 +- libs/jsruntime/Cargo.toml | 4 +- libs/jsruntime/build.rs | 64 +- libs/jsruntime/src/lib.rs | 61 +- libs/jsruntime/src/llvmir/Makefile | 7 +- libs/jsruntime/src/llvmir/README.md | 9 + libs/jsruntime/src/llvmir/bridge.cc | 773 -------- libs/jsruntime/src/llvmir/bridge.hh | 339 ---- libs/jsruntime/src/llvmir/bridge.rs | 244 +-- libs/jsruntime/src/llvmir/cbindgen.toml | 8 + libs/jsruntime/src/llvmir/compiler/bridge.rs | 1675 +++++++++++++++++ .../llvmir/{compiler.hh => compiler/impl.hh} | 93 +- libs/jsruntime/src/llvmir/compiler/mod.rs | 820 ++++---- libs/jsruntime/src/llvmir/compiler/peer.cc | 764 ++++++++ libs/jsruntime/src/llvmir/compiler/peer.rs | 1265 ------------- .../llvmir/{ => compiler}/type_holder.cc.njk | 0 .../llvmir/{ => compiler}/type_holder.hh.njk | 0 libs/jsruntime/src/llvmir/executor.cc | 45 - libs/jsruntime/src/llvmir/executor.hh | 52 - libs/jsruntime/src/llvmir/executor.rs | 54 - libs/jsruntime/src/llvmir/executor/bridge.rs | 72 + .../impl.codegen.hh.njk} | 12 +- libs/jsruntime/src/llvmir/executor/impl.hh | 90 + libs/jsruntime/src/llvmir/executor/mod.rs | 17 + libs/jsruntime/src/llvmir/executor/peer.cc | 39 + libs/jsruntime/src/llvmir/helper.cc | 34 - libs/jsruntime/src/llvmir/helper.hh | 12 - libs/jsruntime/src/llvmir/macros.hh | 6 - libs/jsruntime/src/llvmir/mod.rs | 32 +- libs/jsruntime/src/llvmir/module/bridge.rs | 36 + .../src/llvmir/{module.hh => module/impl.hh} | 0 libs/jsruntime/src/llvmir/module/mod.rs | 18 + libs/jsruntime/src/llvmir/module/peer.cc | 17 + libs/jsruntime/src/llvmir/peer.cc | 12 + libs/jsruntime/src/llvmir/runtime.hh.njk | 10 - libs/jsruntime/src/tasklet.rs | 18 +- libs/jsruntime/src/types.rs | 130 +- .../jsruntime/tests/collect_evaluate_tests.js | 10 +- libs/jsruntime/tests/evaluate.rs.njk | 12 - 40 files changed, 3626 insertions(+), 3408 deletions(-) delete mode 100644 libs/jsruntime/src/llvmir/bridge.cc delete mode 100644 libs/jsruntime/src/llvmir/bridge.hh create mode 100644 libs/jsruntime/src/llvmir/cbindgen.toml create mode 100644 libs/jsruntime/src/llvmir/compiler/bridge.rs rename libs/jsruntime/src/llvmir/{compiler.hh => compiler/impl.hh} (95%) create mode 100644 libs/jsruntime/src/llvmir/compiler/peer.cc delete mode 100644 libs/jsruntime/src/llvmir/compiler/peer.rs rename libs/jsruntime/src/llvmir/{ => compiler}/type_holder.cc.njk (100%) rename libs/jsruntime/src/llvmir/{ => compiler}/type_holder.hh.njk (100%) delete mode 100644 libs/jsruntime/src/llvmir/executor.cc delete mode 100644 libs/jsruntime/src/llvmir/executor.hh delete mode 100644 libs/jsruntime/src/llvmir/executor.rs create mode 100644 libs/jsruntime/src/llvmir/executor/bridge.rs rename libs/jsruntime/src/llvmir/{executor.codegen.cc.njk => executor/impl.codegen.hh.njk} (79%) create mode 100644 libs/jsruntime/src/llvmir/executor/impl.hh create mode 100644 libs/jsruntime/src/llvmir/executor/mod.rs create mode 100644 libs/jsruntime/src/llvmir/executor/peer.cc delete mode 100644 libs/jsruntime/src/llvmir/helper.cc delete mode 100644 libs/jsruntime/src/llvmir/helper.hh delete mode 100644 libs/jsruntime/src/llvmir/macros.hh create mode 100644 libs/jsruntime/src/llvmir/module/bridge.rs rename libs/jsruntime/src/llvmir/{module.hh => module/impl.hh} (100%) create mode 100644 libs/jsruntime/src/llvmir/module/mod.rs create mode 100644 libs/jsruntime/src/llvmir/module/peer.cc create mode 100644 libs/jsruntime/src/llvmir/peer.cc delete mode 100644 libs/jsruntime/src/llvmir/runtime.hh.njk diff --git a/Cargo.lock b/Cargo.lock index 2a1c09a5..835cbbef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,26 +235,6 @@ dependencies = [ "criterion-cycles-per-byte", ] -[[package]] -name = "bindgen" -version = "0.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "log 0.4.22", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn", -] - [[package]] name = "bitflags" version = "2.6.0" @@ -288,6 +268,24 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "cbindgen" +version = "0.27.0" +source = "git+https://github.com/masnagam/cbindgen.git?branch=fix-issue-43#7fcd29d7a7bd204cff6887ae099aa40f716367ed" +dependencies = [ + "clap", + "heck 0.4.1", + "indexmap", + "log 0.4.22", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", + "tempfile", + "toml", +] + [[package]] name = "cc" version = "1.1.37" @@ -299,15 +297,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -353,17 +342,6 @@ dependencies = [ "half", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.5.20" @@ -393,7 +371,7 @@ version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn", @@ -678,12 +656,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - [[package]] name = "half" version = "2.4.1" @@ -706,6 +678,12 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -1005,17 +983,19 @@ version = "0.0.0" dependencies = [ "assert_matches", "base", - "bindgen", "bitflags", "bumpalo", + "cbindgen", "cc", "criterion", "ctor", "duct", "indexmap", "jsparser", + "libc", "logging", - "rustc-hash 2.0.0", + "paste", + "rustc-hash", "static_assertions", "thiserror", ] @@ -1079,16 +1059,6 @@ version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" -[[package]] -name = "libloading" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" -dependencies = [ - "cfg-if", - "windows-targets", -] - [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -1209,12 +1179,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.8.0" @@ -1246,16 +1210,6 @@ dependencies = [ "serde", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1446,16 +1400,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "prettyplease" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" -dependencies = [ - "proc-macro2", - "syn", -] - [[package]] name = "proc-macro2" version = "1.0.89" @@ -1577,12 +1521,6 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.0.0" @@ -1672,6 +1610,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_test" version = "1.0.177" @@ -1928,6 +1875,40 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.5.1" @@ -2315,6 +2296,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + [[package]] name = "yansi" version = "1.0.1" diff --git a/libs/jsruntime/.gitignore b/libs/jsruntime/.gitignore index e3928342..e32477de 100644 --- a/libs/jsruntime/.gitignore +++ b/libs/jsruntime/.gitignore @@ -1,5 +1,5 @@ -/src/llvmir/runtime.hh -/src/llvmir/executor.codegen.cc -/src/llvmir/type_holder.cc -/src/llvmir/type_holder.hh +/src/llvmir/bridge.hh +/src/llvmir/compiler/type_holder.cc +/src/llvmir/compiler/type_holder.hh +/src/llvmir/executor/impl.codegen.hh /tests/evaluate.rs diff --git a/libs/jsruntime/Cargo.toml b/libs/jsruntime/Cargo.toml index 51ef7452..27510d43 100644 --- a/libs/jsruntime/Cargo.toml +++ b/libs/jsruntime/Cargo.toml @@ -12,13 +12,15 @@ bitflags = "2.6.0" bumpalo = "3.16.0" indexmap = "2.6.0" jsparser = { path = "../jsparser", features = ["location"] } +libc = "0.2.162" logging = { path = "../logging" } +paste = "1.0.15" rustc-hash = "2.0.0" static_assertions = "1.1.0" thiserror = "2.0.1" [build-dependencies] -bindgen = "0.70.1" +cbindgen = { git = "https://github.com/masnagam/cbindgen.git", branch = "fix-issue-43" } cc = { version = "1.1.37", features = ["parallel"] } duct = "0.13.7" diff --git a/libs/jsruntime/build.rs b/libs/jsruntime/build.rs index 83fe99c1..08ea4806 100644 --- a/libs/jsruntime/build.rs +++ b/libs/jsruntime/build.rs @@ -3,44 +3,56 @@ use std::path::PathBuf; use duct::cmd; +static CBINDGEN_TOML: &str = "src/llvmir/cbindgen.toml"; + +static BRIDGE_SOURCE_FILES: &[&str] = &[ + "src/llvmir/mod.rs", + "src/llvmir/executor/bridge.rs", + "src/llvmir/module/bridge.rs", +]; + static LLVM_COMPONENTS: &[&str] = &["core", "orcjit", "x86"]; static LLVMIR_SOURCE_FILES: &[&str] = &[ "src/llvmir/bridge.hh", - "src/llvmir/runtime.hh", - "src/llvmir/bridge.cc", - "src/llvmir/compiler.hh", - "src/llvmir/executor.cc", - "src/llvmir/executor.codegen.cc", - "src/llvmir/executor.hh", - "src/llvmir/helper.cc", - "src/llvmir/helper.hh", - "src/llvmir/macros.hh", - "src/llvmir/module.hh", - "src/llvmir/type_holder.hh", - "src/llvmir/type_holder.cc", + "src/llvmir/compiler/impl.hh", + "src/llvmir/compiler/peer.cc", + "src/llvmir/compiler/type_holder.hh", + "src/llvmir/compiler/type_holder.cc", + "src/llvmir/executor/impl.codegen.hh", + "src/llvmir/executor/impl.hh", + "src/llvmir/executor/peer.cc", + "src/llvmir/module/impl.hh", + "src/llvmir/module/peer.cc", + "src/llvmir/peer.cc", ]; fn main() { let profile = std::env::var("PROFILE").unwrap(); + let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); - // Generate bindings for Rust. - let input_file = "src/llvmir/bridge.hh"; - let output_file = out_dir.join("bridge.rs"); - bindgen::Builder::default() - .header(input_file) - .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) - // TODO: Using rustified enum types causes performance regression in fib(41). - // However, wedon't know the exact reason at this point. Deeper investigation is needed. - .derive_debug(false) - .derive_eq(true) + // Generate bridge.hh for LLVM IR bridge. + + let config = cbindgen::Config::from_file(CBINDGEN_TOML).expect("Unable to load cbindgen.toml"); + + cbindgen::Builder::new() + .with_config(config) + .with_crate(crate_dir) .generate() - .expect("Unable to generate bindings for Rust") - .write_to_file(output_file) - .expect("Couldn't write bindings for Rust"); + .expect("Unable to generate bindings") + .write_to_file("src/llvmir/bridge.hh"); + + // Rebuild when cbindgen.toml change. + println!("cargo::rerun-if-changed={CBINDGEN_TOML}"); + + // Rebuild when any of BRIDGE_SOURCE_FILES change. + for src in BRIDGE_SOURCE_FILES { + println!("cargo::rerun-if-changed={src}"); + } + + // Build LLVM IR bridge. - // Build LLVM-IR glue. let llvm_config = LlvmConfig::new(); let cc_files = LLVMIR_SOURCE_FILES .iter() diff --git a/libs/jsruntime/src/lib.rs b/libs/jsruntime/src/lib.rs index 6394da7c..807980a4 100644 --- a/libs/jsruntime/src/lib.rs +++ b/libs/jsruntime/src/lib.rs @@ -5,20 +5,19 @@ mod semantics; mod tasklet; mod types; +use std::ffi::c_void; + use jsparser::SymbolRegistry; use function::FunctionId; use function::FunctionRegistry; use llvmir::Executor; use types::ReturnValue; -use llvmir::Status; pub use llvmir::CompileError; pub use llvmir::Module; -pub use types::Value; pub use semantics::Program; - -type VoidPtr = *mut std::ffi::c_void; +pub use types::Value; pub fn initialize() { llvmir::initialize(); @@ -63,12 +62,12 @@ pub struct Runtime { impl Runtime { pub fn with_extension(extension: X) -> Self { - let runtime_bridge = llvmir::runtime_bridge::(); + let functions = llvmir::RuntimeFunctions::new::(); Self { pref: Default::default(), symbol_registry: Default::default(), function_registry: FunctionRegistry::new(), - executor: Executor::with_runtime_bridge(&runtime_bridge), + executor: Executor::new(&functions), allocator: bumpalo::Bump::new(), tasklet_system: tasklet::System::new(), extension, @@ -99,7 +98,7 @@ impl Runtime { let symbol = self.symbol_registry.intern_str(name); let func_id = self.function_registry.register_host_function(symbol); self.executor - .register_host_function(func_id, into_host_lambda(host_fn)); + .register_host_function(func_id, types::into_lambda(host_fn)); logger::debug!(event = "register_host_function", name, ?symbol, ?func_id); } @@ -119,8 +118,7 @@ impl Runtime { // argv std::ptr::null_mut(), // retv - // TODO: remove type cast - &mut retv as *mut Value as *mut llvmir::bridge::Value, + &mut retv, ) }, None => unreachable!(), @@ -128,8 +126,8 @@ impl Runtime { retv.into_result(status) } - fn as_void_ptr(&mut self) -> VoidPtr { - self as *mut Self as VoidPtr + fn as_void_ptr(&mut self) -> *mut c_void { + self as *mut Self as *mut c_void } fn allocator(&self) -> &bumpalo::Bump { @@ -145,44 +143,3 @@ where Runtime::with_extension(Default::default()) } } - -// See https://www.reddit.com/r/rust/comments/ksfk4j/comment/gifzlhg/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button - -// TODO: remove llvmir::bridge::Value -type HostLambda = unsafe extern "C" fn(VoidPtr, VoidPtr, usize, *mut llvmir::bridge::Value, *mut llvmir::bridge::Value) -> Status; - -// This function generates a wrapper function for each `host_func` at compile time. -#[inline(always)] -fn into_host_lambda(host_fn: F) -> HostLambda -where - F: Fn(&mut Runtime, &[Value]) -> R + Send + Sync + 'static, - R: Clone + ReturnValue, -{ - debug_assert_eq!(std::mem::size_of::(), 0, "Function must have zero size"); - std::mem::forget(host_fn); - host_fn_wrapper:: -} - -unsafe extern "C" fn host_fn_wrapper( - runtime: VoidPtr, - _context: VoidPtr, - argc: usize, - // TODO: remove llvmir::bridge::Value - argv: *mut llvmir::bridge::Value, - retv: *mut llvmir::bridge::Value, -) -> Status -where - F: Fn(&mut Runtime, &[Value]) -> R + Send + Sync + 'static, - R: Clone + ReturnValue, -{ - #[allow(clippy::uninit_assumed_init)] - let host_fn = std::mem::MaybeUninit::::uninit().assume_init(); - let runtime = &mut *(runtime as *mut Runtime); - let args = std::slice::from_raw_parts(argv as *const Value, argc); - // TODO: The return value is copied twice. That's inefficient. - let result = host_fn(runtime, args); - // TODO: remove type cast - let retv = &mut *(retv as *mut Value); - *retv = result.value(); - result.status() -} diff --git a/libs/jsruntime/src/llvmir/Makefile b/libs/jsruntime/src/llvmir/Makefile index 9756b56f..70104589 100644 --- a/libs/jsruntime/src/llvmir/Makefile +++ b/libs/jsruntime/src/llvmir/Makefile @@ -4,10 +4,9 @@ PROJ_DIR := $(realpath ../../../..) TOOLS_BIN := $(PROJ_DIR)/tools/bin CODEGEN_TARGETS := \ - runtime.hh \ - executor.codegen.cc \ - type_holder.cc \ - type_holder.hh + compiler/type_holder.cc \ + compiler/type_holder.hh \ + executor/impl.codegen.hh .PHONY: codegen codegen: $(CODEGEN_TARGETS) diff --git a/libs/jsruntime/src/llvmir/README.md b/libs/jsruntime/src/llvmir/README.md index a8752b6a..a861a8c3 100644 --- a/libs/jsruntime/src/llvmir/README.md +++ b/libs/jsruntime/src/llvmir/README.md @@ -2,6 +2,15 @@ The `llvmir` module implements a compiler and an executor targeting [LLVM IR]. +## Source Tree Structure + +`bridge.hh` will be generated by the build script using [`cbindgen`]. `bridge.rs` in each module +defines interfaces which have to be implemented using LLVM. `peer.cc` in each module implements +the interfaces defined in the corresponding `bridge.rs`. `impl.hh` in each module contains types +which are used in `peer.cc`. + +[`cbindgen`]: https://github.com/masnagam/cbindgen/tree/fix-issue-43 + ## Stacks used in the LLVM IR compiler The LLVM IR compiler uses multiple stacks for different purposes. diff --git a/libs/jsruntime/src/llvmir/bridge.cc b/libs/jsruntime/src/llvmir/bridge.cc deleted file mode 100644 index 9abd2eca..00000000 --- a/libs/jsruntime/src/llvmir/bridge.cc +++ /dev/null @@ -1,773 +0,0 @@ -#include "bridge.hh" - -#include -#include - -#include - -#include "compiler.hh" -#include "executor.hh" -#include "helper.hh" -#include "module.hh" - -#define LLVM_BB(bb) (reinterpret_cast(bb)) -#define PEER_BB(bb) (reinterpret_cast(bb)) - -#define LLVM_LAMBDA(lambda) (reinterpret_cast(lambda)) -#define PEER_LAMBDA(lambda) (reinterpret_cast(lambda)) - -#define LLVM_VALUE(value) (reinterpret_cast(value)) -#define PEER_BOOLEAN(value) (reinterpret_cast(value)) -#define PEER_NUMBER(value) (reinterpret_cast(value)) -#define PEER_CLOSURE(value) (reinterpret_cast(value)) -#define PEER_COROUTINE(value) (reinterpret_cast(value)) -#define PEER_PROMISE(value) (reinterpret_cast(value)) -#define PEER_VALUE(value) (reinterpret_cast(value)) -#define PEER_ARGV(value) (reinterpret_cast(value)) -#define PEER_STATUS(value) (reinterpret_cast(value)) -#define PEER_CAPTURE(value) (reinterpret_cast(value)) - -#define LLVM_SWITCH(inst) (reinterpret_cast(inst)) -#define PEER_SWITCH(inst) (reinterpret_cast(inst)) - -void llvmir_initialize() { - // Uncomment if you want to enable LLVM_DEBUG(). - // llvm::DebugFlag = true; - - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - llvm::InitializeNativeTargetAsmParser(); -} - -// module - -void module_peer_print(Module* self, bool stderr) { - self->Print(stderr); -} - -void module_peer_delete(Module* self) { - delete self; -} - -// compilation - -Compiler* compiler_peer_new() { - return new Compiler(); -} - -void compiler_peer_delete(Compiler* self) { - delete self; -} - -void compiler_peer_start(Compiler* self, bool enable_labels) { - if (enable_labels) { - self->EnableLabels(); - } -} - -Module* compiler_peer_end(Compiler* self) { - return self->TakeModule(); -} - -void compiler_peer_set_data_layout(Compiler* self, const char* data_layout) { - self->SetDataLayout(data_layout); -} - -void compiler_peer_set_target_triple(Compiler* self, const char* triple) { - self->SetTargetTriple(triple); -} - -void compiler_peer_start_function(Compiler* self, uint32_t func_id) { - self->StartFunction(func_id); -} - -void compiler_peer_end_function(Compiler* self, bool optimize) { - self->EndFunction(optimize); -} - -void compiler_peer_set_locals_block(Compiler* self, BasicBlock* block) { - self->SetLocalsBlock(LLVM_BB(block)); -} - -LambdaIr* compiler_peer_get_function(Compiler* self, uint32_t func_id) { - return PEER_LAMBDA(self->GetFunction(func_id)); -} - -// basic block - -BasicBlock* compiler_peer_create_basic_block(Compiler* self, const char* name, size_t name_len) { - return PEER_BB(self->CreateBasicBlock(name, name_len)); -} - -BasicBlock* compiler_peer_get_basic_block(const Compiler* self) { - return PEER_BB(self->GetBasicBlock()); -} - -void compiler_peer_set_basic_block(Compiler* self, BasicBlock* block) { - self->SetBasicBlock(LLVM_BB(block)); -} - -void compiler_peer_move_basic_block_after(Compiler* self, BasicBlock* block) { - self->MoveBasicBlockAfter(LLVM_BB(block)); -} - -bool compiler_peer_is_basic_block_terminated(Compiler* self, BasicBlock* block) { - return self->IsBasicBlockTerminated(LLVM_BB(block)); -} - -// jump - -void compiler_peer_create_br(Compiler* self, BasicBlock* block) { - self->CreateBr(LLVM_BB(block)); -} - -void compiler_peer_create_cond_br(Compiler* self, - BooleanIr* cond, - BasicBlock* then_block, - BasicBlock* else_block) { - self->CreateCondBr(LLVM_VALUE(cond), LLVM_BB(then_block), LLVM_BB(else_block)); -} - -// undefined - -BooleanIr* compiler_peer_create_is_undefined(Compiler* self, ValueIr* value) { - return PEER_BOOLEAN(self->CreateIsUndefined(LLVM_VALUE(value))); -} - -// null - -BooleanIr* compiler_peer_create_is_null(Compiler* self, ValueIr* value) { - return PEER_BOOLEAN(self->CreateIsNull(LLVM_VALUE(value))); -} - -BooleanIr* compiler_peer_create_is_non_nullish(Compiler* self, ValueIr* value) { - return PEER_BOOLEAN(self->CreateIsNonNullish(LLVM_VALUE(value))); -} - -// boolean - -BooleanIr* compiler_peer_create_is_boolean(Compiler* self, ValueIr* value) { - return PEER_BOOLEAN(self->CreateIsBoolean(LLVM_VALUE(value))); -} - -BooleanIr* compiler_peer_create_is_same_boolean(Compiler* self, BooleanIr* a, BooleanIr* b) { - return PEER_BOOLEAN(self->CreateIsSameBoolean(LLVM_VALUE(a), LLVM_VALUE(b))); -} - -BooleanIr* compiler_peer_create_number_to_boolean(Compiler* self, NumberIr* value) { - return PEER_BOOLEAN(self->CreateNumberToBoolean(LLVM_VALUE(value))); -} - -BooleanIr* compiler_peer_create_to_boolean(Compiler* self, ValueIr* value) { - return PEER_BOOLEAN(self->CreateToBoolean(LLVM_VALUE(value))); -} - -BooleanIr* compiler_peer_get_boolean(Compiler* self, bool value) { - return PEER_BOOLEAN(self->GetBoolean(value)); -} - -BooleanIr* compiler_peer_create_logical_not(Compiler* self, BooleanIr* boolean) { - return PEER_BOOLEAN(self->CreateLogicalNot(LLVM_VALUE(boolean))); -} - -BooleanIr* compiler_peer_create_boolean_phi(Compiler* self, - BooleanIr* then_value, - BasicBlock* then_block, - BooleanIr* else_value, - BasicBlock* else_block) { - return PEER_BOOLEAN(self->CreateBooleanPhi(LLVM_VALUE(then_value), LLVM_BB(then_block), - LLVM_VALUE(else_value), LLVM_BB(else_block))); -} - -// number - -BooleanIr* compiler_peer_create_is_number(Compiler* self, ValueIr* value) { - return PEER_BOOLEAN(self->CreateIsNumber(LLVM_VALUE(value))); -} - -BooleanIr* compiler_peer_create_is_same_number(Compiler* self, NumberIr* a, NumberIr* b) { - return PEER_BOOLEAN(self->CreateIsSameNumber(LLVM_VALUE(a), LLVM_VALUE(b))); -} - -NumberIr* compiler_peer_create_boolean_to_number(Compiler* self, BooleanIr* value) { - return PEER_NUMBER(self->CreateUIToFP(LLVM_VALUE(value))); -} - -NumberIr* compiler_peer_to_numeric(Compiler* self, ValueIr* value) { - return PEER_NUMBER(self->ToNumeric(LLVM_VALUE(value))); -} - -NumberIr* compiler_peer_get_nan(Compiler* self) { - return PEER_NUMBER(self->GetNan()); -} - -NumberIr* compiler_peer_get_zero(Compiler* self) { - return PEER_NUMBER(self->GetZero()); -} - -NumberIr* compiler_peer_get_number(Compiler* self, double value) { - return PEER_NUMBER(self->GetNumber(value)); -} - -NumberIr* compiler_peer_create_bitwise_not(Compiler* self, NumberIr* value) { - return PEER_NUMBER(self->CreateBitwiseNot(LLVM_VALUE(value))); -} - -NumberIr* compiler_peer_create_fneg(Compiler* self, NumberIr* value) { - return PEER_NUMBER(self->CreateFNeg(LLVM_VALUE(value))); -} - -NumberIr* compiler_peer_create_fmul(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_NUMBER(self->CreateFMul(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -NumberIr* compiler_peer_create_fdiv(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_NUMBER(self->CreateFDiv(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -NumberIr* compiler_peer_create_frem(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_NUMBER(self->CreateFRem(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -NumberIr* compiler_peer_create_fadd(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_NUMBER(self->CreateFAdd(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -NumberIr* compiler_peer_create_fsub(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_NUMBER(self->CreateFSub(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -NumberIr* compiler_peer_create_left_shift(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_NUMBER(self->CreateLeftShift(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -NumberIr* compiler_peer_create_signed_right_shift(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_NUMBER(self->CreateSignedRightShift(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -NumberIr* compiler_peer_create_unsigned_right_shift(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_NUMBER(self->CreateUnsignedRightShift(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -NumberIr* compiler_peer_create_bitwise_and(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_NUMBER(self->CreateBitwiseAnd(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -NumberIr* compiler_peer_create_bitwise_xor(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_NUMBER(self->CreateBitwiseXor(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -NumberIr* compiler_peer_create_bitwise_or(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_NUMBER(self->CreateBitwiseOr(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -BooleanIr* compiler_peer_create_less_than(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_BOOLEAN(self->CreateLessThan(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -BooleanIr* compiler_peer_create_greater_than(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_BOOLEAN(self->CreateGreaterThan(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -BooleanIr* compiler_peer_create_less_than_or_equal(Compiler* self, NumberIr* lhs, NumberIr* rhs) { - return PEER_BOOLEAN(self->CreateLessThanOrEqual(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -BooleanIr* compiler_peer_create_greater_than_or_equal(Compiler* self, - NumberIr* lhs, - NumberIr* rhs) { - return PEER_BOOLEAN(self->CreateGreaterThanOrEqual(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -NumberIr* compiler_peer_create_number_phi(Compiler* self, - NumberIr* then_value, - BasicBlock* then_block, - NumberIr* else_value, - BasicBlock* else_block) { - return PEER_NUMBER(self->CreateNumberPhi(LLVM_VALUE(then_value), LLVM_BB(then_block), - LLVM_VALUE(else_value), LLVM_BB(else_block))); -} - -// closure - -BooleanIr* compiler_peer_create_is_closure(Compiler* self, ValueIr* value) { - return PEER_BOOLEAN(self->CreateIsClosure(LLVM_VALUE(value))); -} - -BooleanIr* compiler_peer_create_is_same_closure(Compiler* self, ClosureIr* a, ClosureIr* b) { - return PEER_BOOLEAN(self->CreateIsSameClosure(LLVM_VALUE(a), LLVM_VALUE(b))); -} - -ClosureIr* compiler_peer_get_closure_nullptr(Compiler* self) { - return PEER_CLOSURE(self->GetNullptr()); -} - -ClosureIr* compiler_peer_create_closure(Compiler* self, LambdaIr* lambda, uint16_t num_captures) { - return PEER_CLOSURE(self->CreateClosure(LLVM_LAMBDA(lambda), num_captures)); -} - -void compiler_peer_create_store_capture_to_closure(Compiler* self, - CaptureIr* capture, - ClosureIr* closure, - uint16_t index) { - self->CreateStoreCapturePtrToClosure(LLVM_VALUE(capture), LLVM_VALUE(closure), index); -} - -StatusIr* compiler_peer_create_call_on_closure(Compiler* self, - ClosureIr* closure, - uint16_t argc, - ArgvIr* argv, - ValueIr* retv) { - return PEER_STATUS( - self->CreateCallOnClosure(LLVM_VALUE(closure), argc, LLVM_VALUE(argv), LLVM_VALUE(retv))); -} - -ClosureIr* compiler_peer_create_closure_phi(Compiler* self, - ClosureIr* then_value, - BasicBlock* then_block, - ClosureIr* else_value, - BasicBlock* else_block) { - return PEER_CLOSURE(self->CreateClosurePhi(LLVM_VALUE(then_value), LLVM_BB(then_block), - LLVM_VALUE(else_value), LLVM_BB(else_block))); -} - -// promise - -BooleanIr* compiler_peer_create_is_promise(Compiler* self, ValueIr* value) { - return PEER_BOOLEAN(self->CreateIsPromise(LLVM_VALUE(value))); -} - -BooleanIr* compiler_peer_create_is_same_promise(Compiler* self, PromiseIr* a, PromiseIr* b) { - return PEER_BOOLEAN(self->CreateIsSamePromise(LLVM_VALUE(a), LLVM_VALUE(b))); -} - -PromiseIr* compiler_peer_create_register_promise(Compiler* self, CoroutineIr* coroutine) { - return PEER_PROMISE(self->CreateRegisterPromise(LLVM_VALUE(coroutine))); -} - -void compiler_peer_create_await_promise(Compiler* self, PromiseIr* promise, PromiseIr* awaiting) { - self->CreateAwaitPromise(LLVM_VALUE(promise), LLVM_VALUE(awaiting)); -} - -void compiler_peer_create_resume(Compiler* self, PromiseIr* promise) { - self->CreateResume(LLVM_VALUE(promise)); -} - -void compiler_peer_create_emit_promise_resolved(Compiler* self, - PromiseIr* promise, - ValueIr* result) { - self->CreateEmitPromiseResolved(LLVM_VALUE(promise), LLVM_VALUE(result)); -} - -// value - -BooleanIr* compiler_peer_create_has_value(Compiler* self, ValueIr* value) { - return PEER_BOOLEAN(self->CreateHasValue(LLVM_VALUE(value))); -} - -BooleanIr* compiler_peer_create_is_loosely_equal(Compiler* self, ValueIr* lhs, ValueIr* rhs) { - return PEER_BOOLEAN(self->CreateIsLooselyEqual(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -BooleanIr* compiler_peer_create_is_strictly_equal(Compiler* self, ValueIr* lhs, ValueIr* rhs) { - return PEER_BOOLEAN(self->CreateIsStrictlyEqual(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); -} - -BooleanIr* compiler_peer_create_is_same_boolean_value(Compiler* self, - ValueIr* value, - BooleanIr* boolean) { - return PEER_BOOLEAN(self->CreateIsSameBooleanValue(LLVM_VALUE(value), LLVM_VALUE(boolean))); -} - -BooleanIr* compiler_peer_create_is_same_number_value(Compiler* self, - ValueIr* value, - NumberIr* number) { - return PEER_BOOLEAN(self->CreateIsSameNumberValue(LLVM_VALUE(value), LLVM_VALUE(number))); -} - -BooleanIr* compiler_peer_create_is_same_closure_value(Compiler* self, - ValueIr* value, - ClosureIr* closure) { - return PEER_BOOLEAN(self->CreateIsSameClosureValue(LLVM_VALUE(value), LLVM_VALUE(closure))); -} - -BooleanIr* compiler_peer_create_is_same_promise_value(Compiler* self, - ValueIr* value, - PromiseIr* promise) { - return PEER_BOOLEAN(self->CreateIsSamePromiseValue(LLVM_VALUE(value), LLVM_VALUE(promise))); -} - -ValueIr* compiler_peer_create_undefined_to_any(Compiler* self) { - return PEER_VALUE(self->CreateUndefinedToAny()); -} - -ValueIr* compiler_peer_create_null_to_any(Compiler* self) { - return PEER_VALUE(self->CreateNullToAny()); -} - -ValueIr* compiler_peer_create_boolean_to_any(Compiler* self, BooleanIr* boolean) { - return PEER_VALUE(self->CreateBooleanToAny(LLVM_VALUE(boolean))); -} - -ValueIr* compiler_peer_create_number_to_any(Compiler* self, NumberIr* value) { - return PEER_VALUE(self->CreateNumberToAny(LLVM_VALUE(value))); -} - -ValueIr* compiler_peer_create_closure_to_any(Compiler* self, ClosureIr* value) { - return PEER_VALUE(self->CreateClosureToAny(LLVM_VALUE(value))); -} - -ValueIr* compiler_peer_create_value_phi(Compiler* self, - ValueIr* then_value, - BasicBlock* then_block, - ValueIr* else_value, - BasicBlock* else_block) { - return PEER_VALUE(self->CreateValuePhi(LLVM_VALUE(then_value), LLVM_BB(then_block), - LLVM_VALUE(else_value), LLVM_BB(else_block))); -} - -ValueIr* compiler_peer_create_local_value(Compiler* self, uint16_t index) { - return PEER_VALUE(self->CreateLocalValue(index)); -} - -void compiler_peer_create_store_none_to_value(Compiler* self, ValueIr* dest) { - self->CreateStoreNoneToValue(LLVM_VALUE(dest)); -} - -void compiler_peer_create_store_undefined_to_value(Compiler* self, ValueIr* dest) { - self->CreateStoreUndefinedToValue(LLVM_VALUE(dest)); -} - -void compiler_peer_create_store_null_to_value(Compiler* self, ValueIr* dest) { - self->CreateStoreNullToValue(LLVM_VALUE(dest)); -} - -void compiler_peer_create_store_boolean_to_value(Compiler* self, BooleanIr* value, ValueIr* dest) { - self->CreateStoreBooleanToValue(LLVM_VALUE(value), LLVM_VALUE(dest)); -} - -void compiler_peer_create_store_number_to_value(Compiler* self, NumberIr* value, ValueIr* dest) { - self->CreateStoreNumberToValue(LLVM_VALUE(value), LLVM_VALUE(dest)); -} - -void compiler_peer_create_store_closure_to_value(Compiler* self, ClosureIr* value, ValueIr* dest) { - self->CreateStoreClosureToValue(LLVM_VALUE(value), LLVM_VALUE(dest)); -} - -void compiler_peer_create_store_promise_to_value(Compiler* self, PromiseIr* value, ValueIr* dest) { - self->CreateStorePromiseToValue(LLVM_VALUE(value), LLVM_VALUE(dest)); -} - -void compiler_peer_create_store_value_to_value(Compiler* self, ValueIr* value, ValueIr* dest) { - self->CreateStoreValueToValue(LLVM_VALUE(value), LLVM_VALUE(dest)); -} - -ClosureIr* compiler_peer_create_load_closure_from_value(Compiler* self, ValueIr* value) { - return PEER_CLOSURE(self->CreateLoadClosureFromValue(LLVM_VALUE(value))); -} - -PromiseIr* compiler_peer_create_load_promise_from_value(Compiler* self, ValueIr* value) { - return PEER_PROMISE(self->CreateLoadPromiseFromValue(LLVM_VALUE(value))); -} - -// argv - -ArgvIr* compiler_peer_get_argv_nullptr(Compiler* self) { - return PEER_ARGV(self->GetNullptr()); -} - -ArgvIr* compiler_peer_create_argv(Compiler* self, uint16_t argc) { - return PEER_ARGV(self->CreateArgv(argc)); -} - -ValueIr* compiler_peer_create_get_arg_in_argv(Compiler* self, ArgvIr* argv, uint16_t index) { - return PEER_VALUE(self->CreateGetArgInArgv(LLVM_VALUE(argv), index)); -} - -ValueIr* compiler_peer_create_get_argument_value_ptr(Compiler* self, uint16_t index) { - return PEER_VALUE(self->CreateGetArgumentValuePtr(index)); -} - -// retv - -ValueIr* compiler_peer_create_retv(Compiler* self) { - return PEER_VALUE(self->CreateRetv()); -} - -void compiler_peer_create_store_undefined_to_retv(Compiler* self) { - self->CreateStoreUndefinedToRetv(); -} - -void compiler_peer_create_store_null_to_retv(Compiler* self) { - self->CreateStoreNullToRetv(); -} - -void compiler_peer_create_store_boolean_to_retv(Compiler* self, BooleanIr* value) { - self->CreateStoreBooleanToRetv(LLVM_VALUE(value)); -} - -void compiler_peer_create_store_number_to_retv(Compiler* self, NumberIr* value) { - self->CreateStoreNumberToRetv(LLVM_VALUE(value)); -} - -void compiler_peer_create_store_closure_to_retv(Compiler* self, ClosureIr* value) { - self->CreateStoreClosureToRetv(LLVM_VALUE(value)); -} - -void compiler_peer_create_store_promise_to_retv(Compiler* self, PromiseIr* value) { - self->CreateStorePromiseToRetv(LLVM_VALUE(value)); -} - -void compiler_peer_create_store_value_to_retv(Compiler* self, ValueIr* value) { - self->CreateStoreValueToRetv(LLVM_VALUE(value)); -} - -ValueIr* compiler_peer_get_exception(Compiler* self) { - return PEER_VALUE(self->GetException()); -} - -// status - -void compiler_peer_create_alloc_status(Compiler* self) { - self->CreateAllocStatus(); -} - -void compiler_peer_create_store_normal_status(Compiler* self) { - self->CreateStoreNormalStatus(); -} - -void compiler_peer_create_store_exception_status(Compiler* self) { - self->CreateStoreExceptionStatus(); -} - -BooleanIr* compiler_peer_create_is_exception_status(Compiler* self, StatusIr* status) { - return PEER_BOOLEAN(self->CreateIsExceptionStatus(LLVM_VALUE(status))); -} - -// flow selector - -void compiler_peer_create_alloc_flow_selector(Compiler* self) { - self->CreateAllocFlowSelector(); -} - -void compiler_peer_create_set_flow_selector_normal(Compiler* self) { - self->CreateSetFlowSelectorNormal(); -} - -void compiler_peer_create_set_flow_selector_return(Compiler* self) { - self->CreateSetFlowSelectorReturn(); -} - -void compiler_peer_create_set_flow_selector_throw(Compiler* self) { - self->CreateSetFlowSelectorThrow(); -} - -void compiler_peer_create_set_flow_selector_break(Compiler* self, uint32_t depth) { - self->CreateSetFlowSelectorBreak(depth); -} - -void compiler_peer_create_set_flow_selector_continue(Compiler* self, uint32_t depth) { - self->CreateSetFlowSelectorContinue(depth); -} - -BooleanIr* compiler_peer_create_is_flow_selector_normal(Compiler* self) { - return PEER_BOOLEAN(self->CreateIsFlowSelectorNormal()); -} - -BooleanIr* compiler_peer_create_is_flow_selector_normal_or_continue(Compiler* self, - uint32_t depth) { - return PEER_BOOLEAN(self->CreateIsFlowSelectorNormalOrContinue(depth)); -} - -BooleanIr* compiler_peer_create_is_flow_selector_break_or_continue(Compiler* self, - uint32_t depth) { - return PEER_BOOLEAN(self->CreateIsFlowSelectorBreakOrContinue(depth)); -} - -BooleanIr* compiler_peer_create_is_flow_selector_break(Compiler* self, uint32_t depth) { - return PEER_BOOLEAN(self->CreateIsFlowSelectorBreak(depth)); -} - -// capture - -CaptureIr* compiler_peer_create_capture(Compiler* self, ValueIr* value) { - return PEER_CAPTURE(self->CreateCapture(LLVM_VALUE(value))); -} - -ValueIr* compiler_peer_create_get_capture_value_ptr(Compiler* self, uint16_t index) { - return PEER_VALUE(self->CreateGetCaptureValuePtr(index)); -} - -void compiler_peer_create_escape_value(Compiler* self, CaptureIr* capture, ValueIr* value) { - self->CreateEscapeValue(LLVM_VALUE(capture), LLVM_VALUE(value)); -} - -CaptureIr* compiler_peer_create_load_capture(Compiler* self, uint16_t index) { - return PEER_CAPTURE(self->CreateLoadCapture(index)); -} - -// coroutine - -CoroutineIr* compiler_peer_create_coroutine(Compiler* self, - ClosureIr* closure, - uint16_t num_locals, - uint16_t scratch_buffer_len) { - return PEER_COROUTINE( - self->CreateCoroutine(LLVM_VALUE(closure), num_locals, scratch_buffer_len)); -} - -SwitchIr* compiler_peer_create_switch_for_coroutine(Compiler* self, - BasicBlock* block, - uint32_t num_states) { - return PEER_SWITCH(self->CreateSwitchForCoroutine(LLVM_BB(block), num_states)); -} - -void compiler_peer_create_add_state_for_coroutine(Compiler* self, - SwitchIr* inst, - uint32_t state, - BasicBlock* block) { - self->CreateAddStateForCoroutine(LLVM_SWITCH(inst), state, LLVM_BB(block)); -} - -void compiler_peer_create_suspend(Compiler* self) { - self->CreateSuspend(); -} - -void compiler_peer_create_set_coroutine_state(Compiler* self, uint32_t state) { - self->CreateSetCoroutineState(state); -} - -void compiler_peer_create_set_captures_for_coroutine(Compiler* self) { - self->CreateSetCapturesForCoroutine(); -} - -ValueIr* compiler_peer_create_get_local_ptr_from_coroutine(Compiler* self, uint16_t index) { - return PEER_VALUE(self->CreateGetLocalPtrFromCoroutine(index)); -} - -void compiler_peer_create_write_boolean_to_scratch_buffer(Compiler* self, - uint32_t offset, - BooleanIr* value) { - self->CreateWriteBooleanToScratchBuffer(offset, LLVM_VALUE(value)); -} - -BooleanIr* compiler_peer_create_read_boolean_from_scratch_buffer(Compiler* self, uint32_t offset) { - return PEER_BOOLEAN(self->CreateReadBooleanFromScratchBuffer(offset)); -} - -void compiler_peer_create_write_number_to_scratch_buffer(Compiler* self, - uint32_t offset, - NumberIr* value) { - self->CreateWriteNumberToScratchBuffer(offset, LLVM_VALUE(value)); -} - -NumberIr* compiler_peer_create_read_number_from_scratch_buffer(Compiler* self, uint32_t offset) { - return PEER_NUMBER(self->CreateReadNumberFromScratchBuffer(offset)); -} - -void compiler_peer_create_write_closure_to_scratch_buffer(Compiler* self, - uint32_t offset, - ClosureIr* value) { - self->CreateWriteClosureToScratchBuffer(offset, LLVM_VALUE(value)); -} - -ClosureIr* compiler_peer_create_read_closure_from_scratch_buffer(Compiler* self, uint32_t offset) { - return PEER_CLOSURE(self->CreateReadClosureFromScratchBuffer(offset)); -} - -void compiler_peer_create_write_promise_to_scratch_buffer(Compiler* self, - uint32_t offset, - PromiseIr* value) { - self->CreateWritePromiseToScratchBuffer(offset, LLVM_VALUE(value)); -} - -PromiseIr* compiler_peer_create_read_promise_from_scratch_buffer(Compiler* self, uint32_t offset) { - return PEER_PROMISE(self->CreateReadPromiseFromScratchBuffer(offset)); -} - -void compiler_peer_create_write_value_to_scratch_buffer(Compiler* self, - uint32_t offset, - ValueIr* value) { - self->CreateWriteValueToScratchBuffer(offset, LLVM_VALUE(value)); -} - -ValueIr* compiler_peer_create_read_value_from_scratch_buffer(Compiler* self, uint32_t offset) { - return PEER_VALUE(self->CreateReadValueFromScratchBuffer(offset)); -} - -// scope cleanup checker - -void compiler_peer_enable_scope_cleanup_checker(Compiler* self, bool is_coroutine) { - self->EnableScopeCleanupChecker(is_coroutine); -} - -void compiler_peer_set_scope_id_for_checker(Compiler* self, uint16_t scope_id) { - self->SetScopeIdForChecker(scope_id); -} - -void compiler_peer_assert_scope_id(Compiler* self, uint16_t expected) { - self->AssertScopeId(expected); -} - -// print - -void compiler_peer_create_print_value(Compiler* self, ValueIr* value, const char* msg) { - assert(msg != nullptr); - self->CreatePrintValue(LLVM_VALUE(value), msg); -} - -// debugger - -void compiler_peer_create_debugger(Compiler* self) { - self->CreateDebugger(); -} - -// unreachable - -void compiler_peer_create_unreachable(Compiler* self, const char* msg) { - self->CreateUnreachable(msg); -} - -// executor - -Executor* executor_peer_new() { - return llvm::cantFail(Executor::Create()); -} - -void executor_peer_delete(Executor* self) { - delete self; -} - -void executor_peer_register_runtime(Executor* self, const Runtime* runtime) { - self->RegisterRuntime(runtime); -} - -void executor_peer_register_host_function(Executor* self, uint32_t func_id, Lambda lambda) { - self->RegisterHostFunction(func_id, lambda); -} - -void executor_peer_register_module(Executor* self, Module* mod) { - self->RegisterModule(mod); -} - -const char* executor_peer_get_data_layout(const Executor* self) { - return self->data_layout().getStringRepresentation().c_str(); -} - -const char* executor_peer_get_target_triple(const Executor* self) { - return self->target_triple().getTriple().c_str(); -} - -Lambda executor_peer_get_native_function(Executor* self, uint32_t func_id) { - return self->GetNativeFunction(func_id); -} - -// helper functions - -size_t helper_peer_get_basic_block_name_or_as_operand(BasicBlock* block, char* buf, size_t len) { - return GetNameOrAsOperand(LLVM_BB(block), buf, len); -} - -size_t helper_peer_get_value_name_or_as_operand(ValueIr* value, char* buf, size_t len) { - return GetNameOrAsOperand(LLVM_VALUE(value), buf, len); -} diff --git a/libs/jsruntime/src/llvmir/bridge.hh b/libs/jsruntime/src/llvmir/bridge.hh deleted file mode 100644 index 72100280..00000000 --- a/libs/jsruntime/src/llvmir/bridge.hh +++ /dev/null @@ -1,339 +0,0 @@ -#pragma once - -#include -#include - -struct Capture; -struct Closure; -struct Coroutine; -struct Value; - -#define STATUS_UNSET_BIT 0x10 -#define STATUS_MASK 0x0F -#define STATUS_NORMAL 0x00 -#define STATUS_EXCEPTION 0x01 -#define STATUS_SUSPEND 0x02 -#define STATUS_UNSET (STATUS_UNSET_BIT | STATUS_NORMAL) - -enum class Status : uint32_t { - Normal = STATUS_NORMAL, - Exception = STATUS_EXCEPTION, - Suspend = STATUS_SUSPEND, -}; - -static_assert(sizeof(Status) == sizeof(uint32_t), "size mismatched"); - -// TODO: generate from the Rust type. -enum class ValueKind : uint8_t { - // DO NOT CHANGE THE ORDER OF THE FOLLOWING ENUM VARIANTS. - // Some operations heavily rely on the order. - None = 0, - Undefined, - Null, - Boolean, - Number, - Closure, - Promise, -}; - -static_assert(sizeof(ValueKind) == sizeof(uint8_t), "size mismatched"); - -// The actual type of `context` varies depending on usage of the lambda function: -// -// Regular functions: Capture** -// Coroutine functions: Coroutine* -// -// TODO: move to types.rs -typedef Status (*Lambda)(void* runtime, void* context, size_t argc, Value* argv, Value* ret); - -#include "runtime.hh" - -void llvmir_initialize(); - -// Module - -struct Module; -void module_peer_print(Module* self, bool stderr); -void module_peer_delete(Module* self); - -// Compilation - -// opaque types -class Compiler; -struct BasicBlock; -struct LambdaIr; -struct BooleanIr; -struct NumberIr; -struct ClosureIr; -struct CoroutineIr; -struct PromiseIr; -struct ValueIr; -struct ArgvIr; -struct StatusIr; -struct CaptureIr; -struct SwitchIr; - -Compiler* compiler_peer_new(); -void compiler_peer_delete(Compiler* self); - -void compiler_peer_start(Compiler* self, bool enable_labels); -Module* compiler_peer_end(Compiler* self); -void compiler_peer_set_data_layout(Compiler* self, const char* data_layout); -void compiler_peer_set_target_triple(Compiler* self, const char* triple); - -// function -void compiler_peer_start_function(Compiler* self, uint32_t func_id); -void compiler_peer_end_function(Compiler* self, bool optimize); -void compiler_peer_set_locals_block(Compiler* self, BasicBlock* block); -LambdaIr* compiler_peer_get_function(Compiler* self, uint32_t func_id); - -// basic block -BasicBlock* compiler_peer_create_basic_block(Compiler* self, const char* name, size_t name_len); -BasicBlock* compiler_peer_get_basic_block(const Compiler* self); -void compiler_peer_set_basic_block(Compiler* self, BasicBlock* block); -void compiler_peer_move_basic_block_after(Compiler* self, BasicBlock* block); -bool compiler_peer_is_basic_block_terminated(Compiler* self, BasicBlock* block); - -// jump -void compiler_peer_create_br(Compiler* self, BasicBlock* block); -void compiler_peer_create_cond_br(Compiler* self, - BooleanIr* cond, - BasicBlock* then_block, - BasicBlock* else_block); - -// undefined -BooleanIr* compiler_peer_create_is_undefined(Compiler* self, ValueIr* value); - -// null -BooleanIr* compiler_peer_create_is_null(Compiler* self, ValueIr* value); -BooleanIr* compiler_peer_create_is_non_nullish(Compiler* self, ValueIr* value); - -// boolean -BooleanIr* compiler_peer_create_is_boolean(Compiler* self, ValueIr* value); -BooleanIr* compiler_peer_create_is_same_boolean(Compiler* self, BooleanIr* a, BooleanIr* b); -BooleanIr* compiler_peer_create_number_to_boolean(Compiler* self, NumberIr* number); -BooleanIr* compiler_peer_create_to_boolean(Compiler* self, ValueIr* value); -BooleanIr* compiler_peer_get_boolean(Compiler* self, bool value); -BooleanIr* compiler_peer_create_logical_not(Compiler* self, BooleanIr* boolean); -BooleanIr* compiler_peer_create_boolean_phi(Compiler* self, - BooleanIr* then_value, - BasicBlock* then_block, - BooleanIr* else_value, - BasicBlock* else_block); - -// number -BooleanIr* compiler_peer_create_is_number(Compiler* self, ValueIr* value); -BooleanIr* compiler_peer_create_is_same_number(Compiler* self, NumberIr* a, NumberIr* b); -NumberIr* compiler_peer_create_boolean_to_number(Compiler* self, BooleanIr* value); -NumberIr* compiler_peer_to_numeric(Compiler* self, ValueIr* value); -NumberIr* compiler_peer_get_nan(Compiler* self); -NumberIr* compiler_peer_get_zero(Compiler* self); -NumberIr* compiler_peer_get_number(Compiler* self, double value); -NumberIr* compiler_peer_create_bitwise_not(Compiler* self, NumberIr* value); -NumberIr* compiler_peer_create_fneg(Compiler* self, NumberIr* value); -NumberIr* compiler_peer_create_fmul(Compiler* self, NumberIr* lhs, NumberIr* rhs); -NumberIr* compiler_peer_create_fdiv(Compiler* self, NumberIr* lhs, NumberIr* rhs); -NumberIr* compiler_peer_create_frem(Compiler* self, NumberIr* lhs, NumberIr* rhs); -NumberIr* compiler_peer_create_fadd(Compiler* self, NumberIr* lhs, NumberIr* rhs); -NumberIr* compiler_peer_create_fsub(Compiler* self, NumberIr* lhs, NumberIr* rhs); -NumberIr* compiler_peer_create_left_shift(Compiler* self, NumberIr* lhs, NumberIr* rhs); -NumberIr* compiler_peer_create_signed_right_shift(Compiler* self, NumberIr* lhs, NumberIr* rhs); -NumberIr* compiler_peer_create_unsigned_right_shift(Compiler* self, NumberIr* lhs, NumberIr* rhs); -NumberIr* compiler_peer_create_bitwise_and(Compiler* self, NumberIr* lhs, NumberIr* rhs); -NumberIr* compiler_peer_create_bitwise_xor(Compiler* self, NumberIr* lhs, NumberIr* rhs); -NumberIr* compiler_peer_create_bitwise_or(Compiler* self, NumberIr* lhs, NumberIr* rhs); -BooleanIr* compiler_peer_create_less_than(Compiler* self, NumberIr* lhs, NumberIr* rhs); -BooleanIr* compiler_peer_create_greater_than(Compiler* self, NumberIr* lhs, NumberIr* rhs); -BooleanIr* compiler_peer_create_less_than_or_equal(Compiler* self, NumberIr* lhs, NumberIr* rhs); -BooleanIr* compiler_peer_create_greater_than_or_equal(Compiler* self, - NumberIr* lhs, - NumberIr* rhs); -NumberIr* compiler_peer_create_number_phi(Compiler* self, - NumberIr* then_value, - BasicBlock* then_block, - NumberIr* else_value, - BasicBlock* else_block); - -// closure -BooleanIr* compiler_peer_create_is_closure(Compiler* self, ValueIr* value); -BooleanIr* compiler_peer_create_is_same_closure(Compiler* self, ClosureIr* a, ClosureIr* b); -ClosureIr* compiler_peer_get_closure_nullptr(Compiler* self); -ClosureIr* compiler_peer_create_closure(Compiler* self, LambdaIr* lambda, uint16_t num_captures); -void compiler_peer_create_store_capture_to_closure(Compiler* self, - CaptureIr* capture, - ClosureIr* closure, - uint16_t index); -StatusIr* compiler_peer_create_call_on_closure(Compiler* self, - ClosureIr* closure, - uint16_t argc, - ArgvIr* argv, - ValueIr* retv); -ClosureIr* compiler_peer_create_closure_phi(Compiler* self, - ClosureIr* then_value, - BasicBlock* then_block, - ClosureIr* else_value, - BasicBlock* else_block); - -// promise -BooleanIr* compiler_peer_create_is_promise(Compiler* self, ValueIr* value); -BooleanIr* compiler_peer_create_is_same_promise(Compiler* self, PromiseIr* a, PromiseIr* b); -PromiseIr* compiler_peer_create_register_promise(Compiler* self, CoroutineIr* coroutine); -void compiler_peer_create_await_promise(Compiler* self, PromiseIr* promise, PromiseIr* awaiting); -void compiler_peer_create_resume(Compiler* self, PromiseIr* promise); -void compiler_peer_create_emit_promise_resolved(Compiler* self, - PromiseIr* promise, - ValueIr* result); - -// value -BooleanIr* compiler_peer_create_has_value(Compiler* self, ValueIr* value); -BooleanIr* compiler_peer_create_is_loosely_equal(Compiler* self, ValueIr* lhs, ValueIr* rhs); -BooleanIr* compiler_peer_create_is_strictly_equal(Compiler* self, ValueIr* lhs, ValueIr* rhs); -BooleanIr* compiler_peer_create_is_same_boolean_value(Compiler* self, - ValueIr* value, - BooleanIr* boolean); -BooleanIr* compiler_peer_create_is_same_number_value(Compiler* self, - ValueIr* value, - NumberIr* number); -BooleanIr* compiler_peer_create_is_same_closure_value(Compiler* self, - ValueIr* value, - ClosureIr* closure); -BooleanIr* compiler_peer_create_is_same_promise_value(Compiler* self, - ValueIr* value, - PromiseIr* promise); -ValueIr* compiler_peer_create_undefined_to_any(Compiler* self); -ValueIr* compiler_peer_create_null_to_any(Compiler* self); -ValueIr* compiler_peer_create_boolean_to_any(Compiler* self, BooleanIr* boolean); -ValueIr* compiler_peer_create_number_to_any(Compiler* self, NumberIr* number); -ValueIr* compiler_peer_create_closure_to_any(Compiler* self, ClosureIr* closure); -ValueIr* compiler_peer_create_value_phi(Compiler* self, - ValueIr* then_value, - BasicBlock* then_block, - ValueIr* else_value, - BasicBlock* else_block); -ValueIr* compiler_peer_create_local_value(Compiler* self, uint16_t index); -void compiler_peer_create_store_none_to_value(Compiler* self, ValueIr* dest); -void compiler_peer_create_store_undefined_to_value(Compiler* self, ValueIr* dest); -void compiler_peer_create_store_null_to_value(Compiler* self, ValueIr* dest); -void compiler_peer_create_store_boolean_to_value(Compiler* self, BooleanIr* value, ValueIr* dest); -void compiler_peer_create_store_number_to_value(Compiler* self, NumberIr* value, ValueIr* dest); -void compiler_peer_create_store_closure_to_value(Compiler* self, ClosureIr* value, ValueIr* dest); -void compiler_peer_create_store_promise_to_value(Compiler* self, PromiseIr* value, ValueIr* dest); -void compiler_peer_create_store_value_to_value(Compiler* self, ValueIr* value, ValueIr* dest); -ClosureIr* compiler_peer_create_load_closure_from_value(Compiler* self, ValueIr* value); -PromiseIr* compiler_peer_create_load_promise_from_value(Compiler* self, ValueIr* value); - -// argv -ArgvIr* compiler_peer_get_argv_nullptr(Compiler* self); -ArgvIr* compiler_peer_create_argv(Compiler* self, uint16_t argc); -ValueIr* compiler_peer_create_get_arg_in_argv(Compiler* self, ArgvIr* argv, uint16_t index); -ValueIr* compiler_peer_create_get_argument_value_ptr(Compiler* self, uint16_t index); - -// retv -// -// The `retv` variable holds either a returned or thrown value. -ValueIr* compiler_peer_create_retv(Compiler* self); -void compiler_peer_create_store_undefined_to_retv(Compiler* self); -void compiler_peer_create_store_null_to_retv(Compiler* self); -void compiler_peer_create_store_boolean_to_retv(Compiler* self, BooleanIr* value); -void compiler_peer_create_store_number_to_retv(Compiler* self, NumberIr* value); -void compiler_peer_create_store_closure_to_retv(Compiler* self, ClosureIr* value); -void compiler_peer_create_store_promise_to_retv(Compiler* self, PromiseIr* value); -void compiler_peer_create_store_value_to_retv(Compiler* self, ValueIr* value); -ValueIr* compiler_peer_get_exception(Compiler* self); - -// status -// -// TODO: Currently, each lambda has its own status variable. However, it might be possible to use -// a single global variable shared by all lambdas. Because the execution model of JavaScript is a -// single threaded model. -void compiler_peer_create_alloc_status(Compiler* self); -void compiler_peer_create_store_normal_status(Compiler* self); -void compiler_peer_create_store_exception_status(Compiler* self); -BooleanIr* compiler_peer_create_is_exception_status(Compiler* self, StatusIr* status); - -// flow selector -void compiler_peer_create_alloc_flow_selector(Compiler* self); -void compiler_peer_create_set_flow_selector_normal(Compiler* self); -void compiler_peer_create_set_flow_selector_return(Compiler* self); -void compiler_peer_create_set_flow_selector_throw(Compiler* self); -void compiler_peer_create_set_flow_selector_break(Compiler* self, uint32_t depth); -void compiler_peer_create_set_flow_selector_continue(Compiler* self, uint32_t depth); -BooleanIr* compiler_peer_create_is_flow_selector_normal(Compiler* self); -BooleanIr* compiler_peer_create_is_flow_selector_normal_or_continue(Compiler* self, - uint32_t depth); -BooleanIr* compiler_peer_create_is_flow_selector_break_or_continue(Compiler* self, uint32_t depth); -BooleanIr* compiler_peer_create_is_flow_selector_break(Compiler* self, uint32_t depth); - -// capture -CaptureIr* compiler_peer_create_capture(Compiler* self, ValueIr* value); -void compiler_peer_create_escape_value(Compiler* self, CaptureIr* capture, ValueIr* value); -ValueIr* compiler_peer_create_get_capture_value_ptr(Compiler* self, uint16_t index); -CaptureIr* compiler_peer_create_load_capture(Compiler* self, uint16_t index); - -// coroutine -CoroutineIr* compiler_peer_create_coroutine(Compiler* self, - ClosureIr* closure, - uint16_t num_locals, - uint16_t scratch_buffer_len); -SwitchIr* compiler_peer_create_switch_for_coroutine(Compiler* self, - BasicBlock* block, - uint32_t num_states); -void compiler_peer_create_add_state_for_coroutine(Compiler* self, - SwitchIr* switch_ir, - uint32_t state, - BasicBlock* block); -void compiler_peer_create_suspend(Compiler* self); -void compiler_peer_create_set_coroutine_state(Compiler* self, uint32_t state); -void compiler_peer_create_set_captures_for_coroutine(Compiler* self); -ValueIr* compiler_peer_create_get_local_ptr_from_coroutine(Compiler* self, uint16_t index); -void compiler_peer_create_write_boolean_to_scratch_buffer(Compiler* self, - uint32_t offset, - BooleanIr* value); -BooleanIr* compiler_peer_create_read_boolean_from_scratch_buffer(Compiler* self, uint32_t offset); -void compiler_peer_create_write_number_to_scratch_buffer(Compiler* self, - uint32_t offset, - NumberIr* value); -NumberIr* compiler_peer_create_read_number_from_scratch_buffer(Compiler* self, uint32_t offset); -void compiler_peer_create_write_closure_to_scratch_buffer(Compiler* self, - uint32_t offset, - ClosureIr* value); -ClosureIr* compiler_peer_create_read_closure_from_scratch_buffer(Compiler* self, uint32_t offset); -void compiler_peer_create_write_promise_to_scratch_buffer(Compiler* self, - uint32_t offset, - PromiseIr* value); -PromiseIr* compiler_peer_create_read_promise_from_scratch_buffer(Compiler* self, uint32_t offset); -void compiler_peer_create_write_value_to_scratch_buffer(Compiler* self, - uint32_t offset, - ValueIr* value); -ValueIr* compiler_peer_create_read_value_from_scratch_buffer(Compiler* self, uint32_t offset); - -// scope cleanup checker -void compiler_peer_enable_scope_cleanup_checker(Compiler* self, bool is_coroutine); -void compiler_peer_set_scope_id_for_checker(Compiler* self, uint16_t scope_id); -void compiler_peer_assert_scope_id(Compiler* self, uint16_t expected); - -// print -void compiler_peer_create_print_value(Compiler* self, ValueIr* value, const char* msg); - -// debugger -void compiler_peer_create_debugger(Compiler* self); - -// unreachable -void compiler_peer_create_unreachable(Compiler* self, const char* msg); - -// Execution - -class Executor; -Executor* executor_peer_new(); -void executor_peer_delete(Executor* self); -void executor_peer_register_runtime(Executor* self, const Runtime* runtime); -void executor_peer_register_host_function(Executor* self, uint32_t func_id, Lambda func); -void executor_peer_register_module(Executor* self, Module* mod); -const char* executor_peer_get_data_layout(const Executor* self); -const char* executor_peer_get_target_triple(const Executor* self); -Lambda executor_peer_get_native_function(Executor* self, uint32_t func_id); - -// Hepler Functions - -size_t helper_peer_get_basic_block_name_or_as_operand(BasicBlock* block, char* buf, size_t len); -size_t helper_peer_get_value_name_or_as_operand(ValueIr* value, char* buf, size_t len); diff --git a/libs/jsruntime/src/llvmir/bridge.rs b/libs/jsruntime/src/llvmir/bridge.rs index 6cc5b2a0..5876bacb 100644 --- a/libs/jsruntime/src/llvmir/bridge.rs +++ b/libs/jsruntime/src/llvmir/bridge.rs @@ -1,12 +1,65 @@ -#![allow(dead_code)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] -#![allow(non_upper_case_globals)] +use std::ffi::c_char; +use std::ffi::c_void; + +use crate::logger; +use crate::types::Capture; +use crate::types::Closure; +use crate::types::Coroutine; +use crate::types::Lambda; +use crate::types::Value; + +pub fn initialize() { + unsafe { + llvmir_initialize(); + } +} -use crate::types; -use crate::VoidPtr; +#[repr(C)] +pub struct RuntimeFunctions { + to_boolean: unsafe extern "C" fn(*mut c_void, *const Value) -> bool, + to_numeric: unsafe extern "C" fn(*mut c_void, *const Value) -> f64, + to_int32: unsafe extern "C" fn(*mut c_void, f64) -> i32, + to_uint32: unsafe extern "C" fn(*mut c_void, f64) -> u32, + is_loosely_equal: unsafe extern "C" fn(*mut c_void, *const Value, *const Value) -> bool, + is_strictly_equal: unsafe extern "C" fn(*mut c_void, *const Value, *const Value) -> bool, + create_capture: unsafe extern "C" fn(*mut c_void, *mut Value) -> *mut Capture, + create_closure: unsafe extern "C" fn(*mut c_void, Lambda, u16) -> *mut Closure, + create_coroutine: unsafe extern "C" fn(*mut c_void, *mut Closure, u16, u16) -> *mut Coroutine, + register_promise: unsafe extern "C" fn(*mut c_void, *mut Coroutine) -> u32, + await_promise: unsafe extern "C" fn(*mut c_void, u32, u32), + resume: unsafe extern "C" fn(*mut c_void, u32), + emit_promise_resolved: unsafe extern "C" fn(*mut c_void, u32, *const Value), + assert: unsafe extern "C" fn(*mut c_void, bool, *const c_char), + print_u32: unsafe extern "C" fn(*mut c_void, u32, *const c_char), + print_f64: unsafe extern "C" fn(*mut c_void, f64, *const c_char), + print_value: unsafe extern "C" fn(*mut c_void, *const Value, *const c_char), + launch_debugger: unsafe extern "C" fn(*mut c_void), +} -include!(concat!(env!("OUT_DIR"), "/bridge.rs")); +impl RuntimeFunctions { + pub fn new() -> Self { + Self { + to_boolean: runtime_to_boolean, + to_numeric: runtime_to_numeric, + to_int32: runtime_to_int32, + to_uint32: runtime_to_uint32, + is_loosely_equal: runtime_is_loosely_equal, + is_strictly_equal: runtime_is_strictly_equal, + create_capture: runtime_create_capture::, + create_closure: runtime_create_closure::, + create_coroutine: runtime_create_coroutine::, + register_promise: runtime_register_promise::, + await_promise: runtime_await_promise::, + resume: runtime_resume::, + emit_promise_resolved: runtime_emit_promise_resolved::, + assert: runtime_assert, + print_u32: runtime_print_u32, + print_f64: runtime_print_f64, + print_value: runtime_print_value, + launch_debugger: runtime_launch_debugger, + } + } +} macro_rules! into_runtime { ($runtime:expr, $extension:ident) => { @@ -16,68 +69,44 @@ macro_rules! into_runtime { macro_rules! into_value { ($value:expr) => { - // TODO: remove type cast - &*($value as *const crate::types::Value) + &*($value) }; } -pub fn runtime_bridge() -> Runtime { - Runtime { - to_boolean: Some(runtime_to_boolean), - to_numeric: Some(runtime_to_numeric), - to_int32: Some(runtime_to_int32), - to_uint32: Some(runtime_to_uint32), - is_loosely_equal: Some(runtime_is_loosely_equal), - is_strictly_equal: Some(runtime_is_strictly_equal), - create_capture: Some(runtime_create_capture::), - create_closure: Some(runtime_create_closure::), - create_coroutine: Some(runtime_create_coroutine::), - register_promise: Some(runtime_register_promise::), - await_promise: Some(runtime_await_promise::), - resume: Some(runtime_resume::), - emit_promise_resolved: Some(runtime_emit_promise_resolved::), - assert: Some(runtime_assert), - print_u32: Some(runtime_print_u32), - print_f64: Some(runtime_print_f64), - print_value: Some(runtime_print_value), - launch_debugger: Some(runtime_launch_debugger), - } -} - // 7.1.2 ToBoolean ( argument ) -unsafe extern "C" fn runtime_to_boolean(_runtime: VoidPtr, value: *const Value) -> bool { +unsafe extern "C" fn runtime_to_boolean(_runtime: *mut c_void, value: *const Value) -> bool { let value = into_value!(value); match value { - types::Value::None => unreachable!("Value::None"), - types::Value::Undefined => false, - types::Value::Null => false, - types::Value::Boolean(value) => *value, - types::Value::Number(value) if *value == 0.0 => false, - types::Value::Number(value) if value.is_nan() => false, - types::Value::Number(_) => true, - types::Value::Closure(_) => true, - types::Value::Promise(_) => true, + Value::None => unreachable!("Value::None"), + Value::Undefined => false, + Value::Null => false, + Value::Boolean(value) => *value, + Value::Number(value) if *value == 0.0 => false, + Value::Number(value) if value.is_nan() => false, + Value::Number(_) => true, + Value::Closure(_) => true, + Value::Promise(_) => true, } } // 7.1.3 ToNumeric ( value ) // 7.1.4 ToNumber ( argument ) -unsafe extern "C" fn runtime_to_numeric(_runtime: VoidPtr, value: *const Value) -> f64 { +unsafe extern "C" fn runtime_to_numeric(_runtime: *mut c_void, value: *const Value) -> f64 { let value = into_value!(value); match value { - types::Value::None => unreachable!("Value::None"), - types::Value::Undefined => f64::NAN, - types::Value::Null => 0.0, - types::Value::Boolean(value) if *value => 1.0, - types::Value::Boolean(_) => 0.0, - types::Value::Number(value) => *value, - types::Value::Closure(_) => f64::NAN, - types::Value::Promise(_) => f64::NAN, + Value::None => unreachable!("Value::None"), + Value::Undefined => f64::NAN, + Value::Null => 0.0, + Value::Boolean(value) if *value => 1.0, + Value::Boolean(_) => 0.0, + Value::Number(value) => *value, + Value::Closure(_) => f64::NAN, + Value::Promise(_) => f64::NAN, } } // 7.1.6 ToInt32 ( argument ) -unsafe extern "C" fn runtime_to_int32(_runtime: VoidPtr, value: f64) -> i32 { +unsafe extern "C" fn runtime_to_int32(_runtime: *mut c_void, value: f64) -> i32 { const EXP2_31: f64 = (2u64 << 31) as f64; const EXP2_32: f64 = (2u64 << 32) as f64; @@ -102,7 +131,7 @@ unsafe extern "C" fn runtime_to_int32(_runtime: VoidPtr, value: f64) -> i32 { } // 7.1.7 ToUint32 ( argument ) -unsafe extern "C" fn runtime_to_uint32(_runtime: VoidPtr, value: f64) -> u32 { +unsafe extern "C" fn runtime_to_uint32(_runtime: *mut c_void, value: f64) -> u32 { const EXP2_31: f64 = (2u64 << 31) as f64; const EXP2_32: f64 = (2u64 << 32) as f64; @@ -112,31 +141,31 @@ unsafe extern "C" fn runtime_to_uint32(_runtime: VoidPtr, value: f64) -> u32 { } // 3. Let int be truncate(ℝ(number)). - let int_ = dbg!(value.trunc()); + let int_ = value.trunc(); // 4. Let int32bit be int modulo 2**32. - let int32bit = dbg!(int_ % EXP2_32); + let int32bit = int_ % EXP2_32; // int32bit may be negative. // 5. Return 𝔽(int32bit). if int32bit < 0.0 { - dbg!((int32bit + EXP2_31) as u32) + (int32bit + EXP2_31) as u32 } else { - dbg!(int32bit as u32) + int32bit as u32 } } // 7.2.13 IsLooselyEqual ( x, y ) unsafe extern "C" fn runtime_is_loosely_equal( - runtime: VoidPtr, + runtime: *mut c_void, a: *const Value, b: *const Value, ) -> bool { let x = into_value!(a); - debug_assert!(!matches!(x, types::Value::None)); + debug_assert!(!matches!(x, Value::None)); let y = into_value!(b); - debug_assert!(!matches!(y, types::Value::None)); + debug_assert!(!matches!(y, Value::None)); let x_kind = std::mem::discriminant(x); let y_kind = std::mem::discriminant(y); @@ -149,9 +178,9 @@ unsafe extern "C" fn runtime_is_loosely_equal( match (x, y) { // 2. If x is null and y is undefined, return true. - (types::Value::Null, types::Value::Undefined) => true, + (Value::Null, Value::Undefined) => true, // 3. If x is undefined and y is null, return true. - (types::Value::Undefined, types::Value::Null) => true, + (Value::Undefined, Value::Null) => true, // TODO: 4. NOTE: This step is replaced in section B.3.6.2. // TODO: 5. If x is a Number and y is a String, return ! IsLooselyEqual(x, ! ToNumber(y)). // TODO: 6. If x is a String and y is a Number, return ! IsLooselyEqual(! ToNumber(x), y). @@ -173,27 +202,27 @@ unsafe extern "C" fn runtime_is_loosely_equal( // 7.2.14 IsStrictlyEqual ( x, y ) unsafe extern "C" fn runtime_is_strictly_equal( - _runtime: VoidPtr, + _runtime: *mut c_void, a: *const Value, b: *const Value, ) -> bool { let x = into_value!(a); - debug_assert!(!matches!(x, types::Value::None)); + debug_assert!(!matches!(x, Value::None)); let y = into_value!(b); - debug_assert!(!matches!(y, types::Value::None)); + debug_assert!(!matches!(y, Value::None)); x == y } unsafe extern "C" fn runtime_create_capture( - runtime: VoidPtr, + runtime: *mut c_void, target: *mut Value, ) -> *mut Capture { const LAYOUT: std::alloc::Layout = unsafe { std::alloc::Layout::from_size_align_unchecked( - std::mem::size_of::(), - std::mem::align_of::(), + std::mem::size_of::(), + std::mem::align_of::(), ) }; @@ -203,29 +232,27 @@ unsafe extern "C" fn runtime_create_capture( // TODO: GC let ptr = allocator.alloc_layout(LAYOUT); - let capture = ptr.cast::().as_ptr(); - // TODO: remove type cast - (*capture).target = target as *mut types::Value; + let capture = ptr.cast::().as_ptr(); + (*capture).target = target; // `capture.escaped` will be filled with an actual value. - // TODO: remove type cast - capture as *mut types::Capture as *mut Capture + capture } unsafe extern "C" fn runtime_create_closure( - runtime: VoidPtr, + runtime: *mut c_void, lambda: Lambda, num_captures: u16, ) -> *mut Closure { const BASE_LAYOUT: std::alloc::Layout = unsafe { std::alloc::Layout::from_size_align_unchecked( - std::mem::offset_of!(types::Closure, captures), - std::mem::align_of::(), + std::mem::offset_of!(Closure, captures), + std::mem::align_of::(), ) }; - let storage_layout = std::alloc::Layout::array::<*mut types::Capture>(num_captures as usize).unwrap(); + let storage_layout = std::alloc::Layout::array::<*mut Capture>(num_captures as usize).unwrap(); let (layout, _) = BASE_LAYOUT.extend(storage_layout).unwrap(); let runtime = into_runtime!(runtime, X); @@ -234,30 +261,29 @@ unsafe extern "C" fn runtime_create_closure( // TODO: GC let ptr = allocator.alloc_layout(layout); - let closure = ptr.cast::().as_ptr(); + let closure = ptr.cast::().as_ptr(); (*closure).lambda = lambda; (*closure).num_captures = num_captures; // `(*closure).captures[]` will be filled with actual pointers to `Captures`. - // TODO: remove type cast - closure as *mut types::Closure as *mut Closure + closure } unsafe extern "C" fn runtime_create_coroutine( - runtime: VoidPtr, + runtime: *mut c_void, closure: *mut Closure, num_locals: u16, scratch_buffer_len: u16, ) -> *mut Coroutine { const BASE_LAYOUT: std::alloc::Layout = unsafe { std::alloc::Layout::from_size_align_unchecked( - std::mem::offset_of!(types::Coroutine, locals), - std::mem::align_of::(), + std::mem::offset_of!(Coroutine, locals), + std::mem::align_of::(), ) }; // num_locals may be 0. - let locals_layout = std::alloc::Layout::array::(num_locals as usize).unwrap(); + let locals_layout = std::alloc::Layout::array::(num_locals as usize).unwrap(); let (layout, _) = BASE_LAYOUT.extend(locals_layout).unwrap(); // scratch_buffer_len may be 0. @@ -272,40 +298,37 @@ unsafe extern "C" fn runtime_create_coroutine( // TODO: GC let ptr = allocator.alloc_layout(layout); - let coroutine = ptr.cast::().as_ptr(); - // TODO: remove type cast - (*coroutine).closure = closure as *mut types::Closure; + let coroutine = ptr.cast::().as_ptr(); + (*coroutine).closure = closure; (*coroutine).state = 0; (*coroutine).num_locals = num_locals; (*coroutine).scope_id = 0; (*coroutine).scratch_buffer_len = scratch_buffer_len; // `(*coroutine).locals[]` will be initialized in the coroutine. - // TODO: remove type cast - coroutine as *mut types::Coroutine as *mut Coroutine + coroutine } unsafe extern "C" fn runtime_register_promise( - runtime: VoidPtr, + runtime: *mut c_void, coroutine: *mut Coroutine, ) -> u32 { let runtime = into_runtime!(runtime, X); - // TODO: remove type cast - runtime.register_promise(coroutine as *mut types::Coroutine).into() + runtime.register_promise(coroutine).into() } -unsafe extern "C" fn runtime_resume(runtime: VoidPtr, promise: u32) { +unsafe extern "C" fn runtime_resume(runtime: *mut c_void, promise: u32) { let runtime = into_runtime!(runtime, X); - runtime.process_promise(promise.into(), &types::Value::None, &types::Value::None); + runtime.process_promise(promise.into(), &Value::None, &Value::None); } -unsafe extern "C" fn runtime_await_promise(runtime: VoidPtr, promise: u32, awaiting: u32) { +unsafe extern "C" fn runtime_await_promise(runtime: *mut c_void, promise: u32, awaiting: u32) { let runtime = into_runtime!(runtime, X); runtime.await_promise(promise.into(), awaiting.into()); } unsafe extern "C" fn runtime_emit_promise_resolved( - runtime: VoidPtr, + runtime: *mut c_void, promise: u32, result: *const Value, ) { @@ -315,7 +338,7 @@ unsafe extern "C" fn runtime_emit_promise_resolved( } unsafe extern "C" fn runtime_assert( - _runtime: VoidPtr, + _runtime: *mut c_void, assertion: bool, msg: *const std::os::raw::c_char, ) { @@ -326,46 +349,51 @@ unsafe extern "C" fn runtime_assert( } unsafe extern "C" fn runtime_print_u32( - _runtime: VoidPtr, + _runtime: *mut c_void, value: u32, msg: *const std::os::raw::c_char, ) { let msg = std::ffi::CStr::from_ptr(msg); if msg.is_empty() { - crate::logger::debug!("runtime_print_u32: {value:08X}"); + logger::debug!("runtime_print_u32: {value:08X}"); } else { - crate::logger::debug!("runtime_print_u32: {value:08X}: {msg:?}"); + logger::debug!("runtime_print_u32: {value:08X}: {msg:?}"); } } unsafe extern "C" fn runtime_print_f64( - _runtime: VoidPtr, + _runtime: *mut c_void, value: f64, msg: *const std::os::raw::c_char, ) { let msg = std::ffi::CStr::from_ptr(msg); if msg.is_empty() { - crate::logger::debug!("runtime_print_f64: {value}"); + logger::debug!("runtime_print_f64: {value}"); } else { - crate::logger::debug!("runtime_print_f64: {value}: {msg:?}"); + logger::debug!("runtime_print_f64: {value}: {msg:?}"); } } unsafe extern "C" fn runtime_print_value( - _runtime: VoidPtr, + _runtime: *mut c_void, value: *const Value, msg: *const std::os::raw::c_char, ) { let value = into_value!(value); let msg = std::ffi::CStr::from_ptr(msg); if msg.is_empty() { - crate::logger::debug!("runtime_print_value: {value:?}"); + logger::debug!("runtime_print_value: {value:?}"); } else { - crate::logger::debug!("runtime_print_value: {value:?}: {msg:?}"); + logger::debug!("runtime_print_value: {value:?}: {msg:?}"); } } -unsafe extern "C" fn runtime_launch_debugger(_runtime: VoidPtr) { - crate::logger::debug!("runtime_launch_debugger"); +unsafe extern "C" fn runtime_launch_debugger(_runtime: *mut c_void) { + logger::debug!("runtime_launch_debugger"); // TODO(feat): Support debuggers such as Chrome DevTools. } + +#[link(name = "llvmir")] +extern "C" { + fn llvmir_initialize(); +} diff --git a/libs/jsruntime/src/llvmir/cbindgen.toml b/libs/jsruntime/src/llvmir/cbindgen.toml new file mode 100644 index 00000000..05585e50 --- /dev/null +++ b/libs/jsruntime/src/llvmir/cbindgen.toml @@ -0,0 +1,8 @@ +language = "C++" +pragma_once = true +sys_includes = ["cstddef", "cstdint"] +no_includes = true +usize_is_size_t = true + +# Needed for generating forward declarations properly. +style = "both" diff --git a/libs/jsruntime/src/llvmir/compiler/bridge.rs b/libs/jsruntime/src/llvmir/compiler/bridge.rs new file mode 100644 index 00000000..96c5522d --- /dev/null +++ b/libs/jsruntime/src/llvmir/compiler/bridge.rs @@ -0,0 +1,1675 @@ +use std::ffi::c_char; +use std::ffi::c_void; +use std::ffi::CStr; + +use paste::paste; + +use crate::llvmir::module::Module; +use crate::llvmir::module::ModulePeer; +use crate::logger; +use crate::semantics::ScopeRef; +use crate::FunctionId; + +pub struct CompilerBridge(CompilerPeer); + +// TODO(refactor): Case conversion macros such as `case-macro` does not work inside macro_rules +// properly as we expect... +// +// For example, the following macro definition didn't work: +// +// ```rust +// macro_rules! deifne_ir_types { +// ($($name:ident,)*) => { +// $(define_ir_types! {$name, case_macro::snake_case!($name)})* +// }; +// } +// ``` +macro_rules! define_ir_types { + ($($name:ident; $macro:ident,)*) => { + $(define_ir_types! {$name, $macro})* + }; + ($name:ident, $macro:ident) => { + paste! { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct $name([<$name Ptr>]); + + macro_rules! $macro { + ($inner:expr) => { + $name(unsafe { $inner }) + } + } + } + }; +} + +define_ir_types! { + BasicBlock; basic_block, + LambdaIr; lambda_ir, + BooleanIr; boolean_ir, + NumberIr; number_ir, + ClosureIr; closure_ir, + CoroutineIr; coroutine_ir, + PromiseIr; promise_ir, + ValueIr; value_ir, + ArgvIr; argv_ir, + StatusIr; status_ir, + CaptureIr; capture_ir, + SwitchIr; switch_ir, +} + +impl CompilerBridge { + pub fn new() -> Self { + Self(unsafe { compiler_peer_new() }) + } + + pub fn start_compile(&self, enable_labels: bool) { + unsafe { + compiler_peer_start(self.0, enable_labels); + } + } + + pub fn end_compile(&self) -> Module { + Module::new(unsafe { compiler_peer_end(self.0) }) + } + + pub fn set_data_layout(&self, data_layout: &CStr) { + unsafe { + compiler_peer_set_data_layout(self.0, data_layout.as_ptr()); + } + } + + pub fn set_target_triple(&self, triple: &CStr) { + unsafe { + compiler_peer_set_target_triple(self.0, triple.as_ptr()); + } + } + + // function + + pub fn start_function(&self, func_id: FunctionId) { + unsafe { + compiler_peer_start_function(self.0, func_id.into()); + } + } + + pub fn end_function(&self, optimize: bool) { + unsafe { + compiler_peer_end_function(self.0, optimize); + } + } + + pub fn set_locals_block(&self, block: BasicBlock) { + debug_assert_ne!(block, BasicBlock::NONE); + unsafe { + compiler_peer_set_locals_block(self.0, block.0); + } + } + + pub fn get_function(&self, func_id: FunctionId) -> LambdaIr { + lambda_ir!(compiler_peer_get_function(self.0, func_id.into())) + } + + // basic block + + pub fn create_basic_block(&self, name: *const std::ffi::c_char, name_len: usize) -> BasicBlock { + basic_block!(compiler_peer_create_basic_block(self.0, name, name_len)) + } + + pub fn get_basic_block(&self) -> BasicBlock { + basic_block!(compiler_peer_get_basic_block(self.0)) + } + + pub fn set_basic_block(&self, block: BasicBlock) { + debug_assert_ne!(block, BasicBlock::NONE); + unsafe { + compiler_peer_set_basic_block(self.0, block.0); + } + } + + pub fn move_basic_block_after(&self, block: BasicBlock) { + debug_assert_ne!(block, BasicBlock::NONE); + unsafe { + compiler_peer_move_basic_block_after(self.0, block.0); + } + } + + pub fn is_basic_block_terminated(&self, block: BasicBlock) -> bool { + debug_assert_ne!(block, BasicBlock::NONE); + unsafe { compiler_peer_is_basic_block_terminated(self.0, block.0) } + } + + // jump + + pub fn create_br(&self, block: BasicBlock) { + debug_assert_ne!(block, BasicBlock::NONE); + unsafe { + compiler_peer_create_br(self.0, block.0); + } + } + + pub fn create_cond_br(&self, cond: BooleanIr, then_block: BasicBlock, else_block: BasicBlock) { + debug_assert_ne!(then_block, BasicBlock::NONE); + debug_assert_ne!(else_block, BasicBlock::NONE); + unsafe { + compiler_peer_create_cond_br(self.0, cond.0, then_block.0, else_block.0); + } + } + + // undefined + + pub fn create_is_undefined(&self, value: ValueIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_undefined(self.0, value.0) + } + } + + // null + + pub fn create_is_null(&self, value: ValueIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_null(self.0, value.0) + } + } + + pub fn create_is_non_nullish(&self, value: ValueIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_non_nullish(self.0, value.0) + } + } + + // boolean + + pub fn create_is_boolean(&self, value: ValueIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_boolean(self.0, value.0) + } + } + + pub fn create_is_same_boolean(&self, a: BooleanIr, b: BooleanIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_same_boolean(self.0, a.0, b.0) + } + } + + pub fn create_number_to_boolean(&self, value: NumberIr) -> BooleanIr { + debug_assert_ne!(value, NumberIr::NONE); + boolean_ir! { + compiler_peer_create_number_to_boolean(self.0, value.0) + } + } + + pub fn create_to_boolean(&self, value: ValueIr) -> BooleanIr { + debug_assert_ne!(value, ValueIr::NONE); + boolean_ir! { + compiler_peer_create_to_boolean(self.0, value.0) + } + } + + pub fn get_boolean(&self, value: bool) -> BooleanIr { + boolean_ir! { + compiler_peer_get_boolean(self.0, value) + } + } + + pub fn create_logical_not(&self, boolean: BooleanIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_logical_not(self.0, boolean.0) + } + } + + pub fn create_boolean_phi( + &self, + then_value: BooleanIr, + then_block: BasicBlock, + else_value: BooleanIr, + else_block: BasicBlock, + ) -> BooleanIr { + debug_assert_ne!(then_value, BooleanIr::NONE); + debug_assert_ne!(then_block, BasicBlock::NONE); + debug_assert_ne!(else_value, BooleanIr::NONE); + debug_assert_ne!(else_block, BasicBlock::NONE); + boolean_ir! { + compiler_peer_create_boolean_phi(self.0, then_value.0, then_block.0, else_value.0, else_block.0) + } + } + + // number + + pub fn create_is_number(&self, value: ValueIr) -> BooleanIr { + logger::debug!(event = "create_is_number", ?value); + boolean_ir! { + compiler_peer_create_is_number(self.0, value.0) + } + } + + pub fn create_is_same_number(&self, a: NumberIr, b: NumberIr) -> BooleanIr { + logger::debug!(event = "create_is_same_number", ?a, ?b); + boolean_ir! { + compiler_peer_create_is_same_number(self.0, a.0, b.0) + } + } + + pub fn create_boolean_to_number(&self, value: BooleanIr) -> NumberIr { + debug_assert_ne!(value, BooleanIr::NONE); + number_ir! { + compiler_peer_create_boolean_to_number(self.0, value.0) + } + } + + pub fn to_numeric(&self, value: ValueIr) -> NumberIr { + debug_assert_ne!(value, ValueIr::NONE); + number_ir! { + compiler_peer_to_numeric(self.0, value.0) + } + } + + pub fn get_nan(&self) -> NumberIr { + number_ir! { + compiler_peer_get_nan(self.0) + } + } + + pub fn get_zero(&self) -> NumberIr { + number_ir! { + compiler_peer_get_zero(self.0) + } + } + + pub fn get_number(&self, value: f64) -> NumberIr { + number_ir! { + compiler_peer_get_number(self.0, value) + } + } + + pub fn create_bitwise_not(&self, value: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_bitwise_not(self.0, value.0) + } + } + + pub fn create_fneg(&self, value: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_fneg(self.0, value.0) + } + } + + pub fn create_fmul(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_fmul(self.0, lhs.0, rhs.0) + } + } + + pub fn create_fdiv(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_fdiv(self.0, lhs.0, rhs.0) + } + } + + pub fn create_frem(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_frem(self.0, lhs.0, rhs.0) + } + } + + pub fn create_fadd(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_fadd(self.0, lhs.0, rhs.0) + } + } + + pub fn create_fsub(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_fsub(self.0, lhs.0, rhs.0) + } + } + + pub fn create_left_shift(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_left_shift(self.0, lhs.0, rhs.0) + } + } + + pub fn create_signed_right_shift(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_signed_right_shift(self.0, lhs.0, rhs.0) + } + } + + pub fn create_unsigned_right_shift(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_unsigned_right_shift(self.0, lhs.0, rhs.0) + } + } + + pub fn create_bitwise_and(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_bitwise_and(self.0, lhs.0, rhs.0) + } + } + + pub fn create_bitwise_xor(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_bitwise_xor(self.0, lhs.0, rhs.0) + } + } + + pub fn create_bitwise_or(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { + number_ir! { + compiler_peer_create_bitwise_or(self.0, lhs.0, rhs.0) + } + } + + pub fn create_less_than(&self, lhs: NumberIr, rhs: NumberIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_less_than(self.0, lhs.0, rhs.0) + } + } + + pub fn create_greater_than(&self, lhs: NumberIr, rhs: NumberIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_greater_than(self.0, lhs.0, rhs.0) + } + } + + pub fn create_less_than_or_equal(&self, lhs: NumberIr, rhs: NumberIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_less_than_or_equal(self.0, lhs.0, rhs.0) + } + } + + pub fn create_greater_than_or_equal(&self, lhs: NumberIr, rhs: NumberIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_greater_than_or_equal(self.0, lhs.0, rhs.0) + } + } + + pub fn create_number_phi( + &self, + then_value: NumberIr, + then_block: BasicBlock, + else_value: NumberIr, + else_block: BasicBlock, + ) -> NumberIr { + debug_assert_ne!(then_block, BasicBlock::NONE); + debug_assert_ne!(else_block, BasicBlock::NONE); + logger::debug!( + event = "create_number_phi", + ?then_value, + ?then_block, + ?else_value, + ?else_block + ); + number_ir! { + compiler_peer_create_number_phi(self.0, then_value.0, then_block.0, else_value.0, else_block.0) + } + } + + // closure + + pub fn create_is_closure(&self, value: ValueIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_closure(self.0, value.0) + } + } + + pub fn create_is_same_closure(&self, a: ClosureIr, b: ClosureIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_same_closure(self.0, a.0, b.0) + } + } + + pub fn get_closure_nullptr(&self) -> ClosureIr { + closure_ir! { + compiler_peer_get_closure_nullptr(self.0) + } + } + + pub fn create_closure(&self, lambda: LambdaIr, num_captures: u16) -> ClosureIr { + debug_assert_ne!(lambda, LambdaIr::NONE); + closure_ir! { + compiler_peer_create_closure(self.0, lambda.0, num_captures) + } + } + + pub fn create_store_capture_to_closure( + &self, + capture: CaptureIr, + closure: ClosureIr, + index: u16, + ) { + unsafe { + compiler_peer_create_store_capture_to_closure(self.0, capture.0, closure.0, index); + } + } + + pub fn create_call_on_closure( + &self, + closure: ClosureIr, + argc: u16, + argv: ArgvIr, + retv: ValueIr, + ) -> StatusIr { + status_ir! { + compiler_peer_create_call_on_closure(self.0, closure.0, argc, argv.0, retv.0) + } + } + + pub fn create_closure_phi( + &self, + then_value: ClosureIr, + then_block: BasicBlock, + else_value: ClosureIr, + else_block: BasicBlock, + ) -> ClosureIr { + debug_assert_ne!(then_block, BasicBlock::NONE); + debug_assert_ne!(else_block, BasicBlock::NONE); + closure_ir! { + compiler_peer_create_closure_phi(self.0, then_value.0, then_block.0, else_value.0, else_block.0) + } + } + + // promise + + pub fn create_is_promise(&self, value: ValueIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_promise(self.0, value.0) + } + } + + pub fn create_is_same_promise(&self, a: PromiseIr, b: PromiseIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_same_promise(self.0, a.0, b.0) + } + } + + pub fn create_register_promise(&self, coroutine: CoroutineIr) -> PromiseIr { + promise_ir! { + compiler_peer_create_register_promise(self.0, coroutine.0) + } + } + + pub fn create_await_promise(&self, promise: PromiseIr, awaiting: PromiseIr) { + unsafe { + compiler_peer_create_await_promise(self.0, promise.0, awaiting.0); + } + } + + pub fn create_resume(&self, promise: PromiseIr) { + unsafe { + compiler_peer_create_resume(self.0, promise.0); + } + } + + pub fn create_emit_promise_resolved(&self, promise: PromiseIr, result: ValueIr) { + unsafe { + compiler_peer_create_emit_promise_resolved(self.0, promise.0, result.0); + } + } + + // value + + pub fn create_has_value(&self, value: ValueIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_has_value(self.0, value.0) + } + } + + pub fn create_is_loosely_equal(&self, a: ValueIr, b: ValueIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_loosely_equal(self.0, a.0, b.0) + } + } + + pub fn create_is_strictly_equal(&self, a: ValueIr, b: ValueIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_strictly_equal(self.0, a.0, b.0) + } + } + + pub fn create_is_same_boolean_value(&self, any: ValueIr, boolean: BooleanIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_same_boolean_value(self.0, any.0, boolean.0) + } + } + + pub fn create_is_same_number_value(&self, any: ValueIr, number: NumberIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_same_number_value(self.0, any.0, number.0) + } + } + + pub fn create_is_same_closure_value(&self, any: ValueIr, closure: ClosureIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_same_closure_value(self.0, any.0, closure.0) + } + } + + pub fn create_is_same_promise_value(&self, any: ValueIr, promise: PromiseIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_same_promise_value(self.0, any.0, promise.0) + } + } + + pub fn create_undefined_to_any(&self) -> ValueIr { + value_ir! { + compiler_peer_create_undefined_to_any(self.0) + } + } + + pub fn create_null_to_any(&self) -> ValueIr { + value_ir! { + compiler_peer_create_null_to_any(self.0) + } + } + + pub fn create_boolean_to_any(&self, value: BooleanIr) -> ValueIr { + debug_assert_ne!(value, BooleanIr::NONE); + value_ir! { + compiler_peer_create_boolean_to_any(self.0, value.0) + } + } + + pub fn create_number_to_any(&self, value: NumberIr) -> ValueIr { + debug_assert_ne!(value, NumberIr::NONE); + value_ir! { + compiler_peer_create_number_to_any(self.0, value.0) + } + } + + pub fn create_closure_to_any(&self, value: ClosureIr) -> ValueIr { + debug_assert_ne!(value, ClosureIr::NONE); + value_ir! { + compiler_peer_create_closure_to_any(self.0, value.0) + } + } + + pub fn create_value_phi( + &self, + then_value: ValueIr, + then_block: BasicBlock, + else_value: ValueIr, + else_block: BasicBlock, + ) -> ValueIr { + debug_assert_ne!(then_block, BasicBlock::NONE); + debug_assert_ne!(else_block, BasicBlock::NONE); + value_ir! { + compiler_peer_create_value_phi(self.0, then_value.0, then_block.0, else_value.0, else_block.0) + } + } + + pub fn create_local_value(&self, index: u16) -> ValueIr { + value_ir! { + compiler_peer_create_local_value(self.0, index) + } + } + + pub fn create_store_none_to_value(&self, dest: ValueIr) { + unsafe { + compiler_peer_create_store_none_to_value(self.0, dest.0); + } + } + + pub fn create_store_undefined_to_value(&self, dest: ValueIr) { + unsafe { + compiler_peer_create_store_undefined_to_value(self.0, dest.0); + } + } + + pub fn create_store_null_to_value(&self, dest: ValueIr) { + unsafe { + compiler_peer_create_store_null_to_value(self.0, dest.0); + } + } + + pub fn create_store_boolean_to_value(&self, value: BooleanIr, dest: ValueIr) { + debug_assert_ne!(value, BooleanIr::NONE); + unsafe { + compiler_peer_create_store_boolean_to_value(self.0, value.0, dest.0); + } + } + + pub fn create_store_number_to_value(&self, value: NumberIr, dest: ValueIr) { + debug_assert_ne!(value, NumberIr::NONE); + unsafe { + compiler_peer_create_store_number_to_value(self.0, value.0, dest.0); + } + } + + pub fn create_store_closure_to_value(&self, value: ClosureIr, dest: ValueIr) { + debug_assert_ne!(value, ClosureIr::NONE); + unsafe { + compiler_peer_create_store_closure_to_value(self.0, value.0, dest.0); + } + } + + pub fn create_store_promise_to_value(&self, value: PromiseIr, dest: ValueIr) { + debug_assert_ne!(value, PromiseIr::NONE); + unsafe { + compiler_peer_create_store_promise_to_value(self.0, value.0, dest.0); + } + } + + pub fn create_store_value_to_value(&self, value: ValueIr, dest: ValueIr) { + debug_assert_ne!(value, ValueIr::NONE); + unsafe { + compiler_peer_create_store_value_to_value(self.0, value.0, dest.0); + } + } + + pub fn create_load_closure_from_value(&self, value: ValueIr) -> ClosureIr { + closure_ir! { + compiler_peer_create_load_closure_from_value(self.0, value.0) + } + } + + pub fn create_load_promise_from_value(&self, value: ValueIr) -> PromiseIr { + promise_ir! { + compiler_peer_create_load_promise_from_value(self.0, value.0) + } + } + + // argv + + pub fn get_argv_nullptr(&self) -> ArgvIr { + argv_ir! { + compiler_peer_get_argv_nullptr(self.0) + } + } + + pub fn create_argv(&self, argc: u16) -> ArgvIr { + debug_assert!(argc > 0); + argv_ir! { + compiler_peer_create_argv(self.0, argc) + } + } + + pub fn create_get_arg_in_argv(&self, argv: ArgvIr, index: u16) -> ValueIr { + logger::debug!(event = "create_get_arg_in_argv", ?argv, index); + debug_assert_ne!(argv, ArgvIr::NONE); + value_ir! { + compiler_peer_create_get_arg_in_argv(self.0, argv.0, index) + } + } + + pub fn create_get_argument_value_ptr(&self, index: u16) -> ValueIr { + value_ir! { + compiler_peer_create_get_argument_value_ptr(self.0, index) + } + } + + // retv + + pub fn create_retv(&self) -> ValueIr { + value_ir! { + compiler_peer_create_retv(self.0) + } + } + + pub fn create_store_undefined_to_retv(&self) { + unsafe { + compiler_peer_create_store_undefined_to_retv(self.0); + } + } + + pub fn create_store_null_to_retv(&self) { + unsafe { + compiler_peer_create_store_null_to_retv(self.0); + } + } + + pub fn create_store_boolean_to_retv(&self, value: BooleanIr) { + debug_assert_ne!(value, BooleanIr::NONE); + unsafe { + compiler_peer_create_store_boolean_to_retv(self.0, value.0); + } + } + + pub fn create_store_number_to_retv(&self, value: NumberIr) { + debug_assert_ne!(value, NumberIr::NONE); + unsafe { + compiler_peer_create_store_number_to_retv(self.0, value.0); + } + } + + pub fn create_store_closure_to_retv(&self, value: ClosureIr) { + debug_assert_ne!(value, ClosureIr::NONE); + unsafe { + compiler_peer_create_store_closure_to_retv(self.0, value.0); + } + } + + pub fn create_store_promise_to_retv(&self, value: PromiseIr) { + debug_assert_ne!(value, PromiseIr::NONE); + unsafe { + compiler_peer_create_store_promise_to_retv(self.0, value.0); + } + } + + pub fn create_store_value_to_retv(&self, value: ValueIr) { + debug_assert_ne!(value, ValueIr::NONE); + unsafe { + compiler_peer_create_store_value_to_retv(self.0, value.0); + } + } + + pub fn get_exception(&self) -> ValueIr { + value_ir! { + compiler_peer_get_exception(self.0) + } + } + + // status + + pub fn create_alloc_status(&self) { + unsafe { + compiler_peer_create_alloc_status(self.0); + } + } + + pub fn create_store_normal_status(&self) { + unsafe { + compiler_peer_create_store_normal_status(self.0); + } + } + + pub fn create_store_exception_status(&self) { + unsafe { + compiler_peer_create_store_exception_status(self.0); + } + } + + pub fn create_is_exception_status(&self, status: StatusIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_exception_status(self.0, status.0) + } + } + + // flow selector + // + // `break` and `continue` statements are generated unconditional branches in simple situation, + // but conditional branches must be generated in complex situations. + // + // Let think about the following program: + // + // for (;;) { + // let x; + // if (v == 0) + // return; + // // compute something using `x`. + // break; + // } + // + // The `x` variable defined in the scope of the for-loop body and it can be collected as + // garbage once the execution goes out from the scope. Depending on the algorithm of GC to + // use, the runtime must do something for GC when the execution goes out from the scope. In + // this case, the control flow of the `return` and `break` statements are not determined at + // compile time. And a new variable must be needed in order to determine the control flow at + // runtime. + // + // TODO: It might be possible to reuse the status variable instead of introducing the flow + // selector. The both are single variables inside a lambda and have common some values + // partially. If the status variable is reused, it must not be a single global variable. + // Because the execution may suspend by `await`. + // + // TODO: This design is inefficient in a performance point of view, but it makes it possible to + // support various GC algorithms. In addition, we can optimize the runtime cost by removing + // scope sub-graphs in CFG if those has no lexical variables. We can detect such lexical scopes + // in the semantic analysis phase. + + pub fn create_alloc_flow_selector(&self) { + unsafe { + compiler_peer_create_alloc_flow_selector(self.0); + } + } + + pub fn create_set_flow_selector_normal(&self) { + unsafe { + compiler_peer_create_set_flow_selector_normal(self.0); + } + } + + pub fn create_set_flow_selector_return(&self) { + unsafe { + compiler_peer_create_set_flow_selector_return(self.0); + } + } + + pub fn create_set_flow_selector_throw(&self) { + unsafe { + compiler_peer_create_set_flow_selector_throw(self.0); + } + } + + pub fn create_set_flow_selector_break(&self, depth: u32) { + unsafe { + compiler_peer_create_set_flow_selector_break(self.0, depth); + } + } + + pub fn create_set_flow_selector_continue(&self, depth: u32) { + unsafe { + compiler_peer_create_set_flow_selector_continue(self.0, depth); + } + } + + pub fn create_is_flow_selector_normal(&self) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_flow_selector_normal(self.0) + } + } + + pub fn create_is_flow_selector_normal_or_continue(&self, depth: u32) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_flow_selector_normal_or_continue(self.0, depth) + } + } + + pub fn create_is_flow_selector_break_or_continue(&self, depth: u32) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_flow_selector_break_or_continue(self.0, depth) + } + } + + pub fn create_is_flow_selector_break(&self, depth: u32) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_flow_selector_break(self.0, depth) + } + } + + // capture + + pub fn create_capture(&self, value: ValueIr) -> CaptureIr { + capture_ir! { + compiler_peer_create_capture(self.0, value.0) + } + } + + pub fn create_escape_value(&self, capture: CaptureIr, value: ValueIr) { + unsafe { + compiler_peer_create_escape_value(self.0, capture.0, value.0); + } + } + + pub fn create_get_capture_value_ptr(&self, index: u16) -> ValueIr { + value_ir! { + compiler_peer_create_get_capture_value_ptr(self.0, index) + } + } + + pub fn create_load_capture(&self, index: u16) -> CaptureIr { + capture_ir! { + compiler_peer_create_load_capture(self.0, index) + } + } + + // coroutine + + pub fn create_coroutine( + &self, + closure: ClosureIr, + num_locals: u16, + scratch_buffer_len: u16, + ) -> CoroutineIr { + coroutine_ir! { + compiler_peer_create_coroutine(self.0, closure.0, num_locals, scratch_buffer_len) + } + } + + pub fn create_switch_for_coroutine(&self, block: BasicBlock, num_states: u32) -> SwitchIr { + switch_ir! { + compiler_peer_create_switch_for_coroutine(self.0, block.0, num_states) + } + } + + pub fn create_add_state_for_coroutine(&self, inst: SwitchIr, state: u32, block: BasicBlock) { + unsafe { + compiler_peer_create_add_state_for_coroutine(self.0, inst.0, state, block.0); + } + } + + pub fn create_suspend(&self) { + unsafe { + compiler_peer_create_suspend(self.0); + } + } + + pub fn create_set_coroutine_state(&self, state: u32) { + unsafe { + compiler_peer_create_set_coroutine_state(self.0, state); + } + } + + pub fn create_set_captures_for_coroutine(&self) { + unsafe { + compiler_peer_create_set_captures_for_coroutine(self.0); + } + } + + pub fn create_get_local_ptr_from_coroutine(&self, index: u16) -> ValueIr { + value_ir! { + compiler_peer_create_get_local_ptr_from_coroutine(self.0, index) + } + } + + pub fn create_write_boolean_to_scratch_buffer(&self, offset: u32, value: BooleanIr) { + unsafe { + compiler_peer_create_write_boolean_to_scratch_buffer(self.0, offset, value.0); + } + } + + pub fn create_read_boolean_from_scratch_buffer(&self, offset: u32) -> BooleanIr { + boolean_ir! { + compiler_peer_create_read_boolean_from_scratch_buffer(self.0, offset) + } + } + + pub fn create_write_number_to_scratch_buffer(&self, offset: u32, value: NumberIr) { + unsafe { + compiler_peer_create_write_number_to_scratch_buffer(self.0, offset, value.0); + } + } + + pub fn create_read_number_from_scratch_buffer(&self, offset: u32) -> NumberIr { + number_ir! { + compiler_peer_create_read_number_from_scratch_buffer(self.0, offset) + } + } + + pub fn create_write_closure_to_scratch_buffer(&self, offset: u32, value: ClosureIr) { + unsafe { + compiler_peer_create_write_closure_to_scratch_buffer(self.0, offset, value.0); + } + } + + pub fn create_read_closure_from_scratch_buffer(&self, offset: u32) -> ClosureIr { + closure_ir! { + compiler_peer_create_read_closure_from_scratch_buffer(self.0, offset) + } + } + + pub fn create_write_promise_to_scratch_buffer(&self, offset: u32, value: PromiseIr) { + unsafe { + compiler_peer_create_write_promise_to_scratch_buffer(self.0, offset, value.0); + } + } + + pub fn create_read_promise_from_scratch_buffer(&self, offset: u32) -> PromiseIr { + promise_ir! { + compiler_peer_create_read_promise_from_scratch_buffer(self.0, offset) + } + } + + pub fn create_write_value_to_scratch_buffer(&self, offset: u32, value: ValueIr) { + unsafe { + compiler_peer_create_write_value_to_scratch_buffer(self.0, offset, value.0); + } + } + + pub fn create_read_value_from_scratch_buffer(&self, offset: u32) -> ValueIr { + value_ir! { + compiler_peer_create_read_value_from_scratch_buffer(self.0, offset) + } + } + + // scope cleanup checker + + pub fn enable_scope_cleanup_checker(&self, is_coroutine: bool) { + unsafe { + compiler_peer_enable_scope_cleanup_checker(self.0, is_coroutine); + } + } + + pub fn set_scope_id_for_checker(&self, scope_ref: ScopeRef) { + unsafe { + compiler_peer_set_scope_id_for_checker(self.0, scope_ref.id()); + } + } + + pub fn assert_scope_id(&self, expected: ScopeRef) { + unsafe { + compiler_peer_assert_scope_id(self.0, expected.id()); + } + } + + // print + + #[allow(unused)] + pub fn create_print_value(&self, value: ValueIr, msg: &CStr) { + unsafe { + compiler_peer_create_print_value(self.0, value.0, msg.as_ptr()); + } + } + + // debugger + + pub fn create_debugger(&self) { + unsafe { + compiler_peer_create_debugger(self.0); + } + } + + // unreachable + + pub fn create_unreachable(&self, msg: &CStr) { + unsafe { + compiler_peer_create_unreachable(self.0, msg.as_ptr()); + } + } +} + +impl Default for CompilerBridge { + fn default() -> Self { + Self::new() + } +} + +impl Drop for CompilerBridge { + fn drop(&mut self) { + unsafe { + compiler_peer_delete(self.0); + } + } +} + +impl BasicBlock { + pub const NONE: Self = Self(std::ptr::null_mut()); + + pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { + unsafe { + compiler_peer_get_basic_block_name_or_as_operand(self.0, buf, len); + CStr::from_ptr(buf) + } + } +} + +impl LambdaIr { + pub const NONE: Self = Self(std::ptr::null_mut()); + + pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { + unsafe { + compiler_peer_get_value_name_or_as_operand(self.0 as ValueIrPtr, buf, len); + CStr::from_ptr(buf) + } + } +} + +impl BooleanIr { + pub const NONE: Self = Self(std::ptr::null_mut()); + + pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { + unsafe { + compiler_peer_get_value_name_or_as_operand(self.0 as ValueIrPtr, buf, len); + CStr::from_ptr(buf) + } + } +} + +impl NumberIr { + pub const NONE: Self = Self(std::ptr::null_mut()); + + pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { + unsafe { + compiler_peer_get_value_name_or_as_operand(self.0 as ValueIrPtr, buf, len); + CStr::from_ptr(buf) + } + } +} + +impl ClosureIr { + pub const NONE: Self = Self(std::ptr::null_mut()); + + pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { + unsafe { + compiler_peer_get_value_name_or_as_operand(self.0 as ValueIrPtr, buf, len); + CStr::from_ptr(buf) + } + } +} + +impl CoroutineIr { + pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { + unsafe { + compiler_peer_get_value_name_or_as_operand(self.0 as ValueIrPtr, buf, len); + CStr::from_ptr(buf) + } + } +} + +impl PromiseIr { + pub const NONE: Self = Self(std::ptr::null_mut()); + + pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { + unsafe { + compiler_peer_get_value_name_or_as_operand(self.0 as ValueIrPtr, buf, len); + CStr::from_ptr(buf) + } + } +} + +impl ValueIr { + pub const NONE: Self = Self(std::ptr::null_mut()); + + pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { + unsafe { + compiler_peer_get_value_name_or_as_operand(self.0, buf, len); + CStr::from_ptr(buf) + } + } +} + +impl ArgvIr { + pub const NONE: Self = Self(std::ptr::null_mut()); +} + +impl CaptureIr { + pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { + unsafe { + compiler_peer_get_value_name_or_as_operand(self.0 as ValueIrPtr, buf, len); + CStr::from_ptr(buf) + } + } +} + +// DO NOT USE MACROS FOR THE FOLLOWING TYPE DEFINITIONS. +// cbindgen does not support macro expansions. +type CompilerPeer = *mut c_void; +type BasicBlockPtr = *mut c_void; +type LambdaIrPtr = *mut c_void; +type BooleanIrPtr = *mut c_void; +type NumberIrPtr = *mut c_void; +type ClosureIrPtr = *mut c_void; +type CoroutineIrPtr = *mut c_void; +type PromiseIrPtr = *mut c_void; +type ValueIrPtr = *mut c_void; +type ArgvIrPtr = *mut c_void; +type StatusIrPtr = *mut c_void; +type CaptureIrPtr = *mut c_void; +type SwitchIrPtr = *mut c_void; + +#[link(name = "llvmir")] +extern "C" { + fn compiler_peer_new() -> CompilerPeer; + fn compiler_peer_delete(peer: CompilerPeer); + fn compiler_peer_start(peer: CompilerPeer, enable_labels: bool); + fn compiler_peer_end(peer: CompilerPeer) -> ModulePeer; + fn compiler_peer_set_data_layout(peer: CompilerPeer, data_layout: *const c_char); + fn compiler_peer_set_target_triple(peer: CompilerPeer, triple: *const c_char); + + // function + + fn compiler_peer_start_function(peer: CompilerPeer, func_id: u32); + fn compiler_peer_end_function(peer: CompilerPeer, optimize: bool); + fn compiler_peer_set_locals_block(peer: CompilerPeer, block: BasicBlockPtr); + fn compiler_peer_get_function(peer: CompilerPeer, func_id: u32) -> LambdaIrPtr; + + // basic block + + fn compiler_peer_create_basic_block( + peer: CompilerPeer, + name: *const c_char, + name_len: usize, + ) -> BasicBlockPtr; + fn compiler_peer_get_basic_block(peer: CompilerPeer) -> BasicBlockPtr; + fn compiler_peer_set_basic_block(peer: CompilerPeer, block: BasicBlockPtr); + fn compiler_peer_move_basic_block_after(peer: CompilerPeer, block: BasicBlockPtr); + fn compiler_peer_is_basic_block_terminated(peer: CompilerPeer, block: BasicBlockPtr) -> bool; + + // jump + + fn compiler_peer_create_br(peer: CompilerPeer, block: BasicBlockPtr); + fn compiler_peer_create_cond_br( + peer: CompilerPeer, + cond: BooleanIrPtr, + then_block: BasicBlockPtr, + else_block: BasicBlockPtr, + ); + + // undefined + + fn compiler_peer_create_is_undefined(peer: CompilerPeer, value: ValueIrPtr) -> BooleanIrPtr; + + // null + + fn compiler_peer_create_is_null(peer: CompilerPeer, value: ValueIrPtr) -> BooleanIrPtr; + fn compiler_peer_create_is_non_nullish(peer: CompilerPeer, value: ValueIrPtr) -> BooleanIrPtr; + + // boolean + + fn compiler_peer_create_is_boolean(peer: CompilerPeer, value: ValueIrPtr) -> BooleanIrPtr; + fn compiler_peer_create_is_same_boolean( + peer: CompilerPeer, + a: BooleanIrPtr, + b: BooleanIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_number_to_boolean( + peer: CompilerPeer, + number: NumberIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_to_boolean(peer: CompilerPeer, value: ValueIrPtr) -> BooleanIrPtr; + fn compiler_peer_get_boolean(peer: CompilerPeer, value: bool) -> BooleanIrPtr; + fn compiler_peer_create_logical_not(peer: CompilerPeer, value: BooleanIrPtr) -> BooleanIrPtr; + fn compiler_peer_create_boolean_phi( + peer: CompilerPeer, + then_value: BooleanIrPtr, + then_block: BasicBlockPtr, + else_value: BooleanIrPtr, + else_block: BasicBlockPtr, + ) -> BooleanIrPtr; + + // number + + fn compiler_peer_create_is_number(peer: CompilerPeer, value: ValueIrPtr) -> BooleanIrPtr; + fn compiler_peer_create_is_same_number( + peer: CompilerPeer, + a: NumberIrPtr, + b: NumberIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_boolean_to_number( + peer: CompilerPeer, + value: BooleanIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_to_numeric(peer: CompilerPeer, value: ValueIrPtr) -> NumberIrPtr; + fn compiler_peer_get_nan(peer: CompilerPeer) -> NumberIrPtr; + fn compiler_peer_get_zero(peer: CompilerPeer) -> NumberIrPtr; + fn compiler_peer_get_number(peer: CompilerPeer, value: f64) -> NumberIrPtr; + fn compiler_peer_create_bitwise_not(peer: CompilerPeer, value: NumberIrPtr) -> NumberIrPtr; + fn compiler_peer_create_fneg(peer: CompilerPeer, value: NumberIrPtr) -> NumberIrPtr; + fn compiler_peer_create_fmul( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_create_fdiv( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_create_frem( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_create_fadd( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_create_fsub( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_create_left_shift( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_create_signed_right_shift( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_create_unsigned_right_shift( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_create_bitwise_and( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_create_bitwise_xor( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_create_bitwise_or( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> NumberIrPtr; + fn compiler_peer_create_less_than( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_greater_than( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_less_than_or_equal( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_greater_than_or_equal( + peer: CompilerPeer, + lhs: NumberIrPtr, + rhs: NumberIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_number_phi( + peer: CompilerPeer, + then_value: NumberIrPtr, + then_block: BasicBlockPtr, + else_value: NumberIrPtr, + else_block: BasicBlockPtr, + ) -> NumberIrPtr; + + // closure + + fn compiler_peer_create_is_closure(peer: CompilerPeer, value: ValueIrPtr) -> BooleanIrPtr; + fn compiler_peer_create_is_same_closure( + peer: CompilerPeer, + a: ClosureIrPtr, + b: ClosureIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_get_closure_nullptr(peer: CompilerPeer) -> ClosureIrPtr; + fn compiler_peer_create_closure( + peer: CompilerPeer, + lambda: LambdaIrPtr, + num_captures: u16, + ) -> ClosureIrPtr; + fn compiler_peer_create_store_capture_to_closure( + peer: CompilerPeer, + capture: CaptureIrPtr, + closure: ClosureIrPtr, + index: u16, + ); + fn compiler_peer_create_call_on_closure( + peer: CompilerPeer, + closure: ClosureIrPtr, + argc: u16, + argv: ArgvIrPtr, + retv: ValueIrPtr, + ) -> StatusIrPtr; + fn compiler_peer_create_closure_phi( + peer: CompilerPeer, + then_value: ClosureIrPtr, + then_block: BasicBlockPtr, + else_value: ClosureIrPtr, + else_block: BasicBlockPtr, + ) -> ClosureIrPtr; + + // promise + + fn compiler_peer_create_is_promise(peer: CompilerPeer, value: ValueIrPtr) -> BooleanIrPtr; + fn compiler_peer_create_is_same_promise( + peer: CompilerPeer, + a: PromiseIrPtr, + b: PromiseIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_register_promise( + peer: CompilerPeer, + coroutine: CoroutineIrPtr, + ) -> PromiseIrPtr; + fn compiler_peer_create_await_promise( + peer: CompilerPeer, + promise: PromiseIrPtr, + awaiting: PromiseIrPtr, + ); + fn compiler_peer_create_resume(peer: CompilerPeer, promise: PromiseIrPtr); + fn compiler_peer_create_emit_promise_resolved( + peer: CompilerPeer, + promise: PromiseIrPtr, + result: ValueIrPtr, + ); + + // value + + fn compiler_peer_create_has_value(peer: CompilerPeer, value: ValueIrPtr) -> BooleanIrPtr; + fn compiler_peer_create_is_loosely_equal( + peer: CompilerPeer, + lhs: ValueIrPtr, + rhs: ValueIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_is_strictly_equal( + peer: CompilerPeer, + lhs: ValueIrPtr, + rhs: ValueIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_is_same_boolean_value( + peer: CompilerPeer, + value: ValueIrPtr, + boolean: BooleanIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_is_same_number_value( + peer: CompilerPeer, + value: ValueIrPtr, + number: NumberIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_is_same_closure_value( + peer: CompilerPeer, + value: ValueIrPtr, + closure: ClosureIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_is_same_promise_value( + peer: CompilerPeer, + value: ValueIrPtr, + promise: PromiseIrPtr, + ) -> BooleanIrPtr; + fn compiler_peer_create_undefined_to_any(peer: CompilerPeer) -> ValueIrPtr; + fn compiler_peer_create_null_to_any(peer: CompilerPeer) -> ValueIrPtr; + fn compiler_peer_create_boolean_to_any(peer: CompilerPeer, boolean: BooleanIrPtr) + -> ValueIrPtr; + fn compiler_peer_create_number_to_any(peer: CompilerPeer, number: NumberIrPtr) -> ValueIrPtr; + fn compiler_peer_create_closure_to_any(peer: CompilerPeer, closure: ClosureIrPtr) + -> ValueIrPtr; + fn compiler_peer_create_value_phi( + peer: CompilerPeer, + then_value: ValueIrPtr, + then_block: BasicBlockPtr, + else_value: ValueIrPtr, + else_block: BasicBlockPtr, + ) -> ValueIrPtr; + fn compiler_peer_create_local_value(peer: CompilerPeer, index: u16) -> ValueIrPtr; + fn compiler_peer_create_store_none_to_value(peer: CompilerPeer, dest: ValueIrPtr); + fn compiler_peer_create_store_undefined_to_value(peer: CompilerPeer, dest: ValueIrPtr); + fn compiler_peer_create_store_null_to_value(peer: CompilerPeer, dest: ValueIrPtr); + fn compiler_peer_create_store_boolean_to_value( + peer: CompilerPeer, + value: BooleanIrPtr, + dest: ValueIrPtr, + ); + fn compiler_peer_create_store_number_to_value( + peer: CompilerPeer, + value: NumberIrPtr, + dest: ValueIrPtr, + ); + fn compiler_peer_create_store_closure_to_value( + peer: CompilerPeer, + value: ClosureIrPtr, + dest: ValueIrPtr, + ); + fn compiler_peer_create_store_promise_to_value( + peer: CompilerPeer, + value: PromiseIrPtr, + dest: ValueIrPtr, + ); + fn compiler_peer_create_store_value_to_value( + peer: CompilerPeer, + value: ValueIrPtr, + dest: ValueIrPtr, + ); + fn compiler_peer_create_load_closure_from_value( + peer: CompilerPeer, + value: ValueIrPtr, + ) -> ClosureIrPtr; + fn compiler_peer_create_load_promise_from_value( + peer: CompilerPeer, + value: ValueIrPtr, + ) -> PromiseIrPtr; + + // argv + + fn compiler_peer_get_argv_nullptr(peer: CompilerPeer) -> ArgvIrPtr; + fn compiler_peer_create_argv(peer: CompilerPeer, argc: u16) -> ArgvIrPtr; + fn compiler_peer_create_get_arg_in_argv( + peer: CompilerPeer, + argv: ArgvIrPtr, + index: u16, + ) -> ValueIrPtr; + fn compiler_peer_create_get_argument_value_ptr(peer: CompilerPeer, index: u16) -> ValueIrPtr; + + // retv + // + // The `retv` variable holds either a returned or thrown value. + + fn compiler_peer_create_retv(peer: CompilerPeer) -> ValueIrPtr; + fn compiler_peer_create_store_undefined_to_retv(peer: CompilerPeer); + fn compiler_peer_create_store_null_to_retv(peer: CompilerPeer); + fn compiler_peer_create_store_boolean_to_retv(peer: CompilerPeer, value: BooleanIrPtr); + fn compiler_peer_create_store_number_to_retv(peer: CompilerPeer, value: NumberIrPtr); + fn compiler_peer_create_store_closure_to_retv(peer: CompilerPeer, value: ClosureIrPtr); + fn compiler_peer_create_store_promise_to_retv(peer: CompilerPeer, value: PromiseIrPtr); + fn compiler_peer_create_store_value_to_retv(peer: CompilerPeer, value: ValueIrPtr); + fn compiler_peer_get_exception(peer: CompilerPeer) -> ValueIrPtr; + + // status + // + // TODO: Currently, each lambda has its own status variable. However, it might be possible to + // use a single global variable shared by all lambdas. Because the execution model of + // JavaScript is a single threaded model. + + fn compiler_peer_create_alloc_status(peer: CompilerPeer); + fn compiler_peer_create_store_normal_status(peer: CompilerPeer); + fn compiler_peer_create_store_exception_status(peer: CompilerPeer); + fn compiler_peer_create_is_exception_status( + peer: CompilerPeer, + status: StatusIrPtr, + ) -> BooleanIrPtr; + + // flow selector + + fn compiler_peer_create_alloc_flow_selector(peer: CompilerPeer); + fn compiler_peer_create_set_flow_selector_normal(peer: CompilerPeer); + fn compiler_peer_create_set_flow_selector_return(peer: CompilerPeer); + fn compiler_peer_create_set_flow_selector_throw(peer: CompilerPeer); + fn compiler_peer_create_set_flow_selector_break(peer: CompilerPeer, depth: u32); + fn compiler_peer_create_set_flow_selector_continue(peer: CompilerPeer, depth: u32); + fn compiler_peer_create_is_flow_selector_normal(peer: CompilerPeer) -> BooleanIrPtr; + fn compiler_peer_create_is_flow_selector_normal_or_continue( + peer: CompilerPeer, + depth: u32, + ) -> BooleanIrPtr; + fn compiler_peer_create_is_flow_selector_break_or_continue( + peer: CompilerPeer, + depth: u32, + ) -> BooleanIrPtr; + fn compiler_peer_create_is_flow_selector_break(peer: CompilerPeer, depth: u32) -> BooleanIrPtr; + + // capture + + fn compiler_peer_create_capture(peer: CompilerPeer, value: ValueIrPtr) -> CaptureIrPtr; + fn compiler_peer_create_escape_value( + peer: CompilerPeer, + capture: CaptureIrPtr, + value: ValueIrPtr, + ); + fn compiler_peer_create_get_capture_value_ptr(peer: CompilerPeer, index: u16) -> ValueIrPtr; + fn compiler_peer_create_load_capture(peer: CompilerPeer, index: u16) -> CaptureIrPtr; + + // coroutine + + fn compiler_peer_create_coroutine( + peer: CompilerPeer, + closure: ClosureIrPtr, + num_locals: u16, + scratch_buffer_len: u16, + ) -> CoroutineIrPtr; + fn compiler_peer_create_switch_for_coroutine( + peer: CompilerPeer, + block: BasicBlockPtr, + num_states: u32, + ) -> SwitchIrPtr; + fn compiler_peer_create_add_state_for_coroutine( + peer: CompilerPeer, + switch_ir: SwitchIrPtr, + state: u32, + block: BasicBlockPtr, + ); + fn compiler_peer_create_suspend(peer: CompilerPeer); + fn compiler_peer_create_set_coroutine_state(peer: CompilerPeer, state: u32); + fn compiler_peer_create_set_captures_for_coroutine(peer: CompilerPeer); + fn compiler_peer_create_get_local_ptr_from_coroutine( + peer: CompilerPeer, + index: u16, + ) -> ValueIrPtr; + fn compiler_peer_create_write_boolean_to_scratch_buffer( + peer: CompilerPeer, + offset: u32, + value: BooleanIrPtr, + ); + fn compiler_peer_create_read_boolean_from_scratch_buffer( + peer: CompilerPeer, + offset: u32, + ) -> BooleanIrPtr; + fn compiler_peer_create_write_number_to_scratch_buffer( + peer: CompilerPeer, + offset: u32, + value: NumberIrPtr, + ); + fn compiler_peer_create_read_number_from_scratch_buffer( + peer: CompilerPeer, + offset: u32, + ) -> NumberIrPtr; + fn compiler_peer_create_write_closure_to_scratch_buffer( + peer: CompilerPeer, + offset: u32, + value: ClosureIrPtr, + ); + fn compiler_peer_create_read_closure_from_scratch_buffer( + peer: CompilerPeer, + offset: u32, + ) -> ClosureIrPtr; + fn compiler_peer_create_write_promise_to_scratch_buffer( + peer: CompilerPeer, + offset: u32, + value: PromiseIrPtr, + ); + fn compiler_peer_create_read_promise_from_scratch_buffer( + peer: CompilerPeer, + offset: u32, + ) -> PromiseIrPtr; + fn compiler_peer_create_write_value_to_scratch_buffer( + peer: CompilerPeer, + offset: u32, + value: ValueIrPtr, + ); + fn compiler_peer_create_read_value_from_scratch_buffer( + peer: CompilerPeer, + offset: u32, + ) -> ValueIrPtr; + + // scope cleanup checker + + fn compiler_peer_enable_scope_cleanup_checker(peer: CompilerPeer, is_coroutine: bool); + fn compiler_peer_set_scope_id_for_checker(peer: CompilerPeer, scope_id: u16); + fn compiler_peer_assert_scope_id(peer: CompilerPeer, expected: u16); + + // print + + fn compiler_peer_create_print_value(peer: CompilerPeer, value: ValueIrPtr, msg: *const c_char); + + // debugger + + fn compiler_peer_create_debugger(peer: CompilerPeer); + + // unreachable + + fn compiler_peer_create_unreachable(peer: CompilerPeer, msg: *const c_char); + + // helpers + + fn compiler_peer_get_basic_block_name_or_as_operand( + block: BasicBlockPtr, + buf: *mut c_char, + len: usize, + ) -> usize; + fn compiler_peer_get_value_name_or_as_operand( + value: ValueIrPtr, + buf: *mut c_char, + len: usize, + ) -> usize; +} diff --git a/libs/jsruntime/src/llvmir/compiler.hh b/libs/jsruntime/src/llvmir/compiler/impl.hh similarity index 95% rename from libs/jsruntime/src/llvmir/compiler.hh rename to libs/jsruntime/src/llvmir/compiler/impl.hh index 1e460cd5..4249fbc3 100644 --- a/libs/jsruntime/src/llvmir/compiler.hh +++ b/libs/jsruntime/src/llvmir/compiler/impl.hh @@ -34,16 +34,19 @@ #include #pragma GCC diagnostic pop -#include "bridge.hh" -#include "macros.hh" -#include "module.hh" +#include "../module/impl.hh" #include "type_holder.hh" -class TypeHolder; -struct Module; - +#define UNUSED(var) ((void)(var)) #define REG_NAME(expr) (enable_labels_ ? expr : "") +#define STATUS_UNSET_BIT 0x10 +#define STATUS_MASK 0x0F +#define STATUS_NORMAL 0x00 +#define STATUS_EXCEPTION 0x01 +#define STATUS_SUSPEND 0x02 +#define STATUS_UNSET (STATUS_UNSET_BIT | STATUS_NORMAL) + // DO NOT CHANGE THE FOLLOWING VALUES. // The implementation heavily depends on the values. #define FLOW_SELECTOR_KIND_RETURN 0x00000000 @@ -581,40 +584,40 @@ class Compiler { } void CreateStoreNoneToValue(llvm::Value* dest) { - CreateStoreValueKindToValue(ValueKind::None, dest); + CreateStoreValueKindToValue(kValueKindNone, dest); // zeroinitializer can be used in optimization by filling the holder with zero. CreateStoreValueHolderToValue(builder_->getInt64(0), dest); } void CreateStoreUndefinedToValue(llvm::Value* dest) { - CreateStoreValueKindToValue(ValueKind::Undefined, dest); + CreateStoreValueKindToValue(kValueKindUndefined, dest); // zeroinitializer can be used in optimization by filling the holder with zero. CreateStoreValueHolderToValue(builder_->getInt64(0), dest); } void CreateStoreNullToValue(llvm::Value* dest) { - CreateStoreValueKindToValue(ValueKind::Null, dest); + CreateStoreValueKindToValue(kValueKindNull, dest); // zeroinitializer can be used in optimization by filling the holder with zero. CreateStoreValueHolderToValue(builder_->getInt64(0), dest); } void CreateStoreBooleanToValue(llvm::Value* value, llvm::Value* dest) { - CreateStoreValueKindToValue(ValueKind::Boolean, dest); + CreateStoreValueKindToValue(kValueKindBoolean, dest); CreateStoreValueHolderToValue(value, dest); } void CreateStoreNumberToValue(llvm::Value* value, llvm::Value* dest) { - CreateStoreValueKindToValue(ValueKind::Number, dest); + CreateStoreValueKindToValue(kValueKindNumber, dest); CreateStoreValueHolderToValue(value, dest); } void CreateStoreClosureToValue(llvm::Value* value, llvm::Value* dest) { - CreateStoreValueKindToValue(ValueKind::Closure, dest); + CreateStoreValueKindToValue(kValueKindClosure, dest); CreateStoreValueHolderToValue(value, dest); } void CreateStorePromiseToValue(llvm::Value* value, llvm::Value* dest) { - CreateStoreValueKindToValue(ValueKind::Promise, dest); + CreateStoreValueKindToValue(kValueKindPromise, dest); CreateStoreValueHolderToValue(value, dest); } @@ -953,14 +956,43 @@ class Compiler { builder_->CreateUnreachable(); } + // helper functions + + // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + // based-on: Value::getNameOrAsOperand(). + static std::string GetNameOrAsOperand(llvm::Value* value) { + assert(value != nullptr); + + auto name = value->getName(); + if (!name.empty()) { + return std::string(name); + } + + std::string buffer; + llvm::raw_string_ostream os(buffer); + value->printAsOperand(os); + return buffer; + } + + static size_t GetNameOrAsOperand(llvm::Value* value, char* buf, size_t len) { + assert(value != nullptr); + assert(buf != nullptr); + assert(len > 1); + auto s = GetNameOrAsOperand(value); + auto nwritten = std::min(s.size(), len - 1); + memcpy(buf, s.data(), nwritten); + buf[nwritten] = '\0'; + return nwritten; + } + private: - static constexpr uint8_t kValueKindNone = static_cast(ValueKind::None); - static constexpr uint8_t kValueKindUndefined = static_cast(ValueKind::Undefined); - static constexpr uint8_t kValueKindNull = static_cast(ValueKind::Null); - static constexpr uint8_t kValueKindBoolean = static_cast(ValueKind::Boolean); - static constexpr uint8_t kValueKindNumber = static_cast(ValueKind::Number); - static constexpr uint8_t kValueKindClosure = static_cast(ValueKind::Closure); - static constexpr uint8_t kValueKindPromise = static_cast(ValueKind::Promise); + static constexpr uint8_t kValueKindNone = static_cast(Value::Tag::None); + static constexpr uint8_t kValueKindUndefined = static_cast(Value::Tag::Undefined); + static constexpr uint8_t kValueKindNull = static_cast(Value::Tag::Null); + static constexpr uint8_t kValueKindBoolean = static_cast(Value::Tag::Boolean); + static constexpr uint8_t kValueKindNumber = static_cast(Value::Tag::Number); + static constexpr uint8_t kValueKindClosure = static_cast(Value::Tag::Closure); + static constexpr uint8_t kValueKindPromise = static_cast(Value::Tag::Promise); void CreateStore(llvm::Value* value, llvm::Value* dest) { builder_->CreateStore(value, dest); @@ -1088,8 +1120,8 @@ class Compiler { return builder_->CreateLoad(builder_->getDoubleTy(), ptr, REG_NAME("value.number")); } - void CreateStoreValueKindToValue(ValueKind value, llvm::Value* dest) { - CreateStoreValueKindToValue(builder_->getInt8(static_cast(value)), dest); + void CreateStoreValueKindToValue(uint8_t value, llvm::Value* dest) { + CreateStoreValueKindToValue(builder_->getInt8(value), dest); } void CreateStoreValueKindToValue(llvm::Value* value, llvm::Value* dest) { @@ -1103,17 +1135,13 @@ class Compiler { } void CreateMemCpyValue(llvm::Value* dst, llvm::Value* src) { - auto* layout = module_->getDataLayout().getStructLayout(types_->CreateValueType()); - // TODO: can computed at compile-time - auto align = layout->getAlignment(); - auto* size = types_->GetWord(layout->getSizeInBytes()); + auto align = llvm::Align(alignof(Value)); + auto* size = GetSizeofValue(); builder_->CreateMemCpy(dst, align, src, align, size); } llvm::Value* GetSizeofValue() { - auto* layout = module_->getDataLayout().getStructLayout(types_->CreateValueType()); - // TODO: can computed at compile-time - return types_->GetWord(layout->getSizeInBytes()); + return types_->GetWord(sizeof(Value)); } // closure @@ -1247,10 +1275,9 @@ class Compiler { auto* num_locals = CreateLoadNumLocalsFromCoroutine(); auto* num_locals_usize = builder_->CreateSExt(num_locals, types_->GetWordType(), REG_NAME("co.num_locals.usize")); - auto* sizeof_locals = builder_->CreateMul(GetSizeofValue(), num_locals_usize, REG_NAME("co.locals.sizeof")); - auto* layout = module_->getDataLayout().getStructLayout(types_->CreateCoroutineType()); - // TODO: can computed at compile-time - auto* offsetof_locals = types_->GetWord(layout->getElementOffset(5)); + auto* sizeof_locals = + builder_->CreateMul(GetSizeofValue(), num_locals_usize, REG_NAME("co.locals.sizeof")); + auto* offsetof_locals = types_->GetWord(offsetof(Coroutine, locals)); auto* offset = builder_->CreateAdd(offsetof_locals, sizeof_locals, REG_NAME("co.scratch_buffer.offsetof")); return builder_->CreateInBoundsPtrAdd(context_, offset, REG_NAME("co.scratch_buffer.ptr")); diff --git a/libs/jsruntime/src/llvmir/compiler/mod.rs b/libs/jsruntime/src/llvmir/compiler/mod.rs index 4bc25abf..ca65d2fc 100644 --- a/libs/jsruntime/src/llvmir/compiler/mod.rs +++ b/libs/jsruntime/src/llvmir/compiler/mod.rs @@ -1,5 +1,5 @@ +mod bridge; mod control_flow; -mod peer; use std::ffi::CStr; use std::io::Write; @@ -22,21 +22,21 @@ use crate::Program; use crate::Runtime; use crate::Value; -use super::bridge; use super::Module; +use bridge::BasicBlock; +use bridge::BooleanIr; +use bridge::CaptureIr; +use bridge::ClosureIr; +use bridge::CompilerBridge; +use bridge::CoroutineIr; +use bridge::LambdaIr; +use bridge::NumberIr; +use bridge::PromiseIr; +use bridge::StatusIr; +use bridge::SwitchIr; +use bridge::ValueIr; use control_flow::ControlFlowStack; -use peer::BasicBlock; -use peer::BooleanIr; -use peer::CaptureIr; -use peer::ClosureIr; -use peer::CoroutineIr; -use peer::LambdaIr; -use peer::NumberIr; -use peer::PromiseIr; -use peer::StatusIr; -use peer::SwitchIr; -use peer::ValueIr; const VALUE_SIZE: u32 = size_of::() as u32; const VALUE_HOLDER_SIZE: u32 = size_of::() as u32; @@ -72,7 +72,7 @@ impl Runtime { /// A Compiler targeting LLVM IR. struct Compiler<'r, 's> { /// The pointer to the compiler peer. - peer: peer::Compiler, + bridge: CompilerBridge, /// The function registry of the JavaScript program to compile. function_registry: &'r mut FunctionRegistry, @@ -157,7 +157,7 @@ impl<'r, 's> Compiler<'r, 's> { pub fn new(function_registry: &'r mut FunctionRegistry, scope_tree: &'s ScopeTree) -> Self { const DUMP_BUFFER_SIZE: usize = 512; Self { - peer: Default::default(), + bridge: Default::default(), function_registry, scope_tree, operand_stack: Default::default(), @@ -185,28 +185,28 @@ impl<'r, 's> Compiler<'r, 's> { if enable_labels { self.basic_block_name_stack = Some(Default::default()); } - self.peer.start_compile(enable_labels); + self.bridge.start_compile(enable_labels); } fn end_compile(&self) -> Module { logger::debug!(event = "end_compile"); - self.peer.end_compile() + self.bridge.end_compile() } fn set_data_layout(&self, data_layout: &CStr) { logger::debug!(event = "set_data_layout", ?data_layout); - self.peer.set_data_layout(data_layout); + self.bridge.set_data_layout(data_layout); } fn set_target_triple(&self, triple: &CStr) { logger::debug!(event = "set_target_triple", ?triple); - self.peer.set_target_triple(triple); + self.bridge.set_target_triple(triple); } fn start_function(&mut self, symbol: Symbol, func_id: FunctionId) { logger::debug!(event = "start_function", ?symbol, ?func_id); - self.peer.start_function(func_id); + self.bridge.start_function(func_id); let locals_block = self.create_basic_block("locals"); let init_block = self.create_basic_block("init"); @@ -226,18 +226,18 @@ impl<'r, 's> Compiler<'r, 's> { self.control_flow_stack .push_exit_target(return_block, false); - self.peer.set_locals_block(locals_block); + self.bridge.set_locals_block(locals_block); - self.peer.set_basic_block(init_block); - self.peer.create_store_undefined_to_retv(); - self.peer.create_alloc_status(); - self.peer.create_alloc_flow_selector(); + self.bridge.set_basic_block(init_block); + self.bridge.create_store_undefined_to_retv(); + self.bridge.create_alloc_status(); + self.bridge.create_alloc_flow_selector(); if self.enable_scope_cleanup_checker { - self.peer + self.bridge .enable_scope_cleanup_checker(func_id.is_coroutine()); } - self.peer.set_basic_block(body_block); + self.bridge.set_basic_block(body_block); } fn end_function(&mut self, func_id: FunctionId, optimize: bool) { @@ -250,31 +250,31 @@ impl<'r, 's> Compiler<'r, 's> { self.control_flow_stack.pop_exit_target(); let flow = self.control_flow_stack.pop_function_flow(); - self.peer.create_br(flow.return_block); - self.peer.move_basic_block_after(flow.return_block); + self.bridge.create_br(flow.return_block); + self.bridge.move_basic_block_after(flow.return_block); - self.peer.set_basic_block(flow.locals_block); - self.peer.create_br(flow.init_block); - self.peer.move_basic_block_after(flow.init_block); + self.bridge.set_basic_block(flow.locals_block); + self.bridge.create_br(flow.init_block); + self.bridge.move_basic_block_after(flow.init_block); - self.peer.set_basic_block(flow.init_block); - self.peer.create_br(flow.args_block); - self.peer.move_basic_block_after(flow.args_block); + self.bridge.set_basic_block(flow.init_block); + self.bridge.create_br(flow.args_block); + self.bridge.move_basic_block_after(flow.args_block); - self.peer.set_basic_block(flow.args_block); - self.peer.create_br(flow.body_block); - self.peer.move_basic_block_after(flow.body_block); + self.bridge.set_basic_block(flow.args_block); + self.bridge.create_br(flow.body_block); + self.bridge.move_basic_block_after(flow.body_block); - self.peer.set_basic_block(flow.return_block); + self.bridge.set_basic_block(flow.return_block); if let Some(block) = dormant_block { - self.peer.move_basic_block_after(block); + self.bridge.move_basic_block_after(block); } if self.enable_scope_cleanup_checker { - self.peer.assert_scope_id(ScopeRef::NONE); + self.bridge.assert_scope_id(ScopeRef::NONE); } - self.peer.end_function(optimize); + self.bridge.end_function(optimize); self.locals.clear(); @@ -426,12 +426,12 @@ impl<'r, 's> Compiler<'r, 's> { } fn process_boolean(&mut self, value: bool) { - let boolean = self.peer.get_boolean(value); + let boolean = self.bridge.get_boolean(value); self.operand_stack.push(Operand::Boolean(boolean)); } fn process_number(&mut self, value: f64) { - let number = self.peer.get_number(value); + let number = self.bridge.get_number(value); self.operand_stack.push(Operand::Number(number)); } @@ -440,7 +440,7 @@ impl<'r, 's> Compiler<'r, 's> { } fn process_function(&mut self, func_id: FunctionId) { - let lambda = self.peer.get_function(func_id); + let lambda = self.bridge.get_function(func_id); self.operand_stack.push(Operand::Function(lambda)); } @@ -461,25 +461,25 @@ impl<'r, 's> Compiler<'r, 's> { fn process_closure(&mut self, prologue: bool, num_captures: u16) { debug_assert!(self.operand_stack.len() > num_captures as usize); - let backup = self.peer.get_basic_block(); + let backup = self.bridge.get_basic_block(); if prologue { let block = self.control_flow_stack.scope_flow().hoisted_block; - self.peer.set_basic_block(block); + self.bridge.set_basic_block(block); } let lambda = self.pop_lambda(); - let closure = self.peer.create_closure(lambda, num_captures); + let closure = self.bridge.create_closure(lambda, num_captures); for i in 0..num_captures { let capture = self.pop_capture(); - self.peer + self.bridge .create_store_capture_to_closure(capture, closure, i); } self.operand_stack.push(Operand::Closure(closure)); if prologue { - self.peer.set_basic_block(backup); + self.bridge.set_basic_block(backup); } } @@ -498,14 +498,14 @@ impl<'r, 's> Compiler<'r, 's> { debug_assert!(scrach_buffer_len <= u16::MAX as u32); let closure = self.pop_closure(); let coroutine = self - .peer + .bridge .create_coroutine(closure, num_locals, scrach_buffer_len as u16); self.operand_stack.push(Operand::Coroutine(coroutine)); } fn process_promise(&mut self) { let coroutine = self.pop_coroutine(); - let promise = self.peer.create_register_promise(coroutine); + let promise = self.bridge.create_register_promise(coroutine); self.operand_stack.push(Operand::Promise(promise)); } @@ -517,12 +517,12 @@ impl<'r, 's> Compiler<'r, 's> { fn process_exception(&mut self) { // TODO: Should we check status_ at runtime? self.operand_stack - .push(Operand::Any(self.peer.get_exception())); + .push(Operand::Any(self.bridge.get_exception())); } fn process_allocate_locals(&mut self, num_locals: u16) { for i in 0..num_locals { - let local = self.peer.create_local_value(i); + let local = self.bridge.create_local_value(i); self.locals.push(local); } } @@ -554,9 +554,9 @@ impl<'r, 's> Compiler<'r, 's> { fn create_get_value_ptr(&mut self, locator: Locator) -> ValueIr { match locator { - Locator::Argument(index) => self.peer.create_get_argument_value_ptr(index), + Locator::Argument(index) => self.bridge.create_get_argument_value_ptr(index), Locator::Local(index) => self.locals[index as usize], - Locator::Capture(index) => self.peer.create_get_capture_value_ptr(index), + Locator::Capture(index) => self.bridge.create_get_capture_value_ptr(index), _ => unreachable!(), } } @@ -570,13 +570,13 @@ impl<'r, 's> Compiler<'r, 's> { fn create_store_operand_to_value(&mut self, operand: Operand, dest: ValueIr) { match operand { - Operand::Undefined => self.peer.create_store_undefined_to_value(dest), - Operand::Null => self.peer.create_store_null_to_value(dest), - Operand::Boolean(value) => self.peer.create_store_boolean_to_value(value, dest), - Operand::Number(value) => self.peer.create_store_number_to_value(value, dest), - Operand::Closure(value) => self.peer.create_store_closure_to_value(value, dest), - Operand::Promise(value) => self.peer.create_store_promise_to_value(value, dest), - Operand::Any(value) => self.peer.create_store_value_to_value(value, dest), + Operand::Undefined => self.bridge.create_store_undefined_to_value(dest), + Operand::Null => self.bridge.create_store_null_to_value(dest), + Operand::Boolean(value) => self.bridge.create_store_boolean_to_value(value, dest), + Operand::Number(value) => self.bridge.create_store_number_to_value(value, dest), + Operand::Closure(value) => self.bridge.create_store_closure_to_value(value, dest), + Operand::Promise(value) => self.bridge.create_store_promise_to_value(value, dest), + Operand::Any(value) => self.bridge.create_store_value_to_value(value, dest), _ => unreachable!(), } } @@ -596,8 +596,8 @@ impl<'r, 's> Compiler<'r, 's> { fn process_declare_function(&mut self) { let block = self.control_flow_stack.scope_flow().hoisted_block; - let backup = self.peer.get_basic_block(); - self.peer.set_basic_block(block); + let backup = self.bridge.get_basic_block(); + self.bridge.set_basic_block(block); let (operand, _) = self.dereference(); // TODO: operand must hold a lambda. @@ -610,14 +610,14 @@ impl<'r, 's> Compiler<'r, 's> { self.create_store_operand_to_value(operand, value); - self.peer.set_basic_block(backup); + self.bridge.set_basic_block(backup); } fn process_declare_closure(&mut self) { let block = self.control_flow_stack.scope_flow().hoisted_block; - let backup = self.peer.get_basic_block(); - self.peer.set_basic_block(block); + let backup = self.bridge.get_basic_block(); + self.bridge.set_basic_block(block); let (operand, _) = self.dereference(); // TODO: operand must hold a closure. @@ -630,7 +630,7 @@ impl<'r, 's> Compiler<'r, 's> { self.create_store_operand_to_value(operand, value); - self.peer.set_basic_block(backup); + self.bridge.set_basic_block(backup); } fn swap(&mut self) { @@ -649,15 +649,15 @@ impl<'r, 's> Compiler<'r, 's> { fn process_call(&mut self, argc: u16) { let argv = if argc > 0 { - let argv = self.peer.create_argv(argc); + let argv = self.bridge.create_argv(argc); for i in (0..argc).rev() { let (operand, _) = self.dereference(); - let ptr = self.peer.create_get_arg_in_argv(argv, i); + let ptr = self.bridge.create_get_arg_in_argv(argv, i); self.create_store_operand_to_value(operand, ptr); } argv } else { - self.peer.get_argv_nullptr() + self.bridge.get_argv_nullptr() }; let (operand, _) = self.dereference(); @@ -671,9 +671,11 @@ impl<'r, 's> Compiler<'r, 's> { } }; - let retv = self.peer.create_retv(); + let retv = self.bridge.create_retv(); - let status = self.peer.create_call_on_closure(closure, argc, argv, retv); + let status = self + .bridge + .create_call_on_closure(closure, argc, argv, retv); self.create_check_status_for_exception(status, retv); @@ -686,27 +688,31 @@ impl<'r, 's> Compiler<'r, 's> { let end_block = self.create_basic_block("closure"); // if value.is_closure() - let is_closure = self.peer.create_is_closure(value); - self.peer.create_cond_br(is_closure, then_block, else_block); + let is_closure = self.bridge.create_is_closure(value); + self.bridge + .create_cond_br(is_closure, then_block, else_block); // then let (then_value, then_block) = { - self.peer.set_basic_block(then_block); - let closure = self.peer.create_load_closure_from_value(value); - self.peer.create_br(end_block); - (closure, self.peer.get_basic_block()) + self.bridge.set_basic_block(then_block); + let closure = self.bridge.create_load_closure_from_value(value); + self.bridge.create_br(end_block); + (closure, self.bridge.get_basic_block()) }; // else let (else_value, else_block) = { - self.peer.set_basic_block(else_block); + self.bridge.set_basic_block(else_block); // TODO: TypeError self.process_number(1.); self.process_throw(); - self.peer.create_br(end_block); - (self.peer.get_closure_nullptr(), self.peer.get_basic_block()) + self.bridge.create_br(end_block); + ( + self.bridge.get_closure_nullptr(), + self.bridge.get_basic_block(), + ) }; - self.peer.set_basic_block(end_block); - self.peer + self.bridge.set_basic_block(end_block); + self.bridge .create_closure_phi(then_value, then_block, else_value, else_block) } @@ -718,19 +724,19 @@ impl<'r, 's> Compiler<'r, 's> { let else_block = self.create_basic_block("status.normal"); // if status.is_exception() - let is_exception = self.peer.create_is_exception_status(status); - self.peer + let is_exception = self.bridge.create_is_exception_status(status); + self.bridge .create_cond_br(is_exception, then_block, else_block); // then { - self.peer.set_basic_block(then_block); - self.peer.create_store_exception_status(); - self.peer.create_set_flow_selector_throw(); - self.peer.create_store_value_to_retv(retv); - self.peer.create_br(exception_block); + self.bridge.set_basic_block(then_block); + self.bridge.create_store_exception_status(); + self.bridge.create_set_flow_selector_throw(); + self.bridge.create_store_value_to_retv(retv); + self.bridge.create_br(exception_block); } - self.peer.set_basic_block(else_block); + self.bridge.set_basic_block(else_block); } fn process_push_scope(&mut self, scope_ref: ScopeRef) { @@ -754,10 +760,10 @@ impl<'r, 's> Compiler<'r, 's> { self.control_flow_stack .push_exit_target(cleanup_block, false); - self.peer.create_br(init_block); - self.peer.move_basic_block_after(init_block); + self.bridge.create_br(init_block); + self.bridge.move_basic_block_after(init_block); - self.peer.set_basic_block(init_block); + self.bridge.set_basic_block(init_block); let scope = self.scope_tree.scope(scope_ref); for binding in scope.bindings.iter() { @@ -767,21 +773,21 @@ impl<'r, 's> Compiler<'r, 's> { let locator = binding.locator(); if binding.is_captured() { let value = match locator { - Locator::Argument(index) => self.peer.create_get_argument_value_ptr(index), + Locator::Argument(index) => self.bridge.create_get_argument_value_ptr(index), Locator::Local(index) => self.locals[index as usize], _ => unreachable!(), }; - let capture = self.peer.create_capture(value); + let capture = self.bridge.create_capture(value); debug_assert!(!self.captures.contains_key(&locator)); self.captures.insert(locator, capture); } if let Locator::Local(index) = locator { let value = self.locals[index as usize]; - self.peer.create_store_none_to_value(value); + self.bridge.create_store_none_to_value(value); } } - self.peer.set_basic_block(body_block); + self.bridge.set_basic_block(body_block); } fn process_pop_scope(&mut self, scope_ref: ScopeRef) { @@ -800,25 +806,25 @@ impl<'r, 's> Compiler<'r, 's> { let flow = self.control_flow_stack.pop_scope_flow(); debug_assert_eq!(flow.scope_ref, scope_ref); - self.peer.create_br(flow.cleanup_block); - self.peer.move_basic_block_after(flow.cleanup_block); + self.bridge.create_br(flow.cleanup_block); + self.bridge.move_basic_block_after(flow.cleanup_block); - self.peer.set_basic_block(flow.init_block); - self.peer.create_br(precheck_block); - self.peer.move_basic_block_after(precheck_block); + self.bridge.set_basic_block(flow.init_block); + self.bridge.create_br(precheck_block); + self.bridge.move_basic_block_after(precheck_block); - self.peer.set_basic_block(precheck_block); + self.bridge.set_basic_block(precheck_block); if self.enable_scope_cleanup_checker { - self.peer.set_scope_id_for_checker(scope_ref); + self.bridge.set_scope_id_for_checker(scope_ref); } - self.peer.create_br(flow.hoisted_block); - self.peer.move_basic_block_after(flow.hoisted_block); + self.bridge.create_br(flow.hoisted_block); + self.bridge.move_basic_block_after(flow.hoisted_block); - self.peer.set_basic_block(flow.hoisted_block); - self.peer.create_br(flow.body_block); - self.peer.move_basic_block_after(flow.body_block); + self.bridge.set_basic_block(flow.hoisted_block); + self.bridge.create_br(flow.body_block); + self.bridge.move_basic_block_after(flow.body_block); - self.peer.set_basic_block(flow.cleanup_block); + self.bridge.set_basic_block(flow.cleanup_block); let scope = self.scope_tree.scope(scope_ref); for binding in scope.bindings.iter() { if binding.is_captured() { @@ -829,29 +835,29 @@ impl<'r, 's> Compiler<'r, 's> { // TODO: GC } } - self.peer.create_br(postcheck_block); - self.peer.move_basic_block_after(postcheck_block); + self.bridge.create_br(postcheck_block); + self.bridge.move_basic_block_after(postcheck_block); - self.peer.set_basic_block(postcheck_block); + self.bridge.set_basic_block(postcheck_block); if self.enable_scope_cleanup_checker { - self.peer.assert_scope_id(scope_ref); + self.bridge.assert_scope_id(scope_ref); if self.control_flow_stack.has_scope_flow() { let outer_scope_ref = self.control_flow_stack.scope_flow().scope_ref; - self.peer.set_scope_id_for_checker(outer_scope_ref); + self.bridge.set_scope_id_for_checker(outer_scope_ref); } else { - self.peer.set_scope_id_for_checker(ScopeRef::NONE); + self.bridge.set_scope_id_for_checker(ScopeRef::NONE); } } - self.peer.create_br(ctrl_block); - self.peer.move_basic_block_after(ctrl_block); + self.bridge.create_br(ctrl_block); + self.bridge.move_basic_block_after(ctrl_block); - self.peer.set_basic_block(ctrl_block); - let is_normal = self.peer.create_is_flow_selector_normal(); - self.peer + self.bridge.set_basic_block(ctrl_block); + let is_normal = self.bridge.create_is_flow_selector_normal(); + self.bridge .create_cond_br(is_normal, exit_block, parent_exit_block); - self.peer.move_basic_block_after(exit_block); - self.peer.set_basic_block(exit_block); + self.bridge.move_basic_block_after(exit_block); + self.bridge.set_basic_block(exit_block); pop_bb_name!(self); } @@ -861,14 +867,14 @@ impl<'r, 's> Compiler<'r, 's> { debug_assert!(self.captures.contains_key(&locator)); let capture = self.captures.swap_remove(&locator).unwrap(); let value = self.create_get_value_ptr(locator); - self.peer.create_escape_value(capture, value); + self.bridge.create_escape_value(capture, value); } fn process_capture_value(&mut self, declaration: bool) { - let backup = self.peer.get_basic_block(); + let backup = self.bridge.get_basic_block(); if declaration { let block = self.control_flow_stack.scope_flow().hoisted_block; - self.peer.set_basic_block(block); + self.bridge.set_basic_block(block); } let (_, locator) = self.pop_reference(); @@ -877,14 +883,14 @@ impl<'r, 's> Compiler<'r, 's> { debug_assert!(self.captures.contains_key(&locator)); *self.captures.get(&locator).unwrap() } - Locator::Capture(i) => self.peer.create_load_capture(i), + Locator::Capture(i) => self.bridge.create_load_capture(i), _ => unreachable!(), }; self.operand_stack.push(Operand::Capture(capture)); if declaration { - self.peer.set_basic_block(backup); + self.bridge.set_basic_block(backup); } } @@ -896,11 +902,11 @@ impl<'r, 's> Compiler<'r, 's> { let (operand, reference) = self.dereference(); let old_value = self.to_numeric(operand); // TODO: BigInt - let one = self.peer.get_number(1.0); + let one = self.bridge.get_number(1.0); let new_value = if op == '+' { - self.peer.create_fadd(old_value, one) + self.bridge.create_fadd(old_value, one) } else { - self.peer.create_fsub(old_value, one) + self.bridge.create_fsub(old_value, one) }; match reference { Some((symbol, locator)) if symbol != Symbol::NONE => { @@ -924,12 +930,12 @@ impl<'r, 's> Compiler<'r, 's> { // 7.1.4 ToNumber ( argument ) fn to_numeric(&self, operand: Operand) -> NumberIr { match operand { - Operand::Undefined => self.peer.get_nan(), - Operand::Null => self.peer.get_zero(), - Operand::Boolean(value) => self.peer.create_boolean_to_number(value), + Operand::Undefined => self.bridge.get_nan(), + Operand::Null => self.bridge.get_zero(), + Operand::Boolean(value) => self.bridge.create_boolean_to_number(value), Operand::Number(value) => value, - Operand::Closure(_) => self.peer.get_nan(), - Operand::Any(value) => self.peer.to_numeric(value), + Operand::Closure(_) => self.bridge.get_nan(), + Operand::Any(value) => self.bridge.to_numeric(value), _ => unreachable!(), } } @@ -984,7 +990,7 @@ impl<'r, 's> Compiler<'r, 's> { let value = self.to_numeric(operand); // TODO: BigInt // 6.1.6.1.1 Number::unaryMinus ( x ) - let value = self.peer.create_fneg(value); + let value = self.bridge.create_fneg(value); self.operand_stack.push(Operand::Number(value)); } @@ -993,7 +999,7 @@ impl<'r, 's> Compiler<'r, 's> { let (operand, _) = self.dereference(); let number = self.to_numeric(operand); // TODO: BigInt - let number = self.peer.create_bitwise_not(number); + let number = self.bridge.create_bitwise_not(number); self.operand_stack.push(Operand::Number(number)); } @@ -1001,17 +1007,17 @@ impl<'r, 's> Compiler<'r, 's> { fn process_logical_not(&mut self) { let (operand, _) = self.dereference(); let boolean = self.create_to_boolean(operand); - let boolean = self.peer.create_logical_not(boolean); + let boolean = self.bridge.create_logical_not(boolean); self.operand_stack.push(Operand::Boolean(boolean)); } fn create_to_boolean(&mut self, operand: Operand) -> BooleanIr { match operand { - Operand::Undefined | Operand::Null => self.peer.get_boolean(false), + Operand::Undefined | Operand::Null => self.bridge.get_boolean(false), Operand::Boolean(value) => value, - Operand::Number(value) => self.peer.create_number_to_boolean(value), - Operand::Closure(_) => self.peer.get_boolean(true), - Operand::Any(value) => self.peer.create_to_boolean(value), + Operand::Number(value) => self.bridge.create_number_to_boolean(value), + Operand::Closure(_) => self.bridge.get_boolean(true), + Operand::Any(value) => self.bridge.create_to_boolean(value), _ => unreachable!(), } } @@ -1029,7 +1035,7 @@ impl<'r, 's> Compiler<'r, 's> { let (rhs, _) = self.dereference(); let rhs = self.to_numeric(rhs); - let number = self.peer.create_fmul(lhs, rhs); + let number = self.bridge.create_fmul(lhs, rhs); self.operand_stack.push(Operand::Number(number)); } @@ -1041,7 +1047,7 @@ impl<'r, 's> Compiler<'r, 's> { let (rhs, _) = self.dereference(); let rhs = self.to_numeric(rhs); - let number = self.peer.create_fdiv(lhs, rhs); + let number = self.bridge.create_fdiv(lhs, rhs); self.operand_stack.push(Operand::Number(number)); } @@ -1053,7 +1059,7 @@ impl<'r, 's> Compiler<'r, 's> { let (rhs, _) = self.dereference(); let rhs = self.to_numeric(rhs); - let number = self.peer.create_frem(lhs, rhs); + let number = self.bridge.create_frem(lhs, rhs); self.operand_stack.push(Operand::Number(number)); } @@ -1065,7 +1071,7 @@ impl<'r, 's> Compiler<'r, 's> { let (rhs, _) = self.dereference(); let rhs = self.to_numeric(rhs); - let number = self.peer.create_fadd(lhs, rhs); + let number = self.bridge.create_fadd(lhs, rhs); self.operand_stack.push(Operand::Number(number)); } @@ -1077,7 +1083,7 @@ impl<'r, 's> Compiler<'r, 's> { let (rhs, _) = self.dereference(); let rhs = self.to_numeric(rhs); - let number = self.peer.create_fsub(lhs, rhs); + let number = self.bridge.create_fsub(lhs, rhs); self.operand_stack.push(Operand::Number(number)); } @@ -1092,7 +1098,7 @@ impl<'r, 's> Compiler<'r, 's> { // 13.15.3 ApplyStringOrNumericBinaryOperator ( lval, opText, rval ) // TODO: BigInt - let number = self.peer.create_left_shift(lhs, rhs); + let number = self.bridge.create_left_shift(lhs, rhs); self.operand_stack.push(Operand::Number(number)); } @@ -1107,7 +1113,7 @@ impl<'r, 's> Compiler<'r, 's> { // 13.15.3 ApplyStringOrNumericBinaryOperator ( lval, opText, rval ) // TODO: BigInt - let number = self.peer.create_signed_right_shift(lhs, rhs); + let number = self.bridge.create_signed_right_shift(lhs, rhs); self.operand_stack.push(Operand::Number(number)); } @@ -1122,7 +1128,7 @@ impl<'r, 's> Compiler<'r, 's> { // 13.15.3 ApplyStringOrNumericBinaryOperator ( lval, opText, rval ) // TODO: BigInt - let number = self.peer.create_unsigned_right_shift(lhs, rhs); + let number = self.bridge.create_unsigned_right_shift(lhs, rhs); self.operand_stack.push(Operand::Number(number)); } @@ -1134,7 +1140,7 @@ impl<'r, 's> Compiler<'r, 's> { let (rhs, _) = self.dereference(); let rhs = self.to_numeric(rhs); - let boolean = self.peer.create_less_than(lhs, rhs); + let boolean = self.bridge.create_less_than(lhs, rhs); self.operand_stack.push(Operand::Boolean(boolean)); } @@ -1146,7 +1152,7 @@ impl<'r, 's> Compiler<'r, 's> { let (rhs, _) = self.dereference(); let rhs = self.to_numeric(rhs); - let boolean = self.peer.create_greater_than(lhs, rhs); + let boolean = self.bridge.create_greater_than(lhs, rhs); self.operand_stack.push(Operand::Boolean(boolean)); } @@ -1158,7 +1164,7 @@ impl<'r, 's> Compiler<'r, 's> { let (rhs, _) = self.dereference(); let rhs = self.to_numeric(rhs); - let boolean = self.peer.create_less_than_or_equal(lhs, rhs); + let boolean = self.bridge.create_less_than_or_equal(lhs, rhs); self.operand_stack.push(Operand::Boolean(boolean)); } @@ -1170,7 +1176,7 @@ impl<'r, 's> Compiler<'r, 's> { let (rhs, _) = self.dereference(); let rhs = self.to_numeric(rhs); - let boolean = self.peer.create_greater_than_or_equal(lhs, rhs); + let boolean = self.bridge.create_greater_than_or_equal(lhs, rhs); self.operand_stack.push(Operand::Boolean(boolean)); } @@ -1202,12 +1208,12 @@ impl<'r, 's> Compiler<'r, 's> { if let Operand::Any(lhs) = lhs { // TODO: compile-time evaluation let rhs = self.create_to_any(rhs); - return self.peer.create_is_loosely_equal(lhs, rhs); + return self.bridge.create_is_loosely_equal(lhs, rhs); } if let Operand::Any(rhs) = rhs { // TODO: compile-time evaluation let lhs = self.create_to_any(lhs); - return self.peer.create_is_loosely_equal(lhs, rhs); + return self.bridge.create_is_loosely_equal(lhs, rhs); } // 1. If Type(x) is Type(y), then Return IsStrictlyEqual(x, y). @@ -1217,12 +1223,12 @@ impl<'r, 's> Compiler<'r, 's> { // 2. If x is null and y is undefined, return true. if matches!(lhs, Operand::Null) && matches!(rhs, Operand::Undefined) { - return self.peer.get_boolean(true); + return self.bridge.get_boolean(true); } // 3. If x is undefined and y is null, return true. if matches!(lhs, Operand::Undefined) && matches!(rhs, Operand::Null) { - return self.peer.get_boolean(true); + return self.bridge.get_boolean(true); } // TODO: 5. If x is a Number and y is a String, return ! IsLooselyEqual(x, ! ToNumber(y)). @@ -1235,18 +1241,18 @@ impl<'r, 's> Compiler<'r, 's> { // TODO: ... let lhs = self.create_to_any(lhs); let rhs = self.create_to_any(rhs); - self.peer.create_is_loosely_equal(lhs, rhs) + self.bridge.create_is_loosely_equal(lhs, rhs) } fn create_to_any(&mut self, operand: Operand) -> ValueIr { logger::debug!(event = "create_to_any", ?operand); match operand { Operand::Any(value) => value, - Operand::Undefined => self.peer.create_undefined_to_any(), - Operand::Null => self.peer.create_null_to_any(), - Operand::Boolean(value) => self.peer.create_boolean_to_any(value), - Operand::Number(value) => self.peer.create_number_to_any(value), - Operand::Closure(value) => self.peer.create_closure_to_any(value), + Operand::Undefined => self.bridge.create_undefined_to_any(), + Operand::Null => self.bridge.create_null_to_any(), + Operand::Boolean(value) => self.bridge.create_boolean_to_any(value), + Operand::Number(value) => self.bridge.create_number_to_any(value), + Operand::Closure(value) => self.bridge.create_closure_to_any(value), _ => unreachable!(), } } @@ -1261,23 +1267,23 @@ impl<'r, 's> Compiler<'r, 's> { return self.create_any_is_strictly_equal(rhs, lhs); } if std::mem::discriminant(&lhs) != std::mem::discriminant(&rhs) { - return self.peer.get_boolean(false); + return self.bridge.get_boolean(false); } // TODO: BigInt match (lhs, rhs) { - (Operand::Undefined, Operand::Undefined) => self.peer.get_boolean(true), - (Operand::Null, Operand::Null) => self.peer.get_boolean(true), + (Operand::Undefined, Operand::Undefined) => self.bridge.get_boolean(true), + (Operand::Null, Operand::Null) => self.bridge.get_boolean(true), (Operand::Boolean(lhs), Operand::Boolean(rhs)) => { - self.peer.create_is_same_boolean(lhs, rhs) + self.bridge.create_is_same_boolean(lhs, rhs) } (Operand::Number(lhs), Operand::Number(rhs)) => { - self.peer.create_is_same_number(lhs, rhs) + self.bridge.create_is_same_number(lhs, rhs) } (Operand::Closure(lhs), Operand::Closure(rhs)) => { - self.peer.create_is_same_closure(lhs, rhs) + self.bridge.create_is_same_closure(lhs, rhs) } (Operand::Promise(lhs), Operand::Promise(rhs)) => { - self.peer.create_is_same_promise(lhs, rhs) + self.bridge.create_is_same_promise(lhs, rhs) } _ => unreachable!(), } @@ -1286,13 +1292,13 @@ impl<'r, 's> Compiler<'r, 's> { fn create_any_is_strictly_equal(&mut self, lhs: ValueIr, rhs: Operand) -> BooleanIr { logger::debug!(event = "create_any_is_strictly_equal", ?lhs, ?rhs); match rhs { - Operand::Undefined => self.peer.create_is_undefined(lhs), - Operand::Null => self.peer.create_is_null(lhs), + Operand::Undefined => self.bridge.create_is_undefined(lhs), + Operand::Null => self.bridge.create_is_null(lhs), Operand::Boolean(rhs) => self.create_is_same_boolean_value(lhs, rhs), Operand::Number(rhs) => self.create_is_same_number_value(lhs, rhs), Operand::Closure(rhs) => self.create_is_same_closure_value(lhs, rhs), Operand::Promise(rhs) => self.create_is_same_promise_value(lhs, rhs), - Operand::Any(rhs) => self.peer.create_is_strictly_equal(lhs, rhs), + Operand::Any(rhs) => self.bridge.create_is_strictly_equal(lhs, rhs), _ => unreachable!(), } } @@ -1303,19 +1309,19 @@ impl<'r, 's> Compiler<'r, 's> { let merge_block = self.create_basic_block("is_boolean"); // if value.kind == ValueKind::Boolean - let cond = self.peer.create_is_boolean(value); - self.peer.create_cond_br(cond, then_block, else_block); + let cond = self.bridge.create_is_boolean(value); + self.bridge.create_cond_br(cond, then_block, else_block); // { - self.peer.set_basic_block(then_block); - let then_value = self.peer.create_is_same_boolean_value(value, boolean); - self.peer.create_br(merge_block); + self.bridge.set_basic_block(then_block); + let then_value = self.bridge.create_is_same_boolean_value(value, boolean); + self.bridge.create_br(merge_block); // } else { - self.peer.set_basic_block(else_block); - let else_value = self.peer.get_boolean(false); - self.peer.create_br(merge_block); + self.bridge.set_basic_block(else_block); + let else_value = self.bridge.get_boolean(false); + self.bridge.create_br(merge_block); // } - self.peer.set_basic_block(merge_block); - self.peer + self.bridge.set_basic_block(merge_block); + self.bridge .create_boolean_phi(then_value, then_block, else_value, else_block) } @@ -1327,19 +1333,19 @@ impl<'r, 's> Compiler<'r, 's> { let merge_block = self.create_basic_block("is_number"); // if value.kind == ValueKind::Number - let cond = self.peer.create_is_number(value); - self.peer.create_cond_br(cond, then_block, else_block); + let cond = self.bridge.create_is_number(value); + self.bridge.create_cond_br(cond, then_block, else_block); // { - self.peer.set_basic_block(then_block); - let then_value = self.peer.create_is_same_number_value(value, number); - self.peer.create_br(merge_block); + self.bridge.set_basic_block(then_block); + let then_value = self.bridge.create_is_same_number_value(value, number); + self.bridge.create_br(merge_block); // } else { - self.peer.set_basic_block(else_block); - let else_value = self.peer.get_boolean(false); - self.peer.create_br(merge_block); + self.bridge.set_basic_block(else_block); + let else_value = self.bridge.get_boolean(false); + self.bridge.create_br(merge_block); // } - self.peer.set_basic_block(merge_block); - self.peer + self.bridge.set_basic_block(merge_block); + self.bridge .create_boolean_phi(then_value, then_block, else_value, else_block) } @@ -1349,19 +1355,19 @@ impl<'r, 's> Compiler<'r, 's> { let merge_block = self.create_basic_block("is_closure"); // if value.kind == ValueKind::Number - let cond = self.peer.create_is_closure(value); - self.peer.create_cond_br(cond, then_block, else_block); + let cond = self.bridge.create_is_closure(value); + self.bridge.create_cond_br(cond, then_block, else_block); // { - self.peer.set_basic_block(then_block); - let then_value = self.peer.create_is_same_closure_value(value, closure); - self.peer.create_br(merge_block); + self.bridge.set_basic_block(then_block); + let then_value = self.bridge.create_is_same_closure_value(value, closure); + self.bridge.create_br(merge_block); // } else { - self.peer.set_basic_block(else_block); - let else_value = self.peer.get_boolean(false); - self.peer.create_br(merge_block); + self.bridge.set_basic_block(else_block); + let else_value = self.bridge.get_boolean(false); + self.bridge.create_br(merge_block); // } - self.peer.set_basic_block(merge_block); - self.peer + self.bridge.set_basic_block(merge_block); + self.bridge .create_boolean_phi(then_value, then_block, else_value, else_block) } @@ -1371,19 +1377,19 @@ impl<'r, 's> Compiler<'r, 's> { let merge_block = self.create_basic_block("is_promise"); // if value.kind == ValueKind::Promise - let cond = self.peer.create_is_promise(value); - self.peer.create_cond_br(cond, then_block, else_block); + let cond = self.bridge.create_is_promise(value); + self.bridge.create_cond_br(cond, then_block, else_block); // { - self.peer.set_basic_block(then_block); - let then_value = self.peer.create_is_same_promise_value(value, promise); - self.peer.create_br(merge_block); + self.bridge.set_basic_block(then_block); + let then_value = self.bridge.create_is_same_promise_value(value, promise); + self.bridge.create_br(merge_block); // } else { - self.peer.set_basic_block(else_block); - let else_value = self.peer.get_boolean(false); - self.peer.create_br(merge_block); + self.bridge.set_basic_block(else_block); + let else_value = self.bridge.get_boolean(false); + self.bridge.create_br(merge_block); // } - self.peer.set_basic_block(merge_block); - self.peer + self.bridge.set_basic_block(merge_block); + self.bridge .create_boolean_phi(then_value, then_block, else_value, else_block) } @@ -1394,7 +1400,7 @@ impl<'r, 's> Compiler<'r, 's> { let (rhs, _) = self.dereference(); let eq = self.create_is_loosely_equal(lhs, rhs); - let boolean = self.peer.create_logical_not(eq); + let boolean = self.bridge.create_logical_not(eq); self.operand_stack.push(Operand::Boolean(boolean)); } @@ -1415,7 +1421,7 @@ impl<'r, 's> Compiler<'r, 's> { let (rhs, _) = self.dereference(); let eq = self.create_is_strictly_equal(lhs, rhs); - let boolean = self.peer.create_logical_not(eq); + let boolean = self.bridge.create_logical_not(eq); self.operand_stack.push(Operand::Boolean(boolean)); } @@ -1430,7 +1436,7 @@ impl<'r, 's> Compiler<'r, 's> { let rnum = self.to_numeric(rval); // TODO: BigInt - let number = self.peer.create_bitwise_and(lnum, rnum); + let number = self.bridge.create_bitwise_and(lnum, rnum); self.operand_stack.push(Operand::Number(number)); } @@ -1445,7 +1451,7 @@ impl<'r, 's> Compiler<'r, 's> { let rnum = self.to_numeric(rval); // TODO: BigInt - let number = self.peer.create_bitwise_xor(lnum, rnum); + let number = self.bridge.create_bitwise_xor(lnum, rnum); self.operand_stack.push(Operand::Number(number)); } @@ -1460,30 +1466,30 @@ impl<'r, 's> Compiler<'r, 's> { let rnum = self.to_numeric(rval); // TODO: BigInt - let number = self.peer.create_bitwise_or(lnum, rnum); + let number = self.bridge.create_bitwise_or(lnum, rnum); self.operand_stack.push(Operand::Number(number)); } fn process_ternary(&mut self) { let flow = self.control_flow_stack.pop_if_then_else_flow(); let then_block = flow.then_block; - let else_block = self.peer.get_basic_block(); + let else_block = self.bridge.get_basic_block(); let (else_operand, _) = self.dereference(); - self.peer.set_basic_block(then_block); + self.bridge.set_basic_block(then_block); let (then_operand, _) = self.dereference(); let block = self.create_basic_block("ternary"); if std::mem::discriminant(&then_operand) == std::mem::discriminant(&else_operand) { - self.peer.set_basic_block(then_block); - self.peer.create_br(block); + self.bridge.set_basic_block(then_block); + self.bridge.create_br(block); - self.peer.set_basic_block(else_block); - self.peer.create_br(block); + self.bridge.set_basic_block(else_block); + self.bridge.create_br(block); - self.peer.set_basic_block(block); + self.bridge.set_basic_block(block); // In this case, we can use the value of each item as is. match (then_operand, else_operand) { @@ -1497,21 +1503,21 @@ impl<'r, 's> Compiler<'r, 's> { } (Operand::Boolean(then_value), Operand::Boolean(else_value)) => { let boolean = self - .peer + .bridge .create_boolean_phi(then_value, then_block, else_value, else_block); self.operand_stack.push(Operand::Boolean(boolean)); return; } (Operand::Number(then_value), Operand::Number(else_value)) => { let number = self - .peer + .bridge .create_number_phi(then_value, then_block, else_value, else_block); self.operand_stack.push(Operand::Number(number)); return; } (Operand::Any(then_value), Operand::Any(else_value)) => { let any = self - .peer + .bridge .create_value_phi(then_value, then_block, else_value, else_block); self.operand_stack.push(Operand::Any(any)); return; @@ -1522,17 +1528,17 @@ impl<'r, 's> Compiler<'r, 's> { // We have to convert the value before the branch in each block. - self.peer.set_basic_block(then_block); + self.bridge.set_basic_block(then_block); let then_value = self.create_to_any(then_operand); - self.peer.create_br(block); + self.bridge.create_br(block); - self.peer.set_basic_block(else_block); + self.bridge.set_basic_block(else_block); let else_value = self.create_to_any(else_operand); - self.peer.create_br(block); + self.bridge.create_br(block); - self.peer.set_basic_block(block); + self.bridge.set_basic_block(block); let any = self - .peer + .bridge .create_value_phi(then_value, then_block, else_value, else_block); self.operand_stack.push(Operand::Any(any)); } @@ -1561,7 +1567,7 @@ impl<'r, 's> Compiler<'r, 's> { fn process_falsy_short_circuit(&mut self) { let (operand, _) = self.dereference(); let boolean = self.create_to_boolean(operand.clone()); - let boolean = self.peer.create_logical_not(boolean); + let boolean = self.bridge.create_logical_not(boolean); self.operand_stack.push(Operand::Boolean(boolean)); self.process_if_then(); self.operand_stack.push(operand); @@ -1588,11 +1594,11 @@ impl<'r, 's> Compiler<'r, 's> { fn create_is_non_nullish(&mut self, operand: Operand) -> BooleanIr { match operand { - Operand::Undefined | Operand::Null => self.peer.get_boolean(false), + Operand::Undefined | Operand::Null => self.bridge.get_boolean(false), Operand::Boolean(_) | Operand::Number(_) | Operand::Closure(_) => { - self.peer.get_boolean(true) + self.bridge.get_boolean(true) } - Operand::Any(value) => self.peer.create_is_non_nullish(value), + Operand::Any(value) => self.bridge.create_is_non_nullish(value), _ => unreachable!(), } } @@ -1607,64 +1613,65 @@ impl<'r, 's> Compiler<'r, 's> { let cond_value = self.pop_boolean(); let then_block = self.create_basic_block("then"); let else_block = self.create_basic_block("else"); - self.peer.create_cond_br(cond_value, then_block, else_block); - self.peer.set_basic_block(then_block); + self.bridge + .create_cond_br(cond_value, then_block, else_block); + self.bridge.set_basic_block(then_block); self.control_flow_stack .push_if_then_else_flow(then_block, else_block); } fn process_else(&mut self) { - let then_block = self.peer.get_basic_block(); + let then_block = self.bridge.get_basic_block(); let else_block = self.control_flow_stack.update_then_block(then_block); - self.peer.move_basic_block_after(else_block); - self.peer.set_basic_block(else_block); + self.bridge.move_basic_block_after(else_block); + self.bridge.set_basic_block(else_block); } fn process_if_else_statement(&mut self) { let flow = self.control_flow_stack.pop_if_then_else_flow(); - let else_block = self.peer.get_basic_block(); + let else_block = self.bridge.get_basic_block(); let mut block = BasicBlock::NONE; - if self.peer.is_basic_block_terminated(else_block) { + if self.bridge.is_basic_block_terminated(else_block) { // We should not append any instructions after a terminator instruction such as `ret`. } else { block = self.create_basic_block("merge"); - self.peer.create_br(block); + self.bridge.create_br(block); } - if self.peer.is_basic_block_terminated(flow.then_block) { + if self.bridge.is_basic_block_terminated(flow.then_block) { // We should not append any instructions after a terminator instruction such as `ret`. } else { if block == BasicBlock::NONE { block = self.create_basic_block("merge"); } - self.peer.set_basic_block(flow.then_block); - self.peer.create_br(block); + self.bridge.set_basic_block(flow.then_block); + self.bridge.create_br(block); } if block != BasicBlock::NONE { - self.peer.set_basic_block(block); + self.bridge.set_basic_block(block); } } fn process_if_statement(&mut self) { let flow = self.control_flow_stack.pop_if_then_else_flow(); - let then_block = self.peer.get_basic_block(); + let then_block = self.bridge.get_basic_block(); let block = self.create_basic_block("merge"); - if self.peer.is_basic_block_terminated(then_block) { + if self.bridge.is_basic_block_terminated(then_block) { // We should not append any instructions after a terminator instruction such as `ret`. } else { - self.peer.create_br(block); + self.bridge.create_br(block); } - self.peer.move_basic_block_after(flow.else_block); - self.peer.set_basic_block(flow.else_block); - self.peer.create_br(block); + self.bridge.move_basic_block_after(flow.else_block); + self.bridge.set_basic_block(flow.else_block); + self.bridge.create_br(block); - self.peer.set_basic_block(block); + self.bridge.set_basic_block(block); } fn process_do_while_loop(&mut self, id: u16) { @@ -1684,11 +1691,11 @@ impl<'r, 's> Compiler<'r, 's> { self.control_flow_stack .push_loop_body_flow(loop_ctrl, loop_test); - self.peer.create_br(loop_start); + self.bridge.create_br(loop_start); self.build_loop_ctrl_block(loop_ctrl, loop_continue, loop_break); - self.peer.set_basic_block(loop_start); + self.bridge.set_basic_block(loop_start); } fn process_while_loop(&mut self, id: u16) { @@ -1708,11 +1715,11 @@ impl<'r, 's> Compiler<'r, 's> { self.control_flow_stack .push_loop_test_flow(loop_body, loop_exit, loop_body); - self.peer.create_br(loop_start); + self.bridge.create_br(loop_start); self.build_loop_ctrl_block(loop_ctrl, loop_continue, loop_break); - self.peer.set_basic_block(loop_start); + self.bridge.set_basic_block(loop_start); } // TODO: rewrite using if and break @@ -1792,11 +1799,11 @@ impl<'r, 's> Compiler<'r, 's> { insert_point = loop_init; } - self.peer.create_br(loop_start); + self.bridge.create_br(loop_start); self.build_loop_ctrl_block(loop_ctrl, loop_continue, loop_break); - self.peer.set_basic_block(insert_point); + self.bridge.set_basic_block(insert_point); } fn build_loop_ctrl_block( @@ -1814,56 +1821,56 @@ impl<'r, 's> Compiler<'r, 's> { } let exit_id = self.control_flow_stack.exit_id(); - self.peer.set_basic_block(loop_ctrl); + self.bridge.set_basic_block(loop_ctrl); let is_normal_or_continue = self - .peer + .bridge .create_is_flow_selector_normal_or_continue(exit_id.depth()); let is_break_or_continue = self - .peer + .bridge .create_is_flow_selector_break_or_continue(exit_id.depth()); - self.peer.create_cond_br( + self.bridge.create_cond_br( is_break_or_continue, set_normal_block, break_or_continue_block, ); - self.peer.set_basic_block(set_normal_block); - self.peer.create_set_flow_selector_normal(); - self.peer.create_br(break_or_continue_block); + self.bridge.set_basic_block(set_normal_block); + self.bridge.create_set_flow_selector_normal(); + self.bridge.create_br(break_or_continue_block); - self.peer.set_basic_block(break_or_continue_block); - self.peer + self.bridge.set_basic_block(break_or_continue_block); + self.bridge .create_cond_br(is_normal_or_continue, loop_continue, loop_break); } fn process_loop_init(&mut self) { let loop_init = self.control_flow_stack.pop_loop_init_flow(); - self.peer.create_br(loop_init.branch_block); - self.peer.set_basic_block(loop_init.insert_point); + self.bridge.create_br(loop_init.branch_block); + self.bridge.set_basic_block(loop_init.insert_point); } fn process_loop_test(&mut self) { let loop_test = self.control_flow_stack.pop_loop_test_flow(); let (operand, _) = self.dereference(); let cond = self.create_to_boolean(operand); - self.peer + self.bridge .create_cond_br(cond, loop_test.then_block, loop_test.else_block); - self.peer.set_basic_block(loop_test.insert_point); + self.bridge.set_basic_block(loop_test.insert_point); } fn process_loop_next(&mut self) { let loop_next = self.control_flow_stack.pop_loop_next_flow(); // Discard the evaluation result. self.process_discard(); - self.peer.create_br(loop_next.branch_block); - self.peer.set_basic_block(loop_next.insert_point); + self.bridge.create_br(loop_next.branch_block); + self.bridge.set_basic_block(loop_next.insert_point); } fn process_loop_body(&mut self) { let loop_body = self.control_flow_stack.pop_loop_body_flow(); - self.peer.create_br(loop_body.branch_block); - self.peer.move_basic_block_after(loop_body.insert_point); - self.peer.set_basic_block(loop_body.insert_point); + self.bridge.create_br(loop_body.branch_block); + self.bridge.move_basic_block_after(loop_body.insert_point); + self.bridge.set_basic_block(loop_body.insert_point); } fn process_loop_end(&mut self) { @@ -1886,35 +1893,35 @@ impl<'r, 's> Compiler<'r, 's> { debug_assert!(self.pending_labels.is_empty()); let exit_id = self.control_flow_stack.exit_id(); - self.peer.create_br(case_block); + self.bridge.create_br(case_block); - self.peer.set_basic_block(ctrl_block); - let is_break = self.peer.create_is_flow_selector_break(exit_id.depth()); - self.peer + self.bridge.set_basic_block(ctrl_block); + let is_break = self.bridge.create_is_flow_selector_break(exit_id.depth()); + self.bridge .create_cond_br(is_break, set_normal_block, end_block); - self.peer.set_basic_block(set_normal_block); - self.peer.create_set_flow_selector_normal(); - self.peer.create_br(end_block); + self.bridge.set_basic_block(set_normal_block); + self.bridge.create_set_flow_selector_normal(); + self.bridge.create_br(end_block); - self.peer.set_basic_block(case_block); + self.bridge.set_basic_block(case_block); } fn process_case(&mut self) { let clause_start_block = self.create_basic_block("case.clause"); let next_case_block = self.create_basic_block("case"); let cond_value = self.pop_boolean(); - self.peer + self.bridge .create_cond_br(cond_value, clause_start_block, next_case_block); - self.peer.set_basic_block(clause_start_block); + self.bridge.set_basic_block(clause_start_block); self.control_flow_stack .push_case_flow(next_case_block, clause_start_block); } fn process_default(&mut self) { - let next_case_block = self.peer.get_basic_block(); + let next_case_block = self.bridge.get_basic_block(); let clause_start_block = self.create_basic_block("default.clause"); - self.peer.set_basic_block(clause_start_block); + self.bridge.set_basic_block(clause_start_block); self.control_flow_stack .push_case_flow(next_case_block, clause_start_block); self.control_flow_stack @@ -1922,17 +1929,17 @@ impl<'r, 's> Compiler<'r, 's> { } fn process_case_clause(&mut self, has_statement: bool) { - let clause_end_block = self.peer.get_basic_block(); + let clause_end_block = self.bridge.get_basic_block(); let next_case_block = self .control_flow_stack .update_case_flow(clause_end_block, has_statement); - self.peer.set_basic_block(next_case_block); + self.bridge.set_basic_block(next_case_block); } fn process_switch(&mut self, _id: u16, num_cases: u16, _default_index: Option) { pop_bb_name!(self); - let last_case_block = self.peer.get_basic_block(); + let last_case_block = self.bridge.get_basic_block(); // Connect the last basic blocks of each case/default clause to the first basic block of // the statement lists of the next case/default clause if it's not terminated. @@ -1942,11 +1949,11 @@ impl<'r, 's> Compiler<'r, 's> { debug_assert_ne!(fall_through_block, BasicBlock::NONE); for _ in 0..num_cases { let flow = self.control_flow_stack.pop_case_flow(); - let terminated = self.peer.is_basic_block_terminated(flow.clause_end_block); + let terminated = self.bridge.is_basic_block_terminated(flow.clause_end_block); if !terminated { - self.peer.set_basic_block(flow.clause_end_block); - self.peer.create_br(fall_through_block); - self.peer.move_basic_block_after(fall_through_block); + self.bridge.set_basic_block(flow.clause_end_block); + self.bridge.create_br(fall_through_block); + self.bridge.move_basic_block_after(fall_through_block); } fall_through_block = flow.clause_start_block; debug_assert_ne!(fall_through_block, BasicBlock::NONE); @@ -1957,16 +1964,16 @@ impl<'r, 's> Compiler<'r, 's> { // Create an unconditional jump to the statement of the default clause if it exists. // Otherwise, jump to the end block. - self.peer.set_basic_block(last_case_block); - self.peer + self.bridge.set_basic_block(last_case_block); + self.bridge .create_br(if switch.default_block != BasicBlock::NONE { switch.default_block } else { switch.end_block }); - self.peer.move_basic_block_after(switch.end_block); - self.peer.set_basic_block(switch.end_block); + self.bridge.move_basic_block_after(switch.end_block); + self.bridge.set_basic_block(switch.end_block); } fn process_try(&mut self) { @@ -1984,9 +1991,9 @@ impl<'r, 's> Compiler<'r, 's> { self.control_flow_stack.push_exit_target(catch_block, false); // Jump from the end of previous block to the beginning of the try block. - self.peer.create_br(try_block); + self.bridge.create_br(try_block); - self.peer.set_basic_block(try_block); + self.bridge.set_basic_block(try_block); push_bb_name!(self, "try"); } @@ -2005,13 +2012,13 @@ impl<'r, 's> Compiler<'r, 's> { .push_exit_target(finally_block, false); // Jump from the end of the try block to the beginning of the finally block. - self.peer.create_br(finally_block); - self.peer.move_basic_block_after(catch_block); - self.peer.set_basic_block(catch_block); + self.bridge.create_br(finally_block); + self.bridge.move_basic_block_after(catch_block); + self.bridge.set_basic_block(catch_block); if !nominal { - self.peer.create_store_normal_status(); - self.peer.create_set_flow_selector_normal(); + self.bridge.create_store_normal_status(); + self.bridge.create_set_flow_selector_normal(); } push_bb_name!(self, "catch"); @@ -2028,9 +2035,9 @@ impl<'r, 's> Compiler<'r, 's> { self.control_flow_stack.pop_exit_target(); // Jump from the end of the catch block to the beginning of the finally block. - self.peer.create_br(finally_block); - self.peer.move_basic_block_after(finally_block); - self.peer.set_basic_block(finally_block); + self.bridge.create_br(finally_block); + self.bridge.move_basic_block_after(finally_block); + self.bridge.set_basic_block(finally_block); push_bb_name!(self, "finally"); } @@ -2043,12 +2050,12 @@ impl<'r, 's> Compiler<'r, 's> { // Jump from the end of the finally block to the beginning of the outer catch block if // there is an uncaught exception. Otherwise, jump to the beginning of the try-end block. - let is_normal = self.peer.create_is_flow_selector_normal(); - self.peer + let is_normal = self.bridge.create_is_flow_selector_normal(); + self.bridge .create_cond_br(is_normal, flow.end_block, parent_exit_block); - self.peer.move_basic_block_after(flow.end_block); - self.peer.set_basic_block(flow.end_block); + self.bridge.move_basic_block_after(flow.end_block); + self.bridge.set_basic_block(flow.end_block); } fn process_label_start(&mut self, label: Symbol, is_iteration_statement: bool) { @@ -2065,9 +2072,9 @@ impl<'r, 's> Compiler<'r, 's> { let start_block = self.create_basic_block("start"); let end_block = self.create_basic_block("end"); - self.peer.create_br(start_block); - self.peer.move_basic_block_after(end_block); - self.peer.set_basic_block(start_block); + self.bridge.create_br(start_block); + self.bridge.move_basic_block_after(end_block); + self.bridge.set_basic_block(start_block); self.control_flow_stack.push_exit_target(end_block, false); self.control_flow_stack.set_exit_label(label); @@ -2081,27 +2088,28 @@ impl<'r, 's> Compiler<'r, 's> { debug_assert!(self.pending_labels.is_empty()); } else { let end_block = self.control_flow_stack.pop_exit_target(); - self.peer.create_br(end_block); - self.peer.move_basic_block_after(end_block); - self.peer.set_basic_block(end_block); + self.bridge.create_br(end_block); + self.bridge.move_basic_block_after(end_block); + self.bridge.set_basic_block(end_block); } } fn process_continue(&mut self, label: Symbol) { let exit_id = self.control_flow_stack.exit_id_for_label(label); - self.peer.create_set_flow_selector_continue(exit_id.depth()); + self.bridge + .create_set_flow_selector_continue(exit_id.depth()); let block = self.control_flow_stack.exit_block(); - self.peer.create_br(block); + self.bridge.create_br(block); self.create_basic_block_for_deadcode(); } fn process_break(&mut self, label: Symbol) { let exit_id = self.control_flow_stack.exit_id_for_label(label); - self.peer.create_set_flow_selector_break(exit_id.depth()); + self.bridge.create_set_flow_selector_break(exit_id.depth()); let block = self.control_flow_stack.exit_block(); - self.peer.create_br(block); + self.bridge.create_br(block); self.create_basic_block_for_deadcode(); } @@ -2112,25 +2120,25 @@ impl<'r, 's> Compiler<'r, 's> { self.create_store_operand_to_retv(operand); } - self.peer.create_store_normal_status(); - self.peer.create_set_flow_selector_return(); + self.bridge.create_store_normal_status(); + self.bridge.create_set_flow_selector_return(); let next_block = self.control_flow_stack.cleanup_block(); - self.peer.create_br(next_block); + self.bridge.create_br(next_block); self.create_basic_block_for_deadcode(); } fn create_store_operand_to_retv(&mut self, operand: Operand) { match operand { - Operand::Undefined => self.peer.create_store_undefined_to_retv(), - Operand::Null => self.peer.create_store_null_to_retv(), - Operand::Boolean(value) => self.peer.create_store_boolean_to_retv(value), - Operand::Number(value) => self.peer.create_store_number_to_retv(value), - Operand::Closure(value) => self.peer.create_store_closure_to_retv(value), - Operand::Promise(value) => self.peer.create_store_promise_to_retv(value), - Operand::Any(value) => self.peer.create_store_value_to_retv(value), + Operand::Undefined => self.bridge.create_store_undefined_to_retv(), + Operand::Null => self.bridge.create_store_null_to_retv(), + Operand::Boolean(value) => self.bridge.create_store_boolean_to_retv(value), + Operand::Number(value) => self.bridge.create_store_number_to_retv(value), + Operand::Closure(value) => self.bridge.create_store_closure_to_retv(value), + Operand::Promise(value) => self.bridge.create_store_promise_to_retv(value), + Operand::Any(value) => self.bridge.create_store_value_to_retv(value), _ => unreachable!(), } } @@ -2138,32 +2146,32 @@ impl<'r, 's> Compiler<'r, 's> { fn process_throw(&mut self) { let (operand, _) = self.dereference(); self.create_store_operand_to_retv(operand); - self.peer.create_store_exception_status(); - self.peer.create_set_flow_selector_throw(); + self.bridge.create_store_exception_status(); + self.bridge.create_set_flow_selector_throw(); let next_block = self.control_flow_stack.exception_block(); - self.peer.create_br(next_block); - self.peer.move_basic_block_after(next_block); + self.bridge.create_br(next_block); + self.bridge.move_basic_block_after(next_block); self.create_basic_block_for_deadcode(); } fn process_environment(&mut self, num_locals: u16) { let flow = self.control_flow_stack.function_flow(); - let backup = self.peer.get_basic_block(); + let backup = self.bridge.get_basic_block(); // Local variables and captured variables living outer scopes are loaded here from the // `Coroutine` data passed via the `env` argument of the coroutine lambda function to be // generated by the compiler. - self.peer.set_basic_block(flow.init_block); - self.peer.create_set_captures_for_coroutine(); + self.bridge.set_basic_block(flow.init_block); + self.bridge.create_set_captures_for_coroutine(); for i in 0..num_locals { - let local = self.peer.create_get_local_ptr_from_coroutine(i); + let local = self.bridge.create_get_local_ptr_from_coroutine(i); self.locals.push(local); } - self.peer.set_basic_block(backup); + self.bridge.set_basic_block(backup); } fn process_jump_table(&mut self, num_states: u32) { @@ -2171,16 +2179,16 @@ impl<'r, 's> Compiler<'r, 's> { let initial_block = self.create_basic_block("co.initial"); let done_block = self.create_basic_block("co.done"); let inst = self - .peer + .bridge .create_switch_for_coroutine(done_block, num_states); - self.peer + self.bridge .create_add_state_for_coroutine(inst, 0, initial_block); - self.peer.set_basic_block(done_block); - self.peer + self.bridge.set_basic_block(done_block); + self.bridge .create_unreachable(c"the coroutine has already done"); - self.peer.set_basic_block(initial_block); + self.bridge.set_basic_block(initial_block); self.control_flow_stack .push_coroutine_flow(inst, done_block, num_states); @@ -2189,15 +2197,16 @@ impl<'r, 's> Compiler<'r, 's> { fn process_await(&mut self, next_state: u32) { self.resolve_promise(); self.save_operands_to_scratch_buffer(); - self.peer.create_set_coroutine_state(next_state); - self.peer.create_suspend(); + self.bridge.create_set_coroutine_state(next_state); + self.bridge.create_suspend(); // resume block let block = self.create_basic_block("resume"); let inst = self.control_flow_stack.coroutine_switch_inst(); let state = self.control_flow_stack.coroutine_next_state(); - self.peer.create_add_state_for_coroutine(inst, state, block); - self.peer.set_basic_block(block); + self.bridge + .create_add_state_for_coroutine(inst, state, block); + self.bridge.set_basic_block(block); self.load_operands_from_scratch_buffer(); @@ -2205,70 +2214,71 @@ impl<'r, 's> Compiler<'r, 's> { let result_block = self.create_basic_block("result"); // if ##error.has_value() - let error = self.peer.create_get_argument_value_ptr(2); // ##error - let has_error = self.peer.create_has_value(error); - self.peer + let error = self.bridge.create_get_argument_value_ptr(2); // ##error + let has_error = self.bridge.create_has_value(error); + self.bridge .create_cond_br(has_error, has_error_block, result_block); { // throw ##error; - self.peer.set_basic_block(has_error_block); + self.bridge.set_basic_block(has_error_block); self.operand_stack.push(Operand::Any(error)); self.process_throw(); - self.peer.create_br(result_block); + self.bridge.create_br(result_block); } - self.peer.set_basic_block(result_block); - let result = self.peer.create_get_argument_value_ptr(1); // ##result + self.bridge.set_basic_block(result_block); + let result = self.bridge.create_get_argument_value_ptr(1); // ##result self.operand_stack.push(Operand::Any(result)); } fn resolve_promise(&mut self) { - let promise = self.peer.create_get_argument_value_ptr(0); // ##promise - let promise = self.peer.create_load_promise_from_value(promise); + let promise = self.bridge.create_get_argument_value_ptr(0); // ##promise + let promise = self.bridge.create_load_promise_from_value(promise); let (operand, _) = self.dereference(); match operand { Operand::Undefined => { - let result = self.peer.create_undefined_to_any(); - self.peer.create_emit_promise_resolved(promise, result); + let result = self.bridge.create_undefined_to_any(); + self.bridge.create_emit_promise_resolved(promise, result); } Operand::Null => { - let result = self.peer.create_null_to_any(); - self.peer.create_emit_promise_resolved(promise, result); + let result = self.bridge.create_null_to_any(); + self.bridge.create_emit_promise_resolved(promise, result); } Operand::Boolean(value) => { - let result = self.peer.create_boolean_to_any(value); - self.peer.create_emit_promise_resolved(promise, result); + let result = self.bridge.create_boolean_to_any(value); + self.bridge.create_emit_promise_resolved(promise, result); } Operand::Number(value) => { - let result = self.peer.create_number_to_any(value); - self.peer.create_emit_promise_resolved(promise, result); + let result = self.bridge.create_number_to_any(value); + self.bridge.create_emit_promise_resolved(promise, result); } Operand::Closure(value) => { - let result = self.peer.create_closure_to_any(value); - self.peer.create_emit_promise_resolved(promise, result); + let result = self.bridge.create_closure_to_any(value); + self.bridge.create_emit_promise_resolved(promise, result); } Operand::Promise(value) => { - self.peer.create_await_promise(value, promise); + self.bridge.create_await_promise(value, promise); } Operand::Any(value) => { let then_block = self.create_basic_block("is_promise.then"); let else_block = self.create_basic_block("is_promise.else"); let block = self.create_basic_block("block"); // if value.is_promise() - let is_promise = self.peer.create_is_promise(value); - self.peer.create_cond_br(is_promise, then_block, else_block); + let is_promise = self.bridge.create_is_promise(value); + self.bridge + .create_cond_br(is_promise, then_block, else_block); // { - self.peer.set_basic_block(then_block); - let target = self.peer.create_load_promise_from_value(value); - self.peer.create_await_promise(target, promise); - self.peer.create_br(block); + self.bridge.set_basic_block(then_block); + let target = self.bridge.create_load_promise_from_value(value); + self.bridge.create_await_promise(target, promise); + self.bridge.create_br(block); // } else { - self.peer.set_basic_block(else_block); - self.peer.create_emit_promise_resolved(promise, value); - self.peer.create_br(block); + self.bridge.set_basic_block(else_block); + self.bridge.create_emit_promise_resolved(promise, value); + self.bridge.create_br(block); // } - self.peer.set_basic_block(block); + self.bridge.set_basic_block(block); } _ => unreachable!("{operand:?}"), } @@ -2290,28 +2300,28 @@ impl<'r, 's> Compiler<'r, 's> { Operand::Undefined => (), Operand::Null => (), Operand::Boolean(value) => { - self.peer + self.bridge .create_write_boolean_to_scratch_buffer(offset, *value); offset += VALUE_HOLDER_SIZE; } Operand::Number(value) => { - self.peer + self.bridge .create_write_number_to_scratch_buffer(offset, *value); offset += VALUE_HOLDER_SIZE; } Operand::Closure(value) => { // TODO(issue#237): GcCellRef - self.peer + self.bridge .create_write_closure_to_scratch_buffer(offset, *value); offset += VALUE_HOLDER_SIZE; } Operand::Promise(value) => { - self.peer + self.bridge .create_write_promise_to_scratch_buffer(offset, *value); offset += VALUE_HOLDER_SIZE; } Operand::Any(value) => { - self.peer + self.bridge .create_write_value_to_scratch_buffer(offset, *value); offset += VALUE_SIZE; } @@ -2332,24 +2342,24 @@ impl<'r, 's> Compiler<'r, 's> { Operand::Undefined => (), Operand::Null => (), Operand::Boolean(ref mut value) => { - *value = self.peer.create_read_boolean_from_scratch_buffer(offset); + *value = self.bridge.create_read_boolean_from_scratch_buffer(offset); offset += VALUE_HOLDER_SIZE; } Operand::Number(ref mut value) => { - *value = self.peer.create_read_number_from_scratch_buffer(offset); + *value = self.bridge.create_read_number_from_scratch_buffer(offset); offset += VALUE_HOLDER_SIZE; } Operand::Closure(ref mut value) => { // TODO(issue#237): GcCellRef - *value = self.peer.create_read_closure_from_scratch_buffer(offset); + *value = self.bridge.create_read_closure_from_scratch_buffer(offset); offset += VALUE_HOLDER_SIZE; } Operand::Promise(ref mut value) => { - *value = self.peer.create_read_promise_from_scratch_buffer(offset); + *value = self.bridge.create_read_promise_from_scratch_buffer(offset); offset += VALUE_HOLDER_SIZE; } Operand::Any(ref mut value) => { - *value = self.peer.create_read_value_from_scratch_buffer(offset); + *value = self.bridge.create_read_value_from_scratch_buffer(offset); offset += VALUE_SIZE; } Operand::Reference(..) => (), @@ -2360,7 +2370,7 @@ impl<'r, 's> Compiler<'r, 's> { fn process_resume(&mut self) { let promise = self.pop_promise(); - self.peer.create_resume(promise); + self.bridge.create_resume(promise); } fn pop_coroutine(&mut self) -> CoroutineIr { @@ -2391,13 +2401,13 @@ impl<'r, 's> Compiler<'r, 's> { } fn process_debugger(&mut self) { - self.peer.create_debugger(); + self.bridge.create_debugger(); } fn create_basic_block(&mut self, name: &str) -> BasicBlock { push_bb_name!(self, name); let (name, name_len) = bb_name!(self); - let block = self.peer.create_basic_block(name, name_len); + let block = self.bridge.create_basic_block(name, name_len); pop_bb_name!(self); block } @@ -2417,7 +2427,7 @@ impl<'r, 's> Compiler<'r, 's> { // At this point, we don't know whether this is a common method or not... fn create_basic_block_for_deadcode(&mut self) { let block = self.create_basic_block("deadcode"); - self.peer.set_basic_block(block); + self.bridge.set_basic_block(block); } } diff --git a/libs/jsruntime/src/llvmir/compiler/peer.cc b/libs/jsruntime/src/llvmir/compiler/peer.cc new file mode 100644 index 00000000..449955d8 --- /dev/null +++ b/libs/jsruntime/src/llvmir/compiler/peer.cc @@ -0,0 +1,764 @@ +#include +#include + +#include + +#include "../bridge.hh" +#include "impl.hh" + +#define IMPL(peer) reinterpret_cast(peer) +#define PEER(impl) reinterpret_cast(impl) + +#define LLVM_BB(bb) (reinterpret_cast(bb)) +#define PEER_BB(bb) (reinterpret_cast(bb)) + +#define LLVM_LAMBDA(lambda) (reinterpret_cast(lambda)) +#define PEER_LAMBDA(lambda) (reinterpret_cast(lambda)) + +#define LLVM_VALUE(value) (reinterpret_cast(value)) +#define PEER_BOOLEAN(value) (reinterpret_cast(value)) +#define PEER_NUMBER(value) (reinterpret_cast(value)) +#define PEER_CLOSURE(value) (reinterpret_cast(value)) +#define PEER_COROUTINE(value) (reinterpret_cast(value)) +#define PEER_PROMISE(value) (reinterpret_cast(value)) +#define PEER_VALUE(value) (reinterpret_cast(value)) +#define PEER_ARGV(value) (reinterpret_cast(value)) +#define PEER_STATUS(value) (reinterpret_cast(value)) +#define PEER_CAPTURE(value) (reinterpret_cast(value)) + +#define LLVM_SWITCH(inst) (reinterpret_cast(inst)) +#define PEER_SWITCH(inst) (reinterpret_cast(inst)) + +// compilation + +CompilerPeer compiler_peer_new() { + return PEER(new Compiler()); +} + +void compiler_peer_delete(CompilerPeer peer) { + delete IMPL(peer); +} + +void compiler_peer_start(CompilerPeer peer, bool enable_labels) { + if (enable_labels) { + IMPL(peer)->EnableLabels(); + } +} + +ModulePeer compiler_peer_end(CompilerPeer peer) { + return reinterpret_cast(IMPL(peer)->TakeModule()); +} + +void compiler_peer_set_data_layout(CompilerPeer peer, const char* data_layout) { + IMPL(peer)->SetDataLayout(data_layout); +} + +void compiler_peer_set_target_triple(CompilerPeer peer, const char* triple) { + IMPL(peer)->SetTargetTriple(triple); +} + +void compiler_peer_start_function(CompilerPeer peer, uint32_t func_id) { + IMPL(peer)->StartFunction(func_id); +} + +void compiler_peer_end_function(CompilerPeer peer, bool optimize) { + IMPL(peer)->EndFunction(optimize); +} + +void compiler_peer_set_locals_block(CompilerPeer peer, BasicBlockPtr block) { + IMPL(peer)->SetLocalsBlock(LLVM_BB(block)); +} + +LambdaIrPtr compiler_peer_get_function(CompilerPeer peer, uint32_t func_id) { + return PEER_LAMBDA(IMPL(peer)->GetFunction(func_id)); +} + +// basic block + +BasicBlockPtr compiler_peer_create_basic_block(CompilerPeer peer, + const char* name, + size_t name_len) { + return PEER_BB(IMPL(peer)->CreateBasicBlock(name, name_len)); +} + +BasicBlockPtr compiler_peer_get_basic_block(const CompilerPeer peer) { + return PEER_BB(IMPL(peer)->GetBasicBlock()); +} + +void compiler_peer_set_basic_block(CompilerPeer peer, BasicBlockPtr block) { + IMPL(peer)->SetBasicBlock(LLVM_BB(block)); +} + +void compiler_peer_move_basic_block_after(CompilerPeer peer, BasicBlockPtr block) { + IMPL(peer)->MoveBasicBlockAfter(LLVM_BB(block)); +} + +bool compiler_peer_is_basic_block_terminated(CompilerPeer peer, BasicBlockPtr block) { + return IMPL(peer)->IsBasicBlockTerminated(LLVM_BB(block)); +} + +// jump + +void compiler_peer_create_br(CompilerPeer peer, BasicBlockPtr block) { + IMPL(peer)->CreateBr(LLVM_BB(block)); +} + +void compiler_peer_create_cond_br(CompilerPeer peer, + BooleanIrPtr cond, + BasicBlockPtr then_block, + BasicBlockPtr else_block) { + IMPL(peer)->CreateCondBr(LLVM_VALUE(cond), LLVM_BB(then_block), LLVM_BB(else_block)); +} + +// undefined + +BooleanIrPtr compiler_peer_create_is_undefined(CompilerPeer peer, ValueIrPtr value) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsUndefined(LLVM_VALUE(value))); +} + +// null + +BooleanIrPtr compiler_peer_create_is_null(CompilerPeer peer, ValueIrPtr value) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsNull(LLVM_VALUE(value))); +} + +BooleanIrPtr compiler_peer_create_is_non_nullish(CompilerPeer peer, ValueIrPtr value) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsNonNullish(LLVM_VALUE(value))); +} + +// boolean + +BooleanIrPtr compiler_peer_create_is_boolean(CompilerPeer peer, ValueIrPtr value) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsBoolean(LLVM_VALUE(value))); +} + +BooleanIrPtr compiler_peer_create_is_same_boolean(CompilerPeer peer, + BooleanIrPtr a, + BooleanIrPtr b) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsSameBoolean(LLVM_VALUE(a), LLVM_VALUE(b))); +} + +BooleanIrPtr compiler_peer_create_number_to_boolean(CompilerPeer peer, NumberIrPtr value) { + return PEER_BOOLEAN(IMPL(peer)->CreateNumberToBoolean(LLVM_VALUE(value))); +} + +BooleanIrPtr compiler_peer_create_to_boolean(CompilerPeer peer, ValueIrPtr value) { + return PEER_BOOLEAN(IMPL(peer)->CreateToBoolean(LLVM_VALUE(value))); +} + +BooleanIrPtr compiler_peer_get_boolean(CompilerPeer peer, bool value) { + return PEER_BOOLEAN(IMPL(peer)->GetBoolean(value)); +} + +BooleanIrPtr compiler_peer_create_logical_not(CompilerPeer peer, BooleanIrPtr boolean) { + return PEER_BOOLEAN(IMPL(peer)->CreateLogicalNot(LLVM_VALUE(boolean))); +} + +BooleanIrPtr compiler_peer_create_boolean_phi(CompilerPeer peer, + BooleanIrPtr then_value, + BasicBlockPtr then_block, + BooleanIrPtr else_value, + BasicBlockPtr else_block) { + return PEER_BOOLEAN(IMPL(peer)->CreateBooleanPhi(LLVM_VALUE(then_value), LLVM_BB(then_block), + LLVM_VALUE(else_value), LLVM_BB(else_block))); +} + +// number + +BooleanIrPtr compiler_peer_create_is_number(CompilerPeer peer, ValueIrPtr value) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsNumber(LLVM_VALUE(value))); +} + +BooleanIrPtr compiler_peer_create_is_same_number(CompilerPeer peer, NumberIrPtr a, NumberIrPtr b) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsSameNumber(LLVM_VALUE(a), LLVM_VALUE(b))); +} + +NumberIrPtr compiler_peer_create_boolean_to_number(CompilerPeer peer, BooleanIrPtr value) { + return PEER_NUMBER(IMPL(peer)->CreateUIToFP(LLVM_VALUE(value))); +} + +NumberIrPtr compiler_peer_to_numeric(CompilerPeer peer, ValueIrPtr value) { + return PEER_NUMBER(IMPL(peer)->ToNumeric(LLVM_VALUE(value))); +} + +NumberIrPtr compiler_peer_get_nan(CompilerPeer peer) { + return PEER_NUMBER(IMPL(peer)->GetNan()); +} + +NumberIrPtr compiler_peer_get_zero(CompilerPeer peer) { + return PEER_NUMBER(IMPL(peer)->GetZero()); +} + +NumberIrPtr compiler_peer_get_number(CompilerPeer peer, double value) { + return PEER_NUMBER(IMPL(peer)->GetNumber(value)); +} + +NumberIrPtr compiler_peer_create_bitwise_not(CompilerPeer peer, NumberIrPtr value) { + return PEER_NUMBER(IMPL(peer)->CreateBitwiseNot(LLVM_VALUE(value))); +} + +NumberIrPtr compiler_peer_create_fneg(CompilerPeer peer, NumberIrPtr value) { + return PEER_NUMBER(IMPL(peer)->CreateFNeg(LLVM_VALUE(value))); +} + +NumberIrPtr compiler_peer_create_fmul(CompilerPeer peer, NumberIrPtr lhs, NumberIrPtr rhs) { + return PEER_NUMBER(IMPL(peer)->CreateFMul(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +NumberIrPtr compiler_peer_create_fdiv(CompilerPeer peer, NumberIrPtr lhs, NumberIrPtr rhs) { + return PEER_NUMBER(IMPL(peer)->CreateFDiv(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +NumberIrPtr compiler_peer_create_frem(CompilerPeer peer, NumberIrPtr lhs, NumberIrPtr rhs) { + return PEER_NUMBER(IMPL(peer)->CreateFRem(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +NumberIrPtr compiler_peer_create_fadd(CompilerPeer peer, NumberIrPtr lhs, NumberIrPtr rhs) { + return PEER_NUMBER(IMPL(peer)->CreateFAdd(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +NumberIrPtr compiler_peer_create_fsub(CompilerPeer peer, NumberIrPtr lhs, NumberIrPtr rhs) { + return PEER_NUMBER(IMPL(peer)->CreateFSub(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +NumberIrPtr compiler_peer_create_left_shift(CompilerPeer peer, NumberIrPtr lhs, NumberIrPtr rhs) { + return PEER_NUMBER(IMPL(peer)->CreateLeftShift(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +NumberIrPtr compiler_peer_create_signed_right_shift(CompilerPeer peer, + NumberIrPtr lhs, + NumberIrPtr rhs) { + return PEER_NUMBER(IMPL(peer)->CreateSignedRightShift(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +NumberIrPtr compiler_peer_create_unsigned_right_shift(CompilerPeer peer, + NumberIrPtr lhs, + NumberIrPtr rhs) { + return PEER_NUMBER(IMPL(peer)->CreateUnsignedRightShift(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +NumberIrPtr compiler_peer_create_bitwise_and(CompilerPeer peer, NumberIrPtr lhs, NumberIrPtr rhs) { + return PEER_NUMBER(IMPL(peer)->CreateBitwiseAnd(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +NumberIrPtr compiler_peer_create_bitwise_xor(CompilerPeer peer, NumberIrPtr lhs, NumberIrPtr rhs) { + return PEER_NUMBER(IMPL(peer)->CreateBitwiseXor(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +NumberIrPtr compiler_peer_create_bitwise_or(CompilerPeer peer, NumberIrPtr lhs, NumberIrPtr rhs) { + return PEER_NUMBER(IMPL(peer)->CreateBitwiseOr(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +BooleanIrPtr compiler_peer_create_less_than(CompilerPeer peer, NumberIrPtr lhs, NumberIrPtr rhs) { + return PEER_BOOLEAN(IMPL(peer)->CreateLessThan(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +BooleanIrPtr compiler_peer_create_greater_than(CompilerPeer peer, + NumberIrPtr lhs, + NumberIrPtr rhs) { + return PEER_BOOLEAN(IMPL(peer)->CreateGreaterThan(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +BooleanIrPtr compiler_peer_create_less_than_or_equal(CompilerPeer peer, + NumberIrPtr lhs, + NumberIrPtr rhs) { + return PEER_BOOLEAN(IMPL(peer)->CreateLessThanOrEqual(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +BooleanIrPtr compiler_peer_create_greater_than_or_equal(CompilerPeer peer, + NumberIrPtr lhs, + NumberIrPtr rhs) { + return PEER_BOOLEAN(IMPL(peer)->CreateGreaterThanOrEqual(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +NumberIrPtr compiler_peer_create_number_phi(CompilerPeer peer, + NumberIrPtr then_value, + BasicBlockPtr then_block, + NumberIrPtr else_value, + BasicBlockPtr else_block) { + return PEER_NUMBER(IMPL(peer)->CreateNumberPhi(LLVM_VALUE(then_value), LLVM_BB(then_block), + LLVM_VALUE(else_value), LLVM_BB(else_block))); +} + +// closure + +BooleanIrPtr compiler_peer_create_is_closure(CompilerPeer peer, ValueIrPtr value) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsClosure(LLVM_VALUE(value))); +} + +BooleanIrPtr compiler_peer_create_is_same_closure(CompilerPeer peer, + ClosureIrPtr a, + ClosureIrPtr b) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsSameClosure(LLVM_VALUE(a), LLVM_VALUE(b))); +} + +ClosureIrPtr compiler_peer_get_closure_nullptr(CompilerPeer peer) { + return PEER_CLOSURE(IMPL(peer)->GetNullptr()); +} + +ClosureIrPtr compiler_peer_create_closure(CompilerPeer peer, + LambdaIrPtr lambda, + uint16_t num_captures) { + return PEER_CLOSURE(IMPL(peer)->CreateClosure(LLVM_LAMBDA(lambda), num_captures)); +} + +void compiler_peer_create_store_capture_to_closure(CompilerPeer peer, + CaptureIrPtr capture, + ClosureIrPtr closure, + uint16_t index) { + IMPL(peer)->CreateStoreCapturePtrToClosure(LLVM_VALUE(capture), LLVM_VALUE(closure), index); +} + +StatusIrPtr compiler_peer_create_call_on_closure(CompilerPeer peer, + ClosureIrPtr closure, + uint16_t argc, + ArgvIrPtr argv, + ValueIrPtr retv) { + return PEER_STATUS(IMPL(peer)->CreateCallOnClosure(LLVM_VALUE(closure), argc, LLVM_VALUE(argv), + LLVM_VALUE(retv))); +} + +ClosureIrPtr compiler_peer_create_closure_phi(CompilerPeer peer, + ClosureIrPtr then_value, + BasicBlockPtr then_block, + ClosureIrPtr else_value, + BasicBlockPtr else_block) { + return PEER_CLOSURE(IMPL(peer)->CreateClosurePhi(LLVM_VALUE(then_value), LLVM_BB(then_block), + LLVM_VALUE(else_value), LLVM_BB(else_block))); +} + +// promise + +BooleanIrPtr compiler_peer_create_is_promise(CompilerPeer peer, ValueIrPtr value) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsPromise(LLVM_VALUE(value))); +} + +BooleanIrPtr compiler_peer_create_is_same_promise(CompilerPeer peer, + PromiseIrPtr a, + PromiseIrPtr b) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsSamePromise(LLVM_VALUE(a), LLVM_VALUE(b))); +} + +PromiseIrPtr compiler_peer_create_register_promise(CompilerPeer peer, CoroutineIrPtr coroutine) { + return PEER_PROMISE(IMPL(peer)->CreateRegisterPromise(LLVM_VALUE(coroutine))); +} + +void compiler_peer_create_await_promise(CompilerPeer peer, + PromiseIrPtr promise, + PromiseIrPtr awaiting) { + IMPL(peer)->CreateAwaitPromise(LLVM_VALUE(promise), LLVM_VALUE(awaiting)); +} + +void compiler_peer_create_resume(CompilerPeer peer, PromiseIrPtr promise) { + IMPL(peer)->CreateResume(LLVM_VALUE(promise)); +} + +void compiler_peer_create_emit_promise_resolved(CompilerPeer peer, + PromiseIrPtr promise, + ValueIrPtr result) { + IMPL(peer)->CreateEmitPromiseResolved(LLVM_VALUE(promise), LLVM_VALUE(result)); +} + +// value + +BooleanIrPtr compiler_peer_create_has_value(CompilerPeer peer, ValueIrPtr value) { + return PEER_BOOLEAN(IMPL(peer)->CreateHasValue(LLVM_VALUE(value))); +} + +BooleanIrPtr compiler_peer_create_is_loosely_equal(CompilerPeer peer, + ValueIrPtr lhs, + ValueIrPtr rhs) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsLooselyEqual(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +BooleanIrPtr compiler_peer_create_is_strictly_equal(CompilerPeer peer, + ValueIrPtr lhs, + ValueIrPtr rhs) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsStrictlyEqual(LLVM_VALUE(lhs), LLVM_VALUE(rhs))); +} + +BooleanIrPtr compiler_peer_create_is_same_boolean_value(CompilerPeer peer, + ValueIrPtr value, + BooleanIrPtr boolean) { + return PEER_BOOLEAN( + IMPL(peer)->CreateIsSameBooleanValue(LLVM_VALUE(value), LLVM_VALUE(boolean))); +} + +BooleanIrPtr compiler_peer_create_is_same_number_value(CompilerPeer peer, + ValueIrPtr value, + NumberIrPtr number) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsSameNumberValue(LLVM_VALUE(value), LLVM_VALUE(number))); +} + +BooleanIrPtr compiler_peer_create_is_same_closure_value(CompilerPeer peer, + ValueIrPtr value, + ClosureIrPtr closure) { + return PEER_BOOLEAN( + IMPL(peer)->CreateIsSameClosureValue(LLVM_VALUE(value), LLVM_VALUE(closure))); +} + +BooleanIrPtr compiler_peer_create_is_same_promise_value(CompilerPeer peer, + ValueIrPtr value, + PromiseIrPtr promise) { + return PEER_BOOLEAN( + IMPL(peer)->CreateIsSamePromiseValue(LLVM_VALUE(value), LLVM_VALUE(promise))); +} + +ValueIrPtr compiler_peer_create_undefined_to_any(CompilerPeer peer) { + return PEER_VALUE(IMPL(peer)->CreateUndefinedToAny()); +} + +ValueIrPtr compiler_peer_create_null_to_any(CompilerPeer peer) { + return PEER_VALUE(IMPL(peer)->CreateNullToAny()); +} + +ValueIrPtr compiler_peer_create_boolean_to_any(CompilerPeer peer, BooleanIrPtr boolean) { + return PEER_VALUE(IMPL(peer)->CreateBooleanToAny(LLVM_VALUE(boolean))); +} + +ValueIrPtr compiler_peer_create_number_to_any(CompilerPeer peer, NumberIrPtr value) { + return PEER_VALUE(IMPL(peer)->CreateNumberToAny(LLVM_VALUE(value))); +} + +ValueIrPtr compiler_peer_create_closure_to_any(CompilerPeer peer, ClosureIrPtr value) { + return PEER_VALUE(IMPL(peer)->CreateClosureToAny(LLVM_VALUE(value))); +} + +ValueIrPtr compiler_peer_create_value_phi(CompilerPeer peer, + ValueIrPtr then_value, + BasicBlockPtr then_block, + ValueIrPtr else_value, + BasicBlockPtr else_block) { + return PEER_VALUE(IMPL(peer)->CreateValuePhi(LLVM_VALUE(then_value), LLVM_BB(then_block), + LLVM_VALUE(else_value), LLVM_BB(else_block))); +} + +ValueIrPtr compiler_peer_create_local_value(CompilerPeer peer, uint16_t index) { + return PEER_VALUE(IMPL(peer)->CreateLocalValue(index)); +} + +void compiler_peer_create_store_none_to_value(CompilerPeer peer, ValueIrPtr dest) { + IMPL(peer)->CreateStoreNoneToValue(LLVM_VALUE(dest)); +} + +void compiler_peer_create_store_undefined_to_value(CompilerPeer peer, ValueIrPtr dest) { + IMPL(peer)->CreateStoreUndefinedToValue(LLVM_VALUE(dest)); +} + +void compiler_peer_create_store_null_to_value(CompilerPeer peer, ValueIrPtr dest) { + IMPL(peer)->CreateStoreNullToValue(LLVM_VALUE(dest)); +} + +void compiler_peer_create_store_boolean_to_value(CompilerPeer peer, + BooleanIrPtr value, + ValueIrPtr dest) { + IMPL(peer)->CreateStoreBooleanToValue(LLVM_VALUE(value), LLVM_VALUE(dest)); +} + +void compiler_peer_create_store_number_to_value(CompilerPeer peer, + NumberIrPtr value, + ValueIrPtr dest) { + IMPL(peer)->CreateStoreNumberToValue(LLVM_VALUE(value), LLVM_VALUE(dest)); +} + +void compiler_peer_create_store_closure_to_value(CompilerPeer peer, + ClosureIrPtr value, + ValueIrPtr dest) { + IMPL(peer)->CreateStoreClosureToValue(LLVM_VALUE(value), LLVM_VALUE(dest)); +} + +void compiler_peer_create_store_promise_to_value(CompilerPeer peer, + PromiseIrPtr value, + ValueIrPtr dest) { + IMPL(peer)->CreateStorePromiseToValue(LLVM_VALUE(value), LLVM_VALUE(dest)); +} + +void compiler_peer_create_store_value_to_value(CompilerPeer peer, + ValueIrPtr value, + ValueIrPtr dest) { + IMPL(peer)->CreateStoreValueToValue(LLVM_VALUE(value), LLVM_VALUE(dest)); +} + +ClosureIrPtr compiler_peer_create_load_closure_from_value(CompilerPeer peer, ValueIrPtr value) { + return PEER_CLOSURE(IMPL(peer)->CreateLoadClosureFromValue(LLVM_VALUE(value))); +} + +PromiseIrPtr compiler_peer_create_load_promise_from_value(CompilerPeer peer, ValueIrPtr value) { + return PEER_PROMISE(IMPL(peer)->CreateLoadPromiseFromValue(LLVM_VALUE(value))); +} + +// argv + +ArgvIrPtr compiler_peer_get_argv_nullptr(CompilerPeer peer) { + return PEER_ARGV(IMPL(peer)->GetNullptr()); +} + +ArgvIrPtr compiler_peer_create_argv(CompilerPeer peer, uint16_t argc) { + return PEER_ARGV(IMPL(peer)->CreateArgv(argc)); +} + +ValueIrPtr compiler_peer_create_get_arg_in_argv(CompilerPeer peer, + ArgvIrPtr argv, + uint16_t index) { + return PEER_VALUE(IMPL(peer)->CreateGetArgInArgv(LLVM_VALUE(argv), index)); +} + +ValueIrPtr compiler_peer_create_get_argument_value_ptr(CompilerPeer peer, uint16_t index) { + return PEER_VALUE(IMPL(peer)->CreateGetArgumentValuePtr(index)); +} + +// retv + +ValueIrPtr compiler_peer_create_retv(CompilerPeer peer) { + return PEER_VALUE(IMPL(peer)->CreateRetv()); +} + +void compiler_peer_create_store_undefined_to_retv(CompilerPeer peer) { + IMPL(peer)->CreateStoreUndefinedToRetv(); +} + +void compiler_peer_create_store_null_to_retv(CompilerPeer peer) { + IMPL(peer)->CreateStoreNullToRetv(); +} + +void compiler_peer_create_store_boolean_to_retv(CompilerPeer peer, BooleanIrPtr value) { + IMPL(peer)->CreateStoreBooleanToRetv(LLVM_VALUE(value)); +} + +void compiler_peer_create_store_number_to_retv(CompilerPeer peer, NumberIrPtr value) { + IMPL(peer)->CreateStoreNumberToRetv(LLVM_VALUE(value)); +} + +void compiler_peer_create_store_closure_to_retv(CompilerPeer peer, ClosureIrPtr value) { + IMPL(peer)->CreateStoreClosureToRetv(LLVM_VALUE(value)); +} + +void compiler_peer_create_store_promise_to_retv(CompilerPeer peer, PromiseIrPtr value) { + IMPL(peer)->CreateStorePromiseToRetv(LLVM_VALUE(value)); +} + +void compiler_peer_create_store_value_to_retv(CompilerPeer peer, ValueIrPtr value) { + IMPL(peer)->CreateStoreValueToRetv(LLVM_VALUE(value)); +} + +ValueIrPtr compiler_peer_get_exception(CompilerPeer peer) { + return PEER_VALUE(IMPL(peer)->GetException()); +} + +// status + +void compiler_peer_create_alloc_status(CompilerPeer peer) { + IMPL(peer)->CreateAllocStatus(); +} + +void compiler_peer_create_store_normal_status(CompilerPeer peer) { + IMPL(peer)->CreateStoreNormalStatus(); +} + +void compiler_peer_create_store_exception_status(CompilerPeer peer) { + IMPL(peer)->CreateStoreExceptionStatus(); +} + +BooleanIrPtr compiler_peer_create_is_exception_status(CompilerPeer peer, StatusIrPtr status) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsExceptionStatus(LLVM_VALUE(status))); +} + +// flow selector + +void compiler_peer_create_alloc_flow_selector(CompilerPeer peer) { + IMPL(peer)->CreateAllocFlowSelector(); +} + +void compiler_peer_create_set_flow_selector_normal(CompilerPeer peer) { + IMPL(peer)->CreateSetFlowSelectorNormal(); +} + +void compiler_peer_create_set_flow_selector_return(CompilerPeer peer) { + IMPL(peer)->CreateSetFlowSelectorReturn(); +} + +void compiler_peer_create_set_flow_selector_throw(CompilerPeer peer) { + IMPL(peer)->CreateSetFlowSelectorThrow(); +} + +void compiler_peer_create_set_flow_selector_break(CompilerPeer peer, uint32_t depth) { + IMPL(peer)->CreateSetFlowSelectorBreak(depth); +} + +void compiler_peer_create_set_flow_selector_continue(CompilerPeer peer, uint32_t depth) { + IMPL(peer)->CreateSetFlowSelectorContinue(depth); +} + +BooleanIrPtr compiler_peer_create_is_flow_selector_normal(CompilerPeer peer) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsFlowSelectorNormal()); +} + +BooleanIrPtr compiler_peer_create_is_flow_selector_normal_or_continue(CompilerPeer peer, + uint32_t depth) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsFlowSelectorNormalOrContinue(depth)); +} + +BooleanIrPtr compiler_peer_create_is_flow_selector_break_or_continue(CompilerPeer peer, + uint32_t depth) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsFlowSelectorBreakOrContinue(depth)); +} + +BooleanIrPtr compiler_peer_create_is_flow_selector_break(CompilerPeer peer, uint32_t depth) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsFlowSelectorBreak(depth)); +} + +// capture + +CaptureIrPtr compiler_peer_create_capture(CompilerPeer peer, ValueIrPtr value) { + return PEER_CAPTURE(IMPL(peer)->CreateCapture(LLVM_VALUE(value))); +} + +ValueIrPtr compiler_peer_create_get_capture_value_ptr(CompilerPeer peer, uint16_t index) { + return PEER_VALUE(IMPL(peer)->CreateGetCaptureValuePtr(index)); +} + +void compiler_peer_create_escape_value(CompilerPeer peer, CaptureIrPtr capture, ValueIrPtr value) { + IMPL(peer)->CreateEscapeValue(LLVM_VALUE(capture), LLVM_VALUE(value)); +} + +CaptureIrPtr compiler_peer_create_load_capture(CompilerPeer peer, uint16_t index) { + return PEER_CAPTURE(IMPL(peer)->CreateLoadCapture(index)); +} + +// coroutine + +CoroutineIrPtr compiler_peer_create_coroutine(CompilerPeer peer, + ClosureIrPtr closure, + uint16_t num_locals, + uint16_t scratch_buffer_len) { + return PEER_COROUTINE( + IMPL(peer)->CreateCoroutine(LLVM_VALUE(closure), num_locals, scratch_buffer_len)); +} + +SwitchIrPtr compiler_peer_create_switch_for_coroutine(CompilerPeer peer, + BasicBlockPtr block, + uint32_t num_states) { + return PEER_SWITCH(IMPL(peer)->CreateSwitchForCoroutine(LLVM_BB(block), num_states)); +} + +void compiler_peer_create_add_state_for_coroutine(CompilerPeer peer, + SwitchIrPtr inst, + uint32_t state, + BasicBlockPtr block) { + IMPL(peer)->CreateAddStateForCoroutine(LLVM_SWITCH(inst), state, LLVM_BB(block)); +} + +void compiler_peer_create_suspend(CompilerPeer peer) { + IMPL(peer)->CreateSuspend(); +} + +void compiler_peer_create_set_coroutine_state(CompilerPeer peer, uint32_t state) { + IMPL(peer)->CreateSetCoroutineState(state); +} + +void compiler_peer_create_set_captures_for_coroutine(CompilerPeer peer) { + IMPL(peer)->CreateSetCapturesForCoroutine(); +} + +ValueIrPtr compiler_peer_create_get_local_ptr_from_coroutine(CompilerPeer peer, uint16_t index) { + return PEER_VALUE(IMPL(peer)->CreateGetLocalPtrFromCoroutine(index)); +} + +void compiler_peer_create_write_boolean_to_scratch_buffer(CompilerPeer peer, + uint32_t offset, + BooleanIrPtr value) { + IMPL(peer)->CreateWriteBooleanToScratchBuffer(offset, LLVM_VALUE(value)); +} + +BooleanIrPtr compiler_peer_create_read_boolean_from_scratch_buffer(CompilerPeer peer, + uint32_t offset) { + return PEER_BOOLEAN(IMPL(peer)->CreateReadBooleanFromScratchBuffer(offset)); +} + +void compiler_peer_create_write_number_to_scratch_buffer(CompilerPeer peer, + uint32_t offset, + NumberIrPtr value) { + IMPL(peer)->CreateWriteNumberToScratchBuffer(offset, LLVM_VALUE(value)); +} + +NumberIrPtr compiler_peer_create_read_number_from_scratch_buffer(CompilerPeer peer, + uint32_t offset) { + return PEER_NUMBER(IMPL(peer)->CreateReadNumberFromScratchBuffer(offset)); +} + +void compiler_peer_create_write_closure_to_scratch_buffer(CompilerPeer peer, + uint32_t offset, + ClosureIrPtr value) { + IMPL(peer)->CreateWriteClosureToScratchBuffer(offset, LLVM_VALUE(value)); +} + +ClosureIrPtr compiler_peer_create_read_closure_from_scratch_buffer(CompilerPeer peer, + uint32_t offset) { + return PEER_CLOSURE(IMPL(peer)->CreateReadClosureFromScratchBuffer(offset)); +} + +void compiler_peer_create_write_promise_to_scratch_buffer(CompilerPeer peer, + uint32_t offset, + PromiseIrPtr value) { + IMPL(peer)->CreateWritePromiseToScratchBuffer(offset, LLVM_VALUE(value)); +} + +PromiseIrPtr compiler_peer_create_read_promise_from_scratch_buffer(CompilerPeer peer, + uint32_t offset) { + return PEER_PROMISE(IMPL(peer)->CreateReadPromiseFromScratchBuffer(offset)); +} + +void compiler_peer_create_write_value_to_scratch_buffer(CompilerPeer peer, + uint32_t offset, + ValueIrPtr value) { + IMPL(peer)->CreateWriteValueToScratchBuffer(offset, LLVM_VALUE(value)); +} + +ValueIrPtr compiler_peer_create_read_value_from_scratch_buffer(CompilerPeer peer, + uint32_t offset) { + return PEER_VALUE(IMPL(peer)->CreateReadValueFromScratchBuffer(offset)); +} + +// scope cleanup checker + +void compiler_peer_enable_scope_cleanup_checker(CompilerPeer peer, bool is_coroutine) { + IMPL(peer)->EnableScopeCleanupChecker(is_coroutine); +} + +void compiler_peer_set_scope_id_for_checker(CompilerPeer peer, uint16_t scope_id) { + IMPL(peer)->SetScopeIdForChecker(scope_id); +} + +void compiler_peer_assert_scope_id(CompilerPeer peer, uint16_t expected) { + IMPL(peer)->AssertScopeId(expected); +} + +// print + +void compiler_peer_create_print_value(CompilerPeer peer, ValueIrPtr value, const char* msg) { + IMPL(peer)->CreatePrintValue(LLVM_VALUE(value), msg); +} + +// debugger + +void compiler_peer_create_debugger(CompilerPeer peer) { + IMPL(peer)->CreateDebugger(); +} + +// unreachable + +void compiler_peer_create_unreachable(CompilerPeer peer, const char* msg) { + IMPL(peer)->CreateUnreachable(msg); +} + +// helper functions + +size_t compiler_peer_get_basic_block_name_or_as_operand(BasicBlockPtr block, + char* buf, + size_t len) { + return Compiler::GetNameOrAsOperand(LLVM_BB(block), buf, len); +} + +size_t compiler_peer_get_value_name_or_as_operand(ValueIrPtr value, char* buf, size_t len) { + return Compiler::GetNameOrAsOperand(LLVM_VALUE(value), buf, len); +} diff --git a/libs/jsruntime/src/llvmir/compiler/peer.rs b/libs/jsruntime/src/llvmir/compiler/peer.rs deleted file mode 100644 index eda1d351..00000000 --- a/libs/jsruntime/src/llvmir/compiler/peer.rs +++ /dev/null @@ -1,1265 +0,0 @@ -use std::ffi::CStr; - -use crate::logger; -use crate::Module; - -use super::bridge; -use super::FunctionId; -use super::ScopeRef; - -pub struct Compiler(*mut bridge::Compiler); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct BasicBlock(*mut bridge::BasicBlock); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct LambdaIr(*mut bridge::LambdaIr); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct BooleanIr(*mut bridge::BooleanIr); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct NumberIr(*mut bridge::NumberIr); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct ClosureIr(*mut bridge::ClosureIr); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct CoroutineIr(*mut bridge::CoroutineIr); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct PromiseIr(*mut bridge::PromiseIr); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct ValueIr(*mut bridge::ValueIr); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct ArgvIr(*mut bridge::ArgvIr); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct StatusIr(*mut bridge::StatusIr); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct CaptureIr(*mut bridge::CaptureIr); - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct SwitchIr(*mut bridge::SwitchIr); - -macro_rules! basic_block { - ($inner:expr) => { - BasicBlock(unsafe { $inner }) - }; -} - -macro_rules! lambda_ir { - ($inner:expr) => { - LambdaIr(unsafe { $inner }) - }; -} - -macro_rules! boolean_ir { - ($inner:expr) => { - BooleanIr(unsafe { $inner }) - }; -} - -macro_rules! number_ir { - ($inner:expr) => { - NumberIr(unsafe { $inner }) - }; -} - -macro_rules! closure_ir { - ($inner:expr) => { - ClosureIr(unsafe { $inner }) - }; -} - -macro_rules! coroutine_ir { - ($inner:expr) => { - CoroutineIr(unsafe { $inner }) - }; -} - -macro_rules! promise_ir { - ($inner:expr) => { - PromiseIr(unsafe { $inner }) - }; -} - -macro_rules! value_ir { - ($inner:expr) => { - ValueIr(unsafe { $inner }) - }; -} - -macro_rules! argv_ir { - ($inner:expr) => { - ArgvIr(unsafe { $inner }) - }; -} - -macro_rules! status_ir { - ($inner:expr) => { - StatusIr(unsafe { $inner }) - }; -} - -macro_rules! capture_ir { - ($inner:expr) => { - CaptureIr(unsafe { $inner }) - }; -} - -macro_rules! switch_ir { - ($inner:expr) => { - SwitchIr(unsafe { $inner }) - }; -} - -impl Compiler { - pub fn new() -> Self { - Self(unsafe { bridge::compiler_peer_new() }) - } - - pub fn start_compile(&self, enable_labels: bool) { - unsafe { - bridge::compiler_peer_start(self.0, enable_labels); - } - } - - pub fn end_compile(&self) -> Module { - Module { - peer: unsafe { bridge::compiler_peer_end(self.0) }, - } - } - - pub fn set_data_layout(&self, data_layout: &CStr) { - unsafe { - bridge::compiler_peer_set_data_layout(self.0, data_layout.as_ptr()); - } - } - - pub fn set_target_triple(&self, triple: &CStr) { - unsafe { - bridge::compiler_peer_set_target_triple(self.0, triple.as_ptr()); - } - } - - // function - - pub fn start_function(&self, func_id: FunctionId) { - unsafe { - bridge::compiler_peer_start_function(self.0, func_id.into()); - } - } - - pub fn end_function(&self, optimize: bool) { - unsafe { - bridge::compiler_peer_end_function(self.0, optimize); - } - } - - pub fn set_locals_block(&self, block: BasicBlock) { - debug_assert_ne!(block, BasicBlock::NONE); - unsafe { - bridge::compiler_peer_set_locals_block(self.0, block.0); - } - } - - pub fn get_function(&self, func_id: FunctionId) -> LambdaIr { - lambda_ir!(bridge::compiler_peer_get_function(self.0, func_id.into())) - } - - // basic block - - pub fn create_basic_block(&self, name: *const std::ffi::c_char, name_len: usize) -> BasicBlock { - basic_block!(bridge::compiler_peer_create_basic_block( - self.0, name, name_len - )) - } - - pub fn get_basic_block(&self) -> BasicBlock { - basic_block!(bridge::compiler_peer_get_basic_block(self.0)) - } - - pub fn set_basic_block(&self, block: BasicBlock) { - debug_assert_ne!(block, BasicBlock::NONE); - unsafe { - bridge::compiler_peer_set_basic_block(self.0, block.0); - } - } - - pub fn move_basic_block_after(&self, block: BasicBlock) { - debug_assert_ne!(block, BasicBlock::NONE); - unsafe { - bridge::compiler_peer_move_basic_block_after(self.0, block.0); - } - } - - pub fn is_basic_block_terminated(&self, block: BasicBlock) -> bool { - debug_assert_ne!(block, BasicBlock::NONE); - unsafe { bridge::compiler_peer_is_basic_block_terminated(self.0, block.0) } - } - - // jump - - pub fn create_br(&self, block: BasicBlock) { - debug_assert_ne!(block, BasicBlock::NONE); - unsafe { - bridge::compiler_peer_create_br(self.0, block.0); - } - } - - pub fn create_cond_br(&self, cond: BooleanIr, then_block: BasicBlock, else_block: BasicBlock) { - debug_assert_ne!(then_block, BasicBlock::NONE); - debug_assert_ne!(else_block, BasicBlock::NONE); - unsafe { - bridge::compiler_peer_create_cond_br(self.0, cond.0, then_block.0, else_block.0); - } - } - - // undefined - - pub fn create_is_undefined(&self, value: ValueIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_undefined(self.0, value.0) - } - } - - // null - - pub fn create_is_null(&self, value: ValueIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_null(self.0, value.0) - } - } - - pub fn create_is_non_nullish(&self, value: ValueIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_non_nullish(self.0, value.0) - } - } - - // boolean - - pub fn create_is_boolean(&self, value: ValueIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_boolean(self.0, value.0) - } - } - - pub fn create_is_same_boolean(&self, a: BooleanIr, b: BooleanIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_same_boolean(self.0, a.0, b.0) - } - } - - pub fn create_number_to_boolean(&self, value: NumberIr) -> BooleanIr { - debug_assert_ne!(value, NumberIr::NONE); - boolean_ir! { - bridge::compiler_peer_create_number_to_boolean(self.0, value.0) - } - } - - pub fn create_to_boolean(&self, value: ValueIr) -> BooleanIr { - debug_assert_ne!(value, ValueIr::NONE); - boolean_ir! { - bridge::compiler_peer_create_to_boolean(self.0, value.0) - } - } - - pub fn get_boolean(&self, value: bool) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_get_boolean(self.0, value) - } - } - - pub fn create_logical_not(&self, boolean: BooleanIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_logical_not(self.0, boolean.0) - } - } - - pub fn create_boolean_phi( - &self, - then_value: BooleanIr, - then_block: BasicBlock, - else_value: BooleanIr, - else_block: BasicBlock, - ) -> BooleanIr { - debug_assert_ne!(then_value, BooleanIr::NONE); - debug_assert_ne!(then_block, BasicBlock::NONE); - debug_assert_ne!(else_value, BooleanIr::NONE); - debug_assert_ne!(else_block, BasicBlock::NONE); - boolean_ir! { - bridge::compiler_peer_create_boolean_phi(self.0, then_value.0, then_block.0, else_value.0, else_block.0) - } - } - - // number - - pub fn create_is_number(&self, value: ValueIr) -> BooleanIr { - logger::debug!(event = "create_is_number", ?value); - boolean_ir! { - bridge::compiler_peer_create_is_number(self.0, value.0) - } - } - - pub fn create_is_same_number(&self, a: NumberIr, b: NumberIr) -> BooleanIr { - logger::debug!(event = "create_is_same_number", ?a, ?b); - boolean_ir! { - bridge::compiler_peer_create_is_same_number(self.0, a.0, b.0) - } - } - - pub fn create_boolean_to_number(&self, value: BooleanIr) -> NumberIr { - debug_assert_ne!(value, BooleanIr::NONE); - number_ir! { - bridge::compiler_peer_create_boolean_to_number(self.0, value.0) - } - } - - pub fn to_numeric(&self, value: ValueIr) -> NumberIr { - debug_assert_ne!(value, ValueIr::NONE); - number_ir! { - bridge::compiler_peer_to_numeric(self.0, value.0) - } - } - - pub fn get_nan(&self) -> NumberIr { - number_ir! { - bridge::compiler_peer_get_nan(self.0) - } - } - - pub fn get_zero(&self) -> NumberIr { - number_ir! { - bridge::compiler_peer_get_zero(self.0) - } - } - - pub fn get_number(&self, value: f64) -> NumberIr { - number_ir! { - bridge::compiler_peer_get_number(self.0, value) - } - } - - pub fn create_bitwise_not(&self, value: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_bitwise_not(self.0, value.0) - } - } - - pub fn create_fneg(&self, value: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_fneg(self.0, value.0) - } - } - - pub fn create_fmul(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_fmul(self.0, lhs.0, rhs.0) - } - } - - pub fn create_fdiv(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_fdiv(self.0, lhs.0, rhs.0) - } - } - - pub fn create_frem(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_frem(self.0, lhs.0, rhs.0) - } - } - - pub fn create_fadd(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_fadd(self.0, lhs.0, rhs.0) - } - } - - pub fn create_fsub(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_fsub(self.0, lhs.0, rhs.0) - } - } - - pub fn create_left_shift(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_left_shift(self.0, lhs.0, rhs.0) - } - } - - pub fn create_signed_right_shift(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_signed_right_shift(self.0, lhs.0, rhs.0) - } - } - - pub fn create_unsigned_right_shift(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_unsigned_right_shift(self.0, lhs.0, rhs.0) - } - } - - pub fn create_bitwise_and(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_bitwise_and(self.0, lhs.0, rhs.0) - } - } - - pub fn create_bitwise_xor(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_bitwise_xor(self.0, lhs.0, rhs.0) - } - } - - pub fn create_bitwise_or(&self, lhs: NumberIr, rhs: NumberIr) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_bitwise_or(self.0, lhs.0, rhs.0) - } - } - - pub fn create_less_than(&self, lhs: NumberIr, rhs: NumberIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_less_than(self.0, lhs.0, rhs.0) - } - } - - pub fn create_greater_than(&self, lhs: NumberIr, rhs: NumberIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_greater_than(self.0, lhs.0, rhs.0) - } - } - - pub fn create_less_than_or_equal(&self, lhs: NumberIr, rhs: NumberIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_less_than_or_equal(self.0, lhs.0, rhs.0) - } - } - - pub fn create_greater_than_or_equal(&self, lhs: NumberIr, rhs: NumberIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_greater_than_or_equal(self.0, lhs.0, rhs.0) - } - } - - pub fn create_number_phi( - &self, - then_value: NumberIr, - then_block: BasicBlock, - else_value: NumberIr, - else_block: BasicBlock, - ) -> NumberIr { - debug_assert_ne!(then_block, BasicBlock::NONE); - debug_assert_ne!(else_block, BasicBlock::NONE); - logger::debug!( - event = "create_number_phi", - ?then_value, - ?then_block, - ?else_value, - ?else_block - ); - number_ir! { - bridge::compiler_peer_create_number_phi(self.0, then_value.0, then_block.0, else_value.0, else_block.0) - } - } - - // closure - - pub fn create_is_closure(&self, value: ValueIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_closure(self.0, value.0) - } - } - - pub fn create_is_same_closure(&self, a: ClosureIr, b: ClosureIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_same_closure(self.0, a.0, b.0) - } - } - - pub fn get_closure_nullptr(&self) -> ClosureIr { - closure_ir! { - bridge::compiler_peer_get_closure_nullptr(self.0) - } - } - - pub fn create_closure(&self, lambda: LambdaIr, num_captures: u16) -> ClosureIr { - debug_assert_ne!(lambda, LambdaIr::NONE); - closure_ir! { - bridge::compiler_peer_create_closure(self.0, lambda.0, num_captures) - } - } - - pub fn create_store_capture_to_closure( - &self, - capture: CaptureIr, - closure: ClosureIr, - index: u16, - ) { - unsafe { - bridge::compiler_peer_create_store_capture_to_closure( - self.0, capture.0, closure.0, index, - ); - } - } - - pub fn create_call_on_closure( - &self, - closure: ClosureIr, - argc: u16, - argv: ArgvIr, - retv: ValueIr, - ) -> StatusIr { - status_ir! { - bridge::compiler_peer_create_call_on_closure(self.0, closure.0, argc, argv.0, retv.0) - } - } - - pub fn create_closure_phi( - &self, - then_value: ClosureIr, - then_block: BasicBlock, - else_value: ClosureIr, - else_block: BasicBlock, - ) -> ClosureIr { - debug_assert_ne!(then_block, BasicBlock::NONE); - debug_assert_ne!(else_block, BasicBlock::NONE); - closure_ir! { - bridge::compiler_peer_create_closure_phi(self.0, then_value.0, then_block.0, else_value.0, else_block.0) - } - } - - // promise - - pub fn create_is_promise(&self, value: ValueIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_promise(self.0, value.0) - } - } - - pub fn create_is_same_promise(&self, a: PromiseIr, b: PromiseIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_same_promise(self.0, a.0, b.0) - } - } - - pub fn create_register_promise(&self, coroutine: CoroutineIr) -> PromiseIr { - promise_ir! { - bridge::compiler_peer_create_register_promise(self.0, coroutine.0) - } - } - - pub fn create_await_promise(&self, promise: PromiseIr, awaiting: PromiseIr) { - unsafe { - bridge::compiler_peer_create_await_promise(self.0, promise.0, awaiting.0); - } - } - - pub fn create_resume(&self, promise: PromiseIr) { - unsafe { - bridge::compiler_peer_create_resume(self.0, promise.0); - } - } - - pub fn create_emit_promise_resolved(&self, promise: PromiseIr, result: ValueIr) { - unsafe { - bridge::compiler_peer_create_emit_promise_resolved(self.0, promise.0, result.0); - } - } - - // value - - pub fn create_has_value(&self, value: ValueIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_has_value(self.0, value.0) - } - } - - pub fn create_is_loosely_equal(&self, a: ValueIr, b: ValueIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_loosely_equal(self.0, a.0, b.0) - } - } - - pub fn create_is_strictly_equal(&self, a: ValueIr, b: ValueIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_strictly_equal(self.0, a.0, b.0) - } - } - - pub fn create_is_same_boolean_value(&self, any: ValueIr, boolean: BooleanIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_same_boolean_value(self.0, any.0, boolean.0) - } - } - - pub fn create_is_same_number_value(&self, any: ValueIr, number: NumberIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_same_number_value(self.0, any.0, number.0) - } - } - - pub fn create_is_same_closure_value(&self, any: ValueIr, closure: ClosureIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_same_closure_value(self.0, any.0, closure.0) - } - } - - pub fn create_is_same_promise_value(&self, any: ValueIr, promise: PromiseIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_same_promise_value(self.0, any.0, promise.0) - } - } - - pub fn create_undefined_to_any(&self) -> ValueIr { - value_ir! { - bridge::compiler_peer_create_undefined_to_any(self.0) - } - } - - pub fn create_null_to_any(&self) -> ValueIr { - value_ir! { - bridge::compiler_peer_create_null_to_any(self.0) - } - } - - pub fn create_boolean_to_any(&self, value: BooleanIr) -> ValueIr { - debug_assert_ne!(value, BooleanIr::NONE); - value_ir! { - bridge::compiler_peer_create_boolean_to_any(self.0, value.0) - } - } - - pub fn create_number_to_any(&self, value: NumberIr) -> ValueIr { - debug_assert_ne!(value, NumberIr::NONE); - value_ir! { - bridge::compiler_peer_create_number_to_any(self.0, value.0) - } - } - - pub fn create_closure_to_any(&self, value: ClosureIr) -> ValueIr { - debug_assert_ne!(value, ClosureIr::NONE); - value_ir! { - bridge::compiler_peer_create_closure_to_any(self.0, value.0) - } - } - - pub fn create_value_phi( - &self, - then_value: ValueIr, - then_block: BasicBlock, - else_value: ValueIr, - else_block: BasicBlock, - ) -> ValueIr { - debug_assert_ne!(then_block, BasicBlock::NONE); - debug_assert_ne!(else_block, BasicBlock::NONE); - value_ir! { - bridge::compiler_peer_create_value_phi(self.0, then_value.0, then_block.0, else_value.0, else_block.0) - } - } - - pub fn create_local_value(&self, index: u16) -> ValueIr { - value_ir! { - bridge::compiler_peer_create_local_value(self.0, index) - } - } - - pub fn create_store_none_to_value(&self, dest: ValueIr) { - unsafe { - bridge::compiler_peer_create_store_none_to_value(self.0, dest.0); - } - } - - pub fn create_store_undefined_to_value(&self, dest: ValueIr) { - unsafe { - bridge::compiler_peer_create_store_undefined_to_value(self.0, dest.0); - } - } - - pub fn create_store_null_to_value(&self, dest: ValueIr) { - unsafe { - bridge::compiler_peer_create_store_null_to_value(self.0, dest.0); - } - } - - pub fn create_store_boolean_to_value(&self, value: BooleanIr, dest: ValueIr) { - debug_assert_ne!(value, BooleanIr::NONE); - unsafe { - bridge::compiler_peer_create_store_boolean_to_value(self.0, value.0, dest.0); - } - } - - pub fn create_store_number_to_value(&self, value: NumberIr, dest: ValueIr) { - debug_assert_ne!(value, NumberIr::NONE); - unsafe { - bridge::compiler_peer_create_store_number_to_value(self.0, value.0, dest.0); - } - } - - pub fn create_store_closure_to_value(&self, value: ClosureIr, dest: ValueIr) { - debug_assert_ne!(value, ClosureIr::NONE); - unsafe { - bridge::compiler_peer_create_store_closure_to_value(self.0, value.0, dest.0); - } - } - - pub fn create_store_promise_to_value(&self, value: PromiseIr, dest: ValueIr) { - debug_assert_ne!(value, PromiseIr::NONE); - unsafe { - bridge::compiler_peer_create_store_promise_to_value(self.0, value.0, dest.0); - } - } - - pub fn create_store_value_to_value(&self, value: ValueIr, dest: ValueIr) { - debug_assert_ne!(value, ValueIr::NONE); - unsafe { - bridge::compiler_peer_create_store_value_to_value(self.0, value.0, dest.0); - } - } - - pub fn create_load_closure_from_value(&self, value: ValueIr) -> ClosureIr { - closure_ir! { - bridge::compiler_peer_create_load_closure_from_value(self.0, value.0) - } - } - - pub fn create_load_promise_from_value(&self, value: ValueIr) -> PromiseIr { - promise_ir! { - bridge::compiler_peer_create_load_promise_from_value(self.0, value.0) - } - } - - // argv - - pub fn get_argv_nullptr(&self) -> ArgvIr { - argv_ir! { - bridge::compiler_peer_get_argv_nullptr(self.0) - } - } - - pub fn create_argv(&self, argc: u16) -> ArgvIr { - debug_assert!(argc > 0); - argv_ir! { - bridge::compiler_peer_create_argv(self.0, argc) - } - } - - pub fn create_get_arg_in_argv(&self, argv: ArgvIr, index: u16) -> ValueIr { - logger::debug!(event = "create_get_arg_in_argv", ?argv, index); - debug_assert_ne!(argv, ArgvIr::NONE); - value_ir! { - bridge::compiler_peer_create_get_arg_in_argv(self.0, argv.0, index) - } - } - - pub fn create_get_argument_value_ptr(&self, index: u16) -> ValueIr { - value_ir! { - bridge::compiler_peer_create_get_argument_value_ptr(self.0, index) - } - } - - // retv - - pub fn create_retv(&self) -> ValueIr { - value_ir! { - bridge::compiler_peer_create_retv(self.0) - } - } - - pub fn create_store_undefined_to_retv(&self) { - unsafe { - bridge::compiler_peer_create_store_undefined_to_retv(self.0); - } - } - - pub fn create_store_null_to_retv(&self) { - unsafe { - bridge::compiler_peer_create_store_null_to_retv(self.0); - } - } - - pub fn create_store_boolean_to_retv(&self, value: BooleanIr) { - debug_assert_ne!(value, BooleanIr::NONE); - unsafe { - bridge::compiler_peer_create_store_boolean_to_retv(self.0, value.0); - } - } - - pub fn create_store_number_to_retv(&self, value: NumberIr) { - debug_assert_ne!(value, NumberIr::NONE); - unsafe { - bridge::compiler_peer_create_store_number_to_retv(self.0, value.0); - } - } - - pub fn create_store_closure_to_retv(&self, value: ClosureIr) { - debug_assert_ne!(value, ClosureIr::NONE); - unsafe { - bridge::compiler_peer_create_store_closure_to_retv(self.0, value.0); - } - } - - pub fn create_store_promise_to_retv(&self, value: PromiseIr) { - debug_assert_ne!(value, PromiseIr::NONE); - unsafe { - bridge::compiler_peer_create_store_promise_to_retv(self.0, value.0); - } - } - - pub fn create_store_value_to_retv(&self, value: ValueIr) { - debug_assert_ne!(value, ValueIr::NONE); - unsafe { - bridge::compiler_peer_create_store_value_to_retv(self.0, value.0); - } - } - - pub fn get_exception(&self) -> ValueIr { - value_ir! { - bridge::compiler_peer_get_exception(self.0) - } - } - - // status - - pub fn create_alloc_status(&self) { - unsafe { - bridge::compiler_peer_create_alloc_status(self.0); - } - } - - pub fn create_store_normal_status(&self) { - unsafe { - bridge::compiler_peer_create_store_normal_status(self.0); - } - } - - pub fn create_store_exception_status(&self) { - unsafe { - bridge::compiler_peer_create_store_exception_status(self.0); - } - } - - pub fn create_is_exception_status(&self, status: StatusIr) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_exception_status(self.0, status.0) - } - } - - // flow selector - // - // `break` and `continue` statements are generated unconditional branches in simple situation, - // but conditional branches must be generated in complex situations. - // - // Let think about the following program: - // - // for (;;) { - // let x; - // if (v == 0) - // return; - // // compute something using `x`. - // break; - // } - // - // The `x` variable defined in the scope of the for-loop body and it can be collected as - // garbage once the execution goes out from the scope. Depending on the algorithm of GC to - // use, the runtime must do something for GC when the execution goes out from the scope. In - // this case, the control flow of the `return` and `break` statements are not determined at - // compile time. And a new variable must be needed in order to determine the control flow at - // runtime. - // - // TODO: It might be possible to reuse the status variable instead of introducing the flow - // selector. The both are single variables inside a lambda and have common some values - // partially. If the status variable is reused, it must not be a single global variable. - // Because the execution may suspend by `await`. - // - // TODO: This design is inefficient in a performance point of view, but it makes it possible to - // support various GC algorithms. In addition, we can optimize the runtime cost by removing - // scope sub-graphs in CFG if those has no lexical variables. We can detect such lexical scopes - // in the semantic analysis phase. - - pub fn create_alloc_flow_selector(&self) { - unsafe { - bridge::compiler_peer_create_alloc_flow_selector(self.0); - } - } - - pub fn create_set_flow_selector_normal(&self) { - unsafe { - bridge::compiler_peer_create_set_flow_selector_normal(self.0); - } - } - - pub fn create_set_flow_selector_return(&self) { - unsafe { - bridge::compiler_peer_create_set_flow_selector_return(self.0); - } - } - - pub fn create_set_flow_selector_throw(&self) { - unsafe { - bridge::compiler_peer_create_set_flow_selector_throw(self.0); - } - } - - pub fn create_set_flow_selector_break(&self, depth: u32) { - unsafe { - bridge::compiler_peer_create_set_flow_selector_break(self.0, depth); - } - } - - pub fn create_set_flow_selector_continue(&self, depth: u32) { - unsafe { - bridge::compiler_peer_create_set_flow_selector_continue(self.0, depth); - } - } - - pub fn create_is_flow_selector_normal(&self) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_flow_selector_normal(self.0) - } - } - - pub fn create_is_flow_selector_normal_or_continue(&self, depth: u32) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_flow_selector_normal_or_continue(self.0, depth) - } - } - - pub fn create_is_flow_selector_break_or_continue(&self, depth: u32) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_flow_selector_break_or_continue(self.0, depth) - } - } - - pub fn create_is_flow_selector_break(&self, depth: u32) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_is_flow_selector_break(self.0, depth) - } - } - - // capture - - pub fn create_capture(&self, value: ValueIr) -> CaptureIr { - capture_ir! { - bridge::compiler_peer_create_capture(self.0, value.0) - } - } - - pub fn create_escape_value(&self, capture: CaptureIr, value: ValueIr) { - unsafe { - bridge::compiler_peer_create_escape_value(self.0, capture.0, value.0); - } - } - - pub fn create_get_capture_value_ptr(&self, index: u16) -> ValueIr { - value_ir! { - bridge::compiler_peer_create_get_capture_value_ptr(self.0, index) - } - } - - pub fn create_load_capture(&self, index: u16) -> CaptureIr { - capture_ir! { - bridge::compiler_peer_create_load_capture(self.0, index) - } - } - - // coroutine - - pub fn create_coroutine( - &self, - closure: ClosureIr, - num_locals: u16, - scratch_buffer_len: u16, - ) -> CoroutineIr { - coroutine_ir! { - bridge::compiler_peer_create_coroutine(self.0, closure.0, num_locals, scratch_buffer_len) - } - } - - pub fn create_switch_for_coroutine(&self, block: BasicBlock, num_states: u32) -> SwitchIr { - switch_ir! { - bridge::compiler_peer_create_switch_for_coroutine(self.0, block.0, num_states) - } - } - - pub fn create_add_state_for_coroutine(&self, inst: SwitchIr, state: u32, block: BasicBlock) { - unsafe { - bridge::compiler_peer_create_add_state_for_coroutine(self.0, inst.0, state, block.0); - } - } - - pub fn create_suspend(&self) { - unsafe { - bridge::compiler_peer_create_suspend(self.0); - } - } - - pub fn create_set_coroutine_state(&self, state: u32) { - unsafe { - bridge::compiler_peer_create_set_coroutine_state(self.0, state); - } - } - - pub fn create_set_captures_for_coroutine(&self) { - unsafe { - bridge::compiler_peer_create_set_captures_for_coroutine(self.0); - } - } - - pub fn create_get_local_ptr_from_coroutine(&self, index: u16) -> ValueIr { - value_ir! { - bridge::compiler_peer_create_get_local_ptr_from_coroutine(self.0, index) - } - } - - pub fn create_write_boolean_to_scratch_buffer(&self, offset: u32, value: BooleanIr) { - unsafe { - bridge::compiler_peer_create_write_boolean_to_scratch_buffer(self.0, offset, value.0); - } - } - - pub fn create_read_boolean_from_scratch_buffer(&self, offset: u32) -> BooleanIr { - boolean_ir! { - bridge::compiler_peer_create_read_boolean_from_scratch_buffer(self.0, offset) - } - } - - pub fn create_write_number_to_scratch_buffer(&self, offset: u32, value: NumberIr) { - unsafe { - bridge::compiler_peer_create_write_number_to_scratch_buffer(self.0, offset, value.0); - } - } - - pub fn create_read_number_from_scratch_buffer(&self, offset: u32) -> NumberIr { - number_ir! { - bridge::compiler_peer_create_read_number_from_scratch_buffer(self.0, offset) - } - } - - pub fn create_write_closure_to_scratch_buffer(&self, offset: u32, value: ClosureIr) { - unsafe { - bridge::compiler_peer_create_write_closure_to_scratch_buffer(self.0, offset, value.0); - } - } - - pub fn create_read_closure_from_scratch_buffer(&self, offset: u32) -> ClosureIr { - closure_ir! { - bridge::compiler_peer_create_read_closure_from_scratch_buffer(self.0, offset) - } - } - - pub fn create_write_promise_to_scratch_buffer(&self, offset: u32, value: PromiseIr) { - unsafe { - bridge::compiler_peer_create_write_promise_to_scratch_buffer(self.0, offset, value.0); - } - } - - pub fn create_read_promise_from_scratch_buffer(&self, offset: u32) -> PromiseIr { - promise_ir! { - bridge::compiler_peer_create_read_promise_from_scratch_buffer(self.0, offset) - } - } - - pub fn create_write_value_to_scratch_buffer(&self, offset: u32, value: ValueIr) { - unsafe { - bridge::compiler_peer_create_write_value_to_scratch_buffer(self.0, offset, value.0); - } - } - - pub fn create_read_value_from_scratch_buffer(&self, offset: u32) -> ValueIr { - value_ir! { - bridge::compiler_peer_create_read_value_from_scratch_buffer(self.0, offset) - } - } - - // scope cleanup checker - - pub fn enable_scope_cleanup_checker(&self, is_coroutine: bool) { - unsafe { - bridge::compiler_peer_enable_scope_cleanup_checker(self.0, is_coroutine); - } - } - - pub fn set_scope_id_for_checker(&self, scope_ref: ScopeRef) { - unsafe { - bridge::compiler_peer_set_scope_id_for_checker(self.0, scope_ref.id()); - } - } - - pub fn assert_scope_id(&self, expected: ScopeRef) { - unsafe { - bridge::compiler_peer_assert_scope_id(self.0, expected.id()); - } - } - - // print - - #[allow(unused)] - pub fn create_print_value(&self, value: ValueIr, msg: &CStr) { - unsafe { - bridge::compiler_peer_create_print_value(self.0, value.0, msg.as_ptr()); - } - } - - // debugger - - pub fn create_debugger(&self) { - unsafe { - bridge::compiler_peer_create_debugger(self.0); - } - } - - // unreachable - - pub fn create_unreachable(&self, msg: &CStr) { - unsafe { - bridge::compiler_peer_create_unreachable(self.0, msg.as_ptr()); - } - } -} - -impl Default for Compiler { - fn default() -> Self { - Compiler::new() - } -} - -impl Drop for Compiler { - fn drop(&mut self) { - unsafe { - bridge::compiler_peer_delete(self.0); - } - } -} - -impl BasicBlock { - pub const NONE: Self = Self(std::ptr::null_mut()); - - pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { - unsafe { - bridge::helper_peer_get_basic_block_name_or_as_operand(self.0, buf, len); - std::ffi::CStr::from_ptr(buf) - } - } -} - -impl LambdaIr { - pub const NONE: Self = Self(std::ptr::null_mut()); - - pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { - unsafe { - bridge::helper_peer_get_value_name_or_as_operand( - self.0 as *mut bridge::ValueIr, - buf, - len, - ); - std::ffi::CStr::from_ptr(buf) - } - } -} - -impl BooleanIr { - pub const NONE: Self = Self(std::ptr::null_mut()); - - pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { - unsafe { - bridge::helper_peer_get_value_name_or_as_operand( - self.0 as *mut bridge::ValueIr, - buf, - len, - ); - std::ffi::CStr::from_ptr(buf) - } - } -} - -impl NumberIr { - pub const NONE: Self = Self(std::ptr::null_mut()); - - pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { - unsafe { - bridge::helper_peer_get_value_name_or_as_operand( - self.0 as *mut bridge::ValueIr, - buf, - len, - ); - std::ffi::CStr::from_ptr(buf) - } - } -} - -impl ClosureIr { - pub const NONE: Self = Self(std::ptr::null_mut()); - - pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { - unsafe { - bridge::helper_peer_get_value_name_or_as_operand( - self.0 as *mut bridge::ValueIr, - buf, - len, - ); - std::ffi::CStr::from_ptr(buf) - } - } -} - -impl CoroutineIr { - pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { - unsafe { - bridge::helper_peer_get_value_name_or_as_operand( - self.0 as *mut bridge::ValueIr, - buf, - len, - ); - std::ffi::CStr::from_ptr(buf) - } - } -} - -impl PromiseIr { - pub const NONE: Self = Self(std::ptr::null_mut()); - - pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { - unsafe { - bridge::helper_peer_get_value_name_or_as_operand( - self.0 as *mut bridge::ValueIr, - buf, - len, - ); - std::ffi::CStr::from_ptr(buf) - } - } -} - -impl ValueIr { - pub const NONE: Self = Self(std::ptr::null_mut()); - - pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { - unsafe { - bridge::helper_peer_get_value_name_or_as_operand(self.0, buf, len); - std::ffi::CStr::from_ptr(buf) - } - } -} - -impl ArgvIr { - pub const NONE: Self = Self(std::ptr::null_mut()); -} - -impl CaptureIr { - pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { - unsafe { - bridge::helper_peer_get_value_name_or_as_operand( - self.0 as *mut bridge::ValueIr, - buf, - len, - ); - std::ffi::CStr::from_ptr(buf) - } - } -} diff --git a/libs/jsruntime/src/llvmir/type_holder.cc.njk b/libs/jsruntime/src/llvmir/compiler/type_holder.cc.njk similarity index 100% rename from libs/jsruntime/src/llvmir/type_holder.cc.njk rename to libs/jsruntime/src/llvmir/compiler/type_holder.cc.njk diff --git a/libs/jsruntime/src/llvmir/type_holder.hh.njk b/libs/jsruntime/src/llvmir/compiler/type_holder.hh.njk similarity index 100% rename from libs/jsruntime/src/llvmir/type_holder.hh.njk rename to libs/jsruntime/src/llvmir/compiler/type_holder.hh.njk diff --git a/libs/jsruntime/src/llvmir/executor.cc b/libs/jsruntime/src/llvmir/executor.cc deleted file mode 100644 index 1d374e5d..00000000 --- a/libs/jsruntime/src/llvmir/executor.cc +++ /dev/null @@ -1,45 +0,0 @@ -#include "executor.hh" - -#include -#include - -#include "module.hh" - -namespace { - -// TODO(perf): Inefficient. Use a fixed size buffer for formatting func_id. -std::string FuncIdToName(uint32_t func_id) { - std::stringstream ss; - ss << "fn" << func_id; - return ss.str(); -} - -} // namespace - -static llvm::ExitOnError ExitOnErr; - -// static -llvm::Expected Executor::Create() { - auto jit = ExitOnErr(llvm::orc::LLJITBuilder().create()); - return new Executor(std::move(jit)); -} - -void Executor::RegisterHostFunction(uint32_t func_id, Lambda lambda) { - llvm::orc::SymbolMap symbols; - auto name = FuncIdToName(func_id); - symbols[exec_session().intern(name)] = { - llvm::orc::ExecutorAddr::fromPtr(lambda), - llvm::JITSymbolFlags::Exported, - }; - ExitOnErr(main_jd().define(llvm::orc::absoluteSymbols(std::move(symbols)))); -} - -void Executor::RegisterModule(Module* mod) { - ExitOnErr(jit_->addIRModule(std::move(mod->mod))); -} - -Lambda Executor::GetNativeFunction(uint32_t func_id) { - auto name = FuncIdToName(func_id); - auto addr = ExitOnErr(jit_->lookup(name)); - return addr.toPtr(); -} diff --git a/libs/jsruntime/src/llvmir/executor.hh b/libs/jsruntime/src/llvmir/executor.hh deleted file mode 100644 index 09203c78..00000000 --- a/libs/jsruntime/src/llvmir/executor.hh +++ /dev/null @@ -1,52 +0,0 @@ -// Based on KaleidoscopeJIT - -#pragma once - -#include - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wredundant-move" -#pragma GCC diagnostic ignored "-Wunused-parameter" -#include -#include -#pragma GCC diagnostic pop - -#include "bridge.hh" - -struct Module; - -class Executor { - public: - static llvm::Expected Create(); - - explicit Executor(std::unique_ptr&& jit) : jit_(std::move(jit)) {} - - ~Executor() = default; - - void RegisterRuntime(const Runtime* runtime); - void RegisterHostFunction(uint32_t func_id, Lambda lambda); - void RegisterModule(Module* mod); - Lambda GetNativeFunction(uint32_t func_id); - - llvm::orc::ExecutionSession& exec_session() { - return jit_->getExecutionSession(); - } - - const llvm::DataLayout& data_layout() const { - return jit_->getDataLayout(); - } - - const llvm::Triple& target_triple() const { - return jit_->getTargetTriple(); - } - - llvm::orc::JITDylib& main_jd() { - return jit_->getMainJITDylib(); - } - - private: - // See examples in: - // //vendor/src/llvm/llvm-project/examples/HowToUseLLJIT/ - // //vendor/src/llvm/llvm-project/examples/OrcV2Examples/ - std::unique_ptr jit_; -}; diff --git a/libs/jsruntime/src/llvmir/executor.rs b/libs/jsruntime/src/llvmir/executor.rs deleted file mode 100644 index 72642bef..00000000 --- a/libs/jsruntime/src/llvmir/executor.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::ffi::CStr; - -use crate::FunctionId; -use crate::HostLambda; - -use super::bridge; -use super::Module; - -pub struct Executor { - peer: *mut bridge::Executor, -} - -impl Executor { - pub fn with_runtime_bridge(runtime: &bridge::Runtime) -> Self { - let peer = unsafe { - let peer = bridge::executor_peer_new(); - bridge::executor_peer_register_runtime(peer, runtime); - peer - }; - Self { peer } - } - - pub fn register_host_function(&self, func_id: FunctionId, func: HostLambda) { - unsafe { - bridge::executor_peer_register_host_function(self.peer, func_id.into(), Some(func)); - } - } - - pub fn register_module(&self, module: Module) { - unsafe { - bridge::executor_peer_register_module(self.peer, module.peer); - } - } - - pub fn get_data_layout(&self) -> &CStr { - unsafe { CStr::from_ptr(bridge::executor_peer_get_data_layout(self.peer)) } - } - - pub fn get_target_triple(&self) -> &CStr { - unsafe { CStr::from_ptr(bridge::executor_peer_get_target_triple(self.peer)) } - } - - pub fn get_native_function(&self, func_id: FunctionId) -> bridge::Lambda { - unsafe { bridge::executor_peer_get_native_function(self.peer, func_id.into()) } - } -} - -impl Drop for Executor { - fn drop(&mut self) { - unsafe { - bridge::executor_peer_delete(self.peer); - } - } -} diff --git a/libs/jsruntime/src/llvmir/executor/bridge.rs b/libs/jsruntime/src/llvmir/executor/bridge.rs new file mode 100644 index 00000000..a9fe641d --- /dev/null +++ b/libs/jsruntime/src/llvmir/executor/bridge.rs @@ -0,0 +1,72 @@ +use std::ffi::c_char; +use std::ffi::c_void; +use std::ffi::CStr; + +use crate::llvmir::module::Module; +use crate::llvmir::module::ModulePeer; +use crate::llvmir::RuntimeFunctions; +use crate::types::Lambda; +use crate::FunctionId; + +pub struct ExecutorBridge(ExecutorPeer); + +impl ExecutorBridge { + pub fn new(functions: &RuntimeFunctions) -> Self { + Self(unsafe { + let peer = executor_peer_new(); + executor_peer_register_runtime_functions(peer, functions); + peer + }) + } + + pub fn register_host_function(&self, func_id: FunctionId, lambda: Lambda) { + unsafe { + executor_peer_register_host_function(self.0, func_id.into(), lambda); + } + } + + pub fn register_module(&self, module: Module) { + unsafe { + executor_peer_register_module(self.0, module.peer()); + } + } + + pub fn get_data_layout(&self) -> &CStr { + unsafe { CStr::from_ptr(executor_peer_get_data_layout(self.0)) } + } + + pub fn get_target_triple(&self) -> &CStr { + unsafe { CStr::from_ptr(executor_peer_get_target_triple(self.0)) } + } + + pub fn get_native_function(&self, func_id: FunctionId) -> Option { + unsafe { + std::mem::transmute::>(executor_peer_get_native_function( + self.0, + func_id.into(), + )) + } + } +} + +impl Drop for ExecutorBridge { + fn drop(&mut self) { + unsafe { + executor_peer_delete(self.0); + } + } +} + +type ExecutorPeer = *mut c_void; + +#[link(name = "llvmir")] +extern "C" { + fn executor_peer_new() -> ExecutorPeer; + fn executor_peer_delete(peer: ExecutorPeer); + fn executor_peer_register_runtime_functions(peer: ExecutorPeer, functions: &RuntimeFunctions); + fn executor_peer_register_host_function(peer: ExecutorPeer, func_id: u32, func: Lambda); + fn executor_peer_register_module(peer: ExecutorPeer, module: ModulePeer); + fn executor_peer_get_data_layout(peer: ExecutorPeer) -> *const c_char; + fn executor_peer_get_target_triple(peer: ExecutorPeer) -> *const c_char; + fn executor_peer_get_native_function(peer: ExecutorPeer, func_id: u32) -> Lambda; +} diff --git a/libs/jsruntime/src/llvmir/executor.codegen.cc.njk b/libs/jsruntime/src/llvmir/executor/impl.codegen.hh.njk similarity index 79% rename from libs/jsruntime/src/llvmir/executor.codegen.cc.njk rename to libs/jsruntime/src/llvmir/executor/impl.codegen.hh.njk index e2a24d1e..49dcad27 100644 --- a/libs/jsruntime/src/llvmir/executor.codegen.cc.njk +++ b/libs/jsruntime/src/llvmir/executor/impl.codegen.hh.njk @@ -3,20 +3,12 @@ // This file was automagically generated with: // template: {{ template }} -#include "executor.hh" - -#include -#include -#include - -static llvm::ExitOnError ExitOnErr; - // TODO: link libm directly extern "C" double runtime_fmod(double a, double b) { return std::fmod(a, b); } -void Executor::RegisterRuntime(const Runtime* runtime) { +void Executor::RegisterRuntimeFunctions(const RuntimeFunctions* functions) { llvm::orc::SymbolMap symbols; symbols[exec_session().intern("fmod")] = { llvm::orc::ExecutorAddr::fromPtr(runtime_fmod), @@ -32,7 +24,7 @@ void Executor::RegisterRuntime(const Runtime* runtime) { }; {%- for function in data.functions %} symbols[exec_session().intern("runtime_{{ function.name }}")] = { - llvm::orc::ExecutorAddr::fromPtr(runtime->{{ function.name }}), + llvm::orc::ExecutorAddr::fromPtr(functions->{{ function.name }}), llvm::JITSymbolFlags::Exported, }; {%- endfor %} diff --git a/libs/jsruntime/src/llvmir/executor/impl.hh b/libs/jsruntime/src/llvmir/executor/impl.hh new file mode 100644 index 00000000..e7cc71bc --- /dev/null +++ b/libs/jsruntime/src/llvmir/executor/impl.hh @@ -0,0 +1,90 @@ +// Based on KaleidoscopeJIT + +#pragma once + +#include +#include +#include +#include +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-move" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include +#include +#pragma GCC diagnostic pop + +#include "../bridge.hh" +#include "../module/impl.hh" + +namespace { + +// TODO(perf): Inefficient. Use a fixed size buffer for formatting func_id. +std::string FuncIdToName(uint32_t func_id) { + std::stringstream ss; + ss << "fn" << func_id; + return ss.str(); +} + +} // namespace + +static llvm::ExitOnError ExitOnErr; + +class Executor { + public: + static llvm::Expected Create() { + auto jit = ExitOnErr(llvm::orc::LLJITBuilder().create()); + return new Executor(std::move(jit)); + } + + explicit Executor(std::unique_ptr&& jit) : jit_(std::move(jit)) {} + + ~Executor() = default; + + void RegisterRuntimeFunctions(const RuntimeFunctions* functions); + + void RegisterHostFunction(uint32_t func_id, Lambda lambda) { + llvm::orc::SymbolMap symbols; + auto name = FuncIdToName(func_id); + symbols[exec_session().intern(name)] = { + llvm::orc::ExecutorAddr::fromPtr(lambda), + llvm::JITSymbolFlags::Exported, + }; + ExitOnErr(main_jd().define(llvm::orc::absoluteSymbols(std::move(symbols)))); + } + + void RegisterModule(Module* mod) { + ExitOnErr(jit_->addIRModule(std::move(mod->mod))); + } + + Lambda GetNativeFunction(uint32_t func_id) { + auto name = FuncIdToName(func_id); + auto addr = ExitOnErr(jit_->lookup(name)); + return addr.toPtr(); + } + + llvm::orc::ExecutionSession& exec_session() { + return jit_->getExecutionSession(); + } + + const llvm::DataLayout& data_layout() const { + return jit_->getDataLayout(); + } + + const llvm::Triple& target_triple() const { + return jit_->getTargetTriple(); + } + + llvm::orc::JITDylib& main_jd() { + return jit_->getMainJITDylib(); + } + + private: + // See examples in: + // //vendor/src/llvm/llvm-project/examples/HowToUseLLJIT/ + // //vendor/src/llvm/llvm-project/examples/OrcV2Examples/ + std::unique_ptr jit_; +}; + +#include "impl.codegen.hh" diff --git a/libs/jsruntime/src/llvmir/executor/mod.rs b/libs/jsruntime/src/llvmir/executor/mod.rs new file mode 100644 index 00000000..6c1b4844 --- /dev/null +++ b/libs/jsruntime/src/llvmir/executor/mod.rs @@ -0,0 +1,17 @@ +mod bridge; + +use base::macros::delegate_all; + +use bridge::ExecutorBridge; + +use super::RuntimeFunctions; + +pub struct Executor(ExecutorBridge); + +delegate_all! { Executor => ExecutorBridge } + +impl Executor { + pub fn new(functions: &RuntimeFunctions) -> Self { + Self(ExecutorBridge::new(functions)) + } +} diff --git a/libs/jsruntime/src/llvmir/executor/peer.cc b/libs/jsruntime/src/llvmir/executor/peer.cc new file mode 100644 index 00000000..748eb1eb --- /dev/null +++ b/libs/jsruntime/src/llvmir/executor/peer.cc @@ -0,0 +1,39 @@ +#include "../bridge.hh" +#include "../module/impl.hh" +#include "impl.hh" + +#define PEER(impl) reinterpret_cast(impl) +#define IMPL(peer) reinterpret_cast(peer) + +ExecutorPeer executor_peer_new() { + return PEER(llvm::cantFail(Executor::Create())); +} + +void executor_peer_delete(ExecutorPeer peer) { + delete IMPL(peer); +} + +void executor_peer_register_runtime_functions(ExecutorPeer peer, + const RuntimeFunctions* functions) { + IMPL(peer)->RegisterRuntimeFunctions(functions); +} + +void executor_peer_register_host_function(ExecutorPeer peer, uint32_t func_id, Lambda lambda) { + IMPL(peer)->RegisterHostFunction(func_id, lambda); +} + +void executor_peer_register_module(ExecutorPeer peer, ModulePeer mod) { + IMPL(peer)->RegisterModule(reinterpret_cast(mod)); +} + +const char* executor_peer_get_data_layout(const ExecutorPeer peer) { + return IMPL(peer)->data_layout().getStringRepresentation().c_str(); +} + +const char* executor_peer_get_target_triple(const ExecutorPeer peer) { + return IMPL(peer)->target_triple().getTriple().c_str(); +} + +Lambda executor_peer_get_native_function(ExecutorPeer peer, uint32_t func_id) { + return IMPL(peer)->GetNativeFunction(func_id); +} diff --git a/libs/jsruntime/src/llvmir/helper.cc b/libs/jsruntime/src/llvmir/helper.cc deleted file mode 100644 index cf345783..00000000 --- a/libs/jsruntime/src/llvmir/helper.cc +++ /dev/null @@ -1,34 +0,0 @@ -#include "helper.hh" - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#include -#include -#pragma GCC diagnostic pop - -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// based-on: Value::getNameOrAsOperand(). -std::string GetNameOrAsOperand(llvm::Value* value) { - assert(value != nullptr); - - auto name = value->getName(); - if (!name.empty()) { - return std::string(name); - } - - std::string buffer; - llvm::raw_string_ostream os(buffer); - value->printAsOperand(os); - return buffer; -} - -size_t GetNameOrAsOperand(llvm::Value* value, char* buf, size_t len) { - assert(value != nullptr); - assert(buf != nullptr); - assert(len > 1); - auto s = GetNameOrAsOperand(value); - auto nwritten = std::min(s.size(), len - 1); - memcpy(buf, s.data(), nwritten); - buf[nwritten] = '\0'; - return nwritten; -} diff --git a/libs/jsruntime/src/llvmir/helper.hh b/libs/jsruntime/src/llvmir/helper.hh deleted file mode 100644 index 213711d4..00000000 --- a/libs/jsruntime/src/llvmir/helper.hh +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -namespace llvm { -class Value; -} - -std::string GetNameOrAsOperand(llvm::Value* value); -size_t GetNameOrAsOperand(llvm::Value* value, char* buf, size_t len); - -#define V2S(v) (GetNameOrAsOperand(v)) diff --git a/libs/jsruntime/src/llvmir/macros.hh b/libs/jsruntime/src/llvmir/macros.hh deleted file mode 100644 index 2885b8d9..00000000 --- a/libs/jsruntime/src/llvmir/macros.hh +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#define BEGIN_C_LINKAGE extern "C" { -#define END_C_LINKAGE } - -#define UNUSED(var) ((void)(var)) diff --git a/libs/jsruntime/src/llvmir/mod.rs b/libs/jsruntime/src/llvmir/mod.rs index ba43a85f..5855ac53 100644 --- a/libs/jsruntime/src/llvmir/mod.rs +++ b/libs/jsruntime/src/llvmir/mod.rs @@ -1,34 +1,14 @@ -pub mod bridge; +mod bridge; mod compiler; mod executor; +mod module; -pub use bridge::runtime_bridge; -pub use bridge::Status; pub use compiler::CompileError; pub use executor::Executor; +pub use module::Module; -pub fn initialize() { - unsafe { - bridge::llvmir_initialize(); - } -} - -pub struct Module { - peer: *mut bridge::Module, -} +pub use bridge::RuntimeFunctions; -impl Module { - pub fn print(&self, stderr: bool) { - unsafe { - bridge::module_peer_print(self.peer, stderr); - } - } -} - -impl Drop for Module { - fn drop(&mut self) { - unsafe { - bridge::module_peer_delete(self.peer); - } - } +pub fn initialize() { + bridge::initialize(); } diff --git a/libs/jsruntime/src/llvmir/module/bridge.rs b/libs/jsruntime/src/llvmir/module/bridge.rs new file mode 100644 index 00000000..fa46e1a0 --- /dev/null +++ b/libs/jsruntime/src/llvmir/module/bridge.rs @@ -0,0 +1,36 @@ +use std::ffi::c_void; + +pub struct ModuleBridge(ModulePeer); + +impl ModuleBridge { + pub fn print(&self, stderr: bool) { + unsafe { + module_peer_print(self.0, stderr); + } + } + + pub fn new(peer: ModulePeer) -> Self { + Self(peer) + } + + pub fn peer(&self) -> ModulePeer { + self.0 + } +} + +impl Drop for ModuleBridge { + fn drop(&mut self) { + unsafe { + module_peer_delete(self.0); + } + } +} + +// `ModulePeer` is used in other bridge modules. +pub type ModulePeer = *mut c_void; + +#[link(name = "llvmir")] +extern "C" { + fn module_peer_delete(peer: ModulePeer); + fn module_peer_print(peer: ModulePeer, stderr: bool); +} diff --git a/libs/jsruntime/src/llvmir/module.hh b/libs/jsruntime/src/llvmir/module/impl.hh similarity index 100% rename from libs/jsruntime/src/llvmir/module.hh rename to libs/jsruntime/src/llvmir/module/impl.hh diff --git a/libs/jsruntime/src/llvmir/module/mod.rs b/libs/jsruntime/src/llvmir/module/mod.rs new file mode 100644 index 00000000..f9576d6f --- /dev/null +++ b/libs/jsruntime/src/llvmir/module/mod.rs @@ -0,0 +1,18 @@ +mod bridge; + +use base::macros::delegate_all; + +use bridge::ModuleBridge; + +// `ModulePeer` is used in other bridge modules. +pub use bridge::ModulePeer; + +pub struct Module(ModuleBridge); + +delegate_all! {Module => ModuleBridge} + +impl Module { + pub fn new(peer: ModulePeer) -> Self { + Self(ModuleBridge::new(peer)) + } +} diff --git a/libs/jsruntime/src/llvmir/module/peer.cc b/libs/jsruntime/src/llvmir/module/peer.cc new file mode 100644 index 00000000..e16b4c76 --- /dev/null +++ b/libs/jsruntime/src/llvmir/module/peer.cc @@ -0,0 +1,17 @@ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include +#pragma GCC diagnostic pop + +#include "../bridge.hh" +#include "impl.hh" + +#define IMPL(peer) reinterpret_cast(peer) + +void module_peer_delete(ModulePeer peer) { + delete IMPL(peer); +} + +void module_peer_print(ModulePeer peer, bool stderr) { + IMPL(peer)->Print(stderr); +} diff --git a/libs/jsruntime/src/llvmir/peer.cc b/libs/jsruntime/src/llvmir/peer.cc new file mode 100644 index 00000000..621d9489 --- /dev/null +++ b/libs/jsruntime/src/llvmir/peer.cc @@ -0,0 +1,12 @@ +#include + +#include "bridge.hh" + +void llvmir_initialize() { + // Uncomment if you want to enable LLVM_DEBUG(). + // llvm::DebugFlag = true; + + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); +} diff --git a/libs/jsruntime/src/llvmir/runtime.hh.njk b/libs/jsruntime/src/llvmir/runtime.hh.njk deleted file mode 100644 index 191dc5f8..00000000 --- a/libs/jsruntime/src/llvmir/runtime.hh.njk +++ /dev/null @@ -1,10 +0,0 @@ -// DO NOT EDIT THIS FILE BY HAND. -// -// This file was automagically generated with: -// template: {{ template }} - -typedef struct { - {%- for function in data.functions %} - {{ function.c_type }}; - {%- endfor %} -} Runtime; diff --git a/libs/jsruntime/src/tasklet.rs b/libs/jsruntime/src/tasklet.rs index f648dd58..9c53bc7d 100644 --- a/libs/jsruntime/src/tasklet.rs +++ b/libs/jsruntime/src/tasklet.rs @@ -4,6 +4,7 @@ use rustc_hash::FxHashMap; use crate::types::Coroutine; use crate::types::CoroutineStatus; +use crate::types::Promise; use crate::Runtime; use crate::Value; @@ -55,21 +56,6 @@ impl Runtime { } } -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct Promise(u32); - -impl From for Promise { - fn from(value: u32) -> Self { - Self(value) - } -} - -impl From for u32 { - fn from(value: Promise) -> Self { - value.0 - } -} - pub struct System { messages: VecDeque, promises: FxHashMap, @@ -96,7 +82,7 @@ impl System { fn new_promise(&mut self) -> Promise { assert!(self.promises.len() < u32::MAX as usize); loop { - let promise = Promise(self.next_promise); + let promise = self.next_promise.into(); if !self.promises.contains_key(&promise) { return promise; } diff --git a/libs/jsruntime/src/types.rs b/libs/jsruntime/src/types.rs index 3e2ea879..c5891dc6 100644 --- a/libs/jsruntime/src/types.rs +++ b/libs/jsruntime/src/types.rs @@ -1,14 +1,8 @@ use std::ffi::c_void; -use std::marker::PhantomPinned; use std::mem::offset_of; use std::ptr::addr_eq; -use crate::llvmir::bridge::Lambda; -use crate::llvmir::bridge::Status; -use crate::llvmir::bridge::STATUS_NORMAL; -use crate::llvmir::bridge::STATUS_EXCEPTION; -use crate::llvmir::bridge::STATUS_SUSPEND; -use crate::tasklet::Promise; +use crate::Runtime; // CAUTION: This module contains types used in JIT-generated code. Please carefully check the // memory layout of a type you want to change. It's recommended to use compile-time assertions @@ -37,8 +31,8 @@ static_assertions::const_assert_eq!(align_of::(), 8); impl Value { pub fn into_result(self, status: Status) -> Result { match status { - STATUS_NORMAL => Ok(self), - STATUS_EXCEPTION => Err(self), + Status::Normal => Ok(self), + Status::Exception => Err(self), _ => unreachable!(), } } @@ -76,7 +70,7 @@ impl From for Value { impl From for Value { fn from(value: Promise) -> Self { - Self::Promise(value.into()) + Self::Promise(value) } } @@ -88,7 +82,7 @@ impl std::fmt::Debug for Value { Self::Null => write!(f, "null"), Self::Boolean(value) => write!(f, "{value}"), Self::Number(value) => write!(f, "{value}"), - Self::Closure(value) => write!(f, "{:?}", &*value), + Self::Closure(value) => write!(f, "{:?}", unsafe { value.as_ref().unwrap() }), Self::Promise(value) => write!(f, "{value:?}"), } } @@ -119,7 +113,7 @@ static_assertions::const_assert_eq!(align_of::(), 8); impl std::fmt::Debug for Closure { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let lambda = self.lambda.unwrap(); + let lambda = self.lambda; write!(f, "closure({lambda:?}, [")?; let len = self.num_captures as usize; let data = self.captures.as_ptr(); @@ -142,9 +136,13 @@ impl std::fmt::Debug for Closure { // TODO(issue#237): GcCell #[repr(C)] pub struct Capture { + /// A captured value. + /// + /// This may point to the `escaped`. pub target: *mut Value, + + /// Data storage for escaped value. pub escaped: Value, - _pinned: PhantomPinned, } static_assertions::const_assert_eq!(size_of::(), 24); @@ -169,6 +167,8 @@ impl std::fmt::Debug for Capture { } /// A data type to represent a coroutine. +/// +/// The scratch_buffer starts from `&Coroutine::locals[Coroutine::num_locals]`. // // TODO(issue#237): GcCell #[repr(C)] @@ -191,9 +191,9 @@ pub struct Coroutine { pub scratch_buffer_len: u16, /// A variable-length list of local variables used in the coroutine. + /// + /// `Capture::target` may point to one of `locals[]`. pub locals: [Value; 32], - - // The scratch_buffer starts from &locals[num_locals]. } static_assertions::const_assert_eq!(align_of::(), 8); @@ -207,21 +207,20 @@ impl Coroutine { error: &Value, ) -> CoroutineStatus { unsafe { - let lambda = (&*(*coroutine).closure).lambda.unwrap(); + let lambda = (*(*coroutine).closure).lambda; let mut args = [promise.into(), result.clone(), error.clone()]; let mut retv = Value::None; let status = lambda( runtime, coroutine as *mut c_void, - args.len(), - args.as_mut_ptr() as *mut Value as *mut crate::llvmir::bridge::Value, - &mut retv as *mut Value as *mut crate::llvmir::bridge::Value, + args.len() as u16, + args.as_mut_ptr(), + &mut retv as *mut Value, ); match status { - STATUS_NORMAL => CoroutineStatus::Done(retv), - STATUS_EXCEPTION => CoroutineStatus::Error(retv), - STATUS_SUSPEND => CoroutineStatus::Suspend, - _ => unreachable!(), + Status::Normal => CoroutineStatus::Done(retv), + Status::Exception => CoroutineStatus::Error(retv), + Status::Suspend => CoroutineStatus::Suspend, } } } @@ -244,7 +243,7 @@ where T: Clone + Into, { fn status(&self) -> Status { - STATUS_NORMAL + Status::Normal } fn value(&self) -> Value { @@ -259,9 +258,9 @@ where { fn status(&self) -> Status { if self.is_ok() { - STATUS_NORMAL + Status::Normal } else { - STATUS_EXCEPTION + Status::Exception } } @@ -272,3 +271,82 @@ where } } } + +/// Lambda function. +/// +/// The actual type of `context` varies depending on usage of the lambda function: +/// +/// * Regular functions: Capture** +/// * Coroutine functions: Coroutine* +/// +pub type Lambda = unsafe extern "C" fn( + runtime: *mut c_void, + context: *mut c_void, + args: u16, + argv: *mut Value, + retv: *mut Value, +) -> Status; + +// See https://www.reddit.com/r/rust/comments/ksfk4j/comment/gifzlhg/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button + +// This function generates a wrapper function for each `host_fn` at compile time. +pub fn into_lambda(host_fn: F) -> Lambda +where + F: Fn(&mut Runtime, &[Value]) -> R + Send + Sync + 'static, + R: Clone + ReturnValue, +{ + debug_assert_eq!(std::mem::size_of::(), 0, "Function must have zero size"); + std::mem::forget(host_fn); + host_fn_wrapper:: +} + +unsafe extern "C" fn host_fn_wrapper( + runtime: *mut c_void, + _context: *mut c_void, + argc: u16, + argv: *mut Value, + retv: *mut Value, +) -> Status +where + F: Fn(&mut Runtime, &[Value]) -> R + Send + Sync + 'static, + R: Clone + ReturnValue, +{ + #[allow(clippy::uninit_assumed_init)] + let host_fn = std::mem::MaybeUninit::::uninit().assume_init(); + let runtime = &mut *(runtime as *mut Runtime); + let args = std::slice::from_raw_parts(argv as *const Value, argc as usize); + // TODO: The return value is copied twice. That's inefficient. + let result = host_fn(runtime, args); + let retv = &mut *retv; + *retv = result.value(); + result.status() +} + +/// The return value type of `Lambda` function. +#[repr(u32)] +pub enum Status { + Normal, + Exception, + Suspend, +} + +static_assertions::const_assert_eq!(size_of::(), 4); + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(C)] +pub struct Promise(u32); + +static_assertions::const_assert_eq!(size_of::(), 4); +static_assertions::const_assert_eq!(align_of::(), 4); + +impl From for Promise { + fn from(value: u32) -> Self { + Self(value) + } +} + +impl From for u32 { + fn from(value: Promise) -> Self { + value.0 + } +} diff --git a/libs/jsruntime/tests/collect_evaluate_tests.js b/libs/jsruntime/tests/collect_evaluate_tests.js index 9b362851..59cf90d0 100644 --- a/libs/jsruntime/tests/collect_evaluate_tests.js +++ b/libs/jsruntime/tests/collect_evaluate_tests.js @@ -32,18 +32,20 @@ Deno.exit(await main(args, options)); async function main(args, options) { function mapValue(value) { switch (value) { + case undefined: + return undefined; case 'undefined': return 'Value::Undefined'; case 'null': return 'Value::Null'; case 'NaN': - return 'f64::NAN'; + return 'Value::Number(f64::NAN)'; case 'Infinity': - return 'f64::INFINITY'; + return 'Value::Number(f64::INFINITY)'; case '-Infinity': - return '-f64::INFINITY'; + return 'Value::Number(-f64::INFINITY)'; default: - return value; + return `Value::from(${value})`; } } diff --git a/libs/jsruntime/tests/evaluate.rs.njk b/libs/jsruntime/tests/evaluate.rs.njk index ab99544b..225debf4 100644 --- a/libs/jsruntime/tests/evaluate.rs.njk +++ b/libs/jsruntime/tests/evaluate.rs.njk @@ -86,26 +86,14 @@ fn {{ test.name }}(optimize: bool, enable_labels: bool) { let src = include_str!("{{ test.filename }}"); let result = evaluate(src, {{ test.module }}, optimize, enable_labels, vec![ {%- for expected in test.expectedValues %} - {%- if expected === "Value::UNDEFINED" %} {{ expected }}, - {%- elif expected === "Value::NULL" %} - {{ expected }}, - {%- else %} - Value::from({{ expected }}), - {%- endif %} {%- endfor %} ]); {%- if test.throws %} assert_matches!(result, Err(actual) => { // Some cases including `f64::NAN` fail in `assert_eq!()`. let actual = format!("{actual:?}"); - {%- if test.throws === "Value::UNDEFINED" %} - let expected = format!("{:?}", {{ test.throws }}); - {%- elif test.throws === "Value::NULL" %} let expected = format!("{:?}", {{ test.throws }}); - {%- else %} - let expected = format!("{:?}", Value::from({{ test.throws }})); - {%- endif %} assert_eq!(actual, expected); }); {%- else %} From c34f7a1318a7c52031a08b4ab530e38c829b770f Mon Sep 17 00:00:00 2001 From: masnagam Date: Sat, 9 Nov 2024 20:32:50 +0900 Subject: [PATCH 3/5] fix(jsruntime): remove libc --- Cargo.lock | 1 - libs/jsruntime/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 835cbbef..7f091425 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -992,7 +992,6 @@ dependencies = [ "duct", "indexmap", "jsparser", - "libc", "logging", "paste", "rustc-hash", diff --git a/libs/jsruntime/Cargo.toml b/libs/jsruntime/Cargo.toml index 27510d43..b235ffd8 100644 --- a/libs/jsruntime/Cargo.toml +++ b/libs/jsruntime/Cargo.toml @@ -12,7 +12,6 @@ bitflags = "2.6.0" bumpalo = "3.16.0" indexmap = "2.6.0" jsparser = { path = "../jsparser", features = ["location"] } -libc = "0.2.162" logging = { path = "../logging" } paste = "1.0.15" rustc-hash = "2.0.0" From 1f50eca2351c183653d94998e925c0f8d32c4f86 Mon Sep 17 00:00:00 2001 From: masnagam Date: Sat, 9 Nov 2024 20:37:56 +0900 Subject: [PATCH 4/5] fix(jsruntime): update the list of source files --- libs/jsruntime/build.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/jsruntime/build.rs b/libs/jsruntime/build.rs index 08ea4806..20649759 100644 --- a/libs/jsruntime/build.rs +++ b/libs/jsruntime/build.rs @@ -6,9 +6,11 @@ use duct::cmd; static CBINDGEN_TOML: &str = "src/llvmir/cbindgen.toml"; static BRIDGE_SOURCE_FILES: &[&str] = &[ - "src/llvmir/mod.rs", + "src/llvmir/bridge.rs", + "src/llvmir/compiler/bridge.rs", "src/llvmir/executor/bridge.rs", "src/llvmir/module/bridge.rs", + "src/types.rs", ]; static LLVM_COMPONENTS: &[&str] = &["core", "orcjit", "x86"]; From 4bb30e0b0dd5e0074121b1381fdd0f76093fc783 Mon Sep 17 00:00:00 2001 From: masnagam Date: Sun, 10 Nov 2024 08:17:35 +0900 Subject: [PATCH 5/5] build(jsruntime): update clean targets --- libs/jsruntime/Makefile | 1 + libs/jsruntime/src/llvmir/Makefile | 5 ++++- libs/jsruntime/tests/Makefile | 6 +++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libs/jsruntime/Makefile b/libs/jsruntime/Makefile index bbe335ad..2684cb4f 100644 --- a/libs/jsruntime/Makefile +++ b/libs/jsruntime/Makefile @@ -15,6 +15,7 @@ codegen: .PHONY: clean clean: @$(MAKE) -s -C src/llvmir clean + @$(MAKE) -s -C tests clean .PHONY: bench bench: diff --git a/libs/jsruntime/src/llvmir/Makefile b/libs/jsruntime/src/llvmir/Makefile index 70104589..4e7da6bb 100644 --- a/libs/jsruntime/src/llvmir/Makefile +++ b/libs/jsruntime/src/llvmir/Makefile @@ -3,6 +3,9 @@ SHELL := $(shell which bash) -eu -o pipefail -c PROJ_DIR := $(realpath ../../../..) TOOLS_BIN := $(PROJ_DIR)/tools/bin +# The following files are generated by cbindgen in the build script. +CBINDGEN_FILES := bridge.hh + CODEGEN_TARGETS := \ compiler/type_holder.cc \ compiler/type_holder.hh \ @@ -13,7 +16,7 @@ codegen: $(CODEGEN_TARGETS) .PHONY: clean clean: - @rm -f $(CODEGEN_TARGETS) + @rm -f $(CBINDGEN_FILES) $(CODEGEN_TARGETS) # Specify --Wno-error=unknown in clang-format in order to avoid errors caused by unknown options # in //.clang-format. clang-format used in CI jobs may be older than the local one. diff --git a/libs/jsruntime/tests/Makefile b/libs/jsruntime/tests/Makefile index 47e2bf1a..55e4cabc 100644 --- a/libs/jsruntime/tests/Makefile +++ b/libs/jsruntime/tests/Makefile @@ -8,7 +8,11 @@ CODEGEN_TARGETS := evaluate.rs EVALUATE_TESTS := $(wildcard scripts/*.js) $(wildcard modules/*.mjs) .PHONY: codegen -codegen: evaluate.rs +codegen: $(CODEGEN_TARGETS) + +.PHONY: clean +clean: + @rm -f $(CODEGEN_TARGETS) evaluate.rs: evaluate.rs.njk $(EVALUATE_TESTS) collect_evaluate_tests.js @echo 'Generating $(abspath $@)...'