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
Post a Comment