Print this page
11972 resync smatch
        
*** 2,20 ****
   * Simplify - do instruction simplification before CSE
   *
   * Copyright (C) 2004 Linus Torvalds
   */
  
  #include <assert.h>
  
  #include "parse.h"
  #include "expression.h"
  #include "linearize.h"
  #include "flow.h"
  #include "symbol.h"
  
! /* Find the trivial parent for a phi-source */
  static struct basic_block *phi_parent(struct basic_block *source, pseudo_t pseudo)
  {
          /* Can't go upwards if the pseudo is defined in the bb it came from.. */
          if (pseudo->type == PSEUDO_REG) {
                  struct instruction *def = pseudo->def;
--- 2,60 ----
   * Simplify - do instruction simplification before CSE
   *
   * Copyright (C) 2004 Linus Torvalds
   */
  
+ ///
+ // Instruction simplification
+ // --------------------------
+ //
+ // Notation
+ // ^^^^^^^^
+ // The following conventions are used to describe the simplications:
+ // * Uppercase letters are reserved for constants:
+ //   * `M` for a constant mask,
+ //   * `S` for a constant shift,
+ //   * `N` for a constant number of bits (usually other than a shift),
+ //   * `C` or 'K' for others constants.
+ // * Lowercase letters `a`, `b`, `x`, `y`, ... are used for non-constants
+ //   or when it doesn't matter if the pseudo is a constant or not.
+ // * Primes are used if needed to distinguish symbols (`M`, `M'`, ...).
+ // * Expressions or sub-expressions involving only constants are
+ //   understood to be evaluated.
+ // * `$mask(N)` is used for `((1 << N) -1)`
+ // * `$trunc(x, N)` is used for `(x & $mask(N))`
+ // * Expressions like `(-1 << S)`, `(-1 >> S)` and others formulae are
+ //   understood to be truncated to the size of the current instruction
+ //   (needed, since in general this size is not the same as the one used
+ //   by sparse for the evaluation of arithmetic operations).
+ // * `TRUNC(x, N)` is used for a truncation *to* a size of `N` bits
+ // * `ZEXT(x, N)` is used for a zero-extension *from* a size of `N` bits
+ // * `OP(x, C)` is used to represent some generic operation using a constant,
+ //   including when the constant is implicit (e.g. `TRUNC(x, N)`).
+ // * `MASK(x, M)` is used to respresent a 'masking' instruction:
+ //   - `AND(x, M)`
+ //   - `LSR(x, S)`, with `M` = (-1 << S)
+ //   - `SHL(x, S)`, with `M` = (-1 >> S)
+ //   - `TRUNC(x, N)`, with `M` = $mask(N)
+ //   - `ZEXT(x, N)`, with `M` = $mask(N)
+ // * `SHIFT(x, S)` is used for `LSR(x, S)` or `SHL(x, S)`.
+ 
  #include <assert.h>
  
  #include "parse.h"
  #include "expression.h"
  #include "linearize.h"
  #include "flow.h"
  #include "symbol.h"
  
! ///
! // Utilities
! // ^^^^^^^^^
! 
! ///
! // find the trivial parent for a phi-source
  static struct basic_block *phi_parent(struct basic_block *source, pseudo_t pseudo)
  {
          /* Can't go upwards if the pseudo is defined in the bb it came from.. */
          if (pseudo->type == PSEUDO_REG) {
                  struct instruction *def = pseudo->def;
*** 24,43 ****
          if (bb_list_size(source->children) != 1 || bb_list_size(source->parents) != 1)
                  return source;
          return first_basic_block(source->parents);
  }
  
! /*
!  * Copy the phi-node's phisrcs into to given array.
!  * Returns 0 if the the list contained the expected
!  * number of element, a positive number if there was
!  * more than expected and a negative one if less.
!  *
!  * Note: we can't reuse a function like linearize_ptr_list()
!  * because any VOIDs in the phi-list must be ignored here
!  * as in this context they mean 'entry has been removed'.
!  */
  static int get_phisources(struct instruction *sources[], int nbr, struct instruction *insn)
  {
          pseudo_t phi;
          int i = 0;
  
--- 64,82 ----
          if (bb_list_size(source->children) != 1 || bb_list_size(source->parents) != 1)
                  return source;
          return first_basic_block(source->parents);
  }
  
! ///
! // copy the phi-node's phisrcs into to given array
! // @return: 0 if the the list contained the expected
! //      number of element, a positive number if there was
! //      more than expected and a negative one if less.
! //
! // :note: we can't reuse a function like linearize_ptr_list()
! //      because any VOIDs in the phi-list must be ignored here
! //      as in this context they mean 'entry has been removed'.
  static int get_phisources(struct instruction *sources[], int nbr, struct instruction *insn)
  {
          pseudo_t phi;
          int i = 0;
  
*** 66,78 ****
          bb = insn->bb;
          if (get_phisources(array, 2, insn))
                  return 0;
          if (linearize_ptr_list((struct ptr_list *)bb->parents, (void **)parents, 3) != 2)
                  return 0;
!         p1 = array[0]->src1;
          bb1 = array[0]->bb;
!         p2 = array[1]->src1;
          bb2 = array[1]->bb;
  
          /* Only try the simple "direct parents" case */
          if ((bb1 != parents[0] || bb2 != parents[1]) &&
              (bb1 != parents[1] || bb2 != parents[0]))
--- 105,117 ----
          bb = insn->bb;
          if (get_phisources(array, 2, insn))
                  return 0;
          if (linearize_ptr_list((struct ptr_list *)bb->parents, (void **)parents, 3) != 2)
                  return 0;
!         p1 = array[0]->phi_src;
          bb1 = array[0]->bb;
!         p2 = array[1]->phi_src;
          bb2 = array[1]->bb;
  
          /* Only try the simple "direct parents" case */
          if ((bb1 != parents[0] || bb2 != parents[1]) &&
              (bb1 != parents[1] || bb2 != parents[0]))
*** 131,165 ****
          insert_select(source, br, insn, p1, p2);
          kill_instruction(insn);
          return REPEAT_CSE;
  }
  
! static int clean_up_phi(struct instruction *insn)
  {
          pseudo_t phi;
-         struct instruction *last;
-         int same;
  
!         last = NULL;
!         same = 1;
          FOR_EACH_PTR(insn->phi_list, phi) {
                  struct instruction *def;
                  if (phi == VOID)
                          continue;
                  def = phi->def;
!                 if (def->src1 == VOID || !def->bb)
                          continue;
!                 if (last) {
!                         if (last->src1 != def->src1)
!                                 same = 0;
                          continue;
                  }
!                 last = def;
          } END_FOR_EACH_PTR(phi);
  
!         if (same) {
!                 pseudo_t pseudo = last ? last->src1 : VOID;
                  convert_instruction_target(insn, pseudo);
                  kill_instruction(insn);
                  return REPEAT_CSE;
          }
  
--- 170,237 ----
          insert_select(source, br, insn, p1, p2);
          kill_instruction(insn);
          return REPEAT_CSE;
  }
  
! ///
! // detect trivial phi-nodes
! // @insn: the phi-node
! // @pseudo: the candidate resulting pseudo (NULL when starting)
! // @return: the unique result if the phi-node is trivial, NULL otherwise
! //
! // A phi-node is trivial if it has a single possible result:
! //      * all operands are the same
! //      * the operands are themselves defined by a chain or cycle of phi-nodes
! //              and the set of all operands involved contains a single value
! //              not defined by these phi-nodes
! //
! // Since the result is unique, these phi-nodes can be removed.
! static pseudo_t trivial_phi(pseudo_t pseudo, struct instruction *insn, struct pseudo_list **list)
  {
+         pseudo_t target = insn->target;
          pseudo_t phi;
  
!         add_pseudo(list, target);
! 
          FOR_EACH_PTR(insn->phi_list, phi) {
                  struct instruction *def;
+                 pseudo_t src;
+ 
                  if (phi == VOID)
                          continue;
                  def = phi->def;
!                 if (!def->bb)
                          continue;
!                 src = def->phi_src; // bypass OP_PHISRC & get the real source
!                 if (src == VOID)
                          continue;
+                 if (!pseudo) {
+                         pseudo = src;
+                         continue;
                  }
!                 if (src == pseudo)
!                         continue;
!                 if (src == target)
!                         continue;
!                 if (DEF_OPCODE(def, src) == OP_PHI) {
!                         if (pseudo_in_list(*list, src))
!                                 continue;
!                         if ((pseudo = trivial_phi(pseudo, def, list)))
!                                 continue;
!                 }
!                 return NULL;
          } END_FOR_EACH_PTR(phi);
  
!         return pseudo ? pseudo : VOID;
! }
! 
! static int clean_up_phi(struct instruction *insn)
! {
!         struct pseudo_list *list = NULL;
!         pseudo_t pseudo;
! 
!         if ((pseudo = trivial_phi(NULL, insn, &list))) {
                  convert_instruction_target(insn, pseudo);
                  kill_instruction(insn);
                  return REPEAT_CSE;
          }
  
*** 177,209 ****
                                  goto out;
                  }
          } END_FOR_EACH_PTR(pu);
          assert(count <= 0);
  out:
!         if (ptr_list_size((struct ptr_list *) *list) == 0)
                  *list = NULL;
          return count;
  }
  
! static inline void remove_usage(pseudo_t p, pseudo_t *usep)
  {
          if (has_use_list(p)) {
                  delete_pseudo_user_list_entry(&p->users, usep, 1);
!                 if (!p->users)
                          kill_instruction(p->def);
          }
  }
  
  void kill_use(pseudo_t *usep)
  {
          if (usep) {
                  pseudo_t p = *usep;
                  *usep = VOID;
!                 remove_usage(p, usep);
          }
  }
  
  static void kill_use_list(struct pseudo_list *list)
  {
          pseudo_t p;
          FOR_EACH_PTR(list, p) {
                  if (p == VOID)
--- 249,296 ----
                                  goto out;
                  }
          } END_FOR_EACH_PTR(pu);
          assert(count <= 0);
  out:
!         if (pseudo_user_list_empty(*list))
                  *list = NULL;
          return count;
  }
  
! static inline void rem_usage(pseudo_t p, pseudo_t *usep, int kill)
  {
          if (has_use_list(p)) {
+                 if (p->type == PSEUDO_SYM)
+                         repeat_phase |= REPEAT_SYMBOL_CLEANUP;
                  delete_pseudo_user_list_entry(&p->users, usep, 1);
!                 if (kill && !p->users)
                          kill_instruction(p->def);
          }
  }
  
+ static inline void remove_usage(pseudo_t p, pseudo_t *usep)
+ {
+         rem_usage(p, usep, 1);
+ }
+ 
  void kill_use(pseudo_t *usep)
  {
          if (usep) {
                  pseudo_t p = *usep;
                  *usep = VOID;
!                 rem_usage(p, usep, 1);
          }
  }
  
+ // Like kill_use() but do not (recursively) kill dead instructions
+ void remove_use(pseudo_t *usep)
+ {
+         pseudo_t p = *usep;
+         *usep = VOID;
+         rem_usage(p, usep, 0);
+ }
+ 
  static void kill_use_list(struct pseudo_list *list)
  {
          pseudo_t p;
          FOR_EACH_PTR(list, p) {
                  if (p == VOID)
*** 210,232 ****
                          continue;
                  kill_use(THIS_ADDRESS(p));
          } END_FOR_EACH_PTR(p);
  }
  
! /*
!  * kill an instruction:
!  * - remove it from its bb
!  * - remove the usage of all its operands
!  * If forse is zero, the normal case, the function only for
!  * instructions free of (possible) side-effects. Otherwise
!  * the function does that unconditionally (must only be used
!  * for unreachable instructions.
!  */
! void kill_insn(struct instruction *insn, int force)
  {
          if (!insn || !insn->bb)
!                 return;
  
          switch (insn->opcode) {
          case OP_SEL:
          case OP_RANGE:
                  kill_use(&insn->src3);
--- 297,320 ----
                          continue;
                  kill_use(THIS_ADDRESS(p));
          } END_FOR_EACH_PTR(p);
  }
  
! ///
! // kill an instruction
! // @insn: the instruction to be killed
! // @force: if unset, the normal case, the instruction is not killed
! //      if not free of possible side-effect; if set the instruction
! //      is unconditionally killed.
! //
! // The killed instruction is removed from its BB and the usage
! // of all its operands are removed. The instruction is also
! // marked as killed by setting its ->bb to NULL.
! int kill_insn(struct instruction *insn, int force)
  {
          if (!insn || !insn->bb)
!                 return 0;
  
          switch (insn->opcode) {
          case OP_SEL:
          case OP_RANGE:
                  kill_use(&insn->src3);
*** 234,249 ****
  
          case OP_BINARY ... OP_BINCMP_END:
                  kill_use(&insn->src2);
                  /* fall through */
  
!         case OP_CAST:
!         case OP_SCAST:
!         case OP_FPCAST:
!         case OP_PTRCAST:
          case OP_SETVAL:
-         case OP_NOT: case OP_NEG:
          case OP_SLICE:
                  kill_use(&insn->src1);
                  break;
  
          case OP_PHI:
--- 322,333 ----
  
          case OP_BINARY ... OP_BINCMP_END:
                  kill_use(&insn->src2);
                  /* fall through */
  
!         case OP_UNOP ... OP_UNOP_END:
          case OP_SETVAL:
          case OP_SLICE:
                  kill_use(&insn->src1);
                  break;
  
          case OP_PHI:
*** 252,332 ****
          case OP_PHISOURCE:
                  kill_use(&insn->phi_src);
                  break;
  
          case OP_SYMADDR:
                  repeat_phase |= REPEAT_SYMBOL_CLEANUP;
                  break;
  
          case OP_CBR:
          case OP_COMPUTEDGOTO:
                  kill_use(&insn->cond);
                  break;
  
          case OP_CALL:
                  if (!force) {
                          /* a "pure" function can be killed too */
                          if (!(insn->func->type == PSEUDO_SYM))
!                                 return;
                          if (!(insn->func->sym->ctype.modifiers & MOD_PURE))
!                                 return;
                  }
                  kill_use_list(insn->arguments);
                  if (insn->func->type == PSEUDO_REG)
                          kill_use(&insn->func);
                  break;
  
          case OP_LOAD:
!                 if (!force && insn->type->ctype.modifiers & MOD_VOLATILE)
!                         return;
                  kill_use(&insn->src);
                  break;
  
          case OP_STORE:
                  if (!force)
!                         return;
                  kill_use(&insn->src);
                  kill_use(&insn->target);
                  break;
  
          case OP_ENTRY:
                  /* ignore */
!                 return;
  
          case OP_BR:
          default:
                  break;
          }
  
          insn->bb = NULL;
!         repeat_phase |= REPEAT_CSE;
!         return;
  }
  
! /*
!  * Kill trivially dead instructions
!  */
  static int dead_insn(struct instruction *insn, pseudo_t *src1, pseudo_t *src2, pseudo_t *src3)
  {
!         struct pseudo_user *pu;
!         FOR_EACH_PTR(insn->target->users, pu) {
!                 if (*pu->userp != VOID)
                          return 0;
-         } END_FOR_EACH_PTR(pu);
  
          insn->bb = NULL;
          kill_use(src1);
          kill_use(src2);
          kill_use(src3);
          return REPEAT_CSE;
  }
  
  static inline int constant(pseudo_t pseudo)
  {
          return pseudo->type == PSEUDO_VAL;
  }
  
  static int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo)
  {
          convert_instruction_target(insn, pseudo);
  
          switch (insn->opcode) {
--- 336,450 ----
          case OP_PHISOURCE:
                  kill_use(&insn->phi_src);
                  break;
  
          case OP_SYMADDR:
+                 kill_use(&insn->src);
                  repeat_phase |= REPEAT_SYMBOL_CLEANUP;
                  break;
  
          case OP_CBR:
+         case OP_SWITCH:
          case OP_COMPUTEDGOTO:
                  kill_use(&insn->cond);
                  break;
  
          case OP_CALL:
                  if (!force) {
                          /* a "pure" function can be killed too */
                          if (!(insn->func->type == PSEUDO_SYM))
!                                 return 0;
                          if (!(insn->func->sym->ctype.modifiers & MOD_PURE))
!                                 return 0;
                  }
                  kill_use_list(insn->arguments);
                  if (insn->func->type == PSEUDO_REG)
                          kill_use(&insn->func);
                  break;
  
          case OP_LOAD:
!                 if (!force && insn->is_volatile)
!                         return 0;
                  kill_use(&insn->src);
                  break;
  
          case OP_STORE:
                  if (!force)
!                         return 0;
                  kill_use(&insn->src);
                  kill_use(&insn->target);
                  break;
  
          case OP_ENTRY:
                  /* ignore */
!                 return 0;
  
          case OP_BR:
+         case OP_SETFVAL:
          default:
                  break;
          }
  
          insn->bb = NULL;
!         return repeat_phase |= REPEAT_CSE;
  }
  
! ///
! // kill trivially dead instructions
  static int dead_insn(struct instruction *insn, pseudo_t *src1, pseudo_t *src2, pseudo_t *src3)
  {
!         if (has_users(insn->target))
                  return 0;
  
          insn->bb = NULL;
          kill_use(src1);
          kill_use(src2);
          kill_use(src3);
          return REPEAT_CSE;
  }
  
+ static inline bool has_target(struct instruction *insn)
+ {
+         return opcode_table[insn->opcode].flags & OPF_TARGET;
+ }
+ 
+ void remove_dead_insns(struct entrypoint *ep)
+ {
+         struct basic_block *bb;
+ 
+         FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
+                 struct instruction *insn;
+                 FOR_EACH_PTR_REVERSE(bb->insns, insn) {
+                         if (!insn->bb)
+                                 continue;
+                         if (!has_target(insn))
+                                 continue;
+                         if (!has_users(insn->target))
+                                 kill_instruction(insn);
+                 } END_FOR_EACH_PTR_REVERSE(insn);
+         } END_FOR_EACH_PTR_REVERSE(bb);
+ }
+ 
  static inline int constant(pseudo_t pseudo)
  {
          return pseudo->type == PSEUDO_VAL;
  }
  
+ ///
+ // replace the operand of an instruction
+ // @insn: the instruction
+ // @pp: the address of the instruction's operand
+ // @new: the new value for the operand
+ // @return: REPEAT_CSE.
+ static inline int replace_pseudo(struct instruction *insn, pseudo_t *pp, pseudo_t new)
+ {
+         pseudo_t old = *pp;
+         use_pseudo(insn, new, pp);
+         remove_usage(old, pp);
+         return REPEAT_CSE;
+ }
+ 
  static int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo)
  {
          convert_instruction_target(insn, pseudo);
  
          switch (insn->opcode) {
*** 333,349 ****
          case OP_SEL:
          case OP_RANGE:
                  kill_use(&insn->src3);
          case OP_BINARY ... OP_BINCMP_END:
                  kill_use(&insn->src2);
!         case OP_NOT:
!         case OP_NEG:
          case OP_SYMADDR:
-         case OP_CAST:
-         case OP_SCAST:
-         case OP_FPCAST:
-         case OP_PTRCAST:
                  kill_use(&insn->src1);
                  break;
  
          default:
                  assert(0);
--- 451,462 ----
          case OP_SEL:
          case OP_RANGE:
                  kill_use(&insn->src3);
          case OP_BINARY ... OP_BINCMP_END:
                  kill_use(&insn->src2);
!         case OP_UNOP ... OP_UNOP_END:
          case OP_SYMADDR:
                  kill_use(&insn->src1);
                  break;
  
          default:
                  assert(0);
*** 350,361 ****
          }
          insn->bb = NULL;
          return REPEAT_CSE;
  }
  
! unsigned int value_size(long long value)
  {
          value >>= 8;
          if (!value)
                  return 8;
          value >>= 8;
          if (!value)
--- 463,481 ----
          }
          insn->bb = NULL;
          return REPEAT_CSE;
  }
  
! static inline int def_opcode(pseudo_t p)
  {
+         if (p->type != PSEUDO_REG)
+                 return OP_BADOP;
+         return p->def->opcode;
+ }
+ 
+ static unsigned int value_size(long long value)
+ {
          value >>= 8;
          if (!value)
                  return 8;
          value >>= 8;
          if (!value)
*** 364,386 ****
          if (!value)
                  return 32;
          return 64;
  }
  
! /*
!  * Try to determine the maximum size of bits in a pseudo.
!  *
!  * Right now this only follow casts and constant values, but we
!  * could look at things like logical 'and' instructions etc.
!  */
  static unsigned int operand_size(struct instruction *insn, pseudo_t pseudo)
  {
          unsigned int size = insn->size;
  
          if (pseudo->type == PSEUDO_REG) {
                  struct instruction *src = pseudo->def;
!                 if (src && src->opcode == OP_CAST && src->orig_type) {
                          unsigned int orig_size = src->orig_type->bit_size;
                          if (orig_size < size)
                                  size = orig_size;
                  }
          }
--- 484,505 ----
          if (!value)
                  return 32;
          return 64;
  }
  
! ///
! // try to determine the maximum size of bits in a pseudo
! //
! // Right now this only follow casts and constant values, but we
! // could look at things like AND instructions, etc.
  static unsigned int operand_size(struct instruction *insn, pseudo_t pseudo)
  {
          unsigned int size = insn->size;
  
          if (pseudo->type == PSEUDO_REG) {
                  struct instruction *src = pseudo->def;
!                 if (src && src->opcode == OP_ZEXT && src->orig_type) {
                          unsigned int orig_size = src->orig_type->bit_size;
                          if (orig_size < size)
                                  size = orig_size;
                  }
          }
*** 390,410 ****
                          size = orig_size;
          }
          return size;
  }
  
! static int simplify_asr(struct instruction *insn, pseudo_t pseudo, long long value)
  {
!         unsigned int size = operand_size(insn, pseudo);
  
!         if (value >= size) {
!                 warning(insn->pos, "right shift by bigger than source value");
!                 return replace_with_pseudo(insn, value_pseudo(insn->type, 0));
          }
          if (!value)
                  return replace_with_pseudo(insn, pseudo);
          return 0;
  }
  
  static int simplify_mul_div(struct instruction *insn, long long value)
  {
          unsigned long long sbit = 1ULL << (insn->size - 1);
--- 509,929 ----
                          size = orig_size;
          }
          return size;
  }
  
! static pseudo_t eval_insn(struct instruction *insn)
  {
!         /* FIXME! Verify signs and sizes!! */
!         unsigned int size = insn->size;
!         long long left = insn->src1->value;
!         long long right = insn->src2->value;
!         unsigned long long ul, ur;
!         long long res, mask, bits;
  
!         mask = 1ULL << (size-1);
!         bits = mask | (mask-1);
! 
!         if (left & mask)
!                 left |= ~bits;
!         if (right & mask)
!                 right |= ~bits;
!         ul = left & bits;
!         ur = right & bits;
! 
!         switch (insn->opcode) {
!         case OP_ADD:
!                 res = left + right;
!                 break;
!         case OP_SUB:
!                 res = left - right;
!                 break;
!         case OP_MUL:
!                 res = ul * ur;
!                 break;
!         case OP_DIVU:
!                 if (!ur)
!                         goto undef;
!                 res = ul / ur;
!                 break;
!         case OP_DIVS:
!                 if (!right)
!                         goto undef;
!                 if (left == mask && right == -1)
!                         goto undef;
!                 res = left / right;
!                 break;
!         case OP_MODU:
!                 if (!ur)
!                         goto undef;
!                 res = ul % ur;
!                 break;
!         case OP_MODS:
!                 if (!right)
!                         goto undef;
!                 if (left == mask && right == -1)
!                         goto undef;
!                 res = left % right;
!                 break;
!         case OP_SHL:
!                 if (ur >= size)
!                         goto undef;
!                 res = left << right;
!                 break;
!         case OP_LSR:
!                 if (ur >= size)
!                         goto undef;
!                 res = ul >> ur;
!                 break;
!         case OP_ASR:
!                 if (ur >= size)
!                         goto undef;
!                 res = left >> right;
!                 break;
!        /* Logical */
!         case OP_AND:
!                 res = left & right;
!                 break;
!         case OP_OR:
!                 res = left | right;
!                 break;
!         case OP_XOR:
!                 res = left ^ right;
!                 break;
! 
!         /* Binary comparison */
!         case OP_SET_EQ:
!                 res = left == right;
!                 break;
!         case OP_SET_NE:
!                 res = left != right;
!                 break;
!         case OP_SET_LE:
!                 res = left <= right;
!                 break;
!         case OP_SET_GE:
!                 res = left >= right;
!                 break;
!         case OP_SET_LT:
!                 res = left < right;
!                 break;
!         case OP_SET_GT:
!                 res = left > right;
!                 break;
!         case OP_SET_B:
!                 res = ul < ur;
!                 break;
!         case OP_SET_A:
!                 res = ul > ur;
!                 break;
!         case OP_SET_BE:
!                 res = ul <= ur;
!                 break;
!         case OP_SET_AE:
!                 res = ul >= ur;
!                 break;
!         default:
!                 return NULL;
          }
+         res &= bits;
+ 
+         return value_pseudo(res);
+ 
+ undef:
+         return NULL;
+ }
+ 
+ ///
+ // Simplifications
+ // ^^^^^^^^^^^^^^^
+ 
+ ///
+ // try to simplify MASK(OR(AND(x, M'), b), M)
+ // @insn: the masking instruction
+ // @mask: the associated mask (M)
+ // @ora: one of the OR's operands, guaranteed to be PSEUDO_REG
+ // @orb: the other OR's operand
+ // @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise.
+ static int simplify_mask_or_and(struct instruction *insn, unsigned long long mask,
+         pseudo_t ora, pseudo_t orb)
+ {
+         unsigned long long omask, nmask;
+         struct instruction *and = ora->def;
+         pseudo_t src2 = and->src2;
+ 
+         if (and->opcode != OP_AND)
+                 return 0;
+         if (!constant(src2))
+                 return 0;
+         omask = src2->value;
+         nmask = omask & mask;
+         if (nmask == 0) {
+                 // if (M' & M) == 0: ((a & M') | b) -> b
+                 return replace_pseudo(insn, &insn->src1, orb);
+         }
+         if (multi_users(insn->src1))
+                 return 0;       // can't modify anything inside the OR
+         if (nmask == mask) {
+                 struct instruction *or = insn->src1->def;
+                 pseudo_t *arg = (ora == or->src1) ? &or->src1 : &or->src2;
+                 // if (M' & M) == M: ((a & M') | b) -> (a | b)
+                 return replace_pseudo(or, arg, and->src1);
+         }
+         if (nmask != omask && !multi_users(ora)) {
+                 // if (M' & M) != M': AND(a, M') -> AND(a, (M' & M))
+                 and->src2 = value_pseudo(nmask);
+                 return REPEAT_CSE;
+         }
+         return 0;
+ }
+ 
+ ///
+ // try to simplify MASK(OR(a, b), M)
+ // @insn: the masking instruction
+ // @mask: the associated mask (M)
+ // @or: the OR instruction
+ // @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise.
+ static int simplify_mask_or(struct instruction *insn, unsigned long long mask, struct instruction *or)
+ {
+         pseudo_t src1 = or->src1;
+         pseudo_t src2 = or->src2;
+         int rc;
+ 
+         if (src1->type == PSEUDO_REG) {
+                 if ((rc = simplify_mask_or_and(insn, mask, src1, src2)))
+                         return rc;
+         }
+         if (src2->type == PSEUDO_REG) {
+                 if ((rc = simplify_mask_or_and(insn, mask, src2, src1)))
+                         return rc;
+         } else if (src2->type == PSEUDO_VAL) {
+                 unsigned long long oval = src2->value;
+                 unsigned long long nval = oval & mask;
+                 // Try to simplify:
+                 //      MASK(OR(x, C), M)
+                 if (nval == 0) {
+                         // if (C & M) == 0: OR(x, C) -> x
+                         return replace_pseudo(insn, &insn->src1, src1);
+                 }
+                 if (nval == mask) {
+                         // if (C & M) == M: OR(x, C) -> M
+                         return replace_pseudo(insn, &insn->src1, value_pseudo(mask));
+                 }
+                 if (nval != oval && !multi_users(or->target)) {
+                         // if (C & M) != C: OR(x, C) -> OR(x, (C & M))
+                         return replace_pseudo(or, &or->src2, value_pseudo(nval));
+                 }
+         }
+         return 0;
+ }
+ 
+ ///
+ // try to simplify MASK(SHIFT(OR(a, b), S), M)
+ // @sh: the shift instruction
+ // @or: the OR instruction
+ // @mask: the mask associated to MASK (M):
+ // @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise.
+ static int simplify_mask_shift_or(struct instruction *sh, struct instruction *or, unsigned long long mask)
+ {
+         unsigned long long smask = bits_mask(sh->size);
+         int shift = sh->src2->value;
+ 
+         if (sh->opcode == OP_LSR)
+                 mask <<= shift;
+         else
+                 mask >>= shift;
+         return simplify_mask_or(sh, smask & mask, or);
+ }
+ 
+ static int simplify_mask_shift(struct instruction *sh, unsigned long long mask)
+ {
+         struct instruction *inner;
+ 
+         if (!constant(sh->src2) || sh->tainted)
+                 return 0;
+         switch (DEF_OPCODE(inner, sh->src1)) {
+         case OP_OR:
+                 if (!multi_users(sh->target))
+                         return simplify_mask_shift_or(sh, inner, mask);
+                 break;
+         }
+         return 0;
+ }
+ 
+ static long long check_shift_count(struct instruction *insn, unsigned long long uval)
+ {
+         unsigned int size = insn->size;
+         long long sval = uval;
+ 
+         if (uval < size)
+                 return uval;
+ 
+         sval = sign_extend_safe(sval, size);
+         sval = sign_extend_safe(sval, bits_in_int);
+         if (sval < 0)
+                 insn->src2 = value_pseudo(sval);
+         if (insn->tainted)
+                 return sval;
+ 
+         if (sval < 0 && Wshift_count_negative)
+                 warning(insn->pos, "shift count is negative (%lld)", sval);
+         if (sval > 0 && Wshift_count_overflow) {
+                 struct symbol *ctype = insn->type;
+                 const char *tname;
+                 if (ctype->type == SYM_NODE)
+                         ctype = ctype->ctype.base_type;
+                 tname = show_typename(ctype);
+                 warning(insn->pos, "shift too big (%llu) for type %s", sval, tname);
+         }
+         insn->tainted = 1;
+         return sval;
+ }
+ 
+ static int simplify_shift(struct instruction *insn, pseudo_t pseudo, long long value)
+ {
+         struct instruction *def;
+         unsigned long long mask, omask, nmask;
+         unsigned long long nval;
+         unsigned int size;
+         pseudo_t src2;
+ 
          if (!value)
                  return replace_with_pseudo(insn, pseudo);
+         value = check_shift_count(insn, value);
+         if (value < 0)
                  return 0;
+ 
+         size = insn->size;
+         switch (insn->opcode) {
+         case OP_ASR:
+                 if (value >= size)
+                         return 0;
+                 if (pseudo->type != PSEUDO_REG)
+                         break;
+                 def = pseudo->def;
+                 switch (def->opcode) {
+                 case OP_LSR:
+                 case OP_ASR:
+                         if (def == insn)        // cyclic DAG!
+                                 break;
+                         src2 = def->src2;
+                         if (src2->type != PSEUDO_VAL)
+                                 break;
+                         nval = src2->value;
+                         if (nval > insn->size || nval == 0)
+                                 break;
+                         value += nval;
+                         if (def->opcode == OP_LSR)
+                                 insn->opcode = OP_LSR;
+                         else if (value >= size)
+                                 value = size - 1;
+                         goto new_value;
+ 
+                 case OP_ZEXT:
+                         // transform:
+                         //      zext.N  %t <- (O) %a
+                         //      asr.N   %r <- %t, C
+                         // into
+                         //      zext.N  %t <- (O) %a
+                         //      lsr.N   %r <- %t, C
+                         insn->opcode = OP_LSR;
+                         return REPEAT_CSE;
+                 }
+                 break;
+         case OP_LSR:
+                 size = operand_size(insn, pseudo);
+                 if (value >= size)
+                         goto zero;
+                 switch(DEF_OPCODE(def, pseudo)) {
+                 case OP_AND:
+                         // replace (A & M) >> S
+                         // by      (A >> S) & (M >> S)
+                         if (!constant(def->src2))
+                                 break;
+                         mask = bits_mask(insn->size - value) << value;
+                         omask = def->src2->value;
+                         nmask = omask & mask;
+                         if (nmask == 0)
+                                 return replace_with_pseudo(insn, value_pseudo(0));
+                         if (nmask == mask)
+                                 return replace_pseudo(insn, &insn->src1, def->src1);
+                         if (nbr_users(pseudo) > 1)
+                                 break;
+                         def->opcode = OP_LSR;
+                         def->src2 = insn->src2;
+                         insn->opcode = OP_AND;
+                         insn->src2 = value_pseudo(omask >> value);
+                         return REPEAT_CSE;
+                 case OP_LSR:
+                         goto case_shift_shift;
+                 case OP_OR:
+                         mask = bits_mask(size);
+                         return simplify_mask_shift_or(insn, def, mask);
+                 case OP_SHL:
+                         // replace ((x << S) >> S)
+                         // by      (x & (-1 >> S))
+                         if (def->src2 != insn->src2)
+                                 break;
+                         mask = bits_mask(insn->size - value);
+                         goto replace_mask;
+                 }
+                 break;
+         case OP_SHL:
+                 if (value >= size)
+                         goto zero;
+                 switch(DEF_OPCODE(def, pseudo)) {
+                 case OP_AND:
+                         // simplify (A & M) << S
+                         if (!constant(def->src2))
+                                 break;
+                         mask = bits_mask(insn->size) >> value;
+                         omask = def->src2->value;
+                         nmask = omask & mask;
+                         if (nmask == 0)
+                                 return replace_with_pseudo(insn, value_pseudo(0));
+                         if (nmask == mask)
+                                 return replace_pseudo(insn, &insn->src1, def->src1);
+                         // do not simplify into ((A << S) & (M << S))
+                         break;
+                 case OP_LSR:
+                         // replace ((x >> S) << S)
+                         // by      (x & (-1 << S))
+                         if (def->src2 != insn->src2)
+                                 break;
+                         mask = bits_mask(insn->size - value) << value;
+                         goto replace_mask;
+                 case OP_OR:
+                         mask = bits_mask(size);
+                         return simplify_mask_shift_or(insn, def, mask);
+                 case OP_SHL:
+                 case_shift_shift:               // also for LSR - LSR
+                         if (def == insn)        // cyclic DAG!
+                                 break;
+                         src2 = def->src2;
+                         if (src2->type != PSEUDO_VAL)
+                                 break;
+                         nval = src2->value;
+                         if (nval > insn->size)
+                                 break;
+                         value += nval;
+                         goto new_value;
+                 }
+                 break;
+         }
+         return 0;
+ 
+ new_value:
+         if (value < size) {
+                 insn->src2 = value_pseudo(value);
+                 return replace_pseudo(insn, &insn->src1, pseudo->def->src1);
+         }
+ zero:
+         return replace_with_pseudo(insn, value_pseudo(0));
+ replace_mask:
+         insn->opcode = OP_AND;
+         insn->src2 = value_pseudo(mask);
+         return replace_pseudo(insn, &insn->src1, def->src1);
  }
  
  static int simplify_mul_div(struct instruction *insn, long long value)
  {
          unsigned long long sbit = 1ULL << (insn->size - 1);
*** 412,423 ****
  
          if (value == 1)
                  return replace_with_pseudo(insn, insn->src1);
  
          switch (insn->opcode) {
!         case OP_MULS:
!         case OP_MULU:
                  if (value == 0)
                          return replace_with_pseudo(insn, insn->src2);
          /* Fall through */
          case OP_DIVS:
                  if (!(value & sbit))    // positive
--- 931,941 ----
  
          if (value == 1)
                  return replace_with_pseudo(insn, insn->src1);
  
          switch (insn->opcode) {
!         case OP_MUL:
                  if (value == 0)
                          return replace_with_pseudo(insn, insn->src2);
          /* Fall through */
          case OP_DIVS:
                  if (!(value & sbit))    // positive
*** 431,547 ****
          }
  
          return 0;
  }
  
- static int compare_opcode(int opcode, int inverse)
- {
-         if (!inverse)
-                 return opcode;
- 
-         switch (opcode) {
-         case OP_SET_EQ: return OP_SET_NE;
-         case OP_SET_NE: return OP_SET_EQ;
- 
-         case OP_SET_LT: return OP_SET_GE;
-         case OP_SET_LE: return OP_SET_GT;
-         case OP_SET_GT: return OP_SET_LE;
-         case OP_SET_GE: return OP_SET_LT;
- 
-         case OP_SET_A:  return OP_SET_BE;
-         case OP_SET_AE: return OP_SET_B;
-         case OP_SET_B:  return OP_SET_AE;
-         case OP_SET_BE: return OP_SET_A;
- 
-         default:
-                 return opcode;
-         }
- }
- 
  static int simplify_seteq_setne(struct instruction *insn, long long value)
  {
          pseudo_t old = insn->src1;
!         struct instruction *def = old->def;
!         pseudo_t src1, src2;
          int inverse;
          int opcode;
  
          if (value != 0 && value != 1)
                  return 0;
  
          if (!def)
                  return 0;
  
          inverse = (insn->opcode == OP_SET_NE) == value;
          opcode = def->opcode;
          switch (opcode) {
!         case OP_BINCMP ... OP_BINCMP_END:
                  // Convert:
                  //      setcc.n %t <- %a, %b
                  //      setne.m %r <- %t, $0
                  // into:
                  //      setcc.n %t <- %a, %b
                  //      setcc.m %r <- %a, $b
                  // and similar for setne/eq ... 0/1
!                 src1 = def->src1;
!                 src2 = def->src2;
!                 insn->opcode = compare_opcode(opcode, inverse);
!                 use_pseudo(insn, src1, &insn->src1);
!                 use_pseudo(insn, src2, &insn->src2);
                  remove_usage(old, &insn->src1);
                  return REPEAT_CSE;
  
!         default:
                  return 0;
          }
  }
  
  static int simplify_constant_rightside(struct instruction *insn)
  {
          long long value = insn->src2->value;
  
          switch (insn->opcode) {
!         case OP_OR_BOOL:
!                 if (value == 1)
                          return replace_with_pseudo(insn, insn->src2);
                  goto case_neutral_zero;
  
          case OP_SUB:
                  if (value) {
                          insn->opcode = OP_ADD;
!                         insn->src2 = value_pseudo(insn->type, -value);
                          return REPEAT_CSE;
                  }
          /* Fall through */
          case OP_ADD:
-         case OP_OR: case OP_XOR:
-         case OP_SHL:
-         case OP_LSR:
          case_neutral_zero:
                  if (!value)
                          return replace_with_pseudo(insn, insn->src1);
                  return 0;
          case OP_ASR:
!                 return simplify_asr(insn, insn->src1, value);
  
          case OP_MODU: case OP_MODS:
                  if (value == 1)
!                         return replace_with_pseudo(insn, value_pseudo(insn->type, 0));
                  return 0;
  
          case OP_DIVU: case OP_DIVS:
!         case OP_MULU: case OP_MULS:
                  return simplify_mul_div(insn, value);
  
-         case OP_AND_BOOL:
-                 if (value == 1)
-                         return replace_with_pseudo(insn, insn->src1);
-         /* Fall through */
          case OP_AND:
                  if (!value)
                          return replace_with_pseudo(insn, insn->src2);
!                 return 0;
  
          case OP_SET_NE:
          case OP_SET_EQ:
                  return simplify_seteq_setne(insn, value);
          }
--- 949,1129 ----
          }
  
          return 0;
  }
  
  static int simplify_seteq_setne(struct instruction *insn, long long value)
  {
          pseudo_t old = insn->src1;
!         struct instruction *def;
!         unsigned osize;
          int inverse;
          int opcode;
  
          if (value != 0 && value != 1)
                  return 0;
  
+         if (old->type != PSEUDO_REG)
+                 return 0;
+         def = old->def;
          if (!def)
                  return 0;
  
          inverse = (insn->opcode == OP_SET_NE) == value;
+         if (!inverse && def->size == 1 && insn->size == 1) {
+                 // Replace:
+                 //      setne   %r <- %s, $0
+                 // or:
+                 //      seteq   %r <- %s, $1
+                 // by %s when boolean
+                 return replace_with_pseudo(insn, old);
+         }
          opcode = def->opcode;
          switch (opcode) {
!         case OP_AND:
!                 if (inverse)
!                         break;
!                 if (def->size != insn->size)
!                         break;
!                 if (def->src2->type != PSEUDO_VAL)
!                         break;
!                 if (def->src2->value != 1)
!                         break;
!                 return replace_with_pseudo(insn, old);
!         case OP_FPCMP ... OP_BINCMP_END:
                  // Convert:
                  //      setcc.n %t <- %a, %b
                  //      setne.m %r <- %t, $0
                  // into:
                  //      setcc.n %t <- %a, %b
                  //      setcc.m %r <- %a, $b
                  // and similar for setne/eq ... 0/1
!                 insn->opcode = inverse ? opcode_table[opcode].negate : opcode;
!                 use_pseudo(insn, def->src1, &insn->src1);
!                 use_pseudo(insn, def->src2, &insn->src2);
                  remove_usage(old, &insn->src1);
                  return REPEAT_CSE;
  
!         case OP_SEXT:
!                 if (value && (def->orig_type->bit_size == 1))
!                         break;
!                 /* Fall through */
!         case OP_ZEXT:
!                 // Convert:
!                 //      *ext.m  %s <- (1) %a
!                 //      setne.1 %r <- %s, $0
!                 // into:
!                 //      setne.1 %s <- %a, $0
!                 // and same for setne/eq ... 0/1
!                 return replace_pseudo(insn, &insn->src1, def->src);
!         case OP_TRUNC:
!                 if (multi_users(old))
!                         break;
!                 // convert
!                 //      trunc.n %s <- (o) %a
!                 //      setne.m %r <- %s, $0
!                 // into:
!                 //      and.o   %s <- %a, $((1 << o) - 1)
!                 //      setne.m %r <- %s, $0
!                 // and same for setne/eq ... 0/1
!                 osize = def->size;
!                 def->opcode = OP_AND;
!                 def->type = def->orig_type;
!                 def->size = def->type->bit_size;
!                 def->src2 = value_pseudo(bits_mask(osize));
!                 return REPEAT_CSE;
!         }
          return 0;
+ }
+ 
+ static int simplify_constant_mask(struct instruction *insn, unsigned long long mask)
+ {
+         pseudo_t old = insn->src1;
+         unsigned long long omask;
+         unsigned long long nmask;
+         struct instruction *def;
+         int osize;
+ 
+         switch (DEF_OPCODE(def, old)) {
+         case OP_FPCMP ... OP_BINCMP_END:
+                 osize = 1;
+                 goto oldsize;
+         case OP_OR:
+                 return simplify_mask_or(insn, mask, def);
+         case OP_LSR:
+         case OP_SHL:
+                 return simplify_mask_shift(def, mask);
+         case OP_ZEXT:
+                 osize = def->orig_type->bit_size;
+                 /* fall through */
+         oldsize:
+                 omask = (1ULL << osize) - 1;
+                 nmask = mask & omask;
+                 if (nmask == omask)
+                         // the AND mask is redundant
+                         return replace_with_pseudo(insn, old);
+                 if (nmask != mask) {
+                         // can use a smaller mask
+                         insn->src2 = value_pseudo(nmask);
+                         return REPEAT_CSE;
                  }
+                 break;
+         }
+         return 0;
  }
  
  static int simplify_constant_rightside(struct instruction *insn)
  {
          long long value = insn->src2->value;
+         long long sbit = 1ULL << (insn->size - 1);
+         long long bits = sbit | (sbit - 1);
  
          switch (insn->opcode) {
!         case OP_OR:
!                 if ((value & bits) == bits)
                          return replace_with_pseudo(insn, insn->src2);
                  goto case_neutral_zero;
  
+         case OP_XOR:
+                 if ((value & bits) == bits) {
+                         insn->opcode = OP_NOT;
+                         return REPEAT_CSE;
+                 }
+                 goto case_neutral_zero;
+ 
          case OP_SUB:
                  if (value) {
                          insn->opcode = OP_ADD;
!                         insn->src2 = value_pseudo(-value);
                          return REPEAT_CSE;
                  }
          /* Fall through */
          case OP_ADD:
          case_neutral_zero:
                  if (!value)
                          return replace_with_pseudo(insn, insn->src1);
                  return 0;
          case OP_ASR:
!         case OP_SHL:
!         case OP_LSR:
!                 return simplify_shift(insn, insn->src1, value);
  
          case OP_MODU: case OP_MODS:
                  if (value == 1)
!                         return replace_with_pseudo(insn, value_pseudo(0));
                  return 0;
  
          case OP_DIVU: case OP_DIVS:
!         case OP_MUL:
                  return simplify_mul_div(insn, value);
  
          case OP_AND:
                  if (!value)
                          return replace_with_pseudo(insn, insn->src2);
!                 if ((value & bits) == bits)
!                         return replace_with_pseudo(insn, insn->src1);
!                 return simplify_constant_mask(insn, value);
  
          case OP_SET_NE:
          case OP_SET_EQ:
                  return simplify_seteq_setne(insn, value);
          }
*** 559,694 ****
                  return 0;
  
          case OP_SHL:
          case OP_LSR: case OP_ASR:
          case OP_AND:
!         case OP_MULU: case OP_MULS:
                  if (!value)
                          return replace_with_pseudo(insn, insn->src1);
                  return 0;
          }
          return 0;
  }
  
  static int simplify_constant_binop(struct instruction *insn)
  {
!         /* FIXME! Verify signs and sizes!! */
!         long long left = insn->src1->value;
!         long long right = insn->src2->value;
!         unsigned long long ul, ur;
!         long long res, mask, bits;
  
!         mask = 1ULL << (insn->size-1);
!         bits = mask | (mask-1);
! 
!         if (left & mask)
!                 left |= ~bits;
!         if (right & mask)
!                 right |= ~bits;
!         ul = left & bits;
!         ur = right & bits;
! 
!         switch (insn->opcode) {
!         case OP_ADD:
!                 res = left + right;
!                 break;
!         case OP_SUB:
!                 res = left - right;
!                 break;
!         case OP_MULU:
!                 res = ul * ur;
!                 break;
!         case OP_MULS:
!                 res = left * right;
!                 break;
!         case OP_DIVU:
!                 if (!ur)
                          return 0;
-                 res = ul / ur;
-                 break;
-         case OP_DIVS:
-                 if (!right)
-                         return 0;
-                 if (left == mask && right == -1)
-                         return 0;
-                 res = left / right;
-                 break;
-         case OP_MODU:
-                 if (!ur)
-                         return 0;
-                 res = ul % ur;
-                 break;
-         case OP_MODS:
-                 if (!right)
-                         return 0;
-                 if (left == mask && right == -1)
-                         return 0;
-                 res = left % right;
-                 break;
-         case OP_SHL:
-                 res = left << right;
-                 break;
-         case OP_LSR:
-                 res = ul >> ur;
-                 break;
-         case OP_ASR:
-                 res = left >> right;
-                 break;
-        /* Logical */
-         case OP_AND:
-                 res = left & right;
-                 break;
-         case OP_OR:
-                 res = left | right;
-                 break;
-         case OP_XOR:
-                 res = left ^ right;
-                 break;
-         case OP_AND_BOOL:
-                 res = left && right;
-                 break;
-         case OP_OR_BOOL:
-                 res = left || right;
-                 break;
                                 
!         /* Binary comparison */
!         case OP_SET_EQ:
!                 res = left == right;
!                 break;
!         case OP_SET_NE:
!                 res = left != right;
!                 break;
!         case OP_SET_LE:
!                 res = left <= right;
!                 break;
!         case OP_SET_GE:
!                 res = left >= right;
!                 break;
!         case OP_SET_LT:
!                 res = left < right;
!                 break;
!         case OP_SET_GT:
!                 res = left > right;
!                 break;
!         case OP_SET_B:
!                 res = ul < ur;
!                 break;
!         case OP_SET_A:
!                 res = ul > ur;
!                 break;
!         case OP_SET_BE:
!                 res = ul <= ur;
!                 break;
!         case OP_SET_AE:
!                 res = ul >= ur;
!                 break;
!         default:
!                 return 0;
!         }
!         res &= bits;
! 
!         replace_with_pseudo(insn, value_pseudo(insn->type, res));
          return REPEAT_CSE;
  }
  
  static int simplify_binop_same_args(struct instruction *insn, pseudo_t arg)
  {
--- 1141,1166 ----
                  return 0;
  
          case OP_SHL:
          case OP_LSR: case OP_ASR:
          case OP_AND:
!         case OP_MUL:
                  if (!value)
                          return replace_with_pseudo(insn, insn->src1);
                  return 0;
          }
          return 0;
  }
  
  static int simplify_constant_binop(struct instruction *insn)
  {
!         pseudo_t res = eval_insn(insn);
  
!         if (!res)
                  return 0;
  
!         replace_with_pseudo(insn, res);
          return REPEAT_CSE;
  }
  
  static int simplify_binop_same_args(struct instruction *insn, pseudo_t arg)
  {
*** 698,727 ****
          case OP_SET_B:  case OP_SET_A:
                  if (Wtautological_compare)
                          warning(insn->pos, "self-comparison always evaluates to false");
          case OP_SUB:
          case OP_XOR:
!                 return replace_with_pseudo(insn, value_pseudo(insn->type, 0));
  
          case OP_SET_EQ:
          case OP_SET_LE: case OP_SET_GE:
          case OP_SET_BE: case OP_SET_AE:
                  if (Wtautological_compare)
                          warning(insn->pos, "self-comparison always evaluates to true");
!                 return replace_with_pseudo(insn, value_pseudo(insn->type, 1));
  
          case OP_AND:
          case OP_OR:
                  return replace_with_pseudo(insn, arg);
  
-         case OP_AND_BOOL:
-         case OP_OR_BOOL:
-                 remove_usage(arg, &insn->src2);
-                 insn->src2 = value_pseudo(insn->type, 0);
-                 insn->opcode = OP_SET_NE;
-                 return REPEAT_CSE;
- 
          default:
                  break;
          }
  
          return 0;
--- 1170,1192 ----
          case OP_SET_B:  case OP_SET_A:
                  if (Wtautological_compare)
                          warning(insn->pos, "self-comparison always evaluates to false");
          case OP_SUB:
          case OP_XOR:
!                 return replace_with_pseudo(insn, value_pseudo(0));
  
          case OP_SET_EQ:
          case OP_SET_LE: case OP_SET_GE:
          case OP_SET_BE: case OP_SET_AE:
                  if (Wtautological_compare)
                          warning(insn->pos, "self-comparison always evaluates to true");
!                 return replace_with_pseudo(insn, value_pseudo(1));
  
          case OP_AND:
          case OP_OR:
                  return replace_with_pseudo(insn, arg);
  
          default:
                  break;
          }
  
          return 0;
*** 763,779 ****
                  return p2->type == PSEUDO_SYM || p2->type == PSEUDO_VAL;
  
          return 1;
  }
  
! static int simplify_commutative_binop(struct instruction *insn)
  {
!         if (!canonical_order(insn->src1, insn->src2)) {
                  switch_pseudo(insn, &insn->src1, insn, &insn->src2);
!                 return REPEAT_CSE;
!         }
          return 0;
  }
  
  static inline int simple_pseudo(pseudo_t pseudo)
  {
          return pseudo->type == PSEUDO_VAL || pseudo->type == PSEUDO_SYM;
--- 1228,1254 ----
                  return p2->type == PSEUDO_SYM || p2->type == PSEUDO_VAL;
  
          return 1;
  }
  
! static int canonicalize_commutative(struct instruction *insn)
  {
!         if (canonical_order(insn->src1, insn->src2))
!                 return 0;
! 
          switch_pseudo(insn, &insn->src1, insn, &insn->src2);
!         return repeat_phase |= REPEAT_CSE;
! }
! 
! static int canonicalize_compare(struct instruction *insn)
! {
!         if (canonical_order(insn->src1, insn->src2))
                  return 0;
+ 
+         switch_pseudo(insn, &insn->src1, insn, &insn->src2);
+         insn->opcode = opcode_table[insn->opcode].swap;
+         return repeat_phase |= REPEAT_CSE;
  }
  
  static inline int simple_pseudo(pseudo_t pseudo)
  {
          return pseudo->type == PSEUDO_VAL || pseudo->type == PSEUDO_SYM;
*** 793,803 ****
                  return 0;
          if (def->opcode != insn->opcode)
                  return 0;
          if (!simple_pseudo(def->src2))
                  return 0;
!         if (ptr_list_size((struct ptr_list *)def->target->users) != 1)
                  return 0;
          switch_pseudo(def, &def->src1, insn, &insn->src2);
          return REPEAT_CSE;
  }
  
--- 1268,1278 ----
                  return 0;
          if (def->opcode != insn->opcode)
                  return 0;
          if (!simple_pseudo(def->src2))
                  return 0;
!         if (multi_users(def->target))
                  return 0;
          switch_pseudo(def, &def->src1, insn, &insn->src2);
          return REPEAT_CSE;
  }
  
*** 811,827 ****
                  res = ~val;
                  break;
          case OP_NEG:
                  res = -val;
                  break;
          default:
                  return 0;
          }
          mask = 1ULL << (insn->size-1);
          res &= mask | (mask-1);
          
!         replace_with_pseudo(insn, value_pseudo(insn->type, res));
          return REPEAT_CSE;
  }
  
  static int simplify_unop(struct instruction *insn)
  {
--- 1286,1311 ----
                  res = ~val;
                  break;
          case OP_NEG:
                  res = -val;
                  break;
+         case OP_SEXT:
+                 mask = 1ULL << (insn->orig_type->bit_size-1);
+                 if (val & mask)
+                         val |= ~(mask | (mask-1));
+                 /* fall through */
+         case OP_ZEXT:
+         case OP_TRUNC:
+                 res = val;
+                 break;
          default:
                  return 0;
          }
          mask = 1ULL << (insn->size-1);
          res &= mask | (mask-1);
          
!         replace_with_pseudo(insn, value_pseudo(res));
          return REPEAT_CSE;
  }
  
  static int simplify_unop(struct instruction *insn)
  {
*** 875,885 ****
          }
          return 0;
  
  offset:
          /* Invalid code */
!         if (new == orig) {
                  if (new == VOID)
                          return 0;
                  /*
                   * If some BB have been removed it is possible that this
                   * memop is in fact part of a dead BB. In this case
--- 1359,1369 ----
          }
          return 0;
  
  offset:
          /* Invalid code */
!         if (new == orig || new == addr) {
                  if (new == VOID)
                          return 0;
                  /*
                   * If some BB have been removed it is possible that this
                   * memop is in fact part of a dead BB. In this case
*** 887,909 ****
                   * If not part of a dead BB this will be redone after
                   * the BBs have been cleaned up.
                   */
                  if (repeat_phase & REPEAT_CFG_CLEANUP)
                          return 0;
-                 new = VOID;
                  warning(insn->pos, "crazy programmer");
          }
          insn->offset += off->value;
!         use_pseudo(insn, new, &insn->src);
!         remove_usage(addr, &insn->src);
          return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP;
  }
  
! /*
!  * We walk the whole chain of adds/subs backwards. That's not
!  * only more efficient, but it allows us to find loops.
!  */
  static int simplify_memop(struct instruction *insn)
  {
          int one, ret = 0;
          pseudo_t orig = insn->src;
  
--- 1371,1394 ----
                   * If not part of a dead BB this will be redone after
                   * the BBs have been cleaned up.
                   */
                  if (repeat_phase & REPEAT_CFG_CLEANUP)
                          return 0;
                  warning(insn->pos, "crazy programmer");
+                 replace_pseudo(insn, &insn->src, VOID);
+                 return 0;
          }
          insn->offset += off->value;
!         replace_pseudo(insn, &insn->src, new);
          return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP;
  }
  
! ///
! // simplify memops instructions
! //
! // :note: We walk the whole chain of adds/subs backwards.
! //      That's not only more efficient, but it allows us to find loops.
  static int simplify_memop(struct instruction *insn)
  {
          int one, ret = 0;
          pseudo_t orig = insn->src;
  
*** 912,988 ****
                  ret |= one;
          } while (one);
          return ret;
  }
  
- static long long get_cast_value(long long val, int old_size, int new_size, int sign)
- {
-         long long mask;
- 
-         if (sign && new_size > old_size) {
-                 mask = 1 << (old_size-1);
-                 if (val & mask)
-                         val |= ~(mask | (mask-1));
-         }
-         mask = 1 << (new_size-1);
-         return val & (mask | (mask-1));
- }
- 
  static int simplify_cast(struct instruction *insn)
  {
!         struct symbol *orig_type;
!         int orig_size, size;
          pseudo_t src;
  
          if (dead_insn(insn, &insn->src, NULL, NULL))
                  return REPEAT_CSE;
  
!         orig_type = insn->orig_type;
!         if (!orig_type)
!                 return 0;
  
!         /* Keep casts with pointer on either side (not only case of OP_PTRCAST) */
!         if (is_ptr_type(orig_type) || is_ptr_type(insn->type))
!                 return 0;
  
!         orig_size = orig_type->bit_size;
          size = insn->size;
!         src = insn->src;
  
!         /* A cast of a constant? */
!         if (constant(src)) {
!                 int sign = orig_type->ctype.modifiers & MOD_SIGNED;
!                 long long val = get_cast_value(src->value, orig_size, size, sign);
!                 src = value_pseudo(orig_type, val);
!                 goto simplify;
!         }
  
!         /* A cast of a "and" might be a no-op.. */
!         if (src->type == PSEUDO_REG) {
!                 struct instruction *def = src->def;
!                 if (def->opcode == OP_AND && def->size >= size) {
!                         pseudo_t val = def->src2;
!                         if (val->type == PSEUDO_VAL) {
!                                 unsigned long long value = val->value;
!                                 if (!(value >> (size-1)))
!                                         goto simplify;
                          }
                  }
          }
! 
!         if (size == orig_size) {
!                 int op = (orig_type->ctype.modifiers & MOD_SIGNED) ? OP_SCAST : OP_CAST;
!                 if (insn->opcode == op)
!                         goto simplify;
!                 if (insn->opcode == OP_FPCAST && is_float_type(orig_type))
!                         goto simplify;
          }
  
          return 0;
- 
- simplify:
-         return replace_with_pseudo(insn, src);
  }
  
  static int simplify_select(struct instruction *insn)
  {
          pseudo_t cond, src1, src2;
--- 1397,1548 ----
                  ret |= one;
          } while (one);
          return ret;
  }
  
  static int simplify_cast(struct instruction *insn)
  {
!         unsigned long long mask;
!         struct instruction *def;
          pseudo_t src;
+         pseudo_t val;
+         int osize;
+         int size;
  
          if (dead_insn(insn, &insn->src, NULL, NULL))
                  return REPEAT_CSE;
  
!         src = insn->src;
  
!         /* A cast of a constant? */
!         if (constant(src))
!                 return simplify_constant_unop(insn);
  
!         // can merge with the previous instruction?
          size = insn->size;
!         def = src->def;
!         switch (def_opcode(src)) {
!         case OP_AND:
!                 val = def->src2;
!                 if (val->type != PSEUDO_VAL)
!                         break;
!                 /* A cast of a AND might be a no-op.. */
!                 switch (insn->opcode) {
!                 case OP_TRUNC:
!                         if (multi_users(src))
!                                 break;
!                         def->opcode = OP_TRUNC;
!                         def->orig_type = def->type;
!                         def->type = insn->type;
!                         def->size = size;
  
!                         insn->opcode = OP_AND;
!                         mask = val->value;
!                         mask &= (1ULL << size) - 1;
!                         insn->src2 = value_pseudo(mask);
!                         return REPEAT_CSE;
  
!                 case OP_SEXT:
!                         if (val->value & (1 << (def->size - 1)))
!                                 break;
!                         // OK, sign bit is 0
!                 case OP_ZEXT:
!                         if (multi_users(src))
!                                 break;
!                         // transform:
!                         //      and.n   %b <- %a, M
!                         //      *ext.m  %c <- (n) %b
!                         // into:
!                         //      zext.m  %b <- %a
!                         //      and.m   %c <- %b, M
!                         // For ZEXT, the mask will always be small
!                         // enough. For SEXT, it can only be done if
!                         // the mask force the sign bit to 0.
!                         def->opcode = OP_ZEXT;
!                         def->orig_type = insn->orig_type;
!                         def->type = insn->type;
!                         def->size = insn->size;
!                         insn->opcode = OP_AND;
!                         insn->src2 = val;
!                         return REPEAT_CSE;
                  }
+                 break;
+         case OP_FPCMP ... OP_BINCMP_END:
+                 switch (insn->opcode) {
+                 case OP_SEXT:
+                         if (insn->size == 1)
+                                 break;
+                         /* fall through */
+                 case OP_ZEXT:
+                 case OP_TRUNC:
+                         // simplify:
+                         //      setcc.n %t <- %a, %b
+                         //      zext.m  %r <- (n) %t
+                         // into:
+                         //      setcc.m %r <- %a, %b
+                         // and same for s/zext/trunc/
+                         insn->opcode = def->opcode;
+                         use_pseudo(insn, def->src2, &insn->src2);
+                         return replace_pseudo(insn, &insn->src1, def->src1);
                  }
+                 break;
+         case OP_OR:
+                 switch (insn->opcode) {
+                 case OP_TRUNC:
+                         mask = bits_mask(insn->size);
+                         return simplify_mask_or(insn, mask, def);
                  }
!                 break;
!         case OP_LSR:
!         case OP_SHL:
!                 if (insn->opcode != OP_TRUNC)
!                         break;
!                 mask = bits_mask(insn->size);
!                 return simplify_mask_shift(def, mask);
!         case OP_TRUNC:
!                 switch (insn->opcode) {
!                 case OP_TRUNC:
!                         insn->orig_type = def->orig_type;
!                         return replace_pseudo(insn, &insn->src1, def->src);
!                 case OP_ZEXT:
!                         if (size != def->orig_type->bit_size)
!                                 break;
!                         insn->opcode = OP_AND;
!                         insn->src2 = value_pseudo((1ULL << def->size) - 1);
!                         return replace_pseudo(insn, &insn->src1, def->src);
                  }
+                 break;
+         case OP_ZEXT:
+                 switch (insn->opcode) {
+                 case OP_SEXT:
+                         insn->opcode = OP_ZEXT;
+                         /* fall through */
+                 case OP_ZEXT:
+                         insn->orig_type = def->orig_type;
+                         return replace_pseudo(insn, &insn->src, def->src);
+                 }
+                 /* fall through */
+         case OP_SEXT:
+                 switch (insn->opcode) {
+                 case OP_TRUNC:
+                         osize = def->orig_type->bit_size;
+                         if (size == osize)
+                                 return replace_with_pseudo(insn, def->src);
+                         if (size > osize)
+                                 insn->opcode = def->opcode;
+                         insn->orig_type = def->orig_type;
+                         return replace_pseudo(insn, &insn->src, def->src);
+                 }
+                 switch (insn->opcode) {
+                 case OP_SEXT:
+                         insn->orig_type = def->orig_type;
+                         return replace_pseudo(insn, &insn->src, def->src);
+                 }
+                 break;
+         }
  
          return 0;
  }
  
  static int simplify_select(struct instruction *insn)
  {
          pseudo_t cond, src1, src2;
*** 1017,1026 ****
--- 1577,1592 ----
                          /* insn->src1 is already cond */
                          insn->src2 = src1; /* Zero */
                          return REPEAT_CSE;
                  }
          }
+         if (cond == src2 && is_zero(src1)) {
+                 kill_use(&insn->src1);
+                 kill_use(&insn->src3);
+                 replace_with_pseudo(insn, value_pseudo(0));
+                 return REPEAT_CSE;
+         }
          return 0;
  }
  
  static int is_in_range(pseudo_t src, long long low, long long high)
  {
*** 1049,1070 ****
                  return REPEAT_CSE;
          }
          return 0;
  }
  
! /*
!  * Simplify "set_ne/eq $0 + br"
!  */
! static int simplify_cond_branch(struct instruction *br, pseudo_t cond, struct instruction *def, pseudo_t *pp)
  {
!         use_pseudo(br, *pp, &br->cond);
!         remove_usage(cond, &br->cond);
          if (def->opcode == OP_SET_EQ) {
!                 struct basic_block *true = br->bb_true;
!                 struct basic_block *false = br->bb_false;
!                 br->bb_false = true;
!                 br->bb_true = false;
          }
          return REPEAT_CSE;
  }
  
  static int simplify_branch(struct instruction *insn)
--- 1615,1633 ----
                  return REPEAT_CSE;
          }
          return 0;
  }
  
! ///
! // simplify SET_NE/EQ $0 + BR
! static int simplify_cond_branch(struct instruction *br, struct instruction *def, pseudo_t newcond)
  {
!         replace_pseudo(br, &br->cond, newcond);
          if (def->opcode == OP_SET_EQ) {
!                 struct basic_block *tmp = br->bb_true;
!                 br->bb_true = br->bb_false;
!                 br->bb_false = tmp;
          }
          return REPEAT_CSE;
  }
  
  static int simplify_branch(struct instruction *insn)
*** 1094,1106 ****
          if (cond->type == PSEUDO_REG) {
                  struct instruction *def = cond->def;
  
                  if (def->opcode == OP_SET_NE || def->opcode == OP_SET_EQ) {
                          if (constant(def->src1) && !def->src1->value)
!                                 return simplify_cond_branch(insn, cond, def, &def->src2);
                          if (constant(def->src2) && !def->src2->value)
!                                 return simplify_cond_branch(insn, cond, def, &def->src1);
                  }
                  if (def->opcode == OP_SEL) {
                          if (constant(def->src2) && constant(def->src3)) {
                                  long long val1 = def->src2->value;
                                  long long val2 = def->src3->value;
--- 1657,1669 ----
          if (cond->type == PSEUDO_REG) {
                  struct instruction *def = cond->def;
  
                  if (def->opcode == OP_SET_NE || def->opcode == OP_SET_EQ) {
                          if (constant(def->src1) && !def->src1->value)
!                                 return simplify_cond_branch(insn, def, def->src2);
                          if (constant(def->src2) && !def->src2->value)
!                                 return simplify_cond_branch(insn, def, def->src1);
                  }
                  if (def->opcode == OP_SEL) {
                          if (constant(def->src2) && constant(def->src3)) {
                                  long long val1 = def->src2->value;
                                  long long val2 = def->src3->value;
*** 1111,1139 ****
                                  if (val1 && val2) {
                                          insert_branch(insn->bb, insn, insn->bb_true);
                                          return REPEAT_CSE;
                                  }
                                  if (val2) {
!                                         struct basic_block *true = insn->bb_true;
!                                         struct basic_block *false = insn->bb_false;
!                                         insn->bb_false = true;
!                                         insn->bb_true = false;
                                  }
!                                 use_pseudo(insn, def->src1, &insn->cond);
!                                 remove_usage(cond, &insn->cond);
!                                 return REPEAT_CSE;
                          }
                  }
!                 if (def->opcode == OP_CAST || def->opcode == OP_SCAST) {
!                         int orig_size = def->orig_type ? def->orig_type->bit_size : 0;
!                         if (def->size > orig_size) {
!                                 use_pseudo(insn, def->src, &insn->cond);
!                                 remove_usage(cond, &insn->cond);
!                                 return REPEAT_CSE;
                          }
-                 }
-         }
          return 0;
  }
  
  static int simplify_switch(struct instruction *insn)
  {
--- 1674,1693 ----
                                  if (val1 && val2) {
                                          insert_branch(insn->bb, insn, insn->bb_true);
                                          return REPEAT_CSE;
                                  }
                                  if (val2) {
!                                         struct basic_block *tmp = insn->bb_true;
!                                         insn->bb_true = insn->bb_false;
!                                         insn->bb_false = tmp;
                                  }
!                                 return replace_pseudo(insn, &insn->cond, def->src1);
                          }
                  }
!                 if (def->opcode == OP_SEXT || def->opcode == OP_ZEXT)
!                         return replace_pseudo(insn, &insn->cond, def->src);
          }
          return 0;
  }
  
  static int simplify_switch(struct instruction *insn)
  {
*** 1163,1211 ****
  int simplify_instruction(struct instruction *insn)
  {
          if (!insn->bb)
                  return 0;
          switch (insn->opcode) {
!         case OP_ADD: case OP_MULS:
          case OP_AND: case OP_OR: case OP_XOR:
!         case OP_AND_BOOL: case OP_OR_BOOL:
                  if (simplify_binop(insn))
                          return REPEAT_CSE;
-                 if (simplify_commutative_binop(insn))
-                         return REPEAT_CSE;
                  return simplify_associative_binop(insn);
  
-         case OP_MULU:
          case OP_SET_EQ: case OP_SET_NE:
!                 if (simplify_binop(insn))
!                         return REPEAT_CSE;
!                 return simplify_commutative_binop(insn);
  
          case OP_SUB:
          case OP_DIVU: case OP_DIVS:
          case OP_MODU: case OP_MODS:
          case OP_SHL:
          case OP_LSR: case OP_ASR:
-         case OP_SET_LE: case OP_SET_GE:
-         case OP_SET_LT: case OP_SET_GT:
-         case OP_SET_B:  case OP_SET_A:
-         case OP_SET_BE: case OP_SET_AE:
                  return simplify_binop(insn);
  
!         case OP_NOT: case OP_NEG:
                  return simplify_unop(insn);
!         case OP_LOAD: case OP_STORE:
                  return simplify_memop(insn);
          case OP_SYMADDR:
!                 if (dead_insn(insn, NULL, NULL, NULL))
                          return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP;
!                 return replace_with_pseudo(insn, insn->symbol);
!         case OP_CAST:
!         case OP_SCAST:
!         case OP_FPCAST:
!         case OP_PTRCAST:
                  return simplify_cast(insn);
          case OP_PHI:
                  if (dead_insn(insn, NULL, NULL, NULL)) {
                          kill_use_list(insn->phi_list);
                          return REPEAT_CSE;
                  }
--- 1717,1784 ----
  int simplify_instruction(struct instruction *insn)
  {
          if (!insn->bb)
                  return 0;
          switch (insn->opcode) {
!         case OP_ADD: case OP_MUL:
          case OP_AND: case OP_OR: case OP_XOR:
!                 canonicalize_commutative(insn);
                  if (simplify_binop(insn))
                          return REPEAT_CSE;
                  return simplify_associative_binop(insn);
  
          case OP_SET_EQ: case OP_SET_NE:
!                 canonicalize_commutative(insn);
!                 return simplify_binop(insn);
  
+         case OP_SET_LE: case OP_SET_GE:
+         case OP_SET_LT: case OP_SET_GT:
+         case OP_SET_B:  case OP_SET_A:
+         case OP_SET_BE: case OP_SET_AE:
+                 canonicalize_compare(insn);
+                 /* fall through */
          case OP_SUB:
          case OP_DIVU: case OP_DIVS:
          case OP_MODU: case OP_MODS:
          case OP_SHL:
          case OP_LSR: case OP_ASR:
                  return simplify_binop(insn);
  
!         case OP_NOT: case OP_NEG: case OP_FNEG:
                  return simplify_unop(insn);
!         case OP_LOAD:
!                 if (!has_users(insn->target))
!                         return kill_instruction(insn);
!                 /* fall-through */
!         case OP_STORE:
                  return simplify_memop(insn);
          case OP_SYMADDR:
!                 if (dead_insn(insn, &insn->src, NULL, NULL))
                          return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP;
!                 return replace_with_pseudo(insn, insn->src);
!         case OP_SEXT: case OP_ZEXT:
!         case OP_TRUNC:
                  return simplify_cast(insn);
+         case OP_FCVTU: case OP_FCVTS:
+         case OP_UCVTF: case OP_SCVTF:
+         case OP_FCVTF:
+         case OP_PTRCAST:
+                 if (dead_insn(insn, &insn->src, NULL, NULL))
+                         return REPEAT_CSE;
+                 break;
+         case OP_UTPTR:
+         case OP_PTRTU:
+                 return replace_with_pseudo(insn, insn->src);
+         case OP_SLICE:
+                 if (dead_insn(insn, &insn->src, NULL, NULL))
+                         return REPEAT_CSE;
+                 break;
+         case OP_SETVAL:
+         case OP_SETFVAL:
+                 if (dead_insn(insn, NULL, NULL, NULL))
+                         return REPEAT_CSE;
+                 break;
          case OP_PHI:
                  if (dead_insn(insn, NULL, NULL, NULL)) {
                          kill_use_list(insn->phi_list);
                          return REPEAT_CSE;
                  }
*** 1220,1227 ****
--- 1793,1807 ----
                  return simplify_branch(insn);
          case OP_SWITCH:
                  return simplify_switch(insn);
          case OP_RANGE:
                  return simplify_range(insn);
+         case OP_FADD:
+         case OP_FSUB:
+         case OP_FMUL:
+         case OP_FDIV:
+                 if (dead_insn(insn, &insn->src1, &insn->src2, NULL))
+                         return REPEAT_CSE;
+                 break;
          }
          return 0;
  }