xref: /illumos-gate/usr/src/tools/smatch/src/liveness.c (revision cadd68ea0014761eda6a293664086dfa80686d85)
1 /*
2  * Register - track pseudo usage, maybe eventually try to do register
3  * allocation.
4  *
5  * Copyright (C) 2004 Linus Torvalds
6  */
7 
8 #include <assert.h>
9 
10 #include "parse.h"
11 #include "expression.h"
12 #include "linearize.h"
13 #include "flow.h"
14 
15 static void phi_defines(struct instruction * phi_node, pseudo_t target,
16 	void (*defines)(struct basic_block *, pseudo_t))
17 {
18 	pseudo_t phi;
19 	FOR_EACH_PTR(phi_node->phi_list, phi) {
20 		struct instruction *def;
21 		if (phi == VOID)
22 			continue;
23 		def = phi->def;
24 		if (!def || !def->bb)
25 			continue;
26 		defines(def->bb, target);
27 	} END_FOR_EACH_PTR(phi);
28 }
29 
30 static void asm_liveness(struct basic_block *bb, struct instruction *insn,
31 	void (*def)(struct basic_block *, pseudo_t),
32 	void (*use)(struct basic_block *, pseudo_t))
33 {
34 	struct asm_constraint *entry;
35 
36 	FOR_EACH_PTR(insn->asm_rules->inputs, entry) {
37 		use(bb, entry->pseudo);
38 	} END_FOR_EACH_PTR(entry);
39 
40 	FOR_EACH_PTR(insn->asm_rules->outputs, entry) {
41 		def(bb, entry->pseudo);
42 	} END_FOR_EACH_PTR(entry);
43 }
44 
45 static void track_instruction_usage(struct basic_block *bb, struct instruction *insn,
46 	void (*def)(struct basic_block *, pseudo_t),
47 	void (*use)(struct basic_block *, pseudo_t))
48 {
49 	pseudo_t pseudo;
50 
51 	#define USES(x)		use(bb, insn->x)
52 	#define DEFINES(x)	def(bb, insn->x)
53 
54 	switch (insn->opcode) {
55 	case OP_RET:
56 		USES(src);
57 		break;
58 
59 	case OP_CBR:
60 	case OP_SWITCH:
61 		USES(cond);
62 		break;
63 
64 	case OP_COMPUTEDGOTO:
65 		USES(target);
66 		break;
67 
68 	/* Binary */
69 	case OP_BINARY ... OP_BINARY_END:
70 	case OP_BINCMP ... OP_BINCMP_END:
71 		USES(src1); USES(src2); DEFINES(target);
72 		break;
73 
74 	/* Uni */
75 	case OP_NOT: case OP_NEG:
76 		USES(src1); DEFINES(target);
77 		break;
78 
79 	case OP_SEL:
80 		USES(src1); USES(src2); USES(src3); DEFINES(target);
81 		break;
82 
83 	/* Memory */
84 	case OP_LOAD:
85 		USES(src); DEFINES(target);
86 		break;
87 
88 	case OP_STORE:
89 		USES(src); USES(target);
90 		break;
91 
92 	case OP_SETVAL:
93 		DEFINES(target);
94 		break;
95 
96 	case OP_SYMADDR:
97 		USES(symbol); DEFINES(target);
98 		break;
99 
100 	/* Other */
101 	case OP_PHI:
102 		/* Phi-nodes are "backwards" nodes. Their def doesn't matter */
103 		phi_defines(insn, insn->target, def);
104 		break;
105 
106 	case OP_PHISOURCE:
107 		/*
108 		 * We don't care about the phi-source define, they get set
109 		 * up and expanded by the OP_PHI
110 		 */
111 		USES(phi_src);
112 		break;
113 
114 	case OP_CAST:
115 	case OP_SCAST:
116 	case OP_FPCAST:
117 	case OP_PTRCAST:
118 		USES(src); DEFINES(target);
119 		break;
120 
121 	case OP_CALL:
122 		USES(func);
123 		if (insn->target != VOID)
124 			DEFINES(target);
125 		FOR_EACH_PTR(insn->arguments, pseudo) {
126 			use(bb, pseudo);
127 		} END_FOR_EACH_PTR(pseudo);
128 		break;
129 
130 	case OP_SLICE:
131 		USES(base); DEFINES(target);
132 		break;
133 
134 	case OP_ASM:
135 		asm_liveness(bb, insn, def, use);
136 		break;
137 
138 	case OP_RANGE:
139 		USES(src1); USES(src2); USES(src3);
140 		break;
141 
142 	case OP_BADOP:
143 	case OP_INVOKE:
144 	case OP_UNWIND:
145 	case OP_MALLOC:
146 	case OP_FREE:
147 	case OP_ALLOCA:
148 	case OP_GET_ELEMENT_PTR:
149 	case OP_VANEXT:
150 	case OP_VAARG:
151 	case OP_SNOP:
152 	case OP_LNOP:
153 	case OP_NOP:
154 	case OP_CONTEXT:
155 		break;
156 	}
157 }
158 
159 int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo)
160 {
161 	pseudo_t old;
162 	FOR_EACH_PTR(list,old) {
163 		if (old == pseudo)
164 			return 1;
165 	} END_FOR_EACH_PTR(old);
166 	return 0;
167 }
168 
169 static int liveness_changed;
170 
171 static void add_pseudo_exclusive(struct pseudo_list **list, pseudo_t pseudo)
172 {
173 	if (!pseudo_in_list(*list, pseudo)) {
174 		liveness_changed = 1;
175 		add_pseudo(list, pseudo);
176 	}
177 }
178 
179 static inline int trackable_pseudo(pseudo_t pseudo)
180 {
181 	return pseudo && (pseudo->type == PSEUDO_REG || pseudo->type == PSEUDO_ARG);
182 }
183 
184 static void insn_uses(struct basic_block *bb, pseudo_t pseudo)
185 {
186 	if (trackable_pseudo(pseudo)) {
187 		struct instruction *def = pseudo->def;
188 		if (pseudo->type != PSEUDO_REG || def->bb != bb || def->opcode == OP_PHI)
189 			add_pseudo_exclusive(&bb->needs, pseudo);
190 	}
191 }
192 
193 static void insn_defines(struct basic_block *bb, pseudo_t pseudo)
194 {
195 	assert(trackable_pseudo(pseudo));
196 	add_pseudo(&bb->defines, pseudo);
197 }
198 
199 static void track_bb_liveness(struct basic_block *bb)
200 {
201 	pseudo_t needs;
202 
203 	FOR_EACH_PTR(bb->needs, needs) {
204 		struct basic_block *parent;
205 		FOR_EACH_PTR(bb->parents, parent) {
206 			if (!pseudo_in_list(parent->defines, needs)) {
207 				add_pseudo_exclusive(&parent->needs, needs);
208 			}
209 		} END_FOR_EACH_PTR(parent);
210 	} END_FOR_EACH_PTR(needs);
211 }
212 
213 /*
214  * We need to clear the liveness information if we
215  * are going to re-run it.
216  */
217 void clear_liveness(struct entrypoint *ep)
218 {
219 	struct basic_block *bb;
220 
221 	FOR_EACH_PTR(ep->bbs, bb) {
222 		free_ptr_list(&bb->needs);
223 		free_ptr_list(&bb->defines);
224 	} END_FOR_EACH_PTR(bb);
225 }
226 
227 /*
228  * Track inter-bb pseudo liveness. The intra-bb case
229  * is purely local information.
230  */
231 void track_pseudo_liveness(struct entrypoint *ep)
232 {
233 	struct basic_block *bb;
234 
235 	/* Add all the bb pseudo usage */
236 	FOR_EACH_PTR(ep->bbs, bb) {
237 		struct instruction *insn;
238 		FOR_EACH_PTR(bb->insns, insn) {
239 			if (!insn->bb)
240 				continue;
241 			assert(insn->bb == bb);
242 			track_instruction_usage(bb, insn, insn_defines, insn_uses);
243 		} END_FOR_EACH_PTR(insn);
244 	} END_FOR_EACH_PTR(bb);
245 
246 	/* Calculate liveness.. */
247 	do {
248 		liveness_changed = 0;
249 		FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
250 			track_bb_liveness(bb);
251 		} END_FOR_EACH_PTR_REVERSE(bb);
252 	} while (liveness_changed);
253 
254 	/* Remove the pseudos from the "defines" list that are used internally */
255 	FOR_EACH_PTR(ep->bbs, bb) {
256 		pseudo_t def;
257 		FOR_EACH_PTR(bb->defines, def) {
258 			struct basic_block *child;
259 			FOR_EACH_PTR(bb->children, child) {
260 				if (pseudo_in_list(child->needs, def))
261 					goto is_used;
262 			} END_FOR_EACH_PTR(child);
263 			DELETE_CURRENT_PTR(def);
264 is_used:
265 		;
266 		} END_FOR_EACH_PTR(def);
267 		PACK_PTR_LIST(&bb->defines);
268 	} END_FOR_EACH_PTR(bb);
269 }
270 
271 static void merge_pseudo_list(struct pseudo_list *src, struct pseudo_list **dest)
272 {
273 	pseudo_t pseudo;
274 	FOR_EACH_PTR(src, pseudo) {
275 		add_pseudo_exclusive(dest, pseudo);
276 	} END_FOR_EACH_PTR(pseudo);
277 }
278 
279 void track_phi_uses(struct instruction *insn)
280 {
281 	pseudo_t phi;
282 	FOR_EACH_PTR(insn->phi_list, phi) {
283 		struct instruction *def;
284 		if (phi == VOID || !phi->def)
285 			continue;
286 		def = phi->def;
287 		assert(def->opcode == OP_PHISOURCE);
288 		add_ptr_list(&def->phi_users, insn);
289 	} END_FOR_EACH_PTR(phi);
290 }
291 
292 static void track_bb_phi_uses(struct basic_block *bb)
293 {
294 	struct instruction *insn;
295 	FOR_EACH_PTR(bb->insns, insn) {
296 		if (insn->bb && insn->opcode == OP_PHI)
297 			track_phi_uses(insn);
298 	} END_FOR_EACH_PTR(insn);
299 }
300 
301 static struct pseudo_list **live_list;
302 static struct pseudo_list *dead_list;
303 
304 static void death_def(struct basic_block *bb, pseudo_t pseudo)
305 {
306 }
307 
308 static void death_use(struct basic_block *bb, pseudo_t pseudo)
309 {
310 	if (trackable_pseudo(pseudo) && !pseudo_in_list(*live_list, pseudo)) {
311 		add_pseudo(&dead_list, pseudo);
312 		add_pseudo(live_list, pseudo);
313 	}
314 }
315 
316 static void track_pseudo_death_bb(struct basic_block *bb)
317 {
318 	struct pseudo_list *live = NULL;
319 	struct basic_block *child;
320 	struct instruction *insn;
321 
322 	FOR_EACH_PTR(bb->children, child) {
323 		merge_pseudo_list(child->needs, &live);
324 	} END_FOR_EACH_PTR(child);
325 
326 	live_list = &live;
327 	FOR_EACH_PTR_REVERSE(bb->insns, insn) {
328 		if (!insn->bb)
329 			continue;
330 
331 		dead_list = NULL;
332 		track_instruction_usage(bb, insn, death_def, death_use);
333 		if (dead_list) {
334 			pseudo_t dead;
335 			FOR_EACH_PTR(dead_list, dead) {
336 				struct instruction *deathnote = __alloc_instruction(0);
337 				deathnote->bb = bb;
338 				deathnote->opcode = OP_DEATHNOTE;
339 				deathnote->target = dead;
340 				INSERT_CURRENT(deathnote, insn);
341 			} END_FOR_EACH_PTR(dead);
342 			free_ptr_list(&dead_list);
343 		}
344 	} END_FOR_EACH_PTR_REVERSE(insn);
345 	free_ptr_list(&live);
346 }
347 
348 void track_pseudo_death(struct entrypoint *ep)
349 {
350 	struct basic_block *bb;
351 
352 	FOR_EACH_PTR(ep->bbs, bb) {
353 		track_bb_phi_uses(bb);
354 	} END_FOR_EACH_PTR(bb);
355 
356 	FOR_EACH_PTR(ep->bbs, bb) {
357 		track_pseudo_death_bb(bb);
358 	} END_FOR_EACH_PTR(bb);
359 }
360