Hi everyone! π
We are starting our Project Stage 01 as of this point and for this stage, we will be working with GCC passes!
A GCC Pass is basically a phase in the compilation process before the compiler generates machine code. There are a BUNCH of passes already in GCC compiler.
I'll guide you through my process of creating a basic GCC pass.
Here are the requirements for the pass:
- Iterates through the code being compiled;
- Prints the name of every function being compiled;
- Prints a count of the number of basic blocks in each function; and
- Prints a count of the number of gimple statements in each function.
Setup
I will be working in my remote aarch64 and x86 remote servers as usual so please make sure they are set up for you.
I will not go into details about building and install the
gcc
compiler as I have done so in a past lab: Building a GCC Compiler.
After you have completed everything in that article, you will have 3 directories in your repo. (Note: the names might not be the exact same)
-
/gcc-build-001
π where gcc was configured & built -
/gcc-test-001
π where gcc is run -
/git/gcc
π where gcc source code was cloned
One more thing, it is recommended to start a screen session to prevent disconnection issues.
screen -S gcc_pass_dev
π Create a Basic Dummy Pass
I want to first create a minimal pass and make sure that it is working before implementing logic.
First, go into your ~/git/gcc/gcc
subdirectory, which is where the passes are located and create a new pass with .cc
file type.
cd ~/git/gcc/gcc
nano tree-kzaw-basicstats.cc
You can take a look at the other "tree" files to get an idea of how C++ tree files are. Especially for the included headers, it is helpful to look at other tree files!
Below, I will explain my dummy pass section by section.
Explanation of Code
Include Directives
This section includes necessary GCC headers.
#define INCLUDE_MEMORY
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "tree.h"
#include "gimple.h"
#include "tree-pass.h"
#include "cgraph.h"
#include "pretty-print.h"
Pass Data Structure
The pass_data
structure is a fundamental part of GCC's pass manager system. We are making a structure called pass_data_kzaw_basicstats
.
const pass_data pass_data_kzaw_basicstats = {
GIMPLE_PASS, /* type */
"kzaw", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_NONE, /* tv_id */
PROP_cfg, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
Pass Class Definition
We are making a pass class "pass_kzaw_basicstats" that inherits from gimple_opt_pass
. It includes:
- A constructor that passes the data structure & context to the parent class.
- A
gate
method that returns 1 always to enable this pass. - A declaration of the
execute
method that will be implemented separately.
class pass_kzaw_basicstats : public gimple_opt_pass
{
public:
pass_kzaw_basicstats (gcc::context *ctxt)
: gimple_opt_pass (pass_data_kzaw_basicstats, ctxt)
{}
bool gate (function *) final override {
return 1; // Always execute pass
}
unsigned int execute (function *) final override;
};
Execute Method Implementation
This is the main pass implementation. The execute
function basically prints out basic statistics about each function. It uses FOR_EACH_FUNCTION
to iterate through all functions to count and list function names.
It will create a simple output file with basic formatting.
unsigned int
pass_kzaw_basicstats::execute (function *fun)
{
struct cgraph_node *node;
int func_cnt = 0;
FILE *dump_file = fopen("kzaw_basicstats.out", "w");
if (!dump_file)
return 0;
// Iterate through all functions
FOR_EACH_FUNCTION (node)
{
if (node->has_gimple_body_p())
{
fprintf(dump_file, "=== Function %d Name '%s' ===\n",
++func_cnt, node->name());
}
}
fprintf(dump_file, "\n\n#### End kzaw_basicstats diagnostics ####\n\n\n");
fclose(dump_file);
return 0;
}
Factory Function
This is an important factory function that creates an instance of the pass. It would need to be referenced in the pass manager to register the pass with the compiler.
gimple_opt_pass *
make_pass_kzaw_basicstats (gcc::context *ctxt)
{
return new pass_kzaw_basicstats (ctxt);
}
FULL CODE
/* Basic statistics collection pass for GCC
Copyright (C) 2025 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#define INCLUDE_MEMORY
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "tree.h"
#include "gimple.h"
#include "tree-pass.h"
#include "cgraph.h"
#include "pretty-print.h"
namespace {
const pass_data pass_data_kzaw_basicstats = {
GIMPLE_PASS, /* type */
"kzaw", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_NONE, /* tv_id */
PROP_cfg, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
class pass_kzaw_basicstats : public gimple_opt_pass
{
public:
pass_kzaw_basicstats (gcc::context *ctxt)
: gimple_opt_pass (pass_data_kzaw_basicstats, ctxt)
{}
bool gate (function *) final override {
return 1; // Always execute pass
}
unsigned int execute (function *) final override;
};
unsigned int
pass_kzaw_basicstats::execute (function *)
{
struct cgraph_node *node;
int func_cnt = 0;
FILE *dump_file = fopen("kzaw_basicstats.out", "w");
if (!dump_file)
return 0;
// Iterate through all functions
FOR_EACH_FUNCTION (node)
{
if (node->has_gimple_body_p())
{
fprintf(dump_file, "=== Function %d Name '%s' ===\n",
++func_cnt, node->name());
}
}
fprintf(dump_file, "\n\n#### End kzaw_basicstats diagnostics ####\n\n\n");
fclose(dump_file);
return 0;
}
} // anon namespace
gimple_opt_pass *
make_pass_kzaw_basicstats (gcc::context *ctxt)
{
return new pass_kzaw_basicstats (ctxt);
}
π Modify passes.def
file
After you are satisfied with the dummy pass file, you will have to go into passes.def
file to add your pass there. The pass manager uses the file passes.def
to define the order in which passes are called.
The most basic way to add a new pass into this file is to define NEXT_PASS (name_of_pass)
to insert a pass at a specific point in the pass list. You will need to experiment a little bit to see which part is the best part to insert your pass.
It should not be too early or too late.
NEXT_PASS (pass_nrv);
NEXT_PASS (pass_kzaw_basicstats);
NEXT_PASS (pass_gimple_isel);
π Include pass in tree-pass.h
Header file
We will add our factory function definition in the header file. It does not have to be in any order.
...
extern gimple_opt_pass *make_pass_kzaw_basicstats (gcc::context *ctxt);
...
π Modify the Build
Basically we make to modify our Makefile.in
. In there, you have to find an OBJS
macro. It will look like this:
# Language-independent object files.
# We put the *-match.o and insn-*.o files first so that a parallel make
# will build them sooner, because they are large and otherwise tend to be
# the last objects to finish building.
OBJS = \
Then, among the objects, just add your tree pass file with the .o
extension there.
OBJS =
...
tree_kzaw_basicstats.o \
..
IMPORTANT: This change to the Makefile isnβt gonna be picked up by the build system. So go to your build directory/gcc. And remove the Makefile there.
After that
cd ~/gcc-build-001/gcc
rm Makefile
cd ../
~/git/gcc/configure --prefix=$HOME/gcc-test-001
time make -j 24 |& tee build.log
make install
At this point of development, I am having issues with the make build on both servers, where I can't seem to install. Please stay tuned for update!
Top comments (0)