Skip to content

Commit 7733695

Browse files
authored
Add new pass builder API
1 parent ffd0534 commit 7733695

File tree

2 files changed

+211
-0
lines changed

2 files changed

+211
-0
lines changed

passes.go

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package llvm
2+
3+
/*
4+
#include "llvm-c/Transforms/PassBuilder.h"
5+
#include "llvm-c/Error.h"
6+
#include <stdlib.h>
7+
*/
8+
import "C"
9+
import (
10+
"errors"
11+
"unsafe"
12+
)
13+
14+
// PassBuilderOptions allows specifying several options for the PassBuilder.
15+
type PassBuilderOptions struct {
16+
C C.LLVMPassBuilderOptionsRef
17+
}
18+
19+
// NewPassBuilderOptions creates a PassBuilderOptions which can be used
20+
// to specify pass options for RunPasses.
21+
func NewPassBuilderOptions() (pbo PassBuilderOptions) {
22+
pbo.C = C.LLVMCreatePassBuilderOptions()
23+
return
24+
}
25+
26+
// RunPasses runs the specified optimization passes on the functions in the module.
27+
// `passes` is a comma separated list of pass names in the same format as llvm's
28+
// `opt -passes=...` command. Running `opt -print-passes` can list the available
29+
// passes.
30+
//
31+
// Some notable passes include:
32+
//
33+
// default<O0> -- run the default -O0 passes
34+
// default<O1> -- run the default -O1 passes
35+
// default<O2> -- run the default -O2 passes
36+
// default<O3> -- run the default -O3 passes
37+
// default<Os> -- run the default -Os passes, like -O2 but size conscious
38+
// default<Oz> -- run the default -Oz passes, optimizing for size above all else
39+
func (mod Module) RunPasses(passes string, tm TargetMachine, options PassBuilderOptions) error {
40+
cpasses := C.CString(passes)
41+
defer C.free(unsafe.Pointer(cpasses))
42+
43+
err := C.LLVMRunPasses(mod.C, cpasses, tm.C, options.C)
44+
if err != nil {
45+
cstr := C.LLVMGetErrorMessage(err)
46+
gstr := C.GoString(cstr)
47+
C.LLVMDisposeErrorMessage(cstr)
48+
49+
return errors.New(gstr)
50+
}
51+
return nil
52+
}
53+
54+
// SetVerifyEach toggles adding a VerifierPass to the PassBuilder,
55+
// ensuring all functions inside the module are valid. Useful for
56+
// debugging, but adds a significant amount of overhead.
57+
func (pbo PassBuilderOptions) SetVerifyEach(verifyEach bool) {
58+
C.LLVMPassBuilderOptionsSetVerifyEach(pbo.C, boolToLLVMBool(verifyEach))
59+
}
60+
61+
// SetDebugLogging toggles debug logging for the PassBuilder.
62+
func (pbo PassBuilderOptions) SetDebugLogging(debugLogging bool) {
63+
C.LLVMPassBuilderOptionsSetDebugLogging(pbo.C, boolToLLVMBool(debugLogging))
64+
}
65+
66+
// SetLoopInterleaving toggles loop interleaving, which is part of
67+
// loop vectorization.
68+
func (pbo PassBuilderOptions) SetLoopInterleaving(loopInterleaving bool) {
69+
C.LLVMPassBuilderOptionsSetLoopInterleaving(pbo.C, boolToLLVMBool(loopInterleaving))
70+
}
71+
72+
// SetLoopVectorization toggles loop vectorization.
73+
func (pbo PassBuilderOptions) SetLoopVectorization(loopVectorization bool) {
74+
C.LLVMPassBuilderOptionsSetLoopVectorization(pbo.C, boolToLLVMBool(loopVectorization))
75+
}
76+
77+
// SetSLPVectorization toggles Super-Word Level Parallelism vectorization,
78+
// whose goal is to combine multiple similar independent instructions into
79+
// a vector instruction.
80+
func (pbo PassBuilderOptions) SetSLPVectorization(slpVectorization bool) {
81+
C.LLVMPassBuilderOptionsSetSLPVectorization(pbo.C, boolToLLVMBool(slpVectorization))
82+
}
83+
84+
// SetLoopUnrolling toggles loop unrolling.
85+
func (pbo PassBuilderOptions) SetLoopUnrolling(loopUnrolling bool) {
86+
C.LLVMPassBuilderOptionsSetLoopUnrolling(pbo.C, boolToLLVMBool(loopUnrolling))
87+
}
88+
89+
// SetForgetAllSCEVInLoopUnroll toggles forgetting all SCEV (Scalar Evolution)
90+
// information in loop unrolling. Scalar Evolution is a pass that analyses
91+
// the how scalars evolve over iterations of a loop in order to optimize
92+
// the loop better. Forgetting this information can be useful in some cases.
93+
func (pbo PassBuilderOptions) SetForgetAllSCEVInLoopUnroll(forgetSCEV bool) {
94+
C.LLVMPassBuilderOptionsSetForgetAllSCEVInLoopUnroll(pbo.C, boolToLLVMBool(forgetSCEV))
95+
}
96+
97+
// SetLicmMssaOptCap sets a tuning option to cap the number of calls to
98+
// retrieve clobbering accesses in MemorySSA, in Loop Invariant Code Motion
99+
// optimization.
100+
// See [llvm::PipelineTuningOptions::LicmMssaOptCap].
101+
func (pbo PassBuilderOptions) SetLicmMssaOptCap(optCap uint) {
102+
C.LLVMPassBuilderOptionsSetLicmMssaOptCap(pbo.C, C.unsigned(optCap))
103+
}
104+
105+
// SetLicmMssaNoAccForPromotionCap sets a tuning option to cap the number of
106+
// promotions to scalars in Loop Invariant Code Motion with MemorySSA, if
107+
// the number of accesses is too large.
108+
// See [llvm::PipelineTuningOptions::LicmMssaNoAccForPromotionCap].
109+
func (pbo PassBuilderOptions) SetLicmMssaNoAccForPromotionCap(promotionCap uint) {
110+
C.LLVMPassBuilderOptionsSetLicmMssaNoAccForPromotionCap(pbo.C, C.unsigned(promotionCap))
111+
}
112+
113+
// SetCallGraphProfile toggles whether call graph profiling should be used.
114+
func (pbo PassBuilderOptions) SetCallGraphProfile(cgProfile bool) {
115+
C.LLVMPassBuilderOptionsSetCallGraphProfile(pbo.C, boolToLLVMBool(cgProfile))
116+
}
117+
118+
// SetMergeFunctions toggles finding functions which will generate identical
119+
// machine code by considering all pointer types to be equivalent. Once
120+
// identified, they will be folded by replacing a call to one with a call to a
121+
// bitcast of the other.
122+
func (pbo PassBuilderOptions) SetMergeFunctions(mergeFuncs bool) {
123+
C.LLVMPassBuilderOptionsSetMergeFunctions(pbo.C, boolToLLVMBool(mergeFuncs))
124+
}
125+
126+
// Dispose of the memory allocated for the PassBuilderOptions.
127+
func (pbo PassBuilderOptions) Dispose() {
128+
C.LLVMDisposePassBuilderOptions(pbo.C)
129+
}

passes_test.go

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package llvm
2+
3+
import "testing"
4+
5+
func TestPasses(t *testing.T) {
6+
InitializeNativeTarget()
7+
InitializeNativeAsmPrinter()
8+
9+
ctx := NewContext()
10+
11+
mod := ctx.NewModule("fac_module")
12+
13+
fac_args := []Type{ctx.Int32Type()}
14+
fac_type := FunctionType(ctx.Int32Type(), fac_args, false)
15+
fac := AddFunction(mod, "fac", fac_type)
16+
fac.SetFunctionCallConv(CCallConv)
17+
n := fac.Param(0)
18+
19+
entry := AddBasicBlock(fac, "entry")
20+
iftrue := AddBasicBlock(fac, "iftrue")
21+
iffalse := AddBasicBlock(fac, "iffalse")
22+
end := AddBasicBlock(fac, "end")
23+
24+
builder := ctx.NewBuilder()
25+
defer builder.Dispose()
26+
27+
builder.SetInsertPointAtEnd(entry)
28+
If := builder.CreateICmp(IntEQ, n, ConstInt(ctx.Int32Type(), 0, false), "cmptmp")
29+
builder.CreateCondBr(If, iftrue, iffalse)
30+
31+
builder.SetInsertPointAtEnd(iftrue)
32+
res_iftrue := ConstInt(ctx.Int32Type(), 1, false)
33+
builder.CreateBr(end)
34+
35+
builder.SetInsertPointAtEnd(iffalse)
36+
n_minus := builder.CreateSub(n, ConstInt(ctx.Int32Type(), 1, false), "subtmp")
37+
call_fac_args := []Value{n_minus}
38+
call_fac := builder.CreateCall(fac_type, fac, call_fac_args, "calltmp")
39+
res_iffalse := builder.CreateMul(n, call_fac, "multmp")
40+
builder.CreateBr(end)
41+
42+
builder.SetInsertPointAtEnd(end)
43+
res := builder.CreatePHI(ctx.Int32Type(), "result")
44+
phi_vals := []Value{res_iftrue, res_iffalse}
45+
phi_blocks := []BasicBlock{iftrue, iffalse}
46+
res.AddIncoming(phi_vals, phi_blocks)
47+
builder.CreateRet(res)
48+
49+
err := VerifyModule(mod, ReturnStatusAction)
50+
if err != nil {
51+
t.Errorf("Error verifying module: %s", err)
52+
return
53+
}
54+
55+
targ, err := GetTargetFromTriple(DefaultTargetTriple())
56+
if err != nil {
57+
t.Error(err)
58+
}
59+
60+
mt := targ.CreateTargetMachine(DefaultTargetTriple(), "", "", CodeGenLevelDefault, RelocDefault, CodeModelDefault)
61+
62+
pbo := NewPassBuilderOptions()
63+
defer pbo.Dispose()
64+
65+
t.Run("no error running default pass", func(t *testing.T) {
66+
err := mod.RunPasses("default<Os>", mt, pbo)
67+
if err != nil {
68+
t.Error(err)
69+
}
70+
})
71+
72+
t.Run("errors on unknown pass name", func(t *testing.T) {
73+
err := mod.RunPasses("badpassnamedoesnotexist", mt, pbo)
74+
if err == nil {
75+
t.Error("expecting error but got none")
76+
}
77+
78+
if err.Error() != "unknown pass name 'badpassnamedoesnotexist'" {
79+
t.Errorf("expected error about unknow pass name, instead got %s", err)
80+
}
81+
})
82+
}

0 commit comments

Comments
 (0)