1// SPDX-License-Identifier: MIT
2
3#include "ir.h"
4#include "linearize.h"
5#include <stdlib.h>
6#include <assert.h>
7
8
9static 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
26static 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
62static 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
88static 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
96static 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
110static 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
121static 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
186int 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