Project - Stage 3: Tidy & Wrap
Project - Stage 3: Tidy & Wrap
In Stage 2, I created a Pass that determines whether the function should be pruned or not by comparing the cloned function and the original function's basic block and Gimple code in each basic block. This works fine when there is only one cloned function. In Stage 3, I am going to cloned more than one function to see if it can apply to all the functions. My Pass is below:
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "tree-pass.h"
#include "cgraph.h"
#include "function.h"
#include "basic-block.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "gimple-pretty-print.h"
#include "cfg.h"
#include <string>
#include <vector>
#include <map>
namespace{
const pass_data pass_data_project = {
GIMPLE_PASS, /* type */
"pass_project", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_NONE, /* tv_id */
PROP_cfg, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
// Pass class
class pass_project : public gimple_opt_pass {
std::vector<std::string> functions;
std::map<std::string, std::vector<int>> gimpleCodes;
std::map<std::string, int> bbCnts;
public:
// Constructor
pass_project(gcc::context* ctxt) : gimple_opt_pass(pass_data_project, ctxt){};
unsigned int execute(function* fun) override{
cgraph_node* node = cgraph_node::get(fun->decl);
std::string currFunName = node->name();
basic_block bb;
int bb_cnt = 0;
int funIndex = findClonedFunction(currFunName);
if(dump_file){
if(funIndex != EOF){
fprintf(dump_file, "===== This %s is a cloned function =====\n", currFunName.c_str());
}else{
fprintf(dump_file, "===== This is not a clonded function =====\n");
}
}
// Store function names
functions.push_back(currFunName);
// Store Gimple code
FOR_EACH_BB_FN(bb, fun){
bb_cnt++;
for(gimple_stmt_iterator gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)){
gimple* stmt = gsi_stmt(gsi);
gimpleCodes[node->name()].push_back(gimple_code(stmt));
}
}
bbCnts[currFunName] = (bb_cnt);
if(dump_file){
if(funIndex == EOF){
fprintf(dump_file, "NOPRUNE: %s\n\n", removeSuffix(currFunName).c_str());
}else{
fprintf(dump_file, "%s: %s\n\n", isIdenticalFunction(currFunName, funIndex) ? "PRUNE" : "NOPRUNE", removeSuffix(currFunName).c_str());
}
}
return 0;
}
// Function recevie the name of current checking function
// Return the function name index in the vector if the vector has the same function name but it is not a resolver.
// Reutrn -1 if no function name match.
int findClonedFunction(std::string checkingFunName){
if(functions.empty()){
return -1;
}
size_t dotPos = checkingFunName.find('.');
std::string funName = checkingFunName.substr(0, dotPos);
std::string suffix = "";
if(dotPos != std::string::npos){
suffix = checkingFunName.substr(dotPos + 1);
}
for(size_t i = 0; i < functions.size(); ++i){
if(funName == functions[i] && suffix != "resolver"){
return i;
}
}
return -1;
}
bool isIdenticalFunction(std::string currFunName, int orgFunIndex){
fprintf(dump_file, "========== Comparing Original Function and the Cloned Function ==========\n");
fprintf(dump_file, "*** Compare Basic Block ***\n");
fprintf(dump_file, "Current Function Basic Block: %d\n"
"Original Function Basic Block: %d\n", bbCnts[currFunName], bbCnts[functions[orgFunIndex]]);
if(bbCnts[currFunName] != bbCnts[functions[orgFunIndex]]){
fprintf(dump_file, "*** Differnt amount of Basic Blocks ***\n");
return false;
}
fprintf(dump_file, "*** They have same amount of Basic Block ***\n");
fprintf(dump_file, "Cloned FUnction Gimple code(Current) | Orignal Function Gimple code\n");
for(size_t i = 0; i < gimpleCodes[currFunName].size(); ++i){
if(gimpleCodes[currFunName][i] == gimpleCodes[functions[orgFunIndex]][i]){
fprintf(dump_file, "%d | %d\n", gimpleCodes[currFunName][i], gimpleCodes[functions[orgFunIndex]][i]);
}else{
return false;
}
}
return true;
}
std::string removeSuffix(std::string fullName){
size_t dotPos = fullName.find('.');
return dotPos != std::string::npos ? fullName.substr(0, dotPos) : fullName;
}
};
}
//Custom pass creation function
gimple_opt_pass* make_pass_project(gcc::context* ctxt){
return new pass_project(ctxt);
}
Test Case
To generate additional cloned functions, I created a test case file containing three functions marked for cloning.
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
CLONE_ATTRIBUTE
int process_int16(int16_t *data, size_t len) {
int sum = 0;
for (size_t i = 0; i < len; ++i) {
sum += data[i] * 3 - data[i] / 2 + data[i] % 7;
}
return sum;
}
CLONE_ATTRIBUTE
float process_floats(float *data, size_t len) {
float sum = 0.0f;
for (size_t i = 0; i < len; ++i) {
sum += data[i] * 1.5f - data[i] / 3.0f + data[i] * data[i];
}
return sum;
}
CLONE_ATTRIBUTE
int process_int32(int32_t *data, size_t len) {
int result = 0;
for (size_t i = 0; i < len; ++i) {
result += (data[i] & 0xFF) + (data[i] >> 3) - (data[i] << 1);
}
return result;
}
int main() {
int16_t a[128];
float b[128];
int32_t c[128];
for (int i = 0; i < 128; ++i) {
a[i] = i;
b[i] = (float)i;
c[i] = i;
}
printf("process_int16: %d\n", process_int16(a, 128));
printf("process_floats: %.2f\n", process_floats(b, 128));
printf("process_int32: %d\n", process_int32(c, 128));
return 0;
}
To see the result I run:
PRUNE
gcc -D 'CLONE_ATTRIBUTE=__attribute__((target_clones("default", "popcnt")))' -march=x86-64 -O3 -fdump-tree-pass_project testcase.c -o testcase_prune
NOPRUNE
gcc -D 'CLONE_ATTRIBUTE=__attribute__((target_clones("default", "arch=x86-64-v3")))' -march=x86-64 -O3 -fdump-tree-pass_project testcase.c -o testcase_noprune
The result for testcase_prune
The result for testcase_noprune
As you can see, it works really well. However, I believe there's room to analyze the function cloning feature more deeply.
Comments
Post a Comment