Pinpoint plugin on_preserve_component_ref
This commit is contained in:
parent
b725c35be5
commit
3f97c2654d
@ -1,3 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define SPSLR_ATTRIBUTE "spslr"
|
#define SPSLR_ATTRIBUTE "spslr"
|
||||||
|
#define SPSLR_PINPOINT_STAGE0_SEPARATOR "__spslr_offsetof"
|
||||||
|
|||||||
8
pinpoint/pinpoint_error.h
Normal file
8
pinpoint/pinpoint_error.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <safe-diagnostic.h>
|
||||||
|
|
||||||
|
#define pinpoint_fatal_loc(loc, fmt, ...) \
|
||||||
|
fatal_error((loc), "[spslr_pinpoint] " fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define pinpoint_fatal(fmt, ...) \
|
||||||
|
pinpoint_fatal_loc(UNKNOWN_LOCATION, fmt, ##__VA_ARGS__)
|
||||||
11
pinpoint/safegcc/safe-gimple.h
Normal file
11
pinpoint/safegcc/safe-gimple.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include <safe-gcc-plugin.h>
|
||||||
|
|
||||||
|
#ifndef SAFEGCC_GIMPLE_H
|
||||||
|
#define SAFEGCC_GIMPLE_H
|
||||||
|
|
||||||
|
#include <gimple.h>
|
||||||
|
#include <gimplify.h>
|
||||||
|
#include <gimple-iterator.h>
|
||||||
|
#include <gimple-pretty-print.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <safe-diagnostic.h>
|
|
||||||
|
|
||||||
#define spslr_fatal_loc(loc, fmt, ...) \
|
|
||||||
fatal_error((loc), "[spslr_pinpoint] " fmt, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#define spslr_fatal(fmt, ...) \
|
|
||||||
spslr_fatal_loc(UNKNOWN_LOCATION, fmt, ##__VA_ARGS__)
|
|
||||||
@ -1,2 +1,3 @@
|
|||||||
target_sources(spslr_pinpoint PRIVATE on_register_attributes.cpp on_finish_type.cpp on_preserve_component_ref.cpp target.cpp)
|
target_sources(spslr_pinpoint PRIVATE on_register_attributes.cpp on_finish_type.cpp on_preserve_component_ref.cpp
|
||||||
|
target.cpp separator.cpp)
|
||||||
target_include_directories(spslr_pinpoint PRIVATE .)
|
target_include_directories(spslr_pinpoint PRIVATE .)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#include <stage0.h>
|
#include <stage0.h>
|
||||||
#include <spslr_error.h>
|
#include <pinpoint_error.h>
|
||||||
|
|
||||||
void on_finish_type(void* plugin_data, void* user_data) {
|
void on_finish_type(void* plugin_data, void* user_data) {
|
||||||
tree t = (tree)plugin_data;
|
tree t = (tree)plugin_data;
|
||||||
@ -9,6 +9,6 @@ void on_finish_type(void* plugin_data, void* user_data) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (!type->fetch_fields())
|
if (!type->fetch_fields())
|
||||||
spslr_fatal("on_finish_type failed to fetch fields of target \"%s\"", type->name().c_str());
|
pinpoint_fatal("on_finish_type failed to fetch fields of target \"%s\"", type->name().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,40 @@
|
|||||||
#include <stage0.h>
|
#include <stage0.h>
|
||||||
|
#include <pinpoint_error.h>
|
||||||
|
|
||||||
void on_preserve_component_ref(void* plugin_data, void* user_data) {
|
static tree ast_separate_offset(tree ref, UID target, std::size_t offset) {
|
||||||
|
tree separator = make_stage0_ast_separator(target, offset);
|
||||||
|
if (!separator)
|
||||||
|
pinpoint_fatal("ast_separate_offset failed to generate AST separator for target %u at offset %u",
|
||||||
|
(unsigned)target, (unsigned)offset);
|
||||||
|
|
||||||
|
tree base = TREE_OPERAND(ref, 0);
|
||||||
|
|
||||||
|
// ADDR_EXPR can never be a valid base, but such trees can happen during parsing before checks
|
||||||
|
if (TREE_CODE(base) == ADDR_EXPR)
|
||||||
|
pinpoint_fatal("ast_separate_offset encountered ADDR_EXPR as COMPONENT_REF base");
|
||||||
|
|
||||||
|
tree base_ptr = build_fold_addr_expr(base);
|
||||||
|
|
||||||
|
tree field_type = TREE_TYPE(ref); // Type of COMPONENT_REF is type of the accessed field
|
||||||
|
tree field_ptr_type = build_pointer_type(field_type);
|
||||||
|
tree field_ptr = build2(POINTER_PLUS_EXPR, field_ptr_type, base_ptr, separator);
|
||||||
|
|
||||||
|
tree field_ref = build1(INDIRECT_REF, field_type, field_ptr);
|
||||||
|
return field_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_preserve_component_ref(void* plugin_data, void* user_data) {
|
||||||
|
tree* ref = (tree*)plugin_data;
|
||||||
|
if (!ref)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UID target;
|
||||||
|
std::size_t offset;
|
||||||
|
if (!TargetType::reference(*ref, target, offset))
|
||||||
|
return;
|
||||||
|
|
||||||
|
tree separated = ast_separate_offset(*ref, target, offset);
|
||||||
|
if (separated)
|
||||||
|
*ref = separated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
67
pinpoint/stage0/separator.cpp
Normal file
67
pinpoint/stage0/separator.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include <stage0.h>
|
||||||
|
#include <pinpoint_config.h>
|
||||||
|
|
||||||
|
static tree separator_decl = NULL_TREE;
|
||||||
|
|
||||||
|
static tree make_separator_decl() {
|
||||||
|
if (separator_decl)
|
||||||
|
return separator_decl;
|
||||||
|
|
||||||
|
tree args = tree_cons(NULL_TREE, sizetype, tree_cons(NULL_TREE, sizetype, NULL_TREE));
|
||||||
|
tree type = build_function_type(sizetype, args);
|
||||||
|
|
||||||
|
tree tmp_decl = build_fn_decl(SPSLR_PINPOINT_STAGE0_SEPARATOR, type);
|
||||||
|
if (!tmp_decl)
|
||||||
|
return NULL_TREE;
|
||||||
|
|
||||||
|
DECL_EXTERNAL(tmp_decl) = 1;
|
||||||
|
TREE_PUBLIC(tmp_decl) = 1;
|
||||||
|
DECL_ARTIFICIAL(tmp_decl) = 1;
|
||||||
|
|
||||||
|
/* Prevent VOP problems later when removing calls (VOPs mark memory
|
||||||
|
side-effects, which these calls have none of anyways */
|
||||||
|
DECL_PURE_P(tmp_decl) = 1;
|
||||||
|
DECL_IS_NOVOPS(tmp_decl) = 1;
|
||||||
|
|
||||||
|
return (separator_decl = tmp_decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
tree make_stage0_ast_separator(UID target, std::size_t offset) {
|
||||||
|
tree decl = make_separator_decl();
|
||||||
|
if (!decl)
|
||||||
|
return NULL_TREE;
|
||||||
|
|
||||||
|
tree arg0 = size_int(target);
|
||||||
|
tree arg1 = size_int(offset);
|
||||||
|
|
||||||
|
if (!arg0 || !arg1)
|
||||||
|
return NULL_TREE;
|
||||||
|
|
||||||
|
return build_call_expr(decl, 2, arg0, arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
gimple* make_stage0_gimple_separator(UID target, std::size_t offset) {
|
||||||
|
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)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
gimple* call = gimple_build_call(decl, 2, arg0, arg1);
|
||||||
|
if (!call)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
gimple_call_set_lhs(call, res);
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_stage0_separator(gimple* stmt, UID& target, std::size_t& offset) {
|
||||||
|
// TODO
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@ -5,6 +5,7 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include <safe-tree.h>
|
#include <safe-tree.h>
|
||||||
|
#include <safe-gimple.h>
|
||||||
|
|
||||||
using UID = std::size_t;
|
using UID = std::size_t;
|
||||||
constexpr UID UID_INVALID = std::numeric_limits<UID>::max();
|
constexpr UID UID_INVALID = std::numeric_limits<UID>::max();
|
||||||
@ -41,17 +42,26 @@ public:
|
|||||||
bool fields() const;
|
bool fields() const;
|
||||||
std::string name() const;
|
std::string name() const;
|
||||||
const Field* field(std::size_t off, bool exact = true) const;
|
const Field* field(std::size_t off, bool exact = true) const;
|
||||||
|
UID uid() const;
|
||||||
|
|
||||||
static void add(tree t);
|
static void add(tree t);
|
||||||
static std::size_t count();
|
static std::size_t count();
|
||||||
static const TargetType* find(tree t); // O(n)
|
static const TargetType* find(tree t); // O(n)
|
||||||
static const TargetType* find(UID uid); // O(1)
|
static const TargetType* find(UID uid); // O(1)
|
||||||
|
static bool reference(tree ref, UID& target, std::size_t& offset);
|
||||||
private:
|
private:
|
||||||
friend void on_finish_type(void*, void*);
|
friend void on_finish_type(void*, void*);
|
||||||
bool fetch_fields(bool redo = false);
|
bool fetch_fields(bool redo = false);
|
||||||
static TargetType* find_mutable(tree t);
|
static TargetType* find_mutable(tree t);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
bool is_stage0_separator(gimple* stmt, UID& target, std::size_t& offset);
|
||||||
|
|
||||||
void on_register_attributes(void* plugin_data, void* user_data);
|
void on_register_attributes(void* plugin_data, void* user_data);
|
||||||
void on_finish_type(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);
|
void on_preserve_component_ref(void* plugin_data, void* user_data);
|
||||||
|
|||||||
@ -71,6 +71,10 @@ const TargetType::Field* TargetType::field(std::size_t off, bool exact) const {
|
|||||||
return &maybe;
|
return &maybe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UID TargetType::uid() const {
|
||||||
|
return m_uid;
|
||||||
|
}
|
||||||
|
|
||||||
void TargetType::add(tree t) {
|
void TargetType::add(tree t) {
|
||||||
if (find(t) != nullptr)
|
if (find(t) != nullptr)
|
||||||
return;
|
return;
|
||||||
@ -121,6 +125,87 @@ const TargetType* TargetType::find(UID uid) {
|
|||||||
return &it->second;
|
return &it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
|
||||||
|
HOST_WIDE_INT tmp_byte_offset = 0;
|
||||||
|
if (TREE_CODE(DECL_FIELD_OFFSET(field_decl)) == INTEGER_CST)
|
||||||
|
tmp_byte_offset = tree_to_uhwi(DECL_FIELD_OFFSET(field_decl));
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
HOST_WIDE_INT tmp_bit_offset = 0;
|
||||||
|
if (TREE_CODE(DECL_FIELD_BIT_OFFSET(field_decl)) == INTEGER_CST)
|
||||||
|
tmp_bit_offset = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(field_decl));
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
HOST_WIDE_INT bit_offset_bytes = tmp_bit_offset / 8;
|
||||||
|
tmp_byte_offset += bit_offset_bytes;
|
||||||
|
tmp_bit_offset -= bit_offset_bytes * 8;
|
||||||
|
|
||||||
|
HOST_WIDE_INT tmp_bit_size = 0;
|
||||||
|
if (TREE_CODE(DECL_SIZE(field_decl)) == INTEGER_CST)
|
||||||
|
tmp_bit_size = tree_to_uhwi(DECL_SIZE(field_decl));
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool tmp_bitfield = (DECL_BIT_FIELD_TYPE(field_decl) != NULL_TREE);
|
||||||
|
tmp_bitfield |= !(tmp_bit_size % 8 == 0 && tmp_bit_offset == 0);
|
||||||
|
|
||||||
|
// Intra-byte offset counts towards size
|
||||||
|
tmp_bit_size += tmp_bit_offset;
|
||||||
|
|
||||||
|
// Round size up to entire byte
|
||||||
|
HOST_WIDE_INT tmp_bit_overhang = tmp_bit_size % 8;
|
||||||
|
if (tmp_bit_overhang != 0)
|
||||||
|
tmp_bit_size += (8 - tmp_bit_overhang);
|
||||||
|
|
||||||
|
// Set all outputs
|
||||||
|
|
||||||
|
if (offset)
|
||||||
|
*offset = static_cast<std::size_t>(tmp_byte_offset);
|
||||||
|
|
||||||
|
if (size)
|
||||||
|
*size = static_cast<std::size_t>(tmp_bit_size / 8);
|
||||||
|
|
||||||
|
if (bitfield)
|
||||||
|
*bitfield = tmp_bitfield;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TargetType::reference(tree ref, UID& target, std::size_t& offset) {
|
||||||
|
if (!ref || TREE_CODE(ref) != COMPONENT_REF)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
tree base = TREE_OPERAND(ref, 0);
|
||||||
|
if (!base)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
tree base_type = TREE_TYPE(base);
|
||||||
|
if (!base_type)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const TargetType* base_target = TargetType::find(base_type);
|
||||||
|
if (!base_target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
target = base_target->uid();
|
||||||
|
|
||||||
|
tree field_decl = TREE_OPERAND(ref, 1);
|
||||||
|
|
||||||
|
if (!field_info(field_decl, &offset, nullptr, nullptr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Field* f = base_target->field(offset, false);
|
||||||
|
if (!f || (f->flags & Field::FLAG_DANGEROUS))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool foreach_record_field(tree t, std::function<bool(const TargetType::Field&)> callback) {
|
static bool foreach_record_field(tree t, std::function<bool(const TargetType::Field&)> callback) {
|
||||||
if (!t || TREE_CODE(t) != RECORD_TYPE)
|
if (!t || TREE_CODE(t) != RECORD_TYPE)
|
||||||
return false;
|
return false;
|
||||||
@ -132,35 +217,13 @@ static bool foreach_record_field(tree t, std::function<bool(const TargetType::Fi
|
|||||||
if (TREE_CODE(field_decl) != FIELD_DECL)
|
if (TREE_CODE(field_decl) != FIELD_DECL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
HOST_WIDE_INT field_byte_offset = 0;
|
|
||||||
if (TREE_CODE(DECL_FIELD_OFFSET(field_decl)) == INTEGER_CST)
|
|
||||||
field_byte_offset = tree_to_uhwi(DECL_FIELD_OFFSET(field_decl));
|
|
||||||
|
|
||||||
HOST_WIDE_INT field_bit_offset = 0;
|
|
||||||
if (TREE_CODE(DECL_FIELD_BIT_OFFSET(field_decl)) == INTEGER_CST)
|
|
||||||
field_bit_offset = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(field_decl));
|
|
||||||
|
|
||||||
HOST_WIDE_INT field_bit_offset_bytes = field_bit_offset / 8;
|
|
||||||
field_byte_offset += field_bit_offset_bytes;
|
|
||||||
field_bit_offset -= field_bit_offset_bytes * 8;
|
|
||||||
|
|
||||||
HOST_WIDE_INT field_bit_size = 0;
|
|
||||||
if (TREE_CODE(DECL_SIZE(field_decl)) == INTEGER_CST)
|
|
||||||
field_bit_size = tree_to_uhwi(DECL_SIZE(field_decl));
|
|
||||||
|
|
||||||
bool is_bitfield = (DECL_BIT_FIELD_TYPE(field_decl) != NULL_TREE);
|
|
||||||
bool is_multibyte = (field_bit_size % 8 == 0 && field_bit_offset == 0);
|
|
||||||
bool is_dangerous = (is_bitfield || !is_multibyte);
|
|
||||||
|
|
||||||
HOST_WIDE_INT field_offset_bit_size = field_bit_offset + field_bit_size;
|
|
||||||
HOST_WIDE_INT effective_field_size = field_offset_bit_size / 8;
|
|
||||||
if (field_offset_bit_size % 8 != 0)
|
|
||||||
effective_field_size += 1;
|
|
||||||
|
|
||||||
TargetType::Field field;
|
TargetType::Field field;
|
||||||
field.offset = static_cast<decltype(field.offset)>(field_byte_offset);
|
bool is_bitfield;
|
||||||
field.size = static_cast<decltype(field.size)>(effective_field_size);
|
|
||||||
field.flags = (is_dangerous ? TargetType::Field::FLAG_DANGEROUS : 0);
|
if (!field_info(field_decl, &field.offset, &field.size, &is_bitfield))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
field.flags = (is_bitfield ? TargetType::Field::FLAG_DANGEROUS : 0);
|
||||||
|
|
||||||
if (!callback(field))
|
if (!callback(field))
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -34,140 +34,6 @@ int plugin_is_GPL_compatible;
|
|||||||
#define UNSPEC_SPSLR_OFFSETOF 1042
|
#define UNSPEC_SPSLR_OFFSETOF 1042
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Early hook to make COMPONENT_REF nodes survice front end
|
|
||||||
|
|
||||||
struct SPSLROffsetofCallData {
|
|
||||||
using UID = unsigned long;
|
|
||||||
|
|
||||||
UID uid;
|
|
||||||
Target::UID target;
|
|
||||||
Member::OFF member;
|
|
||||||
};
|
|
||||||
|
|
||||||
static SPSLROffsetofCallData::UID spslr_offsetof_next_uid = 0;
|
|
||||||
static std::unordered_map<SPSLROffsetofCallData::UID, SPSLROffsetofCallData> spslr_offsetof_calls;
|
|
||||||
|
|
||||||
static tree make_spslr_offsetof_decl(SPSLROffsetofCallData::UID uid) {
|
|
||||||
tree fntype = build_function_type(sizetype, NULL_TREE);
|
|
||||||
|
|
||||||
char fnname[128];
|
|
||||||
snprintf(fnname, sizeof(fnname), "%s%lu", SPSLR_OFFSETOF, uid);
|
|
||||||
|
|
||||||
tree fnname_tree = get_identifier(fnname);
|
|
||||||
if (!fnname_tree)
|
|
||||||
return NULL_TREE;
|
|
||||||
|
|
||||||
tree fndecl = build_fn_decl(IDENTIFIER_POINTER(fnname_tree), fntype);
|
|
||||||
if (!fndecl)
|
|
||||||
return NULL_TREE;
|
|
||||||
|
|
||||||
DECL_EXTERNAL(fndecl) = 1;
|
|
||||||
TREE_PUBLIC(fndecl) = 1;
|
|
||||||
DECL_ARTIFICIAL(fndecl) = 1;
|
|
||||||
|
|
||||||
// Prevent VOP problems later when removing calls
|
|
||||||
// Explanation -> VOPs mark memory side-effects, which these calls have none of anyways
|
|
||||||
DECL_PURE_P(fndecl) = 1;
|
|
||||||
DECL_IS_NOVOPS(fndecl) = 1;
|
|
||||||
|
|
||||||
return fndecl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_relevant_offsetof(tree ref, Target::UID& tuid, Member::OFF& moff) {
|
|
||||||
if (!ref || TREE_CODE(ref) != COMPONENT_REF)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
tree base = TREE_OPERAND(ref, 0);
|
|
||||||
if (!base)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
tree base_type = TREE_TYPE(base);
|
|
||||||
if (!base_type)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!find_target_log(base_type, tuid))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto target_it = targets.find(tuid);
|
|
||||||
if (target_it == targets.end())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const Target& t = target_it->second;
|
|
||||||
|
|
||||||
tree field = TREE_OPERAND(ref, 1);
|
|
||||||
|
|
||||||
HOST_WIDE_INT field_byte_offset = 0;
|
|
||||||
if (TREE_CODE(DECL_FIELD_OFFSET(field)) == INTEGER_CST)
|
|
||||||
field_byte_offset = tree_to_uhwi(DECL_FIELD_OFFSET(field));
|
|
||||||
|
|
||||||
HOST_WIDE_INT field_bit_offset = 0;
|
|
||||||
if (TREE_CODE(DECL_FIELD_BIT_OFFSET(field)) == INTEGER_CST)
|
|
||||||
field_bit_offset = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(field));
|
|
||||||
|
|
||||||
Member::OFF effective_field_offset = (field_byte_offset + (field_bit_offset / 8));
|
|
||||||
moff = effective_field_offset;
|
|
||||||
|
|
||||||
const Member* m = t.get_member(effective_field_offset);
|
|
||||||
if (!m)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return !(m->flags & Member::FLAG_DANGERZONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static tree instrument_offsetof_maybe(tree ref) {
|
|
||||||
SPSLROffsetofCallData call_data;
|
|
||||||
if (!is_relevant_offsetof(ref, call_data.target, call_data.member))
|
|
||||||
return NULL_TREE;
|
|
||||||
|
|
||||||
location_t loc = EXPR_LOCATION(ref);
|
|
||||||
tree result_type = TREE_TYPE(ref);
|
|
||||||
|
|
||||||
call_data.uid = spslr_offsetof_next_uid++;
|
|
||||||
|
|
||||||
tree spslr_offsetof_decl = NULL_TREE;
|
|
||||||
if (!(spslr_offsetof_decl = make_spslr_offsetof_decl(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(call_data.uid, call_data);
|
|
||||||
tree call_tree = build_call_expr_loc(loc, spslr_offsetof_decl, 0);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
} else {
|
|
||||||
base_addr = build_fold_addr_expr(base);
|
|
||||||
}
|
|
||||||
|
|
||||||
tree char_ptr_type = build_pointer_type(char_type_node);
|
|
||||||
tree base_char_ptr = fold_convert(char_ptr_type, base_addr);
|
|
||||||
|
|
||||||
// Add __spslr_offsetof, cast back to field pointer, then dereference
|
|
||||||
tree plus = build2_loc(loc, POINTER_PLUS_EXPR, char_ptr_type, base_char_ptr, call_tree);
|
|
||||||
|
|
||||||
tree field_ptr_type = build_pointer_type(result_type);
|
|
||||||
tree cast_back = build1_loc(loc, NOP_EXPR, field_ptr_type, plus);
|
|
||||||
|
|
||||||
tree new_ref = build1_loc(loc, INDIRECT_REF, result_type, cast_back);
|
|
||||||
return new_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void on_build_component_ref(void* event_data, void* user_data) {
|
|
||||||
tree* component_ref_node = (tree*)event_data;
|
|
||||||
if (!component_ref_node)
|
|
||||||
return;
|
|
||||||
|
|
||||||
tree repl = instrument_offsetof_maybe(*component_ref_node);
|
|
||||||
if (repl)
|
|
||||||
*component_ref_node = repl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identify any missing COMPONENT_REFs (e.g. from CONSTRUCTOR trees)
|
// Identify any missing COMPONENT_REFs (e.g. from CONSTRUCTOR trees)
|
||||||
|
|
||||||
static const pass_data log_component_refs_pass_data = {
|
static const pass_data log_component_refs_pass_data = {
|
||||||
@ -470,41 +336,6 @@ unsigned int log_component_refs_pass::execute(function* fun) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gimple print pass for debugging
|
|
||||||
|
|
||||||
static const pass_data print_pass_data = {
|
|
||||||
GIMPLE_PASS,
|
|
||||||
"gimple_print",
|
|
||||||
OPTGROUP_NONE,
|
|
||||||
TV_NONE,
|
|
||||||
0,0,0,0, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
struct print_pass : gimple_opt_pass {
|
|
||||||
print_pass(gcc::context *ctxt);
|
|
||||||
unsigned int execute(function* fun) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
print_pass::print_pass(gcc::context *ctxt) : gimple_opt_pass(print_pass_data, ctxt) {}
|
|
||||||
|
|
||||||
unsigned int print_pass::execute(function* fun) {
|
|
||||||
const char* funcname = fun->decl && DECL_NAME(fun->decl)
|
|
||||||
? IDENTIFIER_POINTER(DECL_NAME(fun->decl))
|
|
||||||
: "<anonymous>";
|
|
||||||
|
|
||||||
fprintf(stderr, "[spslr_pinpoint] Function: %s\n", funcname);
|
|
||||||
basic_block bb;
|
|
||||||
FOR_EACH_BB_FN(bb, fun) {
|
|
||||||
for (gimple_stmt_iterator gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
|
|
||||||
gimple* stmt = gsi_stmt(gsi);
|
|
||||||
print_gimple_stmt(stderr, stmt, 0, TDF_SLIM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// At early RTL, replace __spslr_offsetof_<uid> with UNSPECs to avoid ABI and clobbering "problems"
|
// 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) {
|
static bool extract_callee_symbol (rtx call_rtx, const char** out_name) {
|
||||||
@ -791,8 +622,6 @@ int plugin_init (struct plugin_name_args *plugin_info, struct plugin_gcc_version
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
register_callback(plugin_info->base_name, PLUGIN_ATTRIBUTES, on_register_attributes, NULL);
|
|
||||||
register_callback(plugin_info->base_name, PLUGIN_FINISH_TYPE, on_type_complete, NULL);
|
|
||||||
register_callback(plugin_info->base_name, PLUGIN_BUILD_COMPONENT_REF, on_build_component_ref, NULL);
|
register_callback(plugin_info->base_name, PLUGIN_BUILD_COMPONENT_REF, on_build_component_ref, NULL);
|
||||||
|
|
||||||
struct register_pass_info log_component_refs_pass_info;
|
struct register_pass_info log_component_refs_pass_info;
|
||||||
@ -802,13 +631,6 @@ int plugin_init (struct plugin_name_args *plugin_info, struct plugin_gcc_version
|
|||||||
log_component_refs_pass_info.pos_op = PASS_POS_INSERT_BEFORE;
|
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);
|
register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, nullptr, &log_component_refs_pass_info);
|
||||||
|
|
||||||
struct register_pass_info print_pass_info;
|
|
||||||
print_pass_info.pass = new print_pass(nullptr);
|
|
||||||
print_pass_info.ref_pass_instance_number = 1;
|
|
||||||
print_pass_info.reference_pass_name = "cfg";
|
|
||||||
print_pass_info.pos_op = PASS_POS_INSERT_AFTER;
|
|
||||||
register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, nullptr, &print_pass_info);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO
|
TODO
|
||||||
vregs is almost immediately after expand (maybe the first one)
|
vregs is almost immediately after expand (maybe the first one)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user