From 201e2935ed67632b163b75457e9823e4c8f8a73b Mon Sep 17 00:00:00 2001 From: York Jasper Niebuhr Date: Tue, 21 Oct 2025 16:45:52 +0200 Subject: [PATCH] Added COMPONENT_REF chain handling to pinpoint plugin --- playground/spslr_pinpoint.cpp | 240 ++++++++++++++++++++++++++-------- 1 file changed, 183 insertions(+), 57 deletions(-) diff --git a/playground/spslr_pinpoint.cpp b/playground/spslr_pinpoint.cpp index 3e96394..e0cfef6 100644 --- a/playground/spslr_pinpoint.cpp +++ b/playground/spslr_pinpoint.cpp @@ -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) {} +struct GCRChain { + struct Link { + tree t = NULL_TREE; + bool relevant = false; + SPSLROffsetofCallData call_data; + }; + + bool relevant = false; + std::list 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; + + if (TREE_CODE(*tp) == COMPONENT_REF) + *found_flag = 1; + + return NULL_TREE; +} + +static bool contains_component_ref(tree ref) { + 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) { - // TODO -> nested COMPONENT_REFs! - - if (!ref || TREE_CODE(ref) != COMPONENT_REF) - return NULL_TREE; - - SPSLROffsetofCallData call_data; - if (!is_relevant_offsetof(ref, call_data.target, call_data.member)) - return NULL_TREE; - - // Build call gimple statement - - location_t loc = EXPR_LOCATION(ref); - tree result_type = TREE_TYPE(ref); - - if (!require_spslr_offsetof_decl()) { - std::cerr << "spslr_pinpoint -> failed to instrument COMPONENT_REF (" << SPSLR_OFFSETOF << " unavailable)" << std::endl; + GCRChain gcrc; + if (!gimple_component_ref_chain(ref, gcrc)) { + std::cerr << "spslr_pinpoint -> failed to parse COMPONENT_REF chain" << std::endl; return NULL_TREE; } - call_data.uid = spslr_offsetof_next_uid++; - spslr_offsetof_calls.emplace(call_data.uid, call_data); - - tree call_uid_tree = build_int_cst(long_unsigned_type_node, call_data.uid); - - gimple* call_stmt = gimple_build_call(spslr_offsetof_decl, 1, call_uid_tree); - tree offset_tmp = create_tmp_var(size_type_node, NULL); - gimple_call_set_lhs(call_stmt, offset_tmp); - gsi_insert_before(gsi, call_stmt, GSI_SAME_STMT); - - // Get base as char* - tree base = TREE_OPERAND(ref, 0); - tree base_addr; - - if (TREE_CODE(base) == ADDR_EXPR) { - // base_addr = base; - std::cerr << "spslr_pinpoint -> unexpected ADDR_EXPR as COMPONENT_REF base!" << std::endl; + if (!gcrc.relevant) return NULL_TREE; - } else { - base_addr = build_fold_addr_expr(base); - } + + // Store base pointer in a temporary variable (as char*) tree char_ptr_type = build_pointer_type(char_type_node); - tree field_ptr_type = build_pointer_type(result_type); + tree base_tmp = NULL_TREE; + { + tree base_ptr; - // Temporary variable for base pointer needed for GIMPLE - 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); + 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); + } - // Add result of __spslr_offsetof, cast back to field pointer, then dereference - tree plus = build2_loc(loc, POINTER_PLUS_EXPR, char_ptr_type, base_char_ptr_tmp, offset_tmp); + tree base_char_ptr = fold_convert(char_ptr_type, base_ptr); + base_tmp = create_tmp_var(char_ptr_type, NULL); - tree plus_tmp = create_tmp_var(TREE_TYPE(plus), NULL); - gimple* plus_tmp_assignment = gimple_build_assign(plus_tmp, plus); - gsi_insert_before(gsi, plus_tmp_assignment, GSI_SAME_STMT); + gimple* base_tmp_assignment = gimple_build_assign(base_tmp, base_char_ptr); + gsi_insert_before(gsi, base_tmp_assignment, GSI_SAME_STMT); + } - tree cast_back = fold_convert(field_ptr_type, plus_tmp); + // For each component ref in chain, add the member offset to the pointer - tree res_ptr_tmp = create_tmp_var(TREE_TYPE(cast_back), NULL); - gimple* res_ptr_tmp_assignment = gimple_build_assign(res_ptr_tmp, cast_back); - gsi_insert_before(gsi, res_ptr_tmp_assignment, GSI_SAME_STMT); + 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; + } - 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; + 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); + gimple_call_set_lhs(call_stmt, offset_tmp); + gsi_insert_before(gsi, call_stmt, GSI_SAME_STMT); + + // Add call return value to current base pointer + tree addition = build2(POINTER_PLUS_EXPR, char_ptr_type, base_tmp, offset_tmp); + 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); + } else { + // 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; + } + + // Add constant offset to current base pointer + 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); + } + } + + // Cast char pointer back to field pointer and dereference + + tree field_ptr_type = build_pointer_type(TREE_TYPE(ref)); + + tree field_ptr = fold_convert(field_ptr_type, base_tmp); + tree field_ptr_tmp = create_tmp_var(field_ptr_type, NULL); + 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 offset0 = fold_convert(field_ptr_type, build_int_cst(sizetype, 0)); + tree result_ref = build2(MEM_REF, TREE_TYPE(ref), field_ptr_tmp, offset0); + + return result_ref; } static void instrument_tree(const std::list& path, gimple_stmt_iterator* gsi, unsigned& cancel_levels) { if (path.empty() || !gsi) 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) return; gimple_set_modified(gsi_stmt(*gsi), true); - *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.