mirror of
https://github.com/LLVMParty/llvm-nanobind
synced 2026-06-21 13:43:38 +00:00
315 lines
11 KiB
C++
315 lines
11 KiB
C++
/**
|
|
* Test: test_passbuilder
|
|
* Tests for the new pass manager / PassBuilder API
|
|
*
|
|
* This test demonstrates:
|
|
* - Creating PassBuilderOptions
|
|
* - Running optimization passes on modules
|
|
* - Different optimization levels (O0, O1, O2, O3, Os, Oz)
|
|
* - Custom pass pipelines
|
|
*
|
|
* LLVM-C APIs tested:
|
|
* - LLVMCreatePassBuilderOptions
|
|
* - LLVMDisposePassBuilderOptions
|
|
* - LLVMPassBuilderOptionsSetVerifyEach
|
|
* - LLVMPassBuilderOptionsSetDebugLogging
|
|
* - LLVMPassBuilderOptionsSetLoopInterleaving
|
|
* - LLVMPassBuilderOptionsSetLoopVectorization
|
|
* - LLVMPassBuilderOptionsSetSLPVectorization
|
|
* - LLVMPassBuilderOptionsSetLoopUnrolling
|
|
* - LLVMPassBuilderOptionsSetMergeFunctions
|
|
* - LLVMPassBuilderOptionsSetInlinerThreshold
|
|
* - LLVMRunPasses
|
|
*/
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <llvm-c/Analysis.h>
|
|
#include <llvm-c/Core.h>
|
|
#include <llvm-c/Target.h>
|
|
#include <llvm-c/TargetMachine.h>
|
|
#include <llvm-c/Transforms/PassBuilder.h>
|
|
|
|
// Create a module with code that can be optimized
|
|
static LLVMModuleRef create_optimizable_module(LLVMContextRef ctx) {
|
|
LLVMModuleRef mod = LLVMModuleCreateWithNameInContext("optimize_me", ctx);
|
|
LLVMSetTarget(mod, "x86_64-unknown-linux-gnu");
|
|
|
|
LLVMTypeRef i32 = LLVMInt32TypeInContext(ctx);
|
|
LLVMBuilderRef builder = LLVMCreateBuilderInContext(ctx);
|
|
|
|
// Create: int add_inline(int x, int y) { return x + y; }
|
|
LLVMTypeRef add_params[] = {i32, i32};
|
|
LLVMTypeRef add_ty = LLVMFunctionType(i32, add_params, 2, 0);
|
|
LLVMValueRef add_fn = LLVMAddFunction(mod, "add_inline", add_ty);
|
|
LLVMSetLinkage(add_fn, LLVMInternalLinkage);
|
|
|
|
LLVMBasicBlockRef add_entry =
|
|
LLVMAppendBasicBlockInContext(ctx, add_fn, "entry");
|
|
LLVMPositionBuilderAtEnd(builder, add_entry);
|
|
LLVMValueRef sum = LLVMBuildAdd(builder, LLVMGetParam(add_fn, 0),
|
|
LLVMGetParam(add_fn, 1), "sum");
|
|
LLVMBuildRet(builder, sum);
|
|
|
|
// Create: int compute(int n) {
|
|
// int result = 0;
|
|
// for (int i = 0; i < n; i++) {
|
|
// result = add_inline(result, i);
|
|
// }
|
|
// return result;
|
|
// }
|
|
LLVMTypeRef compute_params[] = {i32};
|
|
LLVMTypeRef compute_ty = LLVMFunctionType(i32, compute_params, 1, 0);
|
|
LLVMValueRef compute_fn = LLVMAddFunction(mod, "compute", compute_ty);
|
|
|
|
LLVMBasicBlockRef entry =
|
|
LLVMAppendBasicBlockInContext(ctx, compute_fn, "entry");
|
|
LLVMBasicBlockRef loop =
|
|
LLVMAppendBasicBlockInContext(ctx, compute_fn, "loop");
|
|
LLVMBasicBlockRef exit_bb =
|
|
LLVMAppendBasicBlockInContext(ctx, compute_fn, "exit");
|
|
|
|
// Entry: initialize loop variables
|
|
LLVMPositionBuilderAtEnd(builder, entry);
|
|
LLVMValueRef result_ptr = LLVMBuildAlloca(builder, i32, "result_ptr");
|
|
LLVMValueRef i_ptr = LLVMBuildAlloca(builder, i32, "i_ptr");
|
|
LLVMBuildStore(builder, LLVMConstInt(i32, 0, 0), result_ptr);
|
|
LLVMBuildStore(builder, LLVMConstInt(i32, 0, 0), i_ptr);
|
|
LLVMBuildBr(builder, loop);
|
|
|
|
// Loop
|
|
LLVMPositionBuilderAtEnd(builder, loop);
|
|
LLVMValueRef i_val = LLVMBuildLoad2(builder, i32, i_ptr, "i");
|
|
LLVMValueRef n = LLVMGetParam(compute_fn, 0);
|
|
LLVMValueRef cond = LLVMBuildICmp(builder, LLVMIntSLT, i_val, n, "cond");
|
|
|
|
LLVMBasicBlockRef loop_body =
|
|
LLVMAppendBasicBlockInContext(ctx, compute_fn, "loop_body");
|
|
LLVMBuildCondBr(builder, cond, loop_body, exit_bb);
|
|
|
|
// Loop body
|
|
LLVMPositionBuilderAtEnd(builder, loop_body);
|
|
LLVMValueRef result_val = LLVMBuildLoad2(builder, i32, result_ptr, "result");
|
|
i_val = LLVMBuildLoad2(builder, i32, i_ptr, "i2");
|
|
|
|
// Call add_inline
|
|
LLVMValueRef args[] = {result_val, i_val};
|
|
LLVMValueRef new_result =
|
|
LLVMBuildCall2(builder, add_ty, add_fn, args, 2, "new_result");
|
|
LLVMBuildStore(builder, new_result, result_ptr);
|
|
|
|
// Increment i
|
|
LLVMValueRef new_i =
|
|
LLVMBuildAdd(builder, i_val, LLVMConstInt(i32, 1, 0), "new_i");
|
|
LLVMBuildStore(builder, new_i, i_ptr);
|
|
LLVMBuildBr(builder, loop);
|
|
|
|
// Exit
|
|
LLVMPositionBuilderAtEnd(builder, exit_bb);
|
|
LLVMValueRef final_result =
|
|
LLVMBuildLoad2(builder, i32, result_ptr, "final_result");
|
|
LLVMBuildRet(builder, final_result);
|
|
|
|
LLVMDisposeBuilder(builder);
|
|
return mod;
|
|
}
|
|
|
|
// Count instructions in a module
|
|
static int count_instructions(LLVMModuleRef mod) {
|
|
int count = 0;
|
|
for (LLVMValueRef fn = LLVMGetFirstFunction(mod); fn;
|
|
fn = LLVMGetNextFunction(fn)) {
|
|
for (LLVMBasicBlockRef bb = LLVMGetFirstBasicBlock(fn); bb;
|
|
bb = LLVMGetNextBasicBlock(bb)) {
|
|
for (LLVMValueRef inst = LLVMGetFirstInstruction(bb); inst;
|
|
inst = LLVMGetNextInstruction(inst)) {
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// Count functions in a module
|
|
static int count_functions(LLVMModuleRef mod) {
|
|
int count = 0;
|
|
for (LLVMValueRef fn = LLVMGetFirstFunction(mod); fn;
|
|
fn = LLVMGetNextFunction(fn)) {
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int main() {
|
|
// Initialize all targets (needed for pass manager)
|
|
LLVMInitializeAllTargetInfos();
|
|
LLVMInitializeAllTargets();
|
|
LLVMInitializeAllTargetMCs();
|
|
LLVMInitializeAllAsmPrinters();
|
|
LLVMInitializeAllAsmParsers();
|
|
|
|
LLVMContextRef ctx = LLVMContextCreate();
|
|
|
|
// Get default target for running passes
|
|
char *triple = LLVMGetDefaultTargetTriple();
|
|
LLVMTargetRef target = NULL;
|
|
char *err_msg = NULL;
|
|
if (LLVMGetTargetFromTriple(triple, &target, &err_msg)) {
|
|
fprintf(stderr, "; ERROR: Failed to get target: %s\n", err_msg);
|
|
LLVMDisposeMessage(err_msg);
|
|
LLVMDisposeMessage(triple);
|
|
LLVMContextDispose(ctx);
|
|
return 1;
|
|
}
|
|
if (err_msg) LLVMDisposeMessage(err_msg);
|
|
|
|
LLVMTargetMachineRef tm = LLVMCreateTargetMachine(
|
|
target, triple, "generic", "",
|
|
LLVMCodeGenLevelDefault, LLVMRelocDefault, LLVMCodeModelDefault);
|
|
LLVMDisposeMessage(triple);
|
|
|
|
printf("; Test: test_passbuilder\n");
|
|
printf("; Tests optimization pass running via PassBuilder API\n");
|
|
printf(";\n");
|
|
|
|
// ==========================================================================
|
|
// Test 1: Create PassBuilderOptions with various settings
|
|
// ==========================================================================
|
|
printf("; Test 1: PassBuilderOptions configuration\n");
|
|
|
|
LLVMPassBuilderOptionsRef opts = LLVMCreatePassBuilderOptions();
|
|
|
|
// Configure options
|
|
LLVMPassBuilderOptionsSetVerifyEach(opts, 0);
|
|
LLVMPassBuilderOptionsSetDebugLogging(opts, 0);
|
|
LLVMPassBuilderOptionsSetLoopInterleaving(opts, 1);
|
|
LLVMPassBuilderOptionsSetLoopVectorization(opts, 1);
|
|
LLVMPassBuilderOptionsSetSLPVectorization(opts, 1);
|
|
LLVMPassBuilderOptionsSetLoopUnrolling(opts, 1);
|
|
LLVMPassBuilderOptionsSetMergeFunctions(opts, 1);
|
|
LLVMPassBuilderOptionsSetInlinerThreshold(opts, 250);
|
|
|
|
printf("; PassBuilderOptions created: yes\n");
|
|
printf("; Options configured: yes\n");
|
|
|
|
// ==========================================================================
|
|
// Test 2: Optimization at O0 (no optimization)
|
|
// ==========================================================================
|
|
printf(";\n; Test 2: O0 optimization (no optimization)\n");
|
|
|
|
LLVMModuleRef mod_o0 = create_optimizable_module(ctx);
|
|
int instr_before_o0 = count_instructions(mod_o0);
|
|
int funcs_before_o0 = count_functions(mod_o0);
|
|
|
|
LLVMErrorRef err = LLVMRunPasses(mod_o0, "default<O0>", tm, opts);
|
|
if (err) {
|
|
char *msg = LLVMGetErrorMessage(err);
|
|
fprintf(stderr, "; ERROR: O0 passes failed: %s\n", msg);
|
|
LLVMDisposeErrorMessage(msg);
|
|
LLVMDisposeModule(mod_o0);
|
|
LLVMDisposePassBuilderOptions(opts);
|
|
LLVMDisposeTargetMachine(tm);
|
|
LLVMContextDispose(ctx);
|
|
return 1;
|
|
}
|
|
|
|
int instr_after_o0 = count_instructions(mod_o0);
|
|
int funcs_after_o0 = count_functions(mod_o0);
|
|
|
|
printf("; Instructions before: %d, after: %d\n", instr_before_o0,
|
|
instr_after_o0);
|
|
printf("; Functions before: %d, after: %d\n", funcs_before_o0,
|
|
funcs_after_o0);
|
|
printf("; O0 passes ran successfully: yes\n");
|
|
|
|
LLVMDisposeModule(mod_o0);
|
|
|
|
// ==========================================================================
|
|
// Test 3: Optimization at O2
|
|
// ==========================================================================
|
|
printf(";\n; Test 3: O2 optimization\n");
|
|
|
|
LLVMModuleRef mod_o2 = create_optimizable_module(ctx);
|
|
int instr_before_o2 = count_instructions(mod_o2);
|
|
int funcs_before_o2 = count_functions(mod_o2);
|
|
|
|
err = LLVMRunPasses(mod_o2, "default<O2>", tm, opts);
|
|
if (err) {
|
|
char *msg = LLVMGetErrorMessage(err);
|
|
fprintf(stderr, "; ERROR: O2 passes failed: %s\n", msg);
|
|
LLVMDisposeErrorMessage(msg);
|
|
LLVMDisposeModule(mod_o2);
|
|
LLVMDisposePassBuilderOptions(opts);
|
|
LLVMDisposeTargetMachine(tm);
|
|
LLVMContextDispose(ctx);
|
|
return 1;
|
|
}
|
|
|
|
int instr_after_o2 = count_instructions(mod_o2);
|
|
int funcs_after_o2 = count_functions(mod_o2);
|
|
|
|
printf("; Instructions before: %d, after: %d\n", instr_before_o2,
|
|
instr_after_o2);
|
|
printf("; Functions before: %d, after: %d\n", funcs_before_o2,
|
|
funcs_after_o2);
|
|
printf("; O2 passes ran successfully: yes\n");
|
|
|
|
// At O2, the add_inline function should be inlined and possibly removed
|
|
LLVMValueRef add_fn = LLVMGetNamedFunction(mod_o2, "add_inline");
|
|
printf("; add_inline function removed: %s\n", add_fn ? "no" : "yes");
|
|
|
|
// Verify the optimized module is still valid
|
|
char *verify_error = NULL;
|
|
if (LLVMVerifyModule(mod_o2, LLVMReturnStatusAction, &verify_error)) {
|
|
fprintf(stderr, "; ERROR: Optimized module verification failed: %s\n",
|
|
verify_error);
|
|
LLVMDisposeMessage(verify_error);
|
|
} else {
|
|
printf("; Optimized module verified: yes\n");
|
|
}
|
|
if (verify_error)
|
|
LLVMDisposeMessage(verify_error);
|
|
|
|
LLVMDisposeModule(mod_o2);
|
|
|
|
// ==========================================================================
|
|
// Test 4: Custom pass pipeline
|
|
// ==========================================================================
|
|
printf(";\n; Test 4: Custom pass pipeline\n");
|
|
|
|
LLVMModuleRef mod_custom = create_optimizable_module(ctx);
|
|
int instr_before_custom = count_instructions(mod_custom);
|
|
|
|
// Run just instcombine and simplifycfg
|
|
err = LLVMRunPasses(mod_custom, "instcombine,simplifycfg", tm, opts);
|
|
if (err) {
|
|
char *msg = LLVMGetErrorMessage(err);
|
|
fprintf(stderr, "; ERROR: Custom passes failed: %s\n", msg);
|
|
LLVMDisposeErrorMessage(msg);
|
|
LLVMDisposeModule(mod_custom);
|
|
LLVMDisposePassBuilderOptions(opts);
|
|
LLVMDisposeTargetMachine(tm);
|
|
LLVMContextDispose(ctx);
|
|
return 1;
|
|
}
|
|
|
|
int instr_after_custom = count_instructions(mod_custom);
|
|
printf("; Instructions before: %d, after: %d\n", instr_before_custom,
|
|
instr_after_custom);
|
|
printf("; Custom passes ran successfully: yes\n");
|
|
|
|
// Print the optimized module
|
|
printf(";\n; Optimized module (after instcombine,simplifycfg):\n");
|
|
char *ir = LLVMPrintModuleToString(mod_custom);
|
|
printf("%s", ir);
|
|
LLVMDisposeMessage(ir);
|
|
|
|
LLVMDisposeModule(mod_custom);
|
|
LLVMDisposePassBuilderOptions(opts);
|
|
LLVMDisposeTargetMachine(tm);
|
|
LLVMContextDispose(ctx);
|
|
|
|
return 0;
|
|
}
|