Don't store line numbers in ExecutionContext

Line numbers can be obtained from the instructions, based on the current
instruction index. This doesn't reduce memory usage due to padding, but
it simplifies some of the instruction handling logic.

Initially I made an attempt to change the setup for line numbers
entirely: instead of storing absolute lines per instruction, I
implemented a line offset table similar to the one used in Python.
This ended up reducing the size of bytecode files by about 5%, at the
cost of increasing memory usage by about 5%. Due to the added complexity
of said setup, I decided to not make use of it at this time.
parent e1472acd
Pipeline #163231407 passed with stages
in 76 minutes
......@@ -15,7 +15,7 @@ pub struct CompiledCode {
/// The full file path.
pub file: ObjectPointer,
/// The starting line number.
/// The line number the code object is defined on.
pub line: u16,
/// The names of the arguments, as interned string pointers.
......
......@@ -29,9 +29,6 @@ pub struct ExecutionContext {
/// The register to store this context's return value in.
pub return_register: Option<u16>,
/// The current line that is being executed.
pub line: u16,
/// The current global scope.
pub global_scope: GlobalScopePointer,
......@@ -69,7 +66,6 @@ impl ExecutionContext {
parent: None,
instruction_index: 0,
return_register,
line: block.code.line,
global_scope: block.global_scope,
terminate_upon_return: false,
}
......@@ -84,7 +80,6 @@ impl ExecutionContext {
parent: None,
instruction_index: 0,
return_register: None,
line: block.code.line,
global_scope: block.global_scope,
terminate_upon_return: false,
}
......@@ -94,6 +89,19 @@ impl ExecutionContext {
self.code.file
}
pub fn line(&self) -> u16 {
let mut index = self.instruction_index;
// When entering a new call frame, the instruction index stored points
// to the instruction to run _after_ returning; not the one that is
// being run.
if index > 0 {
index -= 1;
}
self.code.instructions[index].line as u16
}
pub fn name(&self) -> ObjectPointer {
self.code.name
}
......
......@@ -9,7 +9,7 @@ pub fn display_panic(process: &RcProcess, message: &str) {
frames.push(format!(
"\"{}\" line {}, in \"{}\"",
context.code.file.string_value().unwrap(),
context.line.to_string(),
context.line().to_string(),
context.code.name.string_value().unwrap()
));
}
......
......@@ -4,7 +4,6 @@ use crate::object_pointer::ObjectPointer;
use crate::object_value;
use crate::process::RcProcess;
use crate::vm::state::RcState;
use std::i64;
/// Produces a stacktrace containing up to N stack frames.
pub fn allocate_stacktrace(
......@@ -34,8 +33,7 @@ pub fn allocate_stacktrace(
for context in contexts {
let file = context.code.file;
let name = context.code.name;
let line = ObjectPointer::integer(i64::from(context.line));
let line = ObjectPointer::integer(i64::from(context.line()));
let tuple = process.allocate(
object_value::array(vec![file, name, line]),
state.array_prototype,
......
......@@ -230,6 +230,7 @@ impl Instruction {
#[cfg(test)]
mod tests {
use super::*;
use std::mem::size_of;
fn new_instruction() -> Instruction {
Instruction::new(InstructionType::SetLiteral, vec![1, 2], 3)
......@@ -273,4 +274,9 @@ mod tests {
assert!(ins.boolean(0));
}
#[test]
fn test_type_size() {
assert_eq!(size_of::<Instruction>(), 32);
}
}
......@@ -1085,8 +1085,6 @@ impl Machine {
context.set_register(reg, res);
}
InstructionType::RunBlock => {
context.line = instruction.line;
let register = instruction.arg(0);
let block_ptr = context.get_register(instruction.arg(1));
let block = block_ptr.block_value()?;
......@@ -1251,8 +1249,6 @@ impl Machine {
InstructionType::Panic => {
let msg = context.get_register(instruction.arg(0));
context.line = instruction.line;
return Err(msg.string_value()?.to_owned_string());
}
InstructionType::Exit => {
......@@ -1607,8 +1603,6 @@ impl Machine {
context.set_register(reg, rec);
}
InstructionType::RunBlockWithReceiver => {
context.line = instruction.line;
let register = instruction.arg(0);
let block_ptr = context.get_register(instruction.arg(1));
let rec_ptr = context.get_register(instruction.arg(2));
......
......@@ -24,7 +24,7 @@ pub fn setup() -> (Machine, Block, RcProcess) {
name,
name,
1,
vec![new_instruction(InstructionType::Return, vec![0])],
vec![Instruction::new(InstructionType::Return, vec![0], 1)],
);
// Reserve enough space for registers/locals for most tests.
......@@ -47,26 +47,3 @@ pub fn setup() -> (Machine, Block, RcProcess) {
(machine, block, process)
}
/// Creates a new instruction.
pub fn new_instruction(
ins_type: InstructionType,
args: Vec<u16>,
) -> Instruction {
Instruction::new(ins_type, args, 1)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::vm::instruction::InstructionType;
#[test]
fn test_new_instruction() {
let ins = new_instruction(InstructionType::SetLiteral, vec![1, 2]);
assert_eq!(ins.instruction_type, InstructionType::SetLiteral);
assert_eq!(ins.arguments, vec![1, 2]);
assert_eq!(ins.line, 1);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment