mirror of
https://github.com/LLVMParty/llvm-nanobind
synced 2026-06-21 13:43:38 +00:00
230 lines
7.8 KiB
Python
230 lines
7.8 KiB
Python
#!/usr/bin/env -S uv run
|
|
"""
|
|
Test: test_passbuilder
|
|
Tests for the new pass manager / PassBuilder API
|
|
|
|
This is the Python equivalent of tests/test_passbuilder.cpp.
|
|
Output should match the C++ golden master test.
|
|
|
|
LLVM Python APIs tested:
|
|
- llvm.PassBuilderOptions()
|
|
- options.set_verify_each(), set_debug_logging(), etc.
|
|
- Module.run_passes()
|
|
"""
|
|
|
|
import sys
|
|
import llvm
|
|
|
|
|
|
def build_optimizable_module(mod, ctx):
|
|
"""Build optimizable code into an existing module."""
|
|
mod.target_triple = "x86_64-unknown-linux-gnu"
|
|
|
|
i32 = ctx.types.i32
|
|
|
|
# Create: int add_inline(int x, int y) { return x + y; }
|
|
add_ty = ctx.types.function(i32, [i32, i32])
|
|
add_fn = mod.add_function("add_inline", add_ty)
|
|
add_fn.linkage = llvm.Linkage.Internal
|
|
|
|
add_entry = add_fn.append_basic_block("entry")
|
|
with add_entry.create_builder() as builder:
|
|
sum_val = builder.add(add_fn.get_param(0), add_fn.get_param(1), "sum")
|
|
builder.ret(sum_val)
|
|
|
|
# Create: int compute(int n) {
|
|
# int result = 0;
|
|
# for (int i = 0; i < n; i++) {
|
|
# result = add_inline(result, i);
|
|
# }
|
|
# return result;
|
|
# }
|
|
compute_ty = ctx.types.function(i32, [i32])
|
|
compute_fn = mod.add_function("compute", compute_ty)
|
|
|
|
entry = compute_fn.append_basic_block("entry")
|
|
loop = compute_fn.append_basic_block("loop")
|
|
loop_body = compute_fn.append_basic_block("loop_body")
|
|
exit_bb = compute_fn.append_basic_block("exit")
|
|
|
|
with entry.create_builder() as builder:
|
|
# Entry: initialize loop variables
|
|
result_ptr = builder.alloca(i32, "result_ptr")
|
|
i_ptr = builder.alloca(i32, "i_ptr")
|
|
builder.store(i32.constant(0), result_ptr)
|
|
builder.store(i32.constant(0), i_ptr)
|
|
builder.br(loop)
|
|
|
|
# Loop
|
|
builder.position_at_end(loop)
|
|
i_val = builder.load(i32, i_ptr, "i")
|
|
n = compute_fn.get_param(0)
|
|
cond = builder.icmp(llvm.IntPredicate.SLT, i_val, n, "cond")
|
|
builder.cond_br(cond, loop_body, exit_bb)
|
|
|
|
# Loop body
|
|
builder.position_at_end(loop_body)
|
|
result_val = builder.load(i32, result_ptr, "result")
|
|
i_val = builder.load(i32, i_ptr, "i2")
|
|
|
|
# Call add_inline
|
|
new_result = builder.call(add_ty, add_fn, [result_val, i_val], "new_result")
|
|
builder.store(new_result, result_ptr)
|
|
|
|
# Increment i
|
|
new_i = builder.add(i_val, i32.constant(1), "new_i")
|
|
builder.store(new_i, i_ptr)
|
|
builder.br(loop)
|
|
|
|
# Exit
|
|
builder.position_at_end(exit_bb)
|
|
final_result = builder.load(i32, result_ptr, "final_result")
|
|
builder.ret(final_result)
|
|
|
|
|
|
def count_instructions(mod):
|
|
"""Count instructions in a module."""
|
|
count = 0
|
|
for fn in mod.functions:
|
|
for bb in fn.basic_blocks:
|
|
for _ in bb.instructions:
|
|
count += 1
|
|
return count
|
|
|
|
|
|
def count_functions(mod):
|
|
"""Count functions in a module."""
|
|
return len(mod.functions)
|
|
|
|
|
|
def main():
|
|
# Get default target for running passes
|
|
triple = llvm.default_target_triple
|
|
target = llvm.Target.from_triple(triple)
|
|
if target is None:
|
|
print("; ERROR: Failed to get target", file=sys.stderr)
|
|
return 1
|
|
tm = llvm.TargetMachine.create(
|
|
target,
|
|
triple,
|
|
"generic",
|
|
"",
|
|
llvm.CodeGenOptLevel.Default,
|
|
llvm.RelocMode.Default,
|
|
llvm.CodeModel.Default,
|
|
)
|
|
|
|
print("; Test: test_passbuilder")
|
|
print("; Tests optimization pass running via PassBuilder API")
|
|
print(";")
|
|
|
|
with llvm.create_context() as ctx:
|
|
# ==========================================================================
|
|
# Test 1: Create PassBuilderOptions with various settings
|
|
# ==========================================================================
|
|
print("; Test 1: PassBuilderOptions configuration")
|
|
|
|
opts = llvm.PassBuilderOptions()
|
|
|
|
# Configure options
|
|
opts.set_verify_each(False)
|
|
opts.set_debug_logging(False)
|
|
opts.set_loop_interleaving(True)
|
|
opts.set_loop_vectorization(True)
|
|
opts.set_slp_vectorization(True)
|
|
opts.set_loop_unrolling(True)
|
|
opts.set_merge_functions(True)
|
|
opts.set_inliner_threshold(250)
|
|
|
|
print("; PassBuilderOptions created: yes")
|
|
print("; Options configured: yes")
|
|
|
|
# ==========================================================================
|
|
# Test 2: Optimization at O0 (no optimization)
|
|
# ==========================================================================
|
|
print(";")
|
|
print("; Test 2: O0 optimization (no optimization)")
|
|
|
|
with ctx.create_module("optimize_me") as mod_o0:
|
|
build_optimizable_module(mod_o0, ctx)
|
|
instr_before_o0 = count_instructions(mod_o0)
|
|
funcs_before_o0 = count_functions(mod_o0)
|
|
|
|
mod_o0.run_passes("default<O0>", target_machine=tm, options=opts)
|
|
|
|
instr_after_o0 = count_instructions(mod_o0)
|
|
funcs_after_o0 = count_functions(mod_o0)
|
|
|
|
print(
|
|
f"; Instructions before: {instr_before_o0}, after: {instr_after_o0}"
|
|
)
|
|
print(f"; Functions before: {funcs_before_o0}, after: {funcs_after_o0}")
|
|
print("; O0 passes ran successfully: yes")
|
|
|
|
# ==========================================================================
|
|
# Test 3: Optimization at O2
|
|
# ==========================================================================
|
|
print(";")
|
|
print("; Test 3: O2 optimization")
|
|
|
|
with ctx.create_module("optimize_me") as mod_o2:
|
|
build_optimizable_module(mod_o2, ctx)
|
|
instr_before_o2 = count_instructions(mod_o2)
|
|
funcs_before_o2 = count_functions(mod_o2)
|
|
|
|
mod_o2.run_passes("default<O2>", target_machine=tm, options=opts)
|
|
|
|
instr_after_o2 = count_instructions(mod_o2)
|
|
funcs_after_o2 = count_functions(mod_o2)
|
|
|
|
print(
|
|
f"; Instructions before: {instr_before_o2}, after: {instr_after_o2}"
|
|
)
|
|
print(f"; Functions before: {funcs_before_o2}, after: {funcs_after_o2}")
|
|
print("; O2 passes ran successfully: yes")
|
|
|
|
# At O2, the add_inline function should be inlined and possibly removed
|
|
add_fn = mod_o2.get_function("add_inline")
|
|
print(f"; add_inline function removed: {'no' if add_fn else 'yes'}")
|
|
|
|
# Verify the optimized module is still valid
|
|
if not mod_o2.verify():
|
|
print(
|
|
f"; ERROR: Optimized module verification failed: {mod_o2.verification_error}",
|
|
file=sys.stderr,
|
|
)
|
|
else:
|
|
print("; Optimized module verified: yes")
|
|
|
|
# ==========================================================================
|
|
# Test 4: Custom pass pipeline
|
|
# ==========================================================================
|
|
print(";")
|
|
print("; Test 4: Custom pass pipeline")
|
|
|
|
with ctx.create_module("optimize_me") as mod_custom:
|
|
build_optimizable_module(mod_custom, ctx)
|
|
instr_before_custom = count_instructions(mod_custom)
|
|
|
|
# Run just instcombine and simplifycfg
|
|
mod_custom.run_passes(
|
|
"instcombine,simplifycfg", target_machine=tm, options=opts
|
|
)
|
|
|
|
instr_after_custom = count_instructions(mod_custom)
|
|
print(
|
|
f"; Instructions before: {instr_before_custom}, after: {instr_after_custom}"
|
|
)
|
|
print("; Custom passes ran successfully: yes")
|
|
|
|
# Print the optimized module
|
|
print(";")
|
|
print("; Optimized module (after instcombine,simplifycfg):")
|
|
print(mod_custom.to_string(), end="")
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|