18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include "block-range.h" 38c2ecf20Sopenharmony_ci#include "annotate.h" 48c2ecf20Sopenharmony_ci#include <assert.h> 58c2ecf20Sopenharmony_ci#include <stdlib.h> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_cistruct { 88c2ecf20Sopenharmony_ci struct rb_root root; 98c2ecf20Sopenharmony_ci u64 blocks; 108c2ecf20Sopenharmony_ci} block_ranges; 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic void block_range__debug(void) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci /* 158c2ecf20Sopenharmony_ci * XXX still paranoid for now; see if we can make this depend on 168c2ecf20Sopenharmony_ci * DEBUG=1 builds. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci#if 1 198c2ecf20Sopenharmony_ci struct rb_node *rb; 208c2ecf20Sopenharmony_ci u64 old = 0; /* NULL isn't executable */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci for (rb = rb_first(&block_ranges.root); rb; rb = rb_next(rb)) { 238c2ecf20Sopenharmony_ci struct block_range *entry = rb_entry(rb, struct block_range, node); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci assert(old < entry->start); 268c2ecf20Sopenharmony_ci assert(entry->start <= entry->end); /* single instruction block; jump to a jump */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci old = entry->end; 298c2ecf20Sopenharmony_ci } 308c2ecf20Sopenharmony_ci#endif 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct block_range *block_range__find(u64 addr) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct rb_node **p = &block_ranges.root.rb_node; 368c2ecf20Sopenharmony_ci struct rb_node *parent = NULL; 378c2ecf20Sopenharmony_ci struct block_range *entry; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci while (*p != NULL) { 408c2ecf20Sopenharmony_ci parent = *p; 418c2ecf20Sopenharmony_ci entry = rb_entry(parent, struct block_range, node); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (addr < entry->start) 448c2ecf20Sopenharmony_ci p = &parent->rb_left; 458c2ecf20Sopenharmony_ci else if (addr > entry->end) 468c2ecf20Sopenharmony_ci p = &parent->rb_right; 478c2ecf20Sopenharmony_ci else 488c2ecf20Sopenharmony_ci return entry; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return NULL; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic inline void rb_link_left_of_node(struct rb_node *left, struct rb_node *node) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct rb_node **p = &node->rb_left; 578c2ecf20Sopenharmony_ci while (*p) { 588c2ecf20Sopenharmony_ci node = *p; 598c2ecf20Sopenharmony_ci p = &node->rb_right; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci rb_link_node(left, node, p); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic inline void rb_link_right_of_node(struct rb_node *right, struct rb_node *node) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct rb_node **p = &node->rb_right; 678c2ecf20Sopenharmony_ci while (*p) { 688c2ecf20Sopenharmony_ci node = *p; 698c2ecf20Sopenharmony_ci p = &node->rb_left; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci rb_link_node(right, node, p); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/** 758c2ecf20Sopenharmony_ci * block_range__create 768c2ecf20Sopenharmony_ci * @start: branch target starting this basic block 778c2ecf20Sopenharmony_ci * @end: branch ending this basic block 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * Create all the required block ranges to precisely span the given range. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_cistruct block_range_iter block_range__create(u64 start, u64 end) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct rb_node **p = &block_ranges.root.rb_node; 848c2ecf20Sopenharmony_ci struct rb_node *n, *parent = NULL; 858c2ecf20Sopenharmony_ci struct block_range *next, *entry = NULL; 868c2ecf20Sopenharmony_ci struct block_range_iter iter = { NULL, NULL }; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci while (*p != NULL) { 898c2ecf20Sopenharmony_ci parent = *p; 908c2ecf20Sopenharmony_ci entry = rb_entry(parent, struct block_range, node); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (start < entry->start) 938c2ecf20Sopenharmony_ci p = &parent->rb_left; 948c2ecf20Sopenharmony_ci else if (start > entry->end) 958c2ecf20Sopenharmony_ci p = &parent->rb_right; 968c2ecf20Sopenharmony_ci else 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* 1018c2ecf20Sopenharmony_ci * Didn't find anything.. there's a hole at @start, however @end might 1028c2ecf20Sopenharmony_ci * be inside/behind the next range. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci if (!*p) { 1058c2ecf20Sopenharmony_ci if (!entry) /* tree empty */ 1068c2ecf20Sopenharmony_ci goto do_whole; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * If the last node is before, advance one to find the next. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci n = parent; 1128c2ecf20Sopenharmony_ci if (entry->end < start) { 1138c2ecf20Sopenharmony_ci n = rb_next(n); 1148c2ecf20Sopenharmony_ci if (!n) 1158c2ecf20Sopenharmony_ci goto do_whole; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci next = rb_entry(n, struct block_range, node); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (next->start <= end) { /* add head: [start...][n->start...] */ 1208c2ecf20Sopenharmony_ci struct block_range *head = malloc(sizeof(struct block_range)); 1218c2ecf20Sopenharmony_ci if (!head) 1228c2ecf20Sopenharmony_ci return iter; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci *head = (struct block_range){ 1258c2ecf20Sopenharmony_ci .start = start, 1268c2ecf20Sopenharmony_ci .end = next->start - 1, 1278c2ecf20Sopenharmony_ci .is_target = 1, 1288c2ecf20Sopenharmony_ci .is_branch = 0, 1298c2ecf20Sopenharmony_ci }; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci rb_link_left_of_node(&head->node, &next->node); 1328c2ecf20Sopenharmony_ci rb_insert_color(&head->node, &block_ranges.root); 1338c2ecf20Sopenharmony_ci block_range__debug(); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci iter.start = head; 1368c2ecf20Sopenharmony_ci goto do_tail; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cido_whole: 1408c2ecf20Sopenharmony_ci /* 1418c2ecf20Sopenharmony_ci * The whole [start..end] range is non-overlapping. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci entry = malloc(sizeof(struct block_range)); 1448c2ecf20Sopenharmony_ci if (!entry) 1458c2ecf20Sopenharmony_ci return iter; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci *entry = (struct block_range){ 1488c2ecf20Sopenharmony_ci .start = start, 1498c2ecf20Sopenharmony_ci .end = end, 1508c2ecf20Sopenharmony_ci .is_target = 1, 1518c2ecf20Sopenharmony_ci .is_branch = 1, 1528c2ecf20Sopenharmony_ci }; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci rb_link_node(&entry->node, parent, p); 1558c2ecf20Sopenharmony_ci rb_insert_color(&entry->node, &block_ranges.root); 1568c2ecf20Sopenharmony_ci block_range__debug(); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci iter.start = entry; 1598c2ecf20Sopenharmony_ci iter.end = entry; 1608c2ecf20Sopenharmony_ci goto done; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * We found a range that overlapped with ours, split if needed. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci if (entry->start < start) { /* split: [e->start...][start...] */ 1678c2ecf20Sopenharmony_ci struct block_range *head = malloc(sizeof(struct block_range)); 1688c2ecf20Sopenharmony_ci if (!head) 1698c2ecf20Sopenharmony_ci return iter; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci *head = (struct block_range){ 1728c2ecf20Sopenharmony_ci .start = entry->start, 1738c2ecf20Sopenharmony_ci .end = start - 1, 1748c2ecf20Sopenharmony_ci .is_target = entry->is_target, 1758c2ecf20Sopenharmony_ci .is_branch = 0, 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci .coverage = entry->coverage, 1788c2ecf20Sopenharmony_ci .entry = entry->entry, 1798c2ecf20Sopenharmony_ci }; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci entry->start = start; 1828c2ecf20Sopenharmony_ci entry->is_target = 1; 1838c2ecf20Sopenharmony_ci entry->entry = 0; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci rb_link_left_of_node(&head->node, &entry->node); 1868c2ecf20Sopenharmony_ci rb_insert_color(&head->node, &block_ranges.root); 1878c2ecf20Sopenharmony_ci block_range__debug(); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci } else if (entry->start == start) 1908c2ecf20Sopenharmony_ci entry->is_target = 1; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci iter.start = entry; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cido_tail: 1958c2ecf20Sopenharmony_ci /* 1968c2ecf20Sopenharmony_ci * At this point we've got: @iter.start = [@start...] but @end can still be 1978c2ecf20Sopenharmony_ci * inside or beyond it. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ci entry = iter.start; 2008c2ecf20Sopenharmony_ci for (;;) { 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * If @end is inside @entry, split. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci if (end < entry->end) { /* split: [...end][...e->end] */ 2058c2ecf20Sopenharmony_ci struct block_range *tail = malloc(sizeof(struct block_range)); 2068c2ecf20Sopenharmony_ci if (!tail) 2078c2ecf20Sopenharmony_ci return iter; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci *tail = (struct block_range){ 2108c2ecf20Sopenharmony_ci .start = end + 1, 2118c2ecf20Sopenharmony_ci .end = entry->end, 2128c2ecf20Sopenharmony_ci .is_target = 0, 2138c2ecf20Sopenharmony_ci .is_branch = entry->is_branch, 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci .coverage = entry->coverage, 2168c2ecf20Sopenharmony_ci .taken = entry->taken, 2178c2ecf20Sopenharmony_ci .pred = entry->pred, 2188c2ecf20Sopenharmony_ci }; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci entry->end = end; 2218c2ecf20Sopenharmony_ci entry->is_branch = 1; 2228c2ecf20Sopenharmony_ci entry->taken = 0; 2238c2ecf20Sopenharmony_ci entry->pred = 0; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci rb_link_right_of_node(&tail->node, &entry->node); 2268c2ecf20Sopenharmony_ci rb_insert_color(&tail->node, &block_ranges.root); 2278c2ecf20Sopenharmony_ci block_range__debug(); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci iter.end = entry; 2308c2ecf20Sopenharmony_ci goto done; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* 2348c2ecf20Sopenharmony_ci * If @end matches @entry, done 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci if (end == entry->end) { 2378c2ecf20Sopenharmony_ci entry->is_branch = 1; 2388c2ecf20Sopenharmony_ci iter.end = entry; 2398c2ecf20Sopenharmony_ci goto done; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci next = block_range__next(entry); 2438c2ecf20Sopenharmony_ci if (!next) 2448c2ecf20Sopenharmony_ci goto add_tail; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* 2478c2ecf20Sopenharmony_ci * If @end is in beyond @entry but not inside @next, add tail. 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci if (end < next->start) { /* add tail: [...e->end][...end] */ 2508c2ecf20Sopenharmony_ci struct block_range *tail; 2518c2ecf20Sopenharmony_ciadd_tail: 2528c2ecf20Sopenharmony_ci tail = malloc(sizeof(struct block_range)); 2538c2ecf20Sopenharmony_ci if (!tail) 2548c2ecf20Sopenharmony_ci return iter; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci *tail = (struct block_range){ 2578c2ecf20Sopenharmony_ci .start = entry->end + 1, 2588c2ecf20Sopenharmony_ci .end = end, 2598c2ecf20Sopenharmony_ci .is_target = 0, 2608c2ecf20Sopenharmony_ci .is_branch = 1, 2618c2ecf20Sopenharmony_ci }; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci rb_link_right_of_node(&tail->node, &entry->node); 2648c2ecf20Sopenharmony_ci rb_insert_color(&tail->node, &block_ranges.root); 2658c2ecf20Sopenharmony_ci block_range__debug(); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci iter.end = tail; 2688c2ecf20Sopenharmony_ci goto done; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* 2728c2ecf20Sopenharmony_ci * If there is a hole between @entry and @next, fill it. 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ci if (entry->end + 1 != next->start) { 2758c2ecf20Sopenharmony_ci struct block_range *hole = malloc(sizeof(struct block_range)); 2768c2ecf20Sopenharmony_ci if (!hole) 2778c2ecf20Sopenharmony_ci return iter; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci *hole = (struct block_range){ 2808c2ecf20Sopenharmony_ci .start = entry->end + 1, 2818c2ecf20Sopenharmony_ci .end = next->start - 1, 2828c2ecf20Sopenharmony_ci .is_target = 0, 2838c2ecf20Sopenharmony_ci .is_branch = 0, 2848c2ecf20Sopenharmony_ci }; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci rb_link_left_of_node(&hole->node, &next->node); 2878c2ecf20Sopenharmony_ci rb_insert_color(&hole->node, &block_ranges.root); 2888c2ecf20Sopenharmony_ci block_range__debug(); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci entry = next; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cidone: 2958c2ecf20Sopenharmony_ci assert(iter.start->start == start && iter.start->is_target); 2968c2ecf20Sopenharmony_ci assert(iter.end->end == end && iter.end->is_branch); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci block_ranges.blocks++; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return iter; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/* 3058c2ecf20Sopenharmony_ci * Compute coverage as: 3068c2ecf20Sopenharmony_ci * 3078c2ecf20Sopenharmony_ci * br->coverage / br->sym->max_coverage 3088c2ecf20Sopenharmony_ci * 3098c2ecf20Sopenharmony_ci * This ensures each symbol has a 100% spot, to reflect that each symbol has a 3108c2ecf20Sopenharmony_ci * most covered section. 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * Returns [0-1] for coverage and -1 if we had no data what so ever or the 3138c2ecf20Sopenharmony_ci * symbol does not exist. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_cidouble block_range__coverage(struct block_range *br) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct symbol *sym; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (!br) { 3208c2ecf20Sopenharmony_ci if (block_ranges.blocks) 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return -1; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci sym = br->sym; 3278c2ecf20Sopenharmony_ci if (!sym) 3288c2ecf20Sopenharmony_ci return -1; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return (double)br->coverage / symbol__annotation(sym)->max_coverage; 3318c2ecf20Sopenharmony_ci} 332