Skip to content

Commit

Permalink
Add new pass builder API
Browse files Browse the repository at this point in the history
  • Loading branch information
rj45 authored Sep 20, 2023
1 parent ffd0534 commit 7733695
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 0 deletions.
129 changes: 129 additions & 0 deletions passes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package llvm

/*
#include "llvm-c/Transforms/PassBuilder.h"
#include "llvm-c/Error.h"
#include <stdlib.h>
*/
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<O0> -- run the default -O0 passes
// default<O1> -- run the default -O1 passes
// default<O2> -- run the default -O2 passes
// default<O3> -- run the default -O3 passes
// default<Os> -- run the default -Os passes, like -O2 but size conscious
// default<Oz> -- 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)
}
82 changes: 82 additions & 0 deletions passes_test.go
Original file line number Diff line number Diff line change
@@ -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<Os>", 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)
}
})
}

0 comments on commit 7733695

Please sign in to comment.