Finished stage0 of pinpoint plugin
This commit is contained in:
parent
3f97c2654d
commit
31f8c19b6f
@ -13,10 +13,20 @@ int plugin_init(struct plugin_name_args* plugin_info, struct plugin_gcc_version*
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Stage 0 -> logic that happens before all usual passes
|
||||
// Stage 0 -> separates relevant field offsets into function calls
|
||||
|
||||
register_callback(plugin_info->base_name, PLUGIN_ATTRIBUTES, on_register_attributes, NULL);
|
||||
register_callback(plugin_info->base_name, PLUGIN_FINISH_TYPE, on_finish_type, NULL);
|
||||
register_callback(plugin_info->base_name, PLUGIN_BUILD_COMPONENT_REF, on_preserve_component_ref, NULL);
|
||||
|
||||
struct register_pass_info separate_offset_pass_info;
|
||||
separate_offset_pass_info.pass = new separate_offset_pass(nullptr);
|
||||
separate_offset_pass_info.ref_pass_instance_number = 1;
|
||||
separate_offset_pass_info.reference_pass_name = "cfg";
|
||||
separate_offset_pass_info.pos_op = PASS_POS_INSERT_AFTER;
|
||||
register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, nullptr, &separate_offset_pass_info);
|
||||
|
||||
// Stage 1 -> TODO
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4,5 +4,6 @@
|
||||
#define SAFEGCC_TREE_H
|
||||
|
||||
#include <tree.h>
|
||||
#include <tree-pass.h>
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
target_sources(spslr_pinpoint PRIVATE on_register_attributes.cpp on_finish_type.cpp on_preserve_component_ref.cpp
|
||||
target.cpp separator.cpp)
|
||||
target.cpp separator.cpp separate_offset_pass.cpp)
|
||||
target_include_directories(spslr_pinpoint PRIVATE .)
|
||||
|
||||
247
pinpoint/stage0/separate_offset_pass.cpp
Normal file
247
pinpoint/stage0/separate_offset_pass.cpp
Normal file
@ -0,0 +1,247 @@
|
||||
#include <iostream> // TODO
|
||||
#include <stage0.h>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
|
||||
#include <pinpoint_error.h>
|
||||
|
||||
struct ComponentRefChain {
|
||||
struct Link {
|
||||
tree t = NULL_TREE;
|
||||
bool relevant = false;
|
||||
UID target;
|
||||
std::size_t offset;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
if (TREE_CODE(*tp) == COMPONENT_REF)
|
||||
*found_flag = 1;
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
static bool tree_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 component_ref_chain(tree ref, ComponentRefChain& chain) {
|
||||
if (!ref)
|
||||
return false;
|
||||
|
||||
if (TREE_CODE(ref) != COMPONENT_REF) {
|
||||
if (chain.links.empty())
|
||||
return false;
|
||||
|
||||
if (tree_contains_component_ref(ref))
|
||||
return false;
|
||||
|
||||
chain.base = ref;
|
||||
return true;
|
||||
}
|
||||
|
||||
ComponentRefChain::Link link;
|
||||
link.t = ref;
|
||||
link.relevant = TargetType::reference(ref, link.target, link.offset);
|
||||
|
||||
if (link.relevant)
|
||||
chain.relevant = true;
|
||||
|
||||
chain.links.push_front(link);
|
||||
return component_ref_chain(TREE_OPERAND(ref, 0), chain);
|
||||
}
|
||||
|
||||
static tree separate_offset_chain_maybe(tree ref, gimple_stmt_iterator* gsi) {
|
||||
ComponentRefChain crc;
|
||||
if (!component_ref_chain(ref, crc))
|
||||
pinpoint_fatal("separate_offset_chain_maybe encountered invalid COMPONENT_REF chain");
|
||||
|
||||
if (!crc.relevant)
|
||||
return NULL_TREE;
|
||||
|
||||
// Get base pointer
|
||||
|
||||
tree base_tmp = NULL_TREE;
|
||||
|
||||
if (TREE_CODE(crc.base) == ADDR_EXPR)
|
||||
pinpoint_fatal("separate_offset_chain_maybe encountered ADDR_EXPR as base of COMPONENT_REF");
|
||||
|
||||
base_tmp = build_fold_addr_expr(crc.base);
|
||||
|
||||
// For each component ref in chain, add the member offset to the pointer
|
||||
|
||||
for (const ComponentRefChain::Link& cr : crc.links) {
|
||||
// NOTE -> Could fold into single call here (needs to track what offsets contribute, +irrelevant combined)
|
||||
if (cr.relevant) {
|
||||
// Call is a separate statement, the result is added to the base pointer
|
||||
tree return_tmp = create_tmp_var(size_type_node, NULL);
|
||||
|
||||
gimple* call_stmt = make_stage0_gimple_separator(return_tmp, cr.target, cr.offset);
|
||||
if (!call_stmt)
|
||||
pinpoint_fatal("separate_offset_chain_maybe failed to make gimple separator for target %u at offset %u",
|
||||
(unsigned)cr.target, (unsigned)cr.offset);
|
||||
|
||||
gsi_insert_before(gsi, call_stmt, GSI_SAME_STMT);
|
||||
|
||||
// Add call return value to current base pointer (result is field pointer)
|
||||
tree field_ptr_type = build_pointer_type(TREE_TYPE(cr.t));
|
||||
tree field_ptr = build2(POINTER_PLUS_EXPR, field_ptr_type, base_tmp, return_tmp);
|
||||
|
||||
base_tmp = create_tmp_var(field_ptr_type, NULL);
|
||||
gimple* field_ptr_assignment = gimple_build_assign(base_tmp, field_ptr);
|
||||
gsi_insert_before(gsi, field_ptr_assignment, GSI_SAME_STMT);
|
||||
} else {
|
||||
// Add offsetof contant
|
||||
tree field_decl = TREE_OPERAND(cr.t, 1);
|
||||
|
||||
std::size_t field_offset;
|
||||
bool field_bitfield;
|
||||
|
||||
if (!field_info(field_decl, &field_offset, nullptr, &field_bitfield))
|
||||
pinpoint_fatal("separate_offset_chain_maybe failed to get field info of non-target access");
|
||||
|
||||
if (field_bitfield)
|
||||
pinpoint_fatal("separate_offset_chain_maybe encountered bitfield access in relevant COMPONENT_REF chain");
|
||||
|
||||
// Add constant offset to current base pointer (result is field pointer)
|
||||
tree field_ptr_type = build_pointer_type(TREE_TYPE(cr.t));
|
||||
tree field_ptr = build2(POINTER_PLUS_EXPR, field_ptr_type, base_tmp, build_int_cst(sizetype, field_offset));
|
||||
|
||||
base_tmp = create_tmp_var(field_ptr_type, NULL);
|
||||
gimple* field_ptr_assignment = gimple_build_assign(base_tmp, field_ptr);
|
||||
gsi_insert_before(gsi, field_ptr_assignment, GSI_SAME_STMT);
|
||||
}
|
||||
}
|
||||
|
||||
// Current pointer is a field pointer -> dereference
|
||||
|
||||
tree offset0 = fold_convert(TREE_TYPE(base_tmp), build_int_cst(sizetype, 0));
|
||||
tree result_ref = build2(MEM_REF, TREE_TYPE(ref), base_tmp, offset0);
|
||||
|
||||
return result_ref;
|
||||
}
|
||||
|
||||
static void dispatch_separation_maybe(const std::list<tree*>& path, gimple_stmt_iterator* gsi, unsigned& cancel_levels) {
|
||||
if (path.empty() || !gsi)
|
||||
return;
|
||||
|
||||
tree ref = *path.back();
|
||||
if (!ref || TREE_CODE(ref) != COMPONENT_REF)
|
||||
return;
|
||||
|
||||
cancel_levels = 1;
|
||||
|
||||
tree instrumented_ref = separate_offset_chain_maybe(ref, gsi);
|
||||
if (!instrumented_ref)
|
||||
return;
|
||||
|
||||
gimple_set_modified(gsi_stmt(*gsi), true);
|
||||
*path.back() = instrumented_ref;
|
||||
|
||||
// At this point, instrumented_ref is a MEM_REF node (off=0). A wrapping ADDR_EXPR cancels it out.
|
||||
|
||||
if (path.size() < 2)
|
||||
return;
|
||||
|
||||
tree* parent = *(++path.rbegin());
|
||||
|
||||
if (TREE_CODE(*parent) == ADDR_EXPR) {
|
||||
// Note -> the base of the MEM_REF is expected to have the same type as the ADDR_EXPR
|
||||
*parent = TREE_OPERAND(instrumented_ref, 0);
|
||||
cancel_levels++;
|
||||
}
|
||||
}
|
||||
|
||||
static const pass_data separate_offset_pass_data = {
|
||||
GIMPLE_PASS,
|
||||
"separate_offset",
|
||||
OPTGROUP_NONE,
|
||||
TV_NONE,
|
||||
0,0,0,0,
|
||||
TODO_update_ssa
|
||||
};
|
||||
|
||||
separate_offset_pass::separate_offset_pass(gcc::context* ctxt) : gimple_opt_pass(separate_offset_pass_data, ctxt) {}
|
||||
|
||||
struct TreeWalkData {
|
||||
std::list<tree*> path;
|
||||
gimple_stmt_iterator* gsi;
|
||||
unsigned cancel_levels;
|
||||
std::function<void(const std::list<tree*>&, gimple_stmt_iterator*, unsigned&)> callback;
|
||||
};
|
||||
|
||||
static tree walk_tree_level(tree* tp, int* walk_subtrees, void* data) {
|
||||
TreeWalkData* twd = (TreeWalkData*)data;
|
||||
if (!twd)
|
||||
return NULL_TREE;
|
||||
|
||||
if (!twd->path.empty() && twd->path.back() == tp)
|
||||
return NULL_TREE; // root of this level
|
||||
|
||||
if (walk_subtrees)
|
||||
*walk_subtrees = 0;
|
||||
|
||||
twd->cancel_levels = 0;
|
||||
twd->path.push_back(tp);
|
||||
|
||||
twd->callback(twd->path, twd->gsi, twd->cancel_levels);
|
||||
|
||||
if (twd->cancel_levels == 0)
|
||||
walk_tree(tp, walk_tree_level, data, NULL);
|
||||
|
||||
twd->path.pop_back();
|
||||
|
||||
if (twd->cancel_levels > 0)
|
||||
twd->cancel_levels--;
|
||||
|
||||
// Cancel current level if there are still cancel_levels due
|
||||
return twd->cancel_levels == 0 ? NULL_TREE : *tp;
|
||||
}
|
||||
|
||||
static bool walk_gimple_stmt(gimple_stmt_iterator* gsi,
|
||||
std::function<void(const std::list<tree*>&, gimple_stmt_iterator*, unsigned&)> callback) {
|
||||
if (!gsi || gsi_end_p(*gsi) || !callback)
|
||||
return false;
|
||||
|
||||
gimple* stmt = gsi_stmt(*gsi);
|
||||
|
||||
for (int i = 0; i < gimple_num_ops(stmt); i++) {
|
||||
tree* op = gimple_op_ptr(stmt, i);
|
||||
if (!op || !*op)
|
||||
continue;
|
||||
|
||||
TreeWalkData twd;
|
||||
twd.gsi = gsi;
|
||||
twd.callback = callback;
|
||||
|
||||
walk_tree_level(op, NULL, &twd);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int separate_offset_pass::execute(function* fn) {
|
||||
if (!fn)
|
||||
return 0;
|
||||
|
||||
basic_block bb;
|
||||
FOR_EACH_BB_FN(bb, fn) {
|
||||
for (gimple_stmt_iterator gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
|
||||
if (!walk_gimple_stmt(&gsi, dispatch_separation_maybe))
|
||||
pinpoint_fatal("separate_offset pass failed to walk gimple statement");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -40,23 +40,25 @@ tree make_stage0_ast_separator(UID target, std::size_t offset) {
|
||||
return build_call_expr(decl, 2, arg0, arg1);
|
||||
}
|
||||
|
||||
gimple* make_stage0_gimple_separator(UID target, std::size_t offset) {
|
||||
gimple* make_stage0_gimple_separator(tree lhs, UID target, std::size_t offset) {
|
||||
if (!lhs)
|
||||
return nullptr;
|
||||
|
||||
tree decl = make_separator_decl();
|
||||
if (!decl)
|
||||
return nullptr;
|
||||
|
||||
tree arg0 = size_int(target);
|
||||
tree arg1 = size_int(offset);
|
||||
tree res = create_tmp_var(size_type_node, NULL);
|
||||
|
||||
if (!arg0 || !arg1 || !res)
|
||||
if (!arg0 || !arg1)
|
||||
return nullptr;
|
||||
|
||||
gimple* call = gimple_build_call(decl, 2, arg0, arg1);
|
||||
if (!call)
|
||||
return nullptr;
|
||||
|
||||
gimple_call_set_lhs(call, res);
|
||||
gimple_call_set_lhs(call, lhs);
|
||||
return call;
|
||||
}
|
||||
|
||||
|
||||
@ -55,13 +55,21 @@ private:
|
||||
static TargetType* find_mutable(tree t);
|
||||
};
|
||||
|
||||
bool field_info(tree field_decl, std::size_t* offset, std::size_t* size, bool* bitfield);
|
||||
|
||||
/* Stage 0 offsetof separators are function calls, such as:
|
||||
SPSLR_PINPOINT_STAGE0_SEPARATOR(target, member offset) */
|
||||
|
||||
tree make_stage0_ast_separator(UID target, std::size_t offset);
|
||||
gimple* make_stage0_gimple_separator(UID target, std::size_t offset); // lhs is a new temporary variable
|
||||
gimple* make_stage0_gimple_separator(tree lhs, UID target, std::size_t offset);
|
||||
bool is_stage0_separator(gimple* stmt, UID& target, std::size_t& offset);
|
||||
|
||||
void on_register_attributes(void* plugin_data, void* user_data);
|
||||
void on_finish_type(void* plugin_data, void* user_data);
|
||||
void on_preserve_component_ref(void* plugin_data, void* user_data);
|
||||
|
||||
struct separate_offset_pass : gimple_opt_pass {
|
||||
separate_offset_pass(gcc::context* ctxt);
|
||||
unsigned int execute(function* fn) override;
|
||||
};
|
||||
|
||||
|
||||
@ -125,7 +125,7 @@ const TargetType* TargetType::find(UID uid) {
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
static bool field_info(tree field_decl, std::size_t* offset, std::size_t* size, bool* bitfield) {
|
||||
bool field_info(tree field_decl, std::size_t* offset, std::size_t* size, bool* bitfield) {
|
||||
if (!field_decl || TREE_CODE(field_decl) != FIELD_DECL)
|
||||
return false;
|
||||
|
||||
|
||||
@ -34,308 +34,6 @@ int plugin_is_GPL_compatible;
|
||||
#define UNSPEC_SPSLR_OFFSETOF 1042
|
||||
#endif
|
||||
|
||||
// Identify any missing COMPONENT_REFs (e.g. from CONSTRUCTOR trees)
|
||||
|
||||
static const pass_data log_component_refs_pass_data = {
|
||||
GIMPLE_PASS,
|
||||
"log_component_refs",
|
||||
OPTGROUP_NONE,
|
||||
TV_NONE,
|
||||
0,0,0,0,
|
||||
TODO_update_ssa
|
||||
};
|
||||
|
||||
struct log_component_refs_pass : gimple_opt_pass {
|
||||
log_component_refs_pass(gcc::context *ctxt);
|
||||
unsigned int execute(function* fun) override;
|
||||
};
|
||||
|
||||
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<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;
|
||||
|
||||
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) {
|
||||
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;
|
||||
|
||||
// Get base pointer
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
base_tmp = base_ptr;
|
||||
}
|
||||
|
||||
// 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
|
||||
tree spslr_offsetof_decl = NULL_TREE;
|
||||
if (!(spslr_offsetof_decl = make_spslr_offsetof_decl(cr.call_data.uid))) {
|
||||
std::cerr << "spslr_pinpoint -> failed to instrument COMPONENT_REF ("
|
||||
<< SPSLR_OFFSETOF << "<uid> 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
|
||||
gimple* call_stmt = gimple_build_call(spslr_offsetof_decl, 0);
|
||||
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 (result is field pointer)
|
||||
tree field_ptr_type = build_pointer_type(TREE_TYPE(cr.t));
|
||||
tree field_ptr = build2(POINTER_PLUS_EXPR, field_ptr_type, base_tmp, offset_tmp);
|
||||
|
||||
base_tmp = create_tmp_var(field_ptr_type, NULL);
|
||||
gimple* addition_assignment = gimple_build_assign(base_tmp, field_ptr);
|
||||
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 (result is field pointer)
|
||||
tree field_ptr_type = build_pointer_type(TREE_TYPE(cr.t));
|
||||
tree field_ptr = build2(POINTER_PLUS_EXPR, field_ptr_type, base_tmp, build_int_cst(sizetype, offset));
|
||||
|
||||
base_tmp = create_tmp_var(field_ptr_type, NULL);
|
||||
gimple* addition_assignment = gimple_build_assign(base_tmp, field_ptr);
|
||||
gsi_insert_before(gsi, addition_assignment, GSI_SAME_STMT);
|
||||
}
|
||||
}
|
||||
|
||||
// Current pointer is a field pointer -> dereference
|
||||
|
||||
tree offset0 = fold_convert(TREE_TYPE(base_tmp), build_int_cst(sizetype, 0));
|
||||
tree result_ref = build2(MEM_REF, TREE_TYPE(ref), base_tmp, offset0);
|
||||
|
||||
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 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;
|
||||
|
||||
// At this point, instrumented_ref is a MEM_REF node (off=0). A wrapping ADDR_EXPR cancels it out.
|
||||
|
||||
if (path.size() < 2)
|
||||
return;
|
||||
|
||||
tree* parent = *(++path.rbegin());
|
||||
|
||||
if (TREE_CODE(*parent) == ADDR_EXPR) {
|
||||
// Note -> the base of the MEM_REF is expected to have the same type as the ADDR_EXPR
|
||||
*parent = TREE_OPERAND(instrumented_ref, 0);
|
||||
cancel_levels++;
|
||||
}
|
||||
}
|
||||
|
||||
struct TreeWalkData {
|
||||
std::list<tree*> path;
|
||||
gimple_stmt_iterator* gsi;
|
||||
unsigned cancel_levels;
|
||||
};
|
||||
|
||||
static tree walk_tree_level(tree* tp, int* walk_subtrees, void* data) {
|
||||
TreeWalkData* twd = (TreeWalkData*)data;
|
||||
if (!twd)
|
||||
return NULL_TREE;
|
||||
|
||||
if (!twd->path.empty() && twd->path.back() == tp)
|
||||
return NULL_TREE; // root of this level
|
||||
|
||||
if (walk_subtrees)
|
||||
*walk_subtrees = 0;
|
||||
|
||||
twd->cancel_levels = 0;
|
||||
twd->path.push_back(tp);
|
||||
|
||||
instrument_tree(twd->path, twd->gsi, twd->cancel_levels);
|
||||
|
||||
if (twd->cancel_levels == 0)
|
||||
walk_tree(tp, walk_tree_level, data, NULL);
|
||||
|
||||
twd->path.pop_back();
|
||||
|
||||
if (twd->cancel_levels > 0)
|
||||
twd->cancel_levels--;
|
||||
|
||||
// Cancel current level if there are still cancel_levels due
|
||||
return twd->cancel_levels == 0 ? NULL_TREE : *tp;
|
||||
}
|
||||
|
||||
static bool walk_gimple_stmt(gimple_stmt_iterator* gsi) {
|
||||
if (!gsi || gsi_end_p(*gsi))
|
||||
return false;
|
||||
|
||||
gimple* stmt = gsi_stmt(*gsi);
|
||||
|
||||
for (int i = 0; i < gimple_num_ops(stmt); i++) {
|
||||
tree* op = gimple_op_ptr(stmt, i);
|
||||
if (!op || !*op)
|
||||
continue;
|
||||
|
||||
TreeWalkData twd;
|
||||
twd.gsi = gsi;
|
||||
|
||||
walk_tree_level(op, NULL, &twd);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int log_component_refs_pass::execute(function* fun) {
|
||||
const char* funcname = fun->decl && DECL_NAME(fun->decl)
|
||||
? IDENTIFIER_POINTER(DECL_NAME(fun->decl))
|
||||
: "<anonymous>";
|
||||
|
||||
tree fndecl = fun->decl;
|
||||
if (!fndecl)
|
||||
return 0;
|
||||
|
||||
gimple_seq body = gimple_body(fndecl);
|
||||
for (gimple_stmt_iterator gsi = gsi_start(body); walk_gimple_stmt(&gsi); gsi_next(&gsi));
|
||||
|
||||
// print_gimple_seq(stderr, gimple_body(fun->decl), 0, TDF_NONE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// At early RTL, replace __spslr_offsetof_<uid> with UNSPECs to avoid ABI and clobbering "problems"
|
||||
|
||||
static bool extract_callee_symbol (rtx call_rtx, const char** out_name) {
|
||||
@ -622,15 +320,6 @@ int plugin_init (struct plugin_name_args *plugin_info, struct plugin_gcc_version
|
||||
return 1;
|
||||
}
|
||||
|
||||
register_callback(plugin_info->base_name, PLUGIN_BUILD_COMPONENT_REF, on_build_component_ref, NULL);
|
||||
|
||||
struct register_pass_info log_component_refs_pass_info;
|
||||
log_component_refs_pass_info.pass = new log_component_refs_pass(nullptr);
|
||||
log_component_refs_pass_info.ref_pass_instance_number = 1;
|
||||
log_component_refs_pass_info.reference_pass_name = "cfg";
|
||||
log_component_refs_pass_info.pos_op = PASS_POS_INSERT_BEFORE;
|
||||
register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, nullptr, &log_component_refs_pass_info);
|
||||
|
||||
/*
|
||||
TODO
|
||||
vregs is almost immediately after expand (maybe the first one)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
add_executable(subject main.c)
|
||||
add_dependencies(subject spslr_pinpoint spslr_finalize spslr_selfpatch)
|
||||
target_link_libraries(subject PRIVATE spslr_selfpatch)
|
||||
target_compile_options(subject PRIVATE -fplugin=$<TARGET_FILE:spslr_pinpoint>)
|
||||
target_compile_options(subject PRIVATE -fplugin=$<TARGET_FILE:spslr_pinpoint> -fdump-tree-separate_offset)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user