core: Switch to LLVM New Pass Manager
This commit is contained in:
parent
70cb0bd898
commit
5722f3397e
|
@ -8,7 +8,7 @@ use std::sync::Arc;
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
memory_buffer::MemoryBuffer,
|
memory_buffer::MemoryBuffer,
|
||||||
module::{Linkage, Module},
|
module::{Linkage, Module},
|
||||||
passes::{PassManager, PassManagerBuilder},
|
passes::{PassBuilderOptions, PassManager, PassManagerBuilder},
|
||||||
targets::*,
|
targets::*,
|
||||||
OptimizationLevel,
|
OptimizationLevel,
|
||||||
};
|
};
|
||||||
|
@ -654,12 +654,27 @@ impl Nac3 {
|
||||||
global_option = global.get_next_global();
|
global_option = global.get_next_global();
|
||||||
}
|
}
|
||||||
|
|
||||||
let builder = PassManagerBuilder::create();
|
let target_machine = self.llvm_options.target
|
||||||
builder.set_optimization_level(OptimizationLevel::Aggressive);
|
.create_target_machine(self.llvm_options.opt_level)
|
||||||
let passes = PassManager::create(());
|
.expect("couldn't create target machine");
|
||||||
builder.set_inliner_with_threshold(255);
|
|
||||||
builder.populate_module_pass_manager(&passes);
|
if self.llvm_options.legacy_pm {
|
||||||
passes.run_on(&main);
|
let builder = PassManagerBuilder::create();
|
||||||
|
builder.set_optimization_level(OptimizationLevel::Aggressive);
|
||||||
|
let passes = PassManager::create(());
|
||||||
|
builder.set_inliner_with_threshold(255);
|
||||||
|
builder.populate_module_pass_manager(&passes);
|
||||||
|
passes.run_on(&main);
|
||||||
|
} else {
|
||||||
|
let pass_options = PassBuilderOptions::create();
|
||||||
|
pass_options.set_merge_functions(true);
|
||||||
|
let result = main.run_passes("default<O3>", &target_machine, pass_options);
|
||||||
|
if let Err(err) = result {
|
||||||
|
println!("Failed to run optimization for module `main`");
|
||||||
|
println!("{}", err.to_string());
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
link_fn(&main)
|
link_fn(&main)
|
||||||
}
|
}
|
||||||
|
@ -902,7 +917,8 @@ impl Nac3 {
|
||||||
deferred_eval_store: DeferredEvaluationStore::new(),
|
deferred_eval_store: DeferredEvaluationStore::new(),
|
||||||
llvm_options: CodeGenLLVMOptions {
|
llvm_options: CodeGenLLVMOptions {
|
||||||
opt_level: OptimizationLevel::Default,
|
opt_level: OptimizationLevel::Default,
|
||||||
legacy_pm: true,
|
// FIXME(Derppening): Add a field to device_db.py for modifying this option
|
||||||
|
legacy_pm: false,
|
||||||
target: Nac3::get_llvm_target_options(isa),
|
target: Nac3::get_llvm_target_options(isa),
|
||||||
emit_llvm: false,
|
emit_llvm: false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use inkwell::{
|
||||||
builder::Builder,
|
builder::Builder,
|
||||||
context::Context,
|
context::Context,
|
||||||
module::Module,
|
module::Module,
|
||||||
passes::{PassManager, PassManagerBuilder},
|
passes::{PassBuilderOptions, PassManager, PassManagerBuilder},
|
||||||
targets::{CodeModel, RelocMode, Target, TargetMachine, TargetTriple},
|
targets::{CodeModel, RelocMode, Target, TargetMachine, TargetTriple},
|
||||||
types::{AnyType, BasicType, BasicTypeEnum},
|
types::{AnyType, BasicType, BasicTypeEnum},
|
||||||
values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue},
|
values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue},
|
||||||
|
@ -308,23 +308,33 @@ impl WorkerRegistry {
|
||||||
context.i32_type().const_int(4, false),
|
context.i32_type().const_int(4, false),
|
||||||
);
|
);
|
||||||
|
|
||||||
let passes = PassManager::create(&module);
|
let passes = if self.llvm_options.legacy_pm {
|
||||||
|
let pm = PassManager::create(&module);
|
||||||
|
|
||||||
// HACK: This critical section is a work-around for issue
|
// HACK: This critical section is a work-around for issue
|
||||||
// https://git.m-labs.hk/M-Labs/nac3/issues/275
|
// https://git.m-labs.hk/M-Labs/nac3/issues/275
|
||||||
{
|
{
|
||||||
let _data = PASSES_INIT_LOCK.lock();
|
let _data = PASSES_INIT_LOCK.lock();
|
||||||
let pass_builder = PassManagerBuilder::create();
|
let pass_builder = PassManagerBuilder::create();
|
||||||
pass_builder.set_optimization_level(self.llvm_options.opt_level);
|
pass_builder.set_optimization_level(self.llvm_options.opt_level);
|
||||||
pass_builder.populate_function_pass_manager(&passes);
|
pass_builder.populate_function_pass_manager(&pm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(pm)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mut errors = HashSet::new();
|
let mut errors = HashSet::new();
|
||||||
while let Some(task) = self.receiver.recv().unwrap() {
|
while let Some(task) = self.receiver.recv().unwrap() {
|
||||||
match gen_func(&context, generator, self, builder, module, task) {
|
match gen_func(&context, generator, self, builder, module, task) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
builder = result.0;
|
builder = result.0;
|
||||||
passes.run_on(&result.2);
|
|
||||||
|
if let Some(pm) = &passes {
|
||||||
|
pm.run_on(&result.2);
|
||||||
|
};
|
||||||
|
|
||||||
module = result.1;
|
module = result.1;
|
||||||
}
|
}
|
||||||
Err((old_builder, e)) => {
|
Err((old_builder, e)) => {
|
||||||
|
@ -348,6 +358,21 @@ impl WorkerRegistry {
|
||||||
panic!()
|
panic!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self.llvm_options.legacy_pm {
|
||||||
|
let pass_options = PassBuilderOptions::create();
|
||||||
|
let target_machine = self.llvm_options.target.create_target_machine(
|
||||||
|
self.llvm_options.opt_level
|
||||||
|
).expect(format!("could not create target machine from properties {:?}", self.llvm_options.target).as_str());
|
||||||
|
let passes = format!("default<O{}>", self.llvm_options.opt_level as u32);
|
||||||
|
|
||||||
|
let result = module.run_passes(passes.as_str(), &target_machine, pass_options);
|
||||||
|
if let Err(err) = result {
|
||||||
|
println!("Failed to run optimization for module `{}`", module.get_name().to_str().unwrap());
|
||||||
|
println!("{}", err.to_string());
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.llvm_options.emit_llvm {
|
if self.llvm_options.emit_llvm {
|
||||||
println!("LLVM IR for {}", module.get_name().to_str().unwrap());
|
println!("LLVM IR for {}", module.get_name().to_str().unwrap());
|
||||||
println!("{}", module.to_string());
|
println!("{}", module.to_string());
|
||||||
|
|
|
@ -13,7 +13,10 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use inkwell::OptimizationLevel;
|
use inkwell::{
|
||||||
|
targets::{InitializationConfig, Target},
|
||||||
|
OptimizationLevel,
|
||||||
|
};
|
||||||
use nac3parser::{
|
use nac3parser::{
|
||||||
ast::{fold::Fold, StrRef},
|
ast::{fold::Fold, StrRef},
|
||||||
parser::parse_program,
|
parser::parse_program,
|
||||||
|
@ -21,6 +24,7 @@ use nac3parser::{
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use test_case::test_case;
|
||||||
|
|
||||||
struct Resolver {
|
struct Resolver {
|
||||||
id_to_type: HashMap<StrRef, Type>,
|
id_to_type: HashMap<StrRef, Type>,
|
||||||
|
@ -77,350 +81,455 @@ impl SymbolResolver for Resolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
struct CodeGenTestInput {
|
||||||
fn test_primitives() {
|
expected: String,
|
||||||
let source = indoc! { "
|
legacy_pm: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(
|
||||||
|
vec![
|
||||||
|
CodeGenTestInput {
|
||||||
|
expected: indoc! {"
|
||||||
|
; ModuleID = 'test'
|
||||||
|
source_filename = \"test\"
|
||||||
|
|
||||||
|
; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
|
||||||
|
define i32 @testing(i32 %0, i32 %1) local_unnamed_addr #0 !dbg !4 {
|
||||||
|
init:
|
||||||
|
%add = add i32 %1, %0, !dbg !9
|
||||||
|
%cmp = icmp eq i32 %add, 1, !dbg !10
|
||||||
|
%. = select i1 %cmp, i32 %0, i32 0, !dbg !11
|
||||||
|
ret i32 %., !dbg !12
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn }
|
||||||
|
|
||||||
|
!llvm.module.flags = !{!0, !1}
|
||||||
|
!llvm.dbg.cu = !{!2}
|
||||||
|
|
||||||
|
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
|
||||||
|
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
|
||||||
|
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
|
||||||
|
!3 = !DIFile(filename: \"unknown\", directory: \"\")
|
||||||
|
!4 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !5, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !8)
|
||||||
|
!5 = !DISubroutineType(flags: DIFlagPublic, types: !6)
|
||||||
|
!6 = !{!7}
|
||||||
|
!7 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
|
||||||
|
!8 = !{}
|
||||||
|
!9 = !DILocation(line: 1, column: 9, scope: !4)
|
||||||
|
!10 = !DILocation(line: 2, column: 15, scope: !4)
|
||||||
|
!11 = !DILocation(line: 0, scope: !4)
|
||||||
|
!12 = !DILocation(line: 3, column: 8, scope: !4)
|
||||||
|
"}.trim().to_string(),
|
||||||
|
legacy_pm: false,
|
||||||
|
},
|
||||||
|
CodeGenTestInput {
|
||||||
|
expected: indoc! {"
|
||||||
|
; ModuleID = 'test'
|
||||||
|
source_filename = \"test\"
|
||||||
|
|
||||||
|
define i32 @testing(i32 %0, i32 %1) !dbg !4 {
|
||||||
|
init:
|
||||||
|
%add = add i32 %0, %1, !dbg !9
|
||||||
|
%cmp = icmp eq i32 %add, 1, !dbg !10
|
||||||
|
br i1 %cmp, label %then, label %else, !dbg !10
|
||||||
|
|
||||||
|
then: ; preds = %init
|
||||||
|
br label %cont, !dbg !11
|
||||||
|
|
||||||
|
else: ; preds = %init
|
||||||
|
br label %cont, !dbg !12
|
||||||
|
|
||||||
|
cont: ; preds = %else, %then
|
||||||
|
%if_exp_result.0 = phi i32 [ %0, %then ], [ 0, %else ], !dbg !13
|
||||||
|
ret i32 %if_exp_result.0, !dbg !14
|
||||||
|
}
|
||||||
|
|
||||||
|
!llvm.module.flags = !{!0, !1}
|
||||||
|
!llvm.dbg.cu = !{!2}
|
||||||
|
|
||||||
|
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
|
||||||
|
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
|
||||||
|
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
|
||||||
|
!3 = !DIFile(filename: \"unknown\", directory: \"\")
|
||||||
|
!4 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !5, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !8)
|
||||||
|
!5 = !DISubroutineType(flags: DIFlagPublic, types: !6)
|
||||||
|
!6 = !{!7}
|
||||||
|
!7 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
|
||||||
|
!8 = !{}
|
||||||
|
!9 = !DILocation(line: 1, column: 9, scope: !4)
|
||||||
|
!10 = !DILocation(line: 2, column: 15, scope: !4)
|
||||||
|
!11 = !DILocation(line: 2, column: 5, scope: !4)
|
||||||
|
!12 = !DILocation(line: 2, column: 22, scope: !4)
|
||||||
|
!13 = !DILocation(line: 0, scope: !4)
|
||||||
|
!14 = !DILocation(line: 3, column: 8, scope: !4)
|
||||||
|
"}.trim().to_string(),
|
||||||
|
legacy_pm: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
"primitives codegen"
|
||||||
|
)]
|
||||||
|
fn test_primitives(inputs: Vec<CodeGenTestInput>) {
|
||||||
|
Target::initialize_all(&InitializationConfig::default());
|
||||||
|
|
||||||
|
for input in inputs {
|
||||||
|
let CodeGenTestInput { expected, legacy_pm } = input;
|
||||||
|
|
||||||
|
let source = indoc! { "
|
||||||
c = a + b
|
c = a + b
|
||||||
d = a if c == 1 else 0
|
d = a if c == 1 else 0
|
||||||
return d
|
return d
|
||||||
"};
|
"};
|
||||||
let statements = parse_program(source, Default::default()).unwrap();
|
let statements = parse_program(source, Default::default()).unwrap();
|
||||||
|
|
||||||
let composer: TopLevelComposer = Default::default();
|
let composer: TopLevelComposer = Default::default();
|
||||||
let mut unifier = composer.unifier.clone();
|
let mut unifier = composer.unifier.clone();
|
||||||
let primitives = composer.primitives_ty;
|
let primitives = composer.primitives_ty;
|
||||||
let top_level = Arc::new(composer.make_top_level_context());
|
let top_level = Arc::new(composer.make_top_level_context());
|
||||||
unifier.top_level = Some(top_level.clone());
|
unifier.top_level = Some(top_level.clone());
|
||||||
|
|
||||||
let resolver = Arc::new(Resolver {
|
let resolver = Arc::new(Resolver {
|
||||||
id_to_type: HashMap::new(),
|
id_to_type: HashMap::new(),
|
||||||
id_to_def: RwLock::new(HashMap::new()),
|
id_to_def: RwLock::new(HashMap::new()),
|
||||||
class_names: Default::default(),
|
class_names: Default::default(),
|
||||||
}) as Arc<dyn SymbolResolver + Send + Sync>;
|
}) as Arc<dyn SymbolResolver + Send + Sync>;
|
||||||
|
|
||||||
let threads = vec![DefaultCodeGenerator::new("test".into(), 32).into()];
|
let threads = vec![DefaultCodeGenerator::new("test".into(), 32).into()];
|
||||||
let signature = FunSignature {
|
let signature = FunSignature {
|
||||||
args: vec![
|
args: vec![
|
||||||
FuncArg { name: "a".into(), ty: primitives.int32, default_value: None },
|
FuncArg { name: "a".into(), ty: primitives.int32, default_value: None },
|
||||||
FuncArg { name: "b".into(), ty: primitives.int32, default_value: None },
|
FuncArg { name: "b".into(), ty: primitives.int32, default_value: None },
|
||||||
],
|
],
|
||||||
ret: primitives.int32,
|
ret: primitives.int32,
|
||||||
vars: HashMap::new(),
|
vars: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut store = ConcreteTypeStore::new();
|
let mut store = ConcreteTypeStore::new();
|
||||||
let mut cache = HashMap::new();
|
let mut cache = HashMap::new();
|
||||||
let signature = store.from_signature(&mut unifier, &primitives, &signature, &mut cache);
|
let signature = store.from_signature(&mut unifier, &primitives, &signature, &mut cache);
|
||||||
let signature = store.add_cty(signature);
|
let signature = store.add_cty(signature);
|
||||||
|
|
||||||
let mut function_data = FunctionData {
|
let mut function_data = FunctionData {
|
||||||
resolver: resolver.clone(),
|
resolver: resolver.clone(),
|
||||||
bound_variables: Vec::new(),
|
bound_variables: Vec::new(),
|
||||||
return_type: Some(primitives.int32),
|
return_type: Some(primitives.int32),
|
||||||
};
|
};
|
||||||
let mut virtual_checks = Vec::new();
|
let mut virtual_checks = Vec::new();
|
||||||
let mut calls = HashMap::new();
|
let mut calls = HashMap::new();
|
||||||
let mut identifiers: HashSet<_> = ["a".into(), "b".into()].iter().cloned().collect();
|
let mut identifiers: HashSet<_> = ["a".into(), "b".into()].iter().cloned().collect();
|
||||||
let mut inferencer = Inferencer {
|
let mut inferencer = Inferencer {
|
||||||
top_level: &top_level,
|
top_level: &top_level,
|
||||||
function_data: &mut function_data,
|
function_data: &mut function_data,
|
||||||
unifier: &mut unifier,
|
unifier: &mut unifier,
|
||||||
variable_mapping: Default::default(),
|
variable_mapping: Default::default(),
|
||||||
primitives: &primitives,
|
primitives: &primitives,
|
||||||
virtual_checks: &mut virtual_checks,
|
virtual_checks: &mut virtual_checks,
|
||||||
calls: &mut calls,
|
calls: &mut calls,
|
||||||
defined_identifiers: identifiers.clone(),
|
defined_identifiers: identifiers.clone(),
|
||||||
in_handler: false,
|
in_handler: false,
|
||||||
};
|
};
|
||||||
inferencer.variable_mapping.insert("a".into(), inferencer.primitives.int32);
|
inferencer.variable_mapping.insert("a".into(), inferencer.primitives.int32);
|
||||||
inferencer.variable_mapping.insert("b".into(), inferencer.primitives.int32);
|
inferencer.variable_mapping.insert("b".into(), inferencer.primitives.int32);
|
||||||
|
|
||||||
let statements = statements
|
let statements = statements
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| inferencer.fold_stmt(v))
|
.map(|v| inferencer.fold_stmt(v))
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
inferencer.check_block(&statements, &mut identifiers).unwrap();
|
inferencer.check_block(&statements, &mut identifiers).unwrap();
|
||||||
let top_level = Arc::new(TopLevelContext {
|
let top_level = Arc::new(TopLevelContext {
|
||||||
definitions: Arc::new(RwLock::new(std::mem::take(&mut *top_level.definitions.write()))),
|
definitions: Arc::new(RwLock::new(std::mem::take(&mut *top_level.definitions.write()))),
|
||||||
unifiers: Arc::new(RwLock::new(vec![(unifier.get_shared_unifier(), primitives)])),
|
unifiers: Arc::new(RwLock::new(vec![(unifier.get_shared_unifier(), primitives)])),
|
||||||
personality_symbol: None,
|
personality_symbol: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let task = CodeGenTask {
|
let task = CodeGenTask {
|
||||||
subst: Default::default(),
|
subst: Default::default(),
|
||||||
symbol_name: "testing".into(),
|
symbol_name: "testing".into(),
|
||||||
body: Arc::new(statements),
|
body: Arc::new(statements),
|
||||||
unifier_index: 0,
|
unifier_index: 0,
|
||||||
calls: Arc::new(calls),
|
calls: Arc::new(calls),
|
||||||
resolver,
|
resolver,
|
||||||
store,
|
store,
|
||||||
signature,
|
signature,
|
||||||
id: 0,
|
id: 0,
|
||||||
};
|
};
|
||||||
let f = Arc::new(WithCall::new(Box::new(|module| {
|
|
||||||
// the following IR is equivalent to
|
|
||||||
// ```
|
|
||||||
// ; ModuleID = 'test.ll'
|
|
||||||
// source_filename = "test"
|
|
||||||
//
|
|
||||||
// ; Function Attrs: norecurse nounwind readnone
|
|
||||||
// define i32 @testing(i32 %0, i32 %1) local_unnamed_addr #0 {
|
|
||||||
// init:
|
|
||||||
// %add = add i32 %1, %0
|
|
||||||
// %cmp = icmp eq i32 %add, 1
|
|
||||||
// %ifexpr = select i1 %cmp, i32 %0, i32 0
|
|
||||||
// ret i32 %ifexpr
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// attributes #0 = { norecurse nounwind readnone }
|
|
||||||
// ```
|
|
||||||
// after O2 optimization
|
|
||||||
|
|
||||||
let expected = indoc! {"
|
let f = Arc::new(WithCall::new(Box::new(move |module| {
|
||||||
; ModuleID = 'test'
|
// the following IR is equivalent to
|
||||||
source_filename = \"test\"
|
// ```
|
||||||
|
// ; ModuleID = 'test.ll'
|
||||||
define i32 @testing(i32 %0, i32 %1) !dbg !4 {
|
// source_filename = "test"
|
||||||
init:
|
//
|
||||||
%add = add i32 %0, %1, !dbg !9
|
// ; Function Attrs: norecurse nounwind readnone
|
||||||
%cmp = icmp eq i32 %add, 1, !dbg !10
|
// define i32 @testing(i32 %0, i32 %1) local_unnamed_addr #0 {
|
||||||
br i1 %cmp, label %then, label %else, !dbg !10
|
// init:
|
||||||
|
// %add = add i32 %1, %0
|
||||||
then: ; preds = %init
|
// %cmp = icmp eq i32 %add, 1
|
||||||
br label %cont, !dbg !11
|
// %ifexpr = select i1 %cmp, i32 %0, i32 0
|
||||||
|
// ret i32 %ifexpr
|
||||||
else: ; preds = %init
|
// }
|
||||||
br label %cont, !dbg !12
|
//
|
||||||
|
// attributes #0 = { norecurse nounwind readnone }
|
||||||
cont: ; preds = %else, %then
|
// ```
|
||||||
%if_exp_result.0 = phi i32 [ %0, %then ], [ 0, %else ], !dbg !13
|
// after O2 optimization
|
||||||
ret i32 %if_exp_result.0, !dbg !14
|
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());
|
||||||
}
|
})));
|
||||||
|
|
||||||
!llvm.module.flags = !{!0, !1}
|
let llvm_options = CodeGenLLVMOptions {
|
||||||
!llvm.dbg.cu = !{!2}
|
opt_level: OptimizationLevel::Default,
|
||||||
|
legacy_pm,
|
||||||
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
|
target: CodeGenTargetMachineOptions::from_host_triple(),
|
||||||
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
|
emit_llvm: false,
|
||||||
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
|
};
|
||||||
!3 = !DIFile(filename: \"unknown\", directory: \"\")
|
let (registry, handles) = WorkerRegistry::create_workers(
|
||||||
!4 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !5, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !8)
|
threads,
|
||||||
!5 = !DISubroutineType(flags: DIFlagPublic, types: !6)
|
top_level,
|
||||||
!6 = !{!7}
|
&llvm_options,
|
||||||
!7 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
|
f
|
||||||
!8 = !{}
|
);
|
||||||
!9 = !DILocation(line: 1, column: 9, scope: !4)
|
registry.add_task(task);
|
||||||
!10 = !DILocation(line: 2, column: 15, scope: !4)
|
registry.wait_tasks_complete(handles);
|
||||||
!11 = !DILocation(line: 2, column: 5, scope: !4)
|
}
|
||||||
!12 = !DILocation(line: 2, column: 22, scope: !4)
|
|
||||||
!13 = !DILocation(line: 0, scope: !4)
|
|
||||||
!14 = !DILocation(line: 3, column: 8, scope: !4)
|
|
||||||
"}
|
|
||||||
.trim();
|
|
||||||
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());
|
|
||||||
})));
|
|
||||||
|
|
||||||
let llvm_options = CodeGenLLVMOptions {
|
|
||||||
opt_level: OptimizationLevel::Default,
|
|
||||||
legacy_pm: true,
|
|
||||||
target: CodeGenTargetMachineOptions::from_host_triple(),
|
|
||||||
emit_llvm: false,
|
|
||||||
};
|
|
||||||
let (registry, handles) = WorkerRegistry::create_workers(
|
|
||||||
threads,
|
|
||||||
top_level,
|
|
||||||
&llvm_options,
|
|
||||||
f
|
|
||||||
);
|
|
||||||
registry.add_task(task);
|
|
||||||
registry.wait_tasks_complete(handles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test_case(
|
||||||
fn test_simple_call() {
|
vec![
|
||||||
let source_1 = indoc! { "
|
CodeGenTestInput {
|
||||||
|
expected: indoc! {"
|
||||||
|
; ModuleID = 'test'
|
||||||
|
source_filename = \"test\"
|
||||||
|
|
||||||
|
; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
|
||||||
|
define i32 @testing(i32 %0) local_unnamed_addr #0 !dbg !5 {
|
||||||
|
init:
|
||||||
|
%add.i = shl i32 %0, 1, !dbg !10
|
||||||
|
%mul = add i32 %add.i, 2, !dbg !10
|
||||||
|
ret i32 %mul, !dbg !10
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
|
||||||
|
define i32 @foo.0(i32 %0) local_unnamed_addr #0 !dbg !11 {
|
||||||
|
init:
|
||||||
|
%add = add i32 %0, 1, !dbg !12
|
||||||
|
ret i32 %add, !dbg !12
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn }
|
||||||
|
|
||||||
|
!llvm.module.flags = !{!0, !1}
|
||||||
|
!llvm.dbg.cu = !{!2, !4}
|
||||||
|
|
||||||
|
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
|
||||||
|
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
|
||||||
|
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
|
||||||
|
!3 = !DIFile(filename: \"unknown\", directory: \"\")
|
||||||
|
!4 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
|
||||||
|
!5 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !9)
|
||||||
|
!6 = !DISubroutineType(flags: DIFlagPublic, types: !7)
|
||||||
|
!7 = !{!8}
|
||||||
|
!8 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
|
||||||
|
!9 = !{}
|
||||||
|
!10 = !DILocation(line: 2, column: 12, scope: !5)
|
||||||
|
!11 = distinct !DISubprogram(name: \"foo.0\", linkageName: \"foo.0\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, retainedNodes: !9)
|
||||||
|
!12 = !DILocation(line: 1, column: 12, scope: !11)
|
||||||
|
"}.trim().to_string(),
|
||||||
|
legacy_pm: false,
|
||||||
|
},
|
||||||
|
CodeGenTestInput {
|
||||||
|
expected: indoc! {"
|
||||||
|
; ModuleID = 'test'
|
||||||
|
source_filename = \"test\"
|
||||||
|
|
||||||
|
define i32 @testing(i32 %0) !dbg !5 {
|
||||||
|
init:
|
||||||
|
%call = call i32 @foo.0(i32 %0), !dbg !10
|
||||||
|
%mul = mul i32 %call, 2, !dbg !11
|
||||||
|
ret i32 %mul, !dbg !11
|
||||||
|
}
|
||||||
|
|
||||||
|
define i32 @foo.0(i32 %0) !dbg !12 {
|
||||||
|
init:
|
||||||
|
%add = add i32 %0, 1, !dbg !13
|
||||||
|
ret i32 %add, !dbg !13
|
||||||
|
}
|
||||||
|
|
||||||
|
!llvm.module.flags = !{!0, !1}
|
||||||
|
!llvm.dbg.cu = !{!2, !4}
|
||||||
|
|
||||||
|
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
|
||||||
|
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
|
||||||
|
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
|
||||||
|
!3 = !DIFile(filename: \"unknown\", directory: \"\")
|
||||||
|
!4 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
|
||||||
|
!5 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !9)
|
||||||
|
!6 = !DISubroutineType(flags: DIFlagPublic, types: !7)
|
||||||
|
!7 = !{!8}
|
||||||
|
!8 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
|
||||||
|
!9 = !{}
|
||||||
|
!10 = !DILocation(line: 1, column: 9, scope: !5)
|
||||||
|
!11 = !DILocation(line: 2, column: 12, scope: !5)
|
||||||
|
!12 = distinct !DISubprogram(name: \"foo.0\", linkageName: \"foo.0\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, retainedNodes: !9)
|
||||||
|
!13 = !DILocation(line: 1, column: 12, scope: !12)
|
||||||
|
"}.trim().to_string(),
|
||||||
|
legacy_pm: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
"primitives codegen"
|
||||||
|
)]
|
||||||
|
fn test_simple_call(inputs: Vec<CodeGenTestInput>) {
|
||||||
|
Target::initialize_all(&InitializationConfig::default());
|
||||||
|
|
||||||
|
for input in inputs {
|
||||||
|
let CodeGenTestInput { expected, legacy_pm } = input;
|
||||||
|
let source_1 = indoc! { "
|
||||||
a = foo(a)
|
a = foo(a)
|
||||||
return a * 2
|
return a * 2
|
||||||
"};
|
"};
|
||||||
let statements_1 = parse_program(source_1, Default::default()).unwrap();
|
let statements_1 = parse_program(source_1, Default::default()).unwrap();
|
||||||
|
|
||||||
let source_2 = indoc! { "
|
let source_2 = indoc! { "
|
||||||
return a + 1
|
return a + 1
|
||||||
"};
|
"};
|
||||||
let statements_2 = parse_program(source_2, Default::default()).unwrap();
|
let statements_2 = parse_program(source_2, Default::default()).unwrap();
|
||||||
|
|
||||||
let composer: TopLevelComposer = Default::default();
|
let composer: TopLevelComposer = Default::default();
|
||||||
let mut unifier = composer.unifier.clone();
|
let mut unifier = composer.unifier.clone();
|
||||||
let primitives = composer.primitives_ty;
|
let primitives = composer.primitives_ty;
|
||||||
let top_level = Arc::new(composer.make_top_level_context());
|
let top_level = Arc::new(composer.make_top_level_context());
|
||||||
unifier.top_level = Some(top_level.clone());
|
unifier.top_level = Some(top_level.clone());
|
||||||
|
|
||||||
let signature = FunSignature {
|
let signature = FunSignature {
|
||||||
args: vec![FuncArg { name: "a".into(), ty: primitives.int32, default_value: None }],
|
args: vec![FuncArg { name: "a".into(), ty: primitives.int32, default_value: None }],
|
||||||
ret: primitives.int32,
|
ret: primitives.int32,
|
||||||
vars: HashMap::new(),
|
vars: HashMap::new(),
|
||||||
};
|
};
|
||||||
let fun_ty = unifier.add_ty(TypeEnum::TFunc(signature.clone()));
|
let fun_ty = unifier.add_ty(TypeEnum::TFunc(signature.clone()));
|
||||||
let mut store = ConcreteTypeStore::new();
|
let mut store = ConcreteTypeStore::new();
|
||||||
let mut cache = HashMap::new();
|
let mut cache = HashMap::new();
|
||||||
let signature = store.from_signature(&mut unifier, &primitives, &signature, &mut cache);
|
let signature = store.from_signature(&mut unifier, &primitives, &signature, &mut cache);
|
||||||
let signature = store.add_cty(signature);
|
let signature = store.add_cty(signature);
|
||||||
|
|
||||||
let foo_id = top_level.definitions.read().len();
|
let foo_id = top_level.definitions.read().len();
|
||||||
top_level.definitions.write().push(Arc::new(RwLock::new(TopLevelDef::Function {
|
top_level.definitions.write().push(Arc::new(RwLock::new(TopLevelDef::Function {
|
||||||
name: "foo".to_string(),
|
name: "foo".to_string(),
|
||||||
simple_name: "foo".into(),
|
simple_name: "foo".into(),
|
||||||
signature: fun_ty,
|
signature: fun_ty,
|
||||||
var_id: vec![],
|
var_id: vec![],
|
||||||
instance_to_stmt: HashMap::new(),
|
instance_to_stmt: HashMap::new(),
|
||||||
instance_to_symbol: HashMap::new(),
|
instance_to_symbol: HashMap::new(),
|
||||||
resolver: None,
|
resolver: None,
|
||||||
codegen_callback: None,
|
codegen_callback: None,
|
||||||
loc: None,
|
loc: None,
|
||||||
})));
|
})));
|
||||||
|
|
||||||
let resolver = Resolver {
|
let resolver = Resolver {
|
||||||
id_to_type: HashMap::new(),
|
id_to_type: HashMap::new(),
|
||||||
id_to_def: RwLock::new(HashMap::new()),
|
id_to_def: RwLock::new(HashMap::new()),
|
||||||
class_names: Default::default(),
|
class_names: Default::default(),
|
||||||
};
|
};
|
||||||
resolver.add_id_def("foo".into(), DefinitionId(foo_id));
|
resolver.add_id_def("foo".into(), DefinitionId(foo_id));
|
||||||
let resolver = Arc::new(resolver) as Arc<dyn SymbolResolver + Send + Sync>;
|
let resolver = Arc::new(resolver) as Arc<dyn SymbolResolver + Send + Sync>;
|
||||||
|
|
||||||
if let TopLevelDef::Function { resolver: r, .. } =
|
if let TopLevelDef::Function { resolver: r, .. } =
|
||||||
&mut *top_level.definitions.read()[foo_id].write()
|
&mut *top_level.definitions.read()[foo_id].write()
|
||||||
{
|
{
|
||||||
*r = Some(resolver.clone());
|
*r = Some(resolver.clone());
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
let threads = vec![DefaultCodeGenerator::new("test".into(), 32).into()];
|
let threads = vec![DefaultCodeGenerator::new("test".into(), 32).into()];
|
||||||
let mut function_data = FunctionData {
|
let mut function_data = FunctionData {
|
||||||
resolver: resolver.clone(),
|
resolver: resolver.clone(),
|
||||||
bound_variables: Vec::new(),
|
bound_variables: Vec::new(),
|
||||||
return_type: Some(primitives.int32),
|
return_type: Some(primitives.int32),
|
||||||
};
|
};
|
||||||
let mut virtual_checks = Vec::new();
|
let mut virtual_checks = Vec::new();
|
||||||
let mut calls = HashMap::new();
|
let mut calls = HashMap::new();
|
||||||
let mut identifiers: HashSet<_> = ["a".into(), "foo".into()].iter().cloned().collect();
|
let mut identifiers: HashSet<_> = ["a".into(), "foo".into()].iter().cloned().collect();
|
||||||
let mut inferencer = Inferencer {
|
let mut inferencer = Inferencer {
|
||||||
top_level: &top_level,
|
top_level: &top_level,
|
||||||
function_data: &mut function_data,
|
function_data: &mut function_data,
|
||||||
unifier: &mut unifier,
|
unifier: &mut unifier,
|
||||||
variable_mapping: Default::default(),
|
variable_mapping: Default::default(),
|
||||||
primitives: &primitives,
|
primitives: &primitives,
|
||||||
virtual_checks: &mut virtual_checks,
|
virtual_checks: &mut virtual_checks,
|
||||||
calls: &mut calls,
|
calls: &mut calls,
|
||||||
defined_identifiers: identifiers.clone(),
|
defined_identifiers: identifiers.clone(),
|
||||||
in_handler: false,
|
in_handler: false,
|
||||||
};
|
};
|
||||||
inferencer.variable_mapping.insert("a".into(), inferencer.primitives.int32);
|
inferencer.variable_mapping.insert("a".into(), inferencer.primitives.int32);
|
||||||
inferencer.variable_mapping.insert("foo".into(), fun_ty);
|
inferencer.variable_mapping.insert("foo".into(), fun_ty);
|
||||||
|
|
||||||
let statements_1 = statements_1
|
let statements_1 = statements_1
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| inferencer.fold_stmt(v))
|
.map(|v| inferencer.fold_stmt(v))
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let calls1 = inferencer.calls.clone();
|
let calls1 = inferencer.calls.clone();
|
||||||
inferencer.calls.clear();
|
inferencer.calls.clear();
|
||||||
|
|
||||||
let statements_2 = statements_2
|
let statements_2 = statements_2
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| inferencer.fold_stmt(v))
|
.map(|v| inferencer.fold_stmt(v))
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if let TopLevelDef::Function { instance_to_stmt, .. } =
|
if let TopLevelDef::Function { instance_to_stmt, .. } =
|
||||||
&mut *top_level.definitions.read()[foo_id].write()
|
&mut *top_level.definitions.read()[foo_id].write()
|
||||||
{
|
{
|
||||||
instance_to_stmt.insert(
|
instance_to_stmt.insert(
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
FunInstance {
|
FunInstance {
|
||||||
body: Arc::new(statements_2),
|
body: Arc::new(statements_2),
|
||||||
calls: Arc::new(inferencer.calls.clone()),
|
calls: Arc::new(inferencer.calls.clone()),
|
||||||
subst: Default::default(),
|
subst: Default::default(),
|
||||||
unifier_id: 0,
|
unifier_id: 0,
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
inferencer.check_block(&statements_1, &mut identifiers).unwrap();
|
||||||
|
let top_level = Arc::new(TopLevelContext {
|
||||||
|
definitions: Arc::new(RwLock::new(std::mem::take(&mut *top_level.definitions.write()))),
|
||||||
|
unifiers: Arc::new(RwLock::new(vec![(unifier.get_shared_unifier(), primitives)])),
|
||||||
|
personality_symbol: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let task = CodeGenTask {
|
||||||
|
subst: Default::default(),
|
||||||
|
symbol_name: "testing".to_string(),
|
||||||
|
body: Arc::new(statements_1),
|
||||||
|
calls: Arc::new(calls1),
|
||||||
|
unifier_index: 0,
|
||||||
|
resolver,
|
||||||
|
signature,
|
||||||
|
store,
|
||||||
|
id: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let f = Arc::new(WithCall::new(Box::new(move |module| {
|
||||||
|
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());
|
||||||
|
})));
|
||||||
|
|
||||||
|
let llvm_options = CodeGenLLVMOptions {
|
||||||
|
opt_level: OptimizationLevel::Default,
|
||||||
|
legacy_pm,
|
||||||
|
target: CodeGenTargetMachineOptions::from_host_triple(),
|
||||||
|
emit_llvm: false,
|
||||||
|
};
|
||||||
|
let (registry, handles) = WorkerRegistry::create_workers(
|
||||||
|
threads,
|
||||||
|
top_level,
|
||||||
|
&llvm_options,
|
||||||
|
f
|
||||||
);
|
);
|
||||||
} else {
|
registry.add_task(task);
|
||||||
unreachable!()
|
registry.wait_tasks_complete(handles);
|
||||||
}
|
}
|
||||||
|
|
||||||
inferencer.check_block(&statements_1, &mut identifiers).unwrap();
|
|
||||||
let top_level = Arc::new(TopLevelContext {
|
|
||||||
definitions: Arc::new(RwLock::new(std::mem::take(&mut *top_level.definitions.write()))),
|
|
||||||
unifiers: Arc::new(RwLock::new(vec![(unifier.get_shared_unifier(), primitives)])),
|
|
||||||
personality_symbol: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
let task = CodeGenTask {
|
|
||||||
subst: Default::default(),
|
|
||||||
symbol_name: "testing".to_string(),
|
|
||||||
body: Arc::new(statements_1),
|
|
||||||
calls: Arc::new(calls1),
|
|
||||||
unifier_index: 0,
|
|
||||||
resolver,
|
|
||||||
signature,
|
|
||||||
store,
|
|
||||||
id: 0,
|
|
||||||
};
|
|
||||||
let f = Arc::new(WithCall::new(Box::new(|module| {
|
|
||||||
let expected = indoc! {"
|
|
||||||
; ModuleID = 'test'
|
|
||||||
source_filename = \"test\"
|
|
||||||
|
|
||||||
define i32 @testing(i32 %0) !dbg !5 {
|
|
||||||
init:
|
|
||||||
%call = call i32 @foo.0(i32 %0), !dbg !10
|
|
||||||
%mul = mul i32 %call, 2, !dbg !11
|
|
||||||
ret i32 %mul, !dbg !11
|
|
||||||
}
|
|
||||||
|
|
||||||
define i32 @foo.0(i32 %0) !dbg !12 {
|
|
||||||
init:
|
|
||||||
%add = add i32 %0, 1, !dbg !13
|
|
||||||
ret i32 %add, !dbg !13
|
|
||||||
}
|
|
||||||
|
|
||||||
!llvm.module.flags = !{!0, !1}
|
|
||||||
!llvm.dbg.cu = !{!2, !4}
|
|
||||||
|
|
||||||
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
|
|
||||||
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
|
|
||||||
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
|
|
||||||
!3 = !DIFile(filename: \"unknown\", directory: \"\")
|
|
||||||
!4 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
|
|
||||||
!5 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !9)
|
|
||||||
!6 = !DISubroutineType(flags: DIFlagPublic, types: !7)
|
|
||||||
!7 = !{!8}
|
|
||||||
!8 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
|
|
||||||
!9 = !{}
|
|
||||||
!10 = !DILocation(line: 1, column: 9, scope: !5)
|
|
||||||
!11 = !DILocation(line: 2, column: 12, scope: !5)
|
|
||||||
!12 = distinct !DISubprogram(name: \"foo.0\", linkageName: \"foo.0\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, retainedNodes: !9)
|
|
||||||
!13 = !DILocation(line: 1, column: 12, scope: !12)
|
|
||||||
"}
|
|
||||||
.trim();
|
|
||||||
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());
|
|
||||||
})));
|
|
||||||
|
|
||||||
let llvm_options = CodeGenLLVMOptions {
|
|
||||||
opt_level: OptimizationLevel::Default,
|
|
||||||
legacy_pm: true,
|
|
||||||
target: CodeGenTargetMachineOptions::from_host_triple(),
|
|
||||||
emit_llvm: false,
|
|
||||||
};
|
|
||||||
let (registry, handles) = WorkerRegistry::create_workers(
|
|
||||||
threads,
|
|
||||||
top_level,
|
|
||||||
&llvm_options,
|
|
||||||
f
|
|
||||||
);
|
|
||||||
registry.add_task(task);
|
|
||||||
registry.wait_tasks_complete(handles);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
memory_buffer::MemoryBuffer,
|
memory_buffer::MemoryBuffer,
|
||||||
passes::{PassManager, PassManagerBuilder},
|
passes::{PassBuilderOptions, PassManager, PassManagerBuilder},
|
||||||
targets::*,
|
targets::*,
|
||||||
OptimizationLevel,
|
OptimizationLevel,
|
||||||
};
|
};
|
||||||
|
@ -49,6 +49,10 @@ struct CommandLineArgs {
|
||||||
/// Whether to emit LLVM IR at the end of every module.
|
/// Whether to emit LLVM IR at the end of every module.
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
emit_llvm: bool,
|
emit_llvm: bool,
|
||||||
|
|
||||||
|
/// Whether to use LLVM's Legacy Pass Manager for optimizations.
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
legacy_pm: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_typevar_definition(
|
fn handle_typevar_definition(
|
||||||
|
@ -177,7 +181,13 @@ fn handle_assignment_pattern(
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cli = CommandLineArgs::parse();
|
let cli = CommandLineArgs::parse();
|
||||||
let CommandLineArgs { file_name, threads, opt_level, emit_llvm } = cli;
|
let CommandLineArgs {
|
||||||
|
file_name,
|
||||||
|
threads,
|
||||||
|
opt_level,
|
||||||
|
emit_llvm,
|
||||||
|
legacy_pm,
|
||||||
|
} = cli;
|
||||||
let opt_level = match opt_level {
|
let opt_level = match opt_level {
|
||||||
0 => OptimizationLevel::None,
|
0 => OptimizationLevel::None,
|
||||||
1 => OptimizationLevel::Less,
|
1 => OptimizationLevel::Less,
|
||||||
|
@ -272,7 +282,7 @@ fn main() {
|
||||||
|
|
||||||
let llvm_options = CodeGenLLVMOptions {
|
let llvm_options = CodeGenLLVMOptions {
|
||||||
opt_level,
|
opt_level,
|
||||||
legacy_pm: true,
|
legacy_pm,
|
||||||
target: CodeGenTargetMachineOptions::from_host_triple(),
|
target: CodeGenTargetMachineOptions::from_host_triple(),
|
||||||
emit_llvm,
|
emit_llvm,
|
||||||
};
|
};
|
||||||
|
@ -326,16 +336,28 @@ fn main() {
|
||||||
function_iter = func.get_next_function();
|
function_iter = func.get_next_function();
|
||||||
}
|
}
|
||||||
|
|
||||||
let builder = PassManagerBuilder::create();
|
|
||||||
builder.set_optimization_level(OptimizationLevel::Aggressive);
|
|
||||||
let passes = PassManager::create(());
|
|
||||||
builder.set_inliner_with_threshold(255);
|
|
||||||
builder.populate_module_pass_manager(&passes);
|
|
||||||
passes.run_on(&main);
|
|
||||||
|
|
||||||
let target_machine = llvm_options.target
|
let target_machine = llvm_options.target
|
||||||
.create_target_machine(llvm_options.opt_level)
|
.create_target_machine(llvm_options.opt_level)
|
||||||
.expect("couldn't create target machine");
|
.expect("couldn't create target machine");
|
||||||
|
|
||||||
|
if legacy_pm {
|
||||||
|
let builder = PassManagerBuilder::create();
|
||||||
|
builder.set_optimization_level(OptimizationLevel::Aggressive);
|
||||||
|
let passes = PassManager::create(());
|
||||||
|
builder.set_inliner_with_threshold(255);
|
||||||
|
builder.populate_module_pass_manager(&passes);
|
||||||
|
passes.run_on(&main);
|
||||||
|
} else {
|
||||||
|
let pass_options = PassBuilderOptions::create();
|
||||||
|
pass_options.set_merge_functions(true);
|
||||||
|
let result = main.run_passes("default<O3>", &target_machine, pass_options);
|
||||||
|
if let Err(err) = result {
|
||||||
|
println!("Failed to run optimization for module `main`");
|
||||||
|
println!("{}", err.to_string());
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
target_machine
|
target_machine
|
||||||
.write_to_file(&main, FileType::Object, Path::new("module.o"))
|
.write_to_file(&main, FileType::Object, Path::new("module.o"))
|
||||||
.expect("couldn't write module to file");
|
.expect("couldn't write module to file");
|
||||||
|
|
Loading…
Reference in New Issue