Skip to content

Commit 974ba6b

Browse files
authored
Multifile Compilation (#382)
* Add failing tests for multi files * Make sure types are generated once * Make sure correctness tests' datatypes are big enough * Make sure the tests use the multi compile
1 parent b173527 commit 974ba6b

35 files changed

+350
-216
lines changed

src/codegen.rs

+6-9
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,21 @@ impl<'ink> CodeGen<'ink> {
3939
CodeGen { context, module }
4040
}
4141

42-
fn generate_llvm_index(
42+
pub fn generate_llvm_index(
4343
&self,
44-
module: &Module<'ink>,
4544
annotations: &AnnotationMap,
4645
global_index: &Index,
4746
) -> Result<LlvmTypedIndex<'ink>, CompileError> {
4847
let llvm = Llvm::new(self.context, self.context.create_builder());
49-
let mut index = LlvmTypedIndex::new();
48+
let mut index = LlvmTypedIndex::default();
5049
//Generate types index, and any global variables associated with them.
5150
let llvm_type_index =
5251
data_type_generator::generate_data_types(&llvm, global_index, annotations)?;
5352
index.merge(llvm_type_index);
5453

5554
//Generate global variables
5655
let llvm_gv_index = variable_generator::generate_global_variables(
57-
module,
56+
&self.module,
5857
&llvm,
5958
global_index,
6059
annotations,
@@ -65,7 +64,7 @@ impl<'ink> CodeGen<'ink> {
6564
//Generate opaque functions for implementations and associate them with their types
6665
let llvm = Llvm::new(self.context, self.context.create_builder());
6766
let llvm_impl_index = pou_generator::generate_implementation_stubs(
68-
module,
67+
&self.module,
6968
llvm,
7069
global_index,
7170
annotations,
@@ -81,13 +80,11 @@ impl<'ink> CodeGen<'ink> {
8180
unit: &CompilationUnit,
8281
annotations: &AnnotationMap,
8382
global_index: &Index,
83+
llvm_index: &LlvmTypedIndex,
8484
) -> Result<String, CompileError> {
85-
//Associate the index type with LLVM types
86-
let llvm_index = self.generate_llvm_index(&self.module, annotations, global_index)?;
87-
8885
//generate all pous
8986
let llvm = Llvm::new(self.context, self.context.create_builder());
90-
let pou_generator = PouGenerator::new(llvm, global_index, annotations, &llvm_index);
87+
let pou_generator = PouGenerator::new(llvm, global_index, annotations, llvm_index);
9188

9289
//Generate the POU stubs in the first go to make sure they can be referenced.
9390
for implementation in &unit.implementations {

src/codegen/generators/data_type_generator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub fn generate_data_types<'ink>(
5151
llvm,
5252
index,
5353
annotations,
54-
types_index: LlvmTypedIndex::new(),
54+
types_index: LlvmTypedIndex::default(),
5555
};
5656

5757
let types = generator.index.get_types();

src/codegen/generators/pou_generator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub fn generate_implementation_stubs<'ink>(
4545
annotations: &AnnotationMap,
4646
types_index: &LlvmTypedIndex<'ink>,
4747
) -> Result<LlvmTypedIndex<'ink>, CompileError> {
48-
let mut llvm_index = LlvmTypedIndex::new();
48+
let mut llvm_index = LlvmTypedIndex::default();
4949
let pou_generator = PouGenerator::new(llvm, index, annotations, types_index);
5050
for (name, implementation) in index.get_implementations() {
5151
let curr_f = pou_generator.generate_implementation_stub(implementation, module)?;

src/codegen/generators/variable_generator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub fn generate_global_variables<'ctx, 'b>(
1919
annotations: &'b AnnotationMap,
2020
types_index: &'b LlvmTypedIndex<'ctx>,
2121
) -> Result<LlvmTypedIndex<'ctx>, CompileError> {
22-
let mut index = LlvmTypedIndex::new();
22+
let mut index = LlvmTypedIndex::default();
2323
let globals = global_index.get_globals();
2424
let enums = global_index.get_global_qualified_enums();
2525
for (name, variable) in globals.into_iter().chain(enums.into_iter()) {

src/codegen/llvm_index.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::collections::HashMap;
66

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

1919
impl<'ink> LlvmTypedIndex<'ink> {
20-
pub fn new() -> LlvmTypedIndex<'ink> {
21-
LlvmTypedIndex {
22-
parent_index: None,
23-
type_associations: HashMap::new(),
24-
initial_value_associations: HashMap::new(),
25-
loaded_variable_associations: HashMap::new(),
26-
implementations: HashMap::new(),
27-
constants: HashMap::new(),
28-
}
29-
}
30-
3120
pub fn create_child(parent: &'ink LlvmTypedIndex<'ink>) -> LlvmTypedIndex<'ink> {
3221
LlvmTypedIndex {
3322
parent_index: Some(parent),

src/codegen/tests/codegen_error_messages_tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ fn char_assigning_wstring_literal_results_in_casting_error() {
331331
// THEN result should be a casting error
332332
if let Err(msg) = result {
333333
assert_eq!(
334-
CompileError::casting_error("WSTRING".into(), "CHAR".into(), (52..55).into()),
334+
CompileError::casting_error("WSTRING", "CHAR", (52..55).into()),
335335
msg
336336
)
337337
} else {
@@ -355,7 +355,7 @@ fn wchar_assigning_string_literal_results_in_casting_error() {
355355
// THEN result shoud be a casting error
356356
if let Err(msg) = result {
357357
assert_eq!(
358-
CompileError::casting_error("STRING".into(), "WCHAR".into(), (53..56).into()),
358+
CompileError::casting_error("STRING", "WCHAR", (53..56).into()),
359359
msg
360360
)
361361
} else {

src/lib.rs

+53-10
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,24 @@ impl SourceContainer for SourceCode {
396396
}
397397
}
398398

399+
impl From<&str> for SourceCode {
400+
fn from(src: &str) -> Self {
401+
SourceCode {
402+
source: src.into(),
403+
path: "external_file.st".into(),
404+
}
405+
}
406+
}
407+
408+
impl From<String> for SourceCode {
409+
fn from(source: String) -> Self {
410+
SourceCode {
411+
source,
412+
path: "external_file.st".into(),
413+
}
414+
}
415+
}
416+
399417
fn create_source_code<T: Read>(
400418
reader: &mut T,
401419
encoding: Option<&'static Encoding>,
@@ -466,6 +484,7 @@ fn compile_to_obj<T: SourceContainer>(
466484
/// # Arguments
467485
///
468486
/// * `sources` - the source to be compiled
487+
/// * `encoding` - The encoding to parse the files, None for UTF-8
469488
/// * `output` - the location on disk to save the output
470489
/// * `target` - an optional llvm target triple
471490
/// If not provided, the machine's triple will be used.
@@ -489,6 +508,7 @@ pub fn compile_to_static_obj<T: SourceContainer>(
489508
/// # Arguments
490509
///
491510
/// * `sources` - the source to be compiled
511+
/// * `encoding` - The encoding to parse the files, None for UTF-8
492512
/// * `output` - the location on disk to save the output
493513
/// * `target` - an optional llvm target triple
494514
/// If not provided, the machine's triple will be used.
@@ -512,6 +532,7 @@ pub fn compile_to_shared_pic_object<T: SourceContainer>(
512532
/// # Arguments
513533
///
514534
/// * `sources` - the source to be compiled
535+
/// * `encoding` - The encoding to parse the files, None for UTF-8
515536
/// * `output` - the location on disk to save the output
516537
/// * `target` - an optional llvm target triple
517538
/// If not provided, the machine's triple will be used.
@@ -550,30 +571,47 @@ pub fn compile_to_bitcode<T: SourceContainer>(
550571
}
551572

552573
///
553-
/// Compiles the given source into LLVM IR and returns it
574+
/// Compiles the given source into LLVM IR and saves it to the given output location
554575
///
555576
/// # Arguments
556577
///
557578
/// * `sources` - the source to be compiled
579+
/// * `encoding` - The encoding to parse the files, None for UTF-8
580+
/// * `output` - The location to save the generated ir file
558581
pub fn compile_to_ir<T: SourceContainer>(
559582
sources: Vec<T>,
560583
encoding: Option<&'static Encoding>,
561584
output: &str,
562585
) -> Result<(), CompileError> {
563-
let c = Context::create();
564-
let code_gen = compile_module(&c, sources, encoding)?;
565-
let ir = code_gen.module.print_to_string().to_string();
586+
let ir = compile_to_string(sources, encoding)?;
566587
fs::write(output, ir)
567588
.map_err(|err| CompileError::io_write_error(output.into(), err.to_string()))
568589
}
569590

591+
///
592+
/// Compiles the given source into LLVM IR and returns it
593+
///
594+
/// # Arguments
595+
///
596+
/// * `sources` - the source to be compiled
597+
/// * `encoding` - The encoding to parse the files, None for UTF-8
598+
pub fn compile_to_string<T: SourceContainer>(
599+
sources: Vec<T>,
600+
encoding: Option<&'static Encoding>,
601+
) -> Result<String, CompileError> {
602+
let c = Context::create();
603+
let code_gen = compile_module(&c, sources, encoding)?;
604+
Ok(code_gen.module.print_to_string().to_string())
605+
}
606+
570607
///
571608
/// Compiles the given source into a `codegen::CodeGen` using the provided context
572609
///
573610
/// # Arguments
574611
///
575612
/// * `context` - the LLVM Context to be used for the compilation
576613
/// * `sources` - the source to be compiled
614+
/// * `encoding` - The encoding to parse the files, None for UTF-8
577615
pub fn compile_module<'c, T: SourceContainer>(
578616
context: &'c Context,
579617
sources: Vec<T>,
@@ -609,8 +647,9 @@ pub fn compile_module<'c, T: SourceContainer>(
609647

610648
// ### PHASE 2 ###
611649
// annotation & validation everything
612-
type AnnotatedAst<'a> = (&'a CompilationUnit, AnnotationMap);
613-
let mut annotated_units: Vec<AnnotatedAst> = Vec::new();
650+
// type AnnotatedAst<'a> = (&'a CompilationUnit, AnnotationMap);
651+
let mut annotated_units: Vec<&CompilationUnit> = Vec::new();
652+
let mut all_annotations = AnnotationMap::default();
614653
for (file_id, syntax_errors, unit) in all_units.iter() {
615654
let annotations = TypeAnnotator::visit_unit(&full_index, unit);
616655

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

623-
annotated_units.push((unit, annotations));
662+
annotated_units.push(unit);
663+
all_annotations.import(annotations);
624664
}
625665

626666
// ### PHASE 3 ###
627667
// - codegen
628668
let code_generator = codegen::CodeGen::new(context, "main");
629-
630-
for (unit, annotations) in annotated_units {
631-
code_generator.generate(unit, &annotations, &full_index)?;
669+
//Associate the index type with LLVM types
670+
let llvm_index = code_generator.generate_llvm_index(&all_annotations, &full_index)?;
671+
for unit in annotated_units {
672+
code_generator.generate(unit, &all_annotations, &full_index, &llvm_index)?;
632673
}
633674
Ok(code_generator)
634675
}
@@ -667,6 +708,8 @@ fn report_diagnostics(
667708

668709
#[cfg(test)]
669710
mod tests {
711+
mod multi_files;
712+
670713
use inkwell::targets::TargetMachine;
671714

672715
use crate::{create_source_code, get_target_triple};

src/resolver.rs

+5
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ impl AnnotationMap {
151151
}
152152
}
153153

154+
pub fn import(&mut self, other: AnnotationMap) {
155+
self.type_map.extend(other.type_map);
156+
self.type_hint_map.extend(other.type_hint_map);
157+
}
158+
154159
/// annotates the given statement (using it's `get_id()`) with the given type-name
155160
pub fn annotate(&mut self, s: &AstStatement, annotation: StatementAnnotation) {
156161
self.type_map.insert(s.get_id(), annotation);

src/test_utils.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ pub mod tests {
4646

4747
let context = inkwell::context::Context::create();
4848
let code_generator = crate::codegen::CodeGen::new(&context, "main");
49-
code_generator.generate(&unit, &annotations, &index)
49+
let llvm_index = code_generator.generate_llvm_index(&annotations, &index)?;
50+
code_generator.generate(&unit, &annotations, &index, &llvm_index)
5051
}
5152

5253
pub fn codegen(src: &str) -> String {

src/tests/multi_files.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use crate::{compile_to_string, SourceCode};
2+
3+
#[test]
4+
fn multiple_source_files_generated() {
5+
//Given 2 sources
6+
let src1: SourceCode = "
7+
FUNCTION main : INT
8+
VAR_INPUT
9+
10+
END_VAR
11+
12+
VAR
13+
14+
END_VAR
15+
mainProg();
16+
END_FUNCTION
17+
18+
"
19+
.into();
20+
let src2: SourceCode = "
21+
PROGRAM mainProg
22+
VAR_TEMP
23+
END_VAR
24+
END_PROGRAM
25+
"
26+
.into();
27+
//When the are generated
28+
let res = compile_to_string(vec![src1, src2], None).unwrap();
29+
//The datatypes do not conflics
30+
//The functions are defined correctly
31+
insta::assert_snapshot!(res);
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
source: src/tests/multi_files.rs
3+
expression: res
4+
5+
---
6+
; ModuleID = 'main'
7+
source_filename = "main"
8+
9+
%mainProg_interface = type {}
10+
%main_interface = type {}
11+
12+
@mainProg_instance = global %mainProg_interface zeroinitializer
13+
14+
define i16 @main(%main_interface* %0) {
15+
entry:
16+
%main = alloca i16, align 2
17+
br label %input
18+
19+
input: ; preds = %entry
20+
br label %call
21+
22+
call: ; preds = %input
23+
call void @mainProg(%mainProg_interface* @mainProg_instance)
24+
br label %output
25+
26+
output: ; preds = %call
27+
br label %continue
28+
29+
continue: ; preds = %output
30+
%main_ret = load i16, i16* %main, align 2
31+
ret i16 %main_ret
32+
}
33+
34+
define void @mainProg(%mainProg_interface* %0) {
35+
entry:
36+
ret void
37+
}
38+

0 commit comments

Comments
 (0)