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

Multifile Compilation #382

Merged
merged 6 commits into from
Nov 25, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,21 @@ impl<'ink> CodeGen<'ink> {
CodeGen { context, module }
}

fn generate_llvm_index(
pub fn generate_llvm_index(
&self,
module: &Module<'ink>,
annotations: &AnnotationMap,
global_index: &Index,
) -> Result<LlvmTypedIndex<'ink>, CompileError> {
let llvm = Llvm::new(self.context, self.context.create_builder());
let mut index = LlvmTypedIndex::new();
let mut index = LlvmTypedIndex::default();
//Generate types index, and any global variables associated with them.
let llvm_type_index =
data_type_generator::generate_data_types(&llvm, global_index, annotations)?;
index.merge(llvm_type_index);

//Generate global variables
let llvm_gv_index = variable_generator::generate_global_variables(
module,
&self.module,
&llvm,
global_index,
annotations,
Expand All @@ -65,7 +64,7 @@ impl<'ink> CodeGen<'ink> {
//Generate opaque functions for implementations and associate them with their types
let llvm = Llvm::new(self.context, self.context.create_builder());
let llvm_impl_index = pou_generator::generate_implementation_stubs(
module,
&self.module,
llvm,
global_index,
annotations,
Expand All @@ -81,13 +80,11 @@ impl<'ink> CodeGen<'ink> {
unit: &CompilationUnit,
annotations: &AnnotationMap,
global_index: &Index,
llvm_index: &LlvmTypedIndex,
) -> Result<String, CompileError> {
//Associate the index type with LLVM types
let llvm_index = self.generate_llvm_index(&self.module, annotations, global_index)?;

//generate all pous
let llvm = Llvm::new(self.context, self.context.create_builder());
let pou_generator = PouGenerator::new(llvm, global_index, annotations, &llvm_index);
let pou_generator = PouGenerator::new(llvm, global_index, annotations, llvm_index);

//Generate the POU stubs in the first go to make sure they can be referenced.
for implementation in &unit.implementations {
Expand Down
2 changes: 1 addition & 1 deletion src/codegen/generators/data_type_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub fn generate_data_types<'ink>(
llvm,
index,
annotations,
types_index: LlvmTypedIndex::new(),
types_index: LlvmTypedIndex::default(),
};

let types = generator.index.get_types();
Expand Down
2 changes: 1 addition & 1 deletion src/codegen/generators/pou_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub fn generate_implementation_stubs<'ink>(
annotations: &AnnotationMap,
types_index: &LlvmTypedIndex<'ink>,
) -> Result<LlvmTypedIndex<'ink>, CompileError> {
let mut llvm_index = LlvmTypedIndex::new();
let mut llvm_index = LlvmTypedIndex::default();
let pou_generator = PouGenerator::new(llvm, index, annotations, types_index);
for (name, implementation) in index.get_implementations() {
let curr_f = pou_generator.generate_implementation_stub(implementation, module)?;
Expand Down
2 changes: 1 addition & 1 deletion src/codegen/generators/variable_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub fn generate_global_variables<'ctx, 'b>(
annotations: &'b AnnotationMap,
types_index: &'b LlvmTypedIndex<'ctx>,
) -> Result<LlvmTypedIndex<'ctx>, CompileError> {
let mut index = LlvmTypedIndex::new();
let mut index = LlvmTypedIndex::default();
let globals = global_index.get_globals();
let enums = global_index.get_global_qualified_enums();
for (name, variable) in globals.into_iter().chain(enums.into_iter()) {
Expand Down
13 changes: 1 addition & 12 deletions src/codegen/llvm_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::collections::HashMap;

/// Index view containing declared values for the current context
/// Parent Index is the a fallback lookup index for values not declared locally
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub struct LlvmTypedIndex<'ink> {
parent_index: Option<&'ink LlvmTypedIndex<'ink>>,
type_associations: HashMap<String, BasicTypeEnum<'ink>>,
Expand All @@ -17,17 +17,6 @@ pub struct LlvmTypedIndex<'ink> {
}

impl<'ink> LlvmTypedIndex<'ink> {
pub fn new() -> LlvmTypedIndex<'ink> {
LlvmTypedIndex {
parent_index: None,
type_associations: HashMap::new(),
initial_value_associations: HashMap::new(),
loaded_variable_associations: HashMap::new(),
implementations: HashMap::new(),
constants: HashMap::new(),
}
}

pub fn create_child(parent: &'ink LlvmTypedIndex<'ink>) -> LlvmTypedIndex<'ink> {
LlvmTypedIndex {
parent_index: Some(parent),
Expand Down
4 changes: 2 additions & 2 deletions src/codegen/tests/codegen_error_messages_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ fn char_assigning_wstring_literal_results_in_casting_error() {
// THEN result should be a casting error
if let Err(msg) = result {
assert_eq!(
CompileError::casting_error("WSTRING".into(), "CHAR".into(), (52..55).into()),
CompileError::casting_error("WSTRING", "CHAR", (52..55).into()),
msg
)
} else {
Expand All @@ -355,7 +355,7 @@ fn wchar_assigning_string_literal_results_in_casting_error() {
// THEN result shoud be a casting error
if let Err(msg) = result {
assert_eq!(
CompileError::casting_error("STRING".into(), "WCHAR".into(), (53..56).into()),
CompileError::casting_error("STRING", "WCHAR", (53..56).into()),
msg
)
} else {
Expand Down
63 changes: 53 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,24 @@ impl SourceContainer for SourceCode {
}
}

impl From<&str> for SourceCode {
fn from(src: &str) -> Self {
SourceCode {
source: src.into(),
path: "external_file.st".into(),
}
}
}

impl From<String> for SourceCode {
fn from(source: String) -> Self {
SourceCode {
source,
path: "external_file.st".into(),
}
}
}

fn create_source_code<T: Read>(
reader: &mut T,
encoding: Option<&'static Encoding>,
Expand Down Expand Up @@ -466,6 +484,7 @@ fn compile_to_obj<T: SourceContainer>(
/// # Arguments
///
/// * `sources` - the source to be compiled
/// * `encoding` - The encoding to parse the files, None for UTF-8
/// * `output` - the location on disk to save the output
/// * `target` - an optional llvm target triple
/// If not provided, the machine's triple will be used.
Expand All @@ -489,6 +508,7 @@ pub fn compile_to_static_obj<T: SourceContainer>(
/// # Arguments
///
/// * `sources` - the source to be compiled
/// * `encoding` - The encoding to parse the files, None for UTF-8
/// * `output` - the location on disk to save the output
/// * `target` - an optional llvm target triple
/// If not provided, the machine's triple will be used.
Expand All @@ -512,6 +532,7 @@ pub fn compile_to_shared_pic_object<T: SourceContainer>(
/// # Arguments
///
/// * `sources` - the source to be compiled
/// * `encoding` - The encoding to parse the files, None for UTF-8
/// * `output` - the location on disk to save the output
/// * `target` - an optional llvm target triple
/// If not provided, the machine's triple will be used.
Expand Down Expand Up @@ -550,30 +571,47 @@ pub fn compile_to_bitcode<T: SourceContainer>(
}

///
/// Compiles the given source into LLVM IR and returns it
/// Compiles the given source into LLVM IR and saves it to the given output location
///
/// # Arguments
///
/// * `sources` - the source to be compiled
/// * `encoding` - The encoding to parse the files, None for UTF-8
/// * `output` - The location to save the generated ir file
pub fn compile_to_ir<T: SourceContainer>(
sources: Vec<T>,
encoding: Option<&'static Encoding>,
output: &str,
) -> Result<(), CompileError> {
let c = Context::create();
let code_gen = compile_module(&c, sources, encoding)?;
let ir = code_gen.module.print_to_string().to_string();
let ir = compile_to_string(sources, encoding)?;
fs::write(output, ir)
.map_err(|err| CompileError::io_write_error(output.into(), err.to_string()))
}

///
/// Compiles the given source into LLVM IR and returns it
///
/// # Arguments
///
/// * `sources` - the source to be compiled
/// * `encoding` - The encoding to parse the files, None for UTF-8
pub fn compile_to_string<T: SourceContainer>(
sources: Vec<T>,
encoding: Option<&'static Encoding>,
) -> Result<String, CompileError> {
let c = Context::create();
let code_gen = compile_module(&c, sources, encoding)?;
Ok(code_gen.module.print_to_string().to_string())
}

///
/// Compiles the given source into a `codegen::CodeGen` using the provided context
///
/// # Arguments
///
/// * `context` - the LLVM Context to be used for the compilation
/// * `sources` - the source to be compiled
/// * `encoding` - The encoding to parse the files, None for UTF-8
pub fn compile_module<'c, T: SourceContainer>(
context: &'c Context,
sources: Vec<T>,
Expand Down Expand Up @@ -609,8 +647,9 @@ pub fn compile_module<'c, T: SourceContainer>(

// ### PHASE 2 ###
// annotation & validation everything
type AnnotatedAst<'a> = (&'a CompilationUnit, AnnotationMap);
let mut annotated_units: Vec<AnnotatedAst> = Vec::new();
// type AnnotatedAst<'a> = (&'a CompilationUnit, AnnotationMap);
let mut annotated_units: Vec<&CompilationUnit> = Vec::new();
let mut all_annotations = AnnotationMap::default();
for (file_id, syntax_errors, unit) in all_units.iter() {
let annotations = TypeAnnotator::visit_unit(&full_index, unit);

Expand All @@ -620,15 +659,17 @@ pub fn compile_module<'c, T: SourceContainer>(
report_diagnostics(*file_id, syntax_errors.iter(), &files)?;
report_diagnostics(*file_id, validator.diagnostics().iter(), &files)?;

annotated_units.push((unit, annotations));
annotated_units.push(unit);
all_annotations.import(annotations);
}

// ### PHASE 3 ###
// - codegen
let code_generator = codegen::CodeGen::new(context, "main");

for (unit, annotations) in annotated_units {
code_generator.generate(unit, &annotations, &full_index)?;
//Associate the index type with LLVM types
let llvm_index = code_generator.generate_llvm_index(&all_annotations, &full_index)?;
for unit in annotated_units {
code_generator.generate(unit, &all_annotations, &full_index, &llvm_index)?;
}
Ok(code_generator)
}
Expand Down Expand Up @@ -667,6 +708,8 @@ fn report_diagnostics(

#[cfg(test)]
mod tests {
mod multi_files;

use inkwell::targets::TargetMachine;

use crate::{create_source_code, get_target_triple};
Expand Down
5 changes: 5 additions & 0 deletions src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ impl AnnotationMap {
}
}

pub fn import(&mut self, other: AnnotationMap) {
self.type_map.extend(other.type_map);
self.type_hint_map.extend(other.type_hint_map);
}

/// annotates the given statement (using it's `get_id()`) with the given type-name
pub fn annotate(&mut self, s: &AstStatement, annotation: StatementAnnotation) {
self.type_map.insert(s.get_id(), annotation);
Expand Down
3 changes: 2 additions & 1 deletion src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ pub mod tests {

let context = inkwell::context::Context::create();
let code_generator = crate::codegen::CodeGen::new(&context, "main");
code_generator.generate(&unit, &annotations, &index)
let llvm_index = code_generator.generate_llvm_index(&annotations, &index)?;
code_generator.generate(&unit, &annotations, &index, &llvm_index)
}

pub fn codegen(src: &str) -> String {
Expand Down
32 changes: 32 additions & 0 deletions src/tests/multi_files.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::{compile_to_string, SourceCode};

#[test]
fn multiple_source_files_generated() {
//Given 2 sources
let src1: SourceCode = "
FUNCTION main : INT
VAR_INPUT

END_VAR

VAR

END_VAR
mainProg();
END_FUNCTION

"
.into();
let src2: SourceCode = "
PROGRAM mainProg
VAR_TEMP
END_VAR
END_PROGRAM
"
.into();
//When the are generated
let res = compile_to_string(vec![src1, src2], None).unwrap();
//The datatypes do not conflics
//The functions are defined correctly
insta::assert_snapshot!(res);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
source: src/tests/multi_files.rs
expression: res

---
; ModuleID = 'main'
source_filename = "main"

%mainProg_interface = type {}
%main_interface = type {}

@mainProg_instance = global %mainProg_interface zeroinitializer

define i16 @main(%main_interface* %0) {
entry:
%main = alloca i16, align 2
br label %input

input: ; preds = %entry
br label %call

call: ; preds = %input
call void @mainProg(%mainProg_interface* @mainProg_instance)
br label %output

output: ; preds = %call
br label %continue

continue: ; preds = %output
%main_ret = load i16, i16* %main, align 2
ret i16 %main_ret
}

define void @mainProg(%mainProg_interface* %0) {
entry:
ret void
}

Loading