Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Precompile builtin/*.sk #280

Merged
merged 23 commits into from Apr 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ubuntu.yml
Expand Up @@ -27,4 +27,5 @@ jobs:
- name: Build and test
run: |
set -eux
env -- LLC=llc-9 CLANG=clang-9 cargo run -- build_corelib
env -- LLC=llc-9 CLANG=clang-9 cargo test
122 changes: 83 additions & 39 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions Cargo.toml
Expand Up @@ -6,10 +6,11 @@ authors = [ "Yutaka HARA <yutaka.hara.gmail.com>" ]

[dependencies]
backtrace = "0.3"
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm9-0" }
llvm-sys = "90.2.0"
inkwell = { git = "https://github.com/TheDan64/inkwell", features = ["llvm9-0"] }
failure = "0.1.6"
clap = { git = "https://github.com/clap-rs/clap", version = "3.0.0-beta.1", features = ["yaml"]}
clap = { version = "3.0.0-beta.2", features = ["yaml"] }
either = "1.5.3"
env_logger = "0.8.2"
log = "0.4.11"
serde = { version = "1.0.125", features = ["derive"] }
serde_json = "1.0"
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -122,8 +122,11 @@ export CPPFLAGS="-I$(brew --prefix)/opt/llvm@9/include"

```
$ cargo build
$ cargo run -- build_corelib
```

The `build_corelib` subcommand compiles core classes (builtin/*.sk) into ./builtin/builtin.bc and ./builtin/exports.json.

### Run tests

```
Expand Down
3 changes: 3 additions & 0 deletions src/cli.yml
Expand Up @@ -20,3 +20,6 @@ subcommands:
help: "Shiika source (*.sk)"
required: true
index: 1

- build_corelib:
about: "Build corelib"
292 changes: 159 additions & 133 deletions src/code_gen/gen_exprs.rs

Large diffs are not rendered by default.

196 changes: 146 additions & 50 deletions src/code_gen/mod.rs
Expand Up @@ -5,9 +5,9 @@ mod lambda;
mod utils;
use crate::code_gen::code_gen_context::*;
use crate::code_gen::utils::llvm_vtable_name;
use crate::error;
use crate::error::Error;
use crate::hir::*;
use crate::library::LibraryExports;
use crate::mir;
use crate::mir::*;
use crate::names::*;
Expand All @@ -17,6 +17,7 @@ use inkwell::types::*;
use inkwell::values::*;
use inkwell::AddressSpace;
use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc;

/// CodeGen
Expand All @@ -27,6 +28,7 @@ use std::rc::Rc;
/// 'ictx: inkwell context
/// 'run: code_gen::run()
pub struct CodeGen<'hir: 'ictx, 'run, 'ictx: 'run> {
pub generate_main: bool,
pub context: &'ictx inkwell::context::Context,
pub module: &'run inkwell::module::Module<'ictx>,
pub builder: &'run inkwell::builder::Builder<'ictx>,
Expand All @@ -40,21 +42,30 @@ pub struct CodeGen<'hir: 'ictx, 'run, 'ictx: 'run> {
pub llvm_struct_types: HashMap<ClassFullname, inkwell::types::StructType<'ictx>>,
str_literals: &'hir Vec<String>,
vtables: &'hir mir::VTables,
imported_vtables: &'hir mir::VTables,
/// Toplevel `self`
the_main: Option<inkwell::values::BasicValueEnum<'ictx>>,
}

/// Compile hir and dump it to `outpath`
pub fn run(mir: &Mir, outpath: &str) -> Result<(), Error> {
pub fn run(
mir: &Mir,
bc_path: &str,
opt_ll_path: Option<&str>,
generate_main: bool,
) -> Result<(), Error> {
let context = inkwell::context::Context::create();
let module = context.create_module("main");
let builder = context.create_builder();
let mut code_gen = CodeGen::new(&mir, &context, &module, &builder);
code_gen.gen_program(&mir.hir)?;
code_gen
.module
.print_to_file(outpath)
.map_err(|llvm_str| error::plain_runner_error(llvm_str.to_string()))?;
let mut code_gen = CodeGen::new(&mir, &context, &module, &builder, &generate_main);
code_gen.gen_program(&mir.hir, &mir.imports)?;
code_gen.module.write_bitcode_to_path(Path::new(bc_path));
if let Some(ll_path) = opt_ll_path {
code_gen
.module
.print_to_file(ll_path)
.map_err(|llvm_str| crate::error::plain_runner_error(llvm_str.to_string()))?;
}
Ok(())
}

Expand All @@ -64,8 +75,10 @@ impl<'hir: 'ictx, 'run, 'ictx: 'run> CodeGen<'hir, 'run, 'ictx> {
context: &'ictx inkwell::context::Context,
module: &'run inkwell::module::Module<'ictx>,
builder: &'run inkwell::builder::Builder<'ictx>,
generate_main: &bool,
) -> CodeGen<'hir, 'run, 'ictx> {
CodeGen {
generate_main: *generate_main,
context,
module,
builder,
Expand All @@ -79,24 +92,32 @@ impl<'hir: 'ictx, 'run, 'ictx: 'run> CodeGen<'hir, 'run, 'ictx> {
llvm_struct_types: HashMap::new(),
str_literals: &mir.hir.str_literals,
vtables: &mir.vtables,
imported_vtables: &mir.imports.vtables,
the_main: None,
}
}

pub fn gen_program(&mut self, hir: &'hir Hir) -> Result<(), Error> {
pub fn gen_program(&mut self, hir: &'hir Hir, imports: &LibraryExports) -> Result<(), Error> {
self.gen_declares();
self.gen_imports(imports);
self.gen_class_structs(&hir.sk_classes);
self.gen_string_literals(&hir.str_literals);
self.gen_constant_ptrs(&hir.constants);
self.gen_boxing_funcs();
self.gen_method_funcs(&hir.sk_methods);
self.gen_vtables();
self.impl_boxing_funcs();
self.gen_methods(&hir.sk_methods)?;
self.gen_const_inits(&hir.const_inits)?;
self.gen_user_main(&hir.main_exprs, &hir.main_lvars)?;
if self.generate_main {
self.gen_init_constants(&hir.const_inits, &imports.constants);
self.gen_user_main(&hir.main_exprs, &hir.main_lvars)?;
self.gen_main()?;
} else {
// generating builtin
self.impl_boxing_funcs();
self.gen_init_void();
}
self.gen_lambda_funcs(&hir)?;
self.gen_main()?;
Ok(())
}

Expand Down Expand Up @@ -162,6 +183,54 @@ impl<'hir: 'ictx, 'run, 'ictx: 'run> CodeGen<'hir, 'run, 'ictx> {
global.set_constant(true);
}

/// Generate information to use imported items
fn gen_imports(&mut self, imports: &LibraryExports) {
self.gen_import_classes(&imports.sk_classes);
self.gen_import_vtables(&imports.vtables);
self.gen_import_constants(&imports.constants);
}

fn gen_import_classes(&mut self, imported_classes: &SkClasses) {
// LLVM type
for name in imported_classes.keys() {
self.llvm_struct_types
.insert(name.clone(), self.context.opaque_struct_type(&name.0));
}
self.define_class_struct_fields(imported_classes);

// Methods
for (classname, class) in imported_classes {
for (firstname, sig) in &class.method_sigs {
let func_type = self.method_llvm_func_type(&class.instance_ty, sig);
let func_name = classname.method_fullname(&firstname);
self.module
.add_function(&func_name.full_name, func_type, None);
}
}
}

/// Declare `external global` for vtable of each class
fn gen_import_vtables(&self, vtables: &VTables) {
for (fullname, vtable) in vtables.iter() {
let name = llvm_vtable_name(fullname);
let ary_type = self.i8ptr_type.array_type(vtable.size() as u32);
let _global = self.module.add_global(ary_type, None, &name);
}
}

/// Declare `external global` for each imported constant
fn gen_import_constants(&self, imported_constants: &HashMap<ConstFullname, TermTy>) {
for (fullname, ty) in imported_constants {
let name = &fullname.0;
let global = self.module.add_global(self.llvm_type(&ty), None, name);
global.set_linkage(inkwell::module::Linkage::External);
// @init_::XX
let fn_type = self.void_type.fn_type(&[], false);
self.module
.add_function(&format!("init_{}", fullname.0), fn_type, None);
}
}

// Generate vtable constants
fn gen_vtables(&self) {
for (class_fullname, vtable) in self.vtables.iter() {
Expand All @@ -171,7 +240,6 @@ impl<'hir: 'ictx, 'run, 'ictx: 'run> CodeGen<'hir, 'run, 'ictx> {
.module
.add_global(ary_type, None, &llvm_vtable_name(class_fullname));
global.set_constant(true);
global.set_linkage(inkwell::module::Linkage::Internal);
let func_ptrs = method_names
.iter()
.map(|name| {
Expand All @@ -188,6 +256,57 @@ impl<'hir: 'ictx, 'run, 'ictx: 'run> CodeGen<'hir, 'run, 'ictx> {
}
}

/// Generate `init_constants()`
// TODO: imported_constants should be Vec (order matters)
fn gen_init_constants(
&self,
const_inits: &'hir [HirExpression],
imported_constants: &HashMap<ConstFullname, TermTy>,
) {
// define void @init_constants()
let fn_type = self.void_type.fn_type(&[], false);
let function = self.module.add_function("init_constants", fn_type, None);
let basic_block = self.context.append_basic_block(function, "");
self.builder.position_at_end(basic_block);

// call void @"init_::XX"()
for fullname in imported_constants.keys() {
let func = self.get_llvm_func(&format!("init_{}", fullname.0));
self.builder.build_call(func, &[], "");
}
for expr in const_inits {
match &expr.node {
HirExpressionBase::HirConstAssign { fullname, .. } => {
let func = self.get_llvm_func(&format!("init_{}", fullname.0));
self.builder.build_call(func, &[], "");
}
_ => panic!("gen_init_constants: Not a HirConstAssign"),
}
}

self.builder.build_return(None);
}

fn gen_init_void(&mut self) {
// define void @"init_::XX"
let fullname = const_fullname("::Void");
let fn_type = self.void_type.fn_type(&[], false);
let function = self
.module
.add_function(&format!("init_{}", fullname.0), fn_type, None);
let basic_block = self.context.append_basic_block(function, "");
self.builder.position_at_end(basic_block);
// Create ::Void
let ptr = self
.module
.get_global(&"::Void")
.unwrap()
.as_pointer_value();
let value = self.allocate_sk_obj(&class_fullname("Void"), "void_obj");
self.builder.build_store(ptr, value);
self.builder.build_return(None);
}

#[allow(clippy::ptr_arg)]
fn gen_user_main(
&mut self,
Expand Down Expand Up @@ -245,13 +364,17 @@ impl<'hir: 'ictx, 'run, 'ictx: 'run> CodeGen<'hir, 'run, 'ictx> {

/// Create llvm struct types for Shiika objects
fn gen_class_structs(&mut self, classes: &HashMap<ClassFullname, SkClass>) {
// Create all the struct types in advance
// Create all the struct types in advance (because it may be used as other class's ivar)
for name in classes.keys() {
self.llvm_struct_types
.insert(name.clone(), self.context.opaque_struct_type(&name.0));
}

// Set fields for ivars
self.define_class_struct_fields(classes);
}

/// Set fields for ivars
fn define_class_struct_fields(&self, classes: &HashMap<ClassFullname, SkClass>) {
let vt = self.llvm_vtable_ref_type().into();
for (name, class) in classes {
let struct_type = self.llvm_struct_types.get(name).unwrap();
Expand Down Expand Up @@ -315,7 +438,6 @@ impl<'hir: 'ictx, 'run, 'ictx: 'run> CodeGen<'hir, 'run, 'ictx> {
for (fullname, ty) in constants {
let name = &fullname.0;
let global = self.module.add_global(self.llvm_type(&ty), None, name);
global.set_linkage(inkwell::module::Linkage::Internal);
let null = self.i32_type.ptr_type(AddressSpace::Generic).const_null();
match self.llvm_zero_value(ty) {
Some(zero) => global.set_initializer(&zero),
Expand Down Expand Up @@ -346,33 +468,6 @@ impl<'hir: 'ictx, 'run, 'ictx: 'run> CodeGen<'hir, 'run, 'ictx> {
}
}

// define void @init_constants()
let fn_type = self.void_type.fn_type(&[], false);
let function = self.module.add_function("init_constants", fn_type, None);
let basic_block = self.context.append_basic_block(function, "");
self.builder.position_at_end(basic_block);

// call void @"init_::XX"()
for expr in const_inits {
match &expr.node {
HirExpressionBase::HirConstAssign { fullname, .. } => {
let func = self.get_llvm_func(&format!("init_{}", fullname.0));
self.builder.build_call(func, &[], "");
}
_ => panic!("gen_const_inits: Not a HirConstAssign"),
}
}

// Generate ::Void
let ptr = self
.module
.get_global(&"::Void")
.unwrap()
.as_pointer_value();
let value = self.allocate_sk_obj(&class_fullname("Void"), "void_obj");
self.builder.build_store(ptr, value);

self.builder.build_return(None);
Ok(())
}

Expand Down Expand Up @@ -541,23 +636,22 @@ impl<'hir: 'ictx, 'run, 'ictx: 'run> CodeGen<'hir, 'run, 'ictx> {
) -> Result<(), Error> {
let (end_block, mut ctx) = self.new_ctx(function_origin, function, function_params, lvars);
let last_value = self.gen_exprs(&mut ctx, exprs)?;
let ret_block = if exprs.ty.is_never_type() {
self.builder.build_unreachable();
None
} else {
let last_value_block = if last_value.is_some() {
let b = self.context.append_basic_block(ctx.function, "Ret");
self.builder.build_unconditional_branch(b);
self.builder.position_at_end(b);
self.builder.build_unconditional_branch(*end_block);
Some(b)
} else {
None
};

self.builder.position_at_end(*end_block);

if ret_ty.is_never_type() {
// `Never` does not have an instance
self.builder.build_return(None);
} else if exprs.ty.is_never_type() && ctx.returns.is_empty() {
} else if last_value.is_none() && ctx.returns.is_empty() {
// `exprs` ends with `panic` and there is no `return`
let null = self.llvm_type(&ret_ty).into_pointer_type().const_null();
self.builder.build_return(Some(&null));
Expand All @@ -570,8 +664,10 @@ impl<'hir: 'ictx, 'run, 'ictx: 'run> CodeGen<'hir, 'run, 'ictx> {
.iter()
.map(|(v, b)| (v as &dyn inkwell::values::BasicValue, *b))
.collect::<Vec<_>>();
if let Some(b) = ret_block {
incomings.push((&last_value, b));
let v;
if let Some(b) = last_value_block {
v = last_value.unwrap();
incomings.push((&v, b));
}
let phi_node = self
.builder
Expand Down