diff --git a/passes.go b/passes.go new file mode 100644 index 0000000..5a1eefd --- /dev/null +++ b/passes.go @@ -0,0 +1,101 @@ +package llvm + +/* +#include "llvm-c/Transforms/PassBuilder.h" +#include "llvm-c/Error.h" +#include +*/ +import "C" +import ( + "errors" + "unsafe" +) + +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 concious +// 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. +func (pbo PassBuilderOptions) SetVerifyEach(verifyEach bool) { + C.LLVMPassBuilderOptionsSetVerifyEach(pbo.C, boolToLLVMBool(verifyEach)) +} + +func (pbo PassBuilderOptions) SetDebugLogging(debugLogging bool) { + C.LLVMPassBuilderOptionsSetDebugLogging(pbo.C, boolToLLVMBool(debugLogging)) +} + +func (pbo PassBuilderOptions) SetLoopInterleaving(loopInterleaving bool) { + C.LLVMPassBuilderOptionsSetLoopInterleaving(pbo.C, boolToLLVMBool(loopInterleaving)) +} + +func (pbo PassBuilderOptions) SetLoopVectorization(loopVectorization bool) { + C.LLVMPassBuilderOptionsSetLoopVectorization(pbo.C, boolToLLVMBool(loopVectorization)) +} + +func (pbo PassBuilderOptions) SetSLPVectorization(slpVectorization bool) { + C.LLVMPassBuilderOptionsSetSLPVectorization(pbo.C, boolToLLVMBool(slpVectorization)) +} + +func (pbo PassBuilderOptions) SetLoopUnrolling(loopUnrolling bool) { + C.LLVMPassBuilderOptionsSetLoopUnrolling(pbo.C, boolToLLVMBool(loopUnrolling)) +} + +func (pbo PassBuilderOptions) SetForgetAllSCEVInLoopUnroll(forgetSCEV bool) { + C.LLVMPassBuilderOptionsSetForgetAllSCEVInLoopUnroll(pbo.C, boolToLLVMBool(forgetSCEV)) +} + +func (pbo PassBuilderOptions) SetLicmMssaOptCap(optCap uint) { + C.LLVMPassBuilderOptionsSetLicmMssaOptCap(pbo.C, C.unsigned(optCap)) +} + +func (pbo PassBuilderOptions) SetLicmMssaNoAccForPromotionCap(promotionCap uint) { + C.LLVMPassBuilderOptionsSetLicmMssaNoAccForPromotionCap(pbo.C, C.unsigned(promotionCap)) +} + +func (pbo PassBuilderOptions) SetCallGraphProfile(cgProfile bool) { + C.LLVMPassBuilderOptionsSetCallGraphProfile(pbo.C, boolToLLVMBool(cgProfile)) +} + +func (pbo PassBuilderOptions) SetMergeFunctions(mergeFuncs bool) { + C.LLVMPassBuilderOptionsSetMergeFunctions(pbo.C, boolToLLVMBool(mergeFuncs)) +} + +func (pbo PassBuilderOptions) Dispose() { + C.LLVMDisposePassBuilderOptions(pbo.C) +} diff --git a/passes_test.go b/passes_test.go new file mode 100644 index 0000000..7fb9bf3 --- /dev/null +++ b/passes_test.go @@ -0,0 +1,80 @@ +package llvm + +import "testing" + +func TestPasses(t *testing.T) { + InitializeNativeTarget() + InitializeNativeAsmPrinter() + + mod := NewModule("fac_module") + + fac_args := []Type{Int32Type()} + fac_type := FunctionType(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 := NewBuilder() + defer builder.Dispose() + + builder.SetInsertPointAtEnd(entry) + If := builder.CreateICmp(IntEQ, n, ConstInt(Int32Type(), 0, false), "cmptmp") + builder.CreateCondBr(If, iftrue, iffalse) + + builder.SetInsertPointAtEnd(iftrue) + res_iftrue := ConstInt(Int32Type(), 1, false) + builder.CreateBr(end) + + builder.SetInsertPointAtEnd(iffalse) + n_minus := builder.CreateSub(n, ConstInt(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(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) + } + }) +}