Allow goto_jc to be implemented in the backend.

Signed-off-by: Richard Henderson <[email protected]>
---
 docs/devel/tcg-ops.rst           | 11 +++++
 include/tcg/tcg-opc.h            |  1 +
 tcg/tcg-op.c                     | 73 ++++++++++++++++++++------------
 tcg/tcg.c                        | 22 ++++++++++
 tcg/aarch64/tcg-target.c.inc     |  4 ++
 tcg/loongarch64/tcg-target.c.inc |  4 ++
 tcg/ppc64/tcg-target.c.inc       |  4 ++
 tcg/riscv64/tcg-target.c.inc     |  4 ++
 tcg/s390x/tcg-target.c.inc       |  4 ++
 tcg/sparc64/tcg-target.c.inc     |  4 ++
 tcg/tci/tcg-target.c.inc         |  4 ++
 tcg/x86_64/tcg-target.c.inc      |  4 ++
 12 files changed, 111 insertions(+), 28 deletions(-)

diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst
index 51fa183e911..ca640610b42 100644
--- a/docs/devel/tcg-ops.rst
+++ b/docs/devel/tcg-ops.rst
@@ -752,6 +752,17 @@ QEMU specific operations
        | This operation is optional. If the TCG backend does not implement the
          goto_ptr opcode, emitting this op is equivalent to emitting 
exit_tb(0).
 
+   * - goto_jc *jc*, *pc*, *cs_base*, *flags*, *label*
+
+     - | *jc* is a CPUJumpCache pointer.  Generate code for:
+       |
+       | if (jc->pc != pc) goto label;
+       | tb = jc->tb;
+       | if (tb == NULL) goto label;
+       | if (tb->flags != flags) goto label;
+       | if (tb->cs_base != cs_base) goto label;
+       | goto tb->tc.ptr;
+
    * - qemu_ld_i32/i64/i128 *t0*, *t1*, *flags*, *memidx*
 
        qemu_st_i32/i64/i128 *t0*, *t1*, *flags*, *memidx*
diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h
index 884345afe56..ce7703d5830 100644
--- a/include/tcg/tcg-opc.h
+++ b/include/tcg/tcg-opc.h
@@ -122,6 +122,7 @@ DEF(insn_start, 0, 0, INSN_START_WORDS, TCG_OPF_NOT_PRESENT)
 DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT)
 DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT)
 DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
+DEF(goto_jc, 0, 4, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
 
 DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT)
 DEF(plugin_mem_cb, 0, 1, 1, TCG_OPF_NOT_PRESENT)
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index cd10a00bf9e..0c609f8b38e 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -158,6 +158,14 @@ static TCGOp *gen_op_ttttt(TCGOpcode opc, TCGType type,
                        temp_arg(t3), temp_arg(t4));
 }
 
+static TCGOp *gen_op_tttti(TCGOpcode opc, TCGType type,
+                           TCGTemp *t0, TCGTemp *t1, TCGTemp *t2,
+                           TCGTemp *t3, TCGArg a4)
+{
+    return tcg_gen_op5(opc, type, temp_arg(t0), temp_arg(t1),
+                       temp_arg(t2), temp_arg(t3), a4);
+}
+
 static TCGOp *gen_op_ttttti(TCGOpcode opc, TCGType type,
                             TCGTemp *t0, TCGTemp *t1, TCGTemp *t2,
                             TCGTemp *t3, TCGTemp *t4, TCGArg a5)
@@ -1997,7 +2005,13 @@ static void gen_jc_probe(TCGTemp *pc, TCGTemp *cs_base, 
uint32_t flags)
 {
     const TranslationBlock *tb = tcg_ctx->gen_tb;
     TCGLabel *slow = gen_new_label();
-    uint64_t fpair;
+    /*
+     * flags and cflags are adjacent uint32_t, so one aligned 64-bit load
+     * and compare covers both.
+     */
+    uint64_t fpair = (HOST_BIG_ENDIAN
+                      ? deposit64(tb->cflags, 32, 32, flags)
+                      : deposit64(flags, 32, 32, tb->cflags));
 
     QEMU_BUILD_BUG_ON(sizeof_field(CPUJumpCache, array[0]) != 16);
     QEMU_BUILD_BUG_ON(offsetof(CPUJumpCache, array[0].pc) % 8 != 0);
@@ -2018,37 +2032,40 @@ static void gen_jc_probe(TCGTemp *pc, TCGTemp *cs_base, 
uint32_t flags)
                    offsetof(CPUState, tb_jmp_cache) - sizeof(CPUState));
         gen_lea(TCG_TYPE_I64, p, t, h, 4, 0);
 
-        /*
-         * The pc first: on a hash miss it is the field most likely to differ,
-         * and an entry whose tb is NULL has a zero pc that only pc 0 matches.
-         */
-        gen_ld(TCG_TYPE_I64, t, p, offsetof(CPUJumpCache, array[0].pc));
-        gen_brcond(TCG_TYPE_I64, TCG_COND_NE, t, pc, slow);
+        if (tcg_op_supported(INDEX_op_goto_jc, TCG_TYPE_PTR, 0)) {
+            TCGOp *op = gen_op_tttti(INDEX_op_goto_jc, TCG_TYPE_PTR,
+                                     p, pc, cs_base,
+                                     tcg_constant_internal(TCG_TYPE_I64, 
fpair),
+                                     label_arg(slow));
+            add_as_label_use(slow, op);
+        } else {
+            /*
+             * The pc first: on a hash miss it is the field most likely
+             * to differ, and an entry whose tb is NULL has a zero pc
+             * that only pc 0 matches.
+             */
+            gen_ld(TCG_TYPE_I64, t, p, offsetof(CPUJumpCache, array[0].pc));
+            gen_brcond(TCG_TYPE_I64, TCG_COND_NE, t, pc, slow);
 
-        gen_ld(TCG_TYPE_PTR, p, p, offsetof(CPUJumpCache, array[0].tb));
-        gen_brcond(TCG_TYPE_PTR, TCG_COND_EQ,
-                   p, tcg_constant_internal(TCG_TYPE_PTR, 0), slow);
+            gen_ld(TCG_TYPE_PTR, p, p, offsetof(CPUJumpCache, array[0].tb));
+            gen_brcond(TCG_TYPE_PTR, TCG_COND_EQ,
+                       p, tcg_constant_internal(TCG_TYPE_PTR, 0), slow);
 
-        /*
-         * flags and cflags are adjacent uint32_t, so one aligned 64-bit load
-         * and compare covers both.
-         */
-        fpair = (HOST_BIG_ENDIAN
-                 ? deposit64(tb->cflags, 32, 32, flags)
-                 : deposit64(flags, 32, 32, tb->cflags));
-        gen_ld(TCG_TYPE_I64, t, p, offsetof(TranslationBlock, flags));
-        gen_brcond(TCG_TYPE_I64, TCG_COND_NE,
-                   t, tcg_constant_internal(TCG_TYPE_I64, fpair), slow);
+            gen_ld(TCG_TYPE_I64, t, p, offsetof(TranslationBlock, flags));
+            gen_brcond(TCG_TYPE_I64, TCG_COND_NE,
+                       t, tcg_constant_internal(TCG_TYPE_I64, fpair), slow);
 
-        /*
-         * cs_base is a second word of target-specific flags despite the name,
-         * and the pc alone does not imply it on a target that uses it.
-         */
-        gen_ld(TCG_TYPE_I64, t, p, offsetof(TranslationBlock, cs_base));
-        gen_brcond(TCG_TYPE_I64, TCG_COND_NE, t, cs_base, slow);
+            /*
+             * cs_base is a second word of target-specific flags despite
+             * the name, and the pc alone does not imply it on a target
+             * that uses it.
+             */
+            gen_ld(TCG_TYPE_I64, t, p, offsetof(TranslationBlock, cs_base));
+            gen_brcond(TCG_TYPE_I64, TCG_COND_NE, t, cs_base, slow);
 
-        gen_ld(TCG_TYPE_PTR, p, p, offsetof(TranslationBlock, tc.ptr));
-        gen_goto_ptr(p);
+            gen_ld(TCG_TYPE_PTR, p, p, offsetof(TranslationBlock, tc.ptr));
+            gen_goto_ptr(p);
+        }
     }
 
     /*
diff --git a/tcg/tcg.c b/tcg/tcg.c
index b88ee847039..a08e83f3954 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -1101,6 +1101,12 @@ typedef struct TCGOutOpLea {
                 TCGReg a2, unsigned sh, tcg_target_long imm);
 } TCGOutOpLea;
 
+typedef struct TCGOutOpGotoJC {
+    TCGOutOp base;
+    void (*out)(TCGContext *s, TCGReg jc, TCGReg pc, TCGArg cs, bool const_cs,
+                TCGArg fl, bool const_fl, TCGLabel *l);
+} TCGOutOpGotoJC;
+
 #include "tcg-target.c.inc"
 
 #ifndef CONFIG_TCG_INTERPRETER
@@ -1190,6 +1196,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = {
     OUTOP(INDEX_op_extrh_i64_i32, TCGOutOpUnary, outop_extrh_i64_i32),
     OUTOP(INDEX_op_extrl_i64_i32, TCGOutOpUnary, outop_extrl_i64_i32),
     OUTOP(INDEX_op_extu_i32_i64, TCGOutOpUnary, outop_extu_i32_i64),
+    OUTOP(INDEX_op_goto_jc, TCGOutOpGotoJC, outop_goto_jc),
     OUTOP(INDEX_op_ld, TCGOutOpLoad, outop_ld),
     OUTOP(INDEX_op_ld8s, TCGOutOpLoad, outop_ld8s),
     OUTOP(INDEX_op_ld8u, TCGOutOpLoad, outop_ld8u),
@@ -3009,6 +3016,7 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs)
             case INDEX_op_set_label:
             case INDEX_op_br:
             case INDEX_op_brcond:
+            case INDEX_op_goto_jc:
                 col += ne_fprintf(f, "%s$L%d", k ? "," : "",
                                   arg_label(op->args[k])->id);
                 i++, k++;
@@ -3478,6 +3486,9 @@ void tcg_op_remove(TCGContext *s, TCGOp *op)
     case INDEX_op_brcond:
         remove_label_use(op, 3);
         break;
+    case INDEX_op_goto_jc:
+        remove_label_use(op, 4);
+        break;
     default:
         break;
     }
@@ -3576,6 +3587,9 @@ static void move_label_uses(TCGLabel *to, TCGLabel *from)
         case INDEX_op_brcond:
             op->args[3] = label_arg(to);
             break;
+        case INDEX_op_goto_jc:
+            op->args[4] = label_arg(to);
+            break;
         default:
             g_assert_not_reached();
         }
@@ -3650,6 +3664,7 @@ reachable_code_pass(TCGContext *s)
         case INDEX_op_br:
         case INDEX_op_exit_tb:
         case INDEX_op_goto_ptr:
+        case INDEX_op_goto_jc:
             /* Unconditional branches; everything following is dead.  */
             dead = true;
             break;
@@ -6017,6 +6032,13 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp 
*op)
         tcg_out_goto_ptr(s, new_args[0]);
         break;
 
+    case INDEX_op_goto_jc:
+        tcg_debug_assert(!const_args[0]);
+        tcg_debug_assert(!const_args[1]);
+        outop_goto_jc.out(s, new_args[0], new_args[1], new_args[2], 
const_args[2],
+                          new_args[3], const_args[3], arg_label(new_args[4]));
+        break;
+
     default:
         tcg_debug_assert(def->flags & TCG_OPF_VECTOR);
         tcg_out_vec_op(s, op->opc, type - TCG_TYPE_V64,
diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc
index 6836c0dd94f..a0f23914224 100644
--- a/tcg/aarch64/tcg-target.c.inc
+++ b/tcg/aarch64/tcg-target.c.inc
@@ -2008,6 +2008,10 @@ static const TCGOutOpQemuLdSt2 outop_qemu_st2 = {
     .out = tgen_qemu_st2,
 };
 
+static const TCGOutOpGotoJC outop_goto_jc = {
+    .base.static_constraint = C_NotImplemented,
+};
+
 static const tcg_insn_unit *tb_ret_addr;
 
 static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0)
diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc
index 377cd9ec08d..733cce516e0 100644
--- a/tcg/loongarch64/tcg-target.c.inc
+++ b/tcg/loongarch64/tcg-target.c.inc
@@ -2113,6 +2113,10 @@ static const TCGOutOpLea outop_lea = {
     .out = tgen_lea,
 };
 
+static const TCGOutOpGotoJC outop_goto_jc = {
+    .base.static_constraint = C_NotImplemented,
+};
+
 static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece,
                             TCGReg rd, TCGReg rs)
 {
diff --git a/tcg/ppc64/tcg-target.c.inc b/tcg/ppc64/tcg-target.c.inc
index 99f4469f4b9..466ae836d5a 100644
--- a/tcg/ppc64/tcg-target.c.inc
+++ b/tcg/ppc64/tcg-target.c.inc
@@ -3637,6 +3637,10 @@ static const TCGOutOpLea outop_lea = {
     .base.static_constraint = C_NotImplemented,
 };
 
+static const TCGOutOpGotoJC outop_goto_jc = {
+    .base.static_constraint = C_NotImplemented,
+};
+
 int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece)
 {
     switch (opc) {
diff --git a/tcg/riscv64/tcg-target.c.inc b/tcg/riscv64/tcg-target.c.inc
index ba7aab9246c..effa9d7a1ac 100644
--- a/tcg/riscv64/tcg-target.c.inc
+++ b/tcg/riscv64/tcg-target.c.inc
@@ -2743,6 +2743,10 @@ static const TCGOutOpLea outop_lea = {
     .out = tgen_lea,
 };
 
+static const TCGOutOpGotoJC outop_goto_jc = {
+    .base.static_constraint = C_NotImplemented,
+};
+
 static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc,
                            unsigned vecl, unsigned vece,
                            const TCGArg args[TCG_MAX_OP_ARGS],
diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc
index db3b2fc3c92..3b63383315d 100644
--- a/tcg/s390x/tcg-target.c.inc
+++ b/tcg/s390x/tcg-target.c.inc
@@ -3202,6 +3202,10 @@ static const TCGOutOpLea outop_lea = {
     .out = tgen_lea,
 };
 
+static const TCGOutOpGotoJC outop_goto_jc = {
+    .base.static_constraint = C_NotImplemented,
+};
+
 static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece,
                             TCGReg dst, TCGReg src)
 {
diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc
index cb457453ff9..0582dc8bdd3 100644
--- a/tcg/sparc64/tcg-target.c.inc
+++ b/tcg/sparc64/tcg-target.c.inc
@@ -2124,6 +2124,10 @@ static const TCGOutOpLea outop_lea = {
     .base.static_constraint = C_NotImplemented,
 };
 
+static const TCGOutOpGotoJC outop_goto_jc = {
+    .base.static_constraint = C_NotImplemented,
+};
+
 static TCGConstraintSetIndex
 tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags)
 {
diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc
index 6942fdbb4e0..5d8892abc44 100644
--- a/tcg/tci/tcg-target.c.inc
+++ b/tcg/tci/tcg-target.c.inc
@@ -1224,6 +1224,10 @@ static const TCGOutOpLea outop_lea = {
     .base.static_constraint = C_NotImplemented,
 };
 
+static const TCGOutOpGotoJC outop_goto_jc = {
+    .base.static_constraint = C_NotImplemented,
+};
+
 /* Test if a constant matches the constraint. */
 static bool tcg_target_const_match(int64_t val, int ct,
                                    TCGType type, TCGCond cond, int vece)
diff --git a/tcg/x86_64/tcg-target.c.inc b/tcg/x86_64/tcg-target.c.inc
index a41252782e1..08dec0dace8 100644
--- a/tcg/x86_64/tcg-target.c.inc
+++ b/tcg/x86_64/tcg-target.c.inc
@@ -3344,6 +3344,10 @@ static const TCGOutOpLea outop_lea = {
     .out = tgen_lea,
 };
 
+static const TCGOutOpGotoJC outop_goto_jc = {
+    .base.static_constraint = C_NotImplemented,
+};
+
 static int const umin_insn[4] = {
     OPC_PMINUB, OPC_PMINUW, OPC_PMINUD, OPC_VPMINUQ
 };
-- 
2.53.0


Reply via email to