Project Stage 1

 Introduction

This project focuses on creating a custom pass for the GCC compiler, which will analyze functions during compilation. The pass will iterate through functions in the compiled code, print their names, count the number of basic blocks, and count the number of GIMPLE statements.

Step 1: Creating the GCC Pass

Creating the Pass File

I navigated to the gcc/ directory inside the GCC source tree and created a new file:

[ayoung54@aarch64-002 ~]$ cd ~/git/gcc/gcc

[ayoung54@aarch64-002 gcc]$ nano a_pass.cc

Implementing the Pass

I added the following code to define my custom GCC pass in a_pass.cc:

#include "config.h"

#include "system.h"

#include "coretypes.h"

#include "backend.h"

#include "tree-pass.h"

#include "pass_manager.h"

#include "context.h"

#include "diagnostic-core.h"

#include "tree.h"

#include "tree-core.h"

#include "basic-block.h"

#include "gimple.h"

#include "gimple-iterator.h"


namespace {


// Define pass metadata

const pass_data a_pass_data = {

    GIMPLE_PASS,        // Pass operates on GIMPLE representation

    "a_pass"    // Pass name

    OPTGROUP_NONE, // No optimization group

    TV_TREE_OPS,        // Tree operations category

    0, 0, 0, 0          // Default settings

};


// Custom GIMPLE optimization pass class

class a_pass : public gimple_opt_pass {

public:

    // Constructor initializing pass with metadata

    a_pass(gcc::context *ctxt) : gimple_opt_pass(a_pass_data, ctxt) {}


    // Gate function (always true)

    bool gate(function *fun) override { return true; }


    // Main execution function for the pass

    unsigned int execute(function *fun) override {

        fprintf(stderr, "Processing function: %s\n", function_name(fun));


        int basic_block_count = 0, gimple_stmt_count = 0;


        // Single loop to count both basic blocks and GIMPLE statements

        for (auto bb : fun->cfg->x_basic_block_info) {

            if (!bb) continue// Skip null blocks (safety check)

            basic_block_count++;

            for (auto gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {

                gimple_stmt_count++;

            }

        }


        // Print results in a single operation to minimize I/O overhead

        fprintf(stderr, "Number of basic blocks: %d\nNumber of GIMPLE statements: %d\n", 

                basic_block_count, gimple_stmt_count);


        return 0; // Indicate successful execution

    }

};


// Factory function to create an instance of the pass

gimple_opt_pass *make_a_pass(gcc::context *ctxt) {

    return new a_pass(ctxt);

}


// namespace


Step 2: Registering the Pass in GCC

Modifying tree-pass.h

I put added a line before the IPA passes for the pass manager to find my pass.


[ayoung54@aarch64-002 gcc]$ nano tree-pass.h


extern gimple_opt_pass *make_pass_coroutine_early_expand_ifns (gcc::context *ct>

extern gimple_opt_pass *make_pass_adjust_alignment (gcc::context *ctxt);

extern gimple_opt_pass *make_a_pass (gcc::context *ctxt);


/* IPA Passes */

extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt);

extern simple_ipa_opt_pass *make_pass_ipa_function_and_variable_visibility (gcc>

extern simple_ipa_opt_pass *make_pass_ipa_strub_mode (


Modifying passes.def

[ayoung54@aarch64-002 gcc]$ nano passes.def


I added my pass to passes.defafter pass_clean_state:


      POP_INSERT_PASSES ()

      NEXT_PASS (pass_df_finish);

  POP_INSERT_PASSES ()

  NEXT_PASS (pass_clean_state);

  NEXT_PASS (a_pass);

  TERMINATE_PASS_LIST (all_passes)

Step 3: Adding the Pass to GCC's Build System

Configure the build using the command: 

[ayoung54@aarch64-002 gcc]$ ./configure --prefix=$HOME/gcc-test-001

I added a_pass.o to Makefile.in:


warning-control.o \

web.o \

wide-int.o \

wide-int-print.o \

        a_pass.o \

$(out_object_file) \

$(ANALYZER_OBJS) \

$(EXTRA_OBJS) \

$(host_hook_obj)


# Objects in libcommon.a, potentially used by all host binaries and with

# no target dependencies.

Step 4: Rebuilding GCC

Since Makefile.in was modified, I generated a new Makefile that includes my custom pass. The easiest way is to remove the Makefile in the build folder and re-generate the Makefile by the configuration script. 

[ayoung54@aarch64-002 gcc]$ rm Makefile

[ayoung54@aarch64-002 gcc]$ ./configure --prefix=$HOME/gcc-test-001


Rebuild the gcc

[ayoung54@aarch64-002 gcc]$ time make -j$(nproc) |& tee build.log 



The build took:



Step 5: Running and Testing the Pass 

Compiling a Test Program

I created a simple test file:
#include <stdio.h>
int main(){
printf("Alyssa here!\n");
        return 0;
}





PASSED!!!

Conclusion

This project was an insightful experience in understanding the inner workings of GCC and how passes are implemented and registered. The most challenging aspect was navigating the GCC codebase and understanding how passes are structured and executed. Modifying the build system to include my custom pass was also a bit tricky at first, as I had to ensure that the pass was compiled and linked correctly.

Through this project, I have gained a deeper understanding of compiler internals and how passes interact with the code during the compilation process. However, I have identified a gap in my knowledge regarding how GCC optimizes code at various stages. To address this, I plan to study GCC’s other optimization passes and experiment with modifying them.

In terms of further exploration, I am interested in extending my pass to perform more advanced analysis and possibly integrate it into the broader optimization workflow of GCC.

Stay tuned for the next stage of the project!





Comments

Popular posts from this blog

Lab05 - x86_64

Lab 03 - 6502 Program Lab (Revised)