about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorYang Liu <numbksco@gmail.com>2024-04-12 19:13:30 +0800
committerGitHub <noreply@github.com>2024-04-12 13:13:30 +0200
commit1d0e0307c4f2279803a1aa57533c45c6edeea12a (patch)
tree63a6ed6f07cd6bede1b899886fa6e8701d8ca361 /src
parentf94ab1be6f23d4aeb6d1584b267d7620247a4953 (diff)
downloadbox64-1d0e0307c4f2279803a1aa57533c45c6edeea12a.tar.gz
box64-1d0e0307c4f2279803a1aa57533c45c6edeea12a.zip
[LA64_DYNAREC] Made the CALLRET optimization complete (#1438)
Diffstat (limited to 'src')
-rw-r--r--src/dynarec/la64/dynarec_la64_00.c12
-rw-r--r--src/dynarec/la64/dynarec_la64_helper.c45
-rw-r--r--src/dynarec/la64/dynarec_la64_helper.h2
3 files changed, 59 insertions, 0 deletions
diff --git a/src/dynarec/la64/dynarec_la64_00.c b/src/dynarec/la64/dynarec_la64_00.c
index c32d1bc3..bf9722f6 100644
--- a/src/dynarec/la64/dynarec_la64_00.c
+++ b/src/dynarec/la64/dynarec_la64_00.c
@@ -1125,6 +1125,18 @@ uintptr_t dynarec64_00(dynarec_la64_t* dyn, uintptr_t addr, uintptr_t ip, int ni
                     DEFAULT;
             }
             break;
+        case 0xC2:
+            INST_NAME("RETN");
+            // SETFLAGS(X_ALL, SF_SET);    // Hack, set all flags (to an unknown state...)
+            if (box64_dynarec_safeflags) {
+                READFLAGS(X_PEND); // lets play safe here too
+            }
+            BARRIER(BARRIER_FLOAT);
+            i32 = F16;
+            retn_to_epilog(dyn, ninst, rex, i32);
+            *need_epilog = 0;
+            *ok = 0;
+            break;
         case 0xC3:
             INST_NAME("RET");
             // SETFLAGS(X_ALL, SF_SET);    // Hack, set all flags (to an unknown state...)
diff --git a/src/dynarec/la64/dynarec_la64_helper.c b/src/dynarec/la64/dynarec_la64_helper.c
index 5bba9433..917b0d6c 100644
--- a/src/dynarec/la64/dynarec_la64_helper.c
+++ b/src/dynarec/la64/dynarec_la64_helper.c
@@ -445,6 +445,51 @@ void ret_to_epilog(dynarec_la64_t* dyn, int ninst, rex_t rex)
     CLEARIP();
 }
 
+void retn_to_epilog(dynarec_la64_t* dyn, int ninst, rex_t rex, int n)
+{
+    MAYUSE(dyn);
+    MAYUSE(ninst);
+    MESSAGE(LOG_DUMP, "Retn to epilog\n");
+    POP1z(xRIP);
+    if (n > 0x7ff) {
+        MOV64x(w1, n);
+        ADDz(xRSP, xRSP, x1);
+    } else {
+        ADDIz(xRSP, xRSP, n);
+    }
+    MVz(x1, xRIP);
+    SMEND();
+    if (box64_dynarec_callret) {
+        // pop the actual return address from RV64 stack
+        LD_D(x2, xSP, 0);     // native addr
+        LD_D(x6, xSP, 8);     // x86 addr
+        ADDI_D(xSP, xSP, 16); // pop
+        BNE(x6, xRIP, 2 * 4); // is it the right address?
+        BR(x2);
+        // not the correct return address, regular jump, but purge the stack first, it's unsync now...
+        ADDI_D(xSP, xSavedSP, -16);
+    }
+
+    uintptr_t tbl = rex.is32bits ? getJumpTable32() : getJumpTable64();
+    MOV64x(x3, tbl);
+    if (!rex.is32bits) {
+        BSTRPICK_D(x2, xRIP, JMPTABL_START3 + JMPTABL_SHIFT3 - 1, JMPTABL_START3);
+        ALSL_D(x3, x2, x3, 3);
+        LD_D(x3, x3, 0);
+    }
+    BSTRPICK_D(x2, xRIP, JMPTABL_START2 + JMPTABL_SHIFT2 - 1, JMPTABL_START2);
+    ALSL_D(x3, x2, x3, 3);
+    LD_D(x3, x3, 0);
+    BSTRPICK_D(x2, xRIP, JMPTABL_START1 + JMPTABL_SHIFT1 - 1, JMPTABL_START1);
+    ALSL_D(x3, x2, x3, 3);
+    LD_D(x3, x3, 0);
+    BSTRPICK_D(x2, xRIP, JMPTABL_START0 + JMPTABL_SHIFT0 - 1, JMPTABL_START0);
+    ALSL_D(x3, x2, x3, 3);
+    LD_D(x2, x3, 0);
+    BR(x2); // save LR
+    CLEARIP();
+}
+
 void call_c(dynarec_la64_t* dyn, int ninst, void* fnc, int reg, int ret, int saveflags, int savereg)
 {
     MAYUSE(fnc);
diff --git a/src/dynarec/la64/dynarec_la64_helper.h b/src/dynarec/la64/dynarec_la64_helper.h
index 8f306681..bf5e5abb 100644
--- a/src/dynarec/la64/dynarec_la64_helper.h
+++ b/src/dynarec/la64/dynarec_la64_helper.h
@@ -629,6 +629,7 @@ void* la64_next(x64emu_t* emu, uintptr_t addr);
 #define jump_to_epilog_fast STEPNAME(jump_to_epilog_fast)
 #define jump_to_next        STEPNAME(jump_to_next)
 #define ret_to_epilog       STEPNAME(ret_to_epilog)
+#define retn_to_epilog      STEPNAME(retn_to_epilog)
 #define call_c              STEPNAME(call_c)
 #define grab_segdata        STEPNAME(grab_segdata)
 #define emit_cmp16          STEPNAME(emit_cmp16)
@@ -702,6 +703,7 @@ void jump_to_epilog(dynarec_la64_t* dyn, uintptr_t ip, int reg, int ninst);
 void jump_to_epilog_fast(dynarec_la64_t* dyn, uintptr_t ip, int reg, int ninst);
 void jump_to_next(dynarec_la64_t* dyn, uintptr_t ip, int reg, int ninst, int is32bits);
 void ret_to_epilog(dynarec_la64_t* dyn, int ninst, rex_t rex);
+void retn_to_epilog(dynarec_la64_t* dyn, int ninst, rex_t rex, int n);
 void call_c(dynarec_la64_t* dyn, int ninst, void* fnc, int reg, int ret, int saveflags, int save_reg);
 void grab_segdata(dynarec_la64_t* dyn, uintptr_t addr, int ninst, int reg, int segment);
 void emit_cmp8(dynarec_la64_t* dyn, int ninst, int s1, int s2, int s3, int s4, int s5, int s6);