Added COMPONENT_REF chain handling to pinpoint plugin
This commit is contained in:
parent
85ca5b9775
commit
201e2935ed
@ -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) {}
|
||||
|
||||
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;
|
||||
|
||||
struct GCRChain {
|
||||
struct Link {
|
||||
tree t = NULL_TREE;
|
||||
bool relevant = false;
|
||||
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;
|
||||
|
||||
// Build call gimple statement
|
||||
if (TREE_CODE(*tp) == COMPONENT_REF)
|
||||
*found_flag = 1;
|
||||
|
||||
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;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
call_data.uid = spslr_offsetof_next_uid++;
|
||||
spslr_offsetof_calls.emplace(call_data.uid, call_data);
|
||||
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;
|
||||
}
|
||||
|
||||
tree call_uid_tree = build_int_cst(long_unsigned_type_node, call_data.uid);
|
||||
static bool gimple_component_ref_chain(tree ref, GCRChain& chain) {
|
||||
if (!ref)
|
||||
return false;
|
||||
|
||||
gimple* call_stmt = gimple_build_call(spslr_offsetof_decl, 1, call_uid_tree);
|
||||
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;
|
||||
}
|
||||
|
||||
if (!gcrc.relevant)
|
||||
return NULL_TREE;
|
||||
|
||||
// Store base pointer in a temporary variable (as char*)
|
||||
|
||||
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);
|
||||
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;
|
||||
return NULL_TREE;
|
||||
// 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 {
|
||||
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);
|
||||
tree field_ptr_type = build_pointer_type(result_type);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// Cast char pointer back to field pointer and dereference
|
||||
|
||||
// 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 field_ptr_type = build_pointer_type(TREE_TYPE(ref));
|
||||
|
||||
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);
|
||||
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 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);
|
||||
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;
|
||||
return result_ref;
|
||||
}
|
||||
|
||||
static void instrument_tree(const std::list<tree*>& 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.
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user