intrinsics pr-01
This commit is contained in:
parent
f126a64099
commit
eb835bafee
@ -591,8 +591,8 @@ fn parse_syscalls(data: &[u8]) -> Result<Vec<SyscallDecl>, LoadError> {
|
|||||||
if pos + module_len > data.len() {
|
if pos + module_len > data.len() {
|
||||||
return Err(LoadError::UnexpectedEof);
|
return Err(LoadError::UnexpectedEof);
|
||||||
}
|
}
|
||||||
let module =
|
let module = std::str::from_utf8(&data[pos..pos + module_len])
|
||||||
std::str::from_utf8(&data[pos..pos + module_len]).map_err(|_| LoadError::InvalidUtf8)?;
|
.map_err(|_| LoadError::InvalidUtf8)?;
|
||||||
pos += module_len;
|
pos += module_len;
|
||||||
|
|
||||||
if pos + 2 > data.len() {
|
if pos + 2 > data.len() {
|
||||||
@ -634,9 +634,11 @@ fn parse_syscalls(data: &[u8]) -> Result<Vec<SyscallDecl>, LoadError> {
|
|||||||
fn validate_module(module: &BytecodeModule) -> Result<(), LoadError> {
|
fn validate_module(module: &BytecodeModule) -> Result<(), LoadError> {
|
||||||
let mut syscall_identities = HashSet::with_capacity(module.syscalls.len());
|
let mut syscall_identities = HashSet::with_capacity(module.syscalls.len());
|
||||||
for syscall in &module.syscalls {
|
for syscall in &module.syscalls {
|
||||||
if !syscall_identities
|
if !syscall_identities.insert((
|
||||||
.insert((syscall.module.clone(), syscall.name.clone(), syscall.version))
|
syscall.module.clone(),
|
||||||
{
|
syscall.name.clone(),
|
||||||
|
syscall.version,
|
||||||
|
)) {
|
||||||
return Err(LoadError::DuplicateSyscallIdentity);
|
return Err(LoadError::DuplicateSyscallIdentity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
use crate::firmware::firmware_state::{AppCrashesStep, FirmwareState, GameRunningStep, HubHomeStep};
|
use crate::firmware::firmware_state::{
|
||||||
|
AppCrashesStep, FirmwareState, GameRunningStep, HubHomeStep,
|
||||||
|
};
|
||||||
use crate::firmware::prometeu_context::PrometeuContext;
|
use crate::firmware::prometeu_context::PrometeuContext;
|
||||||
use prometeu_hal::cartridge::{AppMode, Cartridge};
|
use prometeu_hal::cartridge::{AppMode, Cartridge};
|
||||||
use prometeu_hal::color::Color;
|
use prometeu_hal::color::Color;
|
||||||
|
|||||||
@ -241,11 +241,7 @@ pub enum LoadError {
|
|||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum DeclaredLoadError {
|
pub enum DeclaredLoadError {
|
||||||
/// The `(module, name, version)` triple is not known by the host.
|
/// The `(module, name, version)` triple is not known by the host.
|
||||||
UnknownSyscall {
|
UnknownSyscall { module: String, name: String, version: u16 },
|
||||||
module: String,
|
|
||||||
name: String,
|
|
||||||
version: u16,
|
|
||||||
},
|
|
||||||
/// The cartridge lacks required capabilities for the syscall.
|
/// The cartridge lacks required capabilities for the syscall.
|
||||||
MissingCapability {
|
MissingCapability {
|
||||||
required: CapFlags,
|
required: CapFlags,
|
||||||
|
|||||||
462
crates/console/prometeu-vm/src/builtins.rs
Normal file
462
crates/console/prometeu-vm/src/builtins.rs
Normal file
@ -0,0 +1,462 @@
|
|||||||
|
use prometeu_bytecode::Value;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct BuiltinTypeKey {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub version: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuiltinTypeKey {
|
||||||
|
pub const fn new(name: &'static str, version: u16) -> Self {
|
||||||
|
Self { name, version }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn key(self) -> (&'static str, u16) {
|
||||||
|
(self.name, self.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct BuiltinConstKey {
|
||||||
|
pub target: &'static str,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub version: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuiltinConstKey {
|
||||||
|
pub const fn new(target: &'static str, name: &'static str, version: u16) -> Self {
|
||||||
|
Self { target, name, version }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn key(self) -> (&'static str, &'static str, u16) {
|
||||||
|
(self.target, self.name, self.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct IntrinsicKey {
|
||||||
|
pub owner: &'static str,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub version: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntrinsicKey {
|
||||||
|
pub const fn new(owner: &'static str, name: &'static str, version: u16) -> Self {
|
||||||
|
Self { owner, name, version }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn key(self) -> (&'static str, &'static str, u16) {
|
||||||
|
(self.owner, self.name, self.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum BuiltinScalarType {
|
||||||
|
Int,
|
||||||
|
Float,
|
||||||
|
Bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum AbiType {
|
||||||
|
Scalar(BuiltinScalarType),
|
||||||
|
Builtin(BuiltinTypeKey),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum BuiltinLayoutType {
|
||||||
|
Scalar(BuiltinScalarType),
|
||||||
|
Builtin(BuiltinTypeKey),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum BuiltinTypeShape {
|
||||||
|
Scalar,
|
||||||
|
Aggregate,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct BuiltinFieldMeta {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub start_slot: u16,
|
||||||
|
pub field_type: BuiltinLayoutType,
|
||||||
|
pub flat_slot_width: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct BuiltinTypeMeta {
|
||||||
|
pub id: u32,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub version: u16,
|
||||||
|
pub shape: BuiltinTypeShape,
|
||||||
|
pub fields: &'static [BuiltinFieldMeta],
|
||||||
|
pub flat_slot_layout: &'static [AbiType],
|
||||||
|
pub flat_slot_width: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuiltinTypeMeta {
|
||||||
|
pub const fn key(&self) -> BuiltinTypeKey {
|
||||||
|
BuiltinTypeKey::new(self.name, self.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn field(&self, name: &str) -> Option<&'static BuiltinFieldMeta> {
|
||||||
|
self.fields.iter().find(|field| field.name == name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flatten_layout(&self) -> Vec<AbiType> {
|
||||||
|
let mut out = Vec::with_capacity(self.flat_slot_width as usize);
|
||||||
|
self.flatten_into(&mut out);
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flatten_into(&self, out: &mut Vec<AbiType>) {
|
||||||
|
match self.shape {
|
||||||
|
BuiltinTypeShape::Scalar => out.extend_from_slice(self.flat_slot_layout),
|
||||||
|
BuiltinTypeShape::Aggregate => {
|
||||||
|
for field in self.fields {
|
||||||
|
flatten_layout_type(field.field_type, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum BuiltinConstSlotValue {
|
||||||
|
Int32(i32),
|
||||||
|
Int64(i64),
|
||||||
|
Float(f64),
|
||||||
|
Bool(bool),
|
||||||
|
NominalBuiltin { builtin: BuiltinTypeKey, carrier: i32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type BuiltinConstHook = fn() -> Vec<Value>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum BuiltinConstMaterializer {
|
||||||
|
Direct(&'static [BuiltinConstSlotValue]),
|
||||||
|
Hook(BuiltinConstHook),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct BuiltinConstMeta {
|
||||||
|
pub key: BuiltinConstKey,
|
||||||
|
pub flat_slot_layout: &'static [AbiType],
|
||||||
|
pub flat_slot_width: u16,
|
||||||
|
pub materializer: BuiltinConstMaterializer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuiltinConstMeta {
|
||||||
|
pub fn direct_slots(&self) -> Option<&'static [BuiltinConstSlotValue]> {
|
||||||
|
match self.materializer {
|
||||||
|
BuiltinConstMaterializer::Direct(slots) => Some(slots),
|
||||||
|
BuiltinConstMaterializer::Hook(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum IntrinsicExecutionError {
|
||||||
|
ArityMismatch { expected: usize, got: usize },
|
||||||
|
TypeMismatch { index: usize, expected: AbiType },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type IntrinsicImplementation = fn(&[Value]) -> Result<Vec<Value>, IntrinsicExecutionError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct IntrinsicMeta {
|
||||||
|
pub id: u32,
|
||||||
|
pub owner: &'static str,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub version: u16,
|
||||||
|
pub arg_layout: &'static [AbiType],
|
||||||
|
pub ret_layout: &'static [AbiType],
|
||||||
|
pub deterministic: bool,
|
||||||
|
pub may_allocate: bool,
|
||||||
|
pub implementation: IntrinsicImplementation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntrinsicMeta {
|
||||||
|
pub const fn key(&self) -> IntrinsicKey {
|
||||||
|
IntrinsicKey::new(self.owner, self.name, self.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const COLOR: BuiltinTypeKey = BuiltinTypeKey::new("color", 1);
|
||||||
|
const VEC2: BuiltinTypeKey = BuiltinTypeKey::new("vec2", 1);
|
||||||
|
const PIXEL: BuiltinTypeKey = BuiltinTypeKey::new("pixel", 1);
|
||||||
|
|
||||||
|
const COLOR_LAYOUT: [AbiType; 1] = [AbiType::Builtin(COLOR)];
|
||||||
|
const VEC2_LAYOUT: [AbiType; 2] =
|
||||||
|
[AbiType::Scalar(BuiltinScalarType::Float), AbiType::Scalar(BuiltinScalarType::Float)];
|
||||||
|
const PIXEL_LAYOUT: [AbiType; 3] = [
|
||||||
|
AbiType::Scalar(BuiltinScalarType::Int),
|
||||||
|
AbiType::Scalar(BuiltinScalarType::Int),
|
||||||
|
AbiType::Builtin(COLOR),
|
||||||
|
];
|
||||||
|
|
||||||
|
const VEC2_FIELDS: [BuiltinFieldMeta; 2] = [
|
||||||
|
BuiltinFieldMeta {
|
||||||
|
name: "x",
|
||||||
|
start_slot: 0,
|
||||||
|
field_type: BuiltinLayoutType::Scalar(BuiltinScalarType::Float),
|
||||||
|
flat_slot_width: 1,
|
||||||
|
},
|
||||||
|
BuiltinFieldMeta {
|
||||||
|
name: "y",
|
||||||
|
start_slot: 1,
|
||||||
|
field_type: BuiltinLayoutType::Scalar(BuiltinScalarType::Float),
|
||||||
|
flat_slot_width: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const PIXEL_FIELDS: [BuiltinFieldMeta; 3] = [
|
||||||
|
BuiltinFieldMeta {
|
||||||
|
name: "x",
|
||||||
|
start_slot: 0,
|
||||||
|
field_type: BuiltinLayoutType::Scalar(BuiltinScalarType::Int),
|
||||||
|
flat_slot_width: 1,
|
||||||
|
},
|
||||||
|
BuiltinFieldMeta {
|
||||||
|
name: "y",
|
||||||
|
start_slot: 1,
|
||||||
|
field_type: BuiltinLayoutType::Scalar(BuiltinScalarType::Int),
|
||||||
|
flat_slot_width: 1,
|
||||||
|
},
|
||||||
|
BuiltinFieldMeta {
|
||||||
|
name: "color",
|
||||||
|
start_slot: 2,
|
||||||
|
field_type: BuiltinLayoutType::Builtin(COLOR),
|
||||||
|
flat_slot_width: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const BUILTIN_TYPES: [BuiltinTypeMeta; 3] = [
|
||||||
|
BuiltinTypeMeta {
|
||||||
|
id: 0x0001,
|
||||||
|
name: COLOR.name,
|
||||||
|
version: COLOR.version,
|
||||||
|
shape: BuiltinTypeShape::Scalar,
|
||||||
|
fields: &[],
|
||||||
|
flat_slot_layout: &COLOR_LAYOUT,
|
||||||
|
flat_slot_width: 1,
|
||||||
|
},
|
||||||
|
BuiltinTypeMeta {
|
||||||
|
id: 0x0002,
|
||||||
|
name: VEC2.name,
|
||||||
|
version: VEC2.version,
|
||||||
|
shape: BuiltinTypeShape::Aggregate,
|
||||||
|
fields: &VEC2_FIELDS,
|
||||||
|
flat_slot_layout: &VEC2_LAYOUT,
|
||||||
|
flat_slot_width: 2,
|
||||||
|
},
|
||||||
|
BuiltinTypeMeta {
|
||||||
|
id: 0x0003,
|
||||||
|
name: PIXEL.name,
|
||||||
|
version: PIXEL.version,
|
||||||
|
shape: BuiltinTypeShape::Aggregate,
|
||||||
|
fields: &PIXEL_FIELDS,
|
||||||
|
flat_slot_layout: &PIXEL_LAYOUT,
|
||||||
|
flat_slot_width: 3,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const VEC2_ZERO_LAYOUT: [AbiType; 2] =
|
||||||
|
[AbiType::Scalar(BuiltinScalarType::Float), AbiType::Scalar(BuiltinScalarType::Float)];
|
||||||
|
const VEC2_ZERO_SLOTS: [BuiltinConstSlotValue; 2] =
|
||||||
|
[BuiltinConstSlotValue::Float(0.0), BuiltinConstSlotValue::Float(0.0)];
|
||||||
|
const BUILTIN_CONSTS: [BuiltinConstMeta; 1] = [BuiltinConstMeta {
|
||||||
|
key: BuiltinConstKey::new("vec2", "zero", 1),
|
||||||
|
flat_slot_layout: &VEC2_ZERO_LAYOUT,
|
||||||
|
flat_slot_width: 2,
|
||||||
|
materializer: BuiltinConstMaterializer::Direct(&VEC2_ZERO_SLOTS),
|
||||||
|
}];
|
||||||
|
|
||||||
|
const VEC2_DOT_ARGS: [AbiType; 4] = [
|
||||||
|
AbiType::Scalar(BuiltinScalarType::Float),
|
||||||
|
AbiType::Scalar(BuiltinScalarType::Float),
|
||||||
|
AbiType::Scalar(BuiltinScalarType::Float),
|
||||||
|
AbiType::Scalar(BuiltinScalarType::Float),
|
||||||
|
];
|
||||||
|
const SINGLE_FLOAT_RET: [AbiType; 1] = [AbiType::Scalar(BuiltinScalarType::Float)];
|
||||||
|
const VEC2_LENGTH_ARGS: [AbiType; 2] =
|
||||||
|
[AbiType::Scalar(BuiltinScalarType::Float), AbiType::Scalar(BuiltinScalarType::Float)];
|
||||||
|
const INTRINSICS: [IntrinsicMeta; 2] = [
|
||||||
|
IntrinsicMeta {
|
||||||
|
id: 0x1000,
|
||||||
|
owner: "vec2",
|
||||||
|
name: "dot",
|
||||||
|
version: 1,
|
||||||
|
arg_layout: &VEC2_DOT_ARGS,
|
||||||
|
ret_layout: &SINGLE_FLOAT_RET,
|
||||||
|
deterministic: true,
|
||||||
|
may_allocate: false,
|
||||||
|
implementation: vec2_dot,
|
||||||
|
},
|
||||||
|
IntrinsicMeta {
|
||||||
|
id: 0x1001,
|
||||||
|
owner: "vec2",
|
||||||
|
name: "length",
|
||||||
|
version: 1,
|
||||||
|
arg_layout: &VEC2_LENGTH_ARGS,
|
||||||
|
ret_layout: &SINGLE_FLOAT_RET,
|
||||||
|
deterministic: true,
|
||||||
|
may_allocate: false,
|
||||||
|
implementation: vec2_length,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn lookup_builtin_type(name: &str, version: u16) -> Option<&'static BuiltinTypeMeta> {
|
||||||
|
BUILTIN_TYPES.iter().find(|meta| meta.name == name && meta.version == version)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_builtin_constant(
|
||||||
|
target: &str,
|
||||||
|
name: &str,
|
||||||
|
version: u16,
|
||||||
|
) -> Option<&'static BuiltinConstMeta> {
|
||||||
|
BUILTIN_CONSTS.iter().find(|meta| {
|
||||||
|
meta.key.target == target && meta.key.name == name && meta.key.version == version
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_intrinsic(owner: &str, name: &str, version: u16) -> Option<&'static IntrinsicMeta> {
|
||||||
|
INTRINSICS
|
||||||
|
.iter()
|
||||||
|
.find(|meta| meta.owner == owner && meta.name == name && meta.version == version)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_intrinsic_by_id(id: u32) -> Option<&'static IntrinsicMeta> {
|
||||||
|
INTRINSICS.iter().find(|meta| meta.id == id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flatten_layout_type(layout_type: BuiltinLayoutType, out: &mut Vec<AbiType>) {
|
||||||
|
match layout_type {
|
||||||
|
BuiltinLayoutType::Scalar(scalar) => out.push(AbiType::Scalar(scalar)),
|
||||||
|
BuiltinLayoutType::Builtin(key) => {
|
||||||
|
let meta = lookup_builtin_type(key.name, key.version).unwrap_or_else(|| {
|
||||||
|
panic!("missing builtin metadata for {}@{}", key.name, key.version)
|
||||||
|
});
|
||||||
|
meta.flatten_into(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_float_arg(args: &[Value], index: usize) -> Result<f64, IntrinsicExecutionError> {
|
||||||
|
let value = args
|
||||||
|
.get(index)
|
||||||
|
.ok_or(IntrinsicExecutionError::ArityMismatch { expected: index + 1, got: args.len() })?;
|
||||||
|
value.as_float().ok_or(IntrinsicExecutionError::TypeMismatch {
|
||||||
|
index,
|
||||||
|
expected: AbiType::Scalar(BuiltinScalarType::Float),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vec2_dot(args: &[Value]) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||||
|
if args.len() != 4 {
|
||||||
|
return Err(IntrinsicExecutionError::ArityMismatch { expected: 4, got: args.len() });
|
||||||
|
}
|
||||||
|
let ax = expect_float_arg(args, 0)?;
|
||||||
|
let ay = expect_float_arg(args, 1)?;
|
||||||
|
let bx = expect_float_arg(args, 2)?;
|
||||||
|
let by = expect_float_arg(args, 3)?;
|
||||||
|
Ok(vec![Value::Float((ax * bx) + (ay * by))])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vec2_length(args: &[Value]) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||||
|
if args.len() != 2 {
|
||||||
|
return Err(IntrinsicExecutionError::ArityMismatch { expected: 2, got: args.len() });
|
||||||
|
}
|
||||||
|
let x = expect_float_arg(args, 0)?;
|
||||||
|
let y = expect_float_arg(args, 1)?;
|
||||||
|
Ok(vec![Value::Float((x * x + y * y).sqrt())])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builtin_lookup_uses_canonical_identity() {
|
||||||
|
let color = lookup_builtin_type("color", 1).expect("color builtin must exist");
|
||||||
|
assert_eq!(color.key(), COLOR);
|
||||||
|
assert!(lookup_builtin_type("Color", 1).is_none());
|
||||||
|
assert!(lookup_builtin_type("color", 2).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builtin_layout_flattening_matches_expected_widths() {
|
||||||
|
let color = lookup_builtin_type("color", 1).expect("color builtin must exist");
|
||||||
|
assert_eq!(color.flatten_layout(), vec![AbiType::Builtin(COLOR)]);
|
||||||
|
assert_eq!(color.flat_slot_width, 1);
|
||||||
|
|
||||||
|
let vec2 = lookup_builtin_type("vec2", 1).expect("vec2 builtin must exist");
|
||||||
|
assert_eq!(vec2.flatten_layout(), vec![AbiType::Scalar(BuiltinScalarType::Float); 2]);
|
||||||
|
assert_eq!(vec2.flat_slot_width, 2);
|
||||||
|
|
||||||
|
let pixel = lookup_builtin_type("pixel", 1).expect("pixel builtin must exist");
|
||||||
|
assert_eq!(
|
||||||
|
pixel.flatten_layout(),
|
||||||
|
vec![
|
||||||
|
AbiType::Scalar(BuiltinScalarType::Int),
|
||||||
|
AbiType::Scalar(BuiltinScalarType::Int),
|
||||||
|
AbiType::Builtin(COLOR),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(pixel.flat_slot_width, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builtin_field_offsets_are_stable() {
|
||||||
|
let vec2 = lookup_builtin_type("vec2", 1).expect("vec2 builtin must exist");
|
||||||
|
assert_eq!(vec2.field("x").map(|field| field.start_slot), Some(0));
|
||||||
|
assert_eq!(vec2.field("y").map(|field| field.start_slot), Some(1));
|
||||||
|
|
||||||
|
let pixel = lookup_builtin_type("pixel", 1).expect("pixel builtin must exist");
|
||||||
|
let color_field = pixel.field("color").expect("pixel.color must exist");
|
||||||
|
assert_eq!(color_field.start_slot, 2);
|
||||||
|
assert_eq!(color_field.flat_slot_width, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builtin_constant_registry_is_separate_from_intrinsics() {
|
||||||
|
let zero = lookup_builtin_constant("vec2", "zero", 1).expect("vec2.zero must exist");
|
||||||
|
assert_eq!(zero.key, BuiltinConstKey::new("vec2", "zero", 1));
|
||||||
|
assert_eq!(zero.flat_slot_width, 2);
|
||||||
|
assert_eq!(zero.flat_slot_layout, &VEC2_ZERO_LAYOUT);
|
||||||
|
assert_eq!(zero.direct_slots(), Some(&VEC2_ZERO_SLOTS[..]));
|
||||||
|
assert!(lookup_intrinsic("vec2", "zero", 1).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn intrinsic_metadata_exposes_arg_and_return_layouts() {
|
||||||
|
let dot = lookup_intrinsic("vec2", "dot", 1).expect("vec2.dot must exist");
|
||||||
|
assert_eq!(dot.arg_layout.len(), 4);
|
||||||
|
assert_eq!(dot.ret_layout.len(), 1);
|
||||||
|
assert!(dot.deterministic);
|
||||||
|
assert!(!dot.may_allocate);
|
||||||
|
|
||||||
|
let length = lookup_intrinsic("vec2", "length", 1).expect("vec2.length must exist");
|
||||||
|
assert_eq!(length.arg_layout.len(), 2);
|
||||||
|
assert_eq!(length.ret_layout.len(), 1);
|
||||||
|
assert_eq!(lookup_intrinsic_by_id(length.id).map(|meta| meta.key()), Some(length.key()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn intrinsic_implementations_are_registered_without_syscalls() {
|
||||||
|
let dot = lookup_intrinsic("vec2", "dot", 1).expect("vec2.dot must exist");
|
||||||
|
let result = (dot.implementation)(&[
|
||||||
|
Value::Float(1.0),
|
||||||
|
Value::Float(2.0),
|
||||||
|
Value::Float(3.0),
|
||||||
|
Value::Float(4.0),
|
||||||
|
])
|
||||||
|
.expect("dot implementation must execute");
|
||||||
|
assert_eq!(result, vec![Value::Float(11.0)]);
|
||||||
|
|
||||||
|
let length = lookup_intrinsic("vec2", "length", 1).expect("vec2.length must exist");
|
||||||
|
let result = (length.implementation)(&[Value::Float(3.0), Value::Float(4.0)])
|
||||||
|
.expect("length implementation must execute");
|
||||||
|
assert_eq!(result, vec![Value::Float(5.0)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
mod builtins;
|
||||||
mod call_frame;
|
mod call_frame;
|
||||||
mod local_addressing;
|
mod local_addressing;
|
||||||
// Keep the verifier internal in production builds, but expose it for integration tests
|
// Keep the verifier internal in production builds, but expose it for integration tests
|
||||||
@ -13,6 +14,13 @@ mod verifier;
|
|||||||
mod virtual_machine;
|
mod virtual_machine;
|
||||||
mod vm_init_error;
|
mod vm_init_error;
|
||||||
|
|
||||||
|
pub use builtins::{
|
||||||
|
AbiType, BuiltinConstKey, BuiltinConstMaterializer, BuiltinConstMeta, BuiltinConstSlotValue,
|
||||||
|
BuiltinFieldMeta, BuiltinLayoutType, BuiltinScalarType, BuiltinTypeKey, BuiltinTypeMeta,
|
||||||
|
BuiltinTypeShape, IntrinsicExecutionError, IntrinsicImplementation, IntrinsicKey,
|
||||||
|
IntrinsicMeta, lookup_builtin_constant, lookup_builtin_type, lookup_intrinsic,
|
||||||
|
lookup_intrinsic_by_id,
|
||||||
|
};
|
||||||
pub use prometeu_hal::{HostContext, HostReturn, NativeInterface, SyscallId};
|
pub use prometeu_hal::{HostContext, HostReturn, NativeInterface, SyscallId};
|
||||||
pub use virtual_machine::{BudgetReport, LogicalFrameEndingReason, VirtualMachine};
|
pub use virtual_machine::{BudgetReport, LogicalFrameEndingReason, VirtualMachine};
|
||||||
pub use vm_init_error::{LoaderPatchError, VmInitError};
|
pub use vm_init_error::{LoaderPatchError, VmInitError};
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
use crate::call_frame::CallFrame;
|
use crate::call_frame::CallFrame;
|
||||||
use crate::heap::{CoroutineState, Heap};
|
use crate::heap::{CoroutineState, Heap};
|
||||||
use crate::object::ObjectKind;
|
use crate::object::ObjectKind;
|
||||||
use crate::roots::{visit_value_for_roots, RootVisitor};
|
use crate::roots::{RootVisitor, visit_value_for_roots};
|
||||||
use crate::scheduler::Scheduler;
|
use crate::scheduler::Scheduler;
|
||||||
use crate::verifier::Verifier;
|
use crate::verifier::Verifier;
|
||||||
use crate::vm_init_error::{LoaderPatchError, VmInitError};
|
use crate::vm_init_error::{LoaderPatchError, VmInitError};
|
||||||
use crate::{HostContext, NativeInterface};
|
use crate::{HostContext, NativeInterface};
|
||||||
use prometeu_bytecode::decode_next;
|
|
||||||
use prometeu_bytecode::isa::core::CoreOpCode as OpCode;
|
|
||||||
use prometeu_bytecode::model::BytecodeModule;
|
|
||||||
use prometeu_bytecode::HeapRef;
|
use prometeu_bytecode::HeapRef;
|
||||||
use prometeu_bytecode::ProgramImage;
|
use prometeu_bytecode::ProgramImage;
|
||||||
use prometeu_bytecode::Value;
|
use prometeu_bytecode::Value;
|
||||||
|
use prometeu_bytecode::decode_next;
|
||||||
|
use prometeu_bytecode::isa::core::CoreOpCode as OpCode;
|
||||||
|
use prometeu_bytecode::model::BytecodeModule;
|
||||||
use prometeu_bytecode::{
|
use prometeu_bytecode::{
|
||||||
TrapInfo, TRAP_BAD_RET_SLOTS, TRAP_DIV_ZERO, TRAP_INVALID_FUNC, TRAP_INVALID_SYSCALL,
|
TRAP_BAD_RET_SLOTS, TRAP_DIV_ZERO, TRAP_INVALID_FUNC, TRAP_INVALID_SYSCALL, TRAP_OOB,
|
||||||
TRAP_OOB, TRAP_STACK_UNDERFLOW, TRAP_TYPE,
|
TRAP_STACK_UNDERFLOW, TRAP_TYPE, TrapInfo,
|
||||||
};
|
};
|
||||||
use prometeu_hal::syscalls::caps::NONE;
|
use prometeu_hal::syscalls::caps::NONE;
|
||||||
use prometeu_hal::vm_fault::VmFault;
|
use prometeu_hal::vm_fault::VmFault;
|
||||||
@ -23,11 +23,9 @@ fn patch_module_hostcalls(
|
|||||||
module: &mut BytecodeModule,
|
module: &mut BytecodeModule,
|
||||||
capabilities: prometeu_hal::syscalls::CapFlags,
|
capabilities: prometeu_hal::syscalls::CapFlags,
|
||||||
) -> Result<(), LoaderPatchError> {
|
) -> Result<(), LoaderPatchError> {
|
||||||
let resolved = prometeu_hal::syscalls::resolve_declared_program_syscalls(
|
let resolved =
|
||||||
&module.syscalls,
|
prometeu_hal::syscalls::resolve_declared_program_syscalls(&module.syscalls, capabilities)
|
||||||
capabilities,
|
.map_err(LoaderPatchError::ResolveFailed)?;
|
||||||
)
|
|
||||||
.map_err(LoaderPatchError::ResolveFailed)?;
|
|
||||||
|
|
||||||
let mut used = vec![false; module.syscalls.len()];
|
let mut used = vec![false; module.syscalls.len()];
|
||||||
let mut pc = 0usize;
|
let mut pc = 0usize;
|
||||||
@ -1652,7 +1650,7 @@ mod tests {
|
|||||||
use crate::HostReturn;
|
use crate::HostReturn;
|
||||||
use prometeu_bytecode::model::{BytecodeModule, SyscallDecl};
|
use prometeu_bytecode::model::{BytecodeModule, SyscallDecl};
|
||||||
use prometeu_bytecode::{
|
use prometeu_bytecode::{
|
||||||
assemble, disassemble, FunctionMeta, TRAP_INVALID_LOCAL, TRAP_STACK_UNDERFLOW,
|
FunctionMeta, TRAP_INVALID_LOCAL, TRAP_STACK_UNDERFLOW, assemble, disassemble,
|
||||||
};
|
};
|
||||||
use prometeu_hal::expect_int;
|
use prometeu_hal::expect_int;
|
||||||
|
|
||||||
@ -3028,9 +3026,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
Err(VmInitError::ImageLoadFailed(
|
Err(VmInitError::ImageLoadFailed(prometeu_bytecode::LoadError::MissingSyscallSection))
|
||||||
prometeu_bytecode::LoadError::MissingSyscallSection
|
|
||||||
))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,25 +2,10 @@
|
|||||||
pub enum LoaderPatchError {
|
pub enum LoaderPatchError {
|
||||||
DecodeFailed(prometeu_bytecode::DecodeError),
|
DecodeFailed(prometeu_bytecode::DecodeError),
|
||||||
ResolveFailed(prometeu_hal::syscalls::DeclaredLoadError),
|
ResolveFailed(prometeu_hal::syscalls::DeclaredLoadError),
|
||||||
RawSyscallInPreloadArtifact {
|
RawSyscallInPreloadArtifact { pc: usize, syscall_id: u32 },
|
||||||
pc: usize,
|
HostcallIndexOutOfBounds { pc: usize, sysc_index: u32, syscalls_len: usize },
|
||||||
syscall_id: u32,
|
UnusedSyscallDecl { sysc_index: u32, module: String, name: String, version: u16 },
|
||||||
},
|
HostcallRemaining { pc: usize, sysc_index: u32 },
|
||||||
HostcallIndexOutOfBounds {
|
|
||||||
pc: usize,
|
|
||||||
sysc_index: u32,
|
|
||||||
syscalls_len: usize,
|
|
||||||
},
|
|
||||||
UnusedSyscallDecl {
|
|
||||||
sysc_index: u32,
|
|
||||||
module: String,
|
|
||||||
name: String,
|
|
||||||
version: u16,
|
|
||||||
},
|
|
||||||
HostcallRemaining {
|
|
||||||
pc: usize,
|
|
||||||
sysc_index: u32,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user