From c74938707dc6dae89eef3983f81dc07ad64990f4 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Fri, 30 Jun 2023 11:58:53 +0200 Subject: [PATCH 01/27] add plumbing for llvm-based compiler --- Cargo.lock | 48 ++++++++ Cargo.toml | 1 + compiler/backend_inkwell/Cargo.toml | 11 ++ compiler/backend_inkwell/candy_rt.c | 39 +++++++ compiler/backend_inkwell/src/lib.rs | 165 ++++++++++++++++++++++++++++ compiler/cli/Cargo.toml | 1 + compiler/cli/src/inkwell.rs | 55 ++++++++++ compiler/cli/src/main.rs | 4 + packages/examples/int.candy | 3 + packages/examples/minimal.candy | 1 + 10 files changed, 328 insertions(+) create mode 100644 compiler/backend_inkwell/Cargo.toml create mode 100644 compiler/backend_inkwell/candy_rt.c create mode 100644 compiler/backend_inkwell/src/lib.rs create mode 100644 compiler/cli/src/inkwell.rs create mode 100644 packages/examples/int.candy create mode 100644 packages/examples/minimal.candy diff --git a/Cargo.lock b/Cargo.lock index 08a072fec..981a9d9c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,6 +63,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backend_inkwell" +version = "0.1.0" +dependencies = [ + "candy_frontend", + "inkwell", + "llvm-sys", +] + [[package]] name = "base64" version = "0.21.2" @@ -112,6 +121,7 @@ checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" name = "candy_cli" version = "0.1.0" dependencies = [ + "backend_inkwell", "candy_frontend", "candy_fuzzer", "candy_language_server", @@ -845,6 +855,31 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inkwell" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4fcb4a4fa0b8f7b4178e24e6317d6f8b95ab500d8e6e1bd4283b6860e369c1" +dependencies = [ + "either", + "inkwell_internals", + "libc", + "llvm-sys", + "once_cell", + "parking_lot 0.12.1", +] + +[[package]] +name = "inkwell_internals" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b185e7d068d6820411502efa14d8fbf010750485399402156b72dd2a548ef8e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "inotify" version = "0.9.6" @@ -993,6 +1028,19 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "llvm-sys" +version = "150.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c31c2e6d67588f0877f9e625c3d9d74130af663399311319d0139fc4f3ea009" +dependencies = [ + "cc", + "lazy_static", + "libc", + "regex", + "semver", +] + [[package]] name = "lock_api" version = "0.4.9" diff --git a/Cargo.toml b/Cargo.toml index 0b3b2fdc4..5feb6e19b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ + "compiler/backend_inkwell", "compiler/cli", "compiler/frontend", "compiler/fuzzer", diff --git a/compiler/backend_inkwell/Cargo.toml b/compiler/backend_inkwell/Cargo.toml new file mode 100644 index 000000000..d420735ef --- /dev/null +++ b/compiler/backend_inkwell/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "backend_inkwell" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +candy_frontend = { version = "0.1.0", path = "../frontend" } +inkwell = { version = "0.2.0", features = ["llvm15-0"] } +llvm-sys = { version = "*", features = ["prefer-dynamic"] } \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt.c b/compiler/backend_inkwell/candy_rt.c new file mode 100644 index 000000000..31c2ef39f --- /dev/null +++ b/compiler/backend_inkwell/candy_rt.c @@ -0,0 +1,39 @@ +#include +#include + +#define int128_t long long int + +typedef enum +{ + CANDY_TYPE_INT, + CANDY_TYPE_TEXT, + CANDY_TYPE_LIST, + CANDY_TYPE_STRUCT, +} candy_type_t; + +typedef struct candy_value +{ + union + { + int128_t integer; + char *text; + struct candy_value *list; + } value; + candy_type_t type; +} candy_value_t; + +candy_value_t *make_candy_int(int128_t value) +{ + candy_value_t *candy_value = malloc(sizeof(candy_value_t)); + candy_value->value.integer = value; + candy_value->type = CANDY_TYPE_INT; + return candy_value; +} + +candy_value_t *make_candy_text(char *text) +{ + candy_value_t *candy_value = malloc(sizeof(candy_value_t)); + candy_value->value.text = text; + candy_value->type = CANDY_TYPE_TEXT; + return candy_value; +} \ No newline at end of file diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs new file mode 100644 index 000000000..8edf58355 --- /dev/null +++ b/compiler/backend_inkwell/src/lib.rs @@ -0,0 +1,165 @@ +use candy_frontend::mir::{Body, Id, Mir}; +use inkwell::{ + basic_block::BasicBlock, + builder::Builder, + context::Context, + module::{Linkage, Module}, + types::{PointerType, StructType}, + values::{BasicValue, FunctionValue}, + AddressSpace, +}; + +pub use inkwell; +use std::{collections::HashMap, rc::Rc, sync::Arc}; + +pub struct CodeGen<'ctx> { + context: &'ctx Context, + module: Module<'ctx>, + builder: Builder<'ctx>, + mir: Arc, + tags: HashMap>, + values: HashMap + 'ctx>>, + candy_type: Option>>, +} + +impl<'ctx> CodeGen<'ctx> { + pub fn new( + context: &'ctx Context, + module: Module<'ctx>, + builder: Builder<'ctx>, + mir: Arc, + ) -> Self { + Self { + context, + module, + builder, + mir, + tags: HashMap::new(), + values: HashMap::new(), + candy_type: None, + } + } + + pub fn compile(mut self) { + let i32_type = self.context.i32_type(); + let i64_type = self.context.i64_type(); + + let candy_type = self.context.opaque_struct_type("candy_type"); + let candy_type_ptr = candy_type.ptr_type(AddressSpace::default()); + + self.candy_type.replace(Rc::new(candy_type_ptr)); + + let make_int_fn_type = candy_type_ptr.fn_type(&[i32_type.into()], false); + self.module + .add_function("make_candy_int", make_int_fn_type, Some(Linkage::External)); + + let main_type = i32_type.fn_type(&[], false); + let main_fn = self.module.add_function("main", main_type, None); + let block = self.context.append_basic_block(main_fn, "entry"); + self.builder.position_at_end(block); + self.compile_mir(&self.mir.body.clone()); + self.builder.position_at_end(block); + self.builder + .build_call(self.module.get_function("candy_main").unwrap(), &[], ""); + let ret_value = i32_type.const_int(0, false); + self.builder.build_return(Some(&ret_value)); + self.module.print_to_stderr(); + self.module + .write_bitcode_to_path(std::path::Path::new("module.bc")); + } + + pub fn compile_mir(&mut self, mir: &Body) { + for (idx, (id, expr)) in mir.expressions.iter().enumerate() { + //dbg!(expr); + match expr { + candy_frontend::mir::Expression::Int(value) => { + let i128_type = self.context.i128_type(); + let v = i128_type.const_int(value.try_into().unwrap(), false); + let make_candy_int = self.module.get_function("make_candy_int").unwrap(); + self.builder.build_call(make_candy_int, &[v.into()], ""); + + //self.values.insert(*id, v); + } + candy_frontend::mir::Expression::Text(_) => todo!(), + candy_frontend::mir::Expression::Tag { symbol, value } => { + self.tags.insert(symbol.clone(), *value); + let i32_type = self.context.i32_type(); + + let global = self.module.add_global(i32_type, None, &symbol); + + let tag = i32_type.const_int(self.tags.len().try_into().unwrap(), false); + global.set_initializer(&tag); + } + candy_frontend::mir::Expression::Builtin(_) => todo!(), + candy_frontend::mir::Expression::List(_) => todo!(), + candy_frontend::mir::Expression::Struct(s) => { + for (id1, id2) in s { + dbg!(id1, id2); + } + self.context.struct_type(&[], false); + } + candy_frontend::mir::Expression::Reference(id) => { + if let Some(v) = self.values.get(id) { + self.builder.build_return(Some(v.as_ref())); + } + } + candy_frontend::mir::Expression::HirId(_) => todo!(), + candy_frontend::mir::Expression::Function { + original_hirs, + parameters, + responsible_parameter, + body, + } => { + let original_name = &original_hirs.iter().next().unwrap().keys[0].to_string(); + let name = match original_name.as_str() { + "main" => "candy_main", + other => other, + }; + + let fn_type = self + .context + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()) + .fn_type(&[], false); + + let function = self.module.add_function(name, fn_type, None); + + let inner_block = self.context.append_basic_block(function, name); + self.builder.position_at_end(inner_block); + self.compile_mir(body); + } + candy_frontend::mir::Expression::Parameter => todo!(), + candy_frontend::mir::Expression::Call { + function, + arguments, + responsible, + } => todo!(), + candy_frontend::mir::Expression::UseModule { + current_module, + relative_path, + responsible, + } => todo!(), + candy_frontend::mir::Expression::Panic { + reason, + responsible, + } => todo!(), + candy_frontend::mir::Expression::TraceCallStarts { + hir_call, + function, + arguments, + responsible, + } => todo!(), + candy_frontend::mir::Expression::TraceCallEnds { return_value } => todo!(), + candy_frontend::mir::Expression::TraceExpressionEvaluated { + hir_expression, + value, + } => todo!(), + candy_frontend::mir::Expression::TraceFoundFuzzableFunction { + hir_definition, + function, + } => todo!(), + } + } + } +} diff --git a/compiler/cli/Cargo.toml b/compiler/cli/Cargo.toml index 8c6166468..356d1437b 100644 --- a/compiler/cli/Cargo.toml +++ b/compiler/cli/Cargo.toml @@ -10,6 +10,7 @@ name = "candy" path = "src/main.rs" [dependencies] +backend_inkwell = { path = "../backend_inkwell" } candy_frontend = { path = "../frontend" } candy_fuzzer = { path = "../fuzzer" } candy_language_server = { path = "../language_server" } diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs new file mode 100644 index 000000000..050b9cb66 --- /dev/null +++ b/compiler/cli/src/inkwell.rs @@ -0,0 +1,55 @@ +use std::path::PathBuf; +use std::sync::Arc; + +use backend_inkwell::CodeGen; +use candy_frontend::error::{CompilerError, CompilerErrorPayload}; +use candy_frontend::mir::Mir; +use candy_frontend::mir_optimize::OptimizeMir; +use candy_frontend::{hir, TracingConfig}; +use clap::{Parser, ValueHint}; + +use crate::database::Database; +use crate::utils::{module_for_path, packages_path}; +use crate::ProgramResult; + +#[derive(Parser, Debug)] +pub(crate) struct Options { + /// The file or package to run. If none is provided, the package of your + /// current working directory will be run. + #[arg(value_hint = ValueHint::FilePath)] + path: Option, +} + +pub(crate) fn compile(options: Options) -> ProgramResult { + let packages_path = packages_path(); + let db = Database::new_with_file_system_module_provider(packages_path); + let path = options + .path + .as_ref() + .map_or_else(|| "Unknown".into(), |p| p.to_string_lossy().to_string()); + let module = module_for_path(options.path)?; + + let (mir, errors) = db + .optimized_mir(module.clone(), TracingConfig::off()) + .map(|(mir, _, errors)| (mir, errors)) + .unwrap_or_else(|error| { + let payload = CompilerErrorPayload::Module(error); + let mir = Mir::build(|body| { + let reason = body.push_text(payload.to_string()); + let responsible = body.push_hir_id(hir::Id::user()); + body.push_panic(reason, responsible); + }); + let errors = vec![CompilerError::for_whole_module(module.clone(), payload)] + .into_iter() + .collect(); + (Arc::new(mir), Arc::new(errors)) + }); + + let context = backend_inkwell::inkwell::context::Context::create(); + let module = context.create_module(&path); + let builder = context.create_builder(); + let mut codegen = CodeGen::new(&context, module, builder, mir); + codegen.compile(); + + ProgramResult::Ok(()) +} diff --git a/compiler/cli/src/main.rs b/compiler/cli/src/main.rs index 167af63c8..11b445f95 100644 --- a/compiler/cli/src/main.rs +++ b/compiler/cli/src/main.rs @@ -10,6 +10,7 @@ mod check; mod database; mod debug; mod fuzz; +mod inkwell; mod lsp; mod run; mod services; @@ -29,6 +30,8 @@ enum CandyOptions { /// Start a Language Server. Lsp, + + Inkwell(inkwell::Options), } #[tokio::main] @@ -44,6 +47,7 @@ async fn main() -> ProgramResult { CandyOptions::Run(options) => run::run(options), CandyOptions::Fuzz(options) => fuzz::fuzz(options), CandyOptions::Lsp => lsp::lsp().await, + CandyOptions::Inkwell(options) => inkwell::compile(options), } } diff --git a/packages/examples/int.candy b/packages/examples/int.candy new file mode 100644 index 000000000..387ec7930 --- /dev/null +++ b/packages/examples/int.candy @@ -0,0 +1,3 @@ +x = 42 + +main environment := x diff --git a/packages/examples/minimal.candy b/packages/examples/minimal.candy new file mode 100644 index 000000000..26e232067 --- /dev/null +++ b/packages/examples/minimal.candy @@ -0,0 +1 @@ +main environment := Nothing From 489b27772dd951a55588110af5c274785cd31844 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Fri, 30 Jun 2023 14:43:21 +0200 Subject: [PATCH 02/27] compile llvm ir with clang --- compiler/backend_inkwell/src/lib.rs | 43 +++++++++++++++++------------ compiler/cli/src/inkwell.rs | 29 +++++++++++++++++-- compiler/cli/src/main.rs | 1 + 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 8edf58355..49511cae6 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -1,16 +1,15 @@ use candy_frontend::mir::{Body, Id, Mir}; use inkwell::{ - basic_block::BasicBlock, builder::Builder, context::Context, module::{Linkage, Module}, - types::{PointerType, StructType}, - values::{BasicValue, FunctionValue}, + support::LLVMString, + values::GlobalValue, AddressSpace, }; pub use inkwell; -use std::{collections::HashMap, rc::Rc, sync::Arc}; +use std::{collections::HashMap, path::Path, rc::Rc, sync::Arc}; pub struct CodeGen<'ctx> { context: &'ctx Context, @@ -18,8 +17,7 @@ pub struct CodeGen<'ctx> { builder: Builder<'ctx>, mir: Arc, tags: HashMap>, - values: HashMap + 'ctx>>, - candy_type: Option>>, + values: HashMap>>, } impl<'ctx> CodeGen<'ctx> { @@ -36,20 +34,17 @@ impl<'ctx> CodeGen<'ctx> { mir, tags: HashMap::new(), values: HashMap::new(), - candy_type: None, } } - pub fn compile(mut self) { + pub fn compile(mut self, path: &Path) -> Result<(), LLVMString> { let i32_type = self.context.i32_type(); - let i64_type = self.context.i64_type(); + let i128_type = self.context.i128_type(); let candy_type = self.context.opaque_struct_type("candy_type"); let candy_type_ptr = candy_type.ptr_type(AddressSpace::default()); - self.candy_type.replace(Rc::new(candy_type_ptr)); - - let make_int_fn_type = candy_type_ptr.fn_type(&[i32_type.into()], false); + let make_int_fn_type = candy_type_ptr.fn_type(&[i128_type.into()], false); self.module .add_function("make_candy_int", make_int_fn_type, Some(Linkage::External)); @@ -64,8 +59,9 @@ impl<'ctx> CodeGen<'ctx> { let ret_value = i32_type.const_int(0, false); self.builder.build_return(Some(&ret_value)); self.module.print_to_stderr(); - self.module - .write_bitcode_to_path(std::path::Path::new("module.bc")); + self.module.verify()?; + self.module.write_bitcode_to_path(path); + Ok(()) } pub fn compile_mir(&mut self, mir: &Body) { @@ -75,10 +71,21 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::mir::Expression::Int(value) => { let i128_type = self.context.i128_type(); let v = i128_type.const_int(value.try_into().unwrap(), false); + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + let global = self.module.add_global(candy_type_ptr, None, ""); + global.set_initializer(&candy_type_ptr.const_null()); let make_candy_int = self.module.get_function("make_candy_int").unwrap(); - self.builder.build_call(make_candy_int, &[v.into()], ""); + let call = self.builder.build_call(make_candy_int, &[v.into()], ""); + self.builder.build_store( + global.as_pointer_value(), + call.try_as_basic_value().unwrap_left(), + ); - //self.values.insert(*id, v); + self.values.insert(*id, Rc::new(global)); } candy_frontend::mir::Expression::Text(_) => todo!(), candy_frontend::mir::Expression::Tag { symbol, value } => { @@ -89,6 +96,7 @@ impl<'ctx> CodeGen<'ctx> { let tag = i32_type.const_int(self.tags.len().try_into().unwrap(), false); global.set_initializer(&tag); + self.values.insert(*id, Rc::new(global)); } candy_frontend::mir::Expression::Builtin(_) => todo!(), candy_frontend::mir::Expression::List(_) => todo!(), @@ -99,8 +107,9 @@ impl<'ctx> CodeGen<'ctx> { self.context.struct_type(&[], false); } candy_frontend::mir::Expression::Reference(id) => { + println!("Reference to {id}"); if let Some(v) = self.values.get(id) { - self.builder.build_return(Some(v.as_ref())); + self.builder.build_return(Some(&v.as_pointer_value())); } } candy_frontend::mir::Expression::HirId(_) => todo!(), diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 050b9cb66..ed7b943fe 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -10,7 +10,7 @@ use clap::{Parser, ValueHint}; use crate::database::Database; use crate::utils::{module_for_path, packages_path}; -use crate::ProgramResult; +use crate::{Exit, ProgramResult}; #[derive(Parser, Debug)] pub(crate) struct Options { @@ -48,8 +48,31 @@ pub(crate) fn compile(options: Options) -> ProgramResult { let context = backend_inkwell::inkwell::context::Context::create(); let module = context.create_module(&path); let builder = context.create_builder(); - let mut codegen = CodeGen::new(&context, module, builder, mir); - codegen.compile(); + let codegen = CodeGen::new(&context, module, builder, mir); + let mut bc_path = PathBuf::new(); + bc_path.push(&format!("{path}.bc")); + codegen + .compile(&bc_path) + .map_err(|e| Exit::LLVMError(e.to_string()))?; + std::process::Command::new("llc") + .arg(&bc_path) + .spawn() + .unwrap() + .wait() + .unwrap(); + let mut s_path = PathBuf::new(); + s_path.push(&format!("{path}.s")); + std::process::Command::new("clang") + .args([ + "compiler/backend_inkwell/candy_rt.c", + s_path.to_str().unwrap(), + "-o", + &s_path.to_str().unwrap().replace(".candy.s", ""), + ]) + .spawn() + .unwrap() + .wait() + .unwrap(); ProgramResult::Ok(()) } diff --git a/compiler/cli/src/main.rs b/compiler/cli/src/main.rs index 11b445f95..d532c8bf7 100644 --- a/compiler/cli/src/main.rs +++ b/compiler/cli/src/main.rs @@ -59,6 +59,7 @@ enum Exit { FuzzingFoundFailingCases, NotInCandyPackage, CodeContainsErrors, + LLVMError(String), } fn init_logger(use_stdout: bool) { From 169476231c2b8fd8e7d3cfb110e61197062e1e51 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 13 Jul 2023 21:54:07 +0200 Subject: [PATCH 03/27] start work on builtins, function calls --- compiler/backend_inkwell/candy_rt.c | 131 ++++++++- compiler/backend_inkwell/src/lib.rs | 409 ++++++++++++++++++++++++---- compiler/cli/src/inkwell.rs | 12 +- 3 files changed, 496 insertions(+), 56 deletions(-) diff --git a/compiler/backend_inkwell/candy_rt.c b/compiler/backend_inkwell/candy_rt.c index 31c2ef39f..d55ba55b5 100644 --- a/compiler/backend_inkwell/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt.c @@ -1,14 +1,18 @@ #include #include +#include +#include #define int128_t long long int typedef enum { - CANDY_TYPE_INT, + CANDY_TYPE_INT = 42, CANDY_TYPE_TEXT, + CANDY_TYPE_TAG, CANDY_TYPE_LIST, CANDY_TYPE_STRUCT, + CANDY_TYPE_FUNCTION, } candy_type_t; typedef struct candy_value @@ -18,10 +22,59 @@ typedef struct candy_value int128_t integer; char *text; struct candy_value *list; + struct candy_value *(*function)(void); } value; candy_type_t type; } candy_value_t; +typedef candy_value_t *(*candy_function)(void); + +candy_value_t __internal_true = { + .value = {.text = "True"}, + .type = CANDY_TYPE_TAG}; + +candy_value_t __internal_false = { + .value = {.text = "False"}, + .type = CANDY_TYPE_TAG}; + +candy_value_t _candy_environment = { + .value = {.text = "Environment"}, + .type = CANDY_TYPE_TAG}; + +// Not particularly elegant, but this is a temporary solution anyway... +candy_value_t *candy_environment = &_candy_environment; + +candy_value_t *to_candy_bool(int value) +{ + if (value) + { + return &__internal_true; + } + else + { + return &__internal_false; + } +} + +void print_candy_value(candy_value_t *value) +{ + switch (value->type) + { + case CANDY_TYPE_INT: + printf("%lld", value->value.integer); + break; + case CANDY_TYPE_TEXT: + printf("%s", value->value.text); + break; + case CANDY_TYPE_TAG: + printf("%s", value->value.text); + break; + default: + printf("", value->type); + break; + } +} + candy_value_t *make_candy_int(int128_t value) { candy_value_t *candy_value = malloc(sizeof(candy_value_t)); @@ -36,4 +89,78 @@ candy_value_t *make_candy_text(char *text) candy_value->value.text = text; candy_value->type = CANDY_TYPE_TEXT; return candy_value; -} \ No newline at end of file +} + +candy_value_t *make_candy_tag(char *tag) +{ + candy_value_t *candy_value = malloc(sizeof(candy_value_t)); + candy_value->value.text = tag; + candy_value->type = CANDY_TYPE_TAG; + return candy_value; +} + +candy_value_t *make_candy_function(candy_function function) +{ + candy_value_t *candy_value = malloc(sizeof(candy_value_t)); + candy_value->type = CANDY_TYPE_FUNCTION; + candy_value->value.function = function; + return candy_value; +} + +candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right) +{ + if (left + ->type != right->type) + { + return &__internal_false; + } + switch (left->type) + { + case CANDY_TYPE_INT: + return to_candy_bool(left->value.integer == right->value.integer); + break; + case CANDY_TYPE_TAG: + return to_candy_bool(strcmp(left->value.text, right->value.text) == 0); + default: + return &__internal_false; + } +} + +candy_value_t *candy_builtin_ifelse(candy_type_t *condition, candy_value_t *then, candy_value_t *otherwise) +{ + if (condition) + { + return then->value.function(); + } + else + { + return otherwise->value.function(); + } +} + +candy_value_t *candy_builtin_typeof(candy_value_t *value) +{ + switch (value->type) + { + case CANDY_TYPE_INT: + return make_candy_tag("int"); + case CANDY_TYPE_TEXT: + return make_candy_tag("text"); + case CANDY_TYPE_TAG: + return make_candy_tag("tag"); + case CANDY_TYPE_LIST: + return make_candy_tag("list"); + case CANDY_TYPE_STRUCT: + return make_candy_tag("struct"); + case CANDY_TYPE_FUNCTION: + return make_candy_tag("function"); + } +} + +void candy_panic(candy_value_t *reason) +{ + printf("The program panicked for the following reason: \n"); + print_candy_value(reason); + printf("\n"); + abort(); +} diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 49511cae6..dfd35f1c5 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -4,7 +4,9 @@ use inkwell::{ context::Context, module::{Linkage, Module}, support::LLVMString, - values::GlobalValue, + values::{ + ArrayValue, BasicMetadataValueEnum, BasicValue, BasicValueEnum, FunctionValue, GlobalValue, + }, AddressSpace, }; @@ -18,6 +20,8 @@ pub struct CodeGen<'ctx> { mir: Arc, tags: HashMap>, values: HashMap>>, + locals: HashMap>>, + functions: HashMap>, Vec)>, } impl<'ctx> CodeGen<'ctx> { @@ -34,12 +38,16 @@ impl<'ctx> CodeGen<'ctx> { mir, tags: HashMap::new(), values: HashMap::new(), + locals: HashMap::new(), + functions: HashMap::new(), } } - pub fn compile(mut self, path: &Path) -> Result<(), LLVMString> { + pub fn compile(mut self, path: &Path, print_llvm_ir: bool) -> Result<(), LLVMString> { let i32_type = self.context.i32_type(); - let i128_type = self.context.i128_type(); + let i128_type = self.context.i64_type(); + let i8_type = self.context.i8_type(); + let void_type = self.context.void_type(); let candy_type = self.context.opaque_struct_type("candy_type"); let candy_type_ptr = candy_type.ptr_type(AddressSpace::default()); @@ -47,6 +55,24 @@ impl<'ctx> CodeGen<'ctx> { let make_int_fn_type = candy_type_ptr.fn_type(&[i128_type.into()], false); self.module .add_function("make_candy_int", make_int_fn_type, Some(Linkage::External)); + let make_tag_fn_type = + candy_type_ptr.fn_type(&[i8_type.ptr_type(AddressSpace::default()).into()], false); + self.module + .add_function("make_candy_tag", make_tag_fn_type, Some(Linkage::External)); + self.module + .add_function("make_candy_text", make_tag_fn_type, Some(Linkage::External)); + //let candy_fn_type = candy_type_ptr.fn_type(&[], false); + let make_function_fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); + self.module.add_function( + "make_candy_function", + make_function_fn_type, + Some(Linkage::External), + ); + let panic_fn_type = void_type.fn_type( + &[candy_type.ptr_type(AddressSpace::default()).into()], + false, + ); + self.module.add_function("candy_panic", panic_fn_type, None); let main_type = i32_type.fn_type(&[], false); let main_fn = self.module.add_function("main", main_type, None); @@ -54,11 +80,19 @@ impl<'ctx> CodeGen<'ctx> { self.builder.position_at_end(block); self.compile_mir(&self.mir.body.clone()); self.builder.position_at_end(block); - self.builder - .build_call(self.module.get_function("candy_main").unwrap(), &[], ""); + let environment = self + .module + .add_global(candy_type_ptr, None, "candy_environment"); + self.builder.build_call( + self.module.get_function("candy_main").unwrap(), + &[environment.as_basic_value_enum().into()], + "", + ); let ret_value = i32_type.const_int(0, false); self.builder.build_return(Some(&ret_value)); - self.module.print_to_stderr(); + if print_llvm_ir { + self.module.print_to_stderr(); + } self.module.verify()?; self.module.write_bitcode_to_path(path); Ok(()) @@ -69,55 +103,226 @@ impl<'ctx> CodeGen<'ctx> { //dbg!(expr); match expr { candy_frontend::mir::Expression::Int(value) => { - let i128_type = self.context.i128_type(); + let i128_type = self.context.i64_type(); let v = i128_type.const_int(value.try_into().unwrap(), false); + let candy_type = self.module.get_struct_type("candy_type").unwrap(); + let candy_type_ptr = candy_type.ptr_type(AddressSpace::default()); + let global = + self.module + .add_global(candy_type_ptr, None, &format!("num_{value}")); + global.set_initializer(&candy_type_ptr.const_null()); + let make_candy_int = self.module.get_function("make_candy_int").unwrap(); + let call = self.builder.build_call(make_candy_int, &[v.into()], ""); + + self.builder.build_store( + global.as_pointer_value(), + call.try_as_basic_value().unwrap_left(), + ); + + self.values.insert(*id, Rc::new(global)); + } + candy_frontend::mir::Expression::Text(text) => { + let i32_type = self.context.i32_type(); + let i8_type = self.context.i8_type(); let candy_type_ptr = self .module .get_struct_type("candy_type") .unwrap() .ptr_type(AddressSpace::default()); - let global = self.module.add_global(candy_type_ptr, None, ""); - global.set_initializer(&candy_type_ptr.const_null()); - let make_candy_int = self.module.get_function("make_candy_int").unwrap(); - let call = self.builder.build_call(make_candy_int, &[v.into()], ""); + + let global = self.module.add_global(candy_type_ptr, None, text); + let v = self.make_str_literal(text); + let len = i32_type.const_int(text.len() as u64, false); + let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); + self.builder.build_store(arr_alloc, v); + let cast = self.builder.build_bitcast( + arr_alloc, + i8_type.ptr_type(AddressSpace::default()), + "", + ); + let make_candy_text = self.module.get_function("make_candy_text").unwrap(); + let call = self.builder.build_call(make_candy_text, &[cast.into()], ""); + self.builder.build_store( global.as_pointer_value(), call.try_as_basic_value().unwrap_left(), ); + //let tag = i32_type.const_int(self.tags.len().try_into().unwrap(), false); + global.set_initializer(&candy_type_ptr.const_null()); self.values.insert(*id, Rc::new(global)); } - candy_frontend::mir::Expression::Text(_) => todo!(), candy_frontend::mir::Expression::Tag { symbol, value } => { self.tags.insert(symbol.clone(), *value); let i32_type = self.context.i32_type(); + let i8_type = self.context.i8_type(); + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let global = self.module.add_global(candy_type_ptr, None, symbol); + let v = self.make_str_literal(symbol); + let len = i32_type.const_int(symbol.len() as u64, false); + let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); + self.builder.build_store(arr_alloc, v); + let cast = self.builder.build_bitcast( + arr_alloc, + i8_type.ptr_type(AddressSpace::default()), + "", + ); + let make_candy_tag = self.module.get_function("make_candy_tag").unwrap(); + let call = self.builder.build_call(make_candy_tag, &[cast.into()], ""); - let global = self.module.add_global(i32_type, None, &symbol); + self.builder.build_store( + global.as_pointer_value(), + call.try_as_basic_value().unwrap_left(), + ); - let tag = i32_type.const_int(self.tags.len().try_into().unwrap(), false); - global.set_initializer(&tag); + //let tag = i32_type.const_int(self.tags.len().try_into().unwrap(), false); + global.set_initializer(&candy_type_ptr.const_null()); self.values.insert(*id, Rc::new(global)); } - candy_frontend::mir::Expression::Builtin(_) => todo!(), - candy_frontend::mir::Expression::List(_) => todo!(), - candy_frontend::mir::Expression::Struct(s) => { - for (id1, id2) in s { - dbg!(id1, id2); + candy_frontend::mir::Expression::Builtin(builtin) => match builtin { + candy_frontend::builtin_functions::BuiltinFunction::ChannelCreate => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::ChannelSend => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ChannelReceive => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::Equals => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = + self.module + .add_function("candy_builtin_equals", fn_type, None); + self.functions.insert(*id, (Rc::new(function), vec![])); + } + candy_frontend::builtin_functions::BuiltinFunction::FunctionRun => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::GetArgumentCount => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::IfElse => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr.fn_type( + &[ + candy_type_ptr.into(), + candy_type_ptr.into(), + candy_type_ptr.into(), + ], + false, + ); + let function = + self.module + .add_function("candy_builtin_ifelse", fn_type, None); + self.functions.insert(*id, (Rc::new(function), vec![])); + } + candy_frontend::builtin_functions::BuiltinFunction::IntAdd => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntBitLength => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseAnd => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseOr => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseXor => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::IntCompareTo => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntDivideTruncating => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::IntModulo => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntMultiply => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntParse => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntRemainder => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntShiftLeft => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntShiftRight => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::IntSubtract => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListFilled => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListGet => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListInsert => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListLength => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListRemoveAt => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListReplace => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::Parallel => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::Print => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::StructGet => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::StructGetKeys => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::StructHasKey => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TagGetValue => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TagHasValue => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TagWithoutValue => { + todo!() } + candy_frontend::builtin_functions::BuiltinFunction::TextCharacters => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::TextConcatenate => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::TextContains => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextEndsWith => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextFromUtf8 => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextGetRange => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextIsEmpty => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextLength => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextStartsWith => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::TextTrimEnd => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextTrimStart => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::ToDebugText => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::Try => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TypeOf => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); + let function = + self.module + .add_function("candy_builtin_typeof", fn_type, None); + self.functions.insert(*id, (Rc::new(function), vec![])); + } + }, + candy_frontend::mir::Expression::List(_) => todo!(), + candy_frontend::mir::Expression::Struct(_s) => { + // Not yet implemented, but not allowed to panic self.context.struct_type(&[], false); } candy_frontend::mir::Expression::Reference(id) => { - println!("Reference to {id}"); if let Some(v) = self.values.get(id) { self.builder.build_return(Some(&v.as_pointer_value())); } } - candy_frontend::mir::Expression::HirId(_) => todo!(), + candy_frontend::mir::Expression::HirId(_id) => { + // Intentionally ignored + } candy_frontend::mir::Expression::Function { original_hirs, parameters, - responsible_parameter, body, + .. } => { let original_name = &original_hirs.iter().next().unwrap().keys[0].to_string(); let name = match original_name.as_str() { @@ -125,14 +330,35 @@ impl<'ctx> CodeGen<'ctx> { other => other, }; - let fn_type = self - .context + let candy_type_ptr = self + .module .get_struct_type("candy_type") .unwrap() - .ptr_type(AddressSpace::default()) - .fn_type(&[], false); + .ptr_type(AddressSpace::default()); + + let params: Vec<_> = parameters.iter().map(|_| candy_type_ptr.into()).collect(); + + let fn_type = candy_type_ptr.fn_type(¶ms, false); let function = self.module.add_function(name, fn_type, None); + self.functions + .insert(*id, (Rc::new(function), parameters.clone())); + + let function_ptr = function.as_global_value().as_pointer_value(); + let make_candy_function = + self.module.get_function("make_candy_function").unwrap(); + let call = + self.builder + .build_call(make_candy_function, &[function_ptr.into()], ""); + + let global = self.module.add_global(candy_type_ptr, None, ""); + global.set_initializer(&function_ptr.get_type().const_null()); + self.builder.build_store( + global.as_pointer_value(), + call.try_as_basic_value().unwrap_left(), + ); + + self.values.insert(*id, Rc::new(global)); let inner_block = self.context.append_basic_block(function, name); self.builder.position_at_end(inner_block); @@ -142,33 +368,110 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::mir::Expression::Call { function, arguments, - responsible, - } => todo!(), - candy_frontend::mir::Expression::UseModule { - current_module, - relative_path, - responsible, - } => todo!(), - candy_frontend::mir::Expression::Panic { - reason, - responsible, - } => todo!(), - candy_frontend::mir::Expression::TraceCallStarts { - hir_call, - function, - arguments, - responsible, - } => todo!(), - candy_frontend::mir::Expression::TraceCallEnds { return_value } => todo!(), - candy_frontend::mir::Expression::TraceExpressionEvaluated { - hir_expression, - value, - } => todo!(), - candy_frontend::mir::Expression::TraceFoundFuzzableFunction { - hir_definition, - function, - } => todo!(), + .. + } => { + let (fun, _params) = self.functions.get(function).unwrap(); + + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let args: Vec<_> = arguments + .iter() + .map(|arg| { + let v = match self.values.get(arg) { + Some(value) => Some(value.as_pointer_value()), + None => match self.locals.get(arg) { + Some(value) => Some(value.into_pointer_value()), + None => match self + .functions + .values() + .find(|(_, args)| args.contains(arg)) + { + Some((fun, args)) => { + let idx = args.iter().position(|i| i == arg).unwrap(); + Some( + fun.get_nth_param(idx as u32) + .unwrap() + .into_pointer_value(), + ) + } + None => Some(candy_type_ptr.const_null()), + }, + }, + }; + self.builder + .build_load(candy_type_ptr, v.unwrap(), "") + .into() + //v.unwrap_or_else(|| panic!("{arg} should be a real ID")) + }) + .collect(); + let call = self.builder.build_call(**fun, &args, ""); + let call_value = Rc::new(call.try_as_basic_value().unwrap_left()); + self.locals.insert(*id, call_value.clone()); + + if idx == mir.expressions.len() - 1 { + self.builder + .build_return(Some(&call_value.into_pointer_value())); + } + } + candy_frontend::mir::Expression::UseModule { .. } => unreachable!(), + candy_frontend::mir::Expression::Panic { reason, .. } => { + let panic_fn = self.module.get_function("candy_panic").unwrap(); + if let Some(reason) = self.values.get(reason) { + self.builder + .build_call(panic_fn, &[reason.as_pointer_value().into()], ""); + } else { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + self.builder.build_call( + panic_fn, + &[candy_type_ptr.const_null().into()], + "", + ); + } + } + candy_frontend::mir::Expression::TraceCallStarts { .. } => unimplemented!(), + candy_frontend::mir::Expression::TraceCallEnds { .. } => unimplemented!(), + candy_frontend::mir::Expression::TraceExpressionEvaluated { .. } => { + unimplemented!() + } + candy_frontend::mir::Expression::TraceFoundFuzzableFunction { .. } => { + unimplemented!() + } } } } + + fn make_str_literal(&self, s: &str) -> ArrayValue<'_> { + let i8_type = self.context.i8_type(); + let content: Vec<_> = s + .chars() + .chain(std::iter::once('\0')) + .map(|c| i8_type.const_int(c as u64, false)) + .collect(); + i8_type.const_array(&content) + } + + fn _get_value_by_id(&self, id: &Id) -> Option { + match self.values.get(id) { + Some(value) => Some(value.as_pointer_value().into()), + None => match self.locals.get(id) { + Some(value) => Some(value.into_pointer_value().into()), + None => match self.functions.values().find(|(_, args)| args.contains(id)) { + Some((fun, args)) => { + let idx = args.iter().position(|i| i == id).unwrap(); + Some(fun.get_nth_param(idx as u32).unwrap().into()) + } + None => None, + }, + }, + } + } } diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index ed7b943fe..faffc58f8 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -14,6 +14,9 @@ use crate::{Exit, ProgramResult}; #[derive(Parser, Debug)] pub(crate) struct Options { + /// If enabled, the compiler will print the generated LLVM IR to stderr. + #[arg(long = "print-llvm-ir", default_value_t = false)] + print_llvm_ir: bool, /// The file or package to run. If none is provided, the package of your /// current working directory will be run. #[arg(value_hint = ValueHint::FilePath)] @@ -45,6 +48,13 @@ pub(crate) fn compile(options: Options) -> ProgramResult { (Arc::new(mir), Arc::new(errors)) }); + if !errors.is_empty() { + for error in errors.iter() { + println!("{:?}", error); + } + std::process::exit(1); + } + let context = backend_inkwell::inkwell::context::Context::create(); let module = context.create_module(&path); let builder = context.create_builder(); @@ -52,7 +62,7 @@ pub(crate) fn compile(options: Options) -> ProgramResult { let mut bc_path = PathBuf::new(); bc_path.push(&format!("{path}.bc")); codegen - .compile(&bc_path) + .compile(&bc_path, options.print_llvm_ir) .map_err(|e| Exit::LLVMError(e.to_string()))?; std::process::Command::new("llc") .arg(&bc_path) From 942724104148b7b926906089ac069800cf5a48a0 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 13 Jul 2023 22:07:54 +0200 Subject: [PATCH 04/27] free globals at end of program --- compiler/backend_inkwell/candy_rt.c | 8 ++++++++ compiler/backend_inkwell/src/lib.rs | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/compiler/backend_inkwell/candy_rt.c b/compiler/backend_inkwell/candy_rt.c index d55ba55b5..39a64e005 100644 --- a/compiler/backend_inkwell/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt.c @@ -164,3 +164,11 @@ void candy_panic(candy_value_t *reason) printf("\n"); abort(); } + +void free_candy_value(candy_value_t *value) +{ + if (value != candy_environment) + { + free(value); + } +} \ No newline at end of file diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index dfd35f1c5..1632750e9 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -73,6 +73,9 @@ impl<'ctx> CodeGen<'ctx> { false, ); self.module.add_function("candy_panic", panic_fn_type, None); + let free_fn = self + .module + .add_function("free_candy_value", panic_fn_type, None); let main_type = i32_type.fn_type(&[], false); let main_fn = self.module.add_function("main", main_type, None); @@ -88,6 +91,12 @@ impl<'ctx> CodeGen<'ctx> { &[environment.as_basic_value_enum().into()], "", ); + for value in self.module.get_globals() { + let val = self + .builder + .build_load(candy_type_ptr, 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)); if print_llvm_ir { From 7a4e8a2934eaee8392bd81b5cfa7d7c1a548dbf7 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Fri, 14 Jul 2023 14:16:05 +0200 Subject: [PATCH 05/27] support ifElse and printing main output --- compiler/backend_inkwell/candy_rt.c | 52 ++++++++- compiler/backend_inkwell/src/lib.rs | 165 +++++++++++++++++++++++----- compiler/cli/src/inkwell.rs | 5 +- 3 files changed, 188 insertions(+), 34 deletions(-) diff --git a/compiler/backend_inkwell/candy_rt.c b/compiler/backend_inkwell/candy_rt.c index 39a64e005..c9b03abe4 100644 --- a/compiler/backend_inkwell/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt.c @@ -56,6 +56,18 @@ candy_value_t *to_candy_bool(int value) } } +int candy_tag_to_bool(candy_value_t *value) +{ + if (strcmp(value->value.text, "True") == 0) + { + return 1; + } + else + { + return 0; + } +} + void print_candy_value(candy_value_t *value) { switch (value->type) @@ -86,7 +98,8 @@ candy_value_t *make_candy_int(int128_t value) candy_value_t *make_candy_text(char *text) { candy_value_t *candy_value = malloc(sizeof(candy_value_t)); - candy_value->value.text = text; + candy_value->value.text = malloc(sizeof(char) * strlen(text)); + strcpy(candy_value->value.text, text); candy_value->type = CANDY_TYPE_TEXT; return candy_value; } @@ -94,7 +107,8 @@ candy_value_t *make_candy_text(char *text) candy_value_t *make_candy_tag(char *tag) { candy_value_t *candy_value = malloc(sizeof(candy_value_t)); - candy_value->value.text = tag; + candy_value->value.text = malloc(sizeof(char) * strlen(tag)); + strcpy(candy_value->value.text, tag); candy_value->type = CANDY_TYPE_TAG; return candy_value; } @@ -126,9 +140,9 @@ candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right) } } -candy_value_t *candy_builtin_ifelse(candy_type_t *condition, candy_value_t *then, candy_value_t *otherwise) +candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise) { - if (condition) + if (candy_tag_to_bool(condition)) { return then->value.function(); } @@ -138,6 +152,32 @@ candy_value_t *candy_builtin_ifelse(candy_type_t *condition, candy_value_t *then } } +candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right) +{ + return make_candy_int(left->value.integer + right->value.integer); +} + +candy_value_t *candy_builtin_int_bit_length(candy_value_t *value) +{ + // This is the max size in the VM. Unsure if it applies here. + return make_candy_int(62); +} + +candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right) +{ + return make_candy_int(left->value.integer & right->value.integer); +} + +candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t *right) +{ + return make_candy_int(left->value.integer | right->value.integer); +} + +candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right) +{ + return make_candy_int(left->value.integer ^ right->value.integer); +} + candy_value_t *candy_builtin_typeof(candy_value_t *value) { switch (value->type) @@ -169,6 +209,10 @@ void free_candy_value(candy_value_t *value) { if (value != candy_environment) { + if (value->type == CANDY_TYPE_TAG || value->type == CANDY_TYPE_TEXT) + { + free(value->value.text); + } free(value); } } \ No newline at end of file diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 1632750e9..5d1900a3d 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -43,7 +43,12 @@ impl<'ctx> CodeGen<'ctx> { } } - pub fn compile(mut self, path: &Path, print_llvm_ir: bool) -> Result<(), LLVMString> { + pub fn compile( + mut self, + path: &Path, + print_llvm_ir: bool, + print_main_output: bool, + ) -> Result<(), LLVMString> { let i32_type = self.context.i32_type(); let i128_type = self.context.i64_type(); let i8_type = self.context.i8_type(); @@ -76,6 +81,9 @@ impl<'ctx> CodeGen<'ctx> { let free_fn = self .module .add_function("free_candy_value", panic_fn_type, None); + let print_fn = self + .module + .add_function("print_candy_value", panic_fn_type, None); let main_type = i32_type.fn_type(&[], false); let main_fn = self.module.add_function("main", main_type, None); @@ -86,16 +94,27 @@ impl<'ctx> CodeGen<'ctx> { let environment = self .module .add_global(candy_type_ptr, None, "candy_environment"); - self.builder.build_call( + let main_res_ptr = self.builder.build_call( self.module.get_function("candy_main").unwrap(), &[environment.as_basic_value_enum().into()], "", ); - for value in self.module.get_globals() { - let val = self - .builder - .build_load(candy_type_ptr, value.as_pointer_value(), ""); - self.builder.build_call(free_fn, &[val.into()], ""); + if print_main_output { + let main_res = self.builder.build_load( + candy_type_ptr, + main_res_ptr + .try_as_basic_value() + .unwrap_left() + .into_pointer_value(), + "", + ); + self.builder.build_call(print_fn, &[main_res.into()], ""); + for value in self.module.get_globals() { + let val = self + .builder + .build_load(candy_type_ptr, 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)); @@ -129,6 +148,11 @@ impl<'ctx> CodeGen<'ctx> { ); self.values.insert(*id, Rc::new(global)); + + if idx == mir.expressions.len() - 1 { + self.builder + .build_return(Some(&global.as_basic_value_enum())); + } } candy_frontend::mir::Expression::Text(text) => { let i32_type = self.context.i32_type(); @@ -157,9 +181,13 @@ impl<'ctx> CodeGen<'ctx> { call.try_as_basic_value().unwrap_left(), ); - //let tag = i32_type.const_int(self.tags.len().try_into().unwrap(), false); global.set_initializer(&candy_type_ptr.const_null()); self.values.insert(*id, Rc::new(global)); + + if idx == mir.expressions.len() - 1 { + self.builder + .build_return(Some(&global.as_basic_value_enum())); + } } candy_frontend::mir::Expression::Tag { symbol, value } => { self.tags.insert(symbol.clone(), *value); @@ -192,6 +220,11 @@ impl<'ctx> CodeGen<'ctx> { //let tag = i32_type.const_int(self.tags.len().try_into().unwrap(), false); global.set_initializer(&candy_type_ptr.const_null()); self.values.insert(*id, Rc::new(global)); + + if idx == mir.expressions.len() - 1 { + self.builder + .build_return(Some(&global.as_basic_value_enum())); + } } candy_frontend::mir::Expression::Builtin(builtin) => match builtin { candy_frontend::builtin_functions::BuiltinFunction::ChannelCreate => { @@ -239,14 +272,78 @@ impl<'ctx> CodeGen<'ctx> { .add_function("candy_builtin_ifelse", fn_type, None); self.functions.insert(*id, (Rc::new(function), vec![])); } - candy_frontend::builtin_functions::BuiltinFunction::IntAdd => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntBitLength => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntAdd => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = + self.module + .add_function("candy_builtin_int_add", fn_type, None); + self.functions.insert(*id, (Rc::new(function), vec![])); + } + candy_frontend::builtin_functions::BuiltinFunction::IntBitLength => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); + let function = + self.module + .add_function("candy_builtin_int_bit_length", fn_type, None); + self.functions.insert(*id, (Rc::new(function), vec![])); + } candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseAnd => { - todo!() + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = self.module.add_function( + "candy_builtin_int_bitwise_and", + fn_type, + None, + ); + self.functions.insert(*id, (Rc::new(function), vec![])); + } + candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseOr => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = + self.module + .add_function("candy_builtin_int_bitwise_or", fn_type, None); + self.functions.insert(*id, (Rc::new(function), vec![])); } - candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseOr => todo!(), candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseXor => { - todo!() + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = self.module.add_function( + "candy_builtin_int_bitwise_xor", + fn_type, + None, + ); + self.functions.insert(*id, (Rc::new(function), vec![])); } candy_frontend::builtin_functions::BuiltinFunction::IntCompareTo => todo!(), candy_frontend::builtin_functions::BuiltinFunction::IntDivideTruncating => { @@ -333,10 +430,14 @@ impl<'ctx> CodeGen<'ctx> { body, .. } => { - let original_name = &original_hirs.iter().next().unwrap().keys[0].to_string(); - let name = match original_name.as_str() { - "main" => "candy_main", - other => other, + //let original_name = &original_hirs.iter().next().unwrap().keys[0].to_string(); + let original_name = format!("{original_hirs:?}") + .replace('{', "") + .replace('}', ""); + let name = if original_name.ends_with("main") { + "candy_main" + } else { + &original_name }; let candy_type_ptr = self @@ -353,6 +454,8 @@ impl<'ctx> CodeGen<'ctx> { self.functions .insert(*id, (Rc::new(function), parameters.clone())); + let main_blocks = self.module.get_function("main").unwrap().get_basic_blocks(); + self.builder.position_at_end(main_blocks[0]); let function_ptr = function.as_global_value().as_pointer_value(); let make_candy_function = self.module.get_function("make_candy_function").unwrap(); @@ -362,10 +465,13 @@ impl<'ctx> CodeGen<'ctx> { let global = self.module.add_global(candy_type_ptr, None, ""); global.set_initializer(&function_ptr.get_type().const_null()); + let current_block = self.builder.get_insert_block().unwrap(); + self.builder.build_store( global.as_pointer_value(), call.try_as_basic_value().unwrap_left(), ); + self.builder.position_at_end(current_block); self.values.insert(*id, Rc::new(global)); @@ -373,7 +479,7 @@ impl<'ctx> CodeGen<'ctx> { self.builder.position_at_end(inner_block); self.compile_mir(body); } - candy_frontend::mir::Expression::Parameter => todo!(), + candy_frontend::mir::Expression::Parameter => unreachable!(), candy_frontend::mir::Expression::Call { function, arguments, @@ -391,9 +497,13 @@ impl<'ctx> CodeGen<'ctx> { .iter() .map(|arg| { let v = match self.values.get(arg) { - Some(value) => Some(value.as_pointer_value()), + Some(value) => Some(self.builder.build_load( + candy_type_ptr, + value.as_pointer_value(), + "", + )), None => match self.locals.get(arg) { - Some(value) => Some(value.into_pointer_value()), + Some(value) => Some(value.as_basic_value_enum()), None => match self .functions .values() @@ -401,20 +511,17 @@ impl<'ctx> CodeGen<'ctx> { { Some((fun, args)) => { let idx = args.iter().position(|i| i == arg).unwrap(); - Some( - fun.get_nth_param(idx as u32) - .unwrap() - .into_pointer_value(), - ) + Some(fun.get_nth_param(idx as u32).unwrap()) } - None => Some(candy_type_ptr.const_null()), + None => Some(candy_type_ptr.const_null().into()), }, }, }; - self.builder - .build_load(candy_type_ptr, v.unwrap(), "") + //self.builder + // .build_load(candy_type_ptr, v.unwrap(), "") + // .into() + v.unwrap_or_else(|| panic!("{arg} should be a real ID")) .into() - //v.unwrap_or_else(|| panic!("{arg} should be a real ID")) }) .collect(); let call = self.builder.build_call(**fun, &args, ""); diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index faffc58f8..59aedddfb 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -17,6 +17,9 @@ pub(crate) struct Options { /// If enabled, the compiler will print the generated LLVM IR to stderr. #[arg(long = "print-llvm-ir", default_value_t = false)] print_llvm_ir: bool, + /// If enabled, the program will print the output of the candy main function. + #[arg(long = "print-main-output", default_value_t = false)] + print_main_output: bool, /// The file or package to run. If none is provided, the package of your /// current working directory will be run. #[arg(value_hint = ValueHint::FilePath)] @@ -62,7 +65,7 @@ pub(crate) fn compile(options: Options) -> ProgramResult { let mut bc_path = PathBuf::new(); bc_path.push(&format!("{path}.bc")); codegen - .compile(&bc_path, options.print_llvm_ir) + .compile(&bc_path, options.print_llvm_ir, options.print_main_output) .map_err(|e| Exit::LLVMError(e.to_string()))?; std::process::Command::new("llc") .arg(&bc_path) From 94f85cb1058735a8f8459bb4dc092aaa25fb920e Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 20 Jul 2023 23:21:15 +0200 Subject: [PATCH 06/27] add support for closures --- compiler/backend_inkwell/README.md | 9 + compiler/backend_inkwell/candy_rt.c | 25 +- compiler/backend_inkwell/src/lib.rs | 691 +++++++++++++++++----------- compiler/cli/src/inkwell.rs | 4 + 4 files changed, 461 insertions(+), 268 deletions(-) create mode 100644 compiler/backend_inkwell/README.md diff --git a/compiler/backend_inkwell/README.md b/compiler/backend_inkwell/README.md new file mode 100644 index 000000000..ba39ef9b5 --- /dev/null +++ b/compiler/backend_inkwell/README.md @@ -0,0 +1,9 @@ +# Candy Inkwell Compiler + +This crate provides an LLVM-based backend for the Candy compiler +using the inkwell crate. It is highly experimental and still +doesn't support many of Candy's features. + +## Requirements + +You currently need to have LLVM 15 installed on your system. You also need to have the `llc` and `clang` commands available. \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt.c b/compiler/backend_inkwell/candy_rt.c index c9b03abe4..c5b712de8 100644 --- a/compiler/backend_inkwell/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt.c @@ -15,6 +15,12 @@ typedef enum CANDY_TYPE_FUNCTION, } candy_type_t; +typedef struct +{ + struct candy_value *environemt; + struct candy_value *(*function)(struct candy_value *); +} candy_function_t; + typedef struct candy_value { union @@ -22,12 +28,12 @@ typedef struct candy_value int128_t integer; char *text; struct candy_value *list; - struct candy_value *(*function)(void); + candy_function_t function; } value; candy_type_t type; } candy_value_t; -typedef candy_value_t *(*candy_function)(void); +typedef candy_value_t *(*candy_function)(candy_value_t *); candy_value_t __internal_true = { .value = {.text = "True"}, @@ -113,11 +119,12 @@ candy_value_t *make_candy_tag(char *tag) return candy_value; } -candy_value_t *make_candy_function(candy_function function) +candy_value_t *make_candy_function(candy_function function, candy_value_t *environment) { candy_value_t *candy_value = malloc(sizeof(candy_value_t)); candy_value->type = CANDY_TYPE_FUNCTION; - candy_value->value.function = function; + candy_value->value.function.function = function; + candy_value->value.function.environemt = environment; return candy_value; } @@ -144,11 +151,15 @@ candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *the { if (candy_tag_to_bool(condition)) { - return then->value.function(); + candy_function then_function = (then->value).function.function; + candy_value_t *environment = (then->value).function.environemt; + return then_function(environment); } else { - return otherwise->value.function(); + candy_function otherwise_function = (otherwise->value).function.function; + candy_value_t *environment = (otherwise->value).function.environemt; + return otherwise_function(environment); } } @@ -202,7 +213,7 @@ void candy_panic(candy_value_t *reason) printf("The program panicked for the following reason: \n"); print_candy_value(reason); printf("\n"); - abort(); + exit(-1); } void free_candy_value(candy_value_t *value) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 5d1900a3d..4583bdcaf 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -4,14 +4,26 @@ use inkwell::{ context::Context, module::{Linkage, Module}, support::LLVMString, - values::{ - ArrayValue, BasicMetadataValueEnum, BasicValue, BasicValueEnum, FunctionValue, GlobalValue, - }, + types::{BasicType, StructType}, + values::{ArrayValue, BasicValue, BasicValueEnum, FunctionValue, GlobalValue}, AddressSpace, }; pub use inkwell; -use std::{collections::HashMap, path::Path, rc::Rc, sync::Arc}; +use std::{ + collections::{HashMap, HashSet}, + path::Path, + rc::Rc, + sync::Arc, +}; + +#[derive(Clone)] +struct FunctionInfo<'ctx> { + function_value: Rc>, + parameters: Vec, + captured_ids: Vec, + env_type: Option>, +} pub struct CodeGen<'ctx> { context: &'ctx Context, @@ -21,7 +33,8 @@ pub struct CodeGen<'ctx> { tags: HashMap>, values: HashMap>>, locals: HashMap>>, - functions: HashMap>, Vec)>, + functions: HashMap>, + unrepresented_ids: HashSet, } impl<'ctx> CodeGen<'ctx> { @@ -40,6 +53,7 @@ impl<'ctx> CodeGen<'ctx> { values: HashMap::new(), locals: HashMap::new(), functions: HashMap::new(), + unrepresented_ids: HashSet::new(), } } @@ -66,8 +80,8 @@ impl<'ctx> CodeGen<'ctx> { .add_function("make_candy_tag", make_tag_fn_type, Some(Linkage::External)); self.module .add_function("make_candy_text", make_tag_fn_type, Some(Linkage::External)); - //let candy_fn_type = candy_type_ptr.fn_type(&[], false); - let make_function_fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); + let make_function_fn_type = + candy_type_ptr.fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); self.module.add_function( "make_candy_function", make_function_fn_type, @@ -88,8 +102,16 @@ impl<'ctx> CodeGen<'ctx> { let main_type = i32_type.fn_type(&[], false); let main_fn = self.module.add_function("main", main_type, None); let block = self.context.append_basic_block(main_fn, "entry"); + + let main_info = FunctionInfo { + function_value: Rc::new(main_fn), + parameters: vec![], + captured_ids: vec![], + env_type: None, + }; + self.builder.position_at_end(block); - self.compile_mir(&self.mir.body.clone()); + self.compile_mir(&self.mir.body.clone(), &main_info); self.builder.position_at_end(block); let environment = self .module @@ -126,9 +148,8 @@ impl<'ctx> CodeGen<'ctx> { Ok(()) } - pub fn compile_mir(&mut self, mir: &Body) { + fn compile_mir(&mut self, mir: &Body, function_ctx: &FunctionInfo<'ctx>) { for (idx, (id, expr)) in mir.expressions.iter().enumerate() { - //dbg!(expr); match expr { candy_frontend::mir::Expression::Int(value) => { let i128_type = self.context.i64_type(); @@ -217,7 +238,6 @@ impl<'ctx> CodeGen<'ctx> { call.try_as_basic_value().unwrap_left(), ); - //let tag = i32_type.const_int(self.tags.len().try_into().unwrap(), false); global.set_initializer(&candy_type_ptr.const_null()); self.values.insert(*id, Rc::new(global)); @@ -226,211 +246,309 @@ impl<'ctx> CodeGen<'ctx> { .build_return(Some(&global.as_basic_value_enum())); } } - candy_frontend::mir::Expression::Builtin(builtin) => match builtin { - candy_frontend::builtin_functions::BuiltinFunction::ChannelCreate => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::ChannelSend => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ChannelReceive => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::Equals => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); - let function = - self.module - .add_function("candy_builtin_equals", fn_type, None); - self.functions.insert(*id, (Rc::new(function), vec![])); - } - candy_frontend::builtin_functions::BuiltinFunction::FunctionRun => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::GetArgumentCount => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::IfElse => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - - let fn_type = candy_type_ptr.fn_type( - &[ - candy_type_ptr.into(), - candy_type_ptr.into(), - candy_type_ptr.into(), - ], - false, - ); - let function = - self.module - .add_function("candy_builtin_ifelse", fn_type, None); - self.functions.insert(*id, (Rc::new(function), vec![])); - } - candy_frontend::builtin_functions::BuiltinFunction::IntAdd => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); - let function = - self.module - .add_function("candy_builtin_int_add", fn_type, None); - self.functions.insert(*id, (Rc::new(function), vec![])); - } - candy_frontend::builtin_functions::BuiltinFunction::IntBitLength => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - - let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); - let function = - self.module - .add_function("candy_builtin_int_bit_length", fn_type, None); - self.functions.insert(*id, (Rc::new(function), vec![])); - } - candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseAnd => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); - let function = self.module.add_function( - "candy_builtin_int_bitwise_and", - fn_type, - None, - ); - self.functions.insert(*id, (Rc::new(function), vec![])); - } - candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseOr => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); - let function = - self.module - .add_function("candy_builtin_int_bitwise_or", fn_type, None); - self.functions.insert(*id, (Rc::new(function), vec![])); - } - candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseXor => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); - let function = self.module.add_function( - "candy_builtin_int_bitwise_xor", - fn_type, - None, - ); - self.functions.insert(*id, (Rc::new(function), vec![])); - } - candy_frontend::builtin_functions::BuiltinFunction::IntCompareTo => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntDivideTruncating => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::IntModulo => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntMultiply => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntParse => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntRemainder => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntShiftLeft => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntShiftRight => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::IntSubtract => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ListFilled => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ListGet => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ListInsert => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ListLength => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ListRemoveAt => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ListReplace => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::Parallel => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::Print => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::StructGet => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::StructGetKeys => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::StructHasKey => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TagGetValue => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TagHasValue => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TagWithoutValue => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::TextCharacters => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::TextConcatenate => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::TextContains => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextEndsWith => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextFromUtf8 => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextGetRange => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextIsEmpty => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextLength => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextStartsWith => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::TextTrimEnd => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextTrimStart => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::ToDebugText => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::Try => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TypeOf => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - - let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); - let function = - self.module - .add_function("candy_builtin_typeof", fn_type, None); - self.functions.insert(*id, (Rc::new(function), vec![])); + candy_frontend::mir::Expression::Builtin(builtin) => { + match builtin { + candy_frontend::builtin_functions::BuiltinFunction::ChannelCreate => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::ChannelSend => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ChannelReceive => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::Equals => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = + self.module + .add_function("candy_builtin_equals", fn_type, None); + self.functions.insert( + *id, + FunctionInfo { + function_value: Rc::new(function), + parameters: vec![], + captured_ids: vec![], + env_type: None, + }, + ); + } + candy_frontend::builtin_functions::BuiltinFunction::FunctionRun => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::GetArgumentCount => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::IfElse => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr.fn_type( + &[ + candy_type_ptr.into(), + candy_type_ptr.into(), + candy_type_ptr.into(), + ], + false, + ); + let function = + self.module + .add_function("candy_builtin_ifelse", fn_type, None); + self.functions.insert( + *id, + FunctionInfo { + function_value: Rc::new(function), + parameters: vec![], + captured_ids: vec![], + env_type: None, + }, + ); + } + candy_frontend::builtin_functions::BuiltinFunction::IntAdd => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = + self.module + .add_function("candy_builtin_int_add", fn_type, None); + self.functions.insert( + *id, + FunctionInfo { + function_value: Rc::new(function), + parameters: vec![], + captured_ids: vec![], + env_type: None, + }, + ); + } + candy_frontend::builtin_functions::BuiltinFunction::IntBitLength => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); + let function = self.module.add_function( + "candy_builtin_int_bit_length", + fn_type, + None, + ); + self.functions.insert( + *id, + FunctionInfo { + function_value: Rc::new(function), + parameters: vec![], + captured_ids: vec![], + env_type: None, + }, + ); + } + candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseAnd => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = self.module.add_function( + "candy_builtin_int_bitwise_and", + fn_type, + None, + ); + self.functions.insert( + *id, + FunctionInfo { + function_value: Rc::new(function), + parameters: vec![], + captured_ids: vec![], + env_type: None, + }, + ); + } + candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseOr => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = self.module.add_function( + "candy_builtin_int_bitwise_or", + fn_type, + None, + ); + self.functions.insert( + *id, + FunctionInfo { + function_value: Rc::new(function), + parameters: vec![], + captured_ids: vec![], + env_type: None, + }, + ); + } + candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseXor => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = self.module.add_function( + "candy_builtin_int_bitwise_xor", + fn_type, + None, + ); + self.functions.insert( + *id, + FunctionInfo { + function_value: Rc::new(function), + parameters: vec![], + captured_ids: vec![], + env_type: None, + }, + ); + } + candy_frontend::builtin_functions::BuiltinFunction::IntCompareTo => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntDivideTruncating => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::IntModulo => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntMultiply => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntParse => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntRemainder => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntShiftLeft => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntShiftRight => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::IntSubtract => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListFilled => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListGet => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListInsert => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListLength => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListRemoveAt => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::ListReplace => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::Parallel => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::Print => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::StructGet => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::StructGetKeys => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::StructHasKey => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TagGetValue => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TagHasValue => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TagWithoutValue => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::TextCharacters => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::TextConcatenate => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::TextContains => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextEndsWith => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextFromUtf8 => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextGetRange => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextIsEmpty => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextLength => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextStartsWith => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::TextTrimEnd => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TextTrimStart => { + todo!() + } + candy_frontend::builtin_functions::BuiltinFunction::ToDebugText => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::Try => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::TypeOf => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); + let function = + self.module + .add_function("candy_builtin_typeof", fn_type, None); + self.functions.insert( + *id, + FunctionInfo { + function_value: Rc::new(function), + parameters: vec![], + captured_ids: vec![], + env_type: None, + }, + ); + } } - }, + self.unrepresented_ids.insert(*id); + } candy_frontend::mir::Expression::List(_) => todo!(), candy_frontend::mir::Expression::Struct(_s) => { // Not yet implemented, but not allowed to panic - self.context.struct_type(&[], false); } candy_frontend::mir::Expression::Reference(id) => { if let Some(v) = self.values.get(id) { self.builder.build_return(Some(&v.as_pointer_value())); } } - candy_frontend::mir::Expression::HirId(_id) => { - // Intentionally ignored + candy_frontend::mir::Expression::HirId(hir_id) => { + let i32_type = self.context.i32_type(); + let i8_type = self.context.i8_type(); + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let text = format!("{hir_id}"); + + let global = self.module.add_global(candy_type_ptr, None, &text); + let v = self.make_str_literal(&text); + let len = i32_type.const_int(text.len() as u64, false); + let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); + self.builder.build_store(arr_alloc, v); + let cast = self.builder.build_bitcast( + arr_alloc, + i8_type.ptr_type(AddressSpace::default()), + "", + ); + let make_candy_text = self.module.get_function("make_candy_text").unwrap(); + let call = self.builder.build_call(make_candy_text, &[cast.into()], ""); + + self.builder.build_store( + global.as_pointer_value(), + call.try_as_basic_value().unwrap_left(), + ); + + global.set_initializer(&candy_type_ptr.const_null()); + self.values.insert(*id, Rc::new(global)); } candy_frontend::mir::Expression::Function { original_hirs, parameters, body, - .. + responsible_parameter, } => { - //let original_name = &original_hirs.iter().next().unwrap().keys[0].to_string(); let original_name = format!("{original_hirs:?}") .replace('{', "") .replace('}', ""); @@ -440,44 +558,91 @@ impl<'ctx> CodeGen<'ctx> { &original_name }; + self.unrepresented_ids.insert(*responsible_parameter); + let candy_type_ptr = self .module .get_struct_type("candy_type") .unwrap() .ptr_type(AddressSpace::default()); - let params: Vec<_> = parameters.iter().map(|_| candy_type_ptr.into()).collect(); + let captured_ids: Vec<_> = expr + .captured_ids() + .into_iter() + .filter(|cap_id| { + !(self.values.contains_key(cap_id) + || self.unrepresented_ids.contains(cap_id)) + }) + .collect(); + + let env_types: Vec<_> = captured_ids + .iter() + .map(|_| candy_type_ptr.as_basic_type_enum()) + .collect(); + + let env_struct_type = self.context.struct_type(&env_types, false); + + let env_ptr = self.builder.build_alloca(env_struct_type, ""); + + let env_struct = self.builder.build_load(env_struct_type, env_ptr, ""); + for (idx, cap_id) in captured_ids.iter().enumerate() { + let value = self.locals.get(cap_id).unwrap(); + self.builder.build_insert_value( + env_struct.into_struct_value(), + *(value.clone()), + idx as u32, + "", + ); + } + + let mut params: Vec<_> = + parameters.iter().map(|_| candy_type_ptr.into()).collect(); + if !captured_ids.is_empty() { + params.push(candy_type_ptr.into()); + } let fn_type = candy_type_ptr.fn_type(¶ms, false); let function = self.module.add_function(name, fn_type, None); - self.functions - .insert(*id, (Rc::new(function), parameters.clone())); + let fun_info = FunctionInfo { + function_value: Rc::new(function), + parameters: parameters.clone(), + captured_ids: captured_ids.clone(), + + env_type: Some(env_struct_type), + }; + self.functions.insert(*id, fun_info.clone()); + + for (id, param) in parameters.iter().zip(function.get_params()) { + self.locals.insert(*id, Rc::new(param)); + } + + let current_block = self.builder.get_insert_block().unwrap(); - let main_blocks = self.module.get_function("main").unwrap().get_basic_blocks(); - self.builder.position_at_end(main_blocks[0]); let function_ptr = function.as_global_value().as_pointer_value(); let make_candy_function = self.module.get_function("make_candy_function").unwrap(); - let call = - self.builder - .build_call(make_candy_function, &[function_ptr.into()], ""); + let call = self.builder.build_call( + make_candy_function, + &[function_ptr.into(), env_ptr.into()], + "", + ); let global = self.module.add_global(candy_type_ptr, None, ""); global.set_initializer(&function_ptr.get_type().const_null()); - let current_block = self.builder.get_insert_block().unwrap(); self.builder.build_store( global.as_pointer_value(), call.try_as_basic_value().unwrap_left(), ); - self.builder.position_at_end(current_block); self.values.insert(*id, Rc::new(global)); let inner_block = self.context.append_basic_block(function, name); self.builder.position_at_end(inner_block); - self.compile_mir(body); + + self.compile_mir(body, &fun_info); + self.builder.position_at_end(current_block); } candy_frontend::mir::Expression::Parameter => unreachable!(), candy_frontend::mir::Expression::Call { @@ -485,7 +650,12 @@ impl<'ctx> CodeGen<'ctx> { arguments, .. } => { - let (fun, _params) = self.functions.get(function).unwrap(); + let FunctionInfo { + function_value, + parameters, + captured_ids: _, + env_type: _, + } = self.functions.get(function).unwrap(); let candy_type_ptr = self .module @@ -496,35 +666,45 @@ impl<'ctx> CodeGen<'ctx> { let args: Vec<_> = arguments .iter() .map(|arg| { - let v = match self.values.get(arg) { - Some(value) => Some(self.builder.build_load( - candy_type_ptr, - value.as_pointer_value(), - "", - )), - None => match self.locals.get(arg) { - Some(value) => Some(value.as_basic_value_enum()), - None => match self - .functions - .values() - .find(|(_, args)| args.contains(arg)) - { - Some((fun, args)) => { - let idx = args.iter().position(|i| i == arg).unwrap(); - Some(fun.get_nth_param(idx as u32).unwrap()) - } - None => Some(candy_type_ptr.const_null().into()), - }, - }, - }; - //self.builder - // .build_load(candy_type_ptr, v.unwrap(), "") - // .into() + let mut v = self.values.get(arg).map(|a| { + self.builder + .build_load(candy_type_ptr, a.as_pointer_value(), "") + }); + if v.is_none() { + if let Some(i) = parameters.iter().position(|i| i == arg) { + v.replace(function_value.get_nth_param(i as u32).unwrap()); + } + } + if v.is_none() { + if let Some(i) = + function_ctx.captured_ids.iter().position(|i| i == arg) + { + let env_ptr = + function_ctx.function_value.get_last_param().unwrap(); + + dbg!(arg, i, env_ptr); + let env_value = self.builder.build_struct_gep( + function_ctx.env_type.unwrap(), + env_ptr.into_pointer_value(), + i as u32, + "", + ); + + if let Ok(env_value) = env_value { + v.replace(env_value.as_basic_value_enum()); + } + } + } + if v.is_none() { + if let Some(value) = self.locals.get(arg) { + v.replace(*value.clone()); + } + } v.unwrap_or_else(|| panic!("{arg} should be a real ID")) .into() }) .collect(); - let call = self.builder.build_call(**fun, &args, ""); + let call = self.builder.build_call(**function_value, &args, ""); let call_value = Rc::new(call.try_as_basic_value().unwrap_left()); self.locals.insert(*id, call_value.clone()); @@ -536,22 +716,27 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::mir::Expression::UseModule { .. } => unreachable!(), candy_frontend::mir::Expression::Panic { reason, .. } => { let panic_fn = self.module.get_function("candy_panic").unwrap(); + + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + if let Some(reason) = self.values.get(reason) { - self.builder - .build_call(panic_fn, &[reason.as_pointer_value().into()], ""); + let reason = + self.builder + .build_load(candy_type_ptr, reason.as_pointer_value(), ""); + self.builder.build_call(panic_fn, &[reason.into()], ""); } else { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - self.builder.build_call( panic_fn, &[candy_type_ptr.const_null().into()], "", ); } + + self.builder.build_unreachable(); } candy_frontend::mir::Expression::TraceCallStarts { .. } => unimplemented!(), candy_frontend::mir::Expression::TraceCallEnds { .. } => unimplemented!(), @@ -574,20 +759,4 @@ impl<'ctx> CodeGen<'ctx> { .collect(); i8_type.const_array(&content) } - - fn _get_value_by_id(&self, id: &Id) -> Option { - match self.values.get(id) { - Some(value) => Some(value.as_pointer_value().into()), - None => match self.locals.get(id) { - Some(value) => Some(value.into_pointer_value().into()), - None => match self.functions.values().find(|(_, args)| args.contains(id)) { - Some((fun, args)) => { - let idx = args.iter().position(|i| i == id).unwrap(); - Some(fun.get_nth_param(idx as u32).unwrap().into()) - } - None => None, - }, - }, - } - } } diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 59aedddfb..e0119e14a 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -20,6 +20,9 @@ pub(crate) struct Options { /// If enabled, the program will print the output of the candy main function. #[arg(long = "print-main-output", default_value_t = false)] print_main_output: bool, + /// If enabled, the LLVM bitcode will be compiled with debug information. + #[arg(short = 'g', default_value_t = false)] + debug: bool, /// The file or package to run. If none is provided, the package of your /// current working directory will be run. #[arg(value_hint = ValueHint::FilePath)] @@ -79,6 +82,7 @@ pub(crate) fn compile(options: Options) -> ProgramResult { .args([ "compiler/backend_inkwell/candy_rt.c", s_path.to_str().unwrap(), + if options.debug { "-g" } else { "" }, "-o", &s_path.to_str().unwrap().replace(".candy.s", ""), ]) From 8e7eb44e633cd38a7946bf8882892fba8def58ca Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Fri, 21 Jul 2023 12:19:10 +0200 Subject: [PATCH 07/27] deref value when using reference instruction --- compiler/backend_inkwell/candy_rt.c | 86 ++++++++++++------- compiler/backend_inkwell/src/lib.rs | 123 ++++++++++++++++++++++++---- 2 files changed, 166 insertions(+), 43 deletions(-) diff --git a/compiler/backend_inkwell/candy_rt.c b/compiler/backend_inkwell/candy_rt.c index c5b712de8..10342486e 100644 --- a/compiler/backend_inkwell/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt.c @@ -17,7 +17,7 @@ typedef enum typedef struct { - struct candy_value *environemt; + struct candy_value *environment; struct candy_value *(*function)(struct candy_value *); } candy_function_t; @@ -50,6 +50,25 @@ candy_value_t _candy_environment = { // Not particularly elegant, but this is a temporary solution anyway... candy_value_t *candy_environment = &_candy_environment; +void print_candy_value(candy_value_t *value) +{ + switch (value->type) + { + case CANDY_TYPE_INT: + printf("%lld", value->value.integer); + break; + case CANDY_TYPE_TEXT: + printf("%s", value->value.text); + break; + case CANDY_TYPE_TAG: + printf("%s", value->value.text); + break; + default: + printf("", value->type); + break; + } +} + candy_value_t *to_candy_bool(int value) { if (value) @@ -68,28 +87,16 @@ int candy_tag_to_bool(candy_value_t *value) { return 1; } - else + else if (strcmp(value->value.text, "False") == 0) { return 0; } -} - -void print_candy_value(candy_value_t *value) -{ - switch (value->type) + else { - case CANDY_TYPE_INT: - printf("%lld", value->value.integer); - break; - case CANDY_TYPE_TEXT: - printf("%s", value->value.text); - break; - case CANDY_TYPE_TAG: - printf("%s", value->value.text); - break; - default: - printf("", value->type); - break; + printf("Got invalid value "); + print_candy_value(value); + printf("\n"); + exit(-1); } } @@ -124,7 +131,7 @@ candy_value_t *make_candy_function(candy_function function, candy_value_t *envir candy_value_t *candy_value = malloc(sizeof(candy_value_t)); candy_value->type = CANDY_TYPE_FUNCTION; candy_value->value.function.function = function; - candy_value->value.function.environemt = environment; + candy_value->value.function.environment = environment; return candy_value; } @@ -152,13 +159,13 @@ candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *the if (candy_tag_to_bool(condition)) { candy_function then_function = (then->value).function.function; - candy_value_t *environment = (then->value).function.environemt; + candy_value_t *environment = (then->value).function.environment; return then_function(environment); } else { candy_function otherwise_function = (otherwise->value).function.function; - candy_value_t *environment = (otherwise->value).function.environemt; + candy_value_t *environment = (otherwise->value).function.environment; return otherwise_function(environment); } } @@ -168,6 +175,11 @@ candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right) return make_candy_int(left->value.integer + right->value.integer); } +candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *right) +{ + return make_candy_int(left->value.integer - right->value.integer); +} + candy_value_t *candy_builtin_int_bit_length(candy_value_t *value) { // This is the max size in the VM. Unsure if it applies here. @@ -189,22 +201,40 @@ candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t return make_candy_int(left->value.integer ^ right->value.integer); } +candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right) +{ + int128_t left_value = left->value.integer; + int128_t right_value = right->value.integer; + if (left_value < right_value) + { + return make_candy_tag("Less"); + } + else if (left_value == right_value) + { + return make_candy_tag("Equal"); + } + else + { + return make_candy_tag("Greater"); + } +} + candy_value_t *candy_builtin_typeof(candy_value_t *value) { switch (value->type) { case CANDY_TYPE_INT: - return make_candy_tag("int"); + return make_candy_tag("Int"); case CANDY_TYPE_TEXT: - return make_candy_tag("text"); + return make_candy_tag("Text"); case CANDY_TYPE_TAG: - return make_candy_tag("tag"); + return make_candy_tag("Tag"); case CANDY_TYPE_LIST: - return make_candy_tag("list"); + return make_candy_tag("List"); case CANDY_TYPE_STRUCT: - return make_candy_tag("struct"); + return make_candy_tag("Struct"); case CANDY_TYPE_FUNCTION: - return make_candy_tag("function"); + return make_candy_tag("Function"); } } diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 4583bdcaf..48101fe68 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -122,15 +122,11 @@ impl<'ctx> CodeGen<'ctx> { "", ); if print_main_output { - let main_res = self.builder.build_load( - candy_type_ptr, - main_res_ptr - .try_as_basic_value() - .unwrap_left() - .into_pointer_value(), + self.builder.build_call( + print_fn, + &[main_res_ptr.try_as_basic_value().unwrap_left().into()], "", ); - self.builder.build_call(print_fn, &[main_res.into()], ""); for value in self.module.get_globals() { let val = self .builder @@ -426,7 +422,30 @@ impl<'ctx> CodeGen<'ctx> { }, ); } - candy_frontend::builtin_functions::BuiltinFunction::IntCompareTo => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntCompareTo => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = self.module.add_function( + "candy_builtin_int_compareto", + fn_type, + None, + ); + self.functions.insert( + *id, + FunctionInfo { + function_value: Rc::new(function), + parameters: vec![], + captured_ids: vec![], + env_type: None, + }, + ); + } candy_frontend::builtin_functions::BuiltinFunction::IntDivideTruncating => { todo!() } @@ -438,7 +457,30 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::builtin_functions::BuiltinFunction::IntShiftRight => { todo!() } - candy_frontend::builtin_functions::BuiltinFunction::IntSubtract => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::IntSubtract => { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let fn_type = candy_type_ptr + .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let function = self.module.add_function( + "candy_builtin_int_subtract", + fn_type, + None, + ); + self.functions.insert( + *id, + FunctionInfo { + function_value: Rc::new(function), + parameters: vec![], + captured_ids: vec![], + env_type: None, + }, + ); + } candy_frontend::builtin_functions::BuiltinFunction::ListFilled => todo!(), candy_frontend::builtin_functions::BuiltinFunction::ListGet => todo!(), candy_frontend::builtin_functions::BuiltinFunction::ListInsert => todo!(), @@ -508,7 +550,15 @@ impl<'ctx> CodeGen<'ctx> { } candy_frontend::mir::Expression::Reference(id) => { if let Some(v) = self.values.get(id) { - self.builder.build_return(Some(&v.as_pointer_value())); + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + let value = + self.builder + .build_load(candy_type_ptr, v.as_pointer_value(), ""); + self.builder.build_return(Some(&value)); } } candy_frontend::mir::Expression::HirId(hir_id) => { @@ -558,7 +608,36 @@ impl<'ctx> CodeGen<'ctx> { &original_name }; - self.unrepresented_ids.insert(*responsible_parameter); + let i32_type = self.context.i32_type(); + let i8_type = self.context.i8_type(); + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + + let text = format!("{responsible_parameter}"); + + let global = self.module.add_global(candy_type_ptr, None, &text); + let v = self.make_str_literal(&text); + let len = i32_type.const_int(text.len() as u64, false); + let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); + self.builder.build_store(arr_alloc, v); + let cast = self.builder.build_bitcast( + arr_alloc, + i8_type.ptr_type(AddressSpace::default()), + "", + ); + let make_candy_text = self.module.get_function("make_candy_text").unwrap(); + let call = self.builder.build_call(make_candy_text, &[cast.into()], ""); + + self.builder.build_store( + global.as_pointer_value(), + call.try_as_basic_value().unwrap_left(), + ); + + global.set_initializer(&candy_type_ptr.const_null()); + self.values.insert(*responsible_parameter, Rc::new(global)); let candy_type_ptr = self .module @@ -586,10 +665,21 @@ impl<'ctx> CodeGen<'ctx> { let env_struct = self.builder.build_load(env_struct_type, env_ptr, ""); for (idx, cap_id) in captured_ids.iter().enumerate() { - let value = self.locals.get(cap_id).unwrap(); + let mut value = self.locals.get(cap_id).map(|v| **v); + + if value.is_none() { + value.replace( + (self + .values + .get(cap_id) + .unwrap_or_else(|| panic!("{cap_id} is not a global"))) + .as_basic_value_enum(), + ); + } + self.builder.build_insert_value( env_struct.into_struct_value(), - *(value.clone()), + value.unwrap(), idx as u32, "", ); @@ -604,6 +694,7 @@ impl<'ctx> CodeGen<'ctx> { let fn_type = candy_type_ptr.fn_type(¶ms, false); let function = self.module.add_function(name, fn_type, None); + let fun_info = FunctionInfo { function_value: Rc::new(function), parameters: parameters.clone(), @@ -655,7 +746,10 @@ impl<'ctx> CodeGen<'ctx> { parameters, captured_ids: _, env_type: _, - } = self.functions.get(function).unwrap(); + } = self + .functions + .get(function) + .unwrap_or_else(|| panic!("Cannot find function with ID {function}")); let candy_type_ptr = self .module @@ -682,7 +776,6 @@ impl<'ctx> CodeGen<'ctx> { let env_ptr = function_ctx.function_value.get_last_param().unwrap(); - dbg!(arg, i, env_ptr); let env_value = self.builder.build_struct_gep( function_ctx.env_type.unwrap(), env_ptr.into_pointer_value(), From 0f82e2f579b3108068e9012c759e237f6adaa006 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Fri, 21 Jul 2023 23:18:11 +0200 Subject: [PATCH 08/27] reorganize candy_rt, add structs --- compiler/backend_inkwell/candy_rt.c | 259 --------------- compiler/backend_inkwell/candy_rt/Makefile | 10 + .../backend_inkwell/candy_rt/candy_builtin.c | 119 +++++++ .../backend_inkwell/candy_rt/candy_builtin.h | 12 + compiler/backend_inkwell/candy_rt/candy_rt.c | 143 +++++++++ compiler/backend_inkwell/candy_rt/candy_rt.h | 57 ++++ compiler/backend_inkwell/src/lib.rs | 294 +++++++++++++----- compiler/cli/src/inkwell.rs | 20 +- 8 files changed, 569 insertions(+), 345 deletions(-) delete mode 100644 compiler/backend_inkwell/candy_rt.c create mode 100644 compiler/backend_inkwell/candy_rt/Makefile create mode 100644 compiler/backend_inkwell/candy_rt/candy_builtin.c create mode 100644 compiler/backend_inkwell/candy_rt/candy_builtin.h create mode 100644 compiler/backend_inkwell/candy_rt/candy_rt.c create mode 100644 compiler/backend_inkwell/candy_rt/candy_rt.h diff --git a/compiler/backend_inkwell/candy_rt.c b/compiler/backend_inkwell/candy_rt.c deleted file mode 100644 index 10342486e..000000000 --- a/compiler/backend_inkwell/candy_rt.c +++ /dev/null @@ -1,259 +0,0 @@ -#include -#include -#include -#include - -#define int128_t long long int - -typedef enum -{ - CANDY_TYPE_INT = 42, - CANDY_TYPE_TEXT, - CANDY_TYPE_TAG, - CANDY_TYPE_LIST, - CANDY_TYPE_STRUCT, - CANDY_TYPE_FUNCTION, -} candy_type_t; - -typedef struct -{ - struct candy_value *environment; - struct candy_value *(*function)(struct candy_value *); -} candy_function_t; - -typedef struct candy_value -{ - union - { - int128_t integer; - char *text; - struct candy_value *list; - candy_function_t function; - } value; - candy_type_t type; -} candy_value_t; - -typedef candy_value_t *(*candy_function)(candy_value_t *); - -candy_value_t __internal_true = { - .value = {.text = "True"}, - .type = CANDY_TYPE_TAG}; - -candy_value_t __internal_false = { - .value = {.text = "False"}, - .type = CANDY_TYPE_TAG}; - -candy_value_t _candy_environment = { - .value = {.text = "Environment"}, - .type = CANDY_TYPE_TAG}; - -// Not particularly elegant, but this is a temporary solution anyway... -candy_value_t *candy_environment = &_candy_environment; - -void print_candy_value(candy_value_t *value) -{ - switch (value->type) - { - case CANDY_TYPE_INT: - printf("%lld", value->value.integer); - break; - case CANDY_TYPE_TEXT: - printf("%s", value->value.text); - break; - case CANDY_TYPE_TAG: - printf("%s", value->value.text); - break; - default: - printf("", value->type); - break; - } -} - -candy_value_t *to_candy_bool(int value) -{ - if (value) - { - return &__internal_true; - } - else - { - return &__internal_false; - } -} - -int candy_tag_to_bool(candy_value_t *value) -{ - if (strcmp(value->value.text, "True") == 0) - { - return 1; - } - else if (strcmp(value->value.text, "False") == 0) - { - return 0; - } - else - { - printf("Got invalid value "); - print_candy_value(value); - printf("\n"); - exit(-1); - } -} - -candy_value_t *make_candy_int(int128_t value) -{ - candy_value_t *candy_value = malloc(sizeof(candy_value_t)); - candy_value->value.integer = value; - candy_value->type = CANDY_TYPE_INT; - return candy_value; -} - -candy_value_t *make_candy_text(char *text) -{ - candy_value_t *candy_value = malloc(sizeof(candy_value_t)); - candy_value->value.text = malloc(sizeof(char) * strlen(text)); - strcpy(candy_value->value.text, text); - candy_value->type = CANDY_TYPE_TEXT; - return candy_value; -} - -candy_value_t *make_candy_tag(char *tag) -{ - candy_value_t *candy_value = malloc(sizeof(candy_value_t)); - candy_value->value.text = malloc(sizeof(char) * strlen(tag)); - strcpy(candy_value->value.text, tag); - candy_value->type = CANDY_TYPE_TAG; - return candy_value; -} - -candy_value_t *make_candy_function(candy_function function, candy_value_t *environment) -{ - candy_value_t *candy_value = malloc(sizeof(candy_value_t)); - candy_value->type = CANDY_TYPE_FUNCTION; - candy_value->value.function.function = function; - candy_value->value.function.environment = environment; - return candy_value; -} - -candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right) -{ - if (left - ->type != right->type) - { - return &__internal_false; - } - switch (left->type) - { - case CANDY_TYPE_INT: - return to_candy_bool(left->value.integer == right->value.integer); - break; - case CANDY_TYPE_TAG: - return to_candy_bool(strcmp(left->value.text, right->value.text) == 0); - default: - return &__internal_false; - } -} - -candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise) -{ - if (candy_tag_to_bool(condition)) - { - candy_function then_function = (then->value).function.function; - candy_value_t *environment = (then->value).function.environment; - return then_function(environment); - } - else - { - candy_function otherwise_function = (otherwise->value).function.function; - candy_value_t *environment = (otherwise->value).function.environment; - return otherwise_function(environment); - } -} - -candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right) -{ - return make_candy_int(left->value.integer + right->value.integer); -} - -candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *right) -{ - return make_candy_int(left->value.integer - right->value.integer); -} - -candy_value_t *candy_builtin_int_bit_length(candy_value_t *value) -{ - // This is the max size in the VM. Unsure if it applies here. - return make_candy_int(62); -} - -candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right) -{ - return make_candy_int(left->value.integer & right->value.integer); -} - -candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t *right) -{ - return make_candy_int(left->value.integer | right->value.integer); -} - -candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right) -{ - return make_candy_int(left->value.integer ^ right->value.integer); -} - -candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right) -{ - int128_t left_value = left->value.integer; - int128_t right_value = right->value.integer; - if (left_value < right_value) - { - return make_candy_tag("Less"); - } - else if (left_value == right_value) - { - return make_candy_tag("Equal"); - } - else - { - return make_candy_tag("Greater"); - } -} - -candy_value_t *candy_builtin_typeof(candy_value_t *value) -{ - switch (value->type) - { - case CANDY_TYPE_INT: - return make_candy_tag("Int"); - case CANDY_TYPE_TEXT: - return make_candy_tag("Text"); - case CANDY_TYPE_TAG: - return make_candy_tag("Tag"); - case CANDY_TYPE_LIST: - return make_candy_tag("List"); - case CANDY_TYPE_STRUCT: - return make_candy_tag("Struct"); - case CANDY_TYPE_FUNCTION: - return make_candy_tag("Function"); - } -} - -void candy_panic(candy_value_t *reason) -{ - printf("The program panicked for the following reason: \n"); - print_candy_value(reason); - printf("\n"); - exit(-1); -} - -void free_candy_value(candy_value_t *value) -{ - if (value != candy_environment) - { - if (value->type == CANDY_TYPE_TAG || value->type == CANDY_TYPE_TEXT) - { - free(value->value.text); - } - free(value); - } -} \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/Makefile b/compiler/backend_inkwell/candy_rt/Makefile new file mode 100644 index 000000000..ce7332219 --- /dev/null +++ b/compiler/backend_inkwell/candy_rt/Makefile @@ -0,0 +1,10 @@ +CC=clang + +candy_rt.a: candy_rt.o candy_builtin.o + ar rcs $@ $^ + +%.o: %.c %.h + +.PHONY: clean +clean: + rm -f *.o *.a \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.c b/compiler/backend_inkwell/candy_rt/candy_builtin.c new file mode 100644 index 000000000..009f23303 --- /dev/null +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.c @@ -0,0 +1,119 @@ +#include +#include "candy_rt.h" + +candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right) +{ + if (left + ->type != right->type) + { + return &__internal_false; + } + switch (left->type) + { + case CANDY_TYPE_INT: + return to_candy_bool(left->value.integer == right->value.integer); + break; + case CANDY_TYPE_TAG: + return to_candy_bool(strcmp(left->value.text, right->value.text) == 0); + default: + return &__internal_false; + } +} + +candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise) +{ + if (candy_tag_to_bool(condition)) + { + candy_function then_function = (then->value).function.function; + candy_value_t *environment = (then->value).function.environment; + return then_function(environment); + } + else + { + candy_function otherwise_function = (otherwise->value).function.function; + candy_value_t *environment = (otherwise->value).function.environment; + return otherwise_function(environment); + } +} + +candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right) +{ + return make_candy_int(left->value.integer + right->value.integer); +} + +candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *right) +{ + return make_candy_int(left->value.integer - right->value.integer); +} + +candy_value_t *candy_builtin_int_bit_length(candy_value_t *value) +{ + // This is the max size in the VM. Unsure if it applies here. + return make_candy_int(62); +} + +candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right) +{ + return make_candy_int(left->value.integer & right->value.integer); +} + +candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t *right) +{ + return make_candy_int(left->value.integer | right->value.integer); +} + +candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right) +{ + return make_candy_int(left->value.integer ^ right->value.integer); +} + +candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right) +{ + int128_t left_value = left->value.integer; + int128_t right_value = right->value.integer; + if (left_value < right_value) + { + return make_candy_tag("Less"); + } + else if (left_value == right_value) + { + return make_candy_tag("Equal"); + } + else + { + return make_candy_tag("Greater"); + } +} + +candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key) +{ + size_t index = 0; + while (!candy_tag_to_bool(candy_builtin_equals(structure->value.structure.keys[index], key))) + { + if (structure->value.structure.keys[index] == NULL) + { + candy_panic(make_candy_text("Attempted to access non-existent struct member")); + } + index++; + } + return structure->value.structure.values[index]; +} + +candy_value_t *candy_builtin_typeof(candy_value_t *value) +{ + switch (value->type) + { + case CANDY_TYPE_INT: + return make_candy_tag("Int"); + case CANDY_TYPE_TEXT: + return make_candy_tag("Text"); + case CANDY_TYPE_TAG: + return make_candy_tag("Tag"); + case CANDY_TYPE_LIST: + return make_candy_tag("List"); + case CANDY_TYPE_STRUCT: + return make_candy_tag("Struct"); + case CANDY_TYPE_FUNCTION: + return make_candy_tag("Function"); + } +} \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.h b/compiler/backend_inkwell/candy_rt/candy_builtin.h new file mode 100644 index 000000000..ddbc092b4 --- /dev/null +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.h @@ -0,0 +1,12 @@ +#include "candy_rt.h" + +candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right); +candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise); +candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right); +candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *right); +candy_value_t *candy_builtin_int_bit_length(candy_value_t *value); +candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right); +candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t *right); +candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right); +candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right); +candy_value_t *candy_builtin_typeof(candy_value_t *value); \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.c b/compiler/backend_inkwell/candy_rt/candy_rt.c new file mode 100644 index 000000000..9769e8a31 --- /dev/null +++ b/compiler/backend_inkwell/candy_rt/candy_rt.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include "candy_rt.h" +#include "candy_builtin.h" + +candy_value_t __internal_true = { + .value = {.text = "True"}, + .type = CANDY_TYPE_TAG}; + +candy_value_t __internal_false = { + .value = {.text = "False"}, + .type = CANDY_TYPE_TAG}; + +candy_value_t _candy_environment = { + .value = {.text = "Environment"}, + .type = CANDY_TYPE_TAG}; + +// Not particularly elegant, but this is a temporary solution anyway... +candy_value_t *candy_environment = &_candy_environment; + +void print_candy_value(candy_value_t *value) +{ + switch (value->type) + { + case CANDY_TYPE_INT: + printf("%lld", value->value.integer); + break; + case CANDY_TYPE_TEXT: + printf("%s", value->value.text); + break; + case CANDY_TYPE_TAG: + printf("%s", value->value.text); + break; + case CANDY_TYPE_FUNCTION: + printf("Function %p", value->value.function.function); + break; + default: + printf("", value->type); + break; + } +} + +candy_value_t *to_candy_bool(int value) +{ + if (value) + { + return &__internal_true; + } + else + { + return &__internal_false; + } +} + +int candy_tag_to_bool(candy_value_t *value) +{ + if (strcmp(value->value.text, "True") == 0) + { + return 1; + } + else if (strcmp(value->value.text, "False") == 0) + { + return 0; + } + else + { + printf("Got invalid value "); + print_candy_value(value); + printf("\n"); + exit(-1); + } +} + +candy_value_t *make_candy_int(int128_t value) +{ + candy_value_t *candy_value = malloc(sizeof(candy_value_t)); + candy_value->value.integer = value; + candy_value->type = CANDY_TYPE_INT; + return candy_value; +} + +candy_value_t *make_candy_text(char *text) +{ + candy_value_t *candy_value = malloc(sizeof(candy_value_t)); + candy_value->value.text = malloc(sizeof(char) * strlen(text)); + strcpy(candy_value->value.text, text); + candy_value->type = CANDY_TYPE_TEXT; + return candy_value; +} + +candy_value_t *make_candy_tag(char *tag) +{ + candy_value_t *candy_value = malloc(sizeof(candy_value_t)); + candy_value->value.text = malloc(sizeof(char) * strlen(tag)); + strcpy(candy_value->value.text, tag); + candy_value->type = CANDY_TYPE_TAG; + return candy_value; +} + +candy_value_t *make_candy_function(candy_function function, candy_value_t *environment) +{ + candy_value_t *candy_value = malloc(sizeof(candy_value_t)); + candy_value->type = CANDY_TYPE_FUNCTION; + candy_value->value.function.function = function; + candy_value->value.function.environment = environment; + return candy_value; +} + +candy_value_t *make_candy_struct(candy_value_t **keys, candy_value_t **values) +{ + candy_value_t *candy_value = malloc(sizeof(candy_value_t)); + candy_value->type = CANDY_TYPE_STRUCT; + candy_value->value.structure.keys = keys; + candy_value->value.structure.values = values; + return candy_value; +} + +candy_value_t *call_candy_function_with(candy_value_t *function, candy_value_t *arg) +{ + return function->value.function.function(arg); +} + +void candy_panic(candy_value_t *reason) +{ + printf("The program panicked for the following reason: \n"); + print_candy_value(reason); + printf("\n"); + exit(-1); +} + +void free_candy_value(candy_value_t *value) +{ + if (value != candy_environment) + { + if (value->type == CANDY_TYPE_TAG || value->type == CANDY_TYPE_TEXT) + { + free(value->value.text); + } + free(value); + } +} \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.h b/compiler/backend_inkwell/candy_rt/candy_rt.h new file mode 100644 index 000000000..fc9a396a1 --- /dev/null +++ b/compiler/backend_inkwell/candy_rt/candy_rt.h @@ -0,0 +1,57 @@ +#ifndef __CANDY_RT_H +#define __CANDY_RT_H + +#define int128_t long long int + +typedef enum +{ + CANDY_TYPE_INT = 42, + CANDY_TYPE_TEXT, + CANDY_TYPE_TAG, + CANDY_TYPE_LIST, + CANDY_TYPE_STRUCT, + CANDY_TYPE_FUNCTION, +} candy_type_t; + +typedef struct +{ + struct candy_value *environment; + struct candy_value *(*function)(struct candy_value *); +} candy_function_t; + +typedef struct +{ + struct candy_value **keys; + struct candy_value **values; +} candy_struct_t; + +typedef struct candy_value +{ + union + { + int128_t integer; + char *text; + struct candy_value *list; + candy_function_t function; + candy_struct_t structure; + } value; + candy_type_t type; +} candy_value_t; + +typedef candy_value_t *(*candy_function)(candy_value_t *); + +extern candy_value_t __internal_true; +extern candy_value_t __internal_false; +extern candy_value_t _candy_environment; +extern candy_value_t *candy_environment; + +void print_candy_value(candy_value_t *value); +candy_value_t *to_candy_bool(int value); +int candy_tag_to_bool(candy_value_t *value); +candy_value_t *make_candy_int(int128_t value); +candy_value_t *make_candy_text(char *text); +candy_value_t *make_candy_tag(char *tag); +candy_value_t *make_candy_function(candy_function function, candy_value_t *environment); +void candy_panic(candy_value_t *reason); +void free_candy_value(candy_value_t *value); +#endif \ No newline at end of file diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 48101fe68..022276133 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -5,7 +5,7 @@ use inkwell::{ module::{Linkage, Module}, support::LLVMString, types::{BasicType, StructType}, - values::{ArrayValue, BasicValue, BasicValueEnum, FunctionValue, GlobalValue}, + values::{ArrayValue, BasicValue, BasicValueEnum, FunctionValue, GlobalValue, PointerValue}, AddressSpace, }; @@ -20,7 +20,6 @@ use std::{ #[derive(Clone)] struct FunctionInfo<'ctx> { function_value: Rc>, - parameters: Vec, captured_ids: Vec, env_type: Option>, } @@ -35,6 +34,7 @@ pub struct CodeGen<'ctx> { locals: HashMap>>, functions: HashMap>, unrepresented_ids: HashSet, + main_return: Option>, } impl<'ctx> CodeGen<'ctx> { @@ -54,6 +54,7 @@ impl<'ctx> CodeGen<'ctx> { locals: HashMap::new(), functions: HashMap::new(), unrepresented_ids: HashSet::new(), + main_return: None, } } @@ -76,8 +77,9 @@ impl<'ctx> CodeGen<'ctx> { .add_function("make_candy_int", make_int_fn_type, Some(Linkage::External)); let make_tag_fn_type = candy_type_ptr.fn_type(&[i8_type.ptr_type(AddressSpace::default()).into()], false); - self.module - .add_function("make_candy_tag", make_tag_fn_type, Some(Linkage::External)); + let make_candy_tag = + self.module + .add_function("make_candy_tag", make_tag_fn_type, Some(Linkage::External)); self.module .add_function("make_candy_text", make_tag_fn_type, Some(Linkage::External)); let make_function_fn_type = @@ -87,6 +89,21 @@ impl<'ctx> CodeGen<'ctx> { make_function_fn_type, Some(Linkage::External), ); + + let make_struct_fn_type = + candy_type_ptr.fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + self.module.add_function( + "make_candy_struct", + make_struct_fn_type, + Some(Linkage::External), + ); + + let struct_get_fn_type = + candy_type_ptr.fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let candy_builtin_struct_get = + self.module + .add_function("candy_builtin_struct_get", struct_get_fn_type, None); + let panic_fn_type = void_type.fn_type( &[candy_type.ptr_type(AddressSpace::default()).into()], false, @@ -103,9 +120,14 @@ impl<'ctx> CodeGen<'ctx> { let main_fn = self.module.add_function("main", main_type, None); let block = self.context.append_basic_block(main_fn, "entry"); + let call_candy_function_type = + candy_type_ptr.fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let call_candy_function_with = + self.module + .add_function("call_candy_function_with", call_candy_function_type, None); + let main_info = FunctionInfo { function_value: Rc::new(main_fn), - parameters: vec![], captured_ids: vec![], env_type: None, }; @@ -113,27 +135,56 @@ impl<'ctx> CodeGen<'ctx> { self.builder.position_at_end(block); self.compile_mir(&self.mir.body.clone(), &main_info); self.builder.position_at_end(block); + let environment = self .module .add_global(candy_type_ptr, None, "candy_environment"); - let main_res_ptr = self.builder.build_call( - self.module.get_function("candy_main").unwrap(), - &[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()], + + if let Some(main_return) = self.main_return { + const MAIN_FN_NAME: &str = "Main"; + let len = i32_type.const_int(MAIN_FN_NAME.len() as u64, false); + let main_text = self.builder.build_array_alloca(i8_type, len, ""); + let main_text_array = self.make_str_literal(MAIN_FN_NAME); + self.builder.build_store(main_text, main_text_array); + + let main_tag = self + .builder + .build_call(make_candy_tag, &[main_text.into()], ""); + + let main_fn = self + .builder + .build_call( + candy_builtin_struct_get, + &[ + main_return.into(), + main_tag.try_as_basic_value().unwrap_left().into(), + ], + "", + ) + .try_as_basic_value() + .unwrap_left(); + + let main_res_ptr = self.builder.build_call( + call_candy_function_with, + &[main_fn.into(), environment.as_basic_value_enum().into()], "", ); - for value in self.module.get_globals() { - let val = self - .builder - .build_load(candy_type_ptr, value.as_pointer_value(), ""); - self.builder.build_call(free_fn, &[val.into()], ""); + + if print_main_output { + self.builder.build_call( + print_fn, + &[main_res_ptr.try_as_basic_value().unwrap_left().into()], + "", + ); + for value in self.module.get_globals() { + let val = self + .builder + .build_load(candy_type_ptr, 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)); if print_llvm_ir { @@ -267,7 +318,6 @@ impl<'ctx> CodeGen<'ctx> { *id, FunctionInfo { function_value: Rc::new(function), - parameters: vec![], captured_ids: vec![], env_type: None, }, @@ -299,7 +349,6 @@ impl<'ctx> CodeGen<'ctx> { *id, FunctionInfo { function_value: Rc::new(function), - parameters: vec![], captured_ids: vec![], env_type: None, }, @@ -321,7 +370,6 @@ impl<'ctx> CodeGen<'ctx> { *id, FunctionInfo { function_value: Rc::new(function), - parameters: vec![], captured_ids: vec![], env_type: None, }, @@ -344,7 +392,6 @@ impl<'ctx> CodeGen<'ctx> { *id, FunctionInfo { function_value: Rc::new(function), - parameters: vec![], captured_ids: vec![], env_type: None, }, @@ -368,7 +415,6 @@ impl<'ctx> CodeGen<'ctx> { *id, FunctionInfo { function_value: Rc::new(function), - parameters: vec![], captured_ids: vec![], env_type: None, }, @@ -392,7 +438,6 @@ impl<'ctx> CodeGen<'ctx> { *id, FunctionInfo { function_value: Rc::new(function), - parameters: vec![], captured_ids: vec![], env_type: None, }, @@ -416,7 +461,6 @@ impl<'ctx> CodeGen<'ctx> { *id, FunctionInfo { function_value: Rc::new(function), - parameters: vec![], captured_ids: vec![], env_type: None, }, @@ -440,7 +484,6 @@ impl<'ctx> CodeGen<'ctx> { *id, FunctionInfo { function_value: Rc::new(function), - parameters: vec![], captured_ids: vec![], env_type: None, }, @@ -475,7 +518,6 @@ impl<'ctx> CodeGen<'ctx> { *id, FunctionInfo { function_value: Rc::new(function), - parameters: vec![], captured_ids: vec![], env_type: None, }, @@ -535,7 +577,6 @@ impl<'ctx> CodeGen<'ctx> { *id, FunctionInfo { function_value: Rc::new(function), - parameters: vec![], captured_ids: vec![], env_type: None, }, @@ -545,8 +586,99 @@ impl<'ctx> CodeGen<'ctx> { self.unrepresented_ids.insert(*id); } candy_frontend::mir::Expression::List(_) => todo!(), - candy_frontend::mir::Expression::Struct(_s) => { - // Not yet implemented, but not allowed to panic + candy_frontend::mir::Expression::Struct(s) => { + let i32_type = self.context.i32_type(); + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + let make_candy_struct = self.module.get_function("make_candy_struct").unwrap(); + + let keys_array = self.builder.build_array_alloca( + candy_type_ptr, + i32_type.const_int(s.len() as u64 + 1, false), + "", + ); + let values_array = self.builder.build_array_alloca( + candy_type_ptr, + i32_type.const_int(s.len() as u64 + 1, false), + "", + ); + for (idx, (key, value)) in s.iter().enumerate() { + let key = self + .get_value_with_id(function_ctx, key) + .unwrap() + .into_pointer_value(); + let value = self + .get_value_with_id(function_ctx, value) + .unwrap() + .into_pointer_value(); + + let key_ptr = unsafe { + self.builder.build_gep( + candy_type_ptr, + keys_array, + &[i32_type.const_int(idx as u64, false)], + "", + ) + }; + self.builder.build_store(key_ptr, key); + let value_ptr = unsafe { + self.builder.build_gep( + candy_type_ptr, + values_array, + &[i32_type.const_int(idx as u64, false)], + "", + ) + }; + self.builder.build_store(value_ptr, value); + } + + // Null-terminate key/value arrays + let key_ptr = unsafe { + self.builder.build_gep( + candy_type_ptr, + keys_array, + &[i32_type.const_int(s.len() as u64, false)], + "", + ) + }; + self.builder + .build_store(key_ptr, candy_type_ptr.const_null()); + let value_ptr = unsafe { + self.builder.build_gep( + candy_type_ptr, + values_array, + &[i32_type.const_int(s.len() as u64, false)], + "", + ) + }; + self.builder + .build_store(value_ptr, candy_type_ptr.const_null()); + + let struct_value = self + .builder + .build_call( + make_candy_struct, + &[keys_array.into(), values_array.into()], + "", + ) + .try_as_basic_value() + .unwrap_left(); + + self.locals.insert(*id, Rc::new(struct_value)); + + let function_ctx_name = + function_ctx.function_value.get_name().to_str().unwrap(); + if idx == mir.expressions.len() - 1 { + if function_ctx_name != "main" { + self.builder + .build_return(Some(&struct_value.into_pointer_value())); + } else { + self.main_return.replace(struct_value.into_pointer_value()); + } + } } candy_frontend::mir::Expression::Reference(id) => { if let Some(v) = self.values.get(id) { @@ -599,14 +731,9 @@ impl<'ctx> CodeGen<'ctx> { body, responsible_parameter, } => { - let original_name = format!("{original_hirs:?}") + let name = format!("{original_hirs:?}") .replace('{', "") .replace('}', ""); - let name = if original_name.ends_with("main") { - "candy_main" - } else { - &original_name - }; let i32_type = self.context.i32_type(); let i8_type = self.context.i8_type(); @@ -693,11 +820,10 @@ impl<'ctx> CodeGen<'ctx> { let fn_type = candy_type_ptr.fn_type(¶ms, false); - let function = self.module.add_function(name, fn_type, None); + let function = self.module.add_function(&name, fn_type, None); let fun_info = FunctionInfo { function_value: Rc::new(function), - parameters: parameters.clone(), captured_ids: captured_ids.clone(), env_type: Some(env_struct_type), @@ -719,7 +845,9 @@ impl<'ctx> CodeGen<'ctx> { "", ); - let global = self.module.add_global(candy_type_ptr, None, ""); + let global = + self.module + .add_global(candy_type_ptr, None, &format!("fun_{name}")); global.set_initializer(&function_ptr.get_type().const_null()); self.builder.build_store( @@ -729,7 +857,7 @@ impl<'ctx> CodeGen<'ctx> { self.values.insert(*id, Rc::new(global)); - let inner_block = self.context.append_basic_block(function, name); + let inner_block = self.context.append_basic_block(function, &name); self.builder.position_at_end(inner_block); self.compile_mir(body, &fun_info); @@ -743,7 +871,6 @@ impl<'ctx> CodeGen<'ctx> { } => { let FunctionInfo { function_value, - parameters, captured_ids: _, env_type: _, } = self @@ -751,51 +878,9 @@ impl<'ctx> CodeGen<'ctx> { .get(function) .unwrap_or_else(|| panic!("Cannot find function with ID {function}")); - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let args: Vec<_> = arguments .iter() - .map(|arg| { - let mut v = self.values.get(arg).map(|a| { - self.builder - .build_load(candy_type_ptr, a.as_pointer_value(), "") - }); - if v.is_none() { - if let Some(i) = parameters.iter().position(|i| i == arg) { - v.replace(function_value.get_nth_param(i as u32).unwrap()); - } - } - if v.is_none() { - if let Some(i) = - function_ctx.captured_ids.iter().position(|i| i == arg) - { - let env_ptr = - function_ctx.function_value.get_last_param().unwrap(); - - let env_value = self.builder.build_struct_gep( - function_ctx.env_type.unwrap(), - env_ptr.into_pointer_value(), - i as u32, - "", - ); - - if let Ok(env_value) = env_value { - v.replace(env_value.as_basic_value_enum()); - } - } - } - if v.is_none() { - if let Some(value) = self.locals.get(arg) { - v.replace(*value.clone()); - } - } - v.unwrap_or_else(|| panic!("{arg} should be a real ID")) - .into() - }) + .map(|arg| self.get_value_with_id(function_ctx, arg).unwrap().into()) .collect(); let call = self.builder.build_call(**function_value, &args, ""); let call_value = Rc::new(call.try_as_basic_value().unwrap_left()); @@ -852,4 +937,43 @@ impl<'ctx> CodeGen<'ctx> { .collect(); i8_type.const_array(&content) } + + fn get_value_with_id( + &self, + function_ctx: &FunctionInfo<'ctx>, + id: &Id, + ) -> Option> { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + let mut v = self.values.get(id).map(|a| { + self.builder + .build_load(candy_type_ptr, a.as_pointer_value(), "") + }); + if v.is_none() { + if let Some(i) = function_ctx.captured_ids.iter().position(|i| i == id) { + let env_ptr = function_ctx.function_value.get_last_param().unwrap(); + + let env_value = self.builder.build_struct_gep( + function_ctx.env_type.unwrap(), + env_ptr.into_pointer_value(), + i as u32, + "", + ); + + if let Ok(env_value) = env_value { + v.replace(env_value.as_basic_value_enum()); + } + } + } + if v.is_none() { + if let Some(value) = self.locals.get(id) { + v.replace(*value.clone()); + } + } + v.unwrap_or_else(|| panic!("{id} should be a real ID")) + .into() + } } diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index e0119e14a..1a5796c6e 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -20,6 +20,9 @@ pub(crate) struct Options { /// If enabled, the program will print the output of the candy main function. #[arg(long = "print-main-output", default_value_t = false)] print_main_output: bool, + /// If enabled, the compiler will build the Candy runtime from scratch. + #[arg(long = "build-rt", default_value_t = false)] + build_rt: bool, /// If enabled, the LLVM bitcode will be compiled with debug information. #[arg(short = 'g', default_value_t = false)] debug: bool, @@ -76,12 +79,27 @@ pub(crate) fn compile(options: Options) -> ProgramResult { .unwrap() .wait() .unwrap(); + if options.build_rt { + std::process::Command::new("make") + .args(&["-C", "compiler/backend_inkwell/candy_rt/", "clean"]) + .spawn() + .unwrap() + .wait() + .unwrap(); + + std::process::Command::new("make") + .args(&["-C", "compiler/backend_inkwell/candy_rt/", "candy_rt.a"]) + .spawn() + .unwrap() + .wait() + .unwrap(); + } let mut s_path = PathBuf::new(); s_path.push(&format!("{path}.s")); std::process::Command::new("clang") .args([ - "compiler/backend_inkwell/candy_rt.c", s_path.to_str().unwrap(), + "compiler/backend_inkwell/candy_rt/candy_rt.a", if options.debug { "-g" } else { "" }, "-o", &s_path.to_str().unwrap().replace(".candy.s", ""), From cedd1f5c437337b366fda78bb61d155967827229 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Sat, 22 Jul 2023 22:48:03 +0200 Subject: [PATCH 09/27] implement list --- .../backend_inkwell/candy_rt/candy_builtin.c | 32 ++++++++++- .../backend_inkwell/candy_rt/candy_builtin.h | 4 ++ compiler/backend_inkwell/candy_rt/candy_rt.c | 27 +++++++++ compiler/backend_inkwell/candy_rt/candy_rt.h | 3 +- compiler/backend_inkwell/src/lib.rs | 55 ++++++++++++++++++- 5 files changed, 117 insertions(+), 4 deletions(-) diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.c b/compiler/backend_inkwell/candy_rt/candy_builtin.c index 009f23303..71e0b2180 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.c +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.c @@ -48,8 +48,7 @@ candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *ri candy_value_t *candy_builtin_int_bit_length(candy_value_t *value) { - // This is the max size in the VM. Unsure if it applies here. - return make_candy_int(62); + return make_candy_int(64); } candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right) @@ -85,6 +84,16 @@ candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *r } } +candy_value_t *candy_builtin_list_length(candy_value_t *list) +{ + size_t index = 0; + while (list->value.list[index] != NULL) + { + index++; + } + return make_candy_int(index); +} + candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key) { size_t index = 0; @@ -99,6 +108,25 @@ candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t return structure->value.structure.values[index]; } +candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure) +{ + return make_candy_list(structure->value.structure.keys); +} + +candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key) +{ + + size_t index = 0; + while (structure->value.structure.keys[index] != NULL) + { + if (candy_tag_to_bool(candy_builtin_equals(structure->value.structure.keys[index], key))) + { + return &__internal_true; + } + } + return &__internal_false; +} + candy_value_t *candy_builtin_typeof(candy_value_t *value) { switch (value->type) diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.h b/compiler/backend_inkwell/candy_rt/candy_builtin.h index ddbc092b4..0d721c801 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.h +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.h @@ -9,4 +9,8 @@ candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right); +candy_value_t *candy_builtin_list_length(candy_value_t *list); +candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key); +candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure); +candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key); candy_value_t *candy_builtin_typeof(candy_value_t *value); \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.c b/compiler/backend_inkwell/candy_rt/candy_rt.c index 9769e8a31..751b1c274 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt/candy_rt.c @@ -33,6 +33,22 @@ void print_candy_value(candy_value_t *value) case CANDY_TYPE_TAG: printf("%s", value->value.text); break; + case CANDY_TYPE_LIST: + printf("("); + candy_value_t *length = candy_builtin_list_length(value); + size_t list_length = length->value.integer; + free_candy_value(length); + size_t index = 0; + for (size_t index = 0; index < list_length; index++) + { + print_candy_value(value->value.list[index]); + if (index != list_length - 1) + { + printf(", "); + } + } + printf(")"); + break; case CANDY_TYPE_FUNCTION: printf("Function %p", value->value.function.function); break; @@ -99,6 +115,14 @@ candy_value_t *make_candy_tag(char *tag) return candy_value; } +candy_value_t *make_candy_list(candy_value_t **values) +{ + candy_value_t *candy_value = malloc(sizeof(candy_value_t)); + candy_value->value.list = values; + candy_value->type = CANDY_TYPE_LIST; + return candy_value; +} + candy_value_t *make_candy_function(candy_function function, candy_value_t *environment) { candy_value_t *candy_value = malloc(sizeof(candy_value_t)); @@ -138,6 +162,9 @@ void free_candy_value(candy_value_t *value) { free(value->value.text); } + // List and struct entries may not be freed as part of freeing + // the list/struct, because they will be freed on their own + // at the end of the main function. free(value); } } \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.h b/compiler/backend_inkwell/candy_rt/candy_rt.h index fc9a396a1..7eb0339a1 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.h +++ b/compiler/backend_inkwell/candy_rt/candy_rt.h @@ -31,7 +31,7 @@ typedef struct candy_value { int128_t integer; char *text; - struct candy_value *list; + struct candy_value **list; candy_function_t function; candy_struct_t structure; } value; @@ -51,6 +51,7 @@ int candy_tag_to_bool(candy_value_t *value); candy_value_t *make_candy_int(int128_t value); candy_value_t *make_candy_text(char *text); candy_value_t *make_candy_tag(char *tag); +candy_value_t *make_candy_list(candy_value_t **values); candy_value_t *make_candy_function(candy_function function, candy_value_t *environment); void candy_panic(candy_value_t *reason); void free_candy_value(candy_value_t *value); diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 022276133..01e516457 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -82,6 +82,8 @@ impl<'ctx> CodeGen<'ctx> { .add_function("make_candy_tag", make_tag_fn_type, Some(Linkage::External)); self.module .add_function("make_candy_text", make_tag_fn_type, Some(Linkage::External)); + self.module + .add_function("make_candy_list", make_tag_fn_type, Some(Linkage::External)); let make_function_fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); self.module.add_function( @@ -585,7 +587,58 @@ impl<'ctx> CodeGen<'ctx> { } self.unrepresented_ids.insert(*id); } - candy_frontend::mir::Expression::List(_) => todo!(), + candy_frontend::mir::Expression::List(list) => { + let i32_type = self.context.i32_type(); + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + let list_array = self.builder.build_array_alloca( + candy_type_ptr, + i32_type.const_int(list.len() as u64 + 1, false), + "", + ); + let values = list.iter().map(|v| { + self.get_value_with_id(function_ctx, v) + .unwrap() + .into_pointer_value() + }); + for (idx, value) in values.enumerate() { + let value_position = unsafe { + self.builder.build_gep( + candy_type_ptr, + list_array, + &[i32_type.const_int(idx as u64, false)], + "", + ) + }; + self.builder.build_store(value_position, value); + } + let end_position = unsafe { + self.builder.build_gep( + candy_type_ptr, + list_array, + &[i32_type.const_int(list.len() as u64, false)], + "", + ) + }; + self.builder + .build_store(end_position, candy_type_ptr.const_null()); + + let global = self.module.add_global(candy_type_ptr, None, ""); + global.set_initializer(&candy_type_ptr.const_null()); + + let make_candy_list = self.module.get_function("make_candy_list").unwrap(); + let candy_list = + self.builder + .build_call(make_candy_list, &[list_array.into()], ""); + self.builder.build_store( + global.as_pointer_value(), + candy_list.try_as_basic_value().unwrap_left(), + ); + self.values.insert(*id, Rc::new(global)); + } candy_frontend::mir::Expression::Struct(s) => { let i32_type = self.context.i32_type(); let candy_type_ptr = self From fc6dd1c379db4da64b3ac2a19d5f9d257f3e41d6 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 3 Aug 2023 19:26:41 +0200 Subject: [PATCH 10/27] add support for indirect calls; make fibonacci run --- .gitignore | 6 + .../backend_inkwell/candy_rt/candy_builtin.c | 10 + .../backend_inkwell/candy_rt/candy_builtin.h | 1 + compiler/backend_inkwell/candy_rt/candy_rt.c | 22 +- compiler/backend_inkwell/candy_rt/candy_rt.h | 8 +- compiler/backend_inkwell/src/lib.rs | 295 +++++++++--------- 6 files changed, 193 insertions(+), 149 deletions(-) diff --git a/.gitignore b/.gitignore index df2f010af..a4ac14780 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,9 @@ coverage/ .buildlog/ .history .svn/ + +# LLVM Artefacts +*.o +*.a +*.ll +*.s \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.c b/compiler/backend_inkwell/candy_rt/candy_builtin.c index 71e0b2180..538812121 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.c +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.c @@ -1,3 +1,4 @@ +#include #include #include "candy_rt.h" @@ -94,6 +95,13 @@ candy_value_t *candy_builtin_list_length(candy_value_t *list) return make_candy_int(index); } +candy_value_t *candy_builtin_print(candy_value_t *value) +{ + print_candy_value(value); + printf("\n"); + return &__internal_nothing; +} + candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key) { size_t index = 0; @@ -143,5 +151,7 @@ candy_value_t *candy_builtin_typeof(candy_value_t *value) return make_candy_tag("Struct"); case CANDY_TYPE_FUNCTION: return make_candy_tag("Function"); + default: + candy_panic(make_candy_text("Unknown type")); } } \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.h b/compiler/backend_inkwell/candy_rt/candy_builtin.h index 0d721c801..e26decd7c 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.h +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.h @@ -10,6 +10,7 @@ candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t * candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_list_length(candy_value_t *list); +candy_value_t *candy_builtin_print(candy_value_t *value); candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key); candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure); candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key); diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.c b/compiler/backend_inkwell/candy_rt/candy_rt.c index 751b1c274..813a5ffbd 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt/candy_rt.c @@ -13,6 +13,10 @@ candy_value_t __internal_false = { .value = {.text = "False"}, .type = CANDY_TYPE_TAG}; +candy_value_t __internal_nothing = { + .value = {.text = "Nothing"}, + .type = CANDY_TYPE_TAG}; + candy_value_t _candy_environment = { .value = {.text = "Environment"}, .type = CANDY_TYPE_TAG}; @@ -100,7 +104,7 @@ candy_value_t *make_candy_int(int128_t value) candy_value_t *make_candy_text(char *text) { candy_value_t *candy_value = malloc(sizeof(candy_value_t)); - candy_value->value.text = malloc(sizeof(char) * strlen(text)); + candy_value->value.text = malloc(sizeof(char) * (strlen(text) + 1)); strcpy(candy_value->value.text, text); candy_value->type = CANDY_TYPE_TEXT; return candy_value; @@ -109,7 +113,7 @@ candy_value_t *make_candy_text(char *text) candy_value_t *make_candy_tag(char *tag) { candy_value_t *candy_value = malloc(sizeof(candy_value_t)); - candy_value->value.text = malloc(sizeof(char) * strlen(tag)); + candy_value->value.text = malloc(sizeof(char) * (strlen(tag) + 1)); strcpy(candy_value->value.text, tag); candy_value->type = CANDY_TYPE_TAG; return candy_value; @@ -123,7 +127,7 @@ candy_value_t *make_candy_list(candy_value_t **values) return candy_value; } -candy_value_t *make_candy_function(candy_function function, candy_value_t *environment) +candy_value_t *make_candy_function(candy_function function, void *environment, int env_size) { candy_value_t *candy_value = malloc(sizeof(candy_value_t)); candy_value->type = CANDY_TYPE_FUNCTION; @@ -146,6 +150,16 @@ candy_value_t *call_candy_function_with(candy_value_t *function, candy_value_t * return function->value.function.function(arg); } +candy_function get_candy_function_pointer(candy_value_t *function) +{ + return function->value.function.function; +} + +void *get_candy_function_environment(candy_value_t *function) +{ + return function->value.function.environment; +} + void candy_panic(candy_value_t *reason) { printf("The program panicked for the following reason: \n"); @@ -156,7 +170,7 @@ void candy_panic(candy_value_t *reason) void free_candy_value(candy_value_t *value) { - if (value != candy_environment) + if (value != candy_environment && value != NULL) { if (value->type == CANDY_TYPE_TAG || value->type == CANDY_TYPE_TEXT) { diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.h b/compiler/backend_inkwell/candy_rt/candy_rt.h index 7eb0339a1..99da7713c 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.h +++ b/compiler/backend_inkwell/candy_rt/candy_rt.h @@ -15,7 +15,7 @@ typedef enum typedef struct { - struct candy_value *environment; + void *environment; struct candy_value *(*function)(struct candy_value *); } candy_function_t; @@ -42,6 +42,7 @@ typedef candy_value_t *(*candy_function)(candy_value_t *); extern candy_value_t __internal_true; extern candy_value_t __internal_false; +extern candy_value_t __internal_nothing; extern candy_value_t _candy_environment; extern candy_value_t *candy_environment; @@ -52,7 +53,10 @@ candy_value_t *make_candy_int(int128_t value); candy_value_t *make_candy_text(char *text); candy_value_t *make_candy_tag(char *tag); candy_value_t *make_candy_list(candy_value_t **values); -candy_value_t *make_candy_function(candy_function function, candy_value_t *environment); +candy_value_t *make_candy_function(candy_function function, void *environment, int env_size); +candy_value_t *call_candy_function_with(candy_value_t *function, candy_value_t *arg); +candy_function get_candy_function_pointer(candy_value_t *function); +void *get_candy_function_environment(candy_value_t *function); void candy_panic(candy_value_t *reason); void free_candy_value(candy_value_t *value); #endif \ No newline at end of file diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 01e516457..2713c938f 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -65,14 +65,14 @@ impl<'ctx> CodeGen<'ctx> { print_main_output: bool, ) -> Result<(), LLVMString> { let i32_type = self.context.i32_type(); - let i128_type = self.context.i64_type(); + let i64_type = self.context.i64_type(); let i8_type = self.context.i8_type(); let void_type = self.context.void_type(); let candy_type = self.context.opaque_struct_type("candy_type"); let candy_type_ptr = candy_type.ptr_type(AddressSpace::default()); - let make_int_fn_type = candy_type_ptr.fn_type(&[i128_type.into()], false); + let make_int_fn_type = candy_type_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 = @@ -84,8 +84,14 @@ impl<'ctx> CodeGen<'ctx> { .add_function("make_candy_text", make_tag_fn_type, Some(Linkage::External)); self.module .add_function("make_candy_list", make_tag_fn_type, Some(Linkage::External)); - let make_function_fn_type = - candy_type_ptr.fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let make_function_fn_type = candy_type_ptr.fn_type( + &[ + candy_type_ptr.into(), + candy_type_ptr.into(), + i64_type.into(), + ], + false, + ); self.module.add_function( "make_candy_function", make_function_fn_type, @@ -118,6 +124,19 @@ impl<'ctx> CodeGen<'ctx> { .module .add_function("print_candy_value", panic_fn_type, None); + let candy_fn_type = candy_type_ptr.fn_type(&[], true); + let get_candy_fn_ptr_type = candy_fn_type + .ptr_type(AddressSpace::default()) + .fn_type(&[candy_type_ptr.into()], false); + self.module + .add_function("get_candy_function_pointer", get_candy_fn_ptr_type, None); + let get_candy_fn_env_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); + self.module.add_function( + "get_candy_function_environment", + get_candy_fn_env_type, + None, + ); + let main_type = i32_type.fn_type(&[], false); let main_fn = self.module.add_function("main", main_type, None); let block = self.context.append_basic_block(main_fn, "entry"); @@ -198,11 +217,17 @@ impl<'ctx> CodeGen<'ctx> { } fn compile_mir(&mut self, mir: &Body, function_ctx: &FunctionInfo<'ctx>) { + let candy_type_ptr = self + .module + .get_struct_type("candy_type") + .unwrap() + .ptr_type(AddressSpace::default()); + for (idx, (id, expr)) in mir.expressions.iter().enumerate() { match expr { candy_frontend::mir::Expression::Int(value) => { - let i128_type = self.context.i64_type(); - let v = i128_type.const_int(value.try_into().unwrap(), false); + let i64_type = self.context.i64_type(); + let v = i64_type.const_int(value.try_into().unwrap(), false); let candy_type = self.module.get_struct_type("candy_type").unwrap(); let candy_type_ptr = candy_type.ptr_type(AddressSpace::default()); let global = @@ -227,15 +252,10 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::mir::Expression::Text(text) => { let i32_type = self.context.i32_type(); let i8_type = self.context.i8_type(); - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); let global = self.module.add_global(candy_type_ptr, None, text); let v = self.make_str_literal(text); - let len = i32_type.const_int(text.len() as u64, false); + let len = i32_type.const_int(text.len() as u64 + 1, false); let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); self.builder.build_store(arr_alloc, v); let cast = self.builder.build_bitcast( @@ -263,11 +283,6 @@ impl<'ctx> CodeGen<'ctx> { self.tags.insert(symbol.clone(), *value); let i32_type = self.context.i32_type(); let i8_type = self.context.i8_type(); - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); let global = self.module.add_global(candy_type_ptr, None, symbol); let v = self.make_str_literal(symbol); @@ -305,12 +320,6 @@ impl<'ctx> CodeGen<'ctx> { todo!() } candy_frontend::builtin_functions::BuiltinFunction::Equals => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let fn_type = candy_type_ptr .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); let function = @@ -330,12 +339,6 @@ impl<'ctx> CodeGen<'ctx> { todo!() } candy_frontend::builtin_functions::BuiltinFunction::IfElse => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let fn_type = candy_type_ptr.fn_type( &[ candy_type_ptr.into(), @@ -357,12 +360,6 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntAdd => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let fn_type = candy_type_ptr .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); let function = @@ -378,12 +375,6 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntBitLength => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); let function = self.module.add_function( "candy_builtin_int_bit_length", @@ -400,12 +391,6 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseAnd => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let fn_type = candy_type_ptr .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); let function = self.module.add_function( @@ -423,12 +408,6 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseOr => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let fn_type = candy_type_ptr .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); let function = self.module.add_function( @@ -446,12 +425,6 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseXor => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let fn_type = candy_type_ptr .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); let function = self.module.add_function( @@ -469,12 +442,6 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntCompareTo => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let fn_type = candy_type_ptr .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); let function = self.module.add_function( @@ -503,12 +470,6 @@ impl<'ctx> CodeGen<'ctx> { todo!() } candy_frontend::builtin_functions::BuiltinFunction::IntSubtract => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let fn_type = candy_type_ptr .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); let function = self.module.add_function( @@ -532,7 +493,20 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::builtin_functions::BuiltinFunction::ListRemoveAt => todo!(), candy_frontend::builtin_functions::BuiltinFunction::ListReplace => todo!(), candy_frontend::builtin_functions::BuiltinFunction::Parallel => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::Print => todo!(), + candy_frontend::builtin_functions::BuiltinFunction::Print => { + let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); + let function = + self.module + .add_function("candy_builtin_print", fn_type, None); + self.functions.insert( + *id, + FunctionInfo { + function_value: Rc::new(function), + captured_ids: vec![], + env_type: None, + }, + ); + } candy_frontend::builtin_functions::BuiltinFunction::StructGet => todo!(), candy_frontend::builtin_functions::BuiltinFunction::StructGetKeys => { todo!() @@ -565,12 +539,6 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::builtin_functions::BuiltinFunction::ToDebugText => todo!(), candy_frontend::builtin_functions::BuiltinFunction::Try => todo!(), candy_frontend::builtin_functions::BuiltinFunction::TypeOf => { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); let function = self.module @@ -733,16 +701,11 @@ impl<'ctx> CodeGen<'ctx> { } } } - candy_frontend::mir::Expression::Reference(id) => { - if let Some(v) = self.values.get(id) { - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); - let value = - self.builder - .build_load(candy_type_ptr, v.as_pointer_value(), ""); + candy_frontend::mir::Expression::Reference(ref_id) => { + let value = self.get_value_with_id(function_ctx, ref_id).unwrap(); + + self.locals.insert(*id, Rc::new(value)); + if idx == mir.expressions.len() - 1 { self.builder.build_return(Some(&value)); } } @@ -786,7 +749,8 @@ impl<'ctx> CodeGen<'ctx> { } => { let name = format!("{original_hirs:?}") .replace('{', "") - .replace('}', ""); + .replace('}', "") + .replace([':', '.'], "_"); let i32_type = self.context.i32_type(); let i8_type = self.context.i8_type(); @@ -800,7 +764,7 @@ impl<'ctx> CodeGen<'ctx> { let global = self.module.add_global(candy_type_ptr, None, &text); let v = self.make_str_literal(&text); - let len = i32_type.const_int(text.len() as u64, false); + let len = i32_type.const_int(text.len() as u64 + 1, false); let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); self.builder.build_store(arr_alloc, v); let cast = self.builder.build_bitcast( @@ -841,9 +805,8 @@ impl<'ctx> CodeGen<'ctx> { let env_struct_type = self.context.struct_type(&env_types, false); - let env_ptr = self.builder.build_alloca(env_struct_type, ""); + let env_ptr = self.builder.build_malloc(env_struct_type, "").unwrap(); - let env_struct = self.builder.build_load(env_struct_type, env_ptr, ""); for (idx, cap_id) in captured_ids.iter().enumerate() { let mut value = self.locals.get(cap_id).map(|v| **v); @@ -857,12 +820,11 @@ impl<'ctx> CodeGen<'ctx> { ); } - self.builder.build_insert_value( - env_struct.into_struct_value(), - value.unwrap(), - idx as u32, - "", - ); + let member = self + .builder + .build_struct_gep(env_struct_type, env_ptr, idx as u32, "") + .unwrap(); + self.builder.build_store(member, value.unwrap()); } let mut params: Vec<_> = @@ -878,8 +840,11 @@ impl<'ctx> CodeGen<'ctx> { let fun_info = FunctionInfo { function_value: Rc::new(function), captured_ids: captured_ids.clone(), - - env_type: Some(env_struct_type), + env_type: if !captured_ids.is_empty() { + Some(env_struct_type) + } else { + None + }, }; self.functions.insert(*id, fun_info.clone()); @@ -889,12 +854,13 @@ impl<'ctx> CodeGen<'ctx> { let current_block = self.builder.get_insert_block().unwrap(); + let env_size = env_struct_type.size_of().unwrap(); let function_ptr = function.as_global_value().as_pointer_value(); let make_candy_function = self.module.get_function("make_candy_function").unwrap(); let call = self.builder.build_call( make_candy_function, - &[function_ptr.into(), env_ptr.into()], + &[function_ptr.into(), env_ptr.into(), env_size.into()], "", ); @@ -922,50 +888,92 @@ impl<'ctx> CodeGen<'ctx> { arguments, .. } => { - let FunctionInfo { - function_value, - captured_ids: _, - env_type: _, - } = self - .functions - .get(function) - .unwrap_or_else(|| panic!("Cannot find function with ID {function}")); - - let args: Vec<_> = arguments + let mut args: Vec<_> = arguments .iter() .map(|arg| self.get_value_with_id(function_ctx, arg).unwrap().into()) .collect(); - let call = self.builder.build_call(**function_value, &args, ""); - let call_value = Rc::new(call.try_as_basic_value().unwrap_left()); - self.locals.insert(*id, call_value.clone()); + if let Some(FunctionInfo { + function_value, + captured_ids: _, + env_type, + }) = self.functions.get(function) + { + if env_type.is_some() { + let get_candy_fn_env = self + .module + .get_function("get_candy_function_environment") + .unwrap(); - if idx == mir.expressions.len() - 1 { - self.builder - .build_return(Some(&call_value.into_pointer_value())); + let fn_object = self.values.get(function).unwrap_or_else(|| { + panic!("Function {function} should have global visibility") + }); + + let fn_env_ptr = self.builder.build_call( + get_candy_fn_env, + &[fn_object.as_pointer_value().into()], + "", + ); + + args.push(fn_env_ptr.try_as_basic_value().unwrap_left().into()); + } + let call = self.builder.build_call(**function_value, &args, ""); + let call_value = Rc::new(call.try_as_basic_value().unwrap_left()); + self.locals.insert(*id, call_value.clone()); + + if idx == mir.expressions.len() - 1 { + self.builder + .build_return(Some(&call_value.into_pointer_value())); + } + } else { + let function_value = self + .get_value_with_id(function_ctx, function) + .unwrap_or_else(|| panic!("There is no function with ID {function}")); + + let get_candy_fn_ptr = self + .module + .get_function("get_candy_function_pointer") + .unwrap(); + let get_candy_fn_env = self + .module + .get_function("get_candy_function_environment") + .unwrap(); + + let fn_ptr = + self.builder + .build_call(get_candy_fn_ptr, &[function_value.into()], ""); + + let fn_env_ptr = + self.builder + .build_call(get_candy_fn_env, &[function_value.into()], ""); + + args.push(fn_env_ptr.try_as_basic_value().unwrap_left().into()); + + let candy_fn_type = candy_type_ptr.fn_type(&[], true); + let inner_fn = fn_ptr + .try_as_basic_value() + .unwrap_left() + .into_pointer_value(); + + let call = + self.builder + .build_indirect_call(candy_fn_type, inner_fn, &args, ""); + + let call_value = Rc::new(call.try_as_basic_value().unwrap_left()); + self.locals.insert(*id, call_value.clone()); + + if idx == mir.expressions.len() - 1 { + self.builder + .build_return(Some(&call_value.into_pointer_value())); + } } } candy_frontend::mir::Expression::UseModule { .. } => unreachable!(), candy_frontend::mir::Expression::Panic { reason, .. } => { let panic_fn = self.module.get_function("candy_panic").unwrap(); - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); + let reason = self.get_value_with_id(function_ctx, reason).unwrap(); - if let Some(reason) = self.values.get(reason) { - let reason = - self.builder - .build_load(candy_type_ptr, reason.as_pointer_value(), ""); - self.builder.build_call(panic_fn, &[reason.into()], ""); - } else { - self.builder.build_call( - panic_fn, - &[candy_type_ptr.const_null().into()], - "", - ); - } + self.builder.build_call(panic_fn, &[reason.into()], ""); self.builder.build_unreachable(); } @@ -1009,16 +1017,17 @@ impl<'ctx> CodeGen<'ctx> { if let Some(i) = function_ctx.captured_ids.iter().position(|i| i == id) { let env_ptr = function_ctx.function_value.get_last_param().unwrap(); - let env_value = self.builder.build_struct_gep( - function_ctx.env_type.unwrap(), - env_ptr.into_pointer_value(), - i as u32, - "", - ); + let env_value = self + .builder + .build_struct_gep( + function_ctx.env_type.unwrap(), + env_ptr.into_pointer_value(), + i as u32, + "", + ) + .unwrap(); - if let Ok(env_value) = env_value { - v.replace(env_value.as_basic_value_enum()); - } + v.replace(self.builder.build_load(candy_type_ptr, env_value, "")); } } if v.is_none() { From 3d9897e34608aba3a1e56ee1d7a6e3dff3b87cd2 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 3 Aug 2023 20:55:21 +0200 Subject: [PATCH 11/27] remove unnecessary allocations in runtime --- .../backend_inkwell/candy_rt/candy_builtin.c | 34 +++++++++---------- compiler/backend_inkwell/candy_rt/candy_rt.c | 34 +++++++++++++++---- compiler/backend_inkwell/candy_rt/candy_rt.h | 24 +++++++++---- compiler/cli/src/inkwell.rs | 7 ++-- 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.c b/compiler/backend_inkwell/candy_rt/candy_builtin.c index 538812121..a8cc8b57b 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.c +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.c @@ -2,7 +2,7 @@ #include #include "candy_rt.h" -candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right) +const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right) { if (left ->type != right->type) @@ -21,7 +21,7 @@ candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right) } } -candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise) +const candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise) { if (candy_tag_to_bool(condition)) { @@ -67,25 +67,25 @@ candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t return make_candy_int(left->value.integer ^ right->value.integer); } -candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right) +const candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right) { int128_t left_value = left->value.integer; int128_t right_value = right->value.integer; if (left_value < right_value) { - return make_candy_tag("Less"); + return &__internal_less; } else if (left_value == right_value) { - return make_candy_tag("Equal"); + return &__internal_equal; } else { - return make_candy_tag("Greater"); + return &__internal_greater; } } -candy_value_t *candy_builtin_list_length(candy_value_t *list) +candy_value_t *candy_builtin_list_length(const candy_value_t *list) { size_t index = 0; while (list->value.list[index] != NULL) @@ -95,7 +95,7 @@ candy_value_t *candy_builtin_list_length(candy_value_t *list) return make_candy_int(index); } -candy_value_t *candy_builtin_print(candy_value_t *value) +const candy_value_t *candy_builtin_print(candy_value_t *value) { print_candy_value(value); printf("\n"); @@ -121,7 +121,7 @@ candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure) return make_candy_list(structure->value.structure.keys); } -candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key) +const candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key) { size_t index = 0; @@ -135,23 +135,23 @@ candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_valu return &__internal_false; } -candy_value_t *candy_builtin_typeof(candy_value_t *value) +const candy_value_t *candy_builtin_typeof(candy_value_t *value) { switch (value->type) { case CANDY_TYPE_INT: - return make_candy_tag("Int"); + return &__internal_int; case CANDY_TYPE_TEXT: - return make_candy_tag("Text"); + return &__internal_text; case CANDY_TYPE_TAG: - return make_candy_tag("Tag"); + return &__internal_tag; case CANDY_TYPE_LIST: - return make_candy_tag("List"); + return &__internal_list; case CANDY_TYPE_STRUCT: - return make_candy_tag("Struct"); + return &__internal_struct; case CANDY_TYPE_FUNCTION: - return make_candy_tag("Function"); + return &__internal_function; default: - candy_panic(make_candy_text("Unknown type")); + candy_panic(&__internal_unknown); } } \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.c b/compiler/backend_inkwell/candy_rt/candy_rt.c index 813a5ffbd..301675816 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt/candy_rt.c @@ -5,18 +5,38 @@ #include "candy_rt.h" #include "candy_builtin.h" -candy_value_t __internal_true = { +const candy_value_t __internal_true = { .value = {.text = "True"}, .type = CANDY_TYPE_TAG}; -candy_value_t __internal_false = { +const candy_value_t __internal_false = { .value = {.text = "False"}, .type = CANDY_TYPE_TAG}; -candy_value_t __internal_nothing = { +const candy_value_t __internal_nothing = { .value = {.text = "Nothing"}, .type = CANDY_TYPE_TAG}; +const candy_value_t __internal_less = { + .value = {.text = "Less"}, + .type = CANDY_TYPE_TAG}; + +const candy_value_t __internal_greater = { + .value = {.text = "Greater"}, + .type = CANDY_TYPE_TAG}; + +const candy_value_t __internal_equal = { + .value = {.text = "Equal"}, + .type = CANDY_TYPE_TAG}; + +const candy_value_t __internal_int = {.value = {.text = "Int"}, .type = CANDY_TYPE_TAG}; +const candy_value_t __internal_text = {.value = {.text = "Text"}, .type = CANDY_TYPE_TAG}; +const candy_value_t __internal_tag = {.value = {.text = "Tag"}, .type = CANDY_TYPE_TAG}; +const candy_value_t __internal_list = {.value = {.text = "List"}, .type = CANDY_TYPE_TAG}; +const candy_value_t __internal_struct = {.value = {.text = "Struct"}, .type = CANDY_TYPE_TAG}; +const candy_value_t __internal_function = {.value = {.text = "Function"}, .type = CANDY_TYPE_TAG}; +const candy_value_t __internal_unknown = {.value = {.text = "Unknown type"}, .type = CANDY_TYPE_TAG}; + candy_value_t _candy_environment = { .value = {.text = "Environment"}, .type = CANDY_TYPE_TAG}; @@ -24,7 +44,7 @@ candy_value_t _candy_environment = { // Not particularly elegant, but this is a temporary solution anyway... candy_value_t *candy_environment = &_candy_environment; -void print_candy_value(candy_value_t *value) +void print_candy_value(const candy_value_t *value) { switch (value->type) { @@ -62,7 +82,7 @@ void print_candy_value(candy_value_t *value) } } -candy_value_t *to_candy_bool(int value) +const candy_value_t *to_candy_bool(int value) { if (value) { @@ -74,7 +94,7 @@ candy_value_t *to_candy_bool(int value) } } -int candy_tag_to_bool(candy_value_t *value) +int candy_tag_to_bool(const candy_value_t *value) { if (strcmp(value->value.text, "True") == 0) { @@ -160,7 +180,7 @@ void *get_candy_function_environment(candy_value_t *function) return function->value.function.environment; } -void candy_panic(candy_value_t *reason) +void candy_panic(const candy_value_t *reason) { printf("The program panicked for the following reason: \n"); print_candy_value(reason); diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.h b/compiler/backend_inkwell/candy_rt/candy_rt.h index 99da7713c..bde9ae52e 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.h +++ b/compiler/backend_inkwell/candy_rt/candy_rt.h @@ -40,15 +40,25 @@ typedef struct candy_value typedef candy_value_t *(*candy_function)(candy_value_t *); -extern candy_value_t __internal_true; -extern candy_value_t __internal_false; -extern candy_value_t __internal_nothing; +const extern candy_value_t __internal_true; +const extern candy_value_t __internal_false; +const extern candy_value_t __internal_nothing; +const extern candy_value_t __internal_less; +const extern candy_value_t __internal_equal; +const extern candy_value_t __internal_greater; +const extern candy_value_t __internal_int; +const extern candy_value_t __internal_text; +const extern candy_value_t __internal_tag; +const extern candy_value_t __internal_list; +const extern candy_value_t __internal_struct; +const extern candy_value_t __internal_function; +const extern candy_value_t __internal_unknown; extern candy_value_t _candy_environment; extern candy_value_t *candy_environment; -void print_candy_value(candy_value_t *value); -candy_value_t *to_candy_bool(int value); -int candy_tag_to_bool(candy_value_t *value); +void print_candy_value(const candy_value_t *value); +const candy_value_t *to_candy_bool(int value); +int candy_tag_to_bool(const candy_value_t *value); candy_value_t *make_candy_int(int128_t value); candy_value_t *make_candy_text(char *text); candy_value_t *make_candy_tag(char *tag); @@ -57,6 +67,6 @@ candy_value_t *make_candy_function(candy_function function, void *environment, i candy_value_t *call_candy_function_with(candy_value_t *function, candy_value_t *arg); candy_function get_candy_function_pointer(candy_value_t *function); void *get_candy_function_environment(candy_value_t *function); -void candy_panic(candy_value_t *reason); +void candy_panic(const candy_value_t *reason); void free_candy_value(candy_value_t *value); #endif \ No newline at end of file diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 1a5796c6e..14de8615e 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -75,20 +75,21 @@ pub(crate) fn compile(options: Options) -> ProgramResult { .map_err(|e| Exit::LLVMError(e.to_string()))?; std::process::Command::new("llc") .arg(&bc_path) + .args(["-O3"]) .spawn() .unwrap() .wait() .unwrap(); if options.build_rt { std::process::Command::new("make") - .args(&["-C", "compiler/backend_inkwell/candy_rt/", "clean"]) + .args(["-C", "compiler/backend_inkwell/candy_rt/", "clean"]) .spawn() .unwrap() .wait() .unwrap(); std::process::Command::new("make") - .args(&["-C", "compiler/backend_inkwell/candy_rt/", "candy_rt.a"]) + .args(["-C", "compiler/backend_inkwell/candy_rt/", "candy_rt.a"]) .spawn() .unwrap() .wait() @@ -101,6 +102,8 @@ pub(crate) fn compile(options: Options) -> ProgramResult { s_path.to_str().unwrap(), "compiler/backend_inkwell/candy_rt/candy_rt.a", if options.debug { "-g" } else { "" }, + "-O3", + "-flto", "-o", &s_path.to_str().unwrap().replace(".candy.s", ""), ]) From f11eabb5e5ef1999537e6c62bcdddfaa4d0bfd57 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 14:14:04 +0200 Subject: [PATCH 12/27] Apply suggestions from code review Co-authored-by: Jonas Wanke --- .gitignore | 2 +- compiler/backend_inkwell/Cargo.toml | 6 +-- compiler/backend_inkwell/README.md | 8 ++-- compiler/backend_inkwell/candy_rt/Makefile | 2 +- .../backend_inkwell/candy_rt/candy_builtin.c | 21 ++------- compiler/backend_inkwell/candy_rt/candy_rt.c | 47 ++++++++++--------- compiler/cli/src/inkwell.rs | 29 ++++++------ compiler/cli/src/main.rs | 2 +- 8 files changed, 54 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index a4ac14780..dfe07b984 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,4 @@ coverage/ *.o *.a *.ll -*.s \ No newline at end of file +*.s diff --git a/compiler/backend_inkwell/Cargo.toml b/compiler/backend_inkwell/Cargo.toml index d420735ef..bf9f4bc73 100644 --- a/compiler/backend_inkwell/Cargo.toml +++ b/compiler/backend_inkwell/Cargo.toml @@ -1,11 +1,9 @@ [package] -name = "backend_inkwell" +name = "candy_backend_inkwell" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] candy_frontend = { version = "0.1.0", path = "../frontend" } inkwell = { version = "0.2.0", features = ["llvm15-0"] } -llvm-sys = { version = "*", features = ["prefer-dynamic"] } \ No newline at end of file +llvm-sys = { version = "150", features = ["prefer-dynamic"] } \ No newline at end of file diff --git a/compiler/backend_inkwell/README.md b/compiler/backend_inkwell/README.md index ba39ef9b5..c00230432 100644 --- a/compiler/backend_inkwell/README.md +++ b/compiler/backend_inkwell/README.md @@ -1,9 +1,9 @@ # Candy Inkwell Compiler -This crate provides an LLVM-based backend for the Candy compiler -using the inkwell crate. It is highly experimental and still -doesn't support many of Candy's features. +This crate provides an LLVM-based backend for the Candy compiler using the inkwell crate. +It is highly experimental and still doesn't support many of Candy's features. ## Requirements -You currently need to have LLVM 15 installed on your system. You also need to have the `llc` and `clang` commands available. \ No newline at end of file +You currently need to have LLVM 15 installed on your system. +You also need to have the `llc` and `clang` commands available. \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/Makefile b/compiler/backend_inkwell/candy_rt/Makefile index ce7332219..147841a18 100644 --- a/compiler/backend_inkwell/candy_rt/Makefile +++ b/compiler/backend_inkwell/candy_rt/Makefile @@ -7,4 +7,4 @@ candy_rt.a: candy_rt.o candy_builtin.o .PHONY: clean clean: - rm -f *.o *.a \ No newline at end of file + rm -f *.o *.a diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.c b/compiler/backend_inkwell/candy_rt/candy_builtin.c index a8cc8b57b..7501e8b61 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.c +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.c @@ -23,18 +23,10 @@ const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *ri const candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise) { - if (candy_tag_to_bool(condition)) - { - candy_function then_function = (then->value).function.function; - candy_value_t *environment = (then->value).function.environment; - return then_function(environment); - } - else - { - candy_function otherwise_function = (otherwise->value).function.function; - candy_value_t *environment = (otherwise->value).function.environment; - return otherwise_function(environment); - } + candy_value_t *body = candy_tag_to_bool(condition) ? then : otherwise; + candy_function function = (body->value).function.function; + candy_value_t *environment = (body->value).function.environment; + return function(environment); } candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right) @@ -107,10 +99,6 @@ candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t size_t index = 0; while (!candy_tag_to_bool(candy_builtin_equals(structure->value.structure.keys[index], key))) { - if (structure->value.structure.keys[index] == NULL) - { - candy_panic(make_candy_text("Attempted to access non-existent struct member")); - } index++; } return structure->value.structure.values[index]; @@ -123,7 +111,6 @@ candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure) const candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key) { - size_t index = 0; while (structure->value.structure.keys[index] != NULL) { diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.c b/compiler/backend_inkwell/candy_rt/candy_rt.c index 301675816..ca6342d79 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt/candy_rt.c @@ -63,13 +63,23 @@ void print_candy_value(const candy_value_t *value) size_t list_length = length->value.integer; free_candy_value(length); size_t index = 0; - for (size_t index = 0; index < list_length; index++) + switch (list_length) { - print_candy_value(value->value.list[index]); - if (index != list_length - 1) + case 1: + print_candy_value(value->value.list[0]); + case 0: + printf(","); + break; + default: + for (size_t index = 0; index < list_length; index++) { - printf(", "); + print_candy_value(value->value.list[index]); + if (index != list_length - 1) + { + printf(", "); + } } + break; } printf(")"); break; @@ -84,14 +94,7 @@ void print_candy_value(const candy_value_t *value) const candy_value_t *to_candy_bool(int value) { - if (value) - { - return &__internal_true; - } - else - { - return &__internal_false; - } + return value ? &__internal_true : &__internal_false; } int candy_tag_to_bool(const candy_value_t *value) @@ -190,15 +193,17 @@ void candy_panic(const candy_value_t *reason) void free_candy_value(candy_value_t *value) { - if (value != candy_environment && value != NULL) + if (value == candy_environment && value == NULL) { - if (value->type == CANDY_TYPE_TAG || value->type == CANDY_TYPE_TEXT) - { - free(value->value.text); - } - // List and struct entries may not be freed as part of freeing - // the list/struct, because they will be freed on their own - // at the end of the main function. - free(value); + return; + } + + if (value->type == CANDY_TYPE_TAG || value->type == CANDY_TYPE_TEXT) + { + free(value->value.text); } + // List and struct entries may not be freed as part of freeing + // the list/struct, because they will be freed on their own + // at the end of the main function. + free(value); } \ No newline at end of file diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 14de8615e..d659a4fd9 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -14,20 +14,24 @@ use crate::{Exit, ProgramResult}; #[derive(Parser, Debug)] pub(crate) struct Options { - /// If enabled, the compiler will print the generated LLVM IR to stderr. + /// If enabled, print the generated LLVM IR to stderr. #[arg(long = "print-llvm-ir", default_value_t = false)] - print_llvm_ir: bool, - /// If enabled, the program will print the output of the candy main function. + print_llvm_ir: bool, + + /// If enabled, print the output of the Candy main function. #[arg(long = "print-main-output", default_value_t = false)] print_main_output: bool, - /// If enabled, the compiler will build the Candy runtime from scratch. + + /// If enabled, build the Candy runtime from scratch. #[arg(long = "build-rt", default_value_t = false)] build_rt: bool, - /// If enabled, the LLVM bitcode will be compiled with debug information. + + /// If enabled, compile the LLVM bitcode with debug information. #[arg(short = 'g', default_value_t = false)] debug: bool, - /// The file or package to run. If none is provided, the package of your - /// current working directory will be run. + + /// The file or package to run. If none is provided, run the package of your + /// current working directory. #[arg(value_hint = ValueHint::FilePath)] path: Option, } @@ -51,9 +55,8 @@ pub(crate) fn compile(options: Options) -> ProgramResult { let responsible = body.push_hir_id(hir::Id::user()); body.push_panic(reason, responsible); }); - let errors = vec![CompilerError::for_whole_module(module.clone(), payload)] - .into_iter() - .collect(); + let errors = + FxHashSet::from_iter([CompilerError::for_whole_module(module.clone(), payload)]); (Arc::new(mir), Arc::new(errors)) }); @@ -68,8 +71,7 @@ pub(crate) fn compile(options: Options) -> ProgramResult { let module = context.create_module(&path); let builder = context.create_builder(); let codegen = CodeGen::new(&context, module, builder, mir); - let mut bc_path = PathBuf::new(); - bc_path.push(&format!("{path}.bc")); + let bc_path = PathBuf::from(format!("{path}.bc")); codegen .compile(&bc_path, options.print_llvm_ir, options.print_main_output) .map_err(|e| Exit::LLVMError(e.to_string()))?; @@ -95,8 +97,7 @@ pub(crate) fn compile(options: Options) -> ProgramResult { .wait() .unwrap(); } - let mut s_path = PathBuf::new(); - s_path.push(&format!("{path}.s")); + let s_path = PathBuf::from(format!("{path}.s")); std::process::Command::new("clang") .args([ s_path.to_str().unwrap(), diff --git a/compiler/cli/src/main.rs b/compiler/cli/src/main.rs index 33b943140..559d1a22f 100644 --- a/compiler/cli/src/main.rs +++ b/compiler/cli/src/main.rs @@ -62,7 +62,7 @@ enum Exit { FuzzingFoundFailingCases, NotInCandyPackage, CodeContainsErrors, - LLVMError(String), + LlvmError(String), GoldOutdated, } From f56f1a8fab542420ac43715ea4ab65539a1c07e4 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 14:15:41 +0200 Subject: [PATCH 13/27] clean up runtime and codegen --- compiler/backend_inkwell/Cargo.toml | 2 +- .../backend_inkwell/candy_rt/candy_builtin.c | 14 +++-- .../backend_inkwell/candy_rt/candy_builtin.h | 14 ++--- compiler/backend_inkwell/candy_rt/candy_rt.c | 4 +- compiler/backend_inkwell/candy_rt/candy_rt.h | 6 +-- compiler/backend_inkwell/src/lib.rs | 53 ++++++++++--------- compiler/cli/src/inkwell.rs | 4 +- 7 files changed, 51 insertions(+), 46 deletions(-) diff --git a/compiler/backend_inkwell/Cargo.toml b/compiler/backend_inkwell/Cargo.toml index d420735ef..c10cf1f09 100644 --- a/compiler/backend_inkwell/Cargo.toml +++ b/compiler/backend_inkwell/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [dependencies] candy_frontend = { version = "0.1.0", path = "../frontend" } inkwell = { version = "0.2.0", features = ["llvm15-0"] } -llvm-sys = { version = "*", features = ["prefer-dynamic"] } \ No newline at end of file +llvm-sys = { version = "*", features = ["prefer-dynamic"] } diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.c b/compiler/backend_inkwell/candy_rt/candy_builtin.c index a8cc8b57b..b1ac98648 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.c +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.c @@ -1,4 +1,5 @@ #include +#include #include #include "candy_rt.h" @@ -49,7 +50,14 @@ candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *ri candy_value_t *candy_builtin_int_bit_length(candy_value_t *value) { - return make_candy_int(64); + long long int_value = value->value.integer; + int shifts = 0; + while (int_value) + { + int_value = int_value >> shifts; + shifts++; + } + return make_candy_int(shifts); } candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right) @@ -69,8 +77,8 @@ candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t const candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right) { - int128_t left_value = left->value.integer; - int128_t right_value = right->value.integer; + int64_t left_value = left->value.integer; + int64_t right_value = right->value.integer; if (left_value < right_value) { return &__internal_less; diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.h b/compiler/backend_inkwell/candy_rt/candy_builtin.h index e26decd7c..9cee47e22 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.h +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.h @@ -1,17 +1,17 @@ #include "candy_rt.h" -candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right); -candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise); +const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right); +const candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise); candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_int_bit_length(candy_value_t *value); candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right); -candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right); -candy_value_t *candy_builtin_list_length(candy_value_t *list); -candy_value_t *candy_builtin_print(candy_value_t *value); +const candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right); +candy_value_t *candy_builtin_list_length(const candy_value_t *list); +const candy_value_t *candy_builtin_print(candy_value_t *value); candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key); candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure); -candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key); -candy_value_t *candy_builtin_typeof(candy_value_t *value); \ No newline at end of file +const candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key); +const candy_value_t *candy_builtin_typeof(candy_value_t *value); \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.c b/compiler/backend_inkwell/candy_rt/candy_rt.c index 301675816..8a9c94e35 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt/candy_rt.c @@ -49,7 +49,7 @@ void print_candy_value(const candy_value_t *value) switch (value->type) { case CANDY_TYPE_INT: - printf("%lld", value->value.integer); + printf("%ld", value->value.integer); break; case CANDY_TYPE_TEXT: printf("%s", value->value.text); @@ -113,7 +113,7 @@ int candy_tag_to_bool(const candy_value_t *value) } } -candy_value_t *make_candy_int(int128_t value) +candy_value_t *make_candy_int(int64_t value) { candy_value_t *candy_value = malloc(sizeof(candy_value_t)); candy_value->value.integer = value; diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.h b/compiler/backend_inkwell/candy_rt/candy_rt.h index bde9ae52e..b844088bf 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.h +++ b/compiler/backend_inkwell/candy_rt/candy_rt.h @@ -1,8 +1,6 @@ #ifndef __CANDY_RT_H #define __CANDY_RT_H -#define int128_t long long int - typedef enum { CANDY_TYPE_INT = 42, @@ -29,7 +27,7 @@ typedef struct candy_value { union { - int128_t integer; + __int64_t integer; char *text; struct candy_value **list; candy_function_t function; @@ -59,7 +57,7 @@ extern candy_value_t *candy_environment; void print_candy_value(const candy_value_t *value); const candy_value_t *to_candy_bool(int value); int candy_tag_to_bool(const candy_value_t *value); -candy_value_t *make_candy_int(int128_t value); +candy_value_t *make_candy_int(int64_t value); candy_value_t *make_candy_text(char *text); candy_value_t *make_candy_tag(char *tag); candy_value_t *make_candy_list(candy_value_t **values); diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 2713c938f..4845766a7 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -38,12 +38,9 @@ pub struct CodeGen<'ctx> { } impl<'ctx> CodeGen<'ctx> { - pub fn new( - context: &'ctx Context, - module: Module<'ctx>, - builder: Builder<'ctx>, - mir: Arc, - ) -> Self { + pub fn new(context: &'ctx Context, module_name: &str, mir: Arc) -> Self { + let module = context.create_module(module_name); + let builder = context.create_builder(); Self { context, module, @@ -69,25 +66,29 @@ impl<'ctx> CodeGen<'ctx> { let i8_type = self.context.i8_type(); let void_type = self.context.void_type(); - let candy_type = self.context.opaque_struct_type("candy_type"); - let candy_type_ptr = candy_type.ptr_type(AddressSpace::default()); + 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_type_ptr.fn_type(&[i64_type.into()], false); + 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_type_ptr.fn_type(&[i8_type.ptr_type(AddressSpace::default()).into()], false); + candy_value_ptr.fn_type(&[i8_type.ptr_type(AddressSpace::default()).into()], false); let make_candy_tag = self.module .add_function("make_candy_tag", make_tag_fn_type, Some(Linkage::External)); self.module .add_function("make_candy_text", make_tag_fn_type, Some(Linkage::External)); - self.module - .add_function("make_candy_list", make_tag_fn_type, Some(Linkage::External)); - let make_function_fn_type = candy_type_ptr.fn_type( + let make_list_fn_type = candy_value_ptr.fn_type(&[candy_value_ptr.into()], false); + self.module.add_function( + "make_candy_list", + make_list_fn_type, + Some(Linkage::External), + ); + let make_function_fn_type = candy_value_ptr.fn_type( &[ - candy_type_ptr.into(), - candy_type_ptr.into(), + candy_value_ptr.into(), + candy_value_ptr.into(), i64_type.into(), ], false, @@ -99,7 +100,7 @@ impl<'ctx> CodeGen<'ctx> { ); let make_struct_fn_type = - candy_type_ptr.fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + 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, @@ -107,13 +108,13 @@ impl<'ctx> CodeGen<'ctx> { ); let struct_get_fn_type = - candy_type_ptr.fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + 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 panic_fn_type = void_type.fn_type( - &[candy_type.ptr_type(AddressSpace::default()).into()], + &[candy_value.ptr_type(AddressSpace::default()).into()], false, ); self.module.add_function("candy_panic", panic_fn_type, None); @@ -124,13 +125,13 @@ impl<'ctx> CodeGen<'ctx> { .module .add_function("print_candy_value", panic_fn_type, None); - let candy_fn_type = candy_type_ptr.fn_type(&[], true); + 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_type_ptr.into()], false); + .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_type_ptr.fn_type(&[candy_type_ptr.into()], false); + let get_candy_fn_env_type = candy_value_ptr.fn_type(&[candy_value_ptr.into()], false); self.module.add_function( "get_candy_function_environment", get_candy_fn_env_type, @@ -142,7 +143,7 @@ impl<'ctx> CodeGen<'ctx> { let block = self.context.append_basic_block(main_fn, "entry"); let call_candy_function_type = - candy_type_ptr.fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + candy_value_ptr.fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); let call_candy_function_with = self.module .add_function("call_candy_function_with", call_candy_function_type, None); @@ -159,7 +160,7 @@ impl<'ctx> CodeGen<'ctx> { let environment = self .module - .add_global(candy_type_ptr, None, "candy_environment"); + .add_global(candy_value_ptr, None, "candy_environment"); if let Some(main_return) = self.main_return { const MAIN_FN_NAME: &str = "Main"; @@ -198,9 +199,9 @@ impl<'ctx> CodeGen<'ctx> { "", ); for value in self.module.get_globals() { - let val = self - .builder - .build_load(candy_type_ptr, value.as_pointer_value(), ""); + let val = + self.builder + .build_load(candy_value_ptr, value.as_pointer_value(), ""); self.builder.build_call(free_fn, &[val.into()], ""); } } diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 14de8615e..f82baee43 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -65,9 +65,7 @@ pub(crate) fn compile(options: Options) -> ProgramResult { } let context = backend_inkwell::inkwell::context::Context::create(); - let module = context.create_module(&path); - let builder = context.create_builder(); - let codegen = CodeGen::new(&context, module, builder, mir); + let codegen = CodeGen::new(&context, &path, mir); let mut bc_path = PathBuf::new(); bc_path.push(&format!("{path}.bc")); codegen From cdb1cb9a0417283efb6be138d2c9d72cd1dbeb00 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 14:17:10 +0200 Subject: [PATCH 14/27] remove int.candy example --- packages/Examples/int.candy | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 packages/Examples/int.candy diff --git a/packages/Examples/int.candy b/packages/Examples/int.candy deleted file mode 100644 index 387ec7930..000000000 --- a/packages/Examples/int.candy +++ /dev/null @@ -1,3 +0,0 @@ -x = 42 - -main environment := x From 874c29908d3205f3acd58645e1580df6946b4be5 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 14:19:51 +0200 Subject: [PATCH 15/27] fix packages after renaming backend_inkwell --- compiler/backend_inkwell/candy_rt/candy_rt.h | 4 +++- compiler/cli/Cargo.toml | 2 +- compiler/cli/src/inkwell.rs | 7 ++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.h b/compiler/backend_inkwell/candy_rt/candy_rt.h index b844088bf..da6611dd5 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.h +++ b/compiler/backend_inkwell/candy_rt/candy_rt.h @@ -1,6 +1,8 @@ #ifndef __CANDY_RT_H #define __CANDY_RT_H +#include + typedef enum { CANDY_TYPE_INT = 42, @@ -27,7 +29,7 @@ typedef struct candy_value { union { - __int64_t integer; + int64_t integer; char *text; struct candy_value **list; candy_function_t function; diff --git a/compiler/cli/Cargo.toml b/compiler/cli/Cargo.toml index e7893f8cb..8b70ed91e 100644 --- a/compiler/cli/Cargo.toml +++ b/compiler/cli/Cargo.toml @@ -10,7 +10,7 @@ name = "candy" path = "src/main.rs" [dependencies] -backend_inkwell = { path = "../backend_inkwell" } +candy_backend_inkwell = { path = "../backend_inkwell" } candy_frontend = { path = "../frontend" } candy_fuzzer = { path = "../fuzzer" } candy_language_server = { path = "../language_server" } diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 05cfd313b..629a2adf5 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -1,12 +1,13 @@ use std::path::PathBuf; use std::sync::Arc; -use backend_inkwell::CodeGen; +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, TracingConfig}; use clap::{Parser, ValueHint}; +use rustc_hash::FxHashSet; use crate::database::Database; use crate::utils::{module_for_path, packages_path}; @@ -67,13 +68,13 @@ pub(crate) fn compile(options: Options) -> ProgramResult { std::process::exit(1); } - let context = backend_inkwell::inkwell::context::Context::create(); + let context = candy_backend_inkwell::inkwell::context::Context::create(); let codegen = CodeGen::new(&context, &path, mir); let mut bc_path = PathBuf::new(); bc_path.push(&format!("{path}.bc")); codegen .compile(&bc_path, options.print_llvm_ir, options.print_main_output) - .map_err(|e| Exit::LLVMError(e.to_string()))?; + .map_err(|e| Exit::LlvmError(e.to_string()))?; std::process::Command::new("llc") .arg(&bc_path) .args(["-O3"]) From 832e868e3977377d0ba6be12f6375fcf68f6c616 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 14:46:25 +0200 Subject: [PATCH 16/27] replace candy_type with candy_value --- compiler/backend_inkwell/src/lib.rs | 183 ++++++++++++++++------------ compiler/cli/src/inkwell.rs | 42 +------ compiler/cli/src/main.rs | 1 + 3 files changed, 111 insertions(+), 115 deletions(-) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 4845766a7..b4719ce4e 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -12,7 +12,7 @@ use inkwell::{ pub use inkwell; use std::{ collections::{HashMap, HashSet}, - path::Path, + path::{Path, PathBuf}, rc::Rc, sync::Arc, }; @@ -56,7 +56,7 @@ impl<'ctx> CodeGen<'ctx> { } pub fn compile( - mut self, + &mut self, path: &Path, print_llvm_ir: bool, print_main_output: bool, @@ -217,10 +217,49 @@ impl<'ctx> CodeGen<'ctx> { Ok(()) } + pub fn compile_asm_and_link( + &self, + path: &str, + build_rt: bool, + debug: bool, + ) -> Result<(), std::io::Error> { + let bc_path = PathBuf::from(&format!("{path}.bc")); + std::process::Command::new("llc") + .arg(&bc_path) + .args(["-O3"]) + .spawn()? + .wait()?; + if build_rt { + std::process::Command::new("make") + .args(["-C", "compiler/backend_inkwell/candy_rt/", "clean"]) + .spawn()? + .wait()?; + + std::process::Command::new("make") + .args(["-C", "compiler/backend_inkwell/candy_rt/", "candy_rt.a"]) + .spawn()? + .wait()?; + } + let s_path = PathBuf::from(format!("{path}.s")); + std::process::Command::new("clang") + .args([ + s_path.to_str().unwrap(), + "compiler/backend_inkwell/candy_rt/candy_rt.a", + if debug { "-g" } else { "" }, + "-O3", + "-flto", + "-o", + &s_path.to_str().unwrap().replace(".candy.s", ""), + ]) + .spawn()? + .wait()?; + Ok(()) + } + fn compile_mir(&mut self, mir: &Body, function_ctx: &FunctionInfo<'ctx>) { - let candy_type_ptr = self + let candy_value_ptr = self .module - .get_struct_type("candy_type") + .get_struct_type("candy_value") .unwrap() .ptr_type(AddressSpace::default()); @@ -229,12 +268,11 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::mir::Expression::Int(value) => { let i64_type = self.context.i64_type(); let v = i64_type.const_int(value.try_into().unwrap(), false); - let candy_type = self.module.get_struct_type("candy_type").unwrap(); - let candy_type_ptr = candy_type.ptr_type(AddressSpace::default()); + let global = self.module - .add_global(candy_type_ptr, None, &format!("num_{value}")); - global.set_initializer(&candy_type_ptr.const_null()); + .add_global(candy_value_ptr, None, &format!("num_{value}")); + global.set_initializer(&candy_value_ptr.const_null()); let make_candy_int = self.module.get_function("make_candy_int").unwrap(); let call = self.builder.build_call(make_candy_int, &[v.into()], ""); @@ -254,7 +292,7 @@ impl<'ctx> CodeGen<'ctx> { let i32_type = self.context.i32_type(); let i8_type = self.context.i8_type(); - let global = self.module.add_global(candy_type_ptr, None, text); + let global = self.module.add_global(candy_value_ptr, None, text); let v = self.make_str_literal(text); let len = i32_type.const_int(text.len() as u64 + 1, false); let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); @@ -272,7 +310,7 @@ impl<'ctx> CodeGen<'ctx> { call.try_as_basic_value().unwrap_left(), ); - global.set_initializer(&candy_type_ptr.const_null()); + global.set_initializer(&candy_value_ptr.const_null()); self.values.insert(*id, Rc::new(global)); if idx == mir.expressions.len() - 1 { @@ -285,7 +323,7 @@ impl<'ctx> CodeGen<'ctx> { let i32_type = self.context.i32_type(); let i8_type = self.context.i8_type(); - let global = self.module.add_global(candy_type_ptr, None, symbol); + let global = self.module.add_global(candy_value_ptr, None, symbol); let v = self.make_str_literal(symbol); let len = i32_type.const_int(symbol.len() as u64, false); let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); @@ -303,7 +341,7 @@ impl<'ctx> CodeGen<'ctx> { call.try_as_basic_value().unwrap_left(), ); - global.set_initializer(&candy_type_ptr.const_null()); + global.set_initializer(&candy_value_ptr.const_null()); self.values.insert(*id, Rc::new(global)); if idx == mir.expressions.len() - 1 { @@ -321,8 +359,8 @@ impl<'ctx> CodeGen<'ctx> { todo!() } candy_frontend::builtin_functions::BuiltinFunction::Equals => { - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let fn_type = candy_value_ptr + .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); let function = self.module .add_function("candy_builtin_equals", fn_type, None); @@ -340,11 +378,11 @@ impl<'ctx> CodeGen<'ctx> { todo!() } candy_frontend::builtin_functions::BuiltinFunction::IfElse => { - let fn_type = candy_type_ptr.fn_type( + let fn_type = candy_value_ptr.fn_type( &[ - candy_type_ptr.into(), - candy_type_ptr.into(), - candy_type_ptr.into(), + candy_value_ptr.into(), + candy_value_ptr.into(), + candy_value_ptr.into(), ], false, ); @@ -361,8 +399,8 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntAdd => { - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let fn_type = candy_value_ptr + .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); let function = self.module .add_function("candy_builtin_int_add", fn_type, None); @@ -376,7 +414,7 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntBitLength => { - let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); + let fn_type = candy_value_ptr.fn_type(&[candy_value_ptr.into()], false); let function = self.module.add_function( "candy_builtin_int_bit_length", fn_type, @@ -392,8 +430,8 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseAnd => { - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let fn_type = candy_value_ptr + .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); let function = self.module.add_function( "candy_builtin_int_bitwise_and", fn_type, @@ -409,8 +447,8 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseOr => { - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let fn_type = candy_value_ptr + .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); let function = self.module.add_function( "candy_builtin_int_bitwise_or", fn_type, @@ -426,8 +464,8 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseXor => { - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let fn_type = candy_value_ptr + .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); let function = self.module.add_function( "candy_builtin_int_bitwise_xor", fn_type, @@ -443,8 +481,8 @@ impl<'ctx> CodeGen<'ctx> { ); } candy_frontend::builtin_functions::BuiltinFunction::IntCompareTo => { - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let fn_type = candy_value_ptr + .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); let function = self.module.add_function( "candy_builtin_int_compareto", fn_type, @@ -471,8 +509,8 @@ impl<'ctx> CodeGen<'ctx> { todo!() } candy_frontend::builtin_functions::BuiltinFunction::IntSubtract => { - let fn_type = candy_type_ptr - .fn_type(&[candy_type_ptr.into(), candy_type_ptr.into()], false); + let fn_type = candy_value_ptr + .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); let function = self.module.add_function( "candy_builtin_int_subtract", fn_type, @@ -495,7 +533,7 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::builtin_functions::BuiltinFunction::ListReplace => todo!(), candy_frontend::builtin_functions::BuiltinFunction::Parallel => todo!(), candy_frontend::builtin_functions::BuiltinFunction::Print => { - let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); + let fn_type = candy_value_ptr.fn_type(&[candy_value_ptr.into()], false); let function = self.module .add_function("candy_builtin_print", fn_type, None); @@ -540,7 +578,7 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::builtin_functions::BuiltinFunction::ToDebugText => todo!(), candy_frontend::builtin_functions::BuiltinFunction::Try => todo!(), candy_frontend::builtin_functions::BuiltinFunction::TypeOf => { - let fn_type = candy_type_ptr.fn_type(&[candy_type_ptr.into()], false); + let fn_type = candy_value_ptr.fn_type(&[candy_value_ptr.into()], false); let function = self.module .add_function("candy_builtin_typeof", fn_type, None); @@ -558,13 +596,9 @@ impl<'ctx> CodeGen<'ctx> { } candy_frontend::mir::Expression::List(list) => { let i32_type = self.context.i32_type(); - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); + let list_array = self.builder.build_array_alloca( - candy_type_ptr, + candy_value_ptr, i32_type.const_int(list.len() as u64 + 1, false), "", ); @@ -576,7 +610,7 @@ impl<'ctx> CodeGen<'ctx> { for (idx, value) in values.enumerate() { let value_position = unsafe { self.builder.build_gep( - candy_type_ptr, + candy_value_ptr, list_array, &[i32_type.const_int(idx as u64, false)], "", @@ -586,17 +620,17 @@ impl<'ctx> CodeGen<'ctx> { } let end_position = unsafe { self.builder.build_gep( - candy_type_ptr, + candy_value_ptr, list_array, &[i32_type.const_int(list.len() as u64, false)], "", ) }; self.builder - .build_store(end_position, candy_type_ptr.const_null()); + .build_store(end_position, candy_value_ptr.const_null()); - let global = self.module.add_global(candy_type_ptr, None, ""); - global.set_initializer(&candy_type_ptr.const_null()); + let global = self.module.add_global(candy_value_ptr, None, ""); + global.set_initializer(&candy_value_ptr.const_null()); let make_candy_list = self.module.get_function("make_candy_list").unwrap(); let candy_list = @@ -610,20 +644,15 @@ impl<'ctx> CodeGen<'ctx> { } candy_frontend::mir::Expression::Struct(s) => { let i32_type = self.context.i32_type(); - let candy_type_ptr = self - .module - .get_struct_type("candy_type") - .unwrap() - .ptr_type(AddressSpace::default()); let make_candy_struct = self.module.get_function("make_candy_struct").unwrap(); let keys_array = self.builder.build_array_alloca( - candy_type_ptr, + candy_value_ptr, i32_type.const_int(s.len() as u64 + 1, false), "", ); let values_array = self.builder.build_array_alloca( - candy_type_ptr, + candy_value_ptr, i32_type.const_int(s.len() as u64 + 1, false), "", ); @@ -639,7 +668,7 @@ impl<'ctx> CodeGen<'ctx> { let key_ptr = unsafe { self.builder.build_gep( - candy_type_ptr, + candy_value_ptr, keys_array, &[i32_type.const_int(idx as u64, false)], "", @@ -648,7 +677,7 @@ impl<'ctx> CodeGen<'ctx> { self.builder.build_store(key_ptr, key); let value_ptr = unsafe { self.builder.build_gep( - candy_type_ptr, + candy_value_ptr, values_array, &[i32_type.const_int(idx as u64, false)], "", @@ -660,24 +689,24 @@ impl<'ctx> CodeGen<'ctx> { // Null-terminate key/value arrays let key_ptr = unsafe { self.builder.build_gep( - candy_type_ptr, + candy_value_ptr, keys_array, &[i32_type.const_int(s.len() as u64, false)], "", ) }; self.builder - .build_store(key_ptr, candy_type_ptr.const_null()); + .build_store(key_ptr, candy_value_ptr.const_null()); let value_ptr = unsafe { self.builder.build_gep( - candy_type_ptr, + candy_value_ptr, values_array, &[i32_type.const_int(s.len() as u64, false)], "", ) }; self.builder - .build_store(value_ptr, candy_type_ptr.const_null()); + .build_store(value_ptr, candy_value_ptr.const_null()); let struct_value = self .builder @@ -713,15 +742,15 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::mir::Expression::HirId(hir_id) => { let i32_type = self.context.i32_type(); let i8_type = self.context.i8_type(); - let candy_type_ptr = self + let candy_value_ptr = self .module - .get_struct_type("candy_type") + .get_struct_type("candy_value") .unwrap() .ptr_type(AddressSpace::default()); let text = format!("{hir_id}"); - let global = self.module.add_global(candy_type_ptr, None, &text); + let global = self.module.add_global(candy_value_ptr, None, &text); let v = self.make_str_literal(&text); let len = i32_type.const_int(text.len() as u64, false); let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); @@ -739,7 +768,7 @@ impl<'ctx> CodeGen<'ctx> { call.try_as_basic_value().unwrap_left(), ); - global.set_initializer(&candy_type_ptr.const_null()); + global.set_initializer(&candy_value_ptr.const_null()); self.values.insert(*id, Rc::new(global)); } candy_frontend::mir::Expression::Function { @@ -755,15 +784,15 @@ impl<'ctx> CodeGen<'ctx> { let i32_type = self.context.i32_type(); let i8_type = self.context.i8_type(); - let candy_type_ptr = self + let candy_value_ptr = self .module - .get_struct_type("candy_type") + .get_struct_type("candy_value") .unwrap() .ptr_type(AddressSpace::default()); let text = format!("{responsible_parameter}"); - let global = self.module.add_global(candy_type_ptr, None, &text); + let global = self.module.add_global(candy_value_ptr, None, &text); let v = self.make_str_literal(&text); let len = i32_type.const_int(text.len() as u64 + 1, false); let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); @@ -781,12 +810,12 @@ impl<'ctx> CodeGen<'ctx> { call.try_as_basic_value().unwrap_left(), ); - global.set_initializer(&candy_type_ptr.const_null()); + global.set_initializer(&candy_value_ptr.const_null()); self.values.insert(*responsible_parameter, Rc::new(global)); - let candy_type_ptr = self + let candy_value_ptr = self .module - .get_struct_type("candy_type") + .get_struct_type("candy_value") .unwrap() .ptr_type(AddressSpace::default()); @@ -801,7 +830,7 @@ impl<'ctx> CodeGen<'ctx> { let env_types: Vec<_> = captured_ids .iter() - .map(|_| candy_type_ptr.as_basic_type_enum()) + .map(|_| candy_value_ptr.as_basic_type_enum()) .collect(); let env_struct_type = self.context.struct_type(&env_types, false); @@ -829,12 +858,12 @@ impl<'ctx> CodeGen<'ctx> { } let mut params: Vec<_> = - parameters.iter().map(|_| candy_type_ptr.into()).collect(); + parameters.iter().map(|_| candy_value_ptr.into()).collect(); if !captured_ids.is_empty() { - params.push(candy_type_ptr.into()); + params.push(candy_value_ptr.into()); } - let fn_type = candy_type_ptr.fn_type(¶ms, false); + let fn_type = candy_value_ptr.fn_type(¶ms, false); let function = self.module.add_function(&name, fn_type, None); @@ -867,7 +896,7 @@ impl<'ctx> CodeGen<'ctx> { let global = self.module - .add_global(candy_type_ptr, None, &format!("fun_{name}")); + .add_global(candy_value_ptr, None, &format!("fun_{name}")); global.set_initializer(&function_ptr.get_type().const_null()); self.builder.build_store( @@ -949,7 +978,7 @@ impl<'ctx> CodeGen<'ctx> { args.push(fn_env_ptr.try_as_basic_value().unwrap_left().into()); - let candy_fn_type = candy_type_ptr.fn_type(&[], true); + let candy_fn_type = candy_value_ptr.fn_type(&[], true); let inner_fn = fn_ptr .try_as_basic_value() .unwrap_left() @@ -1005,14 +1034,14 @@ impl<'ctx> CodeGen<'ctx> { function_ctx: &FunctionInfo<'ctx>, id: &Id, ) -> Option> { - let candy_type_ptr = self + let candy_value_ptr = self .module - .get_struct_type("candy_type") + .get_struct_type("candy_value") .unwrap() .ptr_type(AddressSpace::default()); let mut v = self.values.get(id).map(|a| { self.builder - .build_load(candy_type_ptr, a.as_pointer_value(), "") + .build_load(candy_value_ptr, a.as_pointer_value(), "") }); if v.is_none() { if let Some(i) = function_ctx.captured_ids.iter().position(|i| i == id) { @@ -1028,7 +1057,7 @@ impl<'ctx> CodeGen<'ctx> { ) .unwrap(); - v.replace(self.builder.build_load(candy_type_ptr, env_value, "")); + v.replace(self.builder.build_load(candy_value_ptr, env_value, "")); } } if v.is_none() { diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 629a2adf5..6bbee88d5 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -69,49 +69,15 @@ pub(crate) fn compile(options: Options) -> ProgramResult { } let context = candy_backend_inkwell::inkwell::context::Context::create(); - let codegen = CodeGen::new(&context, &path, mir); + let mut codegen = CodeGen::new(&context, &path, mir); let mut bc_path = PathBuf::new(); bc_path.push(&format!("{path}.bc")); codegen .compile(&bc_path, options.print_llvm_ir, options.print_main_output) .map_err(|e| Exit::LlvmError(e.to_string()))?; - std::process::Command::new("llc") - .arg(&bc_path) - .args(["-O3"]) - .spawn() - .unwrap() - .wait() - .unwrap(); - if options.build_rt { - std::process::Command::new("make") - .args(["-C", "compiler/backend_inkwell/candy_rt/", "clean"]) - .spawn() - .unwrap() - .wait() - .unwrap(); - - std::process::Command::new("make") - .args(["-C", "compiler/backend_inkwell/candy_rt/", "candy_rt.a"]) - .spawn() - .unwrap() - .wait() - .unwrap(); - } - let s_path = PathBuf::from(format!("{path}.s")); - std::process::Command::new("clang") - .args([ - s_path.to_str().unwrap(), - "compiler/backend_inkwell/candy_rt/candy_rt.a", - if options.debug { "-g" } else { "" }, - "-O3", - "-flto", - "-o", - &s_path.to_str().unwrap().replace(".candy.s", ""), - ]) - .spawn() - .unwrap() - .wait() - .unwrap(); + codegen + .compile_asm_and_link(&path, options.build_rt, options.debug) + .map_err(|_| Exit::ExternalError)?; ProgramResult::Ok(()) } diff --git a/compiler/cli/src/main.rs b/compiler/cli/src/main.rs index 559d1a22f..a89a845c1 100644 --- a/compiler/cli/src/main.rs +++ b/compiler/cli/src/main.rs @@ -58,6 +58,7 @@ type ProgramResult = Result<(), Exit>; enum Exit { CodePanicked, DirectoryNotFound, + ExternalError, FileNotFound, FuzzingFoundFailingCases, NotInCandyPackage, From 4c28b548b67b220cfe3e5717a097e8231b1cb6cc Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 16:11:24 +0200 Subject: [PATCH 17/27] deduplicate more of string allocation process --- .../backend_inkwell/candy_rt/candy_builtin.c | 6 +- .../backend_inkwell/candy_rt/candy_builtin.h | 6 +- compiler/backend_inkwell/candy_rt/candy_rt.c | 2 +- compiler/backend_inkwell/src/lib.rs | 350 +++--------------- compiler/frontend/src/builtin_functions.rs | 5 +- 5 files changed, 59 insertions(+), 310 deletions(-) diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.c b/compiler/backend_inkwell/candy_rt/candy_builtin.c index 90d4cab00..2d41863ab 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.c +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.c @@ -22,7 +22,7 @@ const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *ri } } -const candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise) +const candy_value_t *candy_builtin_if_else(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise) { candy_value_t *body = candy_tag_to_bool(condition) ? then : otherwise; candy_function function = (body->value).function.function; @@ -67,7 +67,7 @@ candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t return make_candy_int(left->value.integer ^ right->value.integer); } -const candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right) +const candy_value_t *candy_builtin_int_compare_to(candy_value_t *left, candy_value_t *right) { int64_t left_value = left->value.integer; int64_t right_value = right->value.integer; @@ -130,7 +130,7 @@ const candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, cand return &__internal_false; } -const candy_value_t *candy_builtin_typeof(candy_value_t *value) +const candy_value_t *candy_builtin_type_of(candy_value_t *value) { switch (value->type) { diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.h b/compiler/backend_inkwell/candy_rt/candy_builtin.h index 9cee47e22..01a7067fc 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.h +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.h @@ -1,17 +1,17 @@ #include "candy_rt.h" const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right); -const candy_value_t *candy_builtin_ifelse(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise); +const candy_value_t *candy_builtin_if_else(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise); candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_int_bit_length(candy_value_t *value); candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right); -const candy_value_t *candy_builtin_int_compareto(candy_value_t *left, candy_value_t *right); +const candy_value_t *candy_builtin_int_compare_to(candy_value_t *left, candy_value_t *right); candy_value_t *candy_builtin_list_length(const candy_value_t *list); const candy_value_t *candy_builtin_print(candy_value_t *value); candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key); candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure); const candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key); -const candy_value_t *candy_builtin_typeof(candy_value_t *value); \ No newline at end of file +const candy_value_t *candy_builtin_type_of(candy_value_t *value); \ No newline at end of file diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.c b/compiler/backend_inkwell/candy_rt/candy_rt.c index 00c1c99ee..fee5fe86f 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt/candy_rt.c @@ -193,7 +193,7 @@ void candy_panic(const candy_value_t *reason) void free_candy_value(candy_value_t *value) { - if (value == candy_environment && value == NULL) + if (value == candy_environment || value == NULL) { return; } diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index b4719ce4e..7ec665a79 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -5,7 +5,7 @@ use inkwell::{ module::{Linkage, Module}, support::LLVMString, types::{BasicType, StructType}, - values::{ArrayValue, BasicValue, BasicValueEnum, FunctionValue, GlobalValue, PointerValue}, + values::{BasicValue, BasicValueEnum, FunctionValue, GlobalValue, PointerValue}, AddressSpace, }; @@ -19,7 +19,7 @@ use std::{ #[derive(Clone)] struct FunctionInfo<'ctx> { - function_value: Rc>, + function_value: FunctionValue<'ctx>, captured_ids: Vec, env_type: Option>, } @@ -149,7 +149,7 @@ impl<'ctx> CodeGen<'ctx> { .add_function("call_candy_function_with", call_candy_function_type, None); let main_info = FunctionInfo { - function_value: Rc::new(main_fn), + function_value: main_fn, captured_ids: vec![], env_type: None, }; @@ -164,10 +164,7 @@ impl<'ctx> CodeGen<'ctx> { if let Some(main_return) = self.main_return { const MAIN_FN_NAME: &str = "Main"; - let len = i32_type.const_int(MAIN_FN_NAME.len() as u64, false); - let main_text = self.builder.build_array_alloca(i8_type, len, ""); - let main_text_array = self.make_str_literal(MAIN_FN_NAME); - self.builder.build_store(main_text, main_text_array); + let main_text = self.make_str_literal(MAIN_FN_NAME); let main_tag = self .builder @@ -199,10 +196,12 @@ impl<'ctx> CodeGen<'ctx> { "", ); for value in self.module.get_globals() { - let val = - self.builder - .build_load(candy_value_ptr, value.as_pointer_value(), ""); - self.builder.build_call(free_fn, &[val.into()], ""); + if value != environment { + let val = + self.builder + .build_load(candy_value_ptr, value.as_pointer_value(), ""); + self.builder.build_call(free_fn, &[val.into()], ""); + } } } } @@ -289,21 +288,12 @@ impl<'ctx> CodeGen<'ctx> { } } candy_frontend::mir::Expression::Text(text) => { - let i32_type = self.context.i32_type(); - let i8_type = self.context.i8_type(); - let global = self.module.add_global(candy_value_ptr, None, text); - let v = self.make_str_literal(text); - let len = i32_type.const_int(text.len() as u64 + 1, false); - let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); - self.builder.build_store(arr_alloc, v); - let cast = self.builder.build_bitcast( - arr_alloc, - i8_type.ptr_type(AddressSpace::default()), - "", - ); + let string = self.make_str_literal(text); let make_candy_text = self.module.get_function("make_candy_text").unwrap(); - let call = self.builder.build_call(make_candy_text, &[cast.into()], ""); + let call = self + .builder + .build_call(make_candy_text, &[string.into()], ""); self.builder.build_store( global.as_pointer_value(), @@ -320,21 +310,13 @@ impl<'ctx> CodeGen<'ctx> { } candy_frontend::mir::Expression::Tag { symbol, value } => { self.tags.insert(symbol.clone(), *value); - let i32_type = self.context.i32_type(); - let i8_type = self.context.i8_type(); let global = self.module.add_global(candy_value_ptr, None, symbol); - let v = self.make_str_literal(symbol); - let len = i32_type.const_int(symbol.len() as u64, false); - let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); - self.builder.build_store(arr_alloc, v); - let cast = self.builder.build_bitcast( - arr_alloc, - i8_type.ptr_type(AddressSpace::default()), - "", - ); + let string = self.make_str_literal(symbol); let make_candy_tag = self.module.get_function("make_candy_tag").unwrap(); - let call = self.builder.build_call(make_candy_tag, &[cast.into()], ""); + let call = self + .builder + .build_call(make_candy_tag, &[string.into()], ""); self.builder.build_store( global.as_pointer_value(), @@ -350,248 +332,19 @@ impl<'ctx> CodeGen<'ctx> { } } candy_frontend::mir::Expression::Builtin(builtin) => { - match builtin { - candy_frontend::builtin_functions::BuiltinFunction::ChannelCreate => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::ChannelSend => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ChannelReceive => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::Equals => { - let fn_type = candy_value_ptr - .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - let function = - self.module - .add_function("candy_builtin_equals", fn_type, None); - self.functions.insert( - *id, - FunctionInfo { - function_value: Rc::new(function), - captured_ids: vec![], - env_type: None, - }, - ); - } - candy_frontend::builtin_functions::BuiltinFunction::FunctionRun => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::GetArgumentCount => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::IfElse => { - let fn_type = candy_value_ptr.fn_type( - &[ - candy_value_ptr.into(), - candy_value_ptr.into(), - candy_value_ptr.into(), - ], - false, - ); - let function = - self.module - .add_function("candy_builtin_ifelse", fn_type, None); - self.functions.insert( - *id, - FunctionInfo { - function_value: Rc::new(function), - captured_ids: vec![], - env_type: None, - }, - ); - } - candy_frontend::builtin_functions::BuiltinFunction::IntAdd => { - let fn_type = candy_value_ptr - .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - let function = - self.module - .add_function("candy_builtin_int_add", fn_type, None); - self.functions.insert( - *id, - FunctionInfo { - function_value: Rc::new(function), - captured_ids: vec![], - env_type: None, - }, - ); - } - candy_frontend::builtin_functions::BuiltinFunction::IntBitLength => { - let fn_type = candy_value_ptr.fn_type(&[candy_value_ptr.into()], false); - let function = self.module.add_function( - "candy_builtin_int_bit_length", - fn_type, - None, - ); - self.functions.insert( - *id, - FunctionInfo { - function_value: Rc::new(function), - captured_ids: vec![], - env_type: None, - }, - ); - } - candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseAnd => { - let fn_type = candy_value_ptr - .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - let function = self.module.add_function( - "candy_builtin_int_bitwise_and", - fn_type, - None, - ); - self.functions.insert( - *id, - FunctionInfo { - function_value: Rc::new(function), - captured_ids: vec![], - env_type: None, - }, - ); - } - candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseOr => { - let fn_type = candy_value_ptr - .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - let function = self.module.add_function( - "candy_builtin_int_bitwise_or", - fn_type, - None, - ); - self.functions.insert( - *id, - FunctionInfo { - function_value: Rc::new(function), - captured_ids: vec![], - env_type: None, - }, - ); - } - candy_frontend::builtin_functions::BuiltinFunction::IntBitwiseXor => { - let fn_type = candy_value_ptr - .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - let function = self.module.add_function( - "candy_builtin_int_bitwise_xor", - fn_type, - None, - ); - self.functions.insert( - *id, - FunctionInfo { - function_value: Rc::new(function), - captured_ids: vec![], - env_type: None, - }, - ); - } - candy_frontend::builtin_functions::BuiltinFunction::IntCompareTo => { - let fn_type = candy_value_ptr - .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - let function = self.module.add_function( - "candy_builtin_int_compareto", - fn_type, - None, - ); - self.functions.insert( - *id, - FunctionInfo { - function_value: Rc::new(function), - captured_ids: vec![], - env_type: None, - }, - ); - } - candy_frontend::builtin_functions::BuiltinFunction::IntDivideTruncating => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::IntModulo => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntMultiply => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntParse => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntRemainder => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntShiftLeft => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::IntShiftRight => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::IntSubtract => { - let fn_type = candy_value_ptr - .fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - let function = self.module.add_function( - "candy_builtin_int_subtract", - fn_type, - None, - ); - self.functions.insert( - *id, - FunctionInfo { - function_value: Rc::new(function), - captured_ids: vec![], - env_type: None, - }, - ); - } - candy_frontend::builtin_functions::BuiltinFunction::ListFilled => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ListGet => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ListInsert => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ListLength => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ListRemoveAt => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::ListReplace => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::Parallel => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::Print => { - let fn_type = candy_value_ptr.fn_type(&[candy_value_ptr.into()], false); - let function = - self.module - .add_function("candy_builtin_print", fn_type, None); - self.functions.insert( - *id, - FunctionInfo { - function_value: Rc::new(function), - captured_ids: vec![], - env_type: None, - }, - ); - } - candy_frontend::builtin_functions::BuiltinFunction::StructGet => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::StructGetKeys => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::StructHasKey => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TagGetValue => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TagHasValue => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TagWithoutValue => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::TextCharacters => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::TextConcatenate => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::TextContains => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextEndsWith => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextFromUtf8 => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextGetRange => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextIsEmpty => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextLength => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextStartsWith => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::TextTrimEnd => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TextTrimStart => { - todo!() - } - candy_frontend::builtin_functions::BuiltinFunction::ToDebugText => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::Try => todo!(), - candy_frontend::builtin_functions::BuiltinFunction::TypeOf => { - let fn_type = candy_value_ptr.fn_type(&[candy_value_ptr.into()], false); - let function = - self.module - .add_function("candy_builtin_typeof", fn_type, None); - self.functions.insert( - *id, - FunctionInfo { - function_value: Rc::new(function), - captured_ids: vec![], - env_type: None, - }, - ); - } - } + 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); + self.functions.insert( + *id, + FunctionInfo { + function_value: function, + captured_ids: vec![], + env_type: None, + }, + ); + self.unrepresented_ids.insert(*id); } candy_frontend::mir::Expression::List(list) => { @@ -740,28 +493,14 @@ impl<'ctx> CodeGen<'ctx> { } } candy_frontend::mir::Expression::HirId(hir_id) => { - let i32_type = self.context.i32_type(); - let i8_type = self.context.i8_type(); - let candy_value_ptr = self - .module - .get_struct_type("candy_value") - .unwrap() - .ptr_type(AddressSpace::default()); - let text = format!("{hir_id}"); let global = self.module.add_global(candy_value_ptr, None, &text); - let v = self.make_str_literal(&text); - let len = i32_type.const_int(text.len() as u64, false); - let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); - self.builder.build_store(arr_alloc, v); - let cast = self.builder.build_bitcast( - arr_alloc, - i8_type.ptr_type(AddressSpace::default()), - "", - ); + let string = self.make_str_literal(&text); let make_candy_text = self.module.get_function("make_candy_text").unwrap(); - let call = self.builder.build_call(make_candy_text, &[cast.into()], ""); + let call = self + .builder + .build_call(make_candy_text, &[string.into()], ""); self.builder.build_store( global.as_pointer_value(), @@ -868,7 +607,7 @@ impl<'ctx> CodeGen<'ctx> { let function = self.module.add_function(&name, fn_type, None); let fun_info = FunctionInfo { - function_value: Rc::new(function), + function_value: function, captured_ids: captured_ids.clone(), env_type: if !captured_ids.is_empty() { Some(env_struct_type) @@ -946,7 +685,7 @@ impl<'ctx> CodeGen<'ctx> { args.push(fn_env_ptr.try_as_basic_value().unwrap_left().into()); } - let call = self.builder.build_call(**function_value, &args, ""); + let call = self.builder.build_call(*function_value, &args, ""); let call_value = Rc::new(call.try_as_basic_value().unwrap_left()); self.locals.insert(*id, call_value.clone()); @@ -1019,14 +758,23 @@ impl<'ctx> CodeGen<'ctx> { } } - fn make_str_literal(&self, s: &str) -> ArrayValue<'_> { + fn make_str_literal(&self, text: &str) -> BasicValueEnum<'_> { let i8_type = self.context.i8_type(); - let content: Vec<_> = s + let i64_type = self.context.i64_type(); + + let content: Vec<_> = text .chars() .chain(std::iter::once('\0')) .map(|c| i8_type.const_int(c as u64, false)) .collect(); - i8_type.const_array(&content) + let v = i8_type.const_array(&content); + + let len = i64_type.const_int(text.len() as u64 + 1, false); + let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); + self.builder.build_store(arr_alloc, v); + + self.builder + .build_bitcast(arr_alloc, i8_type.ptr_type(AddressSpace::default()), "") } fn get_value_with_id( diff --git a/compiler/frontend/src/builtin_functions.rs b/compiler/frontend/src/builtin_functions.rs index fef7e87ef..4517affcf 100644 --- a/compiler/frontend/src/builtin_functions.rs +++ b/compiler/frontend/src/builtin_functions.rs @@ -1,8 +1,9 @@ use lazy_static::lazy_static; use strum::IntoEnumIterator; -use strum_macros::EnumIter; +use strum_macros::{AsRefStr, EnumIter}; -#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)] +#[derive(AsRefStr, Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)] +#[strum(serialize_all = "snake_case")] pub enum BuiltinFunction { ChannelCreate, // capacity -> [sendPort, receivePort] ChannelSend, // channel any -> Nothing From 05bf0d9b2e282b3b29c556c875abdf3afa4465ec Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 17:47:07 +0200 Subject: [PATCH 18/27] ignore responsibilities --- compiler/backend_inkwell/candy_rt/candy_rt.h | 5 +- compiler/backend_inkwell/src/lib.rs | 78 +++++--------------- 2 files changed, 21 insertions(+), 62 deletions(-) diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.h b/compiler/backend_inkwell/candy_rt/candy_rt.h index da6611dd5..31233ebc5 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.h +++ b/compiler/backend_inkwell/candy_rt/candy_rt.h @@ -16,7 +16,7 @@ typedef enum typedef struct { void *environment; - struct candy_value *(*function)(struct candy_value *); + struct candy_value *(*function)(struct candy_value *, ...); } candy_function_t; typedef struct @@ -38,7 +38,7 @@ typedef struct candy_value candy_type_t type; } candy_value_t; -typedef candy_value_t *(*candy_function)(candy_value_t *); +typedef candy_value_t *(*candy_function)(candy_value_t *, ...); const extern candy_value_t __internal_true; const extern candy_value_t __internal_false; @@ -53,6 +53,7 @@ const extern candy_value_t __internal_list; const extern candy_value_t __internal_struct; const extern candy_value_t __internal_function; const extern candy_value_t __internal_unknown; +const extern candy_value_t __internal_platform; extern candy_value_t _candy_environment; extern candy_value_t *candy_environment; diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 7ec665a79..8066d3294 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -30,7 +30,7 @@ pub struct CodeGen<'ctx> { builder: Builder<'ctx>, mir: Arc, tags: HashMap>, - values: HashMap>>, + globals: HashMap>>, locals: HashMap>>, functions: HashMap>, unrepresented_ids: HashSet, @@ -47,7 +47,7 @@ impl<'ctx> CodeGen<'ctx> { builder, mir, tags: HashMap::new(), - values: HashMap::new(), + globals: HashMap::new(), locals: HashMap::new(), functions: HashMap::new(), unrepresented_ids: HashSet::new(), @@ -280,7 +280,7 @@ impl<'ctx> CodeGen<'ctx> { call.try_as_basic_value().unwrap_left(), ); - self.values.insert(*id, Rc::new(global)); + self.globals.insert(*id, Rc::new(global)); if idx == mir.expressions.len() - 1 { self.builder @@ -301,7 +301,7 @@ impl<'ctx> CodeGen<'ctx> { ); global.set_initializer(&candy_value_ptr.const_null()); - self.values.insert(*id, Rc::new(global)); + self.globals.insert(*id, Rc::new(global)); if idx == mir.expressions.len() - 1 { self.builder @@ -324,7 +324,7 @@ impl<'ctx> CodeGen<'ctx> { ); global.set_initializer(&candy_value_ptr.const_null()); - self.values.insert(*id, Rc::new(global)); + self.globals.insert(*id, Rc::new(global)); if idx == mir.expressions.len() - 1 { self.builder @@ -393,7 +393,7 @@ impl<'ctx> CodeGen<'ctx> { global.as_pointer_value(), candy_list.try_as_basic_value().unwrap_left(), ); - self.values.insert(*id, Rc::new(global)); + self.globals.insert(*id, Rc::new(global)); } candy_frontend::mir::Expression::Struct(s) => { let i32_type = self.context.i32_type(); @@ -508,7 +508,7 @@ impl<'ctx> CodeGen<'ctx> { ); global.set_initializer(&candy_value_ptr.const_null()); - self.values.insert(*id, Rc::new(global)); + self.globals.insert(*id, Rc::new(global)); } candy_frontend::mir::Expression::Function { original_hirs, @@ -516,53 +516,17 @@ impl<'ctx> CodeGen<'ctx> { body, responsible_parameter, } => { + self.unrepresented_ids.insert(*responsible_parameter); let name = format!("{original_hirs:?}") .replace('{', "") .replace('}', "") .replace([':', '.'], "_"); - let i32_type = self.context.i32_type(); - let i8_type = self.context.i8_type(); - let candy_value_ptr = self - .module - .get_struct_type("candy_value") - .unwrap() - .ptr_type(AddressSpace::default()); - - let text = format!("{responsible_parameter}"); - - let global = self.module.add_global(candy_value_ptr, None, &text); - let v = self.make_str_literal(&text); - let len = i32_type.const_int(text.len() as u64 + 1, false); - let arr_alloc = self.builder.build_array_alloca(i8_type, len, ""); - self.builder.build_store(arr_alloc, v); - let cast = self.builder.build_bitcast( - arr_alloc, - i8_type.ptr_type(AddressSpace::default()), - "", - ); - let make_candy_text = self.module.get_function("make_candy_text").unwrap(); - let call = self.builder.build_call(make_candy_text, &[cast.into()], ""); - - self.builder.build_store( - global.as_pointer_value(), - call.try_as_basic_value().unwrap_left(), - ); - - global.set_initializer(&candy_value_ptr.const_null()); - self.values.insert(*responsible_parameter, Rc::new(global)); - - let candy_value_ptr = self - .module - .get_struct_type("candy_value") - .unwrap() - .ptr_type(AddressSpace::default()); - let captured_ids: Vec<_> = expr .captured_ids() .into_iter() .filter(|cap_id| { - !(self.values.contains_key(cap_id) + !(self.globals.contains_key(cap_id) || self.unrepresented_ids.contains(cap_id)) }) .collect(); @@ -577,17 +541,7 @@ impl<'ctx> CodeGen<'ctx> { let env_ptr = self.builder.build_malloc(env_struct_type, "").unwrap(); for (idx, cap_id) in captured_ids.iter().enumerate() { - let mut value = self.locals.get(cap_id).map(|v| **v); - - if value.is_none() { - value.replace( - (self - .values - .get(cap_id) - .unwrap_or_else(|| panic!("{cap_id} is not a global"))) - .as_basic_value_enum(), - ); - } + let value = self.get_value_with_id(function_ctx, cap_id); let member = self .builder @@ -598,6 +552,7 @@ impl<'ctx> CodeGen<'ctx> { let mut params: Vec<_> = parameters.iter().map(|_| candy_value_ptr.into()).collect(); + if !captured_ids.is_empty() { params.push(candy_value_ptr.into()); } @@ -643,7 +598,7 @@ impl<'ctx> CodeGen<'ctx> { call.try_as_basic_value().unwrap_left(), ); - self.values.insert(*id, Rc::new(global)); + self.globals.insert(*id, Rc::new(global)); let inner_block = self.context.append_basic_block(function, &name); self.builder.position_at_end(inner_block); @@ -655,14 +610,17 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::mir::Expression::Call { function, arguments, - .. + responsible, } => { + self.unrepresented_ids.insert(*responsible); let mut args: Vec<_> = arguments .iter() .map(|arg| self.get_value_with_id(function_ctx, arg).unwrap().into()) .collect(); + if let Some(FunctionInfo { function_value, + captured_ids: _, env_type, }) = self.functions.get(function) @@ -673,7 +631,7 @@ impl<'ctx> CodeGen<'ctx> { .get_function("get_candy_function_environment") .unwrap(); - let fn_object = self.values.get(function).unwrap_or_else(|| { + let fn_object = self.globals.get(function).unwrap_or_else(|| { panic!("Function {function} should have global visibility") }); @@ -787,7 +745,7 @@ impl<'ctx> CodeGen<'ctx> { .get_struct_type("candy_value") .unwrap() .ptr_type(AddressSpace::default()); - let mut v = self.values.get(id).map(|a| { + let mut v = self.globals.get(id).map(|a| { self.builder .build_load(candy_value_ptr, a.as_pointer_value(), "") }); From 65a905c1cb1968f1f72aeb795b3a0cfe534215ef Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 18:11:57 +0200 Subject: [PATCH 19/27] Update compiler/backend_inkwell/src/lib.rs Co-authored-by: Jonas Wanke --- compiler/backend_inkwell/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 8066d3294..830783e51 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -61,10 +61,10 @@ impl<'ctx> CodeGen<'ctx> { print_llvm_ir: bool, print_main_output: bool, ) -> Result<(), LLVMString> { + let void_type = self.context.void_type(); + let i8_type = self.context.i8_type(); let i32_type = self.context.i32_type(); let i64_type = self.context.i64_type(); - let i8_type = self.context.i8_type(); - let void_type = self.context.void_type(); let candy_value = self.context.opaque_struct_type("candy_value"); let candy_value_ptr = candy_value.ptr_type(AddressSpace::default()); From daee6f4023f154cdf32cf75d16e2133fd1b7fe41 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 18:22:42 +0200 Subject: [PATCH 20/27] Update compiler/backend_inkwell/src/lib.rs Co-authored-by: Jonas Wanke --- 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 830783e51..9fd17edaf 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -264,7 +264,7 @@ impl<'ctx> CodeGen<'ctx> { for (idx, (id, expr)) in mir.expressions.iter().enumerate() { match expr { - candy_frontend::mir::Expression::Int(value) => { + Expression::Int(value) => { let i64_type = self.context.i64_type(); let v = i64_type.const_int(value.try_into().unwrap(), false); From 8fb87a11c82029e43c8edd2b45e23f50a1972c26 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 19:24:11 +0200 Subject: [PATCH 21/27] extract global creation into function --- compiler/backend_inkwell/Cargo.toml | 1 + .../backend_inkwell/candy_rt/candy_builtin.c | 2 +- .../backend_inkwell/candy_rt/candy_builtin.h | 2 +- compiler/backend_inkwell/candy_rt/candy_rt.c | 2 +- compiler/backend_inkwell/candy_rt/candy_rt.h | 2 +- compiler/backend_inkwell/src/lib.rs | 221 ++++++++++-------- 6 files changed, 125 insertions(+), 105 deletions(-) diff --git a/compiler/backend_inkwell/Cargo.toml b/compiler/backend_inkwell/Cargo.toml index 0756b512a..645f4050c 100644 --- a/compiler/backend_inkwell/Cargo.toml +++ b/compiler/backend_inkwell/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" [dependencies] 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"] } diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.c b/compiler/backend_inkwell/candy_rt/candy_builtin.c index 2d41863ab..f5819abd4 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.c +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.c @@ -149,4 +149,4 @@ const candy_value_t *candy_builtin_type_of(candy_value_t *value) default: candy_panic(&__internal_unknown); } -} \ No newline at end of file +} diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.h b/compiler/backend_inkwell/candy_rt/candy_builtin.h index 01a7067fc..0492e24ca 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.h +++ b/compiler/backend_inkwell/candy_rt/candy_builtin.h @@ -14,4 +14,4 @@ const candy_value_t *candy_builtin_print(candy_value_t *value); candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key); candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure); const candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key); -const candy_value_t *candy_builtin_type_of(candy_value_t *value); \ No newline at end of file +const candy_value_t *candy_builtin_type_of(candy_value_t *value); diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.c b/compiler/backend_inkwell/candy_rt/candy_rt.c index fee5fe86f..2b5ce924f 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.c +++ b/compiler/backend_inkwell/candy_rt/candy_rt.c @@ -206,4 +206,4 @@ void free_candy_value(candy_value_t *value) // the list/struct, because they will be freed on their own // at the end of the main function. free(value); -} \ No newline at end of file +} diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.h b/compiler/backend_inkwell/candy_rt/candy_rt.h index 31233ebc5..9ffe769ad 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.h +++ b/compiler/backend_inkwell/candy_rt/candy_rt.h @@ -70,4 +70,4 @@ candy_function get_candy_function_pointer(candy_value_t *function); void *get_candy_function_environment(candy_value_t *function); void candy_panic(const candy_value_t *reason); void free_candy_value(candy_value_t *value); -#endif \ No newline at end of file +#endif diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 9fd17edaf..098ed0d7c 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -1,4 +1,6 @@ -use candy_frontend::mir::{Body, Id, Mir}; +#![feature(let_chains)] + +use candy_frontend::mir::{Body, Expression, Id, Mir}; use inkwell::{ builder::Builder, context::Context, @@ -10,10 +12,10 @@ use inkwell::{ }; pub use inkwell; +use itertools::Itertools; use std::{ collections::{HashMap, HashSet}, path::{Path, PathBuf}, - rc::Rc, sync::Arc, }; @@ -30,8 +32,8 @@ pub struct CodeGen<'ctx> { builder: Builder<'ctx>, mir: Arc, tags: HashMap>, - globals: HashMap>>, - locals: HashMap>>, + globals: HashMap>, + locals: HashMap>, functions: HashMap>, unrepresented_ids: HashSet, main_return: Option>, @@ -113,17 +115,18 @@ impl<'ctx> CodeGen<'ctx> { self.module .add_function("candy_builtin_struct_get", struct_get_fn_type, None); - let panic_fn_type = void_type.fn_type( + let ptr_to_void_fn_type = void_type.fn_type( &[candy_value.ptr_type(AddressSpace::default()).into()], false, ); - self.module.add_function("candy_panic", panic_fn_type, None); + self.module + .add_function("candy_panic", ptr_to_void_fn_type, None); let free_fn = self .module - .add_function("free_candy_value", panic_fn_type, None); + .add_function("free_candy_value", ptr_to_void_fn_type, None); let print_fn = self .module - .add_function("print_candy_value", panic_fn_type, None); + .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 @@ -268,40 +271,29 @@ impl<'ctx> CodeGen<'ctx> { let i64_type = self.context.i64_type(); let v = i64_type.const_int(value.try_into().unwrap(), false); - let global = - self.module - .add_global(candy_value_ptr, None, &format!("num_{value}")); - global.set_initializer(&candy_value_ptr.const_null()); let make_candy_int = self.module.get_function("make_candy_int").unwrap(); let call = self.builder.build_call(make_candy_int, &[v.into()], ""); - self.builder.build_store( - global.as_pointer_value(), + let global = self.create_global( + &format!("num_{value}"), + id, call.try_as_basic_value().unwrap_left(), ); - self.globals.insert(*id, Rc::new(global)); - if idx == mir.expressions.len() - 1 { self.builder .build_return(Some(&global.as_basic_value_enum())); } } candy_frontend::mir::Expression::Text(text) => { - let global = self.module.add_global(candy_value_ptr, None, text); let string = self.make_str_literal(text); let make_candy_text = self.module.get_function("make_candy_text").unwrap(); let call = self .builder .build_call(make_candy_text, &[string.into()], ""); - self.builder.build_store( - global.as_pointer_value(), - call.try_as_basic_value().unwrap_left(), - ); - - global.set_initializer(&candy_value_ptr.const_null()); - self.globals.insert(*id, Rc::new(global)); + let global = + self.create_global(text, id, call.try_as_basic_value().unwrap_left()); if idx == mir.expressions.len() - 1 { self.builder @@ -311,20 +303,17 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::mir::Expression::Tag { symbol, value } => { self.tags.insert(symbol.clone(), *value); - let global = self.module.add_global(candy_value_ptr, None, symbol); let string = self.make_str_literal(symbol); let make_candy_tag = self.module.get_function("make_candy_tag").unwrap(); let call = self .builder .build_call(make_candy_tag, &[string.into()], ""); - self.builder.build_store( - global.as_pointer_value(), - call.try_as_basic_value().unwrap_left(), - ); + let global = + self.create_global(symbol, id, call.try_as_basic_value().unwrap_left()); global.set_initializer(&candy_value_ptr.const_null()); - self.globals.insert(*id, Rc::new(global)); + self.globals.insert(*id, global); if idx == mir.expressions.len() - 1 { self.builder @@ -345,14 +334,37 @@ impl<'ctx> CodeGen<'ctx> { }, ); - self.unrepresented_ids.insert(*id); + let i64_type = self.context.i64_type(); + let function_ptr = function.as_global_value().as_pointer_value(); + let make_candy_function = + self.module.get_function("make_candy_function").unwrap(); + let call = self.builder.build_call( + make_candy_function, + &[ + function_ptr.into(), + candy_value_ptr.const_null().into(), + i64_type.const_zero().into(), + ], + "", + ); + + let global = self.create_global( + &format!("fun_{builtin_name}"), + id, + call.try_as_basic_value().unwrap_left(), + ); + + if idx == mir.expressions.len() - 1 { + self.builder + .build_return(Some(&global.as_basic_value_enum())); + } } candy_frontend::mir::Expression::List(list) => { - let i32_type = self.context.i32_type(); + let i64_type = self.context.i64_type(); let list_array = self.builder.build_array_alloca( candy_value_ptr, - i32_type.const_int(list.len() as u64 + 1, false), + i64_type.const_int(list.len() as u64 + 1, false), "", ); let values = list.iter().map(|v| { @@ -365,7 +377,7 @@ impl<'ctx> CodeGen<'ctx> { self.builder.build_gep( candy_value_ptr, list_array, - &[i32_type.const_int(idx as u64, false)], + &[i64_type.const_int(idx as u64, false)], "", ) }; @@ -375,38 +387,38 @@ impl<'ctx> CodeGen<'ctx> { self.builder.build_gep( candy_value_ptr, list_array, - &[i32_type.const_int(list.len() as u64, false)], + &[i64_type.const_int(list.len() as u64, false)], "", ) }; self.builder .build_store(end_position, candy_value_ptr.const_null()); - let global = self.module.add_global(candy_value_ptr, None, ""); - global.set_initializer(&candy_value_ptr.const_null()); - let make_candy_list = self.module.get_function("make_candy_list").unwrap(); let candy_list = self.builder .build_call(make_candy_list, &[list_array.into()], ""); - self.builder.build_store( - global.as_pointer_value(), - candy_list.try_as_basic_value().unwrap_left(), - ); - self.globals.insert(*id, Rc::new(global)); + + let global = + self.create_global("", id, candy_list.try_as_basic_value().unwrap_left()); + + if idx == mir.expressions.len() - 1 { + self.builder + .build_return(Some(&global.as_basic_value_enum())); + } } candy_frontend::mir::Expression::Struct(s) => { - let i32_type = self.context.i32_type(); + 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, - i32_type.const_int(s.len() as u64 + 1, false), + i64_type.const_int(s.len() as u64 + 1, false), "", ); let values_array = self.builder.build_array_alloca( candy_value_ptr, - i32_type.const_int(s.len() as u64 + 1, false), + i64_type.const_int(s.len() as u64 + 1, false), "", ); for (idx, (key, value)) in s.iter().enumerate() { @@ -423,7 +435,7 @@ impl<'ctx> CodeGen<'ctx> { self.builder.build_gep( candy_value_ptr, keys_array, - &[i32_type.const_int(idx as u64, false)], + &[i64_type.const_int(idx as u64, false)], "", ) }; @@ -432,7 +444,7 @@ impl<'ctx> CodeGen<'ctx> { self.builder.build_gep( candy_value_ptr, values_array, - &[i32_type.const_int(idx as u64, false)], + &[i64_type.const_int(idx as u64, false)], "", ) }; @@ -444,7 +456,7 @@ impl<'ctx> CodeGen<'ctx> { self.builder.build_gep( candy_value_ptr, keys_array, - &[i32_type.const_int(s.len() as u64, false)], + &[i64_type.const_int(s.len() as u64, false)], "", ) }; @@ -454,7 +466,7 @@ impl<'ctx> CodeGen<'ctx> { self.builder.build_gep( candy_value_ptr, values_array, - &[i32_type.const_int(s.len() as u64, false)], + &[i64_type.const_int(s.len() as u64, false)], "", ) }; @@ -471,7 +483,7 @@ impl<'ctx> CodeGen<'ctx> { .try_as_basic_value() .unwrap_left(); - self.locals.insert(*id, Rc::new(struct_value)); + self.locals.insert(*id, struct_value); let function_ctx_name = function_ctx.function_value.get_name().to_str().unwrap(); @@ -487,7 +499,7 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::mir::Expression::Reference(ref_id) => { let value = self.get_value_with_id(function_ctx, ref_id).unwrap(); - self.locals.insert(*id, Rc::new(value)); + self.locals.insert(*id, value); if idx == mir.expressions.len() - 1 { self.builder.build_return(Some(&value)); } @@ -495,20 +507,13 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::mir::Expression::HirId(hir_id) => { let text = format!("{hir_id}"); - let global = self.module.add_global(candy_value_ptr, None, &text); let string = self.make_str_literal(&text); let make_candy_text = self.module.get_function("make_candy_text").unwrap(); let call = self .builder .build_call(make_candy_text, &[string.into()], ""); - self.builder.build_store( - global.as_pointer_value(), - call.try_as_basic_value().unwrap_left(), - ); - - global.set_initializer(&candy_value_ptr.const_null()); - self.globals.insert(*id, Rc::new(global)); + self.create_global(&text, id, call.try_as_basic_value().unwrap_left()); } candy_frontend::mir::Expression::Function { original_hirs, @@ -517,10 +522,11 @@ impl<'ctx> CodeGen<'ctx> { responsible_parameter, } => { self.unrepresented_ids.insert(*responsible_parameter); - let name = format!("{original_hirs:?}") - .replace('{', "") - .replace('}', "") - .replace([':', '.'], "_"); + let name = original_hirs + .iter() + .sorted() + .map(|it| it.to_string().replace([':', '.'], "_")) + .join(", "); let captured_ids: Vec<_> = expr .captured_ids() @@ -561,7 +567,7 @@ impl<'ctx> CodeGen<'ctx> { let function = self.module.add_function(&name, fn_type, None); - let fun_info = FunctionInfo { + let function_info = FunctionInfo { function_value: function, captured_ids: captured_ids.clone(), env_type: if !captured_ids.is_empty() { @@ -570,10 +576,10 @@ impl<'ctx> CodeGen<'ctx> { None }, }; - self.functions.insert(*id, fun_info.clone()); + self.functions.insert(*id, function_info.clone()); for (id, param) in parameters.iter().zip(function.get_params()) { - self.locals.insert(*id, Rc::new(param)); + self.locals.insert(*id, param); } let current_block = self.builder.get_insert_block().unwrap(); @@ -588,23 +594,21 @@ impl<'ctx> CodeGen<'ctx> { "", ); - let global = - self.module - .add_global(candy_value_ptr, None, &format!("fun_{name}")); - global.set_initializer(&function_ptr.get_type().const_null()); - - self.builder.build_store( - global.as_pointer_value(), + let global = self.create_global( + &format!("fun_{name}"), + id, call.try_as_basic_value().unwrap_left(), ); - self.globals.insert(*id, Rc::new(global)); - let inner_block = self.context.append_basic_block(function, &name); self.builder.position_at_end(inner_block); - self.compile_mir(body, &fun_info); + self.compile_mir(body, &function_info); self.builder.position_at_end(current_block); + + if idx == mir.expressions.len() - 1 { + self.builder.build_return(Some(&global)); + } } candy_frontend::mir::Expression::Parameter => unreachable!(), candy_frontend::mir::Expression::Call { @@ -644,8 +648,8 @@ impl<'ctx> CodeGen<'ctx> { args.push(fn_env_ptr.try_as_basic_value().unwrap_left().into()); } let call = self.builder.build_call(*function_value, &args, ""); - let call_value = Rc::new(call.try_as_basic_value().unwrap_left()); - self.locals.insert(*id, call_value.clone()); + let call_value = call.try_as_basic_value().unwrap_left(); + self.locals.insert(*id, call_value); if idx == mir.expressions.len() - 1 { self.builder @@ -685,8 +689,8 @@ impl<'ctx> CodeGen<'ctx> { self.builder .build_indirect_call(candy_fn_type, inner_fn, &args, ""); - let call_value = Rc::new(call.try_as_basic_value().unwrap_left()); - self.locals.insert(*id, call_value.clone()); + let call_value = call.try_as_basic_value().unwrap_left(); + self.locals.insert(*id, call_value); if idx == mir.expressions.len() - 1 { self.builder @@ -716,7 +720,26 @@ impl<'ctx> CodeGen<'ctx> { } } - fn make_str_literal(&self, text: &str) -> BasicValueEnum<'_> { + fn create_global>( + &mut self, + name: &str, + id: &Id, + value: V, + ) -> GlobalValue<'ctx> { + let candy_value_ptr = self + .module + .get_struct_type("candy_value") + .unwrap() + .ptr_type(AddressSpace::default()); + let global = self.module.add_global(candy_value_ptr, None, name); + self.builder.build_store(global.as_pointer_value(), value); + + global.set_initializer(&candy_value_ptr.const_null()); + self.globals.insert(*id, global); + global + } + + fn make_str_literal(&self, text: &str) -> BasicValueEnum<'ctx> { let i8_type = self.context.i8_type(); let i64_type = self.context.i64_type(); @@ -749,27 +772,23 @@ impl<'ctx> CodeGen<'ctx> { self.builder .build_load(candy_value_ptr, a.as_pointer_value(), "") }); - if v.is_none() { - if let Some(i) = function_ctx.captured_ids.iter().position(|i| i == id) { - let env_ptr = function_ctx.function_value.get_last_param().unwrap(); - - let env_value = self - .builder - .build_struct_gep( - function_ctx.env_type.unwrap(), - env_ptr.into_pointer_value(), - i as u32, - "", - ) - .unwrap(); + 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(); - v.replace(self.builder.build_load(candy_value_ptr, env_value, "")); - } + let env_value = self + .builder + .build_struct_gep( + function_ctx.env_type.unwrap(), + env_ptr.into_pointer_value(), + i as u32, + "", + ) + .unwrap(); + + v.replace(self.builder.build_load(candy_value_ptr, env_value, "")); } - if v.is_none() { - if let Some(value) = self.locals.get(id) { - v.replace(*value.clone()); - } + if v.is_none() && let Some(value) = self.locals.get(id) { + v.replace(*value); } v.unwrap_or_else(|| panic!("{id} should be a real ID")) .into() From 8a43121f8286664441875f091bfb17b7001f9d17 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 21:08:30 +0200 Subject: [PATCH 22/27] add inkwell feature to cli --- .github/labels.yaml | 2 ++ compiler/cli/Cargo.toml | 6 +++++- compiler/cli/src/inkwell.rs | 5 +++++ compiler/cli/src/main.rs | 5 +++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/labels.yaml b/.github/labels.yaml index d14029294..ce6194a79 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -8,6 +8,8 @@ - compiler/frontend/**/* 'P: Compiler: Fuzzer': - compiler/fuzzer/**/* +'P: Compiler: Inkwell Backend': + - compiler/backend_inkwell/**/* 'P: Compiler: Language Server': - compiler/language_server/**/* 'P: Compiler: VM': diff --git a/compiler/cli/Cargo.toml b/compiler/cli/Cargo.toml index 8b70ed91e..bb85608c8 100644 --- a/compiler/cli/Cargo.toml +++ b/compiler/cli/Cargo.toml @@ -9,8 +9,12 @@ default-run = "candy" name = "candy" path = "src/main.rs" +[features] +default = [] +inkwell = ["candy_backend_inkwell"] + [dependencies] -candy_backend_inkwell = { path = "../backend_inkwell" } +candy_backend_inkwell = { path = "../backend_inkwell", optional = true } candy_frontend = { path = "../frontend" } candy_fuzzer = { path = "../fuzzer" } candy_language_server = { path = "../language_server" } diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 6bbee88d5..0d974f33c 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -13,6 +13,11 @@ use crate::database::Database; use crate::utils::{module_for_path, packages_path}; use crate::{Exit, ProgramResult}; +/// Compile a Candy program to a native binary. +/// +/// This command compiles the given file, or, if no file is provided, the package of +/// your current working directory. The module should export a `main` function. +/// This function is then called with an environment. #[derive(Parser, Debug)] pub(crate) struct Options { /// If enabled, print the generated LLVM IR to stderr. diff --git a/compiler/cli/src/main.rs b/compiler/cli/src/main.rs index a89a845c1..e6c1cff82 100644 --- a/compiler/cli/src/main.rs +++ b/compiler/cli/src/main.rs @@ -12,6 +12,7 @@ mod check; mod database; mod debug; mod fuzz; +#[cfg(feature = "inkwell")] mod inkwell; mod lsp; mod run; @@ -33,6 +34,7 @@ enum CandyOptions { /// Start a Language Server. Lsp, + #[cfg(feature = "inkwell")] Inkwell(inkwell::Options), } @@ -49,6 +51,7 @@ async fn main() -> ProgramResult { CandyOptions::Fuzz(options) => fuzz::fuzz(options), CandyOptions::Debug(options) => debug::debug(options), CandyOptions::Lsp => lsp::lsp().await, + #[cfg(feature = "inkwell")] CandyOptions::Inkwell(options) => inkwell::compile(options), } } @@ -58,11 +61,13 @@ type ProgramResult = Result<(), Exit>; enum Exit { CodePanicked, DirectoryNotFound, + #[cfg(feature = "inkwell")] ExternalError, FileNotFound, FuzzingFoundFailingCases, NotInCandyPackage, CodeContainsErrors, + #[cfg(feature = "inkwell")] LlvmError(String), GoldOutdated, } From aa628c6e016c557e9ea22071afab40a1dcd572ec Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Thu, 10 Aug 2023 21:46:36 +0200 Subject: [PATCH 23/27] rename candy runtime --- .../{candy_rt => candy_runtime}/Makefile | 2 +- .../candy_builtin.c | 2 +- .../candy_builtin.h | 2 +- .../candy_runtime.c} | 4 ++-- .../candy_runtime.h} | 2 +- compiler/backend_inkwell/src/lib.rs | 20 +++++++++++-------- compiler/cli/src/inkwell.rs | 15 +++++++++++--- 7 files changed, 30 insertions(+), 17 deletions(-) rename compiler/backend_inkwell/{candy_rt => candy_runtime}/Makefile (60%) rename compiler/backend_inkwell/{candy_rt => candy_runtime}/candy_builtin.c (99%) rename compiler/backend_inkwell/{candy_rt => candy_runtime}/candy_builtin.h (97%) rename compiler/backend_inkwell/{candy_rt/candy_rt.c => candy_runtime/candy_runtime.c} (98%) rename compiler/backend_inkwell/{candy_rt/candy_rt.h => candy_runtime/candy_runtime.h} (96%) diff --git a/compiler/backend_inkwell/candy_rt/Makefile b/compiler/backend_inkwell/candy_runtime/Makefile similarity index 60% rename from compiler/backend_inkwell/candy_rt/Makefile rename to compiler/backend_inkwell/candy_runtime/Makefile index 147841a18..7d468883b 100644 --- a/compiler/backend_inkwell/candy_rt/Makefile +++ b/compiler/backend_inkwell/candy_runtime/Makefile @@ -1,6 +1,6 @@ CC=clang -candy_rt.a: candy_rt.o candy_builtin.o +candy_runtime.a: candy_runtime.o candy_builtin.o ar rcs $@ $^ %.o: %.c %.h diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.c b/compiler/backend_inkwell/candy_runtime/candy_builtin.c similarity index 99% rename from compiler/backend_inkwell/candy_rt/candy_builtin.c rename to compiler/backend_inkwell/candy_runtime/candy_builtin.c index f5819abd4..7bb5a995c 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.c +++ b/compiler/backend_inkwell/candy_runtime/candy_builtin.c @@ -1,7 +1,7 @@ #include #include #include -#include "candy_rt.h" +#include "candy_runtime.h" const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right) { diff --git a/compiler/backend_inkwell/candy_rt/candy_builtin.h b/compiler/backend_inkwell/candy_runtime/candy_builtin.h similarity index 97% rename from compiler/backend_inkwell/candy_rt/candy_builtin.h rename to compiler/backend_inkwell/candy_runtime/candy_builtin.h index 0492e24ca..0b6d1a3e0 100644 --- a/compiler/backend_inkwell/candy_rt/candy_builtin.h +++ b/compiler/backend_inkwell/candy_runtime/candy_builtin.h @@ -1,4 +1,4 @@ -#include "candy_rt.h" +#include "candy_runtime.h" const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right); const candy_value_t *candy_builtin_if_else(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise); diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.c b/compiler/backend_inkwell/candy_runtime/candy_runtime.c similarity index 98% rename from compiler/backend_inkwell/candy_rt/candy_rt.c rename to compiler/backend_inkwell/candy_runtime/candy_runtime.c index 2b5ce924f..28f42158c 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.c +++ b/compiler/backend_inkwell/candy_runtime/candy_runtime.c @@ -2,7 +2,7 @@ #include #include #include -#include "candy_rt.h" +#include "candy_runtime.h" #include "candy_builtin.h" const candy_value_t __internal_true = { @@ -168,7 +168,7 @@ candy_value_t *make_candy_struct(candy_value_t **keys, candy_value_t **values) return candy_value; } -candy_value_t *call_candy_function_with(candy_value_t *function, candy_value_t *arg) +candy_value_t *run_candy_main(candy_value_t *function, candy_value_t *arg) { return function->value.function.function(arg); } diff --git a/compiler/backend_inkwell/candy_rt/candy_rt.h b/compiler/backend_inkwell/candy_runtime/candy_runtime.h similarity index 96% rename from compiler/backend_inkwell/candy_rt/candy_rt.h rename to compiler/backend_inkwell/candy_runtime/candy_runtime.h index 9ffe769ad..71c4afc68 100644 --- a/compiler/backend_inkwell/candy_rt/candy_rt.h +++ b/compiler/backend_inkwell/candy_runtime/candy_runtime.h @@ -65,7 +65,7 @@ candy_value_t *make_candy_text(char *text); candy_value_t *make_candy_tag(char *tag); candy_value_t *make_candy_list(candy_value_t **values); candy_value_t *make_candy_function(candy_function function, void *environment, int env_size); -candy_value_t *call_candy_function_with(candy_value_t *function, candy_value_t *arg); +candy_value_t *run_candy_main(candy_value_t *function, candy_value_t *arg); candy_function get_candy_function_pointer(candy_value_t *function); void *get_candy_function_environment(candy_value_t *function); void candy_panic(const candy_value_t *reason); diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 098ed0d7c..7f1c99fcd 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -145,11 +145,11 @@ impl<'ctx> CodeGen<'ctx> { let main_fn = self.module.add_function("main", main_type, None); let block = self.context.append_basic_block(main_fn, "entry"); - let call_candy_function_type = + let run_candy_main_type = candy_value_ptr.fn_type(&[candy_value_ptr.into(), candy_value_ptr.into()], false); - let call_candy_function_with = - self.module - .add_function("call_candy_function_with", call_candy_function_type, None); + let run_candy_main = self + .module + .add_function("run_candy_main", run_candy_main_type, None); let main_info = FunctionInfo { function_value: main_fn, @@ -187,7 +187,7 @@ impl<'ctx> CodeGen<'ctx> { .unwrap_left(); let main_res_ptr = self.builder.build_call( - call_candy_function_with, + run_candy_main, &[main_fn.into(), environment.as_basic_value_enum().into()], "", ); @@ -233,12 +233,16 @@ impl<'ctx> CodeGen<'ctx> { .wait()?; if build_rt { std::process::Command::new("make") - .args(["-C", "compiler/backend_inkwell/candy_rt/", "clean"]) + .args(["-C", "compiler/backend_inkwell/candy_runtime/", "clean"]) .spawn()? .wait()?; std::process::Command::new("make") - .args(["-C", "compiler/backend_inkwell/candy_rt/", "candy_rt.a"]) + .args([ + "-C", + "compiler/backend_inkwell/candy_runtime/", + "candy_runtime.a", + ]) .spawn()? .wait()?; } @@ -246,7 +250,7 @@ impl<'ctx> CodeGen<'ctx> { std::process::Command::new("clang") .args([ s_path.to_str().unwrap(), - "compiler/backend_inkwell/candy_rt/candy_rt.a", + "compiler/backend_inkwell/candy_runtime/candy_runtime.a", if debug { "-g" } else { "" }, "-O3", "-flto", diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 0d974f33c..3c3ab6901 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -1,3 +1,4 @@ +use std::ffi::OsStr; use std::path::PathBuf; use std::sync::Arc; @@ -5,7 +6,7 @@ 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, TracingConfig}; +use candy_frontend::{hir, module, TracingConfig}; use clap::{Parser, ValueHint}; use rustc_hash::FxHashSet; @@ -45,11 +46,19 @@ pub(crate) struct Options { pub(crate) fn compile(options: Options) -> ProgramResult { let packages_path = packages_path(); let db = Database::new_with_file_system_module_provider(packages_path); + let module = module_for_path(options.path.clone())?; let path = options .path .as_ref() - .map_or_else(|| "Unknown".into(), |p| p.to_string_lossy().to_string()); - let module = module_for_path(options.path)?; + .unwrap_or_else(|| match &module.package { + module::Package::User(user) => user, + module::Package::Managed(managed) => managed, + _ => unreachable!(), + }) + .file_name() + .unwrap_or(OsStr::new("Executable")) + .to_string_lossy() + .to_string(); let (mir, errors) = db .optimized_mir(module.clone(), TracingConfig::off()) From 008ab8eb2a01692ced4d06400c0390a471f5762f Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Sat, 12 Aug 2023 11:30:35 +0200 Subject: [PATCH 24/27] address requested changes --- .../candy_runtime/candy_builtin.c | 9 +++++++-- compiler/backend_inkwell/src/lib.rs | 17 +++++++---------- compiler/cli/src/inkwell.rs | 10 ++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/compiler/backend_inkwell/candy_runtime/candy_builtin.c b/compiler/backend_inkwell/candy_runtime/candy_builtin.c index 7bb5a995c..2a3b92154 100644 --- a/compiler/backend_inkwell/candy_runtime/candy_builtin.c +++ b/compiler/backend_inkwell/candy_runtime/candy_builtin.c @@ -42,14 +42,19 @@ candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *ri candy_value_t *candy_builtin_int_bit_length(candy_value_t *value) { - long long int_value = value->value.integer; + int64_t int_value = value->value.integer; + int is_negative = int_value < 0; + if (is_negative) + { + int_value = -int_value; + } int shifts = 0; while (int_value) { int_value = int_value >> shifts; shifts++; } - return make_candy_int(shifts); + return make_candy_int(shifts + is_negative); } candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right) diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 7f1c99fcd..67a3a4153 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -15,7 +15,7 @@ pub use inkwell; use itertools::Itertools; use std::{ collections::{HashMap, HashSet}, - path::{Path, PathBuf}, + path::PathBuf, sync::Arc, }; @@ -59,7 +59,7 @@ impl<'ctx> CodeGen<'ctx> { pub fn compile( &mut self, - path: &Path, + path: &str, print_llvm_ir: bool, print_main_output: bool, ) -> Result<(), LLVMString> { @@ -215,7 +215,8 @@ impl<'ctx> CodeGen<'ctx> { self.module.print_to_stderr(); } self.module.verify()?; - self.module.write_bitcode_to_path(path); + let bc_path = PathBuf::from(format!("{path}.bc")); + self.module.write_bitcode_to_path(&bc_path); Ok(()) } @@ -316,9 +317,6 @@ impl<'ctx> CodeGen<'ctx> { let global = self.create_global(symbol, id, call.try_as_basic_value().unwrap_left()); - global.set_initializer(&candy_value_ptr.const_null()); - self.globals.insert(*id, global); - if idx == mir.expressions.len() - 1 { self.builder .build_return(Some(&global.as_basic_value_enum())); @@ -628,7 +626,6 @@ impl<'ctx> CodeGen<'ctx> { if let Some(FunctionInfo { function_value, - captured_ids: _, env_type, }) = self.functions.get(function) @@ -724,11 +721,11 @@ impl<'ctx> CodeGen<'ctx> { } } - fn create_global>( + fn create_global( &mut self, name: &str, id: &Id, - value: V, + value: impl BasicValue<'ctx>, ) -> GlobalValue<'ctx> { let candy_value_ptr = self .module @@ -739,7 +736,7 @@ impl<'ctx> CodeGen<'ctx> { self.builder.build_store(global.as_pointer_value(), value); global.set_initializer(&candy_value_ptr.const_null()); - self.globals.insert(*id, global); + assert!(self.globals.insert(*id, global).is_none()); global } diff --git a/compiler/cli/src/inkwell.rs b/compiler/cli/src/inkwell.rs index 3c3ab6901..699337b3c 100644 --- a/compiler/cli/src/inkwell.rs +++ b/compiler/cli/src/inkwell.rs @@ -30,8 +30,8 @@ pub(crate) struct Options { print_main_output: bool, /// If enabled, build the Candy runtime from scratch. - #[arg(long = "build-rt", default_value_t = false)] - build_rt: bool, + #[arg(long = "build-runtime", default_value_t = false)] + build_runtime: bool, /// If enabled, compile the LLVM bitcode with debug information. #[arg(short = 'g', default_value_t = false)] @@ -84,13 +84,11 @@ pub(crate) fn compile(options: Options) -> ProgramResult { let context = candy_backend_inkwell::inkwell::context::Context::create(); let mut codegen = CodeGen::new(&context, &path, mir); - let mut bc_path = PathBuf::new(); - bc_path.push(&format!("{path}.bc")); codegen - .compile(&bc_path, options.print_llvm_ir, options.print_main_output) + .compile(&path, options.print_llvm_ir, options.print_main_output) .map_err(|e| Exit::LlvmError(e.to_string()))?; codegen - .compile_asm_and_link(&path, options.build_rt, options.debug) + .compile_asm_and_link(&path, options.build_runtime, options.debug) .map_err(|_| Exit::ExternalError)?; ProgramResult::Ok(()) From c97ccc4a5f45b6b26ec9bea3360c916321c6c500 Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Sat, 12 Aug 2023 11:34:18 +0200 Subject: [PATCH 25/27] download llvm in ci --- .github/workflows/compiler.yaml | 3 +++ compiler.code-workspace | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compiler.yaml b/.github/workflows/compiler.yaml index feeb14f4e..539265ef8 100644 --- a/.github/workflows/compiler.yaml +++ b/.github/workflows/compiler.yaml @@ -13,6 +13,9 @@ jobs: - uses: actions/checkout@v3.5.3 - uses: dsherret/rust-toolchain-file@v1 - uses: Swatinem/rust-cache@v2.6.0 + - uses: KyleMayes/install-llvm-action@v1 + with: + version: "15.0" # Compiler - name: 'Compiler: clippy' diff --git a/compiler.code-workspace b/compiler.code-workspace index 69f427c66..273e1b2a1 100644 --- a/compiler.code-workspace +++ b/compiler.code-workspace @@ -12,6 +12,10 @@ "name": "Compiler: VM", "path": "compiler/vm" }, + { + "name": "Compiler: Inkwell Backend", + "path": "compiler/backend_inkwell" + }, { "name": "Compiler: Fuzzer", "path": "compiler/fuzzer" @@ -54,10 +58,16 @@ "explorer.fileNesting.patterns": { "*.candy": "${capture}.candy.*" }, - "git.branchProtection": ["main"], + "git.branchProtection": [ + "main" + ], "rust-analyzer.check.command": "clippy", "rust-analyzer.imports.group.enable": false, "task.allowAutomaticTasks": "on", - "todo-tree.general.tags": ["FIXME", "PERF", "TODO"] + "todo-tree.general.tags": [ + "FIXME", + "PERF", + "TODO" + ] } -} +} \ No newline at end of file From 98b9da4807cd4508eff970b6dc46af0dee99b89c Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Sat, 12 Aug 2023 11:58:18 +0200 Subject: [PATCH 26/27] undo workspace formatting --- compiler.code-workspace | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/compiler.code-workspace b/compiler.code-workspace index 273e1b2a1..d22658c56 100644 --- a/compiler.code-workspace +++ b/compiler.code-workspace @@ -58,16 +58,10 @@ "explorer.fileNesting.patterns": { "*.candy": "${capture}.candy.*" }, - "git.branchProtection": [ - "main" - ], + "git.branchProtection": ["main"], "rust-analyzer.check.command": "clippy", "rust-analyzer.imports.group.enable": false, "task.allowAutomaticTasks": "on", - "todo-tree.general.tags": [ - "FIXME", - "PERF", - "TODO" - ] + "todo-tree.general.tags": ["FIXME", "PERF", "TODO"] } -} \ No newline at end of file +} From b8467e67baedd4410a77d06801d8378bd5ff123e Mon Sep 17 00:00:00 2001 From: Clemens Tiedt Date: Sat, 12 Aug 2023 12:01:46 +0200 Subject: [PATCH 27/27] specify exact llvm action version in ci --- .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 1a7dd8080..6c8e5a3aa 100644 --- a/.github/workflows/compiler.yaml +++ b/.github/workflows/compiler.yaml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v3.5.3 - uses: dsherret/rust-toolchain-file@v1 - uses: Swatinem/rust-cache@v2.6.0 - - uses: KyleMayes/install-llvm-action@v1 + - uses: KyleMayes/install-llvm-action@v1.8.3 with: version: "15.0"