Added COMPONENT_REF chain handling to pinpoint plugin

This commit is contained in:
York Jasper Niebuhr 2025-10-21 16:45:52 +02:00
parent 85ca5b9775
commit 201e2935ed

View File

@ -357,87 +357,213 @@ struct log_component_refs_pass : gimple_opt_pass {
log_component_refs_pass::log_component_refs_pass(gcc::context *ctxt) : gimple_opt_pass(log_component_refs_pass_data, ctxt) {} log_component_refs_pass::log_component_refs_pass(gcc::context *ctxt) : gimple_opt_pass(log_component_refs_pass_data, ctxt) {}
static tree gimple_instrument_offsetof_maybe(tree ref, gimple_stmt_iterator* gsi) { struct GCRChain {
// TODO -> nested COMPONENT_REFs! struct Link {
tree t = NULL_TREE;
if (!ref || TREE_CODE(ref) != COMPONENT_REF) bool relevant = false;
return NULL_TREE;
SPSLROffsetofCallData call_data; SPSLROffsetofCallData call_data;
if (!is_relevant_offsetof(ref, call_data.target, call_data.member)) };
bool relevant = false;
std::list<Link> links;
tree base = NULL_TREE;
};
static tree walk_tree_contains_component_ref(tree* tp, int* walk_subtrees, void* data) {
int* found_flag = (int*)data;
if (!tp || !*tp)
return NULL_TREE; return NULL_TREE;
// Build call gimple statement if (TREE_CODE(*tp) == COMPONENT_REF)
*found_flag = 1;
location_t loc = EXPR_LOCATION(ref); return NULL_TREE;
tree result_type = TREE_TYPE(ref); }
if (!require_spslr_offsetof_decl()) { static bool contains_component_ref(tree ref) {
std::cerr << "spslr_pinpoint -> failed to instrument COMPONENT_REF (" << SPSLR_OFFSETOF << " unavailable)" << std::endl; int found_flag = 0;
walk_tree(&ref, walk_tree_contains_component_ref, &found_flag, NULL);
return found_flag != 0;
}
static bool gimple_component_ref_chain(tree ref, GCRChain& chain) {
if (!ref)
return false;
if (TREE_CODE(ref) != COMPONENT_REF) {
if (chain.links.empty())
return false;
if (contains_component_ref(ref))
return false;
chain.base = ref;
return true;
}
GCRChain::Link link;
link.t = ref;
link.relevant = is_relevant_offsetof(ref, link.call_data.target, link.call_data.member);
if (link.relevant) {
link.call_data.uid = spslr_offsetof_next_uid++;
chain.relevant = true;
}
chain.links.push_front(link);
return gimple_component_ref_chain(TREE_OPERAND(ref, 0), chain);
}
static HOST_WIDE_INT get_field_offset_bits(tree field)
{
gcc_assert(TREE_CODE(field) == FIELD_DECL);
// Get the offset expression (may not be a constant early in compilation)
tree offset_tree = DECL_FIELD_OFFSET(field);
HOST_WIDE_INT bitpos_within_unit = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(field));
// Convert the byte offset to bits
HOST_WIDE_INT bit_offset = bitpos_within_unit;
if (offset_tree && TREE_CODE(offset_tree) == INTEGER_CST)
bit_offset += tree_to_shwi(offset_tree) * BITS_PER_UNIT;
return bit_offset;
}
static bool get_field_offset(tree field, Member::OFF& off) {
if (!field || TREE_CODE(field) != FIELD_DECL)
return false;
if (DECL_BIT_FIELD(field))
return false;
tree byte_offset_tree = DECL_FIELD_OFFSET(field);
tree bit_offset_tree = DECL_FIELD_BIT_OFFSET(field);
if (!byte_offset_tree || !bit_offset_tree)
return false;
if (TREE_CODE(byte_offset_tree) != INTEGER_CST || TREE_CODE(bit_offset_tree) != INTEGER_CST)
return false;
HOST_WIDE_INT byte_offset = tree_to_uhwi(byte_offset_tree);
HOST_WIDE_INT bit_offset = tree_to_uhwi(bit_offset_tree);
if (bit_offset % 8 != 0)
return false;
off = (Member::OFF)byte_offset + (Member::OFF)bit_offset / 8;
return true;
}
static tree gimple_instrument_offsetof_maybe(tree ref, gimple_stmt_iterator* gsi) {
GCRChain gcrc;
if (!gimple_component_ref_chain(ref, gcrc)) {
std::cerr << "spslr_pinpoint -> failed to parse COMPONENT_REF chain" << std::endl;
return NULL_TREE; return NULL_TREE;
} }
call_data.uid = spslr_offsetof_next_uid++; if (!gcrc.relevant)
spslr_offsetof_calls.emplace(call_data.uid, call_data); return NULL_TREE;
tree call_uid_tree = build_int_cst(long_unsigned_type_node, call_data.uid); // Store base pointer in a temporary variable (as char*)
gimple* call_stmt = gimple_build_call(spslr_offsetof_decl, 1, call_uid_tree); tree char_ptr_type = build_pointer_type(char_type_node);
tree base_tmp = NULL_TREE;
{
tree base_ptr;
if (TREE_CODE(gcrc.base) == ADDR_EXPR) {
std::cerr << "spslr_pinpoint -> unexpected ADDR_EXPR as COMPONENT_REF base!" << std::endl;
return NULL_TREE;
} else {
base_ptr = build_fold_addr_expr(gcrc.base);
}
tree base_char_ptr = fold_convert(char_ptr_type, base_ptr);
base_tmp = create_tmp_var(char_ptr_type, NULL);
gimple* base_tmp_assignment = gimple_build_assign(base_tmp, base_char_ptr);
gsi_insert_before(gsi, base_tmp_assignment, GSI_SAME_STMT);
}
// For each component ref in chain, add the member offset to the pointer
for (const GCRChain::Link& cr : gcrc.links) {
// NOTE -> Could fold into single call here (needs to track what offsets contribute, +irrelevant combined)
if (cr.relevant) {
// Add offsetof call
if (!require_spslr_offsetof_decl()) {
std::cerr << "spslr_pinpoint -> failed to instrument COMPONENT_REF ("
<< SPSLR_OFFSETOF << " unavailable)" << std::endl;
return NULL_TREE;
}
spslr_offsetof_calls.emplace(cr.call_data.uid, cr.call_data);
// Call to __spslr_offsetof is a separate statement
tree call_uid_arg = build_int_cst(long_unsigned_type_node, cr.call_data.uid);
gimple* call_stmt = gimple_build_call(spslr_offsetof_decl, 1, call_uid_arg);
tree offset_tmp = create_tmp_var(size_type_node, NULL); tree offset_tmp = create_tmp_var(size_type_node, NULL);
gimple_call_set_lhs(call_stmt, offset_tmp); gimple_call_set_lhs(call_stmt, offset_tmp);
gsi_insert_before(gsi, call_stmt, GSI_SAME_STMT); gsi_insert_before(gsi, call_stmt, GSI_SAME_STMT);
// Get base as char* // Add call return value to current base pointer
tree base = TREE_OPERAND(ref, 0); tree addition = build2(POINTER_PLUS_EXPR, char_ptr_type, base_tmp, offset_tmp);
tree base_addr; base_tmp = create_tmp_var(char_ptr_type, NULL);
gimple* addition_assignment = gimple_build_assign(base_tmp, addition);
if (TREE_CODE(base) == ADDR_EXPR) { gsi_insert_before(gsi, addition_assignment, GSI_SAME_STMT);
// base_addr = base;
std::cerr << "spslr_pinpoint -> unexpected ADDR_EXPR as COMPONENT_REF base!" << std::endl;
return NULL_TREE;
} else { } else {
base_addr = build_fold_addr_expr(base); // Add offsetof contant
tree field = TREE_OPERAND(cr.t, 1);
Member::OFF offset = 0;
if (!get_field_offset(field, offset)) {
std::cerr << "spslr_pinpoint -> failed to get offset of an irrelevant member "
<< "in a relevant COMPONENT_REF chain" << std::endl;
return NULL_TREE;
} }
tree char_ptr_type = build_pointer_type(char_type_node); // Add constant offset to current base pointer
tree field_ptr_type = build_pointer_type(result_type); tree addition = build2(POINTER_PLUS_EXPR, char_ptr_type, base_tmp, build_int_cst(sizetype, offset));
base_tmp = create_tmp_var(char_ptr_type, NULL);
gimple* addition_assignment = gimple_build_assign(base_tmp, addition);
gsi_insert_before(gsi, addition_assignment, GSI_SAME_STMT);
}
}
// Temporary variable for base pointer needed for GIMPLE // Cast char pointer back to field pointer and dereference
tree base_char_ptr = fold_convert(char_ptr_type, base_addr);
tree base_char_ptr_tmp = create_tmp_var(char_ptr_type, NULL);
gimple* base_char_ptr_tmp_assignment = gimple_build_assign(base_char_ptr_tmp, base_char_ptr);
gsi_insert_before(gsi, base_char_ptr_tmp_assignment, GSI_SAME_STMT);
// Add result of __spslr_offsetof, cast back to field pointer, then dereference tree field_ptr_type = build_pointer_type(TREE_TYPE(ref));
tree plus = build2_loc(loc, POINTER_PLUS_EXPR, char_ptr_type, base_char_ptr_tmp, offset_tmp);
tree plus_tmp = create_tmp_var(TREE_TYPE(plus), NULL); tree field_ptr = fold_convert(field_ptr_type, base_tmp);
gimple* plus_tmp_assignment = gimple_build_assign(plus_tmp, plus); tree field_ptr_tmp = create_tmp_var(field_ptr_type, NULL);
gsi_insert_before(gsi, plus_tmp_assignment, GSI_SAME_STMT); gimple* field_ptr_tmp_assignment = gimple_build_assign(field_ptr_tmp, field_ptr);
gsi_insert_before(gsi, field_ptr_tmp_assignment, GSI_SAME_STMT);
tree cast_back = fold_convert(field_ptr_type, plus_tmp); tree offset0 = fold_convert(field_ptr_type, build_int_cst(sizetype, 0));
tree result_ref = build2(MEM_REF, TREE_TYPE(ref), field_ptr_tmp, offset0);
tree res_ptr_tmp = create_tmp_var(TREE_TYPE(cast_back), NULL); return result_ref;
gimple* res_ptr_tmp_assignment = gimple_build_assign(res_ptr_tmp, cast_back);
gsi_insert_before(gsi, res_ptr_tmp_assignment, GSI_SAME_STMT);
tree off0 = build_int_cst(sizetype, 0);
tree new_ref = build2_loc(loc, MEM_REF, result_type, res_ptr_tmp, fold_convert(field_ptr_type, off0));
return new_ref;
} }
static void instrument_tree(const std::list<tree*>& path, gimple_stmt_iterator* gsi, unsigned& cancel_levels) { static void instrument_tree(const std::list<tree*>& path, gimple_stmt_iterator* gsi, unsigned& cancel_levels) {
if (path.empty() || !gsi) if (path.empty() || !gsi)
return; return;
tree instrumented_ref = gimple_instrument_offsetof_maybe(*path.back(), gsi); tree ref = *path.back();
if (!ref || TREE_CODE(ref) != COMPONENT_REF)
return;
cancel_levels = 1;
tree instrumented_ref = gimple_instrument_offsetof_maybe(ref, gsi);
if (!instrumented_ref) if (!instrumented_ref)
return; return;
gimple_set_modified(gsi_stmt(*gsi), true); gimple_set_modified(gsi_stmt(*gsi), true);
*path.back() = instrumented_ref; *path.back() = instrumented_ref;
cancel_levels = 1;
// At this point, instrumented_ref is a MEM_REF node (off=0). A wrapping ADDR_EXPR cancels it out. // At this point, instrumented_ref is a MEM_REF node (off=0). A wrapping ADDR_EXPR cancels it out.