Implemented array handling

This commit is contained in:
York Jasper Niebuhr 2026-04-05 10:55:34 +02:00
parent 7e85fadccf
commit e7851641a1
4 changed files with 143 additions and 75 deletions

View File

@ -36,6 +36,37 @@ static bool compile_datapin(tree type, DataPin& pin, std::size_t offset = 0, std
res = true; res = true;
} }
if (TREE_CODE(type) == ARRAY_TYPE) {
tree elem_type = TREE_TYPE(type);
tree domain = TYPE_DOMAIN(type);
if (!elem_type || !domain)
return res;
tree min_t = TYPE_MIN_VALUE(domain);
tree max_t = TYPE_MAX_VALUE(domain);
if (!min_t || !max_t ||
TREE_CODE(min_t) != INTEGER_CST ||
TREE_CODE(max_t) != INTEGER_CST)
return res;
HOST_WIDE_INT min_i = tree_to_shwi(min_t);
HOST_WIDE_INT max_i = tree_to_shwi(max_t);
std::size_t elem_size = tree_to_uhwi(TYPE_SIZE_UNIT(elem_type));
for (HOST_WIDE_INT i = min_i; i <= max_i; ++i) {
bool sub_res = compile_datapin(
elem_type,
pin,
offset + (std::size_t)(i - min_i) * elem_size,
level + 1
);
res = res || sub_res;
}
return res;
}
// Note -> should probably make sure that randomized structs are never used in unions!
if (TREE_CODE(type) != RECORD_TYPE /* && TREE_CODE(type) != UNION_TYPE */) if (TREE_CODE(type) != RECORD_TYPE /* && TREE_CODE(type) != UNION_TYPE */)
return res; return res;

View File

@ -4,16 +4,23 @@
#include <pinpoint_error.h> #include <pinpoint_error.h>
struct ComponentRefChain { struct AccessChain {
struct Link { struct Step {
enum Kind {
STEP_COMPONENT,
STEP_ARRAY
} kind = STEP_COMPONENT;
tree t = NULL_TREE; tree t = NULL_TREE;
/* COMPONENT_REF data */
bool relevant = false; bool relevant = false;
UID target; UID target = UID_INVALID;
std::size_t offset; std::size_t offset = 0;
}; };
bool relevant = false; bool relevant = false;
std::list<Link> links; std::list<Step> steps;
tree base = NULL_TREE; tree base = NULL_TREE;
}; };
@ -35,100 +42,109 @@ static bool tree_contains_component_ref(tree ref) {
return found_flag != 0; return found_flag != 0;
} }
static bool component_ref_chain(tree ref, ComponentRefChain& chain) { static bool access_chain(tree ref, AccessChain& chain) {
if (!ref) if (!ref)
return false; return false;
if (TREE_CODE(ref) != COMPONENT_REF) { switch (TREE_CODE(ref)) {
if (chain.links.empty()) case COMPONENT_REF: {
return false; AccessChain::Step step;
step.kind = AccessChain::Step::STEP_COMPONENT;
step.t = ref;
step.relevant = TargetType::reference(ref, step.target, step.offset);
if (step.relevant)
chain.relevant = true;
chain.steps.push_front(step);
return access_chain(TREE_OPERAND(ref, 0), chain);
}
case ARRAY_REF:
case ARRAY_RANGE_REF: {
AccessChain::Step step;
step.kind = AccessChain::Step::STEP_ARRAY;
step.t = ref;
chain.steps.push_front(step);
return access_chain(TREE_OPERAND(ref, 0), chain);
}
default:
if (tree_contains_component_ref(ref)) if (tree_contains_component_ref(ref))
return false; return false;
chain.base = ref; chain.base = ref;
return true; 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) { static tree separate_offset_chain_maybe(tree ref, gimple_stmt_iterator* gsi) {
ComponentRefChain crc; AccessChain chain;
if (!component_ref_chain(ref, crc)) if (!access_chain(ref, chain)) {
pinpoint_fatal("separate_offset_chain_maybe encountered invalid COMPONENT_REF chain"); pinpoint_fatal("separate_offset_chain_maybe encountered invalid access chain: top=%s base=%s",
get_tree_code_name(TREE_CODE(ref)),
TREE_OPERAND(ref, 0) ? get_tree_code_name(TREE_CODE(TREE_OPERAND(ref, 0))) : "<null>");
}
if (!crc.relevant) if (!chain.relevant)
return NULL_TREE; return NULL_TREE;
// Get base pointer tree cur_expr = chain.base;
tree base_tmp = NULL_TREE; // NOTE -> Could fold into single call here (needs to track what offsets contribute, +irrelevant combined)
if (TREE_CODE(crc.base) == ADDR_EXPR) for (const AccessChain::Step& step : chain.steps) {
if (step.kind == AccessChain::Step::STEP_COMPONENT) {
if (TREE_CODE(cur_expr) == ADDR_EXPR)
pinpoint_fatal("separate_offset_chain_maybe encountered ADDR_EXPR as base of COMPONENT_REF"); pinpoint_fatal("separate_offset_chain_maybe encountered ADDR_EXPR as base of COMPONENT_REF");
base_tmp = build_fold_addr_expr(crc.base); tree cur_ptr = build_fold_addr_expr(cur_expr);
// For each component ref in chain, add the member offset to the pointer tree field_ptr_type = build_pointer_type(TREE_TYPE(step.t));
tree field_ptr;
for (const ComponentRefChain::Link& cr : crc.links) { if (step.relevant) {
// 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); tree return_tmp = create_tmp_var(size_type_node, NULL);
gimple* call_stmt =
gimple* call_stmt = make_stage0_gimple_separator(return_tmp, cr.target, cr.offset); make_stage0_gimple_separator(return_tmp, step.target, step.offset);
if (!call_stmt) if (!call_stmt)
pinpoint_fatal("separate_offset_chain_maybe failed to make gimple separator for target %u at offset %u", pinpoint_fatal("separate_offset_chain_maybe failed to make gimple separator");
(unsigned)cr.target, (unsigned)cr.offset);
gsi_insert_before(gsi, call_stmt, GSI_SAME_STMT); gsi_insert_before(gsi, call_stmt, GSI_SAME_STMT);
field_ptr = build2(POINTER_PLUS_EXPR, field_ptr_type, cur_ptr, return_tmp);
// 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 { } else {
// Add offsetof contant tree field_decl = TREE_OPERAND(step.t, 1);
tree field_decl = TREE_OPERAND(cr.t, 1);
std::size_t field_offset; std::size_t field_offset;
bool field_bitfield; bool field_bitfield;
if (!field_info(field_decl, &field_offset, nullptr, nullptr, &field_bitfield)) if (!field_info(field_decl, &field_offset, nullptr, nullptr, &field_bitfield))
pinpoint_fatal("separate_offset_chain_maybe failed to get field info of non-target access"); pinpoint_fatal("separate_offset_chain_maybe failed to get field info of non-target access");
if (field_bitfield) if (field_bitfield)
pinpoint_fatal("separate_offset_chain_maybe encountered bitfield access in relevant COMPONENT_REF chain"); 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) field_ptr = build2(POINTER_PLUS_EXPR, field_ptr_type,
tree field_ptr_type = build_pointer_type(TREE_TYPE(cr.t)); cur_ptr, build_int_cst(sizetype, field_offset));
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); tree ptr_tmp = create_tmp_var(field_ptr_type, NULL);
gimple* field_ptr_assignment = gimple_build_assign(base_tmp, field_ptr); gimple* ptr_assign = gimple_build_assign(ptr_tmp, field_ptr);
gsi_insert_before(gsi, field_ptr_assignment, GSI_SAME_STMT); gsi_insert_before(gsi, ptr_assign, GSI_SAME_STMT);
// Dereference constructed pointer expression
tree offset0 = fold_convert(TREE_TYPE(ptr_tmp), build_int_cst(sizetype, 0));
cur_expr = build2(MEM_REF, TREE_TYPE(step.t), ptr_tmp, offset0);
continue;
}
if (step.kind == AccessChain::Step::STEP_ARRAY) {
tree idx = TREE_OPERAND(step.t, 1);
tree low = TREE_OPERAND(step.t, 2);
tree elts = TREE_OPERAND(step.t, 3);
cur_expr = build4(TREE_CODE(step.t), TREE_TYPE(step.t), cur_expr, idx, low, elts);
continue;
} }
} }
// Current pointer is a field pointer -> dereference return cur_expr;
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) { static void dispatch_separation_maybe(const std::list<tree*>& path, gimple_stmt_iterator* gsi, unsigned& cancel_levels) {

View File

@ -7,7 +7,13 @@ const char* second_comm();
int subsecond_pid(); int subsecond_pid();
const char* subsecond_comm(); const char* subsecond_comm();
struct task_struct global = { .pid = 42, .comm = "main_global" }; struct task_struct global = { .pid = 42, .comm = "main_global", .arrfun = {
{ .a = 1, .b = 2, .c = 3, .d = 4.0 },
{ .a = 5, .b = 6, .c = 7, .d = 8.0 },
{ .a = 9, .b = 10, .c = 11, .d = 12.0 },
{ .a = 13, .b = 14, .c = 15, .d = 16.0 },
{ .a = 17, .b = 18, .c = 19, .d = 20.0 }
} };
static void print_layout() { static void print_layout() {
// TODO -> Make builtin __spslr_initial_offsetof(type, field) that is not patched // TODO -> Make builtin __spslr_initial_offsetof(type, field) that is not patched
@ -15,9 +21,14 @@ static void print_layout() {
printf(" stuck0 (int) : %2llu -> %2llu\n", 0, offsetof(struct task_struct, stuck0)); printf(" stuck0 (int) : %2llu -> %2llu\n", 0, offsetof(struct task_struct, stuck0));
printf(" pid (int) : %2llu -> %2llu\n", 8, offsetof(struct task_struct, pid)); printf(" pid (int) : %2llu -> %2llu\n", 8, offsetof(struct task_struct, pid));
printf(" comm (const char*) : %2llu -> %2llu\n", 16, offsetof(struct task_struct, comm)); printf(" comm (const char*) : %2llu -> %2llu\n", 16, offsetof(struct task_struct, comm));
printf(" tasks (struct list_head) : %2llu -> %2llu\n", 24, offsetof(struct task_struct, tasks)); printf(" arrfun (struct ArrayFun [5]) : %2llu -> %2llu\n", 24, offsetof(struct task_struct, arrfun));
printf(" stuck1 (int) : %2llu -> %2llu\n", 40, offsetof(struct task_struct, stuck1)); printf(" tasks (struct list_head) : %2llu -> %2llu\n", 144, offsetof(struct task_struct, tasks));
printf(" stuck2 (int) : %2llu -> %2llu\n", 44, offsetof(struct task_struct, stuck2)); printf(" stuck1 (int) : %2llu -> %2llu\n", 160, offsetof(struct task_struct, stuck1));
printf(" stuck2 (int) : %2llu -> %2llu\n", 166, offsetof(struct task_struct, stuck2));
}
void arr_test() {
printf("Global arrfun[3].b: %d (should be 14)\n", global.arrfun[3].b);
} }
int main(void) int main(void)
@ -55,6 +66,8 @@ int main(void)
printf("Second global: pid=%d comm=\"%s\"\n", second_pid(), second_comm()); printf("Second global: pid=%d comm=\"%s\"\n", second_pid(), second_comm());
printf("Subsecond global: pid=%d comm=\"%s\"\n", subsecond_pid(), subsecond_comm()); printf("Subsecond global: pid=%d comm=\"%s\"\n", subsecond_pid(), subsecond_comm());
arr_test();
return 0; return 0;
} }

View File

@ -43,7 +43,13 @@ static inline void list_add_tail(struct list_head *new, struct list_head *head)
#define list_for_each(pos, head) \ #define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next) for (pos = (head)->next; pos != (head); pos = pos->next)
struct ArrayFun {
int a, b, c;
double d;
} __attribute__((spslr));
// A small struct like the Linux kernel's task_struct // A small struct like the Linux kernel's task_struct
struct task_struct { struct task_struct {
int stuck0; int stuck0;
@ -54,6 +60,8 @@ struct task_struct {
int pid; int pid;
const char *comm; const char *comm;
struct ArrayFun arrfun[5];
randomized_struct_fields_end randomized_struct_fields_end
struct list_head tasks; // linkage for global task list struct list_head tasks; // linkage for global task list