Skip to content

Commit

Permalink
more refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
pufferfish101007 committed Mar 15, 2024
1 parent 89bd234 commit 89cf262
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 66 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ If you experience runtime stack overflow errors in debug mode, try using the `-O
| 0x00 | float64 | `f64` | a float |
| 0x01 | bool64 | `i64` | an integer - only the least significant bit is used |
| 0x02 | externref string (64 bit) | `i64` | wrapped to a 32 bit pointer to an `externref` value in the `anyref` table |
| 0x03 | int64 | `i64` | an integer. may be too large to convert to a float |

### Memory layout guarantees

Expand Down
13 changes: 7 additions & 6 deletions playground/lib/project-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default async (
framerate: 30,
}
) => {
window.open(URL.createObjectURL(new Blob([wasm_bytes], { type: 'application/wasm' })));
const framerate_wait = Math.round(1000 / framerate);
let assert;
let exit;
Expand Down Expand Up @@ -144,10 +145,10 @@ export default async (
},
runtime: {
looks_say: (ty, val, targetIndex) => {
targetOutput(targetIndex, "say", wasm_val_to_js(ty, val));
targetOutput(targetIndex, "say", wasm_val_to_js(ty, val).toString());
},
looks_think: (ty, val, targetIndex) => {
targetOutput(targetIndex, "think", wasm_val_to_js(ty, val));
targetOutput(targetIndex, "think", wasm_val_to_js(ty, val).toString());
},
operator_equals: (ty1, val1, ty2, val2) => {
if (ty1 === ty2 && val1 === val2) return true;
Expand All @@ -159,12 +160,12 @@ export default async (
},
operator_random: (lower, upper) =>
Math.random() * (upper - lower) + lower,
operator_join: (ty1, val1, ty2, val2) =>
wasm_val_to_js(ty1, val1).toString() +
wasm_val_to_js(ty2, val2).toString(),
operator_join: (str1, str2) =>
str1.toString() +
str2.toString(),
operator_letterof: (idx, ty, val) =>
wasm_val_to_js(ty, val).toString()[idx - 1] ?? "",
operator_length: (ty, val) => wasm_val_to_js(ty, val).toString().length,
operator_length: (str) => str.length,
operator_contains: (ty1, val1, ty2, val2) =>
wasm_val_to_js(ty1, val1)
.toString()
Expand Down
53 changes: 28 additions & 25 deletions src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,6 @@ impl IrBlock {
let expected = expected_inputs.get(i).ok_or(make_hq_bug!(""))?;
let actual = &type_stack.get(i).borrow().clone().unwrap().1;
if !expected.includes(actual) {
dbg!("cast: {:?} -> {:?}, input {}", &actual, &expected, i);
add_cast(i, expected);
}
}
Expand All @@ -446,7 +445,6 @@ impl IrBlock {
type_stack.len()
);
}
dbg!(&type_stack);
for i in 0..expected_inputs.len() {
let expected = expected_inputs.get(i).ok_or(make_hq_bug!(""))?;
let actual = &type_stack
Expand Down Expand Up @@ -519,18 +517,27 @@ pub enum InputType {
}

impl InputType {
fn base_type(&self) -> InputType {
pub fn base_type(&self) -> InputType {
use InputType::*;
// unions of types should be represented with the least restrictive type first, so that casting chooses the less restrictive type to cast to
match self {
Any => Union(Box::new(String), Box::new(Number)).base_type(),
Number => Union(Box::new(Float), Box::new(Integer)).base_type(),
Integer => Union(Box::new(Boolean), Box::new(ConcreteInteger)).base_type(),
Integer => Union(Box::new(ConcreteInteger), Box::new(Boolean)).base_type(),
Union(a, b) => Union(Box::new(a.base_type()), Box::new(b.base_type())),
_ => self.clone(),
}
}

fn includes(&self, other: &Self) -> bool {
pub fn least_restrictive_concrete_type(&self) -> InputType {
use InputType::*;
match self.base_type() {
Union(a, _) => a.least_restrictive_concrete_type(),
a @ _ => a.clone(),

Check failure on line 536 in src/ir.rs

View workflow job for this annotation

GitHub Actions / Lint (clippy)

the `a @ _` pattern can be written as just `a`
}
}

pub fn includes(&self, other: &Self) -> bool {
if self.base_type() == other.base_type() {
true
} else if let InputType::Union(a, b) = self.base_type() {
Expand All @@ -556,7 +563,8 @@ impl IrOpcode {
operator_add | operator_subtract | operator_multiply | operator_divide
| operator_mod | operator_random => vec![Number, Number],
operator_round | operator_mathop { .. } => vec![Number],
looks_say | looks_think | data_setvariableto { .. } => vec![Any],
looks_say | looks_think => vec![Unknown],
data_setvariableto { .. } | data_teevariable { .. } => vec![Unknown],
math_integer { .. }
| math_angle { .. }
| math_whole_number { .. }
Expand All @@ -567,7 +575,7 @@ impl IrOpcode {
| data_variable { .. }
| text { .. } => vec![],
operator_lt | operator_gt => vec![Number, Number],
operator_equals => vec![Any, Any],
operator_equals => vec![Unknown, Unknown],
operator_and | operator_or => vec![Boolean, Boolean],
operator_not => vec![Boolean],
operator_join | operator_contains => vec![String, String],
Expand All @@ -582,7 +590,6 @@ impl IrOpcode {
hq_goto_if { .. } => vec![Boolean],
hq_drop(n) => vec![Any; *n],
hq_cast(from, _to) => vec![from.clone()],
data_teevariable { .. } => vec![Any],
pen_setPenColorToColor
| pen_changePenSizeBy
| pen_setPenSizeTo
Expand Down Expand Up @@ -969,6 +976,7 @@ impl IrBlockVec for Vec<IrBlock> {
steps: &mut IndexMap<(String, String), Step, BuildHasherDefault<FNV1aHasher64>>,
target_id: String,
) -> Result<(), HQError> {
use InputType::*;
let block = blocks.get(&block_id).ok_or(make_hq_bug!(""))?;
match block {
Block::Normal { block_info, .. } => {
Expand Down Expand Up @@ -1325,9 +1333,11 @@ impl IrBlockVec for Vec<IrBlock> {
IrOpcode::hq_cast(InputType::Unknown, InputType::Float), // todo: integer
IrOpcode::math_whole_number { NUM: 1.0 },
IrOpcode::operator_subtract,
IrOpcode::hq_cast(Float, Unknown),
IrOpcode::data_teevariable {
VARIABLE: looper_id.clone(),
},
IrOpcode::hq_cast(Unknown, Float),
IrOpcode::math_whole_number { NUM: 1.0 },
IrOpcode::operator_lt,
]
Expand All @@ -1340,15 +1350,10 @@ impl IrBlockVec for Vec<IrBlock> {
),
)?);
}
for op in [
IrOpcode::math_whole_number { NUM: 1.0 },
IrOpcode::operator_lt,
]
.into_iter()
.chain(condition_opcodes.clone().into_iter())
for op in condition_opcodes.iter()
{
looper_opcodes.push(IrBlock::new_with_stack_no_cast(
op,
op.clone(),
Rc::clone(
&looper_opcodes.last().ok_or(make_hq_bug!(""))?.type_stack,
),
Expand Down Expand Up @@ -1384,7 +1389,12 @@ impl IrBlockVec for Vec<IrBlock> {
steps,
target_id.clone(),
)?;
for op in condition_opcodes.into_iter() {
for op in [
IrOpcode::math_whole_number { NUM: 1.0 },
IrOpcode::operator_lt,
]
.into_iter()
.chain(condition_opcodes.clone().into_iter()) {
opcodes.push(IrBlock::new_with_stack_no_cast(
op,
Rc::clone(&opcodes.last().ok_or(make_hq_bug!(""))?.type_stack),
Expand All @@ -1397,9 +1407,11 @@ impl IrBlockVec for Vec<IrBlock> {
);
vec![
IrOpcode::operator_round,
IrOpcode::hq_cast(Integer, Unknown),
IrOpcode::data_teevariable {
VARIABLE: looper_id,
},
IrOpcode::hq_cast(Integer, Unknown),
IrOpcode::math_number { NUM: 1.0 },
IrOpcode::operator_lt,
IrOpcode::hq_goto_if {
Expand Down Expand Up @@ -1610,15 +1622,6 @@ impl IrBlockVec for Vec<IrBlock> {
.rposition(|b| b.type_stack.len() == type_stack.len() - j)
.ok_or(make_hq_bug!(""))?;
let cast_stack = self.get_type_stack(Some(cast_pos));
dbg!(
&j,
&type_stack.len(),
&self.len(),
&cast_pos,
&cast_type,
&cast_stack,
&self
);
let cast_block = IrBlock::new_with_stack_no_cast(
IrOpcode::hq_cast(
{
Expand Down
105 changes: 70 additions & 35 deletions src/targets/wasm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::ir::{InputType, IrBlock, IrOpcode, IrProject, Step, ThreadContext, ThreadStart};
use crate::ir::{
InputType, IrBlock, IrOpcode, IrProject, Step, ThreadContext, ThreadStart, TypeStackImpl,
};
use crate::sb3::VarVal;
use crate::HQError;
use alloc::collections::BTreeMap;
Expand Down Expand Up @@ -72,12 +74,28 @@ fn instructions(
]
}
}
operator_add => vec![F64Add],
operator_subtract => vec![F64Sub],
operator_add => {
if InputType::Integer.includes(&op.type_stack.get(0).borrow().clone().unwrap().1)
&& InputType::Integer.includes(&op.type_stack.get(1).borrow().clone().unwrap().1)
{
vec![I32Add]
} else {
vec![F64Add]
}
}
operator_subtract => {
if InputType::Integer.includes(&op.type_stack.get(0).borrow().clone().unwrap().1)
&& InputType::Integer.includes(&op.type_stack.get(1).borrow().clone().unwrap().1)
{
vec![I32Sub]
} else {
vec![F64Sub]
}
}
operator_divide => vec![F64Div],
operator_multiply => vec![F64Mul],
operator_mod => vec![Call(func_indices::FMOD)],
operator_round => vec![F64Nearest],
operator_multiply => vec![F64Mul], // todo: int opt
operator_mod => vec![Call(func_indices::FMOD)], // todo: int opt
operator_round => vec![F64Nearest, I32TruncF64S],
math_number { NUM }
| math_integer { NUM }
| math_angle { NUM }
Expand Down Expand Up @@ -967,7 +985,8 @@ fn instructions(
End,
]
}
hq_cast(from, to) => match (from.clone(), to.clone()) {
hq_cast(from, to) => match (from.clone(), to.least_restrictive_concrete_type()) {
// cast from type should always be a concrete type
(String, Float) => vec![Call(func_indices::CAST_PRIMITIVE_STRING_FLOAT)],
(String, Boolean) => vec![Call(func_indices::CAST_PRIMITIVE_STRING_BOOL)],
(String, Unknown) => vec![
Expand All @@ -992,10 +1011,16 @@ fn instructions(
LocalGet(step_func_locals::F64),
I64ReinterpretF64,
],
(ConcreteInteger, Unknown) => vec![
LocalSet(step_func_locals::I64),
I32Const(hq_value_types::INT64),
LocalGet(step_func_locals::I64),
],
(Unknown, String) => vec![Call(func_indices::CAST_ANY_STRING)],
(Unknown, Float) => vec![Call(func_indices::CAST_ANY_FLOAT)],
(Unknown, Boolean) => vec![Call(func_indices::CAST_ANY_BOOL)],
_ => hq_todo!("unimplemented cast: {:?} -> {:?}", to, from),
(Unknown, ConcreteInteger) => vec![Call(func_indices::CAST_ANY_INT)],
_ => hq_todo!("unimplemented cast: {:?} -> {:?} at {:?}", to, from, op),
},
other => hq_todo!("missing WASM impl for {:?}", other),
};
Expand Down Expand Up @@ -1150,10 +1175,11 @@ pub mod func_indices {
pub const CAST_ANY_STRING: u32 = 46;
pub const CAST_ANY_FLOAT: u32 = 47;
pub const CAST_ANY_BOOL: u32 = 48;
pub const TABLE_ADD_STRING: u32 = 49;
pub const SPRITE_UPDATE_PEN_COLOR: u32 = 50;
pub const CAST_ANY_INT: u32 = 49;
pub const TABLE_ADD_STRING: u32 = 50;
pub const SPRITE_UPDATE_PEN_COLOR: u32 = 51;
}
pub const BUILTIN_FUNCS: u32 = 51;
pub const BUILTIN_FUNCS: u32 = 52;
pub const IMPORTED_FUNCS: u32 = 42;

pub mod types {
Expand Down Expand Up @@ -1197,6 +1223,7 @@ pub mod types {
pub const EXTERNREFF64I32_NORESULT: u32 = 36;
pub const F64x3F32x4_NORESULT: u32 = 37;
pub const F64x5F32x4_NORESULT: u32 = 38;
pub const EXTERNREFx2_EXTERNREF: u32 = 39;
}

pub mod table_indices {
Expand All @@ -1208,6 +1235,7 @@ pub mod hq_value_types {
pub const FLOAT64: i32 = 0;
pub const BOOL64: i32 = 1;
pub const EXTERN_STRING_REF64: i32 = 2;
pub const INT64: i32 = 3;
}

// the number of bytes that one step takes up in linear memory
Expand Down Expand Up @@ -1349,6 +1377,7 @@ impl TryFrom<IrProject> for WasmProject {
],
[],
);
types.function([ValType::Ref(RefType::EXTERNREF), ValType::Ref(RefType::EXTERNREF)], [ValType::Ref(RefType::EXTERNREF)]);

imports.import("dbg", "log", EntityType::Function(types::I32I64_NORESULT));
imports.import(
Expand Down Expand Up @@ -1395,7 +1424,7 @@ impl TryFrom<IrProject> for WasmProject {
imports.import(
"runtime",
"operator_join",
EntityType::Function(types::I32I64I32I64_EXTERNREF),
EntityType::Function(types::EXTERNREFx2_EXTERNREF),
);
imports.import(
"runtime",
Expand All @@ -1405,7 +1434,7 @@ impl TryFrom<IrProject> for WasmProject {
imports.import(
"runtime",
"operator_length",
EntityType::Function(types::I32I64_F64),
EntityType::Function(types::EXTERNREF_F64),
);
imports.import(
"runtime",
Expand Down Expand Up @@ -1716,6 +1745,12 @@ impl TryFrom<IrProject> for WasmProject {
any2bool_func.instruction(&Instruction::End);
code.function(&any2bool_func);

functions.function(types::I32I64_I64);
let mut any2int_func = Function::new(vec![]);
any2int_func.instruction(&Instruction::Unreachable);
any2int_func.instruction(&Instruction::End);
code.function(&any2int_func);

functions.function(types::EXTERNREF_I32);
let mut tbl_add_string_func = Function::new(vec![(1, ValType::I32)]);
tbl_add_string_func.instruction(&Instruction::LocalGet(0));
Expand Down Expand Up @@ -2199,28 +2234,6 @@ impl TryFrom<IrProject> for WasmProject {
),
});

tables.table(TableType {
element_type: RefType::EXTERNREF,
minimum: string_consts
.len()
.try_into()
.map_err(|_| make_hq_bug!("string_consts len out of bounds"))?,
maximum: None,
});

let step_indices = (BUILTIN_FUNCS
..(u32::try_from(step_funcs.len())
.map_err(|_| make_hq_bug!("step_funcs length out of bounds"))?
+ BUILTIN_FUNCS))
.collect::<Vec<_>>();
let step_func_indices = Elements::Functions(&step_indices[..]);
elements.active(
Some(table_indices::STEP_FUNCS),
&ConstExpr::i32_const(0),
RefType::FUNCREF,
step_func_indices,
);

globals.global(
GlobalType {
val_type: ValType::I32,
Expand Down Expand Up @@ -2309,6 +2322,28 @@ impl TryFrom<IrProject> for WasmProject {

data.active(0, &ConstExpr::i32_const(0), default_data);

tables.table(TableType {
element_type: RefType::EXTERNREF,
minimum: string_consts
.len()
.try_into()
.map_err(|_| make_hq_bug!("string_consts len out of bounds"))?,
maximum: None,
});

let step_indices = (BUILTIN_FUNCS
..(u32::try_from(step_funcs.len())
.map_err(|_| make_hq_bug!("step_funcs length out of bounds"))?
+ BUILTIN_FUNCS))
.collect::<Vec<_>>();
let step_func_indices = Elements::Functions(&step_indices[..]);
elements.active(
Some(table_indices::STEP_FUNCS),
&ConstExpr::i32_const(0),
RefType::FUNCREF,
step_func_indices,
);

module
.section(&types)
.section(&imports)
Expand Down

0 comments on commit 89cf262

Please sign in to comment.