From a470818ce8b8e8ac4efac06e2a28e7d4f0468738 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Wed, 4 Oct 2023 14:15:34 +0200 Subject: [PATCH 01/15] Enable compiling directly to main function --- compiler/backend_inkwell/src/lib.rs | 13 +- compiler/cli/src/debug.rs | 31 ++-- compiler/cli/src/main.rs | 1 - compiler/cli/src/run.rs | 59 ++------ compiler/frontend/src/hir_to_mir.rs | 140 +++++++++++++++--- compiler/frontend/src/lir_optimize.rs | 12 +- compiler/frontend/src/mir/body.rs | 23 ++- compiler/frontend/src/mir/mod.rs | 5 +- compiler/frontend/src/mir_optimize/mod.rs | 15 +- .../src/mir_optimize/module_folding.rs | 9 +- compiler/frontend/src/mir_to_lir.rs | 9 +- compiler/fuzzer/src/lib.rs | 3 +- .../src/debug_adapter/session.rs | 45 ++---- .../analyzer/module_analyzer.rs | 9 +- compiler/language_server/src/features_ir.rs | 28 +++- compiler/vm/benches/utils.rs | 43 +++--- compiler/vm/fuzz/fuzz_targets/vm.rs | 40 ++--- compiler/vm/src/environment.rs | 6 +- compiler/vm/src/heap/mod.rs | 6 +- compiler/vm/src/lib.rs | 18 --- compiler/vm/src/mir_to_byte_code.rs | 7 +- compiler/vm/src/vm.rs | 48 +++++- 22 files changed, 332 insertions(+), 238 deletions(-) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 934acd7bd..ce4d1de7d 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -1,7 +1,6 @@ #![feature(let_chains)] #![warn(unused_crate_dependencies)] -use candy_frontend::module::Module as CandyModule; use candy_frontend::{ mir::{Body, Expression, Id, Mir}, mir_optimize::OptimizeMir, @@ -16,13 +15,13 @@ use inkwell::{ values::{BasicValue, BasicValueEnum, FunctionValue, GlobalValue}, AddressSpace, }; -// We depend on this package (used by inkwell) to specify a version and configure features. -use llvm_sys as _; - +use candy_frontend::hir_to_mir::ExecutionTarget; use candy_frontend::rich_ir::{RichIr, ToRichIr}; use candy_frontend::string_to_rcst::ModuleError; pub use inkwell; use itertools::Itertools; +// We depend on this package (used by inkwell) to specify a version and configure features. +use llvm_sys as _; use std::{ collections::{HashMap, HashSet}, path::PathBuf, @@ -32,12 +31,12 @@ use std::{ #[salsa::query_group(LlvmIrStorage)] pub trait LlvmIrDb: OptimizeMir { #[salsa::transparent] - fn llvm_ir(&self, module: CandyModule) -> Result; + fn llvm_ir(&self, target: ExecutionTarget) -> Result; } #[allow(clippy::needless_pass_by_value)] -fn llvm_ir(db: &dyn LlvmIrDb, module: CandyModule) -> Result { - let (mir, _, _) = db.optimized_mir(module.clone(), TracingConfig::off())?; +fn llvm_ir(db: &dyn LlvmIrDb, target: ExecutionTarget) -> Result { + let (mir, _, _) = db.optimized_mir(target, TracingConfig::off())?; let context = Context::create(); let mut codegen = CodeGen::new(&context, "module", mir); diff --git a/compiler/cli/src/debug.rs b/compiler/cli/src/debug.rs index 8d77281d2..abc9ef293 100644 --- a/compiler/cli/src/debug.rs +++ b/compiler/cli/src/debug.rs @@ -8,7 +8,7 @@ use candy_backend_inkwell::LlvmIrDb; use candy_frontend::{ ast_to_hir::AstToHir, cst_to_ast::CstToAst, - hir_to_mir::HirToMir, + hir_to_mir::{ExecutionTarget, HirToMir}, lir_optimize::OptimizeLir, mir_optimize::OptimizeMir, mir_to_lir::MirToLir, @@ -132,41 +132,45 @@ pub(crate) fn debug(options: Options) -> ProgramResult { Options::Mir(options) => { let module = module_for_path(options.path.clone())?; let tracing = options.to_tracing_config(); - let mir = db.mir(module.clone(), tracing.clone()); + let mir = db.mir(ExecutionTarget::Module(module.clone()), tracing.clone()); mir.ok() .map(|(mir, _)| RichIr::for_mir(&module, &mir, &tracing)) } Options::OptimizedMir(options) => { let module = module_for_path(options.path.clone())?; let tracing = options.to_tracing_config(); - let mir = db.optimized_mir(module.clone(), tracing.clone()); + let mir = db.optimized_mir(ExecutionTarget::Module(module.clone()), tracing.clone()); mir.ok() .map(|(mir, _, _)| RichIr::for_optimized_mir(&module, &mir, &tracing)) } Options::Lir(options) => { let module = module_for_path(options.path.clone())?; let tracing = options.to_tracing_config(); - let lir = db.lir(module.clone(), tracing.clone()); + let lir = db.lir(ExecutionTarget::Module(module.clone()), tracing.clone()); lir.ok() .map(|(lir, _)| RichIr::for_lir(&module, &lir, &tracing)) } Options::OptimizedLir(options) => { let module = module_for_path(options.path.clone())?; let tracing = options.to_tracing_config(); - let lir = db.optimized_lir(module.clone(), tracing.clone()); + let lir = db.optimized_lir(ExecutionTarget::Module(module.clone()), tracing.clone()); lir.ok() .map(|(lir, _)| RichIr::for_lir(&module, &lir, &tracing)) } Options::VmByteCode(options) => { let module = module_for_path(options.path.clone())?; let tracing = options.to_tracing_config(); - let (vm_byte_code, _) = compile_byte_code(&db, module.clone(), tracing.clone()); + let (vm_byte_code, _) = compile_byte_code( + &db, + ExecutionTarget::Module(module.clone()), + tracing.clone(), + ); Some(RichIr::for_byte_code(&module, &vm_byte_code, &tracing)) } #[cfg(feature = "inkwell")] Options::LlvmIr(options) => { let module = module_for_path(options.path.clone())?; - let llvm_ir = db.llvm_ir(module.clone()); + let llvm_ir = db.llvm_ir(ExecutionTarget::Module(module.clone())); llvm_ir.ok() } Options::Gold(options) => return options.run(&db), @@ -313,6 +317,7 @@ impl GoldOptions { { let path = file.path(); let module = module_for_path(path.to_owned())?; + let execution_target = ExecutionTarget::MainFunction(module.clone()); let directory = output_directory.join(path.strip_prefix(&directory).unwrap()); fs::create_dir_all(&directory).unwrap(); @@ -338,32 +343,32 @@ impl GoldOptions { visit("HIR", hir.text); let (mir, _) = db - .mir(module.clone(), Self::TRACING_CONFIG.clone()) + .mir(execution_target.clone(), Self::TRACING_CONFIG.clone()) .unwrap(); let mir = RichIr::for_mir(&module, &mir, &Self::TRACING_CONFIG); visit("MIR", mir.text); let (optimized_mir, _, _) = db - .optimized_mir(module.clone(), Self::TRACING_CONFIG.clone()) + .optimized_mir(execution_target.clone(), Self::TRACING_CONFIG.clone()) .unwrap(); let optimized_mir = RichIr::for_optimized_mir(&module, &optimized_mir, &Self::TRACING_CONFIG); visit("Optimized MIR", optimized_mir.text); let (lir, _) = db - .lir(module.clone(), Self::TRACING_CONFIG.clone()) + .lir(execution_target.clone(), Self::TRACING_CONFIG.clone()) .unwrap(); let lir = RichIr::for_lir(&module, &lir, &Self::TRACING_CONFIG); visit("LIR", lir.text); let (optimized_lir, _) = db - .optimized_lir(module.clone(), Self::TRACING_CONFIG.clone()) + .optimized_lir(execution_target.clone(), Self::TRACING_CONFIG.clone()) .unwrap(); let optimized_lir = RichIr::for_lir(&module, &optimized_lir, &Self::TRACING_CONFIG); visit("Optimized LIR", optimized_lir.text); let (vm_byte_code, _) = - compile_byte_code(db, module.clone(), Self::TRACING_CONFIG.clone()); + compile_byte_code(db, execution_target.clone(), Self::TRACING_CONFIG.clone()); let vm_byte_code_rich_ir = RichIr::for_byte_code(&module, &vm_byte_code, &Self::TRACING_CONFIG); visit( @@ -373,7 +378,7 @@ impl GoldOptions { #[cfg(feature = "inkwell")] { - let llvm_ir = db.llvm_ir(module.clone()).unwrap(); + let llvm_ir = db.llvm_ir(execution_target).unwrap(); visit("LLVM IR", llvm_ir.text); } } diff --git a/compiler/cli/src/main.rs b/compiler/cli/src/main.rs index fdda8fb12..656c16995 100644 --- a/compiler/cli/src/main.rs +++ b/compiler/cli/src/main.rs @@ -68,7 +68,6 @@ enum Exit { ExternalError, FileNotFound, FuzzingFoundFailingCases, - NoMainFunction, NotInCandyPackage, CodeContainsErrors, #[cfg(feature = "inkwell")] diff --git a/compiler/cli/src/run.rs b/compiler/cli/src/run.rs index 3887a29b5..b7c9e3126 100644 --- a/compiler/cli/src/run.rs +++ b/compiler/cli/src/run.rs @@ -3,18 +3,14 @@ use crate::{ utils::{module_for_path, packages_path}, Exit, ProgramResult, }; -use candy_frontend::{hir, TracingConfig, TracingMode}; +use candy_frontend::{hir_to_mir::ExecutionTarget, TracingConfig, TracingMode}; use candy_vm::{ - environment::DefaultEnvironment, - heap::{Heap, HirId}, - mir_to_byte_code::compile_byte_code, - tracer::stack_trace::StackTracer, - Vm, VmFinished, + environment::DefaultEnvironment, heap::Heap, mir_to_byte_code::compile_byte_code, + tracer::stack_trace::StackTracer, Vm, VmFinished, }; use clap::{Parser, ValueHint}; use std::{ path::PathBuf, - rc::Rc, time::{Duration, Instant}, }; use tracing::{debug, error}; @@ -49,7 +45,8 @@ pub(crate) fn run(options: Options) -> ProgramResult { debug!("Running {module}."); let compilation_start = Instant::now(); - let byte_code = Rc::new(compile_byte_code(&db, module.clone(), tracing).0); + let byte_code = + compile_byte_code(&db, ExecutionTarget::MainFunction(module.clone()), tracing).0; let compilation_end = Instant::now(); debug!( @@ -57,55 +54,25 @@ pub(crate) fn run(options: Options) -> ProgramResult { format_duration(compilation_end - compilation_start), ); + debug!("Running program."); let mut heap = Heap::default(); - let VmFinished { tracer, result } = - Vm::for_module(&*byte_code, &mut heap, StackTracer::default()) - .run_forever_without_handles(&mut heap); - let exports = match result { - Ok(exports) => exports, - Err(panic) => { - error!("The module panicked: {}", panic.reason); - error!("{} is responsible.", panic.responsible); - error!( - "This is the stack trace:\n{}", - tracer.format(&db, &packages_path), - ); - return Err(Exit::CodePanicked); - } - }; - let main = match exports.into_main_function(&heap) { - Ok(main) => main, - Err(error) => { - error!("{error}"); - return Err(Exit::NoMainFunction); - } - }; - let discovery_end = Instant::now(); - debug!( - "main function discovery took {}.", - format_duration(discovery_end - compilation_end), - ); - - debug!("Running main function."); let (environment_object, mut environment) = DefaultEnvironment::new(&mut heap, &options.arguments); - let platform = HirId::create(&mut heap, true, hir::Id::platform()); - let vm = Vm::for_function( - byte_code.clone(), + let vm = Vm::for_main_function( + &byte_code, &mut heap, - main, - &[environment_object], - platform, + environment_object, StackTracer::default(), ); - let VmFinished { result, .. } = vm.run_forever_with_environment(&mut heap, &mut environment); + let VmFinished { result, tracer, .. } = + vm.run_forever_with_environment(&mut heap, &mut environment); let result = match result { Ok(return_value) => { debug!("The main function returned: {return_value:?}"); Ok(()) } Err(panic) => { - error!("The main function panicked: {}", panic.reason); + error!("The program panicked: {}", panic.reason); error!("{} is responsible.", panic.responsible); error!( "This is the stack trace:\n{}", @@ -117,7 +84,7 @@ pub(crate) fn run(options: Options) -> ProgramResult { let execution_end = Instant::now(); debug!( "Execution took {}.", - format_duration(execution_end - discovery_end), + format_duration(execution_end - compilation_end), ); drop(byte_code); // Make sure the byte code is kept around until here. diff --git a/compiler/frontend/src/hir_to_mir.rs b/compiler/frontend/src/hir_to_mir.rs index 33307413a..96d6780c2 100644 --- a/compiler/frontend/src/hir_to_mir.rs +++ b/compiler/frontend/src/hir_to_mir.rs @@ -17,20 +17,48 @@ use linked_hash_map::LinkedHashMap; use rustc_hash::{FxHashMap, FxHashSet}; use std::sync::Arc; +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum ExecutionTarget { + Module(Module), + MainFunction(Module), +} +impl ExecutionTarget { + #[must_use] + pub const fn module(&self) -> &Module { + match &self { + Self::Module(module) => module, + Self::MainFunction(module) => module, + } + } +} + #[salsa::query_group(HirToMirStorage)] pub trait HirToMir: PositionConversionDb + CstDb + AstToHir { - fn mir(&self, module: Module, tracing: TracingConfig) -> MirResult; + fn mir(&self, target: ExecutionTarget, tracing: TracingConfig) -> MirResult; } pub type MirResult = Result<(Arc, Arc>), ModuleError>; #[allow(clippy::needless_pass_by_value)] -fn mir(db: &dyn HirToMir, module: Module, tracing: TracingConfig) -> MirResult { +fn mir(db: &dyn HirToMir, target: ExecutionTarget, tracing: TracingConfig) -> MirResult { + let (module, target_is_main_function) = match target { + ExecutionTarget::Module(module) => (module, false), + ExecutionTarget::MainFunction(module) => { + assert_eq!(module.kind, ModuleKind::Code); + (module, true) + } + }; let (mir, errors) = match module.kind { ModuleKind::Code => { let (hir, _) = db.hir(module.clone())?; let mut errors = FxHashSet::default(); - let mir = LoweringContext::compile_module(module, &hir, &tracing, &mut errors); + let mir = LoweringContext::compile_module( + module, + target_is_main_function, + &hir, + &tracing, + &mut errors, + ); (mir, errors) } ModuleKind::Asset => { @@ -39,10 +67,7 @@ fn mir(db: &dyn HirToMir, module: Module, tracing: TracingConfig) -> MirResult { }; ( Mir::build(|body| { - let bytes = bytes - .iter() - .map(|&it| body.push_int(it.into())) - .collect_vec(); + let bytes = bytes.iter().map(|&it| body.push_int(it)).collect_vec(); body.push_list(bytes); }), FxHashSet::default(), @@ -175,6 +200,7 @@ struct OngoingDestructuring { impl<'a> LoweringContext<'a> { fn compile_module( module: Module, + target_is_main_function: bool, hir: &hir::Body, tracing: &TracingConfig, errors: &mut FxHashSet, @@ -184,7 +210,8 @@ impl<'a> LoweringContext<'a> { let needs_function = generate_needs_function(body); - let module_hir_id = body.push_hir_id(hir::Id::new(module, vec![])); + let module_hir_id = hir::Id::new(module, vec![]); + let module_id = body.push_hir_id(module_hir_id.clone()); let mut context = LoweringContext { mapping: &mut mapping, needs_function, @@ -192,9 +219,82 @@ impl<'a> LoweringContext<'a> { ongoing_destructuring: None, errors, }; - context.compile_expressions(body, module_hir_id, &hir.expressions); + context.compile_expressions(body, module_id, &hir.expressions); + + if target_is_main_function { + let export_struct = body.current_return_value(); + LoweringContext::compile_get_main_function_from_export_struct( + body, + &module_hir_id, + module_id, + export_struct, + ); + } }) } + fn compile_get_main_function_from_export_struct( + body: &mut BodyBuilder, + module_hir_id: &hir::Id, + module_id: Id, + export_struct: Id, + ) { + let struct_has_key_function = body.push_builtin(BuiltinFunction::StructHasKey); + let main_tag = body.push_tag("Main".to_string(), None); + let export_contains_main_function = body.push_call( + struct_has_key_function, + vec![export_struct, main_tag], + module_id, + ); + let reason = body.push_text("The module doesn't export a main function.".to_string()); + body.push_panic_if_false( + module_hir_id, + export_contains_main_function, + reason, + module_id, + ); + + let struct_get_function = body.push_builtin(BuiltinFunction::StructGet); + let main_function = body.push_call( + struct_get_function, + vec![export_struct, main_tag], + module_id, + ); + + let type_of_function = body.push_builtin(BuiltinFunction::TypeOf); + let type_of_main = body.push_call(type_of_function, vec![main_function], module_id); + let equals_function = body.push_builtin(BuiltinFunction::Equals); + let function_tag = body.push_tag("Function".to_string(), None); + let type_of_main_equals_function = + body.push_call(equals_function, vec![type_of_main, function_tag], module_id); + let reason = body.push_text("The exported main value is not a function.".to_string()); + body.push_panic_if_false( + module_hir_id, + type_of_main_equals_function, + reason, + module_id, + ); + + let get_argument_count_function = body.push_builtin(BuiltinFunction::GetArgumentCount); + let main_function_parameter_count = + body.push_call(get_argument_count_function, vec![main_function], module_id); + let one = body.push_int(1); + let main_function_parameter_count_is_one = body.push_call( + equals_function, + vec![main_function_parameter_count, one], + module_id, + ); + let reason = body.push_text( + "The exported main function doesn't accept exactly one parameter.".to_string(), + ); + body.push_panic_if_false( + module_hir_id, + main_function_parameter_count_is_one, + reason, + module_id, + ); + + body.push_reference(main_function); + } fn compile_expressions( &mut self, @@ -214,7 +314,7 @@ impl<'a> LoweringContext<'a> { expression: &hir::Expression, ) { let id = match expression { - hir::Expression::Int(int) => body.push_int(int.clone().into()), + hir::Expression::Int(int) => body.push_int(int.clone()), hir::Expression::Text(text) => body.push_text(text.clone()), hir::Expression::Reference(reference) => body.push_reference(self.mapping[reference]), hir::Expression::Symbol(symbol) => body.push_tag(symbol.clone(), None), @@ -267,7 +367,7 @@ impl<'a> LoweringContext<'a> { }, |body| { let list_get_function = body.push_builtin(BuiltinFunction::ListGet); - let one = body.push_int(1.into()); + let one = body.push_int(1); let reason = body.push_call( list_get_function, vec![pattern_result, one], @@ -288,7 +388,7 @@ impl<'a> LoweringContext<'a> { body.push_reference(result) } else { let list_get = body.push_builtin(BuiltinFunction::ListGet); - let index = body.push_int((identifier_id.0 + 1).into()); + let index = body.push_int(identifier_id.0 + 1); let responsible = body.push_hir_id(hir_id.clone()); body.push_call(list_get, vec![result, index], responsible) } @@ -471,7 +571,7 @@ impl<'a> LoweringContext<'a> { }); let else_function = body.push_function(case_id.child("didNotMatch"), |body, _| { let list_get_function = body.push_builtin(BuiltinFunction::ListGet); - let one = body.push_int(1.into()); + let one = body.push_int(1); let reason = body.push_call( list_get_function, vec![pattern_result, one], @@ -531,7 +631,7 @@ impl PatternLoweringContext { match pattern { hir::Pattern::NewIdentifier(_) => self.push_match(body, vec![expression]), hir::Pattern::Int(int) => { - let expected = body.push_int(int.clone().into()); + let expected = body.push_int(int.clone()); self.compile_exact_value(body, expression, expected) } hir::Pattern::Text(text) => { @@ -595,7 +695,7 @@ impl PatternLoweringContext { // Check that it's a list. self.compile_verify_type_condition(body, expression, "List".to_string(), |body| { // Check that the length is correct. - let expected = body.push_int(list.len().into()); + let expected = body.push_int(list.len()); let builtin_list_length = body.push_builtin(BuiltinFunction::ListLength); let actual_length = body.push_call(builtin_list_length, vec![expression], self.responsible); @@ -611,7 +711,7 @@ impl PatternLoweringContext { .enumerate() .map(|(index, item_pattern)| { move |body: &mut BodyBuilder| { - let index = body.push_int(index.into()); + let index = body.push_int(index); let item = body.push_call( builtin_list_get, vec![expression, index], @@ -735,7 +835,7 @@ impl PatternLoweringContext { .position(|it| it == identifier_id); let Some(index) = index else { return body.push_reference(nothing); }; - let index = body.push_int((1 + index).into()); + let index = body.push_int(1 + index); body.push_call(list_get_function, vec![result, index], self.responsible) }) .collect(); @@ -849,7 +949,7 @@ impl PatternLoweringContext { hir::Pattern::NewIdentifier(_) => { panic!("New identifiers can't be used in this part of a pattern.") } - hir::Pattern::Int(int) => body.push_int(int.clone().into()), + hir::Pattern::Int(int) => body.push_int(int.clone()), hir::Pattern::Text(text) => body.push_text(text.clone()), hir::Pattern::Tag { symbol, value } => { let value = value @@ -893,7 +993,7 @@ impl PatternLoweringContext { |body| { let list_get_function = body.push_builtin(BuiltinFunction::ListGet); for index in 0..captured_identifier_count { - let index = body.push_int((index + 1).into()); + let index = body.push_int(index + 1); let captured_identifier = body.push_call( list_get_function, vec![return_value, index], @@ -948,7 +1048,7 @@ impl BodyBuilder { /// boolean. fn push_is_match(&mut self, match_or_no_match: Id, responsible: Id) -> Id { let list_get_function = self.push_builtin(BuiltinFunction::ListGet); - let zero = self.push_int(0.into()); + let zero = self.push_int(0); let match_or_no_match_tag = self.push_call( list_get_function, vec![match_or_no_match, zero], diff --git a/compiler/frontend/src/lir_optimize.rs b/compiler/frontend/src/lir_optimize.rs index b213e8e1a..3d847bcde 100644 --- a/compiler/frontend/src/lir_optimize.rs +++ b/compiler/frontend/src/lir_optimize.rs @@ -1,7 +1,7 @@ use crate::{ + hir_to_mir::ExecutionTarget, lir::{Bodies, Body, Expression, Id, Lir}, mir_to_lir::{LirResult, MirToLir}, - module::Module, utils::HashMapExtension, TracingConfig, }; @@ -11,12 +11,16 @@ use std::{collections::hash_map::Entry, sync::Arc}; #[salsa::query_group(OptimizeLirStorage)] pub trait OptimizeLir: MirToLir { - fn optimized_lir(&self, module: Module, tracing: TracingConfig) -> LirResult; + fn optimized_lir(&self, target: ExecutionTarget, tracing: TracingConfig) -> LirResult; } #[allow(clippy::needless_pass_by_value)] -fn optimized_lir(db: &dyn OptimizeLir, module: Module, tracing: TracingConfig) -> LirResult { - let (lir, errors) = db.lir(module, tracing)?; +fn optimized_lir( + db: &dyn OptimizeLir, + target: ExecutionTarget, + tracing: TracingConfig, +) -> LirResult { + let (lir, errors) = db.lir(target, tracing)?; let mut bodies = Bodies::default(); for (id, body) in lir.bodies().ids_and_bodies() { diff --git a/compiler/frontend/src/mir/body.rs b/compiler/frontend/src/mir/body.rs index 3854a7d96..c9956d675 100644 --- a/compiler/frontend/src/mir/body.rs +++ b/compiler/frontend/src/mir/body.rs @@ -334,8 +334,8 @@ impl BodyBuilder { .push_with_new_id(&mut self.id_generator, expression) } - pub fn push_int(&mut self, value: BigInt) -> Id { - self.push(Expression::Int(value)) + pub fn push_int(&mut self, value: impl Into) -> Id { + self.push(Expression::Int(value.into())) } pub fn push_text(&mut self, value: String) -> Id { self.push(Expression::Text(value)) @@ -418,6 +418,25 @@ impl BodyBuilder { responsible, }) } + pub fn push_panic_if_false( + &mut self, + hir_id: &hir::Id, + condition: Id, + reason: Id, + responsible: Id, + ) -> Id { + self.push_if_else( + hir_id, + condition, + |body| { + body.push_nothing(); + }, + |body| { + body.push_panic(reason, responsible); + }, + responsible, + ) + } #[must_use] pub fn current_return_value(&self) -> Id { diff --git a/compiler/frontend/src/mir/mod.rs b/compiler/frontend/src/mir/mod.rs index c804f9b86..d8e00036d 100644 --- a/compiler/frontend/src/mir/mod.rs +++ b/compiler/frontend/src/mir/mod.rs @@ -16,10 +16,7 @@ pub struct Mir { pub body: Body, } impl Mir { - pub fn build(function: F) -> Self - where - F: FnOnce(&mut BodyBuilder), - { + pub fn build(function: impl FnOnce(&mut BodyBuilder)) -> Self { let mut builder = BodyBuilder::new(IdGenerator::start_at(1)); function(&mut builder); let (id_generator, body) = builder.finish(); diff --git a/compiler/frontend/src/mir_optimize/mod.rs b/compiler/frontend/src/mir_optimize/mod.rs index 38db7b930..396f41400 100644 --- a/compiler/frontend/src/mir_optimize/mod.rs +++ b/compiler/frontend/src/mir_optimize/mod.rs @@ -49,8 +49,8 @@ use self::{ use super::{hir, hir_to_mir::HirToMir, mir::Mir, tracing::TracingConfig}; use crate::{ error::CompilerError, + hir_to_mir::ExecutionTarget, mir::{Body, Expression, MirError, VisibleExpressions}, - module::Module, string_to_rcst::ModuleError, utils::DoHash, }; @@ -75,7 +75,7 @@ mod validate; #[salsa::query_group(OptimizeMirStorage)] pub trait OptimizeMir: HirToMir { #[salsa::cycle(recover_from_cycle)] - fn optimized_mir(&self, module: Module, tracing: TracingConfig) -> OptimizedMirResult; + fn optimized_mir(&self, target: ExecutionTarget, tracing: TracingConfig) -> OptimizedMirResult; } pub type OptimizedMirResult = Result< @@ -90,11 +90,12 @@ pub type OptimizedMirResult = Result< #[allow(clippy::needless_pass_by_value)] fn optimized_mir( db: &dyn OptimizeMir, - module: Module, + target: ExecutionTarget, tracing: TracingConfig, ) -> OptimizedMirResult { + let module = target.module(); debug!("{module}: Compiling."); - let (mir, errors) = db.mir(module.clone(), tracing.clone())?; + let (mir, errors) = db.mir(target.clone(), tracing.clone())?; let mut mir = (*mir).clone(); let mut pureness = PurenessInsights::default(); let mut errors = (*errors).clone(); @@ -219,11 +220,11 @@ impl Context<'_> { fn recover_from_cycle( _db: &dyn OptimizeMir, cycle: &[String], - module: &Module, + target: &ExecutionTarget, _tracing: &TracingConfig, ) -> OptimizedMirResult { let error = CompilerError::for_whole_module( - module.clone(), + target.module().clone(), MirError::ModuleHasCycle { cycle: cycle.to_vec(), }, @@ -231,7 +232,7 @@ fn recover_from_cycle( let mir = Mir::build(|body| { let reason = body.push_text(error.payload.to_string()); - let responsible = body.push_hir_id(hir::Id::new(module.clone(), vec![])); + let responsible = body.push_hir_id(hir::Id::new(target.module().clone(), vec![])); body.push_panic(reason, responsible); }); diff --git a/compiler/frontend/src/mir_optimize/module_folding.rs b/compiler/frontend/src/mir_optimize/module_folding.rs index 66f7ef8ae..a36557c74 100644 --- a/compiler/frontend/src/mir_optimize/module_folding.rs +++ b/compiler/frontend/src/mir_optimize/module_folding.rs @@ -30,6 +30,7 @@ use super::current_expression::{Context, CurrentExpression}; use crate::{ error::{CompilerError, CompilerErrorPayload}, + hir_to_mir::ExecutionTarget, id::IdGenerator, mir::{Body, BodyBuilder, Expression, Id, MirError}, module::{Module, UsePath}, @@ -98,10 +99,10 @@ pub fn apply(context: &mut Context, expression: &mut CurrentExpression) { } }; - match context - .db - .optimized_mir(module_to_import.clone(), context.tracing.for_child_module()) - { + match context.db.optimized_mir( + ExecutionTarget::Module(module_to_import.clone()), + context.tracing.for_child_module(), + ) { Ok((mir, other_pureness, more_errors)) => { context.errors.extend(more_errors.iter().cloned()); diff --git a/compiler/frontend/src/mir_to_lir.rs b/compiler/frontend/src/mir_to_lir.rs index 53d35d8d0..1298c12f4 100644 --- a/compiler/frontend/src/mir_to_lir.rs +++ b/compiler/frontend/src/mir_to_lir.rs @@ -1,11 +1,11 @@ use crate::{ error::CompilerError, hir::{self}, + hir_to_mir::ExecutionTarget, id::CountableId, lir::{self, Lir}, mir::{self}, mir_optimize::OptimizeMir, - module::Module, string_to_rcst::ModuleError, TracingConfig, }; @@ -15,13 +15,14 @@ use std::sync::Arc; #[salsa::query_group(MirToLirStorage)] pub trait MirToLir: OptimizeMir { - fn lir(&self, module: Module, tracing: TracingConfig) -> LirResult; + fn lir(&self, target: ExecutionTarget, tracing: TracingConfig) -> LirResult; } pub type LirResult = Result<(Arc, Arc>), ModuleError>; -fn lir(db: &dyn MirToLir, module: Module, tracing: TracingConfig) -> LirResult { - let (mir, _, errors) = db.optimized_mir(module.clone(), tracing)?; +fn lir(db: &dyn MirToLir, target: ExecutionTarget, tracing: TracingConfig) -> LirResult { + let module = target.module().clone(); + let (mir, _, errors) = db.optimized_mir(target, tracing)?; let mut context = LoweringContext::default(); context.compile_function( diff --git a/compiler/fuzzer/src/lib.rs b/compiler/fuzzer/src/lib.rs index 92b845c07..c56a0d00f 100644 --- a/compiler/fuzzer/src/lib.rs +++ b/compiler/fuzzer/src/lib.rs @@ -20,6 +20,7 @@ pub use self::{ use candy_frontend::{ ast_to_hir::AstToHir, cst::CstDb, + hir_to_mir::ExecutionTarget, mir_optimize::OptimizeMir, module::Module, position::PositionConversionDb, @@ -41,7 +42,7 @@ where calls: TracingMode::Off, evaluated_expressions: TracingMode::Off, }; - let (byte_code, _) = compile_byte_code(db, module, tracing); + let (byte_code, _) = compile_byte_code(db, ExecutionTarget::Module(module), tracing); let byte_code = Rc::new(byte_code); let mut heap = Heap::default(); diff --git a/compiler/language_server/src/debug_adapter/session.rs b/compiler/language_server/src/debug_adapter/session.rs index 826ddabb8..2c7924f16 100644 --- a/compiler/language_server/src/debug_adapter/session.rs +++ b/compiler/language_server/src/debug_adapter/session.rs @@ -5,17 +5,16 @@ use super::{ }; use crate::database::Database; use candy_frontend::{ - hir::Id, + hir_to_mir::ExecutionTarget, module::{Module, ModuleKind, PackagesPath}, TracingConfig, TracingMode, }; use candy_vm::{ byte_code::Instruction, environment::StateAfterRunWithoutHandles, - heap::{Heap, HirId, Struct}, + heap::{Heap, Struct}, mir_to_byte_code::compile_byte_code, - tracer::DummyTracer, - Vm, VmFinished, + Vm, }; use dap::{ events::StoppedEventBody, @@ -190,40 +189,20 @@ impl DebugSession { calls: TracingMode::All, evaluated_expressions: TracingMode::All, }; - let byte_code = compile_byte_code(&self.db, module.clone(), tracing.clone()).0; - let mut heap = Heap::default(); - let VmFinished { result, .. } = Vm::for_module(&byte_code, &mut heap, DummyTracer) - .run_forever_without_handles(&mut heap); - let result = match result { - Ok(result) => result, - Err(error) => { - error!("Module panicked: {}", error.reason); - return Err("module-panicked"); - } - }; - let main = match result.into_main_function(&heap) { - Ok(main) => main, - Err(error) => { - error!("Failed to find main function: {error}"); - return Err("no-main-function"); - } - }; + let byte_code = compile_byte_code( + &self.db, + ExecutionTarget::MainFunction(module.clone()), + tracing.clone(), + ) + .0; self.send_response_ok(request.seq, ResponseBody::Launch) .await; - // Run the `main` function. - let environment = Struct::create(&mut heap, true, &FxHashMap::default()).into(); - let platform = HirId::create(&mut heap, true, Id::platform()); + let mut heap = Heap::default(); + let environment = Struct::create(&mut heap, true, &FxHashMap::default()); let tracer = DebugTracer::default(); - let vm = Vm::for_function( - Rc::new(byte_code), - &mut heap, - main, - &[environment], - platform, - tracer, - ); + let vm = Vm::for_main_function(Rc::new(byte_code), &mut heap, environment, tracer); // TODO: remove when we support pause and continue let vm = match vm.run_n_without_handles(&mut heap, 10000) { diff --git a/compiler/language_server/src/features_candy/analyzer/module_analyzer.rs b/compiler/language_server/src/features_candy/analyzer/module_analyzer.rs index f6eaa2294..0807472b0 100644 --- a/compiler/language_server/src/features_candy/analyzer/module_analyzer.rs +++ b/compiler/language_server/src/features_candy/analyzer/module_analyzer.rs @@ -6,6 +6,7 @@ use crate::{ use candy_frontend::{ ast_to_hir::AstToHir, format::{MaxLength, Precedence}, + hir_to_mir::ExecutionTarget, mir_optimize::OptimizeMir, module::Module, TracingConfig, TracingMode, @@ -96,7 +97,7 @@ impl ModuleAnalyzer { let (mir, _, _) = db .optimized_mir( - self.module.clone(), + ExecutionTarget::Module(self.module.clone()), TracingConfig { register_fuzzables: TracingMode::OnlyCurrent, calls: TracingMode::Off, @@ -113,7 +114,8 @@ impl ModuleAnalyzer { calls: TracingMode::Off, evaluated_expressions: TracingMode::OnlyCurrent, }; - let (byte_code, _) = compile_byte_code(db, self.module.clone(), tracing); + let (byte_code, _) = + compile_byte_code(db, ExecutionTarget::Module(self.module.clone()), tracing); let byte_code = Rc::new(byte_code); let mut heap = Heap::default(); @@ -158,7 +160,8 @@ impl ModuleAnalyzer { calls: TracingMode::Off, evaluated_expressions: TracingMode::Off, }; - let (fuzzing_byte_code, _) = compile_byte_code(db, self.module.clone(), tracing); + let (fuzzing_byte_code, _) = + compile_byte_code(db, ExecutionTarget::Module(self.module.clone()), tracing); let fuzzing_byte_code = Rc::new(fuzzing_byte_code); let mut heap = Heap::default(); diff --git a/compiler/language_server/src/features_ir.rs b/compiler/language_server/src/features_ir.rs index e796b70f5..8e968ecae 100644 --- a/compiler/language_server/src/features_ir.rs +++ b/compiler/language_server/src/features_ir.rs @@ -4,7 +4,7 @@ use candy_backend_inkwell::LlvmIrDb; use candy_frontend::{ ast_to_hir::{AstToHir, HirResult}, cst_to_ast::{AstResult, CstToAst}, - hir_to_mir::{HirToMir, MirResult}, + hir_to_mir::{ExecutionTarget, HirToMir, MirResult}, lir_optimize::OptimizeLir, mir_optimize::{OptimizeMir, OptimizedMirResult}, mir_to_lir::{LirResult, MirToLir}, @@ -108,36 +108,50 @@ impl IrFeatures { Ir::Hir => Self::rich_ir_for_hir(&config.module, db.hir(config.module.clone())), Ir::Mir(tracing_config) => Self::rich_ir_for_mir( &config.module, - db.mir(config.module.clone(), tracing_config.clone()), + db.mir( + ExecutionTarget::Module(config.module.clone()), + tracing_config.clone(), + ), tracing_config, ), Ir::OptimizedMir(tracing_config) => Self::rich_ir_for_optimized_mir( &config.module, - db.optimized_mir(config.module.clone(), tracing_config.clone()), + db.optimized_mir( + ExecutionTarget::Module(config.module.clone()), + tracing_config.clone(), + ), tracing_config, ), Ir::Lir(tracing_config) => Self::rich_ir_for_lir( &config.module, - &db.lir(config.module.clone(), tracing_config.clone()), + &db.lir( + ExecutionTarget::Module(config.module.clone()), + tracing_config.clone(), + ), tracing_config, ), Ir::OptimizedLir(tracing_config) => Self::rich_ir_for_optimized_lir( &config.module, - db.optimized_lir(config.module.clone(), tracing_config.clone()), + db.optimized_lir( + ExecutionTarget::Module(config.module.clone()), + tracing_config.clone(), + ), tracing_config, ), Ir::VmByteCode(tracing_config) => Self::rich_ir_for_vm_byte_code( &config.module, &candy_vm::mir_to_byte_code::compile_byte_code( db, - config.module.clone(), + ExecutionTarget::Module(config.module.clone()), tracing_config.clone(), ) .0, tracing_config, ), #[cfg(feature = "inkwell")] - Ir::LlvmIr => db.llvm_ir(config.module.clone()).unwrap(), + Ir::LlvmIr => db + .llvm_ir(ExecutionTarget::Module(config.module.clone())) + .unwrap(), }; let line_start_offsets = line_start_offsets_raw(&ir.text); diff --git a/compiler/vm/benches/utils.rs b/compiler/vm/benches/utils.rs index 60604ee31..2bbc42c00 100644 --- a/compiler/vm/benches/utils.rs +++ b/compiler/vm/benches/utils.rs @@ -3,8 +3,8 @@ use candy_frontend::{ ast_to_hir::AstToHirStorage, cst::CstDbStorage, cst_to_ast::CstToAstStorage, - hir::{self, HirDbStorage}, - hir_to_mir::HirToMirStorage, + hir::HirDbStorage, + hir_to_mir::{ExecutionTarget, HirToMirStorage}, mir_optimize::OptimizeMirStorage, module::{ GetModuleContentQuery, InMemoryModuleProvider, Module, ModuleDbStorage, ModuleKind, @@ -17,7 +17,7 @@ use candy_frontend::{ }; use candy_vm::{ byte_code::ByteCode, - heap::{Heap, HirId, InlineObject, Struct}, + heap::{Heap, InlineObject, Struct}, mir_to_byte_code::compile_byte_code, tracer::DummyTracer, PopulateInMemoryProviderFromFileSystem, Vm, VmFinished, @@ -82,7 +82,12 @@ pub fn setup() -> Database { db.module_provider.add_str(&MODULE, r#"_ = use "Core""#); // Load `Core` into the cache. - let errors = compile_byte_code(&db, MODULE.clone(), TRACING.clone()).1; + let errors = compile_byte_code( + &db, + ExecutionTarget::Module(MODULE.clone()), + TRACING.clone(), + ) + .1; if !errors.is_empty() { for error in errors.iter() { warn!("{}", error.to_string_with_location(&db)); @@ -95,34 +100,24 @@ pub fn setup() -> Database { pub fn compile(db: &mut Database, source_code: &str) -> ByteCode { db.did_open_module(&MODULE, source_code.as_bytes().to_owned()); - compile_byte_code(db, MODULE.clone(), TRACING.clone()).0 + compile_byte_code( + db, + ExecutionTarget::MainFunction(MODULE.clone()), + TRACING.clone(), + ) + .0 } pub fn run(byte_code: impl Borrow) -> (Heap, InlineObject) { let mut heap = Heap::default(); - let VmFinished { tracer, result } = Vm::for_module(byte_code.borrow(), &mut heap, DummyTracer) - .run_forever_without_handles(&mut heap); - let main = result - .expect("Module panicked.") - .into_main_function(&heap) - .unwrap(); - - // Run the `main` function. let environment = Struct::create(&mut heap, true, &FxHashMap::default()); - let responsible = HirId::create(&mut heap, true, hir::Id::user()); - let VmFinished { result, .. } = Vm::for_function( - byte_code, - &mut heap, - main, - &[environment.into()], - responsible, - tracer, - ) - .run_forever_without_handles(&mut heap); + let VmFinished { result, .. } = + Vm::for_main_function(byte_code, &mut heap, environment, DummyTracer) + .run_forever_without_handles(&mut heap); match result { Ok(return_value) => (heap, return_value), Err(panic) => { - panic!("The main function panicked: {}", panic.reason) + panic!("The program panicked: {}", panic.reason) } } } diff --git a/compiler/vm/fuzz/fuzz_targets/vm.rs b/compiler/vm/fuzz/fuzz_targets/vm.rs index 634819e02..283787c2e 100644 --- a/compiler/vm/fuzz/fuzz_targets/vm.rs +++ b/compiler/vm/fuzz/fuzz_targets/vm.rs @@ -5,8 +5,8 @@ use candy_frontend::{ ast_to_hir::AstToHirStorage, cst::CstDbStorage, cst_to_ast::CstToAstStorage, - hir::{self, HirDbStorage}, - hir_to_mir::HirToMirStorage, + hir::HirDbStorage, + hir_to_mir::{ExecutionTarget, HirToMirStorage}, mir_optimize::OptimizeMirStorage, module::{ InMemoryModuleProvider, Module, ModuleDbStorage, ModuleKind, ModuleProvider, @@ -18,7 +18,7 @@ use candy_frontend::{ TracingConfig, }; use candy_vm::{ - heap::{Heap, HirId, Struct}, + heap::{Heap, Struct}, mir_to_byte_code::compile_byte_code, tracer::DummyTracer, PopulateInMemoryProviderFromFileSystem, Vm, VmFinished, @@ -66,38 +66,24 @@ fuzz_target!(|data: &[u8]| { db.module_provider.load_package_from_file_system("Builtins"); db.module_provider.add(&MODULE, data.to_vec()); - let byte_code = compile_byte_code(&db, MODULE.clone(), TRACING.clone()).0; + let byte_code = compile_byte_code( + &db, + ExecutionTarget::MainFunction(MODULE.clone()), + TRACING.clone(), + ) + .0; let mut heap = Heap::default(); - let VmFinished { result, .. } = - Vm::for_module(&byte_code, &mut heap, DummyTracer).run_forever_without_handles(&mut heap); - let Ok(exports) = result else { - println!("The module panicked."); - return; - }; - let Ok(main) = exports.into_main_function(&heap) else { - println!("The module doesn't export a main function."); - return; - }; - - // Run the `main` function. let environment = Struct::create(&mut heap, true, &Default::default()); - let responsible = HirId::create(&mut heap, true, hir::Id::user()); - let VmFinished { result, .. } = Vm::for_function( - &byte_code, - &mut heap, - main, - &[environment.into()], - responsible, - DummyTracer, - ) - .run_forever_without_handles(&mut heap); + let VmFinished { result, .. } = + Vm::for_main_function(&byte_code, &mut heap, environment, DummyTracer) + .run_forever_without_handles(&mut heap); match result { Ok(return_value) => { println!("The main function returned: {return_value:?}") } Err(panic) => { - panic!("The main function panicked: {}", panic.reason) + panic!("The program panicked: {}", panic.reason) } } }); diff --git a/compiler/vm/src/environment.rs b/compiler/vm/src/environment.rs index d3556459e..afa3636a3 100644 --- a/compiler/vm/src/environment.rs +++ b/compiler/vm/src/environment.rs @@ -1,6 +1,6 @@ use crate::{ byte_code::ByteCode, - heap::{Data, Handle, Heap, InlineObject, Int, List, Struct, Tag, Text}, + heap::{Data, Handle, Heap, Int, List, Struct, Tag, Text}, tracer::Tracer, vm::VmHandleCall, StateAfterRun, StateAfterRunForever, Vm, VmFinished, @@ -53,7 +53,7 @@ pub struct DefaultEnvironment { stdout_handle: Handle, } impl DefaultEnvironment { - pub fn new(heap: &mut Heap, args: &[String]) -> (InlineObject, Self) { + pub fn new(heap: &mut Heap, args: &[String]) -> (Struct, Self) { let arguments = args .iter() .map(|it| Text::create(heap, true, it).into()) @@ -80,7 +80,7 @@ impl DefaultEnvironment { stdin_handle, stdout_handle, }; - (environment_object.into(), environment) + (environment_object, environment) } } impl Environment for DefaultEnvironment { diff --git a/compiler/vm/src/heap/mod.rs b/compiler/vm/src/heap/mod.rs index b3848c5c0..c2f413898 100644 --- a/compiler/vm/src/heap/mod.rs +++ b/compiler/vm/src/heap/mod.rs @@ -238,7 +238,6 @@ pub struct DefaultSymbols { pub int: Text, pub less: Text, pub list: Text, - pub main: Text, pub not_an_integer: Text, pub not_utf8: Text, pub nothing: Text, @@ -264,7 +263,6 @@ impl DefaultSymbols { int: Text::create(heap, false, "Int"), less: Text::create(heap, false, "Less"), list: Text::create(heap, false, "List"), - main: Text::create(heap, false, "Main"), not_an_integer: Text::create(heap, false, "NotAnInteger"), not_utf8: Text::create(heap, false, "NotUtf8"), nothing: Text::create(heap, false, "Nothing"), @@ -303,7 +301,6 @@ impl DefaultSymbols { int: clone_to_heap(heap, address_map, self.int), less: clone_to_heap(heap, address_map, self.less), list: clone_to_heap(heap, address_map, self.list), - main: clone_to_heap(heap, address_map, self.main), not_an_integer: clone_to_heap(heap, address_map, self.not_an_integer), not_utf8: clone_to_heap(heap, address_map, self.not_utf8), nothing: clone_to_heap(heap, address_map, self.nothing), @@ -326,7 +323,7 @@ impl DefaultSymbols { .map(|it| symbols[it]) } #[must_use] - pub const fn all_symbols(&self) -> [Text; 22] { + pub const fn all_symbols(&self) -> [Text; 21] { [ self.arguments, self.builtin, @@ -339,7 +336,6 @@ impl DefaultSymbols { self.int, self.less, self.list, - self.main, self.not_an_integer, self.not_utf8, self.nothing, diff --git a/compiler/vm/src/lib.rs b/compiler/vm/src/lib.rs index c06324ba4..8363adbf9 100644 --- a/compiler/vm/src/lib.rs +++ b/compiler/vm/src/lib.rs @@ -27,11 +27,8 @@ clippy::too_many_lines )] -use crate::heap::{Struct, Tag}; pub use builtin_functions::CAN_USE_STDOUT; -use heap::{Function, Heap, InlineObject}; pub use instruction_pointer::InstructionPointer; -use tracing::debug; pub use utils::PopulateInMemoryProviderFromFileSystem; pub use vm::{Panic, StateAfterRun, StateAfterRunForever, Vm, VmFinished}; @@ -46,18 +43,3 @@ pub mod mir_to_byte_code; pub mod tracer; mod utils; mod vm; - -impl InlineObject { - pub fn into_main_function(self, heap: &Heap) -> Result { - let exported_definitions: Struct = self.try_into().unwrap(); - debug!("The module exports these definitions: {exported_definitions}",); - - exported_definitions - .get(Tag::create(heap.default_symbols().main)) - .ok_or("The module doesn't export a main function.") - .and_then(|main| { - main.try_into() - .map_err(|_| "The exported main value is not a function.") - }) - } -} diff --git a/compiler/vm/src/mir_to_byte_code.rs b/compiler/vm/src/mir_to_byte_code.rs index 23350e15c..1ccf420b6 100644 --- a/compiler/vm/src/mir_to_byte_code.rs +++ b/compiler/vm/src/mir_to_byte_code.rs @@ -7,10 +7,10 @@ use candy_frontend::{ cst::CstDb, error::{CompilerError, CompilerErrorPayload}, hir, + hir_to_mir::ExecutionTarget, id::CountableId, mir::{Body, Expression, Id, Mir}, mir_optimize::OptimizeMir, - module::Module, tracing::TracingConfig, }; use extension_trait::extension_trait; @@ -20,15 +20,16 @@ use std::sync::Arc; pub fn compile_byte_code( db: &Db, - module: Module, + target: ExecutionTarget, tracing: TracingConfig, ) -> (ByteCode, Arc>) where Db: CstDb + OptimizeMir, { + let module = target.module().clone(); #[allow(clippy::map_unwrap_or)] let (mir, errors) = db - .optimized_mir(module.clone(), tracing) + .optimized_mir(target, tracing) .map(|(mir, _, errors)| (mir, errors)) .unwrap_or_else(|error| { let payload = CompilerErrorPayload::Module(error); diff --git a/compiler/vm/src/vm.rs b/compiler/vm/src/vm.rs index e471a7de3..995c2bc23 100644 --- a/compiler/vm/src/vm.rs +++ b/compiler/vm/src/vm.rs @@ -1,11 +1,14 @@ use crate::{ byte_code::ByteCode, - heap::{Function, Handle, Heap, HirId, InlineObject}, + heap::{Function, Handle, Heap, HirId, InlineObject, Struct}, instruction_pointer::InstructionPointer, instructions::InstructionResult, tracer::Tracer, }; -use candy_frontend::hir::Id; +use candy_frontend::{ + hir::{self, Id}, + hir_to_mir::ExecutionTarget, +}; use derive_more::Deref; use extension_trait::extension_trait; use std::{borrow::Borrow, collections::HashMap, fmt::Debug, hash::Hash}; @@ -24,6 +27,14 @@ struct VmInner, T: Tracer> { byte_code: B, state: MachineState, tracer: T, + /// When running a program normally, we first run the module which then + /// returns the main function. To simplify this for VM users, we provide + /// [`Vm::for_main_function`] which does both. + /// + /// This value is set in the above case while running the module itself, and + /// is [`None`] in the second phase or if just running a module or function + /// on its own. + environment_for_main_function: Option, } pub struct MachineState { pub next_instruction: Option, @@ -49,6 +60,21 @@ where B: Borrow, T: Tracer, { + /// Run the module and then run the returned main function accepting a + /// single parameter, the environment. + /// + /// This only supports byte code compiled for + /// [`ExecutionTarget::MainFunction`]. + pub fn for_main_function( + byte_code: B, + heap: &mut Heap, + environment: Struct, + tracer: T, + ) -> Self { + let mut vm = Self::for_module(byte_code, heap, tracer); + vm.inner.environment_for_main_function = Some(environment); + vm + } pub fn for_function( byte_code: B, heap: &mut Heap, @@ -76,6 +102,7 @@ where byte_code, state, tracer, + environment_for_main_function: None, }); Self { inner } } @@ -159,6 +186,23 @@ where let Some(current_instruction) = self.inner.state.next_instruction else { let return_value = self.inner.state.data_stack.pop().unwrap(); self.inner.tracer.call_ended(heap, return_value); + + if let Some(environment) = self.inner.environment_for_main_function { + // We just ran the whole module which returned the main + // function. Now execute this main function using the + // environment we received earlier. + let responsible = HirId::create(heap, true, hir::Id::user()); + let new_vm = Self::for_function( + self.inner.byte_code, + heap, + return_value.try_into().unwrap(), + &[environment.into()], + responsible, + self.inner.tracer, + ); + return StateAfterRun::Running(new_vm); + } + return StateAfterRun::Finished(VmFinished { tracer: self.inner.tracer, result: Ok(return_value), From 862f5740b8bc082cfb3145671ef39c960302ee8c Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Wed, 4 Oct 2023 14:28:21 +0200 Subject: [PATCH 02/15] Remove unused import --- compiler/vm/src/vm.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/vm/src/vm.rs b/compiler/vm/src/vm.rs index 995c2bc23..ab2c87939 100644 --- a/compiler/vm/src/vm.rs +++ b/compiler/vm/src/vm.rs @@ -5,10 +5,7 @@ use crate::{ instructions::InstructionResult, tracer::Tracer, }; -use candy_frontend::{ - hir::{self, Id}, - hir_to_mir::ExecutionTarget, -}; +use candy_frontend::hir::{self, Id}; use derive_more::Deref; use extension_trait::extension_trait; use std::{borrow::Borrow, collections::HashMap, fmt::Debug, hash::Hash}; From effafe3267e0141569f9e68291343c640e0726b7 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Oct 2023 15:36:35 +0200 Subject: [PATCH 03/15] Update Inkwell backend to use ExecutionTarget --- compiler/backend_inkwell/src/lib.rs | 57 ++++++----------------------- compiler/cli/src/inkwell.rs | 31 +++++++++------- 2 files changed, 29 insertions(+), 59 deletions(-) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index ce4d1de7d..48c3d9b32 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -2,10 +2,14 @@ #![warn(unused_crate_dependencies)] use candy_frontend::{ + hir_to_mir::ExecutionTarget, mir::{Body, Expression, Id, Mir}, mir_optimize::OptimizeMir, + rich_ir::{RichIr, ToRichIr}, + string_to_rcst::ModuleError, TracingConfig, }; +pub use inkwell; use inkwell::{ builder::Builder, context::Context, @@ -15,10 +19,6 @@ use inkwell::{ values::{BasicValue, BasicValueEnum, FunctionValue, GlobalValue}, AddressSpace, }; -use candy_frontend::hir_to_mir::ExecutionTarget; -use candy_frontend::rich_ir::{RichIr, ToRichIr}; -use candy_frontend::string_to_rcst::ModuleError; -pub use inkwell; use itertools::Itertools; // We depend on this package (used by inkwell) to specify a version and configure features. use llvm_sys as _; @@ -96,16 +96,6 @@ impl<'ctx> CodeGen<'ctx> { let make_int_fn_type = candy_value_ptr.fn_type(&[i64_type.into()], false); self.module .add_function("make_candy_int", make_int_fn_type, Some(Linkage::External)); - let make_tag_fn_type = candy_value_ptr.fn_type( - &[ - i8_type.ptr_type(AddressSpace::default()).into(), - candy_value_ptr.into(), - ], - false, - ); - let make_candy_tag = - self.module - .add_function("make_candy_tag", make_tag_fn_type, Some(Linkage::External)); let make_text_fn_type = candy_value_ptr.fn_type(&[i8_type.ptr_type(AddressSpace::default()).into()], false); self.module.add_function( @@ -141,12 +131,6 @@ impl<'ctx> CodeGen<'ctx> { Some(Linkage::External), ); - let struct_get_fn_type = - candy_value_ptr.fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - let candy_builtin_struct_get = - self.module - .add_function("candy_builtin_struct_get", struct_get_fn_type, None); - let ptr_to_void_fn_type = void_type.fn_type( &[candy_value.ptr_type(AddressSpace::default()).into()], false, @@ -190,7 +174,7 @@ impl<'ctx> CodeGen<'ctx> { }; self.builder.position_at_end(block); - let main_return = self + let main_function = self .compile_mir(&self.mir.body.clone(), &main_info) .unwrap(); self.builder.position_at_end(block); @@ -199,38 +183,19 @@ impl<'ctx> CodeGen<'ctx> { .module .add_global(candy_value_ptr, None, "candy_environment"); - const MAIN_FN_NAME: &str = "Main"; - let main_text = self.make_str_literal(MAIN_FN_NAME); - - let main_tag = self.builder.build_call( - make_candy_tag, - &[main_text.into(), candy_value_ptr.const_null().into()], - "", - ); - - let main_fn = self - .builder - .build_call( - candy_builtin_struct_get, - &[ - main_return.as_basic_value_enum().into(), - main_tag.try_as_basic_value().unwrap_left().into(), - ], - "", - ) - .try_as_basic_value() - .unwrap_left(); - - let main_res_ptr = self.builder.build_call( + let main_result_ptr = self.builder.build_call( run_candy_main, - &[main_fn.into(), environment.as_basic_value_enum().into()], + &[ + main_function.as_basic_value_enum().into(), + environment.as_basic_value_enum().into(), + ], "", ); if print_main_output { self.builder.build_call( print_fn, - &[main_res_ptr.try_as_basic_value().unwrap_left().into()], + &[main_result_ptr.try_as_basic_value().unwrap_left().into()], "", ); for value in self.module.get_globals() { diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 699337b3c..5d33a11ac 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -1,18 +1,20 @@ -use std::ffi::OsStr; -use std::path::PathBuf; -use std::sync::Arc; - +use crate::{ + database::Database, + utils::{module_for_path, packages_path}, + Exit, ProgramResult, +}; use candy_backend_inkwell::CodeGen; -use candy_frontend::error::{CompilerError, CompilerErrorPayload}; -use candy_frontend::mir::Mir; -use candy_frontend::mir_optimize::OptimizeMir; -use candy_frontend::{hir, module, TracingConfig}; +use candy_frontend::{ + error::{CompilerError, CompilerErrorPayload}, + hir, + hir_to_mir::ExecutionTarget, + mir::Mir, + mir_optimize::OptimizeMir, + module, TracingConfig, +}; use clap::{Parser, ValueHint}; use rustc_hash::FxHashSet; - -use crate::database::Database; -use crate::utils::{module_for_path, packages_path}; -use crate::{Exit, ProgramResult}; +use std::{ffi::OsStr, path::PathBuf, sync::Arc}; /// Compile a Candy program to a native binary. /// @@ -61,7 +63,10 @@ pub(crate) fn compile(options: Options) -> ProgramResult { .to_string(); let (mir, errors) = db - .optimized_mir(module.clone(), TracingConfig::off()) + .optimized_mir( + ExecutionTarget::MainFunction(module.clone()), + TracingConfig::off(), + ) .map(|(mir, _, errors)| (mir, errors)) .unwrap_or_else(|error| { let payload = CompilerErrorPayload::Module(error); From a3f642224c1b423a39cc45f5f863e2ed81a864e1 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Oct 2023 15:58:15 +0200 Subject: [PATCH 04/15] Add missing function declarations --- compiler/backend_inkwell/src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 48c3d9b32..ad9e89d6a 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -96,6 +96,15 @@ impl<'ctx> CodeGen<'ctx> { let make_int_fn_type = candy_value_ptr.fn_type(&[i64_type.into()], false); self.module .add_function("make_candy_int", make_int_fn_type, Some(Linkage::External)); + let make_tag_fn_type = candy_value_ptr.fn_type( + &[ + i8_type.ptr_type(AddressSpace::default()).into(), + candy_value_ptr.into(), + ], + false, + ); + self.module + .add_function("make_candy_tag", make_tag_fn_type, Some(Linkage::External)); let make_text_fn_type = candy_value_ptr.fn_type(&[i8_type.ptr_type(AddressSpace::default()).into()], false); self.module.add_function( @@ -131,6 +140,11 @@ impl<'ctx> CodeGen<'ctx> { Some(Linkage::External), ); + let struct_get_fn_type = + candy_value_ptr.fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); + self.module + .add_function("candy_builtin_struct_get", struct_get_fn_type, None); + let ptr_to_void_fn_type = void_type.fn_type( &[candy_value.ptr_type(AddressSpace::default()).into()], false, From fcbecfa3a4d2d1caeae43ad610395e53fe6343d2 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Oct 2023 16:16:04 +0200 Subject: [PATCH 05/15] Print actual error message --- compiler/cli/src/inkwell.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 5d33a11ac..98f245f43 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -15,6 +15,7 @@ use candy_frontend::{ use clap::{Parser, ValueHint}; use rustc_hash::FxHashSet; use std::{ffi::OsStr, path::PathBuf, sync::Arc}; +use tracing::error; /// Compile a Candy program to a native binary. /// @@ -94,7 +95,10 @@ pub(crate) fn compile(options: Options) -> ProgramResult { .map_err(|e| Exit::LlvmError(e.to_string()))?; codegen .compile_asm_and_link(&path, options.build_runtime, options.debug) - .map_err(|_| Exit::ExternalError)?; + .map_err(|err| { + error!("Failed to compile and link executable: {}", err); + Exit::ExternalError + })?; ProgramResult::Ok(()) } From 97aac5585e578f57932cf559dc75366b4a8edff4 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Oct 2023 16:16:09 +0200 Subject: [PATCH 06/15] Fix wording --- compiler/cli/src/inkwell.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 98f245f43..ca2a178d5 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -40,8 +40,8 @@ pub(crate) struct Options { #[arg(short = 'g', default_value_t = false)] debug: bool, - /// The file or package to run. If none is provided, run the package of your - /// current working directory. + /// The file or package to compile. If none is provided, compile the package + /// of your current working directory. #[arg(value_hint = ValueHint::FilePath)] path: Option, } From 1205128d39eb87cf4606cb64335a2d653023b624 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Oct 2023 17:25:45 +0200 Subject: [PATCH 07/15] =?UTF-8?q?Add=20codeGen.addFunction(=E2=80=A6)=20fu?= =?UTF-8?q?nction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 1 + compiler/backend_inkwell/Cargo.toml | 1 + compiler/backend_inkwell/src/lib.rs | 323 ++++++++++++++++------------ 3 files changed, 186 insertions(+), 139 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 68e2129eb..1ef1cd554 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,6 +105,7 @@ dependencies = [ "inkwell", "itertools 0.11.0", "llvm-sys", + "rustc-hash", "salsa", ] diff --git a/compiler/backend_inkwell/Cargo.toml b/compiler/backend_inkwell/Cargo.toml index d631d85db..fb27fce83 100644 --- a/compiler/backend_inkwell/Cargo.toml +++ b/compiler/backend_inkwell/Cargo.toml @@ -8,4 +8,5 @@ candy_frontend = { version = "0.1.0", path = "../frontend" } inkwell = { version = "0.2.0", features = ["llvm15-0"] } itertools = "0.11.0" llvm-sys = { version = "150", features = ["prefer-dynamic"] } +rustc-hash = "1.1.0" salsa = "0.16.1" diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index ad9e89d6a..592b8ec58 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -2,26 +2,31 @@ #![warn(unused_crate_dependencies)] use candy_frontend::{ + builtin_functions::BuiltinFunction, hir_to_mir::ExecutionTarget, mir::{Body, Expression, Id, Mir}, mir_optimize::OptimizeMir, rich_ir::{RichIr, ToRichIr}, string_to_rcst::ModuleError, + utils::HashMapExtension, TracingConfig, }; pub use inkwell; use inkwell::{ builder::Builder, context::Context, - module::{Linkage, Module}, + module::Module, support::LLVMString, - types::{BasicType, StructType}, + types::{ + BasicMetadataTypeEnum, BasicType, FunctionType, IntType, PointerType, StructType, VoidType, + }, values::{BasicValue, BasicValueEnum, FunctionValue, GlobalValue}, AddressSpace, }; use itertools::Itertools; // We depend on this package (used by inkwell) to specify a version and configure features. use llvm_sys as _; +use rustc_hash::FxHashMap; use std::{ collections::{HashMap, HashSet}, path::PathBuf, @@ -57,6 +62,8 @@ pub struct CodeGen<'ctx> { module: Module<'ctx>, builder: Builder<'ctx>, mir: Arc, + candy_value_pointer_type: PointerType<'ctx>, + builtins: FxHashMap>, globals: HashMap>, locals: HashMap>, functions: HashMap>, @@ -67,11 +74,17 @@ impl<'ctx> CodeGen<'ctx> { pub fn new(context: &'ctx Context, module_name: &str, mir: Arc) -> Self { let module = context.create_module(module_name); let builder = context.create_builder(); + + let candy_value_type = context.opaque_struct_type("candy_value"); + let candy_value_pointer_type = candy_value_type.ptr_type(AddressSpace::default()); + Self { context, module, builder, mir, + candy_value_pointer_type, + builtins: FxHashMap::default(), globals: HashMap::new(), locals: HashMap::new(), functions: HashMap::new(), @@ -90,96 +103,95 @@ impl<'ctx> CodeGen<'ctx> { let i32_type = self.context.i32_type(); let i64_type = self.context.i64_type(); - let candy_value = self.context.opaque_struct_type("candy_value"); - let candy_value_ptr = candy_value.ptr_type(AddressSpace::default()); - - let make_int_fn_type = candy_value_ptr.fn_type(&[i64_type.into()], false); - self.module - .add_function("make_candy_int", make_int_fn_type, Some(Linkage::External)); - let make_tag_fn_type = candy_value_ptr.fn_type( + self.add_function( + "make_candy_int", + &[i64_type.into()], + self.candy_value_pointer_type, + ); + self.add_function( + "make_candy_tag", &[ i8_type.ptr_type(AddressSpace::default()).into(), - candy_value_ptr.into(), + self.candy_value_pointer_type.into(), ], - false, + self.candy_value_pointer_type, ); - self.module - .add_function("make_candy_tag", make_tag_fn_type, Some(Linkage::External)); - let make_text_fn_type = - candy_value_ptr.fn_type(&[i8_type.ptr_type(AddressSpace::default()).into()], false); - self.module.add_function( + self.add_function( "make_candy_text", - make_text_fn_type, - Some(Linkage::External), + &[i8_type.ptr_type(AddressSpace::default()).into()], + self.candy_value_pointer_type, ); - let make_list_fn_type = candy_value_ptr.fn_type(&[candy_value_ptr.into()], false); - self.module.add_function( + self.add_function( "make_candy_list", - make_list_fn_type, - Some(Linkage::External), + &[self.candy_value_pointer_type.into()], + self.candy_value_pointer_type, ); - let make_function_fn_type = candy_value_ptr.fn_type( + self.add_function( + "make_candy_function", &[ - candy_value_ptr.into(), - candy_value_ptr.into(), + self.candy_value_pointer_type.into(), + self.candy_value_pointer_type.into(), i64_type.into(), ], - false, + self.candy_value_pointer_type, ); - self.module.add_function( - "make_candy_function", - make_function_fn_type, - Some(Linkage::External), + self.add_function( + "make_candy_struct", + &[ + self.candy_value_pointer_type.into(), + self.candy_value_pointer_type.into(), + ], + self.candy_value_pointer_type, ); - let make_struct_fn_type = - candy_value_ptr.fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - self.module.add_function( - "make_candy_struct", - make_struct_fn_type, - Some(Linkage::External), + self.add_function( + "candy_builtin_struct_get", + &[ + self.candy_value_pointer_type.into(), + self.candy_value_pointer_type.into(), + ], + self.candy_value_pointer_type, ); - let struct_get_fn_type = - candy_value_ptr.fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - self.module - .add_function("candy_builtin_struct_get", struct_get_fn_type, None); + self.add_function( + "candy_panic", + &[self.candy_value_pointer_type.into()], + void_type, + ); + let free_fn = self.add_function( + "free_candy_value", + &[self.candy_value_pointer_type.into()], + void_type, + ); + let print_fn = self.add_function( + "print_candy_value", + &[self.candy_value_pointer_type.into()], + void_type, + ); - let ptr_to_void_fn_type = void_type.fn_type( - &[candy_value.ptr_type(AddressSpace::default()).into()], - false, + let candy_fn_type = self.candy_value_pointer_type.fn_type(&[], true); + self.add_function( + "get_candy_function_pointer", + &[self.candy_value_pointer_type.into()], + candy_fn_type.ptr_type(AddressSpace::default()), ); - self.module - .add_function("candy_panic", ptr_to_void_fn_type, None); - let free_fn = self - .module - .add_function("free_candy_value", ptr_to_void_fn_type, None); - let print_fn = self - .module - .add_function("print_candy_value", ptr_to_void_fn_type, None); - - let candy_fn_type = candy_value_ptr.fn_type(&[], true); - let get_candy_fn_ptr_type = candy_fn_type - .ptr_type(AddressSpace::default()) - .fn_type(&[candy_value_ptr.into()], false); - self.module - .add_function("get_candy_function_pointer", get_candy_fn_ptr_type, None); - let get_candy_fn_env_type = candy_value_ptr.fn_type(&[candy_value_ptr.into()], false); - self.module.add_function( + self.add_function( "get_candy_function_environment", - get_candy_fn_env_type, - None, + &[self.candy_value_pointer_type.into()], + self.candy_value_pointer_type, ); - let main_type = i32_type.fn_type(&[], false); - let main_fn = self.module.add_function("main", main_type, None); + let main_fn = self.add_function("main", &[], i32_type); let block = self.context.append_basic_block(main_fn, "entry"); - let run_candy_main_type = - candy_value_ptr.fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - let run_candy_main = self - .module - .add_function("run_candy_main", run_candy_main_type, None); + let run_candy_main = self.add_function( + "run_candy_main", + &[ + self.candy_value_pointer_type.into(), + self.candy_value_pointer_type.into(), + ], + self.candy_value_pointer_type, + ); let main_info = FunctionInfo { function_value: main_fn, @@ -193,9 +205,9 @@ impl<'ctx> CodeGen<'ctx> { .unwrap(); self.builder.position_at_end(block); - let environment = self - .module - .add_global(candy_value_ptr, None, "candy_environment"); + let environment = + self.module + .add_global(self.candy_value_pointer_type, None, "candy_environment"); let main_result_ptr = self.builder.build_call( run_candy_main, @@ -214,9 +226,11 @@ impl<'ctx> CodeGen<'ctx> { ); for value in self.module.get_globals() { if value != environment { - let val = - self.builder - .build_load(candy_value_ptr, value.as_pointer_value(), ""); + let val = self.builder.build_load( + self.candy_value_pointer_type, + value.as_pointer_value(), + "", + ); self.builder.build_call(free_fn, &[val.into()], ""); } } @@ -283,14 +297,7 @@ impl<'ctx> CodeGen<'ctx> { mir: &Body, function_ctx: &FunctionInfo<'ctx>, ) -> Option> { - let candy_value_ptr = self - .module - .get_struct_type("candy_value") - .unwrap() - .ptr_type(AddressSpace::default()); - let mut return_value = None; - for (id, expr) in mir.expressions.iter() { let expr_value = match expr { Expression::Int(value) => { @@ -315,7 +322,7 @@ impl<'ctx> CodeGen<'ctx> { Some(global.as_basic_value_enum()) } - candy_frontend::mir::Expression::Text(text) => { + Expression::Text(text) => { let string = self.make_str_literal(text); let make_candy_text = self.module.get_function("make_candy_text").unwrap(); let call = self @@ -330,10 +337,13 @@ impl<'ctx> CodeGen<'ctx> { Some(global.as_basic_value_enum()) } - candy_frontend::mir::Expression::Tag { symbol, value } => { + Expression::Tag { symbol, value } => { let tag_value = match value { Some(value) => self.get_value_with_id(function_ctx, value).unwrap(), - None => candy_value_ptr.const_null().as_basic_value_enum(), + None => self + .candy_value_pointer_type + .const_null() + .as_basic_value_enum(), }; let string = self.make_str_literal(symbol); @@ -352,11 +362,8 @@ impl<'ctx> CodeGen<'ctx> { Some(global.as_basic_value_enum()) } - candy_frontend::mir::Expression::Builtin(builtin) => { - let builtin_name = format!("candy_builtin_{}", builtin.as_ref()); - let args = [candy_value_ptr.into()].repeat(builtin.num_parameters()); - let fn_type = candy_value_ptr.fn_type(args.as_slice(), false); - let function = self.module.add_function(&builtin_name, fn_type, None); + Expression::Builtin(builtin) => { + let function = self.get_builtin(*builtin); self.functions.insert( *id, FunctionInfo { @@ -374,25 +381,25 @@ impl<'ctx> CodeGen<'ctx> { make_candy_function, &[ function_ptr.into(), - candy_value_ptr.const_null().into(), + self.candy_value_pointer_type.const_null().into(), i64_type.const_zero().into(), ], "", ); let global = self.create_global( - &format!("fun_{builtin_name}"), + &format!("fun_candy_builtin_{}", builtin.as_ref()), id, call.try_as_basic_value().unwrap_left(), ); Some(global.as_basic_value_enum()) } - candy_frontend::mir::Expression::List(list) => { + Expression::List(list) => { let i64_type = self.context.i64_type(); let list_array = self.builder.build_array_alloca( - candy_value_ptr, + self.candy_value_pointer_type, i64_type.const_int(list.len() as u64 + 1, false), "", ); @@ -404,7 +411,7 @@ impl<'ctx> CodeGen<'ctx> { for (idx, value) in values.enumerate() { let value_position = unsafe { self.builder.build_gep( - candy_value_ptr, + self.candy_value_pointer_type, list_array, &[i64_type.const_int(idx as u64, false)], "", @@ -414,14 +421,14 @@ impl<'ctx> CodeGen<'ctx> { } let end_position = unsafe { self.builder.build_gep( - candy_value_ptr, + self.candy_value_pointer_type, list_array, &[i64_type.const_int(list.len() as u64, false)], "", ) }; self.builder - .build_store(end_position, candy_value_ptr.const_null()); + .build_store(end_position, self.candy_value_pointer_type.const_null()); let make_candy_list = self.module.get_function("make_candy_list").unwrap(); let candy_list = @@ -433,17 +440,17 @@ impl<'ctx> CodeGen<'ctx> { Some(global.as_basic_value_enum()) } - candy_frontend::mir::Expression::Struct(s) => { + Expression::Struct(s) => { let i64_type = self.context.i64_type(); let make_candy_struct = self.module.get_function("make_candy_struct").unwrap(); let keys_array = self.builder.build_array_alloca( - candy_value_ptr, + self.candy_value_pointer_type, i64_type.const_int(s.len() as u64 + 1, false), "", ); let values_array = self.builder.build_array_alloca( - candy_value_ptr, + self.candy_value_pointer_type, i64_type.const_int(s.len() as u64 + 1, false), "", ); @@ -459,7 +466,7 @@ impl<'ctx> CodeGen<'ctx> { let key_ptr = unsafe { self.builder.build_gep( - candy_value_ptr, + self.candy_value_pointer_type, keys_array, &[i64_type.const_int(idx as u64, false)], "", @@ -468,7 +475,7 @@ impl<'ctx> CodeGen<'ctx> { self.builder.build_store(key_ptr, key); let value_ptr = unsafe { self.builder.build_gep( - candy_value_ptr, + self.candy_value_pointer_type, values_array, &[i64_type.const_int(idx as u64, false)], "", @@ -480,24 +487,24 @@ impl<'ctx> CodeGen<'ctx> { // Null-terminate key/value arrays let key_ptr = unsafe { self.builder.build_gep( - candy_value_ptr, + self.candy_value_pointer_type, keys_array, &[i64_type.const_int(s.len() as u64, false)], "", ) }; self.builder - .build_store(key_ptr, candy_value_ptr.const_null()); + .build_store(key_ptr, self.candy_value_pointer_type.const_null()); let value_ptr = unsafe { self.builder.build_gep( - candy_value_ptr, + self.candy_value_pointer_type, values_array, &[i64_type.const_int(s.len() as u64, false)], "", ) }; self.builder - .build_store(value_ptr, candy_value_ptr.const_null()); + .build_store(value_ptr, self.candy_value_pointer_type.const_null()); let struct_value = self .builder @@ -513,13 +520,13 @@ impl<'ctx> CodeGen<'ctx> { Some(struct_value.into_pointer_value().as_basic_value_enum()) } - candy_frontend::mir::Expression::Reference(ref_id) => { + Expression::Reference(ref_id) => { let value = self.get_value_with_id(function_ctx, ref_id).unwrap(); self.locals.insert(*id, value); Some(value) } - candy_frontend::mir::Expression::HirId(hir_id) => { + Expression::HirId(hir_id) => { let text = format!("{hir_id}"); let string = self.make_str_literal(&text); @@ -533,7 +540,7 @@ impl<'ctx> CodeGen<'ctx> { Some(global.as_basic_value_enum()) } - candy_frontend::mir::Expression::Function { + Expression::Function { original_hirs, parameters, body, @@ -557,7 +564,7 @@ impl<'ctx> CodeGen<'ctx> { let env_types: Vec<_> = captured_ids .iter() - .map(|_| candy_value_ptr.as_basic_type_enum()) + .map(|_| self.candy_value_pointer_type.as_basic_type_enum()) .collect(); let env_struct_type = self.context.struct_type(&env_types, false); @@ -574,16 +581,16 @@ impl<'ctx> CodeGen<'ctx> { self.builder.build_store(member, value.unwrap()); } - let mut params: Vec<_> = - parameters.iter().map(|_| candy_value_ptr.into()).collect(); + let mut params: Vec<_> = parameters + .iter() + .map(|_| self.candy_value_pointer_type.into()) + .collect(); if !captured_ids.is_empty() { - params.push(candy_value_ptr.into()); + params.push(self.candy_value_pointer_type.into()); } - let fn_type = candy_value_ptr.fn_type(¶ms, false); - - let function = self.module.add_function(&name, fn_type, None); + let function = self.add_function(&name, ¶ms, self.candy_value_pointer_type); let function_info = FunctionInfo { function_value: function, @@ -626,8 +633,8 @@ impl<'ctx> CodeGen<'ctx> { Some(global.as_basic_value_enum()) } - candy_frontend::mir::Expression::Parameter => unreachable!(), - candy_frontend::mir::Expression::Call { + Expression::Parameter => unreachable!(), + Expression::Call { function, arguments, responsible, @@ -691,7 +698,7 @@ impl<'ctx> CodeGen<'ctx> { args.push(fn_env_ptr.try_as_basic_value().unwrap_left().into()); - let candy_fn_type = candy_value_ptr.fn_type(&[], true); + let candy_fn_type = self.candy_value_pointer_type.fn_type(&[], true); let inner_fn = fn_ptr .try_as_basic_value() .unwrap_left() @@ -707,8 +714,8 @@ impl<'ctx> CodeGen<'ctx> { Some(call_value.as_basic_value_enum()) } } - candy_frontend::mir::Expression::UseModule { .. } => unreachable!(), - candy_frontend::mir::Expression::Panic { reason, .. } => { + Expression::UseModule { .. } => unreachable!(), + Expression::Panic { reason, .. } => { let panic_fn = self.module.get_function("candy_panic").unwrap(); let reason = self.get_value_with_id(function_ctx, reason).unwrap(); @@ -720,12 +727,12 @@ impl<'ctx> CodeGen<'ctx> { // Early return to avoid building a return instruction. return None; } - candy_frontend::mir::Expression::TraceCallStarts { .. } => unimplemented!(), - candy_frontend::mir::Expression::TraceCallEnds { .. } => unimplemented!(), - candy_frontend::mir::Expression::TraceExpressionEvaluated { .. } => { + Expression::TraceCallStarts { .. } => unimplemented!(), + Expression::TraceCallEnds { .. } => unimplemented!(), + Expression::TraceExpressionEvaluated { .. } => { unimplemented!() } - candy_frontend::mir::Expression::TraceFoundFuzzableFunction { .. } => { + Expression::TraceFoundFuzzableFunction { .. } => { unimplemented!() } }; @@ -744,21 +751,40 @@ impl<'ctx> CodeGen<'ctx> { return_value } + fn get_builtin(&mut self, builtin: BuiltinFunction) -> FunctionValue<'ctx> { + if let Some(function) = self.builtins.get(&builtin) { + return *function; + } + + let function = self.add_function( + &(format!("candy_builtin_{}", builtin.as_ref())), + vec![self.candy_value_pointer_type.into(); builtin.num_parameters()].as_slice(), + self.candy_value_pointer_type, + ); + self.builtins.force_insert(builtin, function); + function + } + fn add_function( + &self, + name: &str, + parameter_types: &[BasicMetadataTypeEnum<'ctx>], + return_type: impl FunctionReturnType<'ctx>, + ) -> FunctionValue<'ctx> { + let function_type = return_type.function_type(parameter_types, false); + self.module.add_function(name, function_type, None) + } fn create_global( &mut self, name: &str, id: &Id, value: impl BasicValue<'ctx>, ) -> GlobalValue<'ctx> { - let candy_value_ptr = self + let global = self .module - .get_struct_type("candy_value") - .unwrap() - .ptr_type(AddressSpace::default()); - let global = self.module.add_global(candy_value_ptr, None, name); + .add_global(self.candy_value_pointer_type, None, name); self.builder.build_store(global.as_pointer_value(), value); - global.set_initializer(&candy_value_ptr.const_null()); + global.set_initializer(&self.candy_value_pointer_type.const_null()); assert!(self.globals.insert(*id, global).is_none()); global } @@ -787,14 +813,9 @@ impl<'ctx> CodeGen<'ctx> { function_ctx: &FunctionInfo<'ctx>, id: &Id, ) -> Option> { - let candy_value_ptr = self - .module - .get_struct_type("candy_value") - .unwrap() - .ptr_type(AddressSpace::default()); let mut v = self.globals.get(id).map(|a| { self.builder - .build_load(candy_value_ptr, a.as_pointer_value(), "") + .build_load(self.candy_value_pointer_type, a.as_pointer_value(), "") }); if v.is_none() && let Some(i) = function_ctx.captured_ids.iter().position(|i| i == id) { let env_ptr = function_ctx.function_value.get_last_param().unwrap(); @@ -809,15 +830,39 @@ impl<'ctx> CodeGen<'ctx> { ) .unwrap(); - v.replace(self.builder.build_load(candy_value_ptr, env_value, "")); + v.replace(self.builder.build_load(self.candy_value_pointer_type, env_value, "")); } if v.is_none() && let Some(value) = self.locals.get(id) { v.replace(*value); } if self.unrepresented_ids.contains(id) { - v.replace(candy_value_ptr.const_null().as_basic_value_enum()); + v.replace( + self.candy_value_pointer_type + .const_null() + .as_basic_value_enum(), + ); } v.unwrap_or_else(|| panic!("{id} should be a real ID")) .into() } } + +trait FunctionReturnType<'ctx> { + fn function_type( + self, + param_types: &[BasicMetadataTypeEnum<'ctx>], + is_var_args: bool, + ) -> FunctionType<'ctx>; +} +macro_rules! impl_function_return_type { + ($($type:ty),*) => { + $( + impl<'ctx> FunctionReturnType<'ctx> for $type { + fn function_type(self, param_types: &[BasicMetadataTypeEnum<'ctx>], is_var_args: bool) -> FunctionType<'ctx> { + self.fn_type(param_types, is_var_args) + } + } + )* + }; +} +impl_function_return_type!(IntType<'ctx>, PointerType<'ctx>, VoidType<'ctx>); From 72f2381c9257109f54e5fe5bd6e7c80b441ffe1c Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Oct 2023 20:04:53 +0200 Subject: [PATCH 08/15] Allow specifying an execution target in `candy debug` --- compiler/cli/src/debug.rs | 84 +++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 25 deletions(-) diff --git a/compiler/cli/src/debug.rs b/compiler/cli/src/debug.rs index abc9ef293..d991075eb 100644 --- a/compiler/cli/src/debug.rs +++ b/compiler/cli/src/debug.rs @@ -12,6 +12,7 @@ use candy_frontend::{ lir_optimize::OptimizeLir, mir_optimize::OptimizeMir, mir_to_lir::MirToLir, + module::Module, position::Offset, rcst_to_cst::RcstToCst, rich_ir::{RichIr, RichIrAnnotation, TokenType}, @@ -20,7 +21,7 @@ use candy_frontend::{ TracingConfig, TracingMode, }; use candy_vm::{byte_code::RichIrForByteCode, heap::HeapData, mir_to_byte_code::compile_byte_code}; -use clap::{Parser, ValueHint}; +use clap::{Parser, ValueEnum, ValueHint}; use colored::{Color, Colorize}; use diffy::{create_patch, PatchFormatter}; use itertools::Itertools; @@ -30,6 +31,7 @@ use rustc_hash::FxHashMap; use std::{ env, fs, io, path::{Path, PathBuf}, + str, }; use walkdir::WalkDir; @@ -52,37 +54,42 @@ pub(crate) enum Options { Hir(OnlyPath), /// Mid-Level Intermediate Representation - Mir(PathAndTracing), + Mir(PathAndExecutionTargetAndTracing), /// Optimized Mid-Level Intermediate Representation - OptimizedMir(PathAndTracing), + OptimizedMir(PathAndExecutionTargetAndTracing), /// Low-Level Intermediate Representation - Lir(PathAndTracing), + Lir(PathAndExecutionTargetAndTracing), /// Optimized Low-Level Intermediate Representation - OptimizedLir(PathAndTracing), + OptimizedLir(PathAndExecutionTargetAndTracing), /// VM Byte Code - VmByteCode(PathAndTracing), + VmByteCode(PathAndExecutionTargetAndTracing), /// LLVM Intermediate Representation #[cfg(feature = "inkwell")] - LlvmIr(OnlyPath), + LlvmIr(PathAndExecutionTarget), #[command(subcommand)] Gold(Gold), } + #[derive(Parser, Debug)] pub(crate) struct OnlyPath { #[arg(value_hint = ValueHint::FilePath)] path: PathBuf, } + #[derive(Parser, Debug)] -pub(crate) struct PathAndTracing { +pub(crate) struct PathAndExecutionTargetAndTracing { #[arg(value_hint = ValueHint::FilePath)] path: PathBuf, + #[arg(long, value_enum, default_value_t = ExecutionTargetKind::Module)] + execution_target: ExecutionTargetKind, + #[arg(long)] register_fuzzables: bool, @@ -92,7 +99,7 @@ pub(crate) struct PathAndTracing { #[arg(long)] trace_evaluated_expressions: bool, } -impl PathAndTracing { +impl PathAndExecutionTargetAndTracing { fn to_tracing_config(&self) -> TracingConfig { TracingConfig { register_fuzzables: TracingMode::only_current_or_off(self.register_fuzzables), @@ -104,6 +111,29 @@ impl PathAndTracing { } } +#[derive(Parser, Debug)] +pub(crate) struct PathAndExecutionTarget { + #[arg(value_hint = ValueHint::FilePath)] + path: PathBuf, + + #[arg(long, value_enum, default_value_t = ExecutionTargetKind::Module)] + execution_target: ExecutionTargetKind, +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)] +pub(crate) enum ExecutionTargetKind { + Module, + MainFunction, +} +impl ExecutionTargetKind { + fn resolve(self, module: Module) -> ExecutionTarget { + match self { + ExecutionTargetKind::Module => ExecutionTarget::Module(module), + ExecutionTargetKind::MainFunction => ExecutionTarget::MainFunction(module), + } + } +} + pub(crate) fn debug(options: Options) -> ProgramResult { let packages_path = packages_path(); let db = Database::new_with_file_system_module_provider(packages_path); @@ -131,47 +161,48 @@ pub(crate) fn debug(options: Options) -> ProgramResult { } Options::Mir(options) => { let module = module_for_path(options.path.clone())?; + let execution_target = options.execution_target.resolve(module.clone()); let tracing = options.to_tracing_config(); - let mir = db.mir(ExecutionTarget::Module(module.clone()), tracing.clone()); + let mir = db.mir(execution_target, tracing.clone()); mir.ok() .map(|(mir, _)| RichIr::for_mir(&module, &mir, &tracing)) } Options::OptimizedMir(options) => { let module = module_for_path(options.path.clone())?; + let execution_target = options.execution_target.resolve(module.clone()); let tracing = options.to_tracing_config(); - let mir = db.optimized_mir(ExecutionTarget::Module(module.clone()), tracing.clone()); + let mir = db.optimized_mir(execution_target, tracing.clone()); mir.ok() .map(|(mir, _, _)| RichIr::for_optimized_mir(&module, &mir, &tracing)) } Options::Lir(options) => { let module = module_for_path(options.path.clone())?; + let execution_target = options.execution_target.resolve(module.clone()); let tracing = options.to_tracing_config(); - let lir = db.lir(ExecutionTarget::Module(module.clone()), tracing.clone()); + let lir = db.lir(execution_target, tracing.clone()); lir.ok() .map(|(lir, _)| RichIr::for_lir(&module, &lir, &tracing)) } Options::OptimizedLir(options) => { let module = module_for_path(options.path.clone())?; + let execution_target = options.execution_target.resolve(module.clone()); let tracing = options.to_tracing_config(); - let lir = db.optimized_lir(ExecutionTarget::Module(module.clone()), tracing.clone()); + let lir = db.optimized_lir(execution_target, tracing.clone()); lir.ok() .map(|(lir, _)| RichIr::for_lir(&module, &lir, &tracing)) } Options::VmByteCode(options) => { let module = module_for_path(options.path.clone())?; + let execution_target = options.execution_target.resolve(module.clone()); let tracing = options.to_tracing_config(); - let (vm_byte_code, _) = compile_byte_code( - &db, - ExecutionTarget::Module(module.clone()), - tracing.clone(), - ); + let (vm_byte_code, _) = compile_byte_code(&db, execution_target, tracing.clone()); Some(RichIr::for_byte_code(&module, &vm_byte_code, &tracing)) } #[cfg(feature = "inkwell")] Options::LlvmIr(options) => { let module = module_for_path(options.path.clone())?; - let llvm_ir = db.llvm_ir(ExecutionTarget::Module(module.clone())); - llvm_ir.ok() + let execution_target = options.execution_target.resolve(module); + db.llvm_ir(execution_target).ok() } Options::Gold(options) => return options.run(&db), }; @@ -189,10 +220,10 @@ pub(crate) fn debug(options: Options) -> ProgramResult { } in annotations { assert!(displayed_byte <= range.start); - let before_annotation = std::str::from_utf8(&bytes[*displayed_byte..*range.start]).unwrap(); + let before_annotation = str::from_utf8(&bytes[*displayed_byte..*range.start]).unwrap(); print!("{before_annotation}"); - let in_annotation = std::str::from_utf8(&bytes[*range.start..*range.end]).unwrap(); + let in_annotation = str::from_utf8(&bytes[*range.start..*range.end]).unwrap(); if let Some(token_type) = token_type { let color = match token_type { @@ -209,12 +240,12 @@ pub(crate) fn debug(options: Options) -> ProgramResult { }; print!("{}", in_annotation.color(color)); } else { - print!("{}", in_annotation) + print!("{in_annotation}") } displayed_byte = range.end; } - let rest = std::str::from_utf8(&bytes[*displayed_byte..]).unwrap(); + let rest = str::from_utf8(&bytes[*displayed_byte..]).unwrap(); println!("{rest}"); Ok(()) @@ -235,6 +266,9 @@ pub(crate) struct GoldOptions { #[arg(value_hint = ValueHint::DirPath)] directory: Option, + #[arg(long, value_enum, default_value_t = ExecutionTargetKind::MainFunction)] + execution_target: ExecutionTargetKind, + #[arg(long, value_hint = ValueHint::DirPath)] output_directory: Option, } @@ -317,7 +351,7 @@ impl GoldOptions { { let path = file.path(); let module = module_for_path(path.to_owned())?; - let execution_target = ExecutionTarget::MainFunction(module.clone()); + let execution_target = self.execution_target.resolve(module.clone()); let directory = output_directory.join(path.strip_prefix(&directory).unwrap()); fs::create_dir_all(&directory).unwrap(); From 2465ad5f60689d57d330120b51cafe1db7bf9c80 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Oct 2023 20:42:16 +0200 Subject: [PATCH 09/15] Support programs without an exported main function --- compiler/backend_inkwell/src/lib.rs | 72 +++++++++++++---------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 592b8ec58..bc997345d 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -144,15 +144,6 @@ impl<'ctx> CodeGen<'ctx> { self.candy_value_pointer_type, ); - self.add_function( - "candy_builtin_struct_get", - &[ - self.candy_value_pointer_type.into(), - self.candy_value_pointer_type.into(), - ], - self.candy_value_pointer_type, - ); - self.add_function( "candy_panic", &[self.candy_value_pointer_type.into()], @@ -200,44 +191,45 @@ impl<'ctx> CodeGen<'ctx> { }; self.builder.position_at_end(block); - let main_function = self - .compile_mir(&self.mir.body.clone(), &main_info) - .unwrap(); + let main_function = self.compile_mir(&self.mir.body.clone(), &main_info); + // This is `None` iff there is no exported main function. self.builder.position_at_end(block); - - let environment = - self.module - .add_global(self.candy_value_pointer_type, None, "candy_environment"); - - let main_result_ptr = self.builder.build_call( - run_candy_main, - &[ - main_function.as_basic_value_enum().into(), - environment.as_basic_value_enum().into(), - ], - "", - ); - - if print_main_output { - self.builder.build_call( - print_fn, - &[main_result_ptr.try_as_basic_value().unwrap_left().into()], + if let Some(main_function) = main_function { + let environment = + self.module + .add_global(self.candy_value_pointer_type, None, "candy_environment"); + + let main_result_ptr = self.builder.build_call( + run_candy_main, + &[ + main_function.as_basic_value_enum().into(), + environment.as_basic_value_enum().into(), + ], "", ); - for value in self.module.get_globals() { - if value != environment { - let val = self.builder.build_load( - self.candy_value_pointer_type, - value.as_pointer_value(), - "", - ); - self.builder.build_call(free_fn, &[val.into()], ""); + + if print_main_output { + self.builder.build_call( + print_fn, + &[main_result_ptr.try_as_basic_value().unwrap_left().into()], + "", + ); + for value in self.module.get_globals() { + if value != environment { + let val = self.builder.build_load( + self.candy_value_pointer_type, + value.as_pointer_value(), + "", + ); + self.builder.build_call(free_fn, &[val.into()], ""); + } } } + + let ret_value = i32_type.const_int(0, false); + self.builder.build_return(Some(&ret_value)); } - let ret_value = i32_type.const_int(0, false); - self.builder.build_return(Some(&ret_value)); if print_llvm_ir { self.module.print_to_stderr(); } From ee1ca98e833d752cc5b3001f4ed6962385924a1f Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Oct 2023 21:05:40 +0200 Subject: [PATCH 10/15] Fix formatting --- compiler/vm/src/environment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/vm/src/environment.rs b/compiler/vm/src/environment.rs index afa3636a3..ed79d3a33 100644 --- a/compiler/vm/src/environment.rs +++ b/compiler/vm/src/environment.rs @@ -1,6 +1,6 @@ use crate::{ byte_code::ByteCode, - heap::{Data, Handle, Heap, Int, List, Struct, Tag, Text}, + heap::{Data, Handle, Heap, Int, List, Struct, Tag, Text}, tracer::Tracer, vm::VmHandleCall, StateAfterRun, StateAfterRunForever, Vm, VmFinished, From 41baac0dca0c2185de2798288e88789af6c5448e Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Oct 2023 21:07:53 +0200 Subject: [PATCH 11/15] Use git diff --quiet i/o --exit-code --- .github/workflows/compiler.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compiler.yaml b/.github/workflows/compiler.yaml index 56b726d25..8720480de 100644 --- a/.github/workflows/compiler.yaml +++ b/.github/workflows/compiler.yaml @@ -116,7 +116,7 @@ jobs: if: github.event_name == 'pull_request' continue-on-error: true working-directory: golden-irs/ - run: git diff --exit-code ${{ github.event.pull_request.base.sha }}_ ${{ github.sha }}_ + run: git diff --quiet ${{ github.event.pull_request.base.sha }}_ ${{ github.sha }}_ - name: Shorten commit SHAs id: short-shas if: steps.diff.outcome == 'failure' From 98b21b7c3ff8bdc718424748ab2bafdec6d927a8 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Fri, 20 Oct 2023 08:44:06 +0200 Subject: [PATCH 12/15] Use FxHashMap/-Set i/o std's --- compiler/backend_inkwell/src/lib.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index afdacf902..7fa6a8069 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -28,11 +28,7 @@ use itertools::Itertools; // We depend on this package (used by inkwell) to specify a version and configure features. use llvm_sys as _; use rustc_hash::FxHashMap; -use std::{ - collections::{HashMap, HashSet}, - path::Path, - sync::Arc, -}; +use std::{path::Path, sync::Arc}; #[salsa::query_group(LlvmIrStorage)] pub trait LlvmIrDb: OptimizeMir { @@ -66,10 +62,10 @@ pub struct CodeGen<'ctx> { mir: Arc, candy_value_pointer_type: PointerType<'ctx>, builtins: FxHashMap>, - globals: HashMap>, - locals: HashMap>, - functions: HashMap>, - unrepresented_ids: HashSet, + globals: FxHashMap>, + locals: FxHashMap>, + functions: FxHashMap>, + unrepresented_ids: FxHashSet, } pub struct LlvmCandyModule<'ctx> { @@ -165,10 +161,10 @@ impl<'ctx> CodeGen<'ctx> { mir, candy_value_pointer_type, builtins: FxHashMap::default(), - globals: HashMap::new(), - locals: HashMap::new(), - functions: HashMap::new(), - unrepresented_ids: HashSet::new(), + globals: FxHashMap::default(), + locals: FxHashMap::default(), + functions: FxHashMap::default(), + unrepresented_ids: FxHashSet::default(), } } From 207f5623757ffddbaf425835932f3844e512500d Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Fri, 20 Oct 2023 08:44:15 +0200 Subject: [PATCH 13/15] Add blank lines for structure --- compiler/backend_inkwell/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 7fa6a8069..4a9ee45bc 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -786,6 +786,7 @@ impl<'ctx> CodeGen<'ctx> { self.builtins.force_insert(builtin, function); function } + fn add_function( &self, name: &str, @@ -795,6 +796,7 @@ impl<'ctx> CodeGen<'ctx> { let function_type = return_type.function_type(parameter_types, false); self.module.add_function(name, function_type, None) } + fn create_global( &mut self, name: &str, From 8ee4d2403351f57f4eb1f82e5974ed3fb1f2b6ca Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 26 Oct 2023 15:54:41 +0200 Subject: [PATCH 14/15] Add missing import --- compiler/backend_inkwell/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 4a9ee45bc..fdd18ccb0 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -27,7 +27,7 @@ use inkwell::{ use itertools::Itertools; // We depend on this package (used by inkwell) to specify a version and configure features. use llvm_sys as _; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use std::{path::Path, sync::Arc}; #[salsa::query_group(LlvmIrStorage)] From 82ea3568ca6bd82da5765174763715407d74b8e7 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 26 Oct 2023 15:54:50 +0200 Subject: [PATCH 15/15] Fix linter complaints --- compiler/cli/src/debug.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/cli/src/debug.rs b/compiler/cli/src/debug.rs index f2786cd6a..7a98da7da 100644 --- a/compiler/cli/src/debug.rs +++ b/compiler/cli/src/debug.rs @@ -83,7 +83,7 @@ pub struct OnlyPath { } #[derive(Parser, Debug)] -pub(crate) struct PathAndExecutionTargetAndTracing { +pub struct PathAndExecutionTargetAndTracing { #[arg(value_hint = ValueHint::FilePath)] path: PathBuf, @@ -113,7 +113,7 @@ impl PathAndExecutionTargetAndTracing { } #[derive(Parser, Debug)] -pub(crate) struct PathAndExecutionTarget { +pub struct PathAndExecutionTarget { #[arg(value_hint = ValueHint::FilePath)] path: PathBuf, @@ -122,20 +122,20 @@ pub(crate) struct PathAndExecutionTarget { } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)] -pub(crate) enum ExecutionTargetKind { +pub enum ExecutionTargetKind { Module, MainFunction, } impl ExecutionTargetKind { - fn resolve(self, module: Module) -> ExecutionTarget { + const fn resolve(self, module: Module) -> ExecutionTarget { match self { - ExecutionTargetKind::Module => ExecutionTarget::Module(module), - ExecutionTargetKind::MainFunction => ExecutionTarget::MainFunction(module), + Self::Module => ExecutionTarget::Module(module), + Self::MainFunction => ExecutionTarget::MainFunction(module), } } } -pub(crate) fn debug(options: Options) -> ProgramResult { +pub fn debug(options: Options) -> ProgramResult { let packages_path = packages_path(); let db = Database::new_with_file_system_module_provider(packages_path);