drc: try to avoid full constant reload
authornotaz <notasas@gmail.com>
Thu, 20 Oct 2011 20:58:56 +0000 (23:58 +0300)
committernotaz <notasas@gmail.com>
Sun, 30 Oct 2011 21:48:08 +0000 (23:48 +0200)
derive from existing ones if possible

libpcsxcore/new_dynarec/assem_arm.c
libpcsxcore/new_dynarec/new_dynarec.c

index 6524d1f..0c16fc2 100644 (file)
@@ -2726,14 +2726,45 @@ emit_extjump_ds(int addr, int target)
 // put rt_val into rt, potentially making use of rs with value rs_val
 static void emit_movimm_from(u_int rs_val,int rs,u_int rt_val,int rt)
 {
-  u_int xor=rs_val^rt_val;
+  u_int armval;
+  int diff;
+  if(genimm(rt_val,&armval)) {
+    assem_debug("mov %s,#%d\n",regname[rt],rt_val);
+    output_w32(0xe3a00000|rd_rn_rm(rt,0,0)|armval);
+    return;
+  }
+  if(genimm(~rt_val,&armval)) {
+    assem_debug("mvn %s,#%d\n",regname[rt],rt_val);
+    output_w32(0xe3e00000|rd_rn_rm(rt,0,0)|armval);
+    return;
+  }
+  diff=rt_val-rs_val;
+  if(genimm(diff,&armval)) {
+    assem_debug("add %s,%s,#%d\n",regname[rt],regname[rs],diff);
+    output_w32(0xe2800000|rd_rn_rm(rt,rs,0)|armval);
+    return;
+  }else if(genimm(-diff,&armval)) {
+    assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],-diff);
+    output_w32(0xe2400000|rd_rn_rm(rt,rs,0)|armval);
+    return;
+  }
+  emit_movimm(rt_val,rt);
+}
+
+// return 1 if above function can do it's job cheaply
+static int is_similar_value(u_int v1,u_int v2)
+{
   u_int xs;
-  for(xs=xor;xs!=0&&(xs&3)==0;xs>>=2)
+  int diff;
+  if(v1==v2) return 1;
+  diff=v2-v1;
+  for(xs=diff;xs!=0&&(xs&3)==0;xs>>=2)
     ;
-  if(xs<0x100)
-    emit_xorimm(rs,xor,rt);
-  else
-    emit_movimm(rt_val,rt);
+  if(xs<0x100) return 1;
+  for(xs=-diff;xs!=0&&(xs&3)==0;xs>>=2)
+    ;
+  if(xs<0x100) return 1;
+  return 0;
 }
 
 // trashes r2
index e6036b2..353179a 100644 (file)
@@ -59,7 +59,8 @@ struct regstat
   uint64_t uu;
   u_int wasconst;
   u_int isconst;
-  u_int waswritten;              // regs that were used as store base before
+  u_int loadedconst;             // host regs that have constants loaded
+  u_int waswritten;              // MIPS regs that were used as store base before
   uint64_t constmap[HOST_REGS];
 };
 
@@ -4258,6 +4259,7 @@ void address_generation(int i,struct regstat *i_regs,signed char entry[])
                  (using_tlb&&((signed int)constmap[i][rs]+offset)>=(signed int)0xC0000000))
               #endif
               emit_movimm(constmap[i][rs]+offset,ra);
+              regs[i].loadedconst|=1<<ra;
             }
           } // else did it in the previous cycle
         } // else load_consts already did it
@@ -4318,6 +4320,7 @@ void address_generation(int i,struct regstat *i_regs,signed char entry[])
              (using_tlb&&((signed int)constmap[i+1][rs]+offset)>=(signed int)0xC0000000))
           #endif
           emit_movimm(constmap[i+1][rs]+offset,ra);
+          regs[i+1].loadedconst|=1<<ra;
         }
       }
       else if(rs1[i+1]==0) {
@@ -4386,22 +4389,51 @@ int get_final_value(int hr, int i, int *value)
 // Load registers with known constants
 void load_consts(signed char pre[],signed char regmap[],int is32,int i)
 {
-  int hr;
+  int hr,hr2;
+  // propagate loaded constant flags
+  if(i==0||bt[i])
+    regs[i].loadedconst=0;
+  else {
+    for(hr=0;hr<HOST_REGS;hr++) {
+      if(hr!=EXCLUDE_REG&&regmap[hr]>=0&&((regs[i-1].isconst>>hr)&1)&&pre[hr]==regmap[hr]
+         &&regmap[hr]==regs[i-1].regmap[hr]&&((regs[i-1].loadedconst>>hr)&1))
+      {
+        regs[i].loadedconst|=1<<hr;
+      }
+    }
+  }
   // Load 32-bit regs
   for(hr=0;hr<HOST_REGS;hr++) {
     if(hr!=EXCLUDE_REG&&regmap[hr]>=0) {
       //if(entry[hr]!=regmap[hr]) {
-      if(i==0||!((regs[i-1].isconst>>hr)&1)||pre[hr]!=regmap[hr]||bt[i]) {
+      if(!((regs[i].loadedconst>>hr)&1)) {
         if(((regs[i].isconst>>hr)&1)&&regmap[hr]<64&&regmap[hr]>0) {
-          int value;
+          int value,similar=0;
           if(get_final_value(hr,i,&value)) {
-            if(value==0) {
+            // see if some other register has similar value
+            for(hr2=0;hr2<HOST_REGS;hr2++) {
+              if(hr2!=EXCLUDE_REG&&((regs[i].loadedconst>>hr2)&1)) {
+                if(is_similar_value(value,constmap[i][hr2])) {
+                  similar=1;
+                  break;
+                }
+              }
+            }
+            if(similar) {
+              int value2;
+              if(get_final_value(hr2,i,&value2)) // is this needed?
+                emit_movimm_from(value2,hr2,value,hr);
+              else
+                emit_movimm(value,hr);
+            }
+            else if(value==0) {
               emit_zeroreg(hr);
             }
             else {
               emit_movimm(value,hr);
             }
           }
+          regs[i].loadedconst|=1<<hr;
         }
       }
     }
@@ -8882,6 +8914,7 @@ int new_recompile_block(int addr)
     regs[i].wasconst=current.isconst;
     regs[i].was32=current.is32;
     regs[i].wasdirty=current.dirty;
+    regs[i].loadedconst=0;
     #if defined(DESTRUCTIVE_WRITEBACK) && !defined(FORCE32)
     // To change a dirty register from 32 to 64 bits, we must write
     // it out during the previous cycle (for branches, 2 cycles)