diff --git a/immix/llvm/memory_manager.cpp b/immix/llvm/memory_manager.cpp index 0e59827b2..960835dba 100644 --- a/immix/llvm/memory_manager.cpp +++ b/immix/llvm/memory_manager.cpp @@ -1,3 +1,10 @@ +/* + LLVM memory manager for Pivot Lang + + This is very muck a work in progress. + We are using this to get immix working with JIT. +*/ + #include "llvm-c/ExecutionEngine.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm-c/Core.h" diff --git a/immix/llvm/plimmix.cpp b/immix/llvm/plimmix.cpp index cdf364f1d..8292e636c 100644 --- a/immix/llvm/plimmix.cpp +++ b/immix/llvm/plimmix.cpp @@ -4,11 +4,20 @@ #include "llvm/Support/Compiler.h" #include "plimmixprinter.cpp" #include "plimmix_pass.cpp" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" + +#include "llvm-c/Types.h" +#include "llvm-c/Transforms/PassBuilder.h" using namespace llvm; namespace { + /* + This is the GC strategy for immix. + It is used to register our immix GC with LLVM. + */ class LLVM_LIBRARY_VISIBILITY PLImmixGC : public GCStrategy { public: @@ -29,10 +38,71 @@ extern "C" void LLVMLinkPLImmixGC() } #include "llvm-c/Transforms/PassManagerBuilder.h" -extern "C" void add_module_pass(llvm::legacy::PassManagerBase * PB) { - PB->add(new Immix()); +extern "C" void add_module_pass(llvm::legacy::PassManagerBase *PB) { + PB->add(new ImmixLegacy()); + +} +/* + param: opt opt level + + The new LLVM Pass Manager does not have official C bindings yet. + So we have to write one ourselves. +*/ +extern "C" void run_module_pass(LLVMModuleRef M, int opt) { + // These must be declared in this order so that they are destroyed in the + // correct order due to inter-analysis-manager references. + LoopAnalysisManager LAM; + FunctionAnalysisManager FAM; + CGSCCAnalysisManager CGAM; + ModuleAnalysisManager MAM; + + // Create the new pass manager builder. + // Take a look at the PassBuilder constructor parameters for more + // customization, e.g. specifying a TargetMachine or various debugging + // options. + PassBuilder PB; + + // Register all the basic analyses with the managers. + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + auto O = OptimizationLevel::O2; + switch (opt) + { + case 0: + O = OptimizationLevel::O0; + break; + case 1: + O = OptimizationLevel::O1; + break; + case 2: + O = OptimizationLevel::O2; + break; + case 3: + O = OptimizationLevel::O3; + break; + default: + break; + } + + // Create the pass manager. + // This one corresponds to a typical -O2 optimization pipeline. + ModulePassManager MPM = PB.buildPerModuleDefaultPipeline(O); + MPM.addPass(ImmixPass()); + + // Optimize the IR! + MPM.run(*unwrap(M), MAM); + + } + +/* + Shadow stack implementation for immix. (used in JIT mode, but may not work now) +*/ extern "C" { /// The map for a single function's stack frame. One of these is diff --git a/immix/llvm/plimmix_pass.cpp b/immix/llvm/plimmix_pass.cpp index ba6b3daf0..3d6cba37c 100644 --- a/immix/llvm/plimmix_pass.cpp +++ b/immix/llvm/plimmix_pass.cpp @@ -1,3 +1,8 @@ +/* + Some LLVM passes helpful for + integrating immix with LLVM +*/ + #include "llvm/IR/PassManager.h" #include "llvm/IR/IRBuilder.h" #include "llvm/Transforms/Utils/ModuleUtils.h" @@ -9,50 +14,85 @@ using namespace llvm; namespace { - struct Immix : public ModulePass + void immixPassLogic(Module &M); + // The new pass manager plugin + class ImmixPass : public PassInfoMixin { static char ID; - Immix() : ModulePass(ID) {} + + public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + }; + PreservedAnalyses ImmixPass::run(Module &M, ModuleAnalysisManager &AM) + { + immixPassLogic(M); + return PreservedAnalyses::all(); + } + + /* + This pass helps integrate immix with LLVM. + + It does the following: + - Sets the GC name to "plimmix" for all functions + - Adds a call to immix_gc_init in the global constructor + - Adds a global variable declaration for the module stack map + + However, it does not generate the stack map. This is done by the + immix compiler plugin. + + Also note that mauch more work is needed to get immix working with + LLVM. Besides the pass, you need to: + - Implement visit functions for all complex types + - insert stack roots for all heap pointers + - replace all malloc calls with immix::alloc + ... + */ + void immixPassLogic(Module &M) + { + for (auto FB = M.functions().begin(), FE = M.functions().end(); FB != FE; ++FB) + { + Function *FV = &*FB; + FV->setGC("plimmix"); + } + // auto gc_init_c = M.getOrInsertFunction("__gc_init_stackmap", Type::getVoidTy(M.getContext())); + auto immix_init_c = M.getOrInsertFunction("immix_gc_init", Type::getVoidTy(M.getContext()), PointerType::get(IntegerType::get(M.getContext(), 8), 0)); + auto immix_init_f = cast(immix_init_c.getCallee()); + immix_init_f->setLinkage(GlobalValue::LinkageTypes::ExternalLinkage); + SmallVector argTypes; + argTypes.push_back(PointerType::get(IntegerType::get(M.getContext(), 8), 0)); + std::string symbol; + symbol += "_IMMIX_GC_MAP_"; + symbol += M.getSourceFileName(); + auto g = M.getOrInsertGlobal(symbol, Type::getInt8Ty(M.getContext())); + GlobalVariable *g_c = cast(g); + g_c->setLinkage(GlobalValue::LinkageTypes::ExternalWeakLinkage); + // auto g = M.getNamedGlobal(symbol); + SmallVector assertArgs; + assertArgs.push_back(g); + Function *gc_init_f; + std::tie(gc_init_f, std::ignore) = createSanitizerCtorAndInitFunctions(M, "__gc_init_stackmap", "immix_gc_init", argTypes, assertArgs); + gc_init_f->setLinkage(GlobalValue::LinkageTypes::InternalLinkage); + // appendToCompilerUsed(M, gc_init_f); + appendToGlobalCtors(M, gc_init_f, 1000); + } + + // The old pass manager plugin + struct ImmixLegacy : public ModulePass + { + static char ID; + ImmixLegacy() : ModulePass(ID) {} bool runOnModule(Module &M) override { - for (auto FB = M.functions().begin(), FE = M.functions().end(); FB != FE; ++FB) - { - Function *FV = &*FB; - FV->setGC("plimmix"); - } - // auto gc_init_c = M.getOrInsertFunction("__gc_init_stackmap", Type::getVoidTy(M.getContext())); - auto immix_init_c = M.getOrInsertFunction("immix_gc_init", Type::getVoidTy(M.getContext()), PointerType::get(IntegerType::get(M.getContext(), 8), 0)); - auto immix_init_f = cast(immix_init_c.getCallee()); - immix_init_f->setLinkage(GlobalValue::LinkageTypes::ExternalLinkage); - SmallVector argTypes; - argTypes.push_back(PointerType::get(IntegerType::get(M.getContext(), 8), 0)); - std::string symbol; - symbol += "_IMMIX_GC_MAP_"; - symbol += M.getSourceFileName(); - auto g = M.getOrInsertGlobal(symbol, Type::getInt8Ty(M.getContext())); - GlobalVariable *g_c = cast(g); - g_c->setLinkage(GlobalValue::LinkageTypes::ExternalWeakLinkage); - // auto g = M.getNamedGlobal(symbol); - SmallVector assertArgs; - assertArgs.push_back(g); - Function *gc_init_f; - std::tie(gc_init_f, std::ignore) = createSanitizerCtorAndInitFunctions(M, "__gc_init_stackmap", "immix_gc_init", argTypes, assertArgs); - gc_init_f->setLinkage(GlobalValue::LinkageTypes::InternalLinkage); - // appendToCompilerUsed(M, gc_init_f); - appendToGlobalCtors(M, gc_init_f, 1000); + immixPassLogic(M); return true; } }; } -char Immix::ID = 0; -static RegisterPass X("plimmix", "plimmix gc Pass", - false /* Only looks at CFG */, - false /* Analysis Pass */); - -// static llvm::RegisterStandardPasses Y( -// llvm::PassManagerBuilder::EP_EarlyAsPossible, -// [](const llvm::PassManagerBuilder &Builder, -// llvm::legacy::PassManagerBase &PM) -// { PM.add(new Immix()); }); + +// char ImmixPass::ID = 0; +char ImmixLegacy::ID = 0; +static RegisterPass X("plimmix", "plimmix gc Pass", + false /* Only looks at CFG */, + false /* Analysis Pass */); diff --git a/immix/llvm/plimmixprinter.cpp b/immix/llvm/plimmixprinter.cpp index d86a02112..f4f54f86e 100644 --- a/immix/llvm/plimmixprinter.cpp +++ b/immix/llvm/plimmixprinter.cpp @@ -1,9 +1,22 @@ #include "llvm/CodeGen/GCMetadataPrinter.h" #include "llvm/Support/Compiler.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/DataLayout.h" +// #include "llvm/Target/TargetAsmInfo.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/IR/GCStrategy.h" +#include "llvm/CodeGen/GCMetadata.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Target/TargetLoweringObjectFile.h" +#include "llvm/MC/MCAsmInfo.h" using namespace llvm; namespace { + /* + Stackmap printer for immix. + */ class LLVM_LIBRARY_VISIBILITY PLImmixGCPrinter : public GCMetadataPrinter { public: @@ -17,22 +30,16 @@ namespace P("plimmix", "pivot-lang immix garbage collector."); } -#include "llvm/CodeGen/AsmPrinter.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/DataLayout.h" -// #include "llvm/Target/TargetAsmInfo.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/IR/GCStrategy.h" -#include "llvm/CodeGen/GCMetadata.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/Target/TargetLoweringObjectFile.h" -#include "llvm/MC/MCAsmInfo.h" void PLImmixGCPrinter::beginAssembly(Module &M, GCModuleInfo &Info, AsmPrinter &AP) { // Nothing to do. } + +/* + We need to emit the stack map in the data section. +*/ void PLImmixGCPrinter::finishAssembly(Module &M, GCModuleInfo &Info, AsmPrinter &AP) { unsigned IntPtrSize = AP.getPointerSize(); diff --git a/src/ast/builder/llvmbuilder.rs b/src/ast/builder/llvmbuilder.rs index 5d75a87dc..4e838a8d4 100644 --- a/src/ast/builder/llvmbuilder.rs +++ b/src/ast/builder/llvmbuilder.rs @@ -144,6 +144,7 @@ pub struct LLVMBuilder<'a, 'ctx> { optimized: Arc>, used: Arc>>>, difile: Cell>, + optlevel: OptimizationLevel, } pub fn get_target_machine(level: OptimizationLevel) -> TargetMachine { @@ -184,6 +185,7 @@ impl<'a, 'ctx> LLVMBuilder<'a, 'ctx> { dibuilder: &'a DebugInfoBuilder<'ctx>, diunit: &'a DICompileUnit<'ctx>, tm: &'a TargetMachine, + opt: OptimizationLevel, ) -> Self { module.set_triple(&TargetMachine::get_default_triple()); Self { @@ -204,6 +206,7 @@ impl<'a, 'ctx> LLVMBuilder<'a, 'ctx> { optimized: Arc::new(RefCell::new(false)), used: Default::default(), difile: Cell::new(diunit.get_file()), + optlevel: opt, } } fn alloc_raw( @@ -1513,7 +1516,6 @@ impl<'a, 'ctx> LLVMBuilder<'a, 'ctx> { .add_global(used_arr.get_type(), None, "llvm.used"); used_global.set_linkage(Linkage::Appending); used_global.set_initializer(&used_arr); - if crate::ast::jit_config::IS_JIT.load(std::sync::atomic::Ordering::Relaxed) { // jit is using shadow stack, skip immix pass self.module.get_functions().for_each(|f| { @@ -1521,16 +1523,20 @@ impl<'a, 'ctx> LLVMBuilder<'a, 'ctx> { }); } else { extern "C" { - fn add_module_pass(ptr: *mut u8); + // fn add_module_pass(ptr: *mut u8); + fn run_module_pass(m: *mut u8, tm: i32); } - let ptr = unsafe { llvm_sys::core::LLVMCreatePassManager() }; - let mpm: inkwell::passes::PassManager = - unsafe { inkwell::passes::PassManager::new(ptr) }; - + // let ptr = unsafe { llvm_sys::core::LLVMCreatePassManager() }; + // let mpm: inkwell::passes::PassManager = + // unsafe { inkwell::passes::PassManager::new(ptr) }; + + // unsafe { + // add_module_pass(ptr as _); + // }; + // mpm.run_on(self.module); unsafe { - add_module_pass(ptr as _); - }; - mpm.run_on(self.module); + run_module_pass(self.module.as_mut_ptr() as _, self.optlevel as i32); + } } *self.optimized.borrow_mut() = true; } diff --git a/src/ast/compiler.rs b/src/ast/compiler.rs index c67bc5ce5..5a3cdf61a 100644 --- a/src/ast/compiler.rs +++ b/src/ast/compiler.rs @@ -92,6 +92,7 @@ pub fn compile_dry_file(db: &dyn Db, docs: FileCompileInput) -> Option ModWrapper { unreachable!("llvm feature is not enabled"); #[cfg(feature = "llvm")] { - let builder = LLVMBuilder::new(context, &a, &b, &c, &d, &e); + let builder = + LLVMBuilder::new(context, &a, &b, &c, &d, &e, params.opt(db).to_llvm()); builder.into() } } diff --git a/src/ast/node/program/salsa_structs.rs b/src/ast/node/program/salsa_structs.rs index 99394a7f1..637234140 100644 --- a/src/ast/node/program/salsa_structs.rs +++ b/src/ast/node/program/salsa_structs.rs @@ -1,3 +1,4 @@ +use crate::ast::compiler::HashOptimizationLevel; use crate::ast::node::macro_nodes::MacroNode; use crate::ast::node::NodeEnum; use crate::ast::pltype::FNValue; @@ -34,6 +35,7 @@ pub struct ProgramEmitParam { #[return_ref] pub macro_table: UnsafeWrapper>>, pub is_active_file: bool, + pub opt: HashOptimizationLevel, } pub type MthdTableWrapper = @@ -64,4 +66,5 @@ pub struct Program { pub params: EmitParams, pub docs: MemDocsInput, pub config: Config, + pub opt: HashOptimizationLevel, } diff --git a/src/lsp/mem_docs.rs b/src/lsp/mem_docs.rs index ff1b82ae9..683e32feb 100644 --- a/src/lsp/mem_docs.rs +++ b/src/lsp/mem_docs.rs @@ -10,7 +10,7 @@ use rustc_hash::FxHashMap; use crate::{ ast::{ - compiler::{ActionType, Options}, + compiler::{ActionType, HashOptimizationLevel, Options}, range::Pos, }, nomparser::SourceProgram, @@ -56,6 +56,7 @@ pub struct FileCompileInput { pub modpath: String, pub docs: MemDocsInput, pub config: Config, + pub opt: HashOptimizationLevel, } #[salsa::tracked] impl FileCompileInput { @@ -176,6 +177,7 @@ impl MemDocsInput { parant.to_str().unwrap().to_string(), self, config, + self.op(db).optimization, )) } }