🚧 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
	
	 _
						_