Monday, March 10, 2025

8th Posting: Project 1

For this project we are going to make custom GCC pass for our GCC compiler that we done in Lab 4. The requirements simply follows:

Create a pass for the current development version of the GCC compiler which:

  1. Iterates through the code being compiled;
  2. Prints the name of every function being compiled;
  3. Prints a count of the number of basic blocks in each function; and
  4. Prints a count of the number of gimple statements in each function.

Before we start anything lets go ahead and recap the file structures with ll command



So if we go back and see we have these build and test directory, as well as the source directory that we ran git clone. Lets get into the source directory and get into gcc folder
We see a huge directory with a lot of different files here!  we are now going to create a file called tree-sj-pass.cc and add our basic gcc pass code as below

#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 sj_pass_data = {

    GIMPLE_PASS,          // Pass type

    "tree-sj-pass",   // Pass name

    OPTGROUP_NONE,        // No optimization group

    TV_TREE_OPS,          // Timevar

    0, 0, 0, 0           // Default settings

};


// Custom GIMPLE pass class

class sj_pass : public gimple_opt_pass {

public:

    // Constructor

    sj_pass(gcc::context *ctxt) : gimple_opt_pass(sj_pass_data, ctxt) {}


    // Gate function - always true

    bool gate(function *fun) override {

        return fun != nullptr;

    }


    // Main execution function

    unsigned int execute(function *fun) override {

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


        int basic_block_count = 0, gimple_stmt_count = 0;


        // Iterate over basic blocks

        basic_block bb;

        FOR_EACH_BB_FN(bb, fun) {

            if (!bb) continue;  // Safety check

            basic_block_count++;


            // Iterate over GIMPLE statements

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

                gimple_stmt_count++;

            }

}


// Print results

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

                basic_block_count, gimple_stmt_count);


        return 0;

    }

};

}


// Factory function to create an instance of the pass

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

    return new sj_pass(ctxt);


}





Now we are modifying tree-pass.h file to register our newly made file tree-sj-pass.cc
(Looking back made some weird mistake here with typo with dashes..as well as context typo)

Then now we are modifying passes.def 

Then now we are adding tree-sj-pass.o to Makefile.in which is in the same directory to make sure that GCC includes my pass in build process.


Now since Makefile has been modified we are going to rebuild it.


;;; currently encountering linkage error with tree-sj-pass.o, will continue to attempt and update here.

--update, seems like it was relate to namespace error,  now i am running 
make clean in build directory
then build again with
time make -j$(nproc) |& tee make.log
And now we are encountering another error that is relate to configuring libgcc, currently on investigating and work in process..



RE-starting from scratch:
Currently recloning everything again to try from scratch.

Make install works successfully:


Now we are going back and adding a basic pass file again which is tree-sj.cc same as above code for basic pass but just a different names and conventions.

#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 tree_sj_pass_data = {

    GIMPLE_PASS,         // Pass type

    "tree-sj",           // Pass name

    OPTGROUP_NONE, // No optimization group

    TV_TREE_OPS,         // Timevar

    0, 0, 0, 0           // Default settings

};


// Custom GIMPLE pass class

class tree_sj_pass : public gimple_opt_pass {

public:

    // Constructor

    tree_sj_pass(gcc::context *ctxt) : gimple_opt_pass(tree_sj_pass_data, ctxt) {}


    // Gate function - always true

    bool gate(function *fun) override {

        return fun != nullptr;

    }


    // Main execution function

    unsigned int execute(function *fun) override {

        fprintf(stderr, "[tree-sj] Processing function: %s\n", function_name(fun));


        int basic_block_count = 0;

        int gimple_stmt_count = 0;

        basic_block bb;

        // Iterate over basic blocks

        FOR_EACH_BB_FN(bb, fun) {

            if (!bb) continue;  // Safety check

            basic_block_count++;


            // Iterate over GIMPLE statements

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

                gimple_stmt_count++;

            }

}


// Print results

        fprintf(stderr, "[tree-sj] Basic Blocks: %d | GIMPLE Statements: %d\n",

                basic_block_count, gimple_stmt_count);


        return 0;

    }

};


} // namespace

Save and exit, and edit tree-pass.h to add declaration for recognizing our function.

extern gimple_opt_pass *make_pass_harden_control_flow_redundancy (gcc::context

                                                                  *ctxt);

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



and edit passes.def to ensure pass is running during compilation
for adding NEXT_PASS(pass_sj)


and edit Makefile.in  to ensure gcc includes my above basic pass.

        tree-complex.o \

        tree-data-ref.o \

        tree-sj.o \

        tree-dfa.o \

        tree-diagnostic.o \

        tree-diagnostic-client-data-hooks.o \

        tree-dump.o \


and edit passes.def to ensure pass is running during compilation




then if you run the build command again you will see its getting built

few things to remember while debugging :
- naming convention syntaxes are of course important, double check, doublecheck
- which line you are putting all those edits are important as well

  NEXT_PASS (pass_asan_O0);

  NEXT_PASS (pass_tsan_O0);

  NEXT_PASS (pass_sanopt);

  NEXT_PASS (pass_cleanup_eh);

  NEXT_PASS (pass_musttail);

  NEXT_PASS (pass_lower_resx);

  NEXT_PASS (pass_sj);

  NEXT_PASS (pass_nrv);

  NEXT_PASS (pass_gimple_isel);

  NEXT_PASS (pass_harden_conditiona



and i run a testing file from gcc-test directory 

slee569@x86-001:~/gcc-test-001$ /home/slee569/gcc-build-001/gcc/xgcc -B/home/slee569/gcc-build-001/gcc/ test.c -o test_c.out

./test_c.out

[tree-sj] Processing function: main

[tree-sj] Basic Blocks: 2 | GIMPLE Statements: 5

Hello from GCC!

PASS running test file here! hows it going!



and got the expected result that is running with my customized GCC pass.







This project was really really time consuming if you start to mess up some little details like typos... dont be like me and make sure whatever you write is consistent and keep think about the connection between .def .h makefile and your basic gcc pass. Also again make sure to edit the files in right place, i tried a lot of times with wrong placement(out of scope, linkage issue, ggc not finding certain things that i implemented, sometimes even suspecting xgcc might be corrupted but it turned out that i was the one who made mistakes on editing files..) i think its all coming from what we edited so make sure its in right lines.


------------------------------------------------
Summary:

🔴 Configuration Issues: Cache and environment inconsistencies made it hard to get a clean build.
🔴 Pass Registration Errors: Undefined references and missing make_pass_sj() function led to failed builds.
🔴 GCC Compilation Complexity: Minor changes could break the build, requiring a lot of debugging.

Successful Build: Despite errors, we eventually got a working customized GCC!
Custom Pass Execution: tree-sj executed and provided basic block & GIMPLE statistics.
Improved Debugging Skills: Learned to analyze config.log, build.log, and compiler errors efficiently.

Final Takeaway:

This project was a hands-on journey into compiler design, teaching us how GCC compiles, optimizes, and executes code. We overcame tough debugging challenges and built a working custom pass, reinforcing our understanding of compiler internals and optimization techniques. 





No comments:

Post a Comment

10th Posting - Project: Stage 3

 Hello All! now we are on the final stage of the project which is project 3. If you remember correctly from my Stage 2 posting, i was able t...