🚧 wip: decode Instructions lazily
I don't like it. I need to step back and be more methodical about optimizing.main
parent
47d5fe3df1
commit
43a3294c57
|
@ -8,6 +8,10 @@ authors = ["ReactorScram"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lunar_wave_vm = { path = "../lunar_wave_vm" }
|
lunar_wave_vm = { path = "../lunar_wave_vm" }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
lto = "fat"
|
||||||
|
|
||||||
[target.x86_64-unknown-linux-gnu]
|
[target.x86_64-unknown-linux-gnu]
|
||||||
linker = "/usr/bin/clang"
|
linker = "/usr/bin/clang"
|
||||||
# Recommended for flamegraph
|
# Recommended for flamegraph
|
||||||
|
|
|
@ -104,6 +104,68 @@ fn i_sc (buf: [u8; 4]) -> Option <i8> {
|
||||||
i8::try_from (i32::try_from (c).ok ()? - 127).ok ()
|
i8::try_from (i32::try_from (c).ok ()? - 127).ok ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait DecodeInstruction {
|
||||||
|
fn opcode (self) -> u8;
|
||||||
|
|
||||||
|
fn a (self) -> u8;
|
||||||
|
fn ax (self) -> u32;
|
||||||
|
fn b (self) -> u8;
|
||||||
|
fn bx (self) -> u32;
|
||||||
|
fn c (self) -> u8;
|
||||||
|
fn k (self) -> bool;
|
||||||
|
fn sb (self) -> i8;
|
||||||
|
fn sbx (self) -> i32;
|
||||||
|
fn sc (self) -> i8;
|
||||||
|
fn sj (self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DecodeInstruction for u32 {
|
||||||
|
#[inline(always)]
|
||||||
|
fn opcode (self) -> u8 {
|
||||||
|
((self >> 0) & 0x7f) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn a (self) -> u8 {
|
||||||
|
((self >> 7) & 0xff) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ax (self) -> u32 {
|
||||||
|
self >> 7
|
||||||
|
}
|
||||||
|
|
||||||
|
fn b (self) -> u8 {
|
||||||
|
((self >> 16) & 0xff) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bx (self) -> u32 {
|
||||||
|
(self >> 15) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c (self) -> u8 {
|
||||||
|
(self >> 24) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn k (self) -> bool {
|
||||||
|
((self >> 15) & 0x1) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sb (self) -> i8 {
|
||||||
|
((((self >> 16) & 0xff) as i16) - 127) as i8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sbx (self) -> i32 {
|
||||||
|
(self >> 15) as i32 - 65535
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sc (self) -> i8 {
|
||||||
|
(((self >> 24) as i16) - 127) as i8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sj (self) -> i32 {
|
||||||
|
((self >> 7) as i32) - 0xffffff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
|
pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
|
||||||
{
|
{
|
||||||
let opcode = buf [0] & 0x7f;
|
let opcode = buf [0] & 0x7f;
|
||||||
|
@ -151,9 +213,9 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
|
||||||
0x33 => Inst::Not (a, b),
|
0x33 => Inst::Not (a, b),
|
||||||
0x34 => Inst::Len (a, b),
|
0x34 => Inst::Len (a, b),
|
||||||
0x35 => Inst::Concat (a, b),
|
0x35 => Inst::Concat (a, b),
|
||||||
|
0x38 => Inst::Jmp (s_j),
|
||||||
0x3c => Inst::EqK (a, b, k),
|
0x3c => Inst::EqK (a, b, k),
|
||||||
0x3d => Inst::EqI (a, i_sb (buf)?, k),
|
0x3d => Inst::EqI (a, i_sb (buf)?, k),
|
||||||
0x38 => Inst::Jmp (s_j),
|
|
||||||
0x42 => Inst::Test (a, k),
|
0x42 => Inst::Test (a, k),
|
||||||
0x44 => Inst::Call (a, b, c),
|
0x44 => Inst::Call (a, b, c),
|
||||||
0x45 => Inst::TailCall (a, b, c, k),
|
0x45 => Inst::TailCall (a, b, c, k),
|
||||||
|
@ -264,9 +326,11 @@ pub fn parse_block <R: Read> (rdr: &mut R, si: &mut Interner, blocks: &mut Vec <
|
||||||
for _ in 0..inst_count {
|
for _ in 0..inst_count {
|
||||||
let mut buf = [0u8; 4];
|
let mut buf = [0u8; 4];
|
||||||
rdr.read_exact (&mut buf).ok ().unwrap ();
|
rdr.read_exact (&mut buf).ok ().unwrap ();
|
||||||
instructions.push (parse_inst (buf).expect (&format! ("{buf:?}")));
|
instructions.push (u32::from_le_bytes (buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let instructions = Rc::from (instructions);
|
||||||
|
|
||||||
let constant_count = parse_int (rdr).unwrap ();
|
let constant_count = parse_int (rdr).unwrap ();
|
||||||
|
|
||||||
let mut constants = Vec::with_capacity (constant_count as usize);
|
let mut constants = Vec::with_capacity (constant_count as usize);
|
||||||
|
|
|
@ -16,9 +16,9 @@ pub struct Upvalue {
|
||||||
pub kind: u8,
|
pub kind: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Clone, Debug, Default)]
|
#[derive (Clone, Debug)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
pub instructions: Vec <Instruction>,
|
pub instructions: Rc <[u32]>,
|
||||||
pub constants: Vec <Value>,
|
pub constants: Vec <Value>,
|
||||||
pub upvalues: Vec <Upvalue>,
|
pub upvalues: Vec <Upvalue>,
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ pub struct State {
|
||||||
|
|
||||||
pub debug_print: bool,
|
pub debug_print: bool,
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
current_block: Rc <Block>,
|
current_instructions: Rc <[u32]>,
|
||||||
pub upvalues: Vec <Value>,
|
pub upvalues: Vec <Value>,
|
||||||
pub si: Interner,
|
pub si: Interner,
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ pub enum StepError {
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn new (chunk: Chunk, upvalues: Vec <Value>) -> Self {
|
pub fn new (chunk: Chunk, upvalues: Vec <Value>) -> Self {
|
||||||
let current_block = Rc::clone (&chunk.blocks [0]);
|
let current_instructions = Rc::clone (&chunk.blocks [0].instructions);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
// TODO: Stack is actually supposed to grow to a limit of
|
// TODO: Stack is actually supposed to grow to a limit of
|
||||||
|
@ -194,7 +194,7 @@ impl State {
|
||||||
stack_top: Default::default (),
|
stack_top: Default::default (),
|
||||||
debug_print: false,
|
debug_print: false,
|
||||||
chunk,
|
chunk,
|
||||||
current_block,
|
current_instructions,
|
||||||
upvalues,
|
upvalues,
|
||||||
si: Default::default (),
|
si: Default::default (),
|
||||||
}
|
}
|
||||||
|
@ -202,9 +202,9 @@ impl State {
|
||||||
|
|
||||||
pub fn new_with_args <I: Iterator <Item = String>> (chunk: Chunk, mut si: Interner, args: I) -> Self {
|
pub fn new_with_args <I: Iterator <Item = String>> (chunk: Chunk, mut si: Interner, args: I) -> Self {
|
||||||
let upvalues = Self::upvalues_from_args (&mut si, args);
|
let upvalues = Self::upvalues_from_args (&mut si, args);
|
||||||
let current_block = match chunk.blocks.get (0) {
|
let current_instructions = match chunk.blocks.get (0) {
|
||||||
Some (x) => Rc::clone (&x),
|
Some (x) => Rc::clone (&x.instructions),
|
||||||
None => Default::default (),
|
None => Rc::from ([]),
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -216,7 +216,7 @@ impl State {
|
||||||
stack_top: Default::default (),
|
stack_top: Default::default (),
|
||||||
debug_print: false,
|
debug_print: false,
|
||||||
chunk,
|
chunk,
|
||||||
current_block,
|
current_instructions,
|
||||||
upvalues,
|
upvalues,
|
||||||
si,
|
si,
|
||||||
}
|
}
|
||||||
|
@ -403,7 +403,7 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_get_field (&mut self, a: u8, b: u8, c: u8) {
|
fn op_get_field (&mut self, a: u8, b: u8, c: u8) {
|
||||||
let block = &self.current_block;
|
let block = &self.chunk.blocks [self.stack_top.block_idx];
|
||||||
let constants = &block.constants;
|
let constants = &block.constants;
|
||||||
|
|
||||||
let key = match &constants [usize::from (c)] {
|
let key = match &constants [usize::from (c)] {
|
||||||
|
@ -441,6 +441,7 @@ impl State {
|
||||||
// No need for metamethods
|
// No need for metamethods
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
dbg! (&self.stack_top);
|
||||||
panic! ("Not sure how to implement OP_MMBIN for these 2 values {a:?}, {b:?}");
|
panic! ("Not sure how to implement OP_MMBIN for these 2 values {a:?}, {b:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -454,14 +455,17 @@ impl State {
|
||||||
(Value::Integer (b), Value::Integer (c)) => Value::from (b * c),
|
(Value::Integer (b), Value::Integer (c)) => Value::from (b * c),
|
||||||
(Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 * c),
|
(Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 * c),
|
||||||
(Value::Float (b), Value::Integer (c)) => Value::from (b * *c as f64),
|
(Value::Float (b), Value::Integer (c)) => Value::from (b * *c as f64),
|
||||||
_ => return false,
|
(b, c) => {
|
||||||
|
panic! ("OP_MUL unimplemented for {b:?}, {c:?}");
|
||||||
|
return false;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_set_field (&mut self, a: u8, b: u8, c: u8, k: bool) {
|
fn op_set_field (&mut self, a: u8, b: u8, c: u8, k: bool) {
|
||||||
let block = &self.current_block;
|
let block = &self.chunk.blocks [self.stack_top.block_idx];
|
||||||
let constants = &block.constants;
|
let constants = &block.constants;
|
||||||
|
|
||||||
let b = usize::try_from (b).unwrap ();
|
let b = usize::try_from (b).unwrap ();
|
||||||
|
@ -500,25 +504,25 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn constants (&self) -> &[Value] {
|
fn constants (&self) -> &[Value] {
|
||||||
&self.current_block.constants
|
&self.chunk.blocks [self.stack_top.block_idx].constants
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_block_idx (&mut self, block_idx: usize) {
|
fn set_block_idx (&mut self, block_idx: usize) {
|
||||||
self.stack_top.block_idx = block_idx;
|
self.stack_top.block_idx = block_idx;
|
||||||
self.current_block = Rc::clone (&self.chunk.blocks [block_idx]);
|
self.current_instructions = Rc::clone (&self.chunk.blocks [block_idx].instructions);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_stack_top (&mut self, frame: StackFrame) {
|
fn set_stack_top (&mut self, frame: StackFrame) {
|
||||||
self.stack_top = frame;
|
self.stack_top = frame;
|
||||||
self.current_block = match self.chunk.blocks.get (frame.block_idx) {
|
self.current_instructions = match self.chunk.blocks.get (frame.block_idx) {
|
||||||
Some (x) => Rc::clone (&x),
|
Some (x) => Rc::clone (&x.instructions),
|
||||||
None => Default::default (),
|
None => Rc::from ([]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch (&self) -> &Instruction {
|
fn fetch (&self) -> u32 {
|
||||||
match self.current_block.instructions.get (self.stack_top.program_counter) {
|
match self.current_instructions.get (self.stack_top.program_counter) {
|
||||||
Some (x) => x,
|
Some (x) => *x,
|
||||||
None => {
|
None => {
|
||||||
dbg! (&self.stack, &self.stack_top);
|
dbg! (&self.stack, &self.stack_top);
|
||||||
panic! ("program_counter went out of bounds");
|
panic! ("program_counter went out of bounds");
|
||||||
|
@ -532,35 +536,38 @@ impl State {
|
||||||
|
|
||||||
pub fn step (&mut self) -> Result <Option <StepOutput>, StepError>
|
pub fn step (&mut self) -> Result <Option <StepOutput>, StepError>
|
||||||
{
|
{
|
||||||
let instruction = self.fetch ();
|
use crate::loader::DecodeInstruction;
|
||||||
|
|
||||||
|
let i = self.fetch ();
|
||||||
|
|
||||||
let make_step_error = |msg| {
|
let make_step_error = |msg| {
|
||||||
self.make_step_error (msg, &instruction)
|
panic! ("unimplemented {msg}")
|
||||||
};
|
};
|
||||||
|
|
||||||
match *instruction {
|
match i.opcode () {
|
||||||
Instruction::Add (a, b, c) => {
|
0x22 => {
|
||||||
if self.op_add (a, b, c) {
|
if self.op_add (i.a (), i.b (), i.c ()) {
|
||||||
self.stack_top.program_counter += 1;
|
self.stack_top.program_counter += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::AddI (a, b, s_c) => {
|
0x15 => {
|
||||||
let v_b = self.reg (b);
|
let v_b = self.reg (i.b ());
|
||||||
|
let s_c = i.sc ();
|
||||||
|
|
||||||
*self.reg_mut (a) = match v_b {
|
*self.reg_mut (i.a ()) = match v_b {
|
||||||
Value::Integer (v_b) => Value::from (v_b + s_c as i64),
|
Value::Integer (v_b) => Value::from (v_b + s_c as i64),
|
||||||
Value::Float (v_b) => Value::from (v_b + s_c as f64),
|
Value::Float (v_b) => Value::from (v_b + s_c as f64),
|
||||||
x => panic! ("{x}"),
|
x => panic! ("{x}"),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
Instruction::Call (a, b, c) => {
|
0x44 => {
|
||||||
if self.op_call (a, b, c) {
|
if self.op_call (i.a (), i.b (), i.c ()) {
|
||||||
// Skip the PC increment at the bottom
|
// Skip the PC increment at the bottom
|
||||||
return Ok (None);
|
return Ok (None);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::Closure (a, b) => {
|
0x4f => {
|
||||||
let b = usize::try_from (b).unwrap ();
|
let b = usize::try_from (i.bx ()).unwrap ();
|
||||||
|
|
||||||
let idx = self.stack_top.block_idx + b + 1;
|
let idx = self.stack_top.block_idx + b + 1;
|
||||||
let block = &self.chunk.blocks [idx];
|
let block = &self.chunk.blocks [idx];
|
||||||
|
@ -577,39 +584,40 @@ impl State {
|
||||||
new_upvalues.push (val);
|
new_upvalues.push (val);
|
||||||
}
|
}
|
||||||
|
|
||||||
*self.reg_mut (a) = Value::from (BogusClosure {
|
*self.reg_mut (i.a ()) = Value::from (BogusClosure {
|
||||||
idx,
|
idx,
|
||||||
upvalues: new_upvalues,
|
upvalues: new_upvalues,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Instruction::Concat (_a, _b) => {
|
0x35 => {
|
||||||
unimplemented! ("OP_CONCAT")
|
unimplemented! ("OP_CONCAT")
|
||||||
},
|
},
|
||||||
Instruction::Div (a, b, c) => {
|
0x27 => {
|
||||||
if self.op_div (a, b, c) {
|
if self.op_div (i.a (), i.b (), i.c ()) {
|
||||||
self.stack_top.program_counter += 1;
|
self.stack_top.program_counter += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::EqI (a, sb, k_flag) => {
|
0x3d => {
|
||||||
if (self.reg (a).as_int ().unwrap () == sb as i64) != k_flag
|
if (self.reg (i.a ()).as_int ().unwrap () == i.sb () as i64) != i.k ()
|
||||||
{
|
{
|
||||||
self.stack_top.program_counter += 1;
|
self.stack_top.program_counter += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::EqK (a, b, k) => {
|
0x3c => {
|
||||||
let b = usize::from (b);
|
let b = usize::from (i.b ());
|
||||||
|
|
||||||
if (*self.reg (a) == self.constants ()[b]) != k {
|
if (*self.reg (i.a ()) == self.constants ()[b]) != i.k () {
|
||||||
self.stack_top.program_counter += 1;
|
self.stack_top.program_counter += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::ExtraArg (ax) => {
|
0x52 => {
|
||||||
// This is used for NewTable. Maybe it's for reserving
|
// This is used for NewTable. Maybe it's for reserving
|
||||||
// capacity in the array or something?
|
// capacity in the array or something?
|
||||||
|
|
||||||
assert_eq! (ax, 0, "implemented only for ax == 0");
|
assert_eq! (i.ax (), 0, "implemented only for ax == 0");
|
||||||
},
|
},
|
||||||
Instruction::ForLoop (a, bx) => {
|
0x49 => {
|
||||||
|
let a = i.a ();
|
||||||
let mut iter = self.reg (a + 3).as_int ().unwrap ();
|
let mut iter = self.reg (a + 3).as_int ().unwrap ();
|
||||||
iter += 1;
|
iter += 1;
|
||||||
*self.reg_mut (a + 3) = iter.into ();
|
*self.reg_mut (a + 3) = iter.into ();
|
||||||
|
@ -617,28 +625,29 @@ impl State {
|
||||||
let stop = self.reg (a + 1).as_int ().unwrap ();
|
let stop = self.reg (a + 1).as_int ().unwrap ();
|
||||||
|
|
||||||
if iter <= stop {
|
if iter <= stop {
|
||||||
self.stack_top.program_counter -= usize::try_from (bx).unwrap ();
|
self.stack_top.program_counter -= usize::try_from (i.bx ()).unwrap ();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::ForPrep (a, bx) => {
|
0x4a => {
|
||||||
|
let a = i.a ();
|
||||||
let start = self.reg (a).as_int ().unwrap ();
|
let start = self.reg (a).as_int ().unwrap ();
|
||||||
let stop = self.reg (a + 1).as_int ().unwrap ();
|
let stop = self.reg (a + 1).as_int ().unwrap ();
|
||||||
|
|
||||||
if start > stop {
|
if start > stop {
|
||||||
self.stack_top.program_counter += usize::try_from (bx).unwrap () + 1;
|
self.stack_top.program_counter += usize::try_from (i.bx ()).unwrap () + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
*self.reg_mut (a + 3) = start.into ();
|
*self.reg_mut (a + 3) = start.into ();
|
||||||
},
|
},
|
||||||
Instruction::GetField (a, b, c) => {
|
0x0e => {
|
||||||
self.op_get_field (a, b, c);
|
self.op_get_field (i.a (), i.b (), i.c ());
|
||||||
},
|
},
|
||||||
Instruction::GetTable (a, b, c) => {
|
0x0c => {
|
||||||
self.op_get_table (a, b, c);
|
self.op_get_table (i.a (), i.b (), i.c ());
|
||||||
},
|
},
|
||||||
Instruction::GetTabUp (a, b, c) => {
|
0x0b => {
|
||||||
let b = usize::try_from (b).unwrap ();
|
let b = usize::try_from (i.b ()).unwrap ();
|
||||||
let c = usize::try_from (c).unwrap ();
|
let c = usize::try_from (i.c ()).unwrap ();
|
||||||
|
|
||||||
// If we're inside a closure, use its upvalues
|
// If we're inside a closure, use its upvalues
|
||||||
// instead of the chunk's upvalues
|
// instead of the chunk's upvalues
|
||||||
|
@ -664,26 +673,26 @@ impl State {
|
||||||
_ => panic! ("GetTabUp only supports string keys"),
|
_ => panic! ("GetTabUp only supports string keys"),
|
||||||
};
|
};
|
||||||
|
|
||||||
*self.reg_mut (a) = table.get_str (key).clone ();
|
*self.reg_mut (i.a ()) = table.get_str (key).clone ();
|
||||||
},
|
},
|
||||||
Instruction::GetI (a, b, c) => {
|
0x0d => {
|
||||||
let key = i64::try_from (c).unwrap ();
|
let key = i64::try_from (i.c ()).unwrap ();
|
||||||
|
|
||||||
let value = {
|
let value = {
|
||||||
let table = self.reg (b).as_table ().expect ("GetI only works on tables").borrow ();
|
let table = self.reg (i.b ()).as_table ().expect ("GetI only works on tables").borrow ();
|
||||||
table.get_int (key).clone ()
|
table.get_int (key).clone ()
|
||||||
};
|
};
|
||||||
|
|
||||||
*self.reg_mut (a) = value;
|
*self.reg_mut (i.a ()) = value;
|
||||||
},
|
},
|
||||||
Instruction::GetUpVal (a, b) => {
|
0x09 => {
|
||||||
let this_func = self.stack_top.register_offset - 1;
|
let this_func = self.stack_top.register_offset - 1;
|
||||||
let closure = match &self.registers [this_func] {
|
let closure = match &self.registers [this_func] {
|
||||||
Value::BogusClosure (rc) => rc,
|
Value::BogusClosure (rc) => rc,
|
||||||
_ => panic! ("Can't do GetUpVal outside a closure"),
|
_ => panic! ("Can't do GetUpVal outside a closure"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let b = usize::try_from (b).unwrap ();
|
let b = usize::try_from (i.b ()).unwrap ();
|
||||||
|
|
||||||
let upvalue = match closure.borrow ().upvalues.get (b) {
|
let upvalue = match closure.borrow ().upvalues.get (b) {
|
||||||
Some (x) => x.clone (),
|
Some (x) => x.clone (),
|
||||||
|
@ -692,11 +701,11 @@ impl State {
|
||||||
panic! ("Missing upvalue");
|
panic! ("Missing upvalue");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
*self.reg_mut (a) = upvalue;
|
*self.reg_mut (i.a ()) = upvalue;
|
||||||
},
|
},
|
||||||
Instruction::Jmp (s_j) => self.stack_top.program_counter = usize::try_from (i32::try_from (self.stack_top.program_counter).unwrap () + s_j).unwrap (),
|
0x38 => self.stack_top.program_counter = usize::try_from (i32::try_from (self.stack_top.program_counter).unwrap () + i.sj ()).unwrap (),
|
||||||
Instruction::Len (a, b) => {
|
0x34 => {
|
||||||
let len = match self.reg (b) {
|
let len = match self.reg (i.b ()) {
|
||||||
Value::BogusClosure (_) => Err (make_step_error ("attempt to get length of a function value"))?,
|
Value::BogusClosure (_) => Err (make_step_error ("attempt to get length of a function value"))?,
|
||||||
Value::Boolean (_) => Err (make_step_error ("attempt to get length of a boolean value"))?,
|
Value::Boolean (_) => Err (make_step_error ("attempt to get length of a boolean value"))?,
|
||||||
Value::Float (_) => Err (make_step_error ("attempt to get length of a number value"))?,
|
Value::Float (_) => Err (make_step_error ("attempt to get length of a number value"))?,
|
||||||
|
@ -707,60 +716,60 @@ impl State {
|
||||||
Value::Table (t) => t.borrow ().length ().into (),
|
Value::Table (t) => t.borrow ().length ().into (),
|
||||||
};
|
};
|
||||||
|
|
||||||
*self.reg_mut (a) = len;
|
*self.reg_mut (i.a ()) = len;
|
||||||
}
|
}
|
||||||
Instruction::LoadF (a, sbx) => {
|
0x02 => {
|
||||||
*self.reg_mut (a) = Value::Float (sbx as f64);
|
*self.reg_mut (i.a ()) = Value::Float (i.sbx () as f64);
|
||||||
}
|
}
|
||||||
Instruction::LoadFalse (a) => {
|
0x05 => {
|
||||||
*self.reg_mut (a) = false.into ();
|
*self.reg_mut (i.a ()) = false.into ();
|
||||||
},
|
},
|
||||||
Instruction::LoadI (a, sbx) => {
|
0x01 => {
|
||||||
*self.reg_mut (a) = Value::Integer (sbx as i64);
|
*self.reg_mut (i.a ()) = Value::Integer (i.sbx () as i64);
|
||||||
},
|
},
|
||||||
Instruction::LoadK (a, bx) => {
|
0x03 => {
|
||||||
let bx = usize::try_from (bx).unwrap ();
|
let bx = usize::try_from (i.bx ()).unwrap ();
|
||||||
|
|
||||||
*self.reg_mut (a) = self.constants ()[bx].clone ();
|
*self.reg_mut (i.a ()) = self.constants ()[bx].clone ();
|
||||||
},
|
},
|
||||||
Instruction::LoadNil (a) => {
|
0x08 => {
|
||||||
*self.reg_mut (a) = Value::Nil;
|
*self.reg_mut (i.a ()) = Value::Nil;
|
||||||
},
|
},
|
||||||
Instruction::LoadTrue (a) => {
|
0x07 => {
|
||||||
*self.reg_mut (a) = true.into ();
|
*self.reg_mut (i.a ()) = true.into ();
|
||||||
},
|
},
|
||||||
Instruction::MmBin (a, b, c) => {
|
0x2e => {
|
||||||
self.op_mmbin (a, b, c);
|
self.op_mmbin (i.a (), i.b (), i.c ());
|
||||||
},
|
},
|
||||||
Instruction::MmBinI (_a, _s_b, _c, _k) => {
|
0x2f => {
|
||||||
// Ignore
|
// Ignore
|
||||||
},
|
},
|
||||||
Instruction::MmBinK (_a, _b, _c, _k) => {
|
0x30 => {
|
||||||
// Ignore
|
// Ignore
|
||||||
},
|
},
|
||||||
Instruction::ModK (a, b, c) => {
|
0x19 => {
|
||||||
let b = self.reg (b).as_int().unwrap ();
|
let b = self.reg (i.b ()).as_int().unwrap ();
|
||||||
let c = self.constants ()[usize::from (c)].as_int ().unwrap ();
|
let c = self.constants ()[usize::from (i.c ())].as_int ().unwrap ();
|
||||||
|
|
||||||
*self.reg_mut (a) = (b % c).into ();
|
*self.reg_mut (i.a ()) = (b % c).into ();
|
||||||
},
|
},
|
||||||
Instruction::Move (a, b) => {
|
0x00 => {
|
||||||
// If the value in b is deleted instead of duplicated,
|
// If the value in b is deleted instead of duplicated,
|
||||||
// a bunch of tests fail
|
// a bunch of tests fail
|
||||||
|
|
||||||
*self.reg_mut (a) = self.reg (b).clone ();
|
*self.reg_mut (i.a ()) = self.reg (i.b ()).clone ();
|
||||||
},
|
},
|
||||||
Instruction::Mul (a, b, c) => {
|
0x24 => {
|
||||||
// If we handled the mul as a regular int or float,
|
// If we handled the mul as a regular int or float,
|
||||||
// skip the OP_MMBIN that probably comes after this
|
// skip the OP_MMBIN that probably comes after this
|
||||||
|
|
||||||
if self.op_mul (a, b, c) {
|
if self.op_mul (i.a (), i.b (), i.c ()) {
|
||||||
self.stack_top.program_counter += 1;
|
self.stack_top.program_counter += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::MulK (a, b, c) => {
|
0x18 => {
|
||||||
let v_b = self.reg (b);
|
let v_b = self.reg (i.b ());
|
||||||
let v_c = &self.constants ()[usize::from (c)];
|
let v_c = &self.constants ()[usize::from (i.c ())];
|
||||||
|
|
||||||
let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ())
|
let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ())
|
||||||
{
|
{
|
||||||
|
@ -772,24 +781,24 @@ impl State {
|
||||||
Value::from (v_b * v_c)
|
Value::from (v_b * v_c)
|
||||||
};
|
};
|
||||||
|
|
||||||
*self.reg_mut (a) = x;
|
*self.reg_mut (i.a ()) = x;
|
||||||
},
|
},
|
||||||
Instruction::NewTable (a) => {
|
0x13 => {
|
||||||
*self.reg_mut (a) = Value::Table (Default::default ());
|
*self.reg_mut (i.a ()) = Value::Table (Default::default ());
|
||||||
},
|
},
|
||||||
Instruction::Not (a, b) => {
|
0x33 => {
|
||||||
*self.reg_mut (a) = Value::Boolean (! self.reg (b).is_truthy());
|
*self.reg_mut (i.a ()) = Value::Boolean (! self.reg (i.b ()).is_truthy());
|
||||||
}
|
}
|
||||||
Instruction::Return (a, b, _c, k_flag) => {
|
0x46 => {
|
||||||
let a = usize::try_from (a).unwrap ();
|
let a = usize::try_from (i.a ()).unwrap ();
|
||||||
let b = usize::try_from (b).unwrap ();
|
let b = usize::try_from (i.b ()).unwrap ();
|
||||||
|
|
||||||
let popped_frame = self.stack_top;
|
let popped_frame = self.stack_top;
|
||||||
|
|
||||||
// Build closure if needed. No point building if we're
|
// Build closure if needed. No point building if we're
|
||||||
// popping the last frame and exiting the program.
|
// popping the last frame and exiting the program.
|
||||||
|
|
||||||
if k_flag && ! self.stack.is_empty () {
|
if i.k () && ! self.stack.is_empty () {
|
||||||
let closure_idx = match &self.registers [popped_frame.register_offset + a] {
|
let closure_idx = match &self.registers [popped_frame.register_offset + a] {
|
||||||
Value::BogusClosure (rc) => rc.borrow ().idx,
|
Value::BogusClosure (rc) => rc.borrow ().idx,
|
||||||
_ => panic! ("Impossible"),
|
_ => panic! ("Impossible"),
|
||||||
|
@ -837,14 +846,14 @@ impl State {
|
||||||
return Ok (Some (StepOutput::ChunkReturned (self.registers [a..(a + b - 1)].to_vec())));
|
return Ok (Some (StepOutput::ChunkReturned (self.registers [a..(a + b - 1)].to_vec())));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::Return0 => {
|
0x47 => {
|
||||||
let popped_frame = self.stack_top;
|
let popped_frame = self.stack_top;
|
||||||
let x = self.stack.pop ().unwrap ();
|
let x = self.stack.pop ().unwrap ();
|
||||||
self.set_stack_top (x);
|
self.set_stack_top (x);
|
||||||
self.top = popped_frame.register_offset - 1 + 0;
|
self.top = popped_frame.register_offset - 1 + 0;
|
||||||
},
|
},
|
||||||
Instruction::Return1 (a) => {
|
0x48 => {
|
||||||
let a = usize::try_from (a).unwrap ();
|
let a = usize::try_from (i.a ()).unwrap ();
|
||||||
let popped_frame = self.stack_top;
|
let popped_frame = self.stack_top;
|
||||||
|
|
||||||
self.registers [popped_frame.register_offset - 1] = self.register_window ()[a].clone ();
|
self.registers [popped_frame.register_offset - 1] = self.register_window ()[a].clone ();
|
||||||
|
@ -858,42 +867,46 @@ impl State {
|
||||||
|
|
||||||
self.top = popped_frame.register_offset - 1 + 1;
|
self.top = popped_frame.register_offset - 1 + 1;
|
||||||
},
|
},
|
||||||
Instruction::SetField (a, b, c, k_flag) => {
|
0x12 => {
|
||||||
self.op_set_field (a, b, c, k_flag);
|
self.op_set_field (i.a (), i.b (), i.c (), i.k ());
|
||||||
},
|
},
|
||||||
Instruction::SetI (a, b, c, k_flag) => {
|
0x11 => {
|
||||||
let value = if k_flag {
|
let value = if i.k () {
|
||||||
&self.constants ()[usize::from (c)]
|
&self.constants ()[usize::from (i.c ())]
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.reg (c)
|
self.reg (i.c ())
|
||||||
}
|
}
|
||||||
.clone ();
|
.clone ();
|
||||||
|
|
||||||
let mut dst = self.reg_mut (a).as_table ().expect ("SetI only works on tables").borrow_mut ();
|
let mut dst = self.reg_mut (i.a ()).as_table ().expect ("SetI only works on tables").borrow_mut ();
|
||||||
|
|
||||||
dst.insert_int (i64::from (b), value);
|
dst.insert_int (i64::from (i.b ()), value);
|
||||||
},
|
},
|
||||||
Instruction::SetList (a, b, c, k_flag) => {
|
0x4e => {
|
||||||
|
let a = i.a ();
|
||||||
|
let b = i.b ();
|
||||||
|
|
||||||
if b == 0 {
|
if b == 0 {
|
||||||
panic! ("SetList with b == 0 not implemented");
|
panic! ("SetList with b == 0 not implemented");
|
||||||
}
|
}
|
||||||
if k_flag {
|
if i.k () {
|
||||||
panic! ("SetList with k = true not implemented");
|
panic! ("SetList with k = true not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dst = self.reg (a).as_table ().expect ("SetList only works on tables").borrow_mut ();
|
let mut dst = self.reg (a).as_table ().expect ("SetList only works on tables").borrow_mut ();
|
||||||
|
|
||||||
for i in 1..=b {
|
for j in 1..=b {
|
||||||
let src = self.reg (a + i);
|
let src = self.reg (a + j);
|
||||||
dst.insert_int (i64::from (c + i), src.clone ());
|
dst.insert_int (i64::from (i.c () + j), src.clone ());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::SetTabUp (a, b, c, k_flag) => {
|
0x0f => {
|
||||||
let a = usize::try_from (a).unwrap ();
|
let a = usize::try_from (i.a ()).unwrap ();
|
||||||
let b = usize::try_from (b).unwrap ();
|
let b = usize::try_from (i.b ()).unwrap ();
|
||||||
|
|
||||||
let value = if k_flag {
|
let c = i.c ();
|
||||||
|
let value = if i.k () {
|
||||||
&self.constants ()[usize::from (c)]
|
&self.constants ()[usize::from (c)]
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -905,14 +918,14 @@ impl State {
|
||||||
let table = self.upvalues.get_mut (a).unwrap ().as_table ().unwrap ();
|
let table = self.upvalues.get_mut (a).unwrap ().as_table ().unwrap ();
|
||||||
table.borrow_mut ().insert_str (key, value);
|
table.borrow_mut ().insert_str (key, value);
|
||||||
},
|
},
|
||||||
Instruction::Sub (a, b, c) => {
|
0x23 => {
|
||||||
if self.op_sub (a, b, c) {
|
if self.op_sub (i.a (), i.b (), i.c ()) {
|
||||||
self.stack_top.program_counter += 1;
|
self.stack_top.program_counter += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::TailCall (a, b, c, k) => {
|
0x45 => {
|
||||||
let a = usize::from (a);
|
let a = usize::from (i.a ());
|
||||||
assert! (!k, "closing over values in tail calls not implemented");
|
assert! (!i.k (), "closing over values in tail calls not implemented");
|
||||||
|
|
||||||
let offset = self.stack_top.register_offset;
|
let offset = self.stack_top.register_offset;
|
||||||
let value = self.registers [offset + a].take ();
|
let value = self.registers [offset + a].take ();
|
||||||
|
@ -922,7 +935,7 @@ impl State {
|
||||||
|
|
||||||
// Shift inputs into place
|
// Shift inputs into place
|
||||||
|
|
||||||
let b = usize::from (b);
|
let b = usize::from (i.b ());
|
||||||
|
|
||||||
let num_args = if b == 0 {
|
let num_args = if b == 0 {
|
||||||
self.top - a
|
self.top - a
|
||||||
|
@ -948,7 +961,7 @@ impl State {
|
||||||
},
|
},
|
||||||
Value::RsFunc (x) => {
|
Value::RsFunc (x) => {
|
||||||
// Shift inputs into place
|
// Shift inputs into place
|
||||||
let b = usize::from (b);
|
let b = usize::from (i.b ());
|
||||||
for i in (offset)..(offset + b) {
|
for i in (offset)..(offset + b) {
|
||||||
self.registers [i] = self.registers [i + a + 1].take ();
|
self.registers [i] = self.registers [i + a + 1].take ();
|
||||||
}
|
}
|
||||||
|
@ -979,7 +992,7 @@ impl State {
|
||||||
self.set_stack_top (new_frame);
|
self.set_stack_top (new_frame);
|
||||||
|
|
||||||
// Set up top for the next call
|
// Set up top for the next call
|
||||||
if c == 0 {
|
if i.c () == 0 {
|
||||||
self.top = popped_frame.register_offset - 1 + num_results;
|
self.top = popped_frame.register_offset - 1 + num_results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -994,13 +1007,13 @@ impl State {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::Test (a, k_flag) => {
|
0x42 => {
|
||||||
if self.reg (a).is_truthy() != k_flag {
|
if self.reg (i.a ()).is_truthy() != i.k () {
|
||||||
self.stack_top.program_counter += 1;
|
self.stack_top.program_counter += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::UnM (a, b) => {
|
0x31 => {
|
||||||
let v_b = self.reg (b);
|
let v_b = self.reg (i.b ());
|
||||||
|
|
||||||
let x = if let Some (v_b) = v_b.as_int ()
|
let x = if let Some (v_b) = v_b.as_int ()
|
||||||
{
|
{
|
||||||
|
@ -1011,9 +1024,10 @@ impl State {
|
||||||
Value::from (-v_b)
|
Value::from (-v_b)
|
||||||
};
|
};
|
||||||
|
|
||||||
*self.reg_mut (a) = x;
|
*self.reg_mut (i.a ()) = x;
|
||||||
},
|
},
|
||||||
Instruction::VarArgPrep (_) => (),
|
0x51 => (),
|
||||||
|
x => unimplemented! ("{x}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.incr_pc ();
|
self.incr_pc ();
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
use std::hash::Hash;
|
use std::{
|
||||||
|
hash::Hash,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
instruction::Instruction as Inst,
|
instruction::Instruction as Inst,
|
||||||
loader,
|
loader::{
|
||||||
|
self,
|
||||||
|
DecodeInstruction,
|
||||||
|
},
|
||||||
state::{
|
state::{
|
||||||
Block,
|
Block,
|
||||||
Chunk,
|
Chunk,
|
||||||
|
@ -63,7 +69,7 @@ fn bools () {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let mut si = Interner::default ();
|
let mut si = Interner::default ();
|
||||||
|
/*
|
||||||
let chunk = Chunk {
|
let chunk = Chunk {
|
||||||
blocks: vec! [
|
blocks: vec! [
|
||||||
Block {
|
Block {
|
||||||
|
@ -125,6 +131,7 @@ fn bools () {
|
||||||
let actual = run_chunk (&mut vm, &arg, chunk.clone ());
|
let actual = run_chunk (&mut vm, &arg, chunk.clone ());
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -151,7 +158,7 @@ fn floats () {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let mut si = Interner::default ();
|
let mut si = Interner::default ();
|
||||||
|
/*
|
||||||
let block = Block {
|
let block = Block {
|
||||||
instructions: vec! [
|
instructions: vec! [
|
||||||
Inst::VarArgPrep (0),
|
Inst::VarArgPrep (0),
|
||||||
|
@ -186,6 +193,7 @@ fn floats () {
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -194,10 +202,49 @@ fn fma () {
|
||||||
let mut si = Interner::default ();
|
let mut si = Interner::default ();
|
||||||
let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
|
let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
|
||||||
let chunk = crate::loader::parse_chunk (bytecode, &mut si).unwrap ();
|
let chunk = crate::loader::parse_chunk (bytecode, &mut si).unwrap ();
|
||||||
assert_eq! (chunk.blocks.len (), 5);
|
|
||||||
|
|
||||||
|
assert_eq! (chunk.blocks.len (), 5);
|
||||||
assert_eq! (chunk.blocks [3].upvalues.len (), 2);
|
assert_eq! (chunk.blocks [3].upvalues.len (), 2);
|
||||||
|
|
||||||
|
let i = chunk.blocks [1].instructions [0];
|
||||||
|
assert_eq! (i.opcode (), 0x22);
|
||||||
|
assert_eq! (i.a (), 2);
|
||||||
|
assert_eq! (i.b (), 0);
|
||||||
|
assert_eq! (i.c (), 1);
|
||||||
|
|
||||||
|
let i = chunk.blocks [1].instructions [1];
|
||||||
|
assert_eq! (i.opcode (), 0x2e);
|
||||||
|
assert_eq! (i.a (), 0);
|
||||||
|
assert_eq! (i.b (), 1);
|
||||||
|
assert_eq! (i.c (), 6);
|
||||||
|
|
||||||
|
let i = chunk.blocks [2].instructions [0];
|
||||||
|
assert_eq! (i.opcode (), 0x24);
|
||||||
|
assert_eq! (i.a (), 2);
|
||||||
|
assert_eq! (i.b (), 0);
|
||||||
|
assert_eq! (i.c (), 1);
|
||||||
|
|
||||||
|
let i = chunk.blocks [2].instructions [1];
|
||||||
|
assert_eq! (i.opcode (), 0x2e);
|
||||||
|
assert_eq! (i.a (), 0);
|
||||||
|
assert_eq! (i.b (), 1);
|
||||||
|
assert_eq! (i.c (), 8);
|
||||||
|
|
||||||
|
let i = chunk.blocks [3].instructions [2];
|
||||||
|
assert_eq! (i.opcode (), 0x00);
|
||||||
|
assert_eq! (i.a (), 5);
|
||||||
|
assert_eq! (i.b (), 0);
|
||||||
|
|
||||||
|
let i = chunk.blocks [3].instructions [4];
|
||||||
|
assert_eq! (i.opcode (), 0x44);
|
||||||
|
assert_eq! (i.a (), 4);
|
||||||
|
assert_eq! (i.b (), 3);
|
||||||
|
assert_eq! (i.c (), 2);
|
||||||
|
|
||||||
|
let i = chunk.blocks [4].instructions [1];
|
||||||
|
assert_eq! (i.opcode (), 0x01);
|
||||||
|
assert_eq! (i.a (), 1);
|
||||||
|
assert_eq! (i.sbx (), 10);
|
||||||
|
|
||||||
let mut vm = crate::State::new_with_args (chunk, si, vec! ["_exe_name".to_string ()].into_iter ());
|
let mut vm = crate::State::new_with_args (chunk, si, vec! ["_exe_name".to_string ()].into_iter ());
|
||||||
let actual = vm.execute ().unwrap ();
|
let actual = vm.execute ().unwrap ();
|
||||||
|
@ -318,7 +365,15 @@ fn is_93 () {
|
||||||
let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
|
let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
|
||||||
let chunk = loader::parse_chunk (&bc, &mut si).unwrap ();
|
let chunk = loader::parse_chunk (&bc, &mut si).unwrap ();
|
||||||
|
|
||||||
assert_eq! (chunk.blocks [0].instructions [3], Inst::EqK (0, 1, false));
|
let i = chunk.blocks [0].instructions [3];
|
||||||
|
assert_eq! (i.opcode (), 0x3c);
|
||||||
|
assert_eq! (i.a (), 0);
|
||||||
|
assert_eq! (i.b (), 1);
|
||||||
|
assert_eq! (i.k (), false);
|
||||||
|
|
||||||
|
let i = chunk.blocks [0].instructions [4];
|
||||||
|
assert_eq! (i.opcode (), 0x38);
|
||||||
|
assert_eq! (i.sj (), 6);
|
||||||
|
|
||||||
let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter());
|
let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter());
|
||||||
|
|
||||||
|
@ -403,8 +458,6 @@ fn tables_2 () {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tailcall () {
|
fn tailcall () {
|
||||||
use crate::instruction::Instruction;
|
|
||||||
|
|
||||||
let mut si = Interner::default ();
|
let mut si = Interner::default ();
|
||||||
|
|
||||||
let src = br#"
|
let src = br#"
|
||||||
|
@ -414,7 +467,8 @@ fn tailcall () {
|
||||||
let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
|
let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
|
||||||
let chunk = loader::parse_chunk (&bc, &mut si).unwrap ();
|
let chunk = loader::parse_chunk (&bc, &mut si).unwrap ();
|
||||||
|
|
||||||
assert_eq! (chunk.blocks [0].instructions [3], Instruction::TailCall (0, 2, 1, false));
|
// assert_eq! (chunk.blocks [0].instructions [3].opcode (), Instruction::TailCall (0, 2, 1, false));
|
||||||
|
assert_eq! (chunk.blocks [0].instructions [3].opcode (), 0x45);
|
||||||
|
|
||||||
let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter());
|
let mut vm = crate::State::new_with_args (Chunk::default (), si, vec! [].into_iter());
|
||||||
|
|
||||||
|
@ -425,7 +479,7 @@ fn tailcall () {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sizes () {
|
fn rust_stuff () {
|
||||||
// Per https://www.lua.org/doc/jucs05.pdf,
|
// Per https://www.lua.org/doc/jucs05.pdf,
|
||||||
// "The Implementation of Lua 5.0",
|
// "The Implementation of Lua 5.0",
|
||||||
//
|
//
|
||||||
|
@ -481,4 +535,7 @@ fn sizes () {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq! (size_of::<crate::instruction::Instruction> (), 8);
|
assert_eq! (size_of::<crate::instruction::Instruction> (), 8);
|
||||||
|
|
||||||
|
let x = vec! [100, 101, 102, 103];
|
||||||
|
let x: Rc <[u32]> = Rc::from (x);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue