Project stage-3 (testing & reflection)
Introduction
In this blog, we will test and validate the prune-clones I made last time. In this process, I will explain how I modified the files passes.cc and passes.def, and why I made prune_clones.h separately because prune_clones.cc was not enough.
passes.cc and passes.def modification process
1. Modify passes.cc
The passes.cc file defines and implements several optimization passes for GCC, where you might want to add a prune-clones pass:
- Define and implement prune_clones related classes and functions.
- Clearly define what role this path plays in the overall optimization process of the compiler.
// passes.cc
#include "gcc.h"
...
#include "prune_clones.h"
// function generating prune_clones pass
gimple_opt_pass * make_pass_prune_clones(gcc::context *ctxt)
{
return new pass_prune_clones(ctxt);
}
The passes.def file defines the optimization passes used by GCC. You can modify this file to add a prune_clones pass. Passes.def usually defines the name and order of the passes.
// passes.def
#include "gcc.h"
...
NEXT_PASS(pass_prune_clones);
...
Why I added prune_clones.h
prune_clones.cc is usually a file that contains the implementation of the pass. However, it may be desirable to separate the definition and implementation of the pass, as prune_clones passes may be required in other parts as well. This helps to increase the modularity and reusability of the code. In this project, prune_clones feature needs to be added in Makefile and passes files.
- prune_clones.cc
In the last project, I supplemented the function of the prune-clone function a little more. 'prune_clones' iterates through the cloned versions of a function represented by nodes, compares each clone to the default version, and removes any duplicates it finds.
unsigned int prune_clones(cgraph_node *node);
// Constructor definition for pass_prune_clones
pass_prune_clones::pass_prune_clones(gcc::context *ctxt)
: gimple_opt_pass(pass_data_prune_clones, ctxt) {}
unsigned int pass_prune_clones::execute(function *fun) {
for (cgraph_node *node = cgraph_nodes; node; node = node->next) {
if (node->simd_clone) {
printf("Found SIMD clones for function: %s\n", cgraph_node_name(node));
prune_clones(node);
}
}
return 0;
}
namespace {
// Define the pass data structure
const pass_data pass_data_prune_clones = {
GIMPLE_PASS, // type
"prune_clones", // name
OPTGROUP_NONE, // optinfo_flags
TV_NONE, // tv_id
PROP_gimple_any, // PROP_gimple
0, // properties_provided
0, // properties_destroyed
0, // todo_flags_start
0 // todo_flags_finish
};
gimple_opt_pass *make_pass_prune_clones(gcc::context *ctxt) {
return new pass_prune_clones(ctxt);
}
} // namespace
// Function to prune duplicate clones
unsigned int prune_clones(cgraph_node *node) {
printf("Pruning clones for function: %s\n", cgraph_node_name(node));
cgraph_function_version_info *default_fvi = node->function_version();
cgraph_function_version_info *curr_fvi = node->function_version();
cgraph_function_version_info *prev_fvi = nullptr;
default_fvi
is initialized to point to the default version of the function associated with node
.curr_fvi
is also initialized to the default version initially.prev_fvi
is set to nullptr to keep track of the previous version as we iterate through the list of versions.while (curr_fvi->next) {
prev_fvi = curr_fvi;
// Get the next clone
curr_fvi = curr_fvi->next;
// Compare to the default node
// Prune if duplicate
if(curr_fvi->clone_of == default_fvi->clone_of) {
printf("Removing duplicate clone: %s\n", cgraph_node_name(curr_fvi->clone_of));
prev_fvi->next = curr_fvi->next;
delete curr_fvi;
}
}
return 0;
}
- This while loop iterates through the list of function versions (curr_fvi) starting from the default version (default_fvi). It stops when there are no more clones (curr_fvi->next is nullptr).
- Inside the loop, it checks if the current function version (curr_fvi) is a duplicate clone of the default version (default_fvi). If they are clones of the same function (clone_of refers to the original function node):
- It adjusts the linked list to skip over curr_fvi (
prev_fvi-> next = curr_fvi->
next). - It deletes the current function version (delete curr_fvi), freeing memory associated with the duplicate clone.
- prune_clones.h
#ifndef PRUNE_CLONES_H
#define PRUNE_CLONES_H
#include "context.h"
#include "tree-pass.h"
#include "cgraph.h"
class pass_prune_clones : public gimaple_opt_pass {
public:
pass_prune_clones(gcc::context *ctxt);
unsigned int execute(function *fun) final override;
};
gimple_opt_pass *make_pass_prune_clones(gcc::context *ctxt);
unsigned int prune_clones(cgraph_node *node);
#endif // PRUNE_CLONES_H
I encountered many difficulties while learning this subject, but I was able to learn and grow a lot along the way. As I went through the process of analyzing and modifying the structure of the code, I was able to gain a deep understanding of the internal behavior of the GCC. In addition, the experience of adding and managing optimization passes helped me hone important skills in software development.
댓글
댓글 쓰기