From 3f97c2654d3b78867c4c1891fda851ccbe593f4e Mon Sep 17 00:00:00 2001 From: York Jasper Niebuhr Date: Fri, 24 Oct 2025 23:07:51 +0200 Subject: [PATCH] Pinpoint plugin on_preserve_component_ref --- pinpoint/pinpoint_config.h | 1 + pinpoint/pinpoint_error.h | 8 + pinpoint/safegcc/safe-gimple.h | 11 ++ pinpoint/spslr_error.h | 8 - pinpoint/stage0/CMakeLists.txt | 3 +- pinpoint/stage0/on_finish_type.cpp | 4 +- pinpoint/stage0/on_preserve_component_ref.cpp | 36 +++- pinpoint/stage0/separator.cpp | 67 +++++++ pinpoint/stage0/stage0.h | 10 + pinpoint/stage0/target.cpp | 119 +++++++++--- playground/spslr_pinpoint.cpp | 178 ------------------ 11 files changed, 227 insertions(+), 218 deletions(-) create mode 100644 pinpoint/pinpoint_error.h create mode 100644 pinpoint/safegcc/safe-gimple.h delete mode 100644 pinpoint/spslr_error.h create mode 100644 pinpoint/stage0/separator.cpp diff --git a/pinpoint/pinpoint_config.h b/pinpoint/pinpoint_config.h index 7c9521c..b7f4afe 100644 --- a/pinpoint/pinpoint_config.h +++ b/pinpoint/pinpoint_config.h @@ -1,3 +1,4 @@ #pragma once #define SPSLR_ATTRIBUTE "spslr" +#define SPSLR_PINPOINT_STAGE0_SEPARATOR "__spslr_offsetof" diff --git a/pinpoint/pinpoint_error.h b/pinpoint/pinpoint_error.h new file mode 100644 index 0000000..daa17de --- /dev/null +++ b/pinpoint/pinpoint_error.h @@ -0,0 +1,8 @@ +#pragma once +#include + +#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__) diff --git a/pinpoint/safegcc/safe-gimple.h b/pinpoint/safegcc/safe-gimple.h new file mode 100644 index 0000000..ad6e0a9 --- /dev/null +++ b/pinpoint/safegcc/safe-gimple.h @@ -0,0 +1,11 @@ +#include + +#ifndef SAFEGCC_GIMPLE_H +#define SAFEGCC_GIMPLE_H + +#include +#include +#include +#include + +#endif diff --git a/pinpoint/spslr_error.h b/pinpoint/spslr_error.h deleted file mode 100644 index 54c8d62..0000000 --- a/pinpoint/spslr_error.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include - -#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__) diff --git a/pinpoint/stage0/CMakeLists.txt b/pinpoint/stage0/CMakeLists.txt index 12f8aa7..b4d77fd 100644 --- a/pinpoint/stage0/CMakeLists.txt +++ b/pinpoint/stage0/CMakeLists.txt @@ -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 .) diff --git a/pinpoint/stage0/on_finish_type.cpp b/pinpoint/stage0/on_finish_type.cpp index 077d4b4..1f32840 100644 --- a/pinpoint/stage0/on_finish_type.cpp +++ b/pinpoint/stage0/on_finish_type.cpp @@ -1,5 +1,5 @@ #include -#include +#include void on_finish_type(void* plugin_data, void* user_data) { tree t = (tree)plugin_data; @@ -9,6 +9,6 @@ void on_finish_type(void* plugin_data, void* user_data) { return; 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()); } diff --git a/pinpoint/stage0/on_preserve_component_ref.cpp b/pinpoint/stage0/on_preserve_component_ref.cpp index 4efb4e5..184e94e 100644 --- a/pinpoint/stage0/on_preserve_component_ref.cpp +++ b/pinpoint/stage0/on_preserve_component_ref.cpp @@ -1,6 +1,40 @@ #include +#include -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; } diff --git a/pinpoint/stage0/separator.cpp b/pinpoint/stage0/separator.cpp new file mode 100644 index 0000000..13fd881 --- /dev/null +++ b/pinpoint/stage0/separator.cpp @@ -0,0 +1,67 @@ +#include +#include + +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; +} + diff --git a/pinpoint/stage0/stage0.h b/pinpoint/stage0/stage0.h index 1978953..9e7b99f 100644 --- a/pinpoint/stage0/stage0.h +++ b/pinpoint/stage0/stage0.h @@ -5,6 +5,7 @@ #include #include +#include using UID = std::size_t; constexpr UID UID_INVALID = std::numeric_limits::max(); @@ -41,17 +42,26 @@ public: bool fields() const; std::string name() const; const Field* field(std::size_t off, bool exact = true) const; + UID uid() const; static void add(tree t); static std::size_t count(); static const TargetType* find(tree t); // O(n) static const TargetType* find(UID uid); // O(1) + static bool reference(tree ref, UID& target, std::size_t& offset); private: friend void on_finish_type(void*, void*); bool fetch_fields(bool redo = false); 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_finish_type(void* plugin_data, void* user_data); void on_preserve_component_ref(void* plugin_data, void* user_data); diff --git a/pinpoint/stage0/target.cpp b/pinpoint/stage0/target.cpp index 6bfa49b..e03d783 100644 --- a/pinpoint/stage0/target.cpp +++ b/pinpoint/stage0/target.cpp @@ -71,6 +71,10 @@ const TargetType::Field* TargetType::field(std::size_t off, bool exact) const { return &maybe; } +UID TargetType::uid() const { + return m_uid; +} + void TargetType::add(tree t) { if (find(t) != nullptr) return; @@ -121,6 +125,87 @@ 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) { + 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(tmp_byte_offset); + + if (size) + *size = static_cast(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 callback) { if (!t || TREE_CODE(t) != RECORD_TYPE) return false; @@ -132,35 +217,13 @@ static bool foreach_record_field(tree t, std::function(field_byte_offset); - field.size = static_cast(effective_field_size); - field.flags = (is_dangerous ? TargetType::Field::FLAG_DANGEROUS : 0); + bool is_bitfield; + + 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)) return false; diff --git a/playground/spslr_pinpoint.cpp b/playground/spslr_pinpoint.cpp index 0207970..3746359 100644 --- a/playground/spslr_pinpoint.cpp +++ b/playground/spslr_pinpoint.cpp @@ -34,140 +34,6 @@ int plugin_is_GPL_compatible; #define UNSPEC_SPSLR_OFFSETOF 1042 #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 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 << " 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) static const pass_data log_component_refs_pass_data = { @@ -470,41 +336,6 @@ unsigned int log_component_refs_pass::execute(function* fun) { 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)) - : ""; - - 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_ with UNSPECs to avoid ABI and clobbering "problems" 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; } - 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); 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; 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 vregs is almost immediately after expand (maybe the first one)