2005-07-21 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mono / mini / cfold.c
index 11213f07caca1a150079f8a6d5103993c49bfca5..c9daabdb7f6c5d4c781fafb13ba90adec44ec3c3 100644 (file)
@@ -9,8 +9,8 @@
  */
 #include "mini.h"
 
-static int
-is_power_of_two (guint32 val)
+int
+mono_is_power_of_two (guint32 val)
 {
        int i, j, k;
 
@@ -51,6 +51,12 @@ is_power_of_two (guint32 val)
                                inst->inst_i1 = tmp;    \
                        } \
                } \
+               if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_ADD) {    \
+                       if (inst->inst_i1->inst_c0 == 0) { \
+                               *inst = *(inst->inst_i0); \
+                               return; \
+                       } \
+               } \
                if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_MUL) {    \
                        int power2;     \
                        if (inst->inst_i1->inst_c0 == 1) {      \
@@ -60,13 +66,22 @@ is_power_of_two (guint32 val)
                                inst->opcode = CEE_NEG; \
                                return; \
                        }       \
-                       power2 = is_power_of_two (inst->inst_i1->inst_c0);      \
+                       power2 = mono_is_power_of_two (inst->inst_i1->inst_c0); \
                        if (power2 < 0) return; \
                        inst->opcode = CEE_SHL; \
                        inst->inst_i1->inst_c0 = power2;        \
                } \
                 return;
 
+#ifndef G_MININT32
+#define MYGINT32_MAX 2147483647
+#define G_MININT32 (-MYGINT32_MAX -1)
+#endif
+
+/* 
+ * We can't let this cause a division by zero exception since the division 
+ * might not be executed during runtime.
+ */
 #define FOLD_BINOPZ(name,op,cast)      \
        case name:      \
                if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_REM_UN && inst->inst_i1->inst_c0 == 2) {  \
@@ -75,13 +90,14 @@ is_power_of_two (guint32 val)
                        return; \
                }       \
                if (inst->inst_i1->opcode == OP_ICONST) {       \
-                       /* let the runtime throw the exception. */      \
                        if (!inst->inst_i1->inst_c0) return;    \
                        if (inst->inst_i0->opcode == OP_ICONST) {       \
+                if ((inst->inst_i0->inst_c0 == G_MININT32) && (inst->inst_i1->inst_c0 == -1)) \
+                    return; \
                                inst->inst_c0 = (cast)inst->inst_i0->inst_c0 op (cast)inst->inst_i1->inst_c0;   \
                                inst->opcode = OP_ICONST;       \
                        } else {        \
-                               int power2 = is_power_of_two (inst->inst_i1->inst_c0);  \
+                               int power2 = mono_is_power_of_two (inst->inst_i1->inst_c0);     \
                                if (power2 < 0) return; \
                                if (inst->opcode == CEE_REM_UN) {       \
                                        inst->opcode = CEE_AND; \
@@ -214,6 +230,13 @@ mono_constant_fold_inst (MonoInst *inst, gpointer data)
         *      *ovf* opcodes? I'ts slow and hard to do in C.
         *      switch can be replaced by a simple jump 
         */
+#if SIZEOF_VOID_P == 4
+       case CEE_CONV_I4:
+               if ((inst->inst_left->type == STACK_I4) || (inst->inst_left->type == STACK_PTR)) {
+                       *inst = *inst->inst_left;
+               }
+               break;  
+#endif
        default:
                return;
        }
@@ -231,3 +254,67 @@ mono_constant_fold (MonoCompile *cfg)
        }
 }
 
+/*
+ * If the arguments to the cond branch are constants, eval and
+ * return BRANCH_NOT_TAKEN for not taken, BRANCH_TAKEN for taken,
+ * BRANCH_UNDEF otherwise.
+ * If this code is changed to handle also non-const values, make sure
+ * side effects are handled in optimize_branches() in mini.c, by
+ * inserting pop instructions.
+ */
+int
+mono_eval_cond_branch (MonoInst *ins)
+{
+       MonoInst *left, *right;
+       /* FIXME: handle also 64 bit ints */
+       left = ins->inst_left->inst_left;
+       if (left->opcode != OP_ICONST && left->opcode != OP_PCONST)
+               return BRANCH_UNDEF;
+       right = ins->inst_left->inst_right;
+       if (right->opcode != OP_ICONST && right->opcode != OP_PCONST)
+               return BRANCH_UNDEF;
+       switch (ins->opcode) {
+       case CEE_BEQ:
+               if (left->inst_c0 == right->inst_c0)
+                       return BRANCH_TAKEN;
+               return BRANCH_NOT_TAKEN;
+       case CEE_BGE:
+               if (left->inst_c0 >= right->inst_c0)
+                       return BRANCH_TAKEN;
+               return BRANCH_NOT_TAKEN;
+       case CEE_BGT:
+               if (left->inst_c0 > right->inst_c0)
+                       return BRANCH_TAKEN;
+               return BRANCH_NOT_TAKEN;
+       case CEE_BLE:
+               if (left->inst_c0 <= right->inst_c0)
+                       return BRANCH_TAKEN;
+               return BRANCH_NOT_TAKEN;
+       case CEE_BLT:
+               if (left->inst_c0 < right->inst_c0)
+                       return BRANCH_TAKEN;
+               return BRANCH_NOT_TAKEN;
+       case CEE_BNE_UN:
+               if ((gsize)left->inst_c0 != (gsize)right->inst_c0)
+                       return BRANCH_TAKEN;
+               return BRANCH_NOT_TAKEN;
+       case CEE_BGE_UN:
+               if ((gsize)left->inst_c0 >= (gsize)right->inst_c0)
+                       return BRANCH_TAKEN;
+               return BRANCH_NOT_TAKEN;
+       case CEE_BGT_UN:
+               if ((gsize)left->inst_c0 > (gsize)right->inst_c0)
+                       return BRANCH_TAKEN;
+               return BRANCH_NOT_TAKEN;
+       case CEE_BLE_UN:
+               if ((gsize)left->inst_c0 <= (gsize)right->inst_c0)
+                       return BRANCH_TAKEN;
+               return BRANCH_NOT_TAKEN;
+       case CEE_BLT_UN:
+               if ((gsize)left->inst_c0 < (gsize)right->inst_c0)
+                       return BRANCH_TAKEN;
+               return BRANCH_NOT_TAKEN;
+       }
+       return BRANCH_UNDEF;
+}
+