Project Stage 1: Create a GCC Pass
Introduction
I created a custom pass for the GCC compiler for Stage 1 of my SPO600 project, which examines generated code by displaying the total number of GIMPLE statements in each function, reporting the function name, and counting basic blocks. During compilation, this will aid in revealing information about the code structure.
Environment Setup
I established a three-directory structure for my GCC development environment:
bashCopy# Directory structure
~/git/gcc/ # GCC source code repository
~/gcc-build-001/ # Build directory
~/gcc-test-001/ # Installation and testing directory
I found four important files that would require editing to construct a custom pass after looking over the GCC documentation:
A new source file (
tree-my-pass.cc) - Containing the pass implementationpasses.def - To register my pass in the compilation pipeline
tree-pass.h - To declare my pass's factory function
Makefile. in - To integrate my new source file into the build system
Developing the Custom Pass
Step 1: Creating the Pass Source File
First, I created a new file tree-my-pass.cc in the GCC source code directory with the following implementation:
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "tree.h"
#include "gimple.h"
#include "tree-pass.h"
#include "gimple-iterator.h"
#include "pass_manager.h"
#include "basic-block.h"
namespace {
const pass_data my_pass_data = {
GIMPLE_PASS, // Pass type: operates on GIMPLE
"my-pass", // Name of the pass
OPTGROUP_NONE, // No optimization group
TV_NONE, // No TV id
0, // No properties required
0, // No properties provided
0, // No properties destroyed
0 // No specific flags
};
class my_pass : public gimple_opt_pass {
public:
my_pass(gcc::context *ctxt)
: gimple_opt_pass(my_pass_data, ctxt) { }
// The gate method: always run this pass
bool gate(function *) override {
return true;
}
// The execute method: called for every function
unsigned int execute(function *fun) override {
if (dump_file) {
fprintf(dump_file, "Processing function: %s\n", function_name(fun));
// Count basic blocks
int basic_block_count = 0;
basic_block bb;
FOR_EACH_BB_FN(bb, fun) {
basic_block_count++;
}
fprintf(dump_file, "Number of basic blocks: %d\n", basic_block_count);
// Count GIMPLE statements
int gimple_stmt_count = 0;
FOR_EACH_BB_FN(bb, fun) {
for (gimple_stmt_iterator gsi = gsi_start_bb(bb);
!gsi_end_p(gsi);
gsi_next(&gsi)) {
gimple_stmt_count++;
}
}
fprintf(dump_file, "Number of GIMPLE statements: %d\n", gimple_stmt_count);
}
return 0;
}
};
}
// Factory function to create an instance of the pass
gimple_opt_pass *
make_pass_my_pass(gcc::context *ctxt)
{
return new my_pass(ctxt);
}
This implementation:
Specifies a GIMPLE pass that each function uses.
Counts the fundamental components in every function.
Counts GIMPLE statements in each function
Outputs the results to the dump file
Step 2: Registering the Pass in passes.def
To register my pass in the GCC pipeline, I added the following line to passes.def:
NEXT_PASS (pass_my_pass, 1)

This registers my pass with a unique name and indicates it should be executed in the compiler's pass pipeline.
Step 3: Declaring the Pass in tree-pass.h
To make my pass's factory function available, I added this declaration to tree-pass.h:
extern gimple_opt_pass *make_pass_my_pass (gcc::context *ctxt);
Step 4: Updating Makefile.in
To include my pass in the build, I added it to the list of object files in Makefile.in:
tree-my-pass.o \
I made sure to use the same indentation and trailing backslash as other entries to maintain proper Makefile syntax.
Building and Testing Process
Building GCC with My Custom Pass
I configured and built GCC with my custom pass:
~/gcc-build-001
../git/gcc/configure --prefix=$HOME/gcc-test-001 --enable-languages=c --disable-bootstrap --disable-multilib
time make -j$(nproc) |& tee rebuild.log
Testing the Custom Pass
After resolving the build issues, I tested my pass with a simple C program:
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(5, 3);
printf("Result: %d\n", result);
return 0;
}
Compiling with my custom pass enabled:
~/gcc-test-001/bin/gcc -fdump-tree-my-pass test.c -o test
This generated a dump file test.c.234t.my-pass with output like:
Processing function: add
Number of basic blocks: 1
Number of GIMPLE statements: 1
Processing function: main
Number of basic blocks: 2
Number of GIMPLE statements: 4
Reflections and Learning
The GCC's pass management mechanism and intermediate representations have been nicely explained by this project. The way that GIMPLE statements break apart seemingly straightforward operations—a single line of C code might produce numerous GIMPLE statements—and expose the complexity concealed underneath high-level code shocked me the most. I also observed that during optimization, GCC reuses fundamental blocks. Functions that appeared to need a lot of control routes were frequently reduced to only two or three simple blocks, demonstrating how the compiler removes unnecessary code paths that we might not be aware of.


Comments
Post a Comment