BATCH 3 | Project Stage 2, Part 2 - Testing, Errors and Fixes!
Project Stage 2
Testing, Errors and Fixes!
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"
#include <string.h>
namespace {
const pass_data count_pass_data = {
GIMPLE_PASS,
"count",
OPTGROUP_NONE,
TV_NONE,
PROP_gimple_any,
0, 0, 0, 0
};
static bool seen_clone = false;
static char stored_base[256] = "";
static int stored_bb_count = 0;
static int stored_gimple_count = 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;
}
// Execute is called once per function.
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);
}
// Count the basic blocks and GIMPLE statements.
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++;
}
// Step 3: Compare cloned functions.
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 {
// Second clone encountered; compare with stored clone.
if (strcmp(stored_base, base_name) == 0 &&
stored_bb_count == bb_count &&
stored_gimple_count == gimple_count) {
// They are substantially the same.
if (dump_file)
fprintf(dump_file, "PRUNE: %s\n", stored_base);
else
fprintf(stderr, "PRUNE: %s\n", stored_base);
} else {
// They differ.
if (dump_file)
fprintf(dump_file, "NOPRUNE: %s\n", stored_base);
else
fprintf(stderr, "NOPRUNE: %s\n", stored_base);
}
// Reset stored clone info for the next pair.
seen_clone = false;
}
}
// Step 4: Output the function's basic info (always).
if (dump_file)
fprintf(dump_file, "Function %s: %d BBs, %d GIMPLE stmts\n",
full_name, bb_count, gimple_count);
else
fprintf(stderr, "Function %s: %d BBs, %d GIMPLE stmts\n",
full_name, bb_count, gimple_count);
return 0;
}
};
}
opt_pass *make_count_pass(gcc::context *ctxt) {
return new count_pass(ctxt);
}
Let's investigate!
// Step 3: Compare cloned functions.
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 {
// Second clone encountered; compare with stored clone.
if (strcmp(stored_base, base_name) == 0 &&
stored_bb_count == bb_count &&
stored_gimple_count == gimple_count) {
// They are substantially the same.
if (dump_file)
fprintf(dump_file, "PRUNE: %s\n", stored_base);
else
fprintf(stderr, "PRUNE: %s\n", stored_base);
} else {
// They differ.
if (dump_file)
fprintf(dump_file, "NOPRUNE: %s\n", stored_base);
else
fprintf(stderr, "NOPRUNE: %s\n", stored_base);
}
// Reset stored clone info for the next pair.
seen_clone = false;
}
}
// Determine if this function is a resolver.
bool is_resolver = (strstr(full_name, "resolver") != nullptr);
const char *dot = strchr(full_name, '.');
if (dot != nullptr) {
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 {
strncpy(base_name, full_name, sizeof(base_name) - 1);
base_name[sizeof(base_name) - 1] = '\0';
fprintf(stderr, "Processing original function (for clone comparison): %s\n", full_name);
}
...
...
...
// Only perform clone comparison for non-resolver functions.
if (!is_resolver) {
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) {
// They are substantially the same.
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;
}
} else {
fprintf(stderr, "Skipping resolver function: %s\n", full_name);
}
End of Part 2
Thank you so much for taking the time to read this blog post. Stay tuned for Part 3 of Project Stage 2 where I do function comparisons and lessons learned throughout the process of getting my clone-prune analysis pass to perform as intended!
Comments
Post a Comment