diff --git a/Cargo.lock b/Cargo.lock index 5bf2a53db3..173e3b7617 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,10 +8,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "bytecode_def" +version = "0.1.0" + [[package]] name = "libjs_rust" version = "0.1.0" dependencies = [ + "bytecode_def", "num-bigint", "num-integer", "num-traits", diff --git a/Cargo.toml b/Cargo.toml index 1fff079f5c..a31dd6f073 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,9 @@ members = [ "Libraries/LibJS/Rust", ] +exclude = [ + "Libraries/LibJS/AsmIntGen", +] resolver = "2" [profile.release] diff --git a/Libraries/LibJS/BytecodeDef/Cargo.toml b/Libraries/LibJS/BytecodeDef/Cargo.toml new file mode 100644 index 0000000000..bff0e85bc0 --- /dev/null +++ b/Libraries/LibJS/BytecodeDef/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "bytecode_def" +version = "0.1.0" +edition = "2021" diff --git a/Libraries/LibJS/BytecodeDef/src/lib.rs b/Libraries/LibJS/BytecodeDef/src/lib.rs new file mode 100644 index 0000000000..a4e1b67e8c --- /dev/null +++ b/Libraries/LibJS/BytecodeDef/src/lib.rs @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2026-present, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +//! Shared Bytecode.def parser and layout computation. +//! +//! Used by both `Libraries/LibJS/Rust/build.rs` (bytecode codegen) and +//! `Libraries/LibJS/AsmIntGen` (assembly interpreter codegen) to ensure a single source +//! of truth for instruction field offsets and sizes. + +use std::collections::HashMap; + +#[derive(Debug, Clone)] +pub struct Field { + pub name: String, + pub ty: String, + pub is_array: bool, +} + +#[derive(Debug, Clone)] +pub struct OpDef { + pub name: String, + pub fields: Vec, + pub is_terminator: bool, +} + +pub struct FieldType { + pub rust_type: &'static str, + pub align: usize, + pub size: usize, + pub kind: &'static str, +} + +impl From<(&'static str, usize, usize, &'static str)> for FieldType { + fn from(v: (&'static str, usize, usize, &'static str)) -> Self { + Self { + rust_type: v.0, + align: v.1, + size: v.2, + kind: v.3, + } + } +} + +/// The alignment of the C++ Instruction base class (`alignas(void*)`). +/// On 64-bit: alignof(void*) = 8. +pub const STRUCT_ALIGN: usize = 8; + +pub fn parse_bytecode_def(content: &str) -> Vec { + let mut ops = Vec::new(); + let mut current: Option = None; + let mut in_op = false; + + for raw_line in content.lines() { + let stripped = raw_line.trim(); + if stripped.is_empty() || stripped.starts_with("//") || stripped.starts_with('#') { + continue; + } + + if stripped.starts_with("op ") { + assert!(!in_op, "Nested op blocks"); + in_op = true; + let rest = stripped.strip_prefix("op ").unwrap().trim(); + let name = if let Some(idx) = rest.find('<') { + rest[..idx].trim().to_string() + } else { + rest.to_string() + }; + current = Some(OpDef { + name, + fields: Vec::new(), + is_terminator: false, + }); + continue; + } + + if stripped == "endop" { + assert!(in_op && current.is_some(), "endop without op"); + ops.push(current.take().unwrap()); + in_op = false; + continue; + } + + if !in_op { + continue; + } + + if stripped.starts_with('@') { + if stripped == "@terminator" { + current.as_mut().unwrap().is_terminator = true; + } + continue; + } + + let (lhs, rhs) = stripped.split_once(':').expect("Malformed field line"); + let field_name = lhs.trim().to_string(); + let mut field_type = rhs.trim().to_string(); + let is_array = field_type.ends_with("[]"); + if is_array { + field_type = field_type[..field_type.len() - 2].trim().to_string(); + } + current.as_mut().unwrap().fields.push(Field { + name: field_name, + ty: field_type, + is_array, + }); + } + assert!(!in_op, "Unclosed op block"); + + // Remove the base "Instruction" definition (not an actual opcode). + ops.retain(|op| op.name != "Instruction"); + ops +} + +pub fn field_type_info(ty: &str) -> FieldType { + match ty { + "bool" => ("bool", 1, 1, "bool"), + "u32" => ("u32", 4, 4, "u32"), + "Operand" => ("Operand", 4, 4, "operand"), + "Optional" => ("Option", 4, 4, "optional_operand"), + "Label" => ("Label", 4, 4, "label"), + "Optional