Skip to content

Commit

Permalink
Attach a compile server to the runtime. Move related objects there. T…
Browse files Browse the repository at this point in the history
…he server also owns a Simplify (and Refactor), which is used to avoid compiling the same thing twice if subsequent compiles are called. Instead of compiling functions statically, compile them through the server in all places.
  • Loading branch information
Ivorforce committed Jul 16, 2024
1 parent 31c73fe commit 192a4f6
Show file tree
Hide file tree
Showing 14 changed files with 251 additions and 226 deletions.
4 changes: 2 additions & 2 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ pub mod opcode;
pub mod disassembler;
pub mod data;
pub mod runtime;
pub mod data_layout;
pub mod compile;
mod tests;
mod data_layout;
mod compile;
13 changes: 7 additions & 6 deletions src/interpreter/builtins/vm.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::rc::Rc;
use std::path::PathBuf;
use std::rc::Rc;

use crate::error::RResult;
use crate::interpreter::compile::function_compiler::InlineFunction;
use crate::interpreter::opcode::{OpCode, Primitive};
Expand All @@ -18,30 +19,30 @@ pub fn load(runtime: &mut Runtime) -> RResult<()> {
runtime.get_or_load_module(&module_name("core"))?;

for function in runtime.source.module_by_name[&module_name("core.debug")].explicit_functions(&runtime.source) {
runtime.function_inlines.insert(Rc::clone(function), match function.declared_representation.name.as_str() {
runtime.compile_server.function_inlines.insert(Rc::clone(function), match function.declared_representation.name.as_str() {
"_write_line" => inline_fn_push(OpCode::PRINT),
"_exit_with_error" => inline_fn_push(OpCode::PANIC),
_ => continue,
});
}

for function in runtime.source.module_by_name[&module_name("core.transpilation")].explicit_functions(&runtime.source) {
runtime.function_inlines.insert(Rc::clone(function), match function.declared_representation.name.as_str() {
runtime.compile_server.function_inlines.insert(Rc::clone(function), match function.declared_representation.name.as_str() {
"add" => inline_fn_push(OpCode::TRANSPILE_ADD),
_ => continue,
});
}

for function in runtime.source.module_by_name[&module_name("core.bool")].explicit_functions(&runtime.source) {
runtime.function_inlines.insert(Rc::clone(function), match function.declared_representation.name.as_str() {
runtime.compile_server.function_inlines.insert(Rc::clone(function), match function.declared_representation.name.as_str() {
"true" => inline_fn_push_with_u8(OpCode::LOAD8, true as u8),
"false" => inline_fn_push_with_u8(OpCode::LOAD8, false as u8),
_ => continue,
});
}

for function in runtime.source.module_by_name[&module_name("core.strings")].explicit_functions(&runtime.source) {
runtime.function_inlines.insert(Rc::clone(function), match function.declared_representation.name.as_str() {
runtime.compile_server.function_inlines.insert(Rc::clone(function), match function.declared_representation.name.as_str() {
"add" => inline_fn_push(OpCode::ADD_STRING),
_ => continue,
});
Expand All @@ -56,7 +57,7 @@ pub fn load(runtime: &mut Runtime) -> RResult<()> {
continue;
};

runtime.function_inlines.insert(Rc::clone(function), match descriptor {
runtime.compile_server.function_inlines.insert(Rc::clone(function), match descriptor {
FunctionLogicDescriptor::Stub => todo!(),
FunctionLogicDescriptor::Clone(type_) => {
if type_ == &TypeProto::unit_struct(&runtime.traits.as_ref().unwrap().String) {
Expand Down
3 changes: 2 additions & 1 deletion src/interpreter/compile.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod function_compiler;
mod compile_server;
pub mod compile_server;
pub mod function_descriptor_compiler;
99 changes: 99 additions & 0 deletions src/interpreter/compile/compile_server.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,102 @@
use std::collections::HashMap;
use std::rc::Rc;

use uuid::Uuid;

use crate::error::{RResult, RuntimeError};
use crate::interpreter::chunks::Chunk;
use crate::interpreter::compile::function_compiler::{compile_function, InlineFunction};
use crate::interpreter::compile::function_descriptor_compiler::compile_descriptor;
use crate::interpreter::data_layout::{create_data_layout, DataLayout};
use crate::program::functions::{FunctionHead, FunctionLogic};
use crate::program::traits::StructInfo;
use crate::refactor::Refactor;
use crate::refactor::simplify::Simplify;
use crate::source::Source;

pub struct CompileServer {
pub simplify: Simplify,

// These are optimized for running and may not reflect the source code itself.
// They are also only loaded on demand.
pub function_evaluators: HashMap<Uuid, Rc<Chunk>>,
// TODO We'll need these only in the future when we compile functions to constants.
// pub global_assignments: HashMap<Uuid, Value>,
pub function_inlines: HashMap<Rc<FunctionHead>, InlineFunction>,
pub data_layouts: HashMap<Rc<StructInfo>, Rc<DataLayout>>,
}

impl CompileServer {
pub fn new() -> CompileServer {
CompileServer {
simplify: Simplify {
refactor: Refactor::new(),
inline: true,
trim_locals: true,
monomorphize: true,
},
function_evaluators: Default::default(),
function_inlines: Default::default(),
data_layouts: Default::default(),
}
}

pub fn compile_deep(&mut self, source: &Source, function: &Rc<FunctionHead>) -> RResult<Rc<Chunk>> {
let FunctionLogic::Implementation(implementation) = source.fn_logic[function].clone() else {
return Err(RuntimeError::error("main! function was somehow internal.").to_array());
};

self.simplify.refactor.add(implementation);

self.simplify.run(source);

let needed_functions = self.simplify.refactor.gather_needed_functions(source);
let fn_logic = self.simplify.refactor.fn_logic.clone();

let mut errors = vec![];

for function in needed_functions {
match &fn_logic[&function] {
FunctionLogic::Descriptor(d) => {
if self.function_inlines.contains_key(&function) || self.function_evaluators.contains_key(&function.function_id) {
continue
}

compile_descriptor(&function, d, self);
}
FunctionLogic::Implementation(implementation) => {
match compile_function(self, implementation) {
Ok(compiled) => drop(self.function_evaluators.insert(function.function_id, compiled)),
Err(err) => errors.extend(err),
};
}
}
}

let FunctionLogic::Implementation(implementation) = &fn_logic[function] else {
errors.push(RuntimeError::error("main! function was somehow internal after refactor."));
return Err(errors);
};

match compile_function(self, implementation) {
Ok(compiled) => {
if !errors.is_empty() { Err(errors) }
else { Ok(compiled) }
},
Err(err) => {
errors.extend(err);
Err(errors)
},
}
}

pub fn get_data_layout(&mut self, struct_info: &Rc<StructInfo>) -> Rc<DataLayout> {
if let Some(layout) = self.data_layouts.get(struct_info) {
return Rc::clone(layout)
}

let layout = create_data_layout(Rc::clone(struct_info));
self.data_layouts.insert(Rc::clone(struct_info), Rc::clone(&layout));
layout
}
}
147 changes: 8 additions & 139 deletions src/interpreter/compile/function_compiler.rs
Original file line number Diff line number Diff line change
@@ -1,85 +1,29 @@
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::mem::transmute;
use std::rc::Rc;

use itertools::Itertools;

use crate::error::{RResult, RuntimeError};
use crate::error::RResult;
use crate::interpreter::chunks::Chunk;
use crate::interpreter::data::{string_to_ptr, uuid_to_ptr, Value};
use crate::interpreter::compile::compile_server::CompileServer;
use crate::interpreter::data::{string_to_ptr, Value};
use crate::interpreter::opcode::OpCode;
use crate::interpreter::runtime::Runtime;
use crate::program::allocation::ObjectReference;
use crate::program::expression_tree::{ExpressionID, ExpressionOperation};
use crate::program::functions::{FunctionHead, FunctionImplementation, FunctionLogic, FunctionLogicDescriptor};
use crate::refactor::Refactor;
use crate::refactor::simplify::Simplify;
use crate::transpiler;
use crate::program::functions::FunctionImplementation;

pub type InlineFunction = Rc<dyn Fn(&mut FunctionCompiler, &ExpressionID) -> RResult<()>>;

pub struct FunctionCompiler<'a> {
pub runtime: &'a Runtime,
pub compile_server: &'a CompileServer,
pub implementation: &'a FunctionImplementation,
pub chunk: Chunk,
pub alloced_locals: Vec<Rc<ObjectReference>>,
}

pub fn compile_deep(runtime: &mut Runtime, function: &Rc<FunctionHead>) -> RResult<Rc<Chunk>> {
let FunctionLogic::Implementation(implementation) = runtime.source.fn_logic[function].clone() else {
return Err(RuntimeError::error("main! function was somehow internal.").to_array());
};

let mut refactor = Refactor::new();
refactor.add(implementation);

let mut simplify = Simplify::new(&mut refactor, &transpiler::Config::default());
simplify.run(runtime);

let needed_functions = refactor.gather_needed_functions(runtime);
let fn_logic = refactor.fn_logic;

let mut errors = vec![];

for function in needed_functions {
match &fn_logic[&function] {
FunctionLogic::Descriptor(d) => {
if runtime.function_inlines.contains_key(&function) || runtime.function_evaluators.contains_key(&function.function_id) {
continue
}

compile_descriptor(&function, d, runtime);
}
FunctionLogic::Implementation(implementation) => {
match compile_function(runtime, implementation) {
Ok(compiled) => drop(runtime.function_evaluators.insert(function.function_id, compiled)),
Err(err) => errors.extend(err),
};
}
}
}

let FunctionLogic::Implementation(implementation) = &fn_logic[function] else {
errors.push(RuntimeError::error("main! function was somehow internal after refactor."));
return Err(errors);
};

match compile_function(runtime, implementation) {
Ok(compiled) => {
if !errors.is_empty() { Err(errors) }
else { Ok(compiled) }
},
Err(err) => {
errors.extend(err);
Err(errors)
},
}
}

fn compile_function(runtime: &mut Runtime, implementation: &FunctionImplementation) -> RResult<Rc<Chunk>> {
pub fn compile_function(compile_server: &CompileServer, implementation: &FunctionImplementation) -> RResult<Rc<Chunk>> {
let mut compiler = FunctionCompiler {
runtime,
compile_server,
implementation,
chunk: Chunk::new(),
alloced_locals: vec![],
Expand Down Expand Up @@ -162,7 +106,7 @@ impl FunctionCompiler<'_> {
self.compile_return();
},
ExpressionOperation::FunctionCall(function_binding) => {
if let Some(inline_fn) = self.runtime.function_inlines.get(&function_binding.function) {
if let Some(inline_fn) = self.compile_server.function_inlines.get(&function_binding.function) {
inline_fn(self, expression)?;
}
else {
Expand Down Expand Up @@ -221,78 +165,3 @@ impl FunctionCompiler<'_> {
i32::try_from(self.alloced_locals.iter().position(|l| l == object).unwrap()).unwrap() - i32::try_from(self.implementation.head.interface.parameters.len()).unwrap()
}
}

pub fn compile_descriptor(function: &Rc<FunctionHead>, descriptor: &FunctionLogicDescriptor, runtime: &mut Runtime) {
match descriptor {
FunctionLogicDescriptor::Stub => todo!("{:?}", function),
FunctionLogicDescriptor::Clone(_) => todo!("{:?}", function),
FunctionLogicDescriptor::TraitProvider(trait_) => {
let uuid = trait_.id;
runtime.function_inlines.insert(Rc::clone(function), Rc::new(move |compiler, expression| {
unsafe { compiler.chunk.constants.push(Value { ptr: uuid_to_ptr(uuid) }); }
compiler.chunk.push_with_u32(OpCode::LOAD_CONSTANT_32, u32::try_from(compiler.chunk.constants.len() - 1).unwrap());
Ok(())
}));
},
FunctionLogicDescriptor::FunctionProvider(f) => {
let uuid = f.function_id;
runtime.function_inlines.insert(Rc::clone(function), Rc::new(move |compiler, expression| {
unsafe { compiler.chunk.constants.push(Value { ptr: uuid_to_ptr(uuid) }); }
compiler.chunk.push_with_u32(OpCode::LOAD_CONSTANT_32, u32::try_from(compiler.chunk.constants.len() - 1).unwrap());
Ok(())
}));
}
FunctionLogicDescriptor::PrimitiveOperation { .. } => todo!("{:?}", descriptor),
FunctionLogicDescriptor::Constructor(struct_info) => {
let data_layout = runtime.get_data_layout(struct_info);

runtime.function_inlines.insert(Rc::clone(function), Rc::new(move |compiler, expression| {
let arguments = &compiler.implementation.expression_tree.children[&expression];
assert_eq!(arguments.len(), data_layout.fields.len() + 1);

compiler.chunk.push_with_u32(OpCode::ALLOC_32, u32::try_from(data_layout.fields.len()).unwrap());
for (idx, arg) in arguments.iter().skip(1).enumerate() {
// If needed, duplicate the object pointer.
if idx < arguments.len() - 1 {
compiler.chunk.push(OpCode::DUP64);
}

// Evaluate the field at the given index.
compiler.compile_expression(arg)?;
compiler.chunk.push_with_u32(OpCode::SET_MEMBER_32, u32::try_from(idx).unwrap());
}

Ok(())
}));
},
FunctionLogicDescriptor::GetMemberField(struct_info, ref_) => {
let data_layout = runtime.get_data_layout(struct_info);
let slot_index = data_layout.fields.iter().position(|r| r == ref_).unwrap();

runtime.function_inlines.insert(Rc::clone(function), Rc::new(move |compiler, expression| {
let arguments = &compiler.implementation.expression_tree.children[&expression];

compiler.compile_expression(&arguments[0])?;

compiler.chunk.push_with_u32(OpCode::GET_MEMBER_32, u32::try_from(slot_index).unwrap());

Ok(())
}));
},
FunctionLogicDescriptor::SetMemberField(struct_info, ref_) => {
let data_layout = runtime.get_data_layout(struct_info);
let slot_index = data_layout.fields.iter().position(|r| r == ref_).unwrap();

runtime.function_inlines.insert(Rc::clone(function), Rc::new(move |compiler, expression| {
let arguments = &compiler.implementation.expression_tree.children[&expression];

compiler.compile_expression(&arguments[0])?;
compiler.compile_expression(&arguments[1])?;

compiler.chunk.push_with_u32(OpCode::SET_MEMBER_32, u32::try_from(slot_index).unwrap());

Ok(())
}));
},
}
}
Loading

0 comments on commit 192a4f6

Please sign in to comment.