Skip to content

Commit

Permalink
Merge pull request #9 from baszalmstra/function_calls
Browse files Browse the repository at this point in the history
  • Loading branch information
Wodann authored Oct 19, 2019
2 parents 739ae2e + a7a571a commit d42805c
Show file tree
Hide file tree
Showing 38 changed files with 1,376 additions and 365 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ matrix:
fast_finish: true

cache: cargo
before_cache:
- find ./target/debug -maxdepth 1 -type f -delete
- rm -fr ./target/debug/{deps,.fingerprint}/{*mun*,*mun_*,*test_utils*, *tools*}
- rm -f ./target/.rustc_info.json

addons:
apt:
Expand Down
5 changes: 0 additions & 5 deletions crates/mun/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,3 @@ clap = "2.33.0"
mun_compiler = { path = "../mun_compiler" }
mun_compiler_daemon = { path = "../mun_compiler_daemon" }
mun_runtime = { path = "../mun_runtime" }

[build-dependencies]
lazy_static = "1.4.0"
semver = "0.9.0"
regex = "1.3.1"
Binary file modified crates/mun/main.dll
Binary file not shown.
6 changes: 4 additions & 2 deletions crates/mun/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extern crate failure;
use std::time::Duration;

use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use mun_compiler::PathOrInline;
use mun_runtime::{invoke_fn, MunRuntime, RuntimeBuilder};

fn main() -> Result<(), failure::Error> {
Expand Down Expand Up @@ -77,7 +78,7 @@ fn build(matches: &ArgMatches) -> Result<(), failure::Error> {
if matches.is_present("watch") {
mun_compiler_daemon::main(&options)
} else {
mun_compiler::main(&options)
mun_compiler::main(&options).map(|_| {})
}
}

Expand All @@ -101,9 +102,10 @@ fn compiler_options(matches: &ArgMatches) -> Result<mun_compiler::CompilerOption
};

Ok(mun_compiler::CompilerOptions {
input: matches.value_of("INPUT").unwrap().into(), // Safe because its a required arg
input: PathOrInline::Path(matches.value_of("INPUT").unwrap().into()), // Safe because its a required arg
target: matches.value_of("target").map(|t| t.to_string()),
optimization_lvl,
out_dir: None,
})
}

Expand Down
20 changes: 13 additions & 7 deletions crates/mun/test/main.mun
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
// function to add two floats
fn add(a:float, b:float):float {
let c = a+b+5.0
c
}

// function to subtract two floats
fn subtract(a:float, b:float):float {
a-b
Expand All @@ -15,5 +9,17 @@ fn multiply(a:float, b:float):float {
}

fn main():int {
5
add(5, 3)
}

fn add_impl(a:int, b:int):int {
a+b
}

fn add(a:int, b:int):int {
add_impl(a,b)
}

fn test():int {
add(4,5)
}
2 changes: 1 addition & 1 deletion crates/mun_abi/src/autogen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use crate::prelude::*;
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

use std::ffi::{c_void, CStr};
use std::os::raw::c_char;
use std::slice;

impl TypeInfo {
Expand Down Expand Up @@ -153,6 +152,7 @@ impl AssemblyInfo {
mod tests {
use super::*;
use std::ffi::CString;
use std::os::raw::c_char;
use std::ptr;

fn fake_type_info(name: &CStr) -> TypeInfo {
Expand Down
7 changes: 5 additions & 2 deletions crates/mun_codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ md5="0.6.1"
array-init="0.1.0"
tempfile = "3"
lazy_static = "1.4.0"
test_utils = { path="../test_utils"}

[dev-dependencies]
test_utils = { path="../test_utils"}
[build-dependencies]
lazy_static = "1.4.0"
semver = "0.9.0"
regex = "1.3.1"
File renamed without changes.
122 changes: 96 additions & 26 deletions crates/mun_codegen/src/code_gen.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,80 @@
use crate::IrDatabase;
use failure::Fail;
use inkwell::module::Module;
use inkwell::passes::{PassManager, PassManagerBuilder};
use inkwell::targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target};
use inkwell::OptimizationLevel;
use mun_hir::FileId;
use std::io;
use std::io::Write;
use std::path::Path;
use std::process::{Child, Stdio};

mod abi_types;
mod linker;
pub fn write_module_shared_object(db: &impl IrDatabase, file_id: FileId) -> bool {
let module = db.module_ir(file_id);

#[derive(Debug, Fail)]
enum CodeGenerationError {
#[fail(display = "linker error: {}", 0)]
LinkerError(String),
#[fail(display = "linker error: {}", 0)]
SpawningLinkerError(io::Error),
#[fail(display = "unknown target triple: {}", 0)]
UnknownTargetTriple(String),
#[fail(display = "error creating target machine")]
CouldNotCreateTargetMachine,
#[fail(display = "error creating object file")]
CouldNotCreateObjectFile(io::Error),
#[fail(display = "error generating machine code")]
CodeGenerationError(String),
}

/// Construct a shared object for the given `hir::FileId` at the specified output file location.
pub fn write_module_shared_object(
db: &impl IrDatabase,
file_id: FileId,
output_file_path: &Path,
) -> Result<(), failure::Error> {
let target = db.target();

// Clone the module so we can modify it safely
let llvm_module = module.llvm_module.clone();
// Construct a module for the assembly
let assembly_module = db.context().create_module(
output_file_path
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("unknown"),
);

// Generate the `get_symbols` method.
symbols::gen_symbols(db, &module.functions, &llvm_module);
// Generate IR for the module and clone it so that we can modify it without modifying the
// cached value.
let module = db.module_ir(file_id);
assembly_module
.link_in_module(module.llvm_module.clone())
.map_err(|e| CodeGenerationError::LinkerError(e.to_string()))?;

// Generate the `get_info` method.
symbols::gen_reflection_ir(
db,
&module.functions,
&module.dispatch_table,
&assembly_module,
);

// Initialize the x86 target
Target::initialize_x86(&InitializationConfig::default());

let llvm_target = Target::from_triple(&target.llvm_target).unwrap();
// Construct the LLVM target from the specified target.
let llvm_target = Target::from_triple(&target.llvm_target)
.map_err(|e| CodeGenerationError::UnknownTargetTriple(e.to_string()))?;
assembly_module.set_target(&llvm_target);

// Optimize the assembly module
optimize_module(&assembly_module, db.optimization_lvl());

// Debug print the IR
//println!("{}", assembly_module.print_to_string().to_string());

// Construct target machine for machine code generation
let target_machine = llvm_target
.create_target_machine(
&target.llvm_target,
Expand All @@ -27,40 +84,53 @@ pub fn write_module_shared_object(db: &impl IrDatabase, file_id: FileId) -> bool
RelocMode::PIC,
CodeModel::Default,
)
.unwrap();

let relative_path = db.file_relative_path(file_id);
let original_filename = Path::new(relative_path.file_name().unwrap());
.ok_or(CodeGenerationError::CouldNotCreateTargetMachine)?;

// Generate object file
let obj_file = {
let obj = target_machine
.write_to_memory_buffer(&llvm_module, FileType::Object)
.unwrap();
let mut obj_file = tempfile::NamedTempFile::new().unwrap();
obj_file.write(obj.as_slice()).unwrap();
.write_to_memory_buffer(&assembly_module, FileType::Object)
.map_err(|e| CodeGenerationError::CodeGenerationError(e.to_string()))?;
let mut obj_file = tempfile::NamedTempFile::new()
.map_err(CodeGenerationError::CouldNotCreateObjectFile)?;
obj_file
.write(obj.as_slice())
.map_err(CodeGenerationError::CouldNotCreateObjectFile)?;
obj_file
};

// Contruct a linker for the target
// Construct a linker for the target
let mut linker = linker::create_with_target(&target);
linker.add_object(obj_file.path());

// Determine output file
let dll_extension = if target.options.dll_suffix.starts_with(".") {
&target.options.dll_suffix[1..]
} else {
&target.options.dll_suffix
};
let output_file_path = original_filename.with_extension(dll_extension);

// Link the object
linker.build_shared_object(&output_file_path);

let mut cmd = linker.finalize();
cmd.spawn().unwrap().wait().unwrap();
let result = cmd
.stderr(Stdio::piped())
.spawn()
.and_then(Child::wait_with_output)
.map_err(CodeGenerationError::SpawningLinkerError)?;

if !result.status.success() {
let error = String::from_utf8(result.stderr)
.unwrap_or("<linker error contains invalid utf8>".to_owned());
Err(CodeGenerationError::LinkerError(error).into())
} else {
Ok(())
}
}

/// Optimizes the specified LLVM `Module` using the default passes for the given
/// `OptimizationLevel`.
fn optimize_module(module: &Module, optimization_lvl: OptimizationLevel) {
let pass_builder = PassManagerBuilder::create();
pass_builder.set_optimization_level(optimization_lvl);

true
let module_pass_manager = PassManager::create(());
pass_builder.populate_module_pass_manager(&module_pass_manager);
module_pass_manager.run_on(module);
}

pub mod symbols;
112 changes: 112 additions & 0 deletions crates/mun_codegen/src/code_gen/abi_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use inkwell::context::ContextRef;
use inkwell::types::{ArrayType, IntType, StructType};
use inkwell::AddressSpace;

pub(super) struct AbiTypes {
pub guid_type: ArrayType,
pub privacy_type: IntType,
pub type_info_type: StructType,
pub function_signature_type: StructType,
pub function_info_type: StructType,
pub module_info_type: StructType,
pub dispatch_table_type: StructType,
pub assembly_info_type: StructType,
}

/// Returns an `AbiTypes` struct that contains references to all LLVM ABI types.
pub(super) fn gen_abi_types(context: ContextRef) -> AbiTypes {
let str_type = context.i8_type().ptr_type(AddressSpace::Const);

// Construct the `MunGuid` type
let guid_type = context.i8_type().array_type(16);

// Construct the `MunPrivacy` enum
let privacy_type = context.i8_type();

// Construct the `MunTypeInfo` struct
let type_info_type = context.opaque_struct_type("struct.MunTypeInfo");
type_info_type.set_body(
&[
guid_type.into(), // guid
str_type.into(), // name
],
false,
);

// Construct the `MunFunctionSignature` type
let function_signature_type = context.opaque_struct_type("struct.MunFunctionSignature");
function_signature_type.set_body(
&[
str_type.into(), // name
type_info_type.ptr_type(AddressSpace::Const).into(), // arg_types
type_info_type.ptr_type(AddressSpace::Const).into(), // return_type
context.i16_type().into(), // num_arg_types
privacy_type.into(), // privacy
],
false,
);

// Construct the `MunFunctionInfo` struct
let function_info_type = context.opaque_struct_type("struct.MunFunctionInfo");
function_info_type.set_body(
&[
function_signature_type.into(), // signature
context
.void_type()
.fn_type(&[], false)
.ptr_type(AddressSpace::Const)
.into(), // fn_ptr
],
false,
);

// Construct the `MunModuleInfo` struct
let module_info_type = context.opaque_struct_type("struct.MunModuleInfo");
module_info_type.set_body(
&[
str_type.into(), // path
function_info_type.ptr_type(AddressSpace::Const).into(), // functions
context.i32_type().into(), // num_functions
],
false,
);

// Construct the `MunDispatchTable` struct
let dispatch_table_type = context.opaque_struct_type("struct.MunDispatchTable");
dispatch_table_type.set_body(
&[
function_signature_type.ptr_type(AddressSpace::Const).into(), // signatures
context
.void_type()
.fn_type(&[], false)
.ptr_type(AddressSpace::Generic)
.ptr_type(AddressSpace::Const)
.into(), // fn_ptrs
context.i32_type().into(), // num_entries
],
false,
);

// Construct the `MunAssemblyInfo` struct
let assembly_info_type = context.opaque_struct_type("struct.MunAssemblyInfo");
assembly_info_type.set_body(
&[
module_info_type.into(),
dispatch_table_type.into(),
str_type.ptr_type(AddressSpace::Const).into(),
context.i32_type().into(),
],
false,
);

AbiTypes {
guid_type,
privacy_type,
type_info_type,
function_signature_type,
function_info_type,
module_info_type,
dispatch_table_type,
assembly_info_type,
}
}
Loading

0 comments on commit d42805c

Please sign in to comment.