🚧 wip: interning Tables
Performs better but the tests are all busted, won't even compile. The instruction decoding change messed that up too. Might roll that back.main
							parent
							
								
									32ddedc066
								
							
						
					
					
						commit
						465ee55183
					
				|  | @ -5,6 +5,7 @@ use crate::{ | |||
| 	string_interner::Interner, | ||||
| 	value::{ | ||||
| 		BogusClosure, | ||||
| 		Table, | ||||
| 		Value, | ||||
| 	}, | ||||
| }; | ||||
|  | @ -59,6 +60,7 @@ pub struct State { | |||
| 	current_instructions: Rc <[u32]>, | ||||
| 	pub upvalues: Vec <Value>, | ||||
| 	pub si: Interner, | ||||
| 	tables: Vec <crate::value::Table>, | ||||
| } | ||||
| 
 | ||||
| fn lw_io_write (l: &mut State, num_args: usize) -> usize { | ||||
|  | @ -114,7 +116,7 @@ fn lw_table_concat (l: &mut State, num_args: usize) -> usize { | |||
| 	assert_eq! (num_args, 2); | ||||
| 	
 | ||||
| 	let s = { | ||||
| 		let t = l.reg (0).as_table ().unwrap ().borrow (); | ||||
| 		let t = &l.tables [l.reg (0).as_table ().unwrap ()]; | ||||
| 		let joiner = l.reg (1).as_str ().unwrap (); | ||||
| 		
 | ||||
| 		let mut s = String::new (); | ||||
|  | @ -135,9 +137,9 @@ fn lw_table_concat (l: &mut State, num_args: usize) -> usize { | |||
| fn lw_table_pack (l: &mut State, num_args: usize) -> usize { | ||||
| 	let mut v = vec! []; | ||||
| 	for i in 0..num_args { | ||||
| 		v.push (l.reg (u8::try_from (i).unwrap ()).clone ()); | ||||
| 		v.push ((Value::from (i + 1), l.reg (u8::try_from (i).unwrap ()).clone ())); | ||||
| 	} | ||||
| 	*l.reg_mut (0) = Value::from_iter (v.into_iter ()); | ||||
| 	*l.reg_mut (0) = l.intern_table (Table::from_iter (v)); | ||||
| 	1 | ||||
| } | ||||
| 
 | ||||
|  | @ -197,17 +199,17 @@ impl State { | |||
| 			current_instructions, | ||||
| 			upvalues, | ||||
| 			si: Default::default (), | ||||
| 			tables: Default::default (), | ||||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	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); | ||||
| 	pub fn new_with_args <I: Iterator <Item = String>> (chunk: Chunk, si: Interner, args: I) -> Self { | ||||
| 		let current_instructions = match chunk.blocks.get (0) { | ||||
| 			Some (x) => Rc::clone (&x.instructions), | ||||
| 			None => Rc::from ([]), | ||||
| 		}; | ||||
| 		
 | ||||
| 		Self { | ||||
| 		let mut that = Self { | ||||
| 			// TODO: Stack is actually supposed to grow to a limit of
 | ||||
| 			// idk 10,000. I thought it was fixed at 256.
 | ||||
| 			registers: vec! [Value::Nil; 256], | ||||
|  | @ -217,9 +219,13 @@ impl State { | |||
| 			debug_print: false, | ||||
| 			chunk, | ||||
| 			current_instructions, | ||||
| 			upvalues, | ||||
| 			upvalues: Default::default (), | ||||
| 			si, | ||||
| 		} | ||||
| 			tables: Default::default (), | ||||
| 		}; | ||||
| 		that.upvalues = that.upvalues_from_args (args); | ||||
| 		
 | ||||
| 		that | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn at_breakpoint (&self, bp: &Breakpoint) -> bool { | ||||
|  | @ -227,40 +233,46 @@ impl State { | |||
| 		frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn upvalues_from_args <I: Iterator <Item = String>> (si: &mut Interner, args: I) -> Vec <Value> | ||||
| 	fn intern_table (&mut self, table: Table) -> Value { | ||||
| 		let t = self.tables.len (); | ||||
| 		self.tables.push (table); | ||||
| 		Value::Table (t) | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn upvalues_from_args <I: Iterator <Item = String>> (&mut self, args: I) -> Vec <Value> | ||||
| 	{ | ||||
| 		let arg = args.map (|s| si.intern (&s)).enumerate (); | ||||
| 		let arg = Value::from_iter (arg.map (|(i, v)| (Value::from (i), Value::String (v)))); | ||||
| 		let arg: Vec <_> = args.map (|s| self.si.intern (&s)).enumerate ().collect (); | ||||
| 		let arg = self.intern_table (Table::from_iter (arg.into_iter ().map (|(i, v)| (Value::from (i), Value::String (v))))); | ||||
| 		
 | ||||
| 		let io: Vec <_> = [ | ||||
| 			("write", Value::RsFunc (lw_io_write)), | ||||
| 		].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); | ||||
| 		].into_iter ().map (|(k, v)| (Value::String (self.si.intern (k)), v)).collect (); | ||||
| 		
 | ||||
| 		let math: Vec <_> = [ | ||||
| 			("sqrt", Value::RsFunc (lw_sqrt)), | ||||
| 		].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); | ||||
| 		].into_iter ().map (|(k, v)| (Value::String (self.si.intern (k)), v)).collect (); | ||||
| 		
 | ||||
| 		let string: Vec <_> = [ | ||||
| 			("format", Value::RsFunc (lw_string_format)), | ||||
| 		].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); | ||||
| 		].into_iter ().map (|(k, v)| (Value::String (self.si.intern (k)), v)).collect (); | ||||
| 		
 | ||||
| 		let table: Vec <_> = [ | ||||
| 			("concat", Value::RsFunc (lw_table_concat)), | ||||
| 			("pack", Value::RsFunc (lw_table_pack)), | ||||
| 		].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); | ||||
| 		].into_iter ().map (|(k, v)| (Value::String (self.si.intern (k)), v)).collect (); | ||||
| 		
 | ||||
| 		let env = [ | ||||
| 		let env: Vec <_> = [ | ||||
| 			("arg", arg), | ||||
| 			("io", Value::from_iter (io.into_iter ())), | ||||
| 			("math", Value::from_iter (math.into_iter ())), | ||||
| 			("io", self.intern_table (Table::from_iter (io))), | ||||
| 			("math", self.intern_table (Table::from_iter (math))), | ||||
| 			("print", Value::RsFunc (lw_print)), | ||||
| 			("string", Value::from_iter (string.into_iter ())), | ||||
| 			("table", Value::from_iter (table.into_iter ())), | ||||
| 			("string", self.intern_table (Table::from_iter (string))), | ||||
| 			("table", self.intern_table (Table::from_iter (table))), | ||||
| 			("tonumber", Value::RsFunc (lw_tonumber)), | ||||
| 		].into_iter ().map (|(k, v)| (si.intern (k), v)); | ||||
| 		].into_iter ().map (|(k, v)| (Value::String (self.si.intern (k)), v)).collect (); | ||||
| 		
 | ||||
| 		vec! [ | ||||
| 			Value::from_iter (env.into_iter ()), | ||||
| 			self.intern_table (Table::from_iter (env)), | ||||
| 		] | ||||
| 	} | ||||
| 	
 | ||||
|  | @ -413,7 +425,7 @@ impl State { | |||
| 		
 | ||||
| 		let val = match &self.registers [self.stack_top.register_offset + usize::from (b)] { | ||||
| 			Value::Nil => panic! ("R[B] must not be nil"), | ||||
| 			Value::Table (t) => t.borrow ().get_str (*key).clone (), | ||||
| 			Value::Table (t) => self.tables [*t].get_str (*key).clone (), | ||||
| 			_ => panic! ("R[B] must be a table"), | ||||
| 		}; | ||||
| 		
 | ||||
|  | @ -422,13 +434,13 @@ impl State { | |||
| 	
 | ||||
| 	fn op_get_table (&mut self, a: u8, b: u8, c: u8) { | ||||
| 		let t = match self.reg (b) { | ||||
| 			Value::Table (t) => t, | ||||
| 			Value::Table (t) => *t, | ||||
| 			_ => panic! ("R[B] must be a table"), | ||||
| 		}; | ||||
| 		
 | ||||
| 		let key = self.reg (c); | ||||
| 		
 | ||||
| 		let val = t.borrow ().get (key.clone ()).clone (); | ||||
| 		let val = self.tables [t].get (key.clone ()).clone (); | ||||
| 		
 | ||||
| 		*self.reg_mut (a) = val; | ||||
| 	} | ||||
|  | @ -482,8 +494,9 @@ impl State { | |||
| 			self.reg_mut (c) | ||||
| 		}.clone (); | ||||
| 		
 | ||||
| 		let mut dst = self.reg (a).as_table () | ||||
| 		.expect ("SetField only works on tables").borrow_mut (); | ||||
| 		let t = self.reg (a).as_table () | ||||
| 		.expect ("SetField only works on tables"); | ||||
| 		let dst = &mut self.tables [t]; | ||||
| 		
 | ||||
| 		dst.insert_str (key, value); | ||||
| 	} | ||||
|  | @ -666,7 +679,7 @@ impl State { | |||
| 					self.upvalues.get (b).unwrap ().clone () | ||||
| 				}; | ||||
| 				
 | ||||
| 				let table = value.as_table ().expect ("GetTabUp only works on tables").borrow (); | ||||
| 				let table = &self.tables [value.as_table ().expect ("GetTabUp only works on tables")]; | ||||
| 				
 | ||||
| 				let key = match self.constants ().get (c).unwrap () { | ||||
| 					Value::String (s) => *s, | ||||
|  | @ -679,7 +692,7 @@ impl State { | |||
| 				let key = i64::try_from (i.c ()).unwrap (); | ||||
| 				
 | ||||
| 				let value = { | ||||
| 					let table = self.reg (i.b ()).as_table ().expect ("GetI only works on tables").borrow (); | ||||
| 					let table = &self.tables [self.reg (i.b ()).as_table ().expect ("GetI only works on tables")]; | ||||
| 					table.get_int (key).clone () | ||||
| 				}; | ||||
| 				
 | ||||
|  | @ -713,7 +726,7 @@ impl State { | |||
| 					Value::Nil => Err (make_step_error ("attempt to get length of a nil value"))?, | ||||
| 					Value::RsFunc (_) => Err (make_step_error ("attempt to get length of a function value"))?, | ||||
| 					Value::String (s) => self.si.get (*s).len ().into (), | ||||
| 					Value::Table (t) => t.borrow ().length ().into (), | ||||
| 					Value::Table (t) => self.tables [*t].length ().into (), | ||||
| 				}; | ||||
| 				
 | ||||
| 				*self.reg_mut (i.a ()) = len; | ||||
|  | @ -784,7 +797,9 @@ impl State { | |||
| 				*self.reg_mut (i.a ()) = x; | ||||
| 			}, | ||||
| 			0x13 => { | ||||
| 				*self.reg_mut (i.a ()) = Value::Table (Default::default ()); | ||||
| 				let t = self.tables.len (); | ||||
| 				self.tables.push (Default::default ()); | ||||
| 				*self.reg_mut (i.a ()) = Value::Table (t); | ||||
| 			}, | ||||
| 			0x33 => { | ||||
| 				*self.reg_mut (i.a ()) = Value::Boolean (! self.reg (i.b ()).is_truthy()); | ||||
|  | @ -879,13 +894,14 @@ impl State { | |||
| 				} | ||||
| 				.clone (); | ||||
| 				
 | ||||
| 				let mut dst = self.reg_mut (i.a ()).as_table ().expect ("SetI only works on tables").borrow_mut (); | ||||
| 				let t = self.reg_mut (i.a ()).as_table ().expect ("SetI only works on tables"); | ||||
| 				let dst = &mut self.tables [t]; | ||||
| 				
 | ||||
| 				dst.insert_int (i64::from (i.b ()), value); | ||||
| 			}, | ||||
| 			0x4e => { | ||||
| 				let a = i.a (); | ||||
| 				let b = i.b (); | ||||
| 				let b = i.b () as usize; | ||||
| 				
 | ||||
| 				if b == 0 { | ||||
| 					panic! ("SetList with b == 0 not implemented"); | ||||
|  | @ -894,11 +910,12 @@ impl State { | |||
| 					panic! ("SetList with k = true not implemented"); | ||||
| 				} | ||||
| 				
 | ||||
| 				let mut dst = self.reg (a).as_table ().expect ("SetList only works on tables").borrow_mut (); | ||||
| 				let t = self.reg (a).as_table ().expect ("SetList only works on tables"); | ||||
| 				let dst = &mut self.tables [t]; | ||||
| 				
 | ||||
| 				for j in 1..=b { | ||||
| 					let src = self.reg (a + j); | ||||
| 					dst.insert_int (i64::from (i.c () + j), src.clone ()); | ||||
| 					let src = self.registers [self.stack_top.register_offset + a as usize + j].clone (); | ||||
| 					dst.insert_int (i64::try_from (i.c () as usize + j).unwrap (), src); | ||||
| 				} | ||||
| 			}, | ||||
| 			0x0f => { | ||||
|  | @ -915,8 +932,8 @@ impl State { | |||
| 				.clone (); | ||||
| 				
 | ||||
| 				let key = self.constants ().get (b).unwrap ().as_str ().expect ("SetTabUp K[B] must be a string"); | ||||
| 				let table = self.upvalues.get_mut (a).unwrap ().as_table ().unwrap (); | ||||
| 				table.borrow_mut ().insert_str (key, value); | ||||
| 				let t = self.upvalues.get_mut (a).unwrap ().as_table ().unwrap (); | ||||
| 				self.tables [t].insert_str (key, value); | ||||
| 			}, | ||||
| 			0x23 => { | ||||
| 				if self.op_sub (i.a (), i.b (), i.c ()) { | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ fn calculate_hash<T: Hash>(t: &T) -> u64 { | |||
| /// and returns the output
 | ||||
| 
 | ||||
| fn run_chunk (vm: &mut State, args: &[&str], chunk: Chunk) -> Vec <Value> { | ||||
| 	vm.upvalues = State::upvalues_from_args(&mut vm.si, args.into_iter ().map (|s| s.to_string ())); | ||||
| 	vm.upvalues = vm.upvalues_from_args(args.into_iter ().map (|s| s.to_string ())); | ||||
| 	vm.set_chunk (chunk); | ||||
| 	vm.execute ().unwrap () | ||||
| } | ||||
|  |  | |||
|  | @ -33,7 +33,7 @@ pub enum Value { | |||
| 	Integer (i64), | ||||
| 	RsFunc (fn (&mut crate::state::State, usize) -> usize), | ||||
| 	String (InternedString), | ||||
| 	Table (Rc <RefCell <Table>>), | ||||
| 	Table (usize), | ||||
| 	
 | ||||
| 	// These are all bogus, I haven't figured out how to implement
 | ||||
| 	// closures yet
 | ||||
|  | @ -53,7 +53,7 @@ impl fmt::Debug for Value { | |||
| 			Value::Integer (x) => write! (f, "{}", x), | ||||
| 			Value::RsFunc (x) => write! (f, "function: {:?}", x), | ||||
| 			Value::String (s) => write! (f, "unimplemented Debug",), | ||||
| 			Value::Table (t) => write! (f, "{:?}", t.borrow ()), | ||||
| 			Value::Table (t) => write! (f, "table"), | ||||
| 			
 | ||||
| 			Value::BogusClosure (x) => write! (f, "{:?}", x.borrow ()), | ||||
| 		} | ||||
|  | @ -76,7 +76,7 @@ impl fmt::Display for Value { | |||
| 			Value::Integer (x) => write! (f, "{}", x), | ||||
| 			Value::RsFunc (x) => write! (f, "function: {:?}", x), | ||||
| 			Value::String (s) => write! (f, "unimplemented Display"), | ||||
| 			Value::Table (t) => write! (f, "table: {:?}", std::rc::Rc::as_ptr (t)), | ||||
| 			Value::Table (t) => write! (f, "table"), | ||||
| 			
 | ||||
| 			Value::BogusClosure (x) => write! (f, "BogusClosure: {:?}", std::rc::Rc::as_ptr (x)), | ||||
| 		} | ||||
|  | @ -125,31 +125,6 @@ impl From <InternedString> for Value { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From <Table> for Value { | ||||
| 	fn from (x: Table) -> Self { | ||||
| 		Self::Table (Rc::new (RefCell::new (x))) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl FromIterator <(Value, Value)> for Value { | ||||
| 	fn from_iter <I: IntoIterator <Item=(Value, Value)>> (i: I) -> Self { | ||||
| 		let table = Table::from_iter (i); | ||||
| 		Self::from (table) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl FromIterator <(InternedString, Value)> for Value { | ||||
| 	fn from_iter <I: IntoIterator <Item=(InternedString, Value)>> (i: I) -> Self { | ||||
| 		Self::from_iter (i.into_iter ().map (|(s, v)| (Value::String (s), v))) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl FromIterator <Value> for Value { | ||||
| 	fn from_iter <I: IntoIterator <Item=Value>> (i: I) -> Self { | ||||
| 		Self::from_iter ((1..).zip (i.into_iter ()).map (|(i, v)| (Value::from (i), v))) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Eq for Value {} | ||||
| 
 | ||||
| impl std::hash::Hash for Value { | ||||
|  | @ -160,14 +135,31 @@ impl std::hash::Hash for Value { | |||
| 		match self { | ||||
| 			// TODO: Weaken to a Lua error
 | ||||
| 			Self::Nil => panic! ("can't hash a nil value"), | ||||
| 			Self::Boolean (x) => x.hash (state), | ||||
| 			Self::Float (x) => x.to_ne_bytes ().hash (state), | ||||
| 			Self::Integer (x) => x.hash (state), | ||||
| 			
 | ||||
| 			Self::RsFunc (x) => x.hash (state), | ||||
| 			Self::Boolean (x) => { | ||||
| 				[0x01].hash (state); | ||||
| 				x.hash (state) | ||||
| 			}, | ||||
| 			Self::Float (x) => { | ||||
| 				[0x02].hash (state); | ||||
| 				x.to_ne_bytes ().hash (state) | ||||
| 			}, | ||||
| 			Self::Integer (x) => { | ||||
| 				[0x03].hash (state); | ||||
| 				x.hash (state) | ||||
| 			}, | ||||
| 			Self::RsFunc (x) => { | ||||
| 				[0x04].hash (state); | ||||
| 				x.hash (state) | ||||
| 			}, | ||||
| 			// TODO: Implement string interning so we don't hash the whole string here
 | ||||
| 			Self::String (x) => x.hash (state), | ||||
| 			Self::Table (x) => Rc::as_ptr (&x).hash (state), | ||||
| 			Self::String (x) => { | ||||
| 				[0x05].hash (state); | ||||
| 				x.hash (state) | ||||
| 			}, | ||||
| 			Self::Table (x) => { | ||||
| 				[0x06].hash (state); | ||||
| 				x.hash (state) | ||||
| 			}, | ||||
| 			
 | ||||
| 			Self::BogusClosure (_) => panic! ("can't hash Bogus values"), | ||||
| 		} | ||||
|  | @ -215,9 +207,9 @@ impl Value { | |||
| 		} | ||||
| 	} | ||||
| 	
 | ||||
| 	pub fn as_table (&self) -> Option <&Rc <RefCell <Table>>> { | ||||
| 	pub fn as_table (&self) -> Option <usize> { | ||||
| 		match self { | ||||
| 			Self::Table (t) => Some (t), | ||||
| 			Self::Table (t) => Some (*t), | ||||
| 			_ => None, | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
							
								
								
									
										4
									
								
								notes.md
								
								
								
								
							
							
						
						
									
										4
									
								
								notes.md
								
								
								
								
							|  | @ -49,7 +49,9 @@ Did absolutely nothing. I couldn't outsmart LLVM. | |||
| 
 | ||||
| ## Remove RefCell | ||||
| 
 | ||||
| (upcoming) | ||||
| Result: Worked well. Dropped from about 3,700 to 3,200 samples. The code got even uglier. | ||||
| 
 | ||||
| Plan: | ||||
| 
 | ||||
| I think the `borrow` and `borrow_mut` calls slow down OP_GETFIELD and OP_SETFIELD. I can remove them if I store all the tables in State directly, replacing `Rc <RefCell <Table>>` with my own ref counting. This might | ||||
| remove a layer of indirection, too. | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 _
						_