1 // SPDX-License-Identifier: MIT
   2 
   3 #include "ir.h"
   4 #include "linearize.h"
   5 #include <stdlib.h>
   6 #include <assert.h>
   7 
   8 
   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 
  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 
  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 
  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 
  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 
 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 
 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 
 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 }