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

feat: use llvm new pass manager api #352

Merged
merged 2 commits into from
Dec 1, 2023
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
7 changes: 7 additions & 0 deletions immix/llvm/memory_manager.cpp
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
74 changes: 72 additions & 2 deletions immix/llvm/plimmix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could add a comment about run_module_pass to explain:

  • what does this function do
  • which llvm API it calls(add a llvm link if possible)
  • the mapping between optimization level and integer.

// 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
Expand Down
114 changes: 77 additions & 37 deletions immix/llvm/plimmix_pass.cpp
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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<ImmixPass>
{
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious, why not use FB directly

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<Function>(immix_init_c.getCallee());
immix_init_f->setLinkage(GlobalValue::LinkageTypes::ExternalLinkage);
SmallVector<Type *, 1> 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<GlobalVariable>(g);
g_c->setLinkage(GlobalValue::LinkageTypes::ExternalWeakLinkage);
// auto g = M.getNamedGlobal(symbol);
SmallVector<Value *, 1> 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<Function>(immix_init_c.getCallee());
immix_init_f->setLinkage(GlobalValue::LinkageTypes::ExternalLinkage);
SmallVector<Type *, 1> 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<GlobalVariable>(g);
g_c->setLinkage(GlobalValue::LinkageTypes::ExternalWeakLinkage);
// auto g = M.getNamedGlobal(symbol);
SmallVector<Value *, 1> 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<Immix> 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused comment

char ImmixLegacy::ID = 0;
static RegisterPass<ImmixLegacy> X("plimmix", "plimmix gc Pass",
false /* Only looks at CFG */,
false /* Analysis Pass */);
27 changes: 17 additions & 10 deletions immix/llvm/plimmixprinter.cpp
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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();
Expand Down
24 changes: 15 additions & 9 deletions src/ast/builder/llvmbuilder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ pub struct LLVMBuilder<'a, 'ctx> {
optimized: Arc<RefCell<bool>>,
used: Arc<RefCell<Vec<FunctionValue<'ctx>>>>,
difile: Cell<DIFile<'ctx>>,
optlevel: OptimizationLevel,
}

pub fn get_target_machine(level: OptimizationLevel) -> TargetMachine {
Expand Down Expand Up @@ -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 {
Expand All @@ -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(
Expand Down Expand Up @@ -1513,24 +1516,27 @@ 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| {
f.set_gc("shadow-stack");
});
} 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<Module> =
unsafe { inkwell::passes::PassManager::new(ptr) };

// let ptr = unsafe { llvm_sys::core::LLVMCreatePassManager() };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove unused comments.

// let mpm: inkwell::passes::PassManager<Module> =
// 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;
}
Expand Down
1 change: 1 addition & 0 deletions src/ast/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pub fn compile_dry_file(db: &dyn Db, docs: FileCompileInput) -> Option<ModWrappe
docs.get_emit_params(db),
docs.docs(db),
docs.config(db),
docs.opt(db),
);
log::trace!("entering emit");
Some(program.emit(db))
Expand Down
4 changes: 3 additions & 1 deletion src/ast/node/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ impl Program {
UnsafeWrapper::new(global_mthd_map),
UnsafeWrapper::new(global_macro_map),
self.is_active_file(db),
self.opt(db),
);

let nn = p.node(db).node(db);
Expand Down Expand Up @@ -549,7 +550,8 @@ pub fn emit_file(db: &dyn Db, params: ProgramEmitParam) -> 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()
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/ast/node/program/salsa_structs.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -34,6 +35,7 @@ pub struct ProgramEmitParam {
#[return_ref]
pub macro_table: UnsafeWrapper<FxHashMap<String, Arc<MacroNode>>>,
pub is_active_file: bool,
pub opt: HashOptimizationLevel,
}

pub type MthdTableWrapper =
Expand Down Expand Up @@ -64,4 +66,5 @@ pub struct Program {
pub params: EmitParams,
pub docs: MemDocsInput,
pub config: Config,
pub opt: HashOptimizationLevel,
}
Loading
Loading