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.