blob: 2e284c251ee81da8d39a9ff8596da550e560a0f0 [file] [log] [blame]
// SPDX-License-Identifier: MIT
#include "ir.h"
#include "linearize.h"
#include <stdlib.h>
#include <assert.h>
static int nbr_phi_operands(struct instruction *insn)
{
pseudo_t p;
int nbr = 0;
if (!insn->phi_list)
return 0;
FOR_EACH_PTR(insn->phi_list, p) {
if (p == VOID)
continue;
nbr++;
} END_FOR_EACH_PTR(p);
return nbr;
}
static int check_phi_node(struct instruction *insn)
{
struct basic_block *par;
pseudo_t phi;
int err = 0;
if (!has_users(insn->target))
return err;
if (bb_list_size(insn->bb->parents) != nbr_phi_operands(insn)) {
sparse_error(insn->pos, "bad number of phi operands in:\n\t%s",
show_instruction(insn));
info(insn->pos, "parents: %d", bb_list_size(insn->bb->parents));
info(insn->pos, "phisrcs: %d", nbr_phi_operands(insn));
return 1;
}
PREPARE_PTR_LIST(insn->bb->parents, par);
FOR_EACH_PTR(insn->phi_list, phi) {
struct instruction *src;
if (phi == VOID)
continue;
assert(phi->type == PSEUDO_PHI);
src = phi->def;
if (src->bb != par) {
sparse_error(src->pos, "wrong BB for %s:", show_instruction(src));
info(src->pos, "expected: %s", show_label(par));
info(src->pos, " got: %s", show_label(src->bb));
err++;
}
NEXT_PTR_LIST(par);
} END_FOR_EACH_PTR(phi);
FINISH_PTR_LIST(par);
return err;
}
static int check_user(struct instruction *insn, pseudo_t pseudo)
{
struct instruction *def;
if (!pseudo) {
show_entry(insn->bb->ep);
sparse_error(insn->pos, "null pseudo in %s", show_instruction(insn));
return 1;
}
switch (pseudo->type) {
case PSEUDO_PHI:
case PSEUDO_REG:
def = pseudo->def;
if (def && def->bb)
break;
show_entry(insn->bb->ep);
sparse_error(insn->pos, "wrong usage for %s in %s", show_pseudo(pseudo),
show_instruction(insn));
return 1;
default:
break;
}
return 0;
}
static int check_branch(struct entrypoint *ep, struct instruction *insn, struct basic_block *bb)
{
if (bb->ep && lookup_bb(ep->bbs, bb))
return 0;
sparse_error(insn->pos, "branch to dead BB: %s", show_instruction(insn));
return 1;
}
static int check_switch(struct entrypoint *ep, struct instruction *insn)
{
struct multijmp *jmp;
int err = 0;
FOR_EACH_PTR(insn->multijmp_list, jmp) {
err = check_branch(ep, insn, jmp->target);
if (err)
return err;
} END_FOR_EACH_PTR(jmp);
return err;
}
static int check_return(struct instruction *insn)
{
struct symbol *ctype = insn->type;
if (ctype && ctype->bit_size > 0 && insn->src == VOID) {
sparse_error(insn->pos, "return without value");
return 1;
}
return 0;
}
static int validate_insn(struct entrypoint *ep, struct instruction *insn)
{
int err = 0;
switch (insn->opcode) {
case OP_SEL:
case OP_RANGE:
err += check_user(insn, insn->src3);
/* fall through */
case OP_BINARY ... OP_BINCMP_END:
err += check_user(insn, insn->src2);
/* fall through */
case OP_UNOP ... OP_UNOP_END:
case OP_SLICE:
case OP_SYMADDR:
case OP_PHISOURCE:
err += check_user(insn, insn->src1);
break;
case OP_CBR:
err += check_branch(ep, insn, insn->bb_true);
err += check_branch(ep, insn, insn->bb_false);
/* fall through */
case OP_COMPUTEDGOTO:
err += check_user(insn, insn->cond);
break;
case OP_PHI:
err += check_phi_node(insn);
break;
case OP_CALL:
// FIXME: ignore for now
break;
case OP_STORE:
err += check_user(insn, insn->target);
/* fall through */
case OP_LOAD:
err += check_user(insn, insn->src);
break;
case OP_RET:
err += check_return(insn);
break;
case OP_BR:
err += check_branch(ep, insn, insn->bb_true);
break;
case OP_SWITCH:
err += check_switch(ep, insn);
break;
case OP_ENTRY:
case OP_SETVAL:
default:
break;
}
return err;
}
int ir_validate(struct entrypoint *ep)
{
struct basic_block *bb;
int err = 0;
if (!dbg_ir || has_error)
return 0;
FOR_EACH_PTR(ep->bbs, bb) {
struct instruction *insn;
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
err += validate_insn(ep, insn);
} END_FOR_EACH_PTR(insn);
} END_FOR_EACH_PTR(bb);
if (err)
abort();
return err;
}