BATCH 3 | Project Stage 2, Part 1 - Clone-Pruning Analysis Pass
PROJECT STAGE 2
Clone-Pruning Analysis Pass
Instructions
1. Identifies one or more functions which have been cloned. These functions will have the name function.variant where the function portion is the same, and there will be a corresponding resolver named function.resolver.
3. Emit a message in the GCC diagnostic dump for the pass that indicates if the functions should be pruned (in the case that they're substantially the same) or not pruned (if they are different). The diagnostic dump may contain other information.
Let's Dive In!
In this blog, I will take you through the steps I followed to complete this stage of the project:
STEP 1: Start with Stage 1 Pass Code
Since my custom pass successfully integrated into the compiler, I started off by using my code as a starting point since this pass iterates over functions and prints diagnostic information including function names, GIMPLE statements counts and basic block counts. By using this as a foundation, I can make some updates on the code to further my analysis.
Here is my code for Project Stage 1:
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "tree.h"
#include "basic-block.h"
#include "function.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "tree-pass.h"
#include "context.h"
namespace {
const pass_data count_pass_data = {
GIMPLE_PASS,
"count",
OPTGROUP_NONE,
TV_NONE,
PROP_gimple_any,
0, 0, 0, 0
};
class count_pass : public gimple_opt_pass {
public:
count_pass(gcc::context *ctxt)
: gimple_opt_pass(count_pass_data, ctxt) {}
bool gate(function *fun) override {
(void) fun;
return true;
}
unsigned int execute(function *fun) override {
int bb_count = 0, gimple_count = 0;
basic_block bb;
FOR_EACH_BB_FN(bb, fun) {
for (gimple_stmt_iterator gsi = gsi_start_bb(bb);
!gsi_end_p(gsi);
gsi_next(&gsi)) {
gimple_count++;
}
bb_count++;
}
fprintf(stderr, "Function %s: %d BBs, %d GIMPLE stmts\n",
function_name(fun), bb_count, gimple_count);
}
return 0;
}
};
opt_pass *make_count_pass(gcc::context *ctxt) {
return new count_pass(ctxt);
}
- I used function_name(fun) to obtain a C string that represents the name of the function being compiled.
- I checked for a dot to determine whether this function name contains (.), otherwise it likely is not a cloned function.
- Lastly, I extracted the base name to use whenever I out put the diagnostic message later on (PRUNE or NOPRUNE).
unsigned int execute(function *fun) override {
// Step 2: Detect cloned functions by examining the function name.
const char *full_name = function_name(fun);
char base_name[256] = "";
bool is_clone = false;
const char *dot = strchr(full_name, '.');
if (dot != nullptr) {
// A dot indicates this function is a clone.
is_clone = true;
size_t len = dot - full_name;
if (len >= sizeof(base_name))
len = sizeof(base_name) - 1;
strncpy(base_name, full_name, len);
base_name[len] = '\0';
fprintf(stderr, "Detected clone for function: %s (full name: %s)\n",
base_name, full_name);
} else {
// Not a clone. Use the full name as the base name.
strncpy(base_name, full_name, sizeof(base_name) - 1);
base_name[sizeof(base_name) - 1] = '\0';
fprintf(stderr, "Processing original function: %s\n", full_name);
}
if (is_clone) {
if (!seen_clone) {
seen_clone = true;
strncpy(stored_base, base_name, sizeof(stored_base) - 1);
stored_base[sizeof(stored_base) - 1] = '\0';
stored_bb_count = bb_count;
stored_gimple_count = gimple_count;
} else {
if (strcmp(stored_base, base_name) == 0 &&
stored_bb_count == bb_count &&
stored_gimple_count == gimple_count) {
if (dump_file)
fprintf(dump_file, "PRUNE: %s\n", stored_base);
else
fprintf(stderr, "PRUNE: %s\n", stored_base);
} else {
if (dump_file)
fprintf(dump_file, "NOPRUNE: %s\n", stored_base);
else
fprintf(stderr, "NOPRUNE: %s\n", stored_base);
}
seen_clone = false;
}
}
Comments
Post a Comment