diff --git a/passes.go b/passes.go new file mode 100644 index 0000000..be2514d --- /dev/null +++ b/passes.go @@ -0,0 +1,129 @@ +package llvm + +/* +#include "llvm-c/Transforms/PassBuilder.h" +#include "llvm-c/Error.h" +#include +*/ +import "C" +import ( + "errors" + "unsafe" +) + +// PassBuilderOptions allows specifying several options for the PassBuilder. +type PassBuilderOptions struct { + C C.LLVMPassBuilderOptionsRef +} + +// NewPassBuilderOptions creates a PassBuilderOptions which can be used +// to specify pass options for RunPasses. +func NewPassBuilderOptions() (pbo PassBuilderOptions) { + pbo.C = C.LLVMCreatePassBuilderOptions() + return +} + +// RunPasses runs the specified optimization passes on the functions in the module. +// `passes` is a comma separated list of pass names in the same format as llvm's +// `opt -passes=...` command. Running `opt -print-passes` can list the available +// passes. +// +// Some notable passes include: +// +// default -- run the default -O0 passes +// default -- run the default -O1 passes +// default -- run the default -O2 passes +// default -- run the default -O3 passes +// default -- run the default -Os passes, like -O2 but size conscious +// default -- run the default -Oz passes, optimizing for size above all else +func (mod Module) RunPasses(passes string, tm TargetMachine, options PassBuilderOptions) error { + cpasses := C.CString(passes) + defer C.free(unsafe.Pointer(cpasses)) + + err := C.LLVMRunPasses(mod.C, cpasses, tm.C, options.C) + if err != nil { + cstr := C.LLVMGetErrorMessage(err) + gstr := C.GoString(cstr) + C.LLVMDisposeErrorMessage(cstr) + + return errors.New(gstr) + } + return nil +} + +// SetVerifyEach toggles adding a VerifierPass to the PassBuilder, +// ensuring all functions inside the module are valid. Useful for +// debugging, but adds a significant amount of overhead. +func (pbo PassBuilderOptions) SetVerifyEach(verifyEach bool) { + C.LLVMPassBuilderOptionsSetVerifyEach(pbo.C, boolToLLVMBool(verifyEach)) +} + +// SetDebugLogging toggles debug logging for the PassBuilder. +func (pbo PassBuilderOptions) SetDebugLogging(debugLogging bool) { + C.LLVMPassBuilderOptionsSetDebugLogging(pbo.C, boolToLLVMBool(debugLogging)) +} + +// SetLoopInterleaving toggles loop interleaving, which is part of +// loop vectorization. +func (pbo PassBuilderOptions) SetLoopInterleaving(loopInterleaving bool) { + C.LLVMPassBuilderOptionsSetLoopInterleaving(pbo.C, boolToLLVMBool(loopInterleaving)) +} + +// SetLoopVectorization toggles loop vectorization. +func (pbo PassBuilderOptions) SetLoopVectorization(loopVectorization bool) { + C.LLVMPassBuilderOptionsSetLoopVectorization(pbo.C, boolToLLVMBool(loopVectorization)) +} + +// SetSLPVectorization toggles Super-Word Level Parallelism vectorization, +// whose goal is to combine multiple similar independent instructions into +// a vector instruction. +func (pbo PassBuilderOptions) SetSLPVectorization(slpVectorization bool) { + C.LLVMPassBuilderOptionsSetSLPVectorization(pbo.C, boolToLLVMBool(slpVectorization)) +} + +// SetLoopUnrolling toggles loop unrolling. +func (pbo PassBuilderOptions) SetLoopUnrolling(loopUnrolling bool) { + C.LLVMPassBuilderOptionsSetLoopUnrolling(pbo.C, boolToLLVMBool(loopUnrolling)) +} + +// SetForgetAllSCEVInLoopUnroll toggles forgetting all SCEV (Scalar Evolution) +// information in loop unrolling. Scalar Evolution is a pass that analyses +// the how scalars evolve over iterations of a loop in order to optimize +// the loop better. Forgetting this information can be useful in some cases. +func (pbo PassBuilderOptions) SetForgetAllSCEVInLoopUnroll(forgetSCEV bool) { + C.LLVMPassBuilderOptionsSetForgetAllSCEVInLoopUnroll(pbo.C, boolToLLVMBool(forgetSCEV)) +} + +// SetLicmMssaOptCap sets a tuning option to cap the number of calls to +// retrieve clobbering accesses in MemorySSA, in Loop Invariant Code Motion +// optimization. +// See [llvm::PipelineTuningOptions::LicmMssaOptCap]. +func (pbo PassBuilderOptions) SetLicmMssaOptCap(optCap uint) { + C.LLVMPassBuilderOptionsSetLicmMssaOptCap(pbo.C, C.unsigned(optCap)) +} + +// SetLicmMssaNoAccForPromotionCap sets a tuning option to cap the number of +// promotions to scalars in Loop Invariant Code Motion with MemorySSA, if +// the number of accesses is too large. +// See [llvm::PipelineTuningOptions::LicmMssaNoAccForPromotionCap]. +func (pbo PassBuilderOptions) SetLicmMssaNoAccForPromotionCap(promotionCap uint) { + C.LLVMPassBuilderOptionsSetLicmMssaNoAccForPromotionCap(pbo.C, C.unsigned(promotionCap)) +} + +// SetCallGraphProfile toggles whether call graph profiling should be used. +func (pbo PassBuilderOptions) SetCallGraphProfile(cgProfile bool) { + C.LLVMPassBuilderOptionsSetCallGraphProfile(pbo.C, boolToLLVMBool(cgProfile)) +} + +// SetMergeFunctions toggles finding functions which will generate identical +// machine code by considering all pointer types to be equivalent. Once +// identified, they will be folded by replacing a call to one with a call to a +// bitcast of the other. +func (pbo PassBuilderOptions) SetMergeFunctions(mergeFuncs bool) { + C.LLVMPassBuilderOptionsSetMergeFunctions(pbo.C, boolToLLVMBool(mergeFuncs)) +} + +// Dispose of the memory allocated for the PassBuilderOptions. +func (pbo PassBuilderOptions) Dispose() { + C.LLVMDisposePassBuilderOptions(pbo.C) +} diff --git a/passes_test.go b/passes_test.go new file mode 100644 index 0000000..3cbc13b --- /dev/null +++ b/passes_test.go @@ -0,0 +1,82 @@ +package llvm + +import "testing" + +func TestPasses(t *testing.T) { + InitializeNativeTarget() + InitializeNativeAsmPrinter() + + ctx := NewContext() + + mod := ctx.NewModule("fac_module") + + fac_args := []Type{ctx.Int32Type()} + fac_type := FunctionType(ctx.Int32Type(), fac_args, false) + fac := AddFunction(mod, "fac", fac_type) + fac.SetFunctionCallConv(CCallConv) + n := fac.Param(0) + + entry := AddBasicBlock(fac, "entry") + iftrue := AddBasicBlock(fac, "iftrue") + iffalse := AddBasicBlock(fac, "iffalse") + end := AddBasicBlock(fac, "end") + + builder := ctx.NewBuilder() + defer builder.Dispose() + + builder.SetInsertPointAtEnd(entry) + If := builder.CreateICmp(IntEQ, n, ConstInt(ctx.Int32Type(), 0, false), "cmptmp") + builder.CreateCondBr(If, iftrue, iffalse) + + builder.SetInsertPointAtEnd(iftrue) + res_iftrue := ConstInt(ctx.Int32Type(), 1, false) + builder.CreateBr(end) + + builder.SetInsertPointAtEnd(iffalse) + n_minus := builder.CreateSub(n, ConstInt(ctx.Int32Type(), 1, false), "subtmp") + call_fac_args := []Value{n_minus} + call_fac := builder.CreateCall(fac_type, fac, call_fac_args, "calltmp") + res_iffalse := builder.CreateMul(n, call_fac, "multmp") + builder.CreateBr(end) + + builder.SetInsertPointAtEnd(end) + res := builder.CreatePHI(ctx.Int32Type(), "result") + phi_vals := []Value{res_iftrue, res_iffalse} + phi_blocks := []BasicBlock{iftrue, iffalse} + res.AddIncoming(phi_vals, phi_blocks) + builder.CreateRet(res) + + err := VerifyModule(mod, ReturnStatusAction) + if err != nil { + t.Errorf("Error verifying module: %s", err) + return + } + + targ, err := GetTargetFromTriple(DefaultTargetTriple()) + if err != nil { + t.Error(err) + } + + mt := targ.CreateTargetMachine(DefaultTargetTriple(), "", "", CodeGenLevelDefault, RelocDefault, CodeModelDefault) + + pbo := NewPassBuilderOptions() + defer pbo.Dispose() + + t.Run("no error running default pass", func(t *testing.T) { + err := mod.RunPasses("default", mt, pbo) + if err != nil { + t.Error(err) + } + }) + + t.Run("errors on unknown pass name", func(t *testing.T) { + err := mod.RunPasses("badpassnamedoesnotexist", mt, pbo) + if err == nil { + t.Error("expecting error but got none") + } + + if err.Error() != "unknown pass name 'badpassnamedoesnotexist'" { + t.Errorf("expected error about unknow pass name, instead got %s", err) + } + }) +}