xref: /illumos-gate/usr/src/tools/smatch/src/ir.c (revision c85f09cc)
1 // SPDX-License-Identifier: MIT
2 
3 #include "ir.h"
4 #include "linearize.h"
5 #include <stdlib.h>
6 #include <assert.h>
7 
8 
nbr_phi_operands(struct instruction * insn)9 static int nbr_phi_operands(struct instruction *insn)
10 {
11 	pseudo_t p;
12 	int nbr = 0;
13 
14 	if (!insn->phi_list)
15 		return 0;
16 
17 	FOR_EACH_PTR(insn->phi_list, p) {
18 		if (p == VOID)
19 			continue;
20 		nbr++;
21 	} END_FOR_EACH_PTR(p);
22 
23 	return nbr;
24 }
25 
check_phi_node(struct instruction * insn)26 static int check_phi_node(struct instruction *insn)
27 {
28 	struct basic_block *par;
29 	pseudo_t phi;
30 	int err = 0;
31 
32 	if (!has_users(insn->target))
33 		return err;
34 
35 	if (bb_list_size(insn->bb->parents) != nbr_phi_operands(insn)) {
36 		sparse_error(insn->pos, "bad number of phi operands in:\n\t%s",
37 			show_instruction(insn));
38 		info(insn->pos, "parents: %d", bb_list_size(insn->bb->parents));
39 		info(insn->pos, "phisrcs: %d", nbr_phi_operands(insn));
40 		return 1;
41 	}
42 
43 	PREPARE_PTR_LIST(insn->bb->parents, par);
44 	FOR_EACH_PTR(insn->phi_list, phi) {
45 		struct instruction *src;
46 		if (phi == VOID)
47 			continue;
48 		assert(phi->type == PSEUDO_PHI);
49 		src = phi->def;
50 		if (src->bb != par) {
51 			sparse_error(src->pos, "wrong BB for %s:", show_instruction(src));
52 			info(src->pos, "expected: %s", show_label(par));
53 			info(src->pos, "     got: %s", show_label(src->bb));
54 			err++;
55 		}
56 		NEXT_PTR_LIST(par);
57 	} END_FOR_EACH_PTR(phi);
58 	FINISH_PTR_LIST(par);
59 	return err;
60 }
61 
check_user(struct instruction * insn,pseudo_t pseudo)62 static int check_user(struct instruction *insn, pseudo_t pseudo)
63 {
64 	struct instruction *def;
65 
66 	if (!pseudo) {
67 		show_entry(insn->bb->ep);
68 		sparse_error(insn->pos, "null pseudo in %s", show_instruction(insn));
69 		return 1;
70 	}
71 	switch (pseudo->type) {
72 	case PSEUDO_PHI:
73 	case PSEUDO_REG:
74 		def = pseudo->def;
75 		if (def && def->bb)
76 			break;
77 		show_entry(insn->bb->ep);
78 		sparse_error(insn->pos, "wrong usage for %s in %s", show_pseudo(pseudo),
79 			show_instruction(insn));
80 		return 1;
81 
82 	default:
83 		break;
84 	}
85 	return 0;
86 }
87 
check_branch(struct entrypoint * ep,struct instruction * insn,struct basic_block * bb)88 static int check_branch(struct entrypoint *ep, struct instruction *insn, struct basic_block *bb)
89 {
90 	if (bb->ep && lookup_bb(ep->bbs, bb))
91 		return 0;
92 	sparse_error(insn->pos, "branch to dead BB: %s", show_instruction(insn));
93 	return 1;
94 }
95 
check_switch(struct entrypoint * ep,struct instruction * insn)96 static int check_switch(struct entrypoint *ep, struct instruction *insn)
97 {
98 	struct multijmp *jmp;
99 	int err = 0;
100 
101 	FOR_EACH_PTR(insn->multijmp_list, jmp) {
102 		err = check_branch(ep, insn, jmp->target);
103 		if (err)
104 			return err;
105 	} END_FOR_EACH_PTR(jmp);
106 
107 	return err;
108 }
109 
check_return(struct instruction * insn)110 static int check_return(struct instruction *insn)
111 {
112 	struct symbol *ctype = insn->type;
113 
114 	if (ctype && ctype->bit_size > 0 && insn->src == VOID) {
115 		sparse_error(insn->pos, "return without value");
116 		return 1;
117 	}
118 	return 0;
119 }
120 
validate_insn(struct entrypoint * ep,struct instruction * insn)121 static int validate_insn(struct entrypoint *ep, struct instruction *insn)
122 {
123 	int err = 0;
124 
125 	switch (insn->opcode) {
126 	case OP_SEL:
127 	case OP_RANGE:
128 		err += check_user(insn, insn->src3);
129 		/* fall through */
130 
131 	case OP_BINARY ... OP_BINCMP_END:
132 		err += check_user(insn, insn->src2);
133 		/* fall through */
134 
135 	case OP_UNOP ... OP_UNOP_END:
136 	case OP_SLICE:
137 	case OP_SYMADDR:
138 	case OP_PHISOURCE:
139 		err += check_user(insn, insn->src1);
140 		break;
141 
142 	case OP_CBR:
143 		err += check_branch(ep, insn, insn->bb_true);
144 		err += check_branch(ep, insn, insn->bb_false);
145 		/* fall through */
146 	case OP_COMPUTEDGOTO:
147 		err += check_user(insn, insn->cond);
148 		break;
149 
150 	case OP_PHI:
151 		err += check_phi_node(insn);
152 		break;
153 
154 	case OP_CALL:
155 		// FIXME: ignore for now
156 		break;
157 
158 	case OP_STORE:
159 		err += check_user(insn, insn->target);
160 		/* fall through */
161 
162 	case OP_LOAD:
163 		err += check_user(insn, insn->src);
164 		break;
165 
166 	case OP_RET:
167 		err += check_return(insn);
168 		break;
169 
170 	case OP_BR:
171 		err += check_branch(ep, insn, insn->bb_true);
172 		break;
173 	case OP_SWITCH:
174 		err += check_switch(ep, insn);
175 		break;
176 
177 	case OP_ENTRY:
178 	case OP_SETVAL:
179 	default:
180 		break;
181 	}
182 
183 	return err;
184 }
185 
ir_validate(struct entrypoint * ep)186 int ir_validate(struct entrypoint *ep)
187 {
188 	struct basic_block *bb;
189 	int err = 0;
190 
191 	if (!dbg_ir || has_error)
192 		return 0;
193 
194 	FOR_EACH_PTR(ep->bbs, bb) {
195 		struct instruction *insn;
196 		FOR_EACH_PTR(bb->insns, insn) {
197 			if (!insn->bb)
198 				continue;
199 			err += validate_insn(ep, insn);
200 		} END_FOR_EACH_PTR(insn);
201 	} END_FOR_EACH_PTR(bb);
202 
203 	if (err)
204 		abort();
205 	return err;
206 }
207