about summary refs log tree commit diff stats
path: root/src/dynarec/dynarec_native.c
diff options
context:
space:
mode:
authorptitSeb <sebastien.chev@gmail.com>2024-07-07 10:37:51 +0200
committerptitSeb <sebastien.chev@gmail.com>2024-07-07 10:37:51 +0200
commit14b0323bf64ba28b081effb78ef7d3897fd5d64d (patch)
tree724d37afadc393dc6ffd994449cafa8f57212f86 /src/dynarec/dynarec_native.c
parentb4828477794a8e69a96f0ca7991ad0e619d1b2a3 (diff)
downloadbox64-14b0323bf64ba28b081effb78ef7d3897fd5d64d.tar.gz
box64-14b0323bf64ba28b081effb78ef7d3897fd5d64d.zip
[ARM64_DYNAREC] Reworked ymm0 propagation
Diffstat (limited to 'src/dynarec/dynarec_native.c')
-rw-r--r--src/dynarec/dynarec_native.c100
1 files changed, 72 insertions, 28 deletions
diff --git a/src/dynarec/dynarec_native.c b/src/dynarec/dynarec_native.c
index 15ecdce8..a233c690 100644
--- a/src/dynarec/dynarec_native.c
+++ b/src/dynarec/dynarec_native.c
@@ -405,35 +405,81 @@ static int updateNeed(dynarec_native_t* dyn, int ninst, uint8_t need) {
     return ninst;
 }
 
-// update Ymm0 and Purge_ymm0.
-static int updateYmm0(dynarec_native_t* dyn, int ninst) {
+static void updateYmm0s(dynarec_native_t* dyn, int ninst, int max_ninst_reached) {
+    int can_incr = ninst == max_ninst_reached; // Are we the top-level call?
     int ok = 1;
-    while (ok && ninst<dyn->size) {
-        uint16_t ymm0 = dyn->insts[ninst].ymm0_in; // entry ymm0
-        ymm0&=~dyn->insts[ninst].purge_ymm; // entry after purge
-        uint16_t ymm0_out = (ymm0|dyn->insts[ninst].ymm0_add)&~dyn->insts[ninst].ymm0_sub;  // ymm0 after the opcode
-        ok = dyn->insts[ninst].x64.has_next;    // continue?
-        if(ok) ok = (dyn->insts[ninst].ymm0_in!=ymm0) || (dyn->insts[ninst+1].ymm0_in!=ymm0_out); // continue if there has been any change...
-        if(ok) dyn->insts[ninst+1].ymm0_in=ymm0_out;   // make the change
-        dyn->insts[ninst].ymm0_out = ymm0_out;  // update ymm0_out
-        dyn->insts[ninst].ymm0_in = ymm0;  // write purged ymm0, as it's done at the entry
-        int jmp = (dyn->insts[ninst].x64.jmp)?dyn->insts[ninst].x64.jmp_insts:-1;
-        if(jmp!=-1) {
-            // check if a purge is needed at jump point
-            ymm0_out&=~dyn->insts[jmp].purge_ymm;
-            ok = (dyn->insts[jmp].pred_sz==1) && (dyn->insts[jmp].ymm0_in!=ymm0_out);
-            if(dyn->insts[jmp].pred_sz==1)
-                dyn->insts[jmp].ymm0_in = ymm0_out;
-            uint16_t ymm0_jmp = dyn->insts[jmp].ymm0_in;
-            uint16_t to_purge = ymm0_jmp&~ymm0_out; // if there are too many ymm0 at jump point
-            if(to_purge)
-                dyn->insts[jmp].purge_ymm|=to_purge;
-            if(to_purge || ok)
-                updateYmm0(dyn, jmp);
+    while ((can_incr || ok) && ninst<dyn->size) {
+        //if(box64_dynarec_dump) dynarec_log(LOG_NONE, "update ninst=%d (%d): can_incr=%d\n", ninst, max_ninst_reached, can_incr);
+        uint16_t new_purge_ymm, new_ymm0_in, new_ymm0_out;
+
+        if (ninst && dyn->insts[ninst].pred_sz && dyn->insts[ninst].x64.alive) {
+            uint16_t ymm0_union = 0, ymm0_inter = (uint16_t)-1; // The union of the empty set is empty, the intersection is the universe
+            for (int i = 0; i < dyn->insts[ninst].pred_sz; ++i) {
+                int pred = dyn->insts[ninst].pred[i];
+                //if(box64_dynarec_dump) dynarec_log(LOG_NONE, "\twith pred[%d] = %d", i, pred);
+                if (pred >= max_ninst_reached) {
+                    //if(box64_dynarec_dump) dynarec_log(LOG_NONE, " (skipped)\n");
+                    continue;
+                }
+
+                int pred_out = dyn->insts[pred].x64.has_callret ? 0 : dyn->insts[pred].ymm0_out;
+                //if(box64_dynarec_dump) dynarec_log(LOG_NONE, " ~> %04X\n", pred_out);
+                ymm0_union |= pred_out;
+                ymm0_inter &= pred_out;
+            }
+            //if(box64_dynarec_dump) dynarec_log(LOG_NONE, "\t=> %04X,%04X\n", ymm0_union, ymm0_inter);
+            // Notice the default values yield something coherent here (if all pred are after ninst)
+            new_purge_ymm = ymm0_union & ~ymm0_inter;
+            new_ymm0_in = ymm0_inter;
+            new_ymm0_out = (ymm0_inter | dyn->insts[ninst].ymm0_add) & ~dyn->insts[ninst].ymm0_sub;
+
+            if ((dyn->insts[ninst].purge_ymm != new_purge_ymm) || (dyn->insts[ninst].ymm0_in != new_ymm0_in) || (dyn->insts[ninst].ymm0_out != new_ymm0_out)) {
+                // Need to update self and next(s)
+                dyn->insts[ninst].purge_ymm = new_purge_ymm;
+                dyn->insts[ninst].ymm0_in = new_ymm0_in;
+                dyn->insts[ninst].ymm0_out = new_ymm0_out;
+
+                if (can_incr) {
+                    // We always have ninst == max_ninst_reached when can_incr == 1
+                    ++max_ninst_reached;
+                } else {
+                    // We need to stop here if the opcode has no "real" next or if we reached the ninst of the toplevel
+                    ok = (max_ninst_reached - 1 != ninst) && dyn->insts[ninst].x64.has_next && !dyn->insts[ninst].x64.has_callret;
+                }
+
+                int jmp = (dyn->insts[ninst].x64.jmp)?dyn->insts[ninst].x64.jmp_insts:-1;
+                if((jmp!=-1) && (jmp < max_ninst_reached)) {
+                    //if(box64_dynarec_dump) dynarec_log(LOG_NONE, "\t! jump to %d\n", jmp);
+                    // The jump goes before the last instruction reached, update the destination
+                    // If this is the top level call, this means the jump goes backward (jmp != ninst)
+                    // Otherwise, since we don't update all instructions, we may miss the update (don't use jmp < ninst)
+                    updateYmm0s(dyn, jmp, max_ninst_reached);
+                }
+            } else {
+                if (can_incr) {
+                    // We always have ninst == max_ninst_reached when can_incr == 1
+                    ++max_ninst_reached;
+
+                    // Also update jumps to before (they are skipped otherwise)
+                    int jmp = (dyn->insts[ninst].x64.jmp)?dyn->insts[ninst].x64.jmp_insts:-1;
+                    if((jmp!=-1) && (jmp < max_ninst_reached)) {
+                        //if(box64_dynarec_dump) dynarec_log(LOG_NONE, "\t! jump to %d\n", jmp);
+                        updateYmm0s(dyn, jmp, max_ninst_reached);
+                    }
+                } else {
+                    // We didn't update anything, we can leave
+                    ok = 0;
+                }
+            }
+        } else if (can_incr) {
+            // We always have ninst == max_ninst_reached when can_incr == 1
+            ++max_ninst_reached;
+        } else {
+            // We didn't update anything, we can leave
+            ok = 0;
         }
         ++ninst;
     }
-    return ninst;
 }
 
 void* current_helper = NULL;
@@ -628,9 +674,7 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr, int alternate, int is32bit
         CancelBlock64(0);
         return CreateEmptyBlock(block, addr);
     }
-    pos = 0;
-    while(pos<helper.size)
-        pos = updateYmm0(&helper, pos);
+    updateYmm0s(&helper, 0, 0);
 
 
     // pass 1, float optimizations, first pass for flags