DEV Community

Cover image for Create a Basic GCC Pass - Project Stage 01
Juro Zaw
Juro Zaw

Posted on

Create a Basic GCC Pass - Project Stage 01

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
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ 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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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 */
};
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
}

Enter fullscreen mode Exit fullscreen mode

πŸ“Œ 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);
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ 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);
...
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ 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 = \
Enter fullscreen mode Exit fullscreen mode

Then, among the objects, just add your tree pass file with the .o extension there.

OBJS = 
...
 tree_kzaw_basicstats.o \
..
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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)