📌 Find My Source Code Here: GitHub
Hello everyone, welcome back!
This is a continuation of my Project 01 blog post. There were some errors in Project Stage 01 so we will be fixing that.
You can find the first part here: Link.
📌 Install Error?
So my issue was that I could not make install
after running the make
command in the directory. This is the error:
This error is vague but it seems to generally mean that
-
Makefile
was corrupted or was not completed - Configure didn't finish properly
- Wrong directory
In this case, i think that the first point makes the most sense to be the issue.
📌 Build Error?
So when I check the build.log
to investigate, I saw a BUNCH of code lines that says undefined reference to ...
. Example of a part of the log:
This definitely means that make
was not completed successfully, had an error at one point and all the error cascaded.
If I check config.log
, I can also find some 'missing' files.
📌 Basic Code
This is the changed code, I used for my basic tree-kzaw.cc
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "tree.h"
#include "gimple.h"
#include "tree-pass.h"
#include "ssa.h"
#include "tree-pretty-print.h"
#include "gimple-iterator.h"
#include "gimple-walk.h"
#include "internal-fn.h"
#include "gimple-pretty-print.h"
// Added headers:
#include "gimple-ssa.h"
#include "cgraph.h"
#include "attribs.h"
#include "pretty-print.h"
#include "tree-inline.h"
#include "intl.h"
// Test pass
namespace {
const pass_data pass_data_kzaw =
{
GIMPLE_PASS, /* type */
"kzaw", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_TREE_NRV, /* tv_id */
PROP_cfg , /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
class pass_kzaw : public gimple_opt_pass
{
public:
pass_kzaw (gcc::context *ctxt)
: gimple_opt_pass (pass_data_kzaw, ctxt)
{}
/* opt_pass methods: */
bool gate (function *) final override {
return 1;
}
unsigned int execute (function *) final override;
};
unsigned int
pass_kzaw::execute (function *) {
struct cgraph_node *node;
int func_cnt = 0;
FOR_EACH_FUNCTION (node)
{
if (dump_file)
{
fprintf(dump_file, "=== Function %d Name '%s' ===\n", ++func_cnt, node->name() );
}
}
if (dump_file)
{
fprintf(dump_file, "\n\n#### End kzaw diagnostics, start regular dump of current gimple ####\n\n\n");
}
return 0; // Moved return statement outside the if block to ensure it always returns
}
} // anonymous namespace
// Factory function that creates an instance of the pass
gimple_opt_pass *
make_pass_kzaw (gcc::context *ctxt)
{
return new pass_kzaw (ctxt);
}
📌 What did I do?
After trying at the hour for many many hours, I decided to rebuild the whole gcc
from scratch. Yes, this would take long but it was the only viable option as the logs were too big of a size and complicated to decipher.
I deleted the ~/git/gcc
and rebuilt the entire thing again.
However, I realized there are very important details that you will need to consider for creating your own GCC pass.
✅ Get the Names Right
The main error you could face is getting and putting the names. This is very IMPORTANT! For example, taking the naming convention from my code:
-
tree-name.cc
: This is the name of your pass definition file. You will put your logic in here. When you add a.o
toOBJS
inMakefile.in
, you are using the filename so it will betree-name.o
. -
pass_name
: This will typically be the name of your pass itself! Not to be mistaken with the file name. This is what you are using inpasses.def
where you will define aNEXT_PASS(pass_name)
. -
make_pass_name
: This is the name of function within your file. It will be used in thetree-pass.h
header file so use it when declaring the function within the header.
✅ Get Directories Right
Specifically, please be carefully with where you are calling the commands from. They have to be very specific. Here are important ones:
-
Configure command: The configure command (
~/git/gcc/configure --prefix=$HOME/gcc-test-001
) need to be called from your build directory. (~/gcc-build-001/
) -
GCC subdirectory: Creating a pass with regards to
gcc
source code must be in~/git/gcc/gcc
. -
Makefile
remove: When you changeMakefile.in
for the first time, and you need to remove the initialMakefile
. Make sure that you are in the~/gcc-build-001/gcc
subdirectory!
If everything goes and attention is detailed, the install will be successful!
Now let's move on 😄
📌 Test it Out
We have confirmation that our gcc
is installed. Now we will need to test it out.
test.c
file
Make a new test.c
file:
#include
void function1() {
printf("I'm function1!\n");
}
void function2() {
printf("I'm function2!\n");
}
void function3() {
printf("I'm function3!\n");
}
int main() {
function1();
function2();
function3();
return 0;
}
Makefile
To make
and run our new test.c
file, we will need to add a new Makefile.
BINARIES=test
CCFLAGS=-g -O0 -fno-builtin -fdump-tree-kzaw
all: ${BINARIES}
test: test.c
$$HOME/gcc-test-001/bin/gcc ${CCFLAGS} -o test test.c
clean:
rm ${BINARIES} *.o || true
Things to note:
- Add
-fdump-tree-kzaw
to check our dump file. (The dump file name is from our pass file name) - Make sure to configure our local
gcc
, not the servergcc
. We define the path from our test directory.
Run command:
make test
Now check your dumpfile, you will see something like this which means it is working:
📌 Updating the Code
Now that our basic code is working, it is time to edit it so that it can count both the basic blocks and GIMPLE statements!
The goal is to traverse basic blocks and statements within a function, while the old code traverses functions in the entire program.
The main portion of the code we will be editing is the execute()
function.
First, we'll modify the function signature to actually use the function *fun
parameter.
unsigned int
pass_kzaw::execute (function *fun)
Next, we need to declare variables for going through basic blocks and counting both blocks and statements.
basic_block block;
int block_count = 0, statement_count = 0;
Instead of traversing all functions in the program with FOR_EACH_FUNCTION
, we now use FOR_EACH_BB_FN
to iterate through all basic blocks in the current function.
// Traverse basic blocks in the current function
FOR_EACH_BB_FN (block, fun)
{
block_count++;
if (dump_file)
{
fprintf (dump_file, "===== Basic block count: %d =====\n", block_count);
}
...
For each basic block, we add a nested loop using gimple_stmt_iterator to traverse all statements within that block,
// For each basic block, traverse statements
for (gimple_stmt_iterator gsi = gsi_start_bb (block); !gsi_end_p (gsi); gsi_next (&gsi))
{
gimple *g = gsi_stmt (gsi);
statement_count++;
if (dump_file)
{
fprintf (dump_file, "----- Statement count: %d -----\n", statement_count);
print_gimple_stmt (dump_file, g, 0, TDF_VOPS|TDF_MEMSYMS);
}
}
}
Finally, we add summary statistics that report the total number of basic blocks and statements encountered.
// Print summary information
if (dump_file)
{
fprintf (dump_file, "------------------------------------\n");
fprintf (dump_file, "Total Basic Blocks: %d\n", block_count);
fprintf (dump_file, "Total Gimple Statements: %d\n", statement_count);
fprintf (dump_file, "------------------------------------\n\n");
}
return 0;
Completed execute()
Code:
unsigned int
pass_kzaw::execute (function *fun)
{
basic_block block;
int block_count = 0, statement_count = 0;
// Traverse basic blocks in the current function
FOR_EACH_BB_FN (block, fun)
{
block_count++;
if (dump_file)
{
fprintf (dump_file, "===== Basic block count: %d =====\n", block_count);
}
// For each basic block, traverse statements
for (gimple_stmt_iterator gsi = gsi_start_bb (block); !gsi_end_p (gsi); gsi_next (&gsi))
{
gimple *g = gsi_stmt (gsi);
statement_count++;
if (dump_file)
{
fprintf (dump_file, "----- Statement count: %d -----\n", statement_count);
print_gimple_stmt (dump_file, g, 0, TDF_VOPS|TDF_MEMSYMS);
}
}
}
// Print summary information
if (dump_file)
{
fprintf (dump_file, "------------------------------------\n");
fprintf (dump_file, "Total Basic Blocks: %d\n", block_count);
fprintf (dump_file, "Total Gimple Statements: %d\n", statement_count);
fprintf (dump_file, "------------------------------------\n\n");
}
return 0;
}
Once you have finished editing the tree-name.cc
file and the logic, please rebuild the gcc
in your build directory using make
.
Note that we don't have to do all of these
Makefile.in
stuff as it is only done the first time you add a pass.
When you go to your test file, make and run again, this will be the output!
Dump File Output:
;; Function function1 (function1, funcdef_no=0, decl_uid=2335, cgraph_uid=1, symbol_order=0)
===== Basic block count: 1 =====
----- Statement count: 1 -----
# .MEM_2 = VDEF <.MEM_1(D)>
printf ("I\'m function1!\n");
----- Statement count: 2 -----
# VUSE <.MEM_2>
return;
------------------------------------
Total Basic Blocks: 1
Total Gimple Statements: 2
------------------------------------
void function1 ()
{
<bb 2> :
printf ("I\'m function1!\n");
return;
}
;; Function function2 (function2, funcdef_no=1, decl_uid=2337, cgraph_uid=2, symbol_order=1)
===== Basic block count: 1 =====
----- Statement count: 1 -----
# .MEM_2 = VDEF <.MEM_1(D)>
printf ("I\'m function2!\n");
----- Statement count: 2 -----
# VUSE <.MEM_2>
return;
------------------------------------
Total Basic Blocks: 1
Total Gimple Statements: 2
------------------------------------
void function2 ()
{
<bb 2> :
printf ("I\'m function2!\n");
return;
}
;; Function function3 (function3, funcdef_no=2, decl_uid=2339, cgraph_uid=3, symbol_order=2)
===== Basic block count: 1 =====
----- Statement count: 1 -----
# .MEM_2 = VDEF <.MEM_1(D)>
printf ("I\'m function3!\n");
----- Statement count: 2 -----
# VUSE <.MEM_2>
return;
------------------------------------
Total Basic Blocks: 1
Total Gimple Statements: 2
------------------------------------
void function3 ()
{
<bb 2> :
printf ("I\'m function3!\n");
return;
}
;; Function main (main, funcdef_no=3, decl_uid=2341, cgraph_uid=4, symbol_order=3)
===== Basic block count: 1 =====
----- Statement count: 1 -----
# .MEM_2 = VDEF <.MEM_1(D)>
function1 ();
----- Statement count: 2 -----
# .MEM_3 = VDEF <.MEM_2>
function2 ();
----- Statement count: 3 -----
# .MEM_4 = VDEF <.MEM_3>
function3 ();
----- Statement count: 4 -----
_5 = 0;
===== Basic block count: 2 =====
----- Statement count: 5 -----
<L0>:
----- Statement count: 6 -----
# VUSE <.MEM_4>
return _5;
------------------------------------
Total Basic Blocks: 2
Total Gimple Statements: 6
------------------------------------
int main ()
{
int D.2344;
int _5;
<bb 2> :
function1 ();
function2 ();
function3 ();
_5 = 0;
<bb 3> :
<L0>:
return _5;
}
This code can be run both in x86
and aarch64
servers!
That brings us to the end of Project Stage 01! 😄
Conclusion
This stage took me a few tries to get running, but that made me understand all the more about creating gcc
passes. Even a simple thing such as creating a pass can take so long if you are not being cautious about your file names, function names and directories.
All my learnings from my errors are condensed into a section at the start of this post. Please make sure to check it out so that you don't repeat my mistakes.
I will see you in Project Stage 02. Please stay safe!