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_LABEL:
179 case OP_SETVAL:
180 default:
181 break;
182 }
183
184 return err;
185 }
186
ir_validate(struct entrypoint * ep)187 int ir_validate(struct entrypoint *ep)
188 {
189 struct basic_block *bb;
190 int err = 0;
191
192 if (!dbg_ir || has_error)
193 return 0;
194
195 FOR_EACH_PTR(ep->bbs, bb) {
196 struct instruction *insn;
197 FOR_EACH_PTR(bb->insns, insn) {
198 if (!insn->bb)
199 continue;
200 err += validate_insn(ep, insn);
201 } END_FOR_EACH_PTR(insn);
202 } END_FOR_EACH_PTR(bb);
203
204 if (err)
205 abort();
206 return err;
207 }
208