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