Project Stage III: Multi-Clone Analysis in GCC Pass

 Introduction

In Stage III, I enhanced my GCC pass to analyze programs with multiple cloned functions simultaneously, making individual PRUNE/NOPRUNE decisions for each clone. This involved refining GIMPLE code comparisons, handling compiler optimization challenges, and validating results on both x86_64 and aarch64 architectures.


Implementation

Core Logic (Key Code Snippets)

1. Data Structure for Clones

// Stores GIMPLE statements and operand types for each function  
struct GimpleStmtInfo {  
    int code;  
    std::vector<tree> operands;  
};  

std::map<std::string, std::vector<GimpleStmtInfo>> function_map;  
2. Clone Detection
// Identify cloned functions by name (e.g., "func.clone1")  
if (func_name.find(base_name + ".") == 0 &&  
    func_name.find(".resolver") == std::string::npos) {  
    // Compare GIMPLE sequences  
    bool is_same = (base_gimple == cloned_gimple);  
    fprintf(dump_file, "Result: %s\n", is_same ? "PRUNE" : "NOPRUNE");  
}  


3. GIMPLE Code Extraction

FOR_EACH_BB_FN(bb, fun) {  
    for (gimple_stmt_iterator gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {  
        gimple *stmt = gsi_stmt(gsi);  
        GimpleStmtInfo info;  
        info.code = gimple_code(stmt);  
        for (int i = 0; i < gimple_num_ops(stmt); i++)  
            info.operands.push_back(TREE_TYPE(gimple_op(stmt, i)));  
        function_map[func_name].push_back(info);  
    }  
}  


Test Case Design

File: clone-test-tpatel103.c

#include <stdio.h>  
#include <stdint.h>  

#define CLONE_ATTRIBUTE __attribute__((target_clones("default")))  

// Clone 1: PRUNE candidate  
CLONE_ATTRIBUTE  
float analyze_data(float *arr, int len) {  
    float sum = 0;  
    for (int i = 0; i < len; i++)  
        sum += arr[i] * 2.0f;  
    return sum;  
}  

// Clone 2: NOPRUNE candidate (modified operation)  
CLONE_ATTRIBUTE  
float analyze_data_v2(float *arr, int len) {  
    float sum = 0;  
    for (int i = 0; i < len; i++)  
        sum += arr[i] / 2.0f;  // Changed operation  
    return sum;  
}  

int main() {  
    float data[4] = {1.5, 2.5, 3.5, 4.5};  
    printf("Original: %f\n", analyze_data(data, 4));  
    printf("Modified: %f\n", analyze_data_v2(data, 4));  
    return 0;  
}   

Key Challenges & Solutions

1. GIMPLE Dump Corruption

  • Problem: Optimizations (-O2) caused truncated outputs like 8 instead of full codes.
  • Fix: Compiled with -O0 -fno-inline to disable optimizations.

2. Cross-Architecture Consistency

  • Validated identical function hashes between x86_64 and aarch64 dumps.

3. Multiple Clone Handling

  • Implemented nested loop comparisons:

for (auto &base_entry : function_map) {  
    for (auto &clone_entry : function_map) {  
        // Compare GIMPLE sequences  
    }  
}  

Code Limitations

1. Name Convention Dependency

  • Only detects clones with GCC-style .cloneN suffixes.

2. Operand Type Sensitivity

  • Fails to prune clones if operand types differ (e.g., float vs double).

3. Statement Coverage

  • Supports GIMPLE_ASSIGN, GIMPLE_CALL, but skips complex loop structures.


Reflections

Technical Learnings

  • GIMPLE Internals: Mastered extracting statement codes/operands through GCC’s internal APIs.
  • Multi-Clone Workflow: Developed efficient comparison logic using nested map structures.
  • Compiler Flags: Learned how -O0 and -fno-inline preserve code structure for analysis.

Project Insights

  • Testing Complexity: Creating clones with deliberate differences (e.g., / vs *) was crucial for validating NOPRUNE cases.
  • Architecture Independence: Verified that GIMPLE dumps are consistent across ISAs when optimizations are disabled.


Future Work

  • Add support for loop-variant clones
  • Integrate machine learning for pattern-based pruning


Comments

Popular posts from this blog

Lab-1: Exploring 6502 Assembly

Lab 2 - 6502 Math Lab