diff mbox series

[RFC,v5,05/10] tcg: Add tcg opcodes and helpers for native library calls

Message ID 20230825104526.1754950-6-fufuyqqqqqq@gmail.com
State New
Headers show
Series None | expand

Commit Message

Yeqi Fu Aug. 25, 2023, 10:45 a.m. UTC
This commit implements tcg opcodes and helpers for native library
calls. A table is used to store the parameter types and return value
types for each native library function. In terms of types, only three
types are of real concern: the two base sizes int and intptr_t, and
if the value is a pointer, tcg_gen_g2h and tcg_gen_h2g are used for
address conversion.

Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
---
 accel/tcg/tcg-runtime.h      |  22 ++++
 include/native/native-defs.h |  42 ++++++++
 include/tcg/tcg-op-common.h  |  11 ++
 include/tcg/tcg.h            |   9 ++
 tcg/tcg-op.c                 | 193 ++++++++++++++++++++++++++++++++++-
 5 files changed, 276 insertions(+), 1 deletion(-)
 create mode 100644 include/native/native-defs.h

Comments

Richard Henderson Aug. 26, 2023, 2:24 a.m. UTC | #1
On 8/25/23 03:45, Yeqi Fu wrote:
> This commit implements tcg opcodes and helpers for native library
> calls. A table is used to store the parameter types and return value
> types for each native library function. In terms of types, only three
> types are of real concern: the two base sizes int and intptr_t, and
> if the value is a pointer, tcg_gen_g2h and tcg_gen_h2g are used for
> address conversion.
> 
> Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
> ---
>   accel/tcg/tcg-runtime.h      |  22 ++++
>   include/native/native-defs.h |  42 ++++++++
>   include/tcg/tcg-op-common.h  |  11 ++
>   include/tcg/tcg.h            |   9 ++
>   tcg/tcg-op.c                 | 193 ++++++++++++++++++++++++++++++++++-
>   5 files changed, 276 insertions(+), 1 deletion(-)
>   create mode 100644 include/native/native-defs.h
> 
> diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h
> index 39e68007f9..bda78b4489 100644
> --- a/accel/tcg/tcg-runtime.h
> +++ b/accel/tcg/tcg-runtime.h
> @@ -37,6 +37,28 @@ DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env)
>    */
>   #define helper_memset memset
>   DEF_HELPER_FLAGS_3(memset, TCG_CALL_NO_RWG, ptr, ptr, int, ptr)
> +
> +#define helper_memcpy memcpy
> +DEF_HELPER_FLAGS_3(memcpy, TCG_CALL_NO_RWG, ptr, ptr, ptr, ptr)
> +
> +#define helper_strncpy strncpy
> +DEF_HELPER_FLAGS_3(strncpy, TCG_CALL_NO_RWG, ptr, ptr, ptr, ptr)
> +
> +#define helper_memcmp memcmp
> +DEF_HELPER_FLAGS_3(memcmp, TCG_CALL_NO_RWG, int, ptr, ptr, ptr)
> +
> +#define helper_strncmp strncmp
> +DEF_HELPER_FLAGS_3(strncmp, TCG_CALL_NO_RWG, int, ptr, ptr, ptr)
> +
> +#define helper_strcpy strcpy
> +DEF_HELPER_FLAGS_2(strcpy, TCG_CALL_NO_RWG, ptr, ptr, ptr)
> +
> +#define helper_strcat strcat
> +DEF_HELPER_FLAGS_2(strcat, TCG_CALL_NO_RWG, ptr, ptr, ptr)
> +
> +#define helper_strcmp strcmp
> +DEF_HELPER_FLAGS_2(strcmp, TCG_CALL_NO_RWG, int, ptr, ptr)

You cannot just call these directly.  This will fail immediately whenever the guest does 
something silly like

	memcpy(NULL, "foo", 4);

This must raise SIGSEGV to the guest.

If we leave the bulk transform to tcg, the absolute minimum is

void * HELPER(memcpy)(void *dst, void *src, target_ulong len)
{
     set_helper_retaddr(GETPC());
     void *r = memcpy(dst, src, len);
     clear_helper_retaddr();
     return r;
}

There is no way to do this thread-local storage update from TCG.

But if we need to have a helper at all, we might as well do more and *not* leave the 
transform to tcg.  Something akin to

target_ulong HELPER(memcpy)(target_ulong dst, target_ulong src, target_ulong len)
{
     uintptr_t ra = GETPC();
     CPUState *cpu = thread_cpu;
     void *h_dst, *h_src;

     if (!h2g_valid(src)) {
        cpu_loop_exit_sigsegv(cpu, src, MMU_DATA_LOAD, 1, ra);
     }
     if (!h2g_valid(dst)) {
        cpu_loop_exit_sigsegv(cpu, dst, MMU_DATA_STORE, 1, ra);
     }

     set_helper_retaddr(ra);
     memcpy(g2h(cpu, dst), g2h(cpu, src), len);
     clear_helper_retaddr(ra);

     /* memcpy always returns its first argument */
     return dst;
}


> --- /dev/null
> +++ b/include/native/native-defs.h
> @@ -0,0 +1,42 @@
> +/*
> + * Argument encoding. We only really care about 3 types. The two base
> + * sizes (int and intptr_t) and if the value is a pointer (in which
> + * case we need to adjust it g2h before passing to the native
> + * function).
> + */
> +#define TYPE_NO_ARG 0x0
> +#define TYPE_INT_ARG 0x1
> +#define TYPE_IPTR_ARG 0x2
> +#define TYPE_PTR_ARG 0x3
> +
> +#define ENCODE_TYPE(ret_value, arg1, arg2, arg3) \
> +    ((ret_value) | (arg1 << 4) | (arg2 << 8) | (arg3 << 12))

Supposing we do the transform in tcg, this duplicates include/exec/helper-head.h, and 
dh_typemask().

> +static const FuncHelper func_helper_table[] = {
> +    { .func = "memset",
> +      .helper = (helper_func)gen_helper_memset,
> +      .type = TYPE_AAIP },
> +    { .func = "memcpy",
> +      .helper = (helper_func)gen_helper_memcpy,
> +      .type = TYPE_AAAP },
> +    { .func = "strncpy",
> +      .helper = (helper_func)gen_helper_strncpy,
> +      .type = TYPE_AAAP },
> +    { .func = "memcmp",
> +      .helper = (helper_func)gen_helper_memcmp,
> +      .type = TYPE_IAAP },
> +    { .func = "strncmp",
> +      .helper = (helper_func)gen_helper_strncmp,
> +      .type = TYPE_IAAP },
> +    { .func = "strcpy",
> +      .helper = (helper_func)gen_helper_strcpy,
> +      .type = TYPE_AAA },
> +    { .func = "strcat",
> +      .helper = (helper_func)gen_helper_strcat,
> +      .type = TYPE_AAA },
> +    { .func = "strcmp",
> +      .helper = (helper_func)gen_helper_strcmp,
> +      .type = TYPE_IAA },
> +};
> +/* p: iptr ; i: i32 ; a: ptr(address) */
> +void gen_native_call_i32(const char *func_name, TCGv_i32 ret, TCGv_i32 arg1,
> +                         TCGv_i32 arg2, TCGv_i32 arg3)
> +{
> +    TCGv_ptr arg1_ptr = tcg_temp_new_ptr();
> +    TCGv_ptr arg2_ptr = tcg_temp_new_ptr();
> +    TCGv_ptr arg3_ptr = tcg_temp_new_ptr();
> +    TCGv_ptr ret_ptr = tcg_temp_new_ptr();
> +    unsigned int i;
> +    for (i = 0; i < sizeof(func_helper_table) / sizeof(FuncHelper); i++) {
> +        if (strcmp(func_name, func_helper_table[i].func) == 0) {
> +            break;
> +        }
> +    }
> +    g_assert(i < sizeof(func_helper_table) / sizeof(FuncHelper));

So you assert() if libnative.so gives you something that doesn't match?

Surely returning false here, and allowing the translator to fail the transformation is 
better.  This would generate SIGILL at runtime, which still kills the guest program, but 
it feels cleaner.  You could also do

   qemu_log_mask(LOG_GUEST_ERROR, "Unimplemented libnative call to \"%s\"\n", func_name);


r~
diff mbox series

Patch

diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h
index 39e68007f9..bda78b4489 100644
--- a/accel/tcg/tcg-runtime.h
+++ b/accel/tcg/tcg-runtime.h
@@ -37,6 +37,28 @@  DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env)
  */
 #define helper_memset memset
 DEF_HELPER_FLAGS_3(memset, TCG_CALL_NO_RWG, ptr, ptr, int, ptr)
+
+#define helper_memcpy memcpy
+DEF_HELPER_FLAGS_3(memcpy, TCG_CALL_NO_RWG, ptr, ptr, ptr, ptr)
+
+#define helper_strncpy strncpy
+DEF_HELPER_FLAGS_3(strncpy, TCG_CALL_NO_RWG, ptr, ptr, ptr, ptr)
+
+#define helper_memcmp memcmp
+DEF_HELPER_FLAGS_3(memcmp, TCG_CALL_NO_RWG, int, ptr, ptr, ptr)
+
+#define helper_strncmp strncmp
+DEF_HELPER_FLAGS_3(strncmp, TCG_CALL_NO_RWG, int, ptr, ptr, ptr)
+
+#define helper_strcpy strcpy
+DEF_HELPER_FLAGS_2(strcpy, TCG_CALL_NO_RWG, ptr, ptr, ptr)
+
+#define helper_strcat strcat
+DEF_HELPER_FLAGS_2(strcat, TCG_CALL_NO_RWG, ptr, ptr, ptr)
+
+#define helper_strcmp strcmp
+DEF_HELPER_FLAGS_2(strcmp, TCG_CALL_NO_RWG, int, ptr, ptr)
+
 #endif /* IN_HELPER_PROTO */
 
 DEF_HELPER_FLAGS_3(ld_i128, TCG_CALL_NO_WG, i128, env, i64, i32)
diff --git a/include/native/native-defs.h b/include/native/native-defs.h
new file mode 100644
index 0000000000..517bb86db0
--- /dev/null
+++ b/include/native/native-defs.h
@@ -0,0 +1,42 @@ 
+/*
+ * Argument encoding. We only really care about 3 types. The two base
+ * sizes (int and intptr_t) and if the value is a pointer (in which
+ * case we need to adjust it g2h before passing to the native
+ * function).
+ */
+#define TYPE_NO_ARG 0x0
+#define TYPE_INT_ARG 0x1
+#define TYPE_IPTR_ARG 0x2
+#define TYPE_PTR_ARG 0x3
+
+#define ENCODE_TYPE(ret_value, arg1, arg2, arg3) \
+    ((ret_value) | (arg1 << 4) | (arg2 << 8) | (arg3 << 12))
+
+#define TYPE_AAAP \
+    ENCODE_TYPE(TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_IPTR_ARG)
+#define TYPE_IAAP \
+    ENCODE_TYPE(TYPE_INT_ARG, TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_IPTR_ARG)
+#define TYPE_AAIP \
+    ENCODE_TYPE(TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_INT_ARG, TYPE_IPTR_ARG)
+#define TYPE_AAA \
+    ENCODE_TYPE(TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_NO_ARG)
+#define TYPE_IAA \
+    ENCODE_TYPE(TYPE_INT_ARG, TYPE_PTR_ARG, TYPE_PTR_ARG, TYPE_NO_ARG)
+
+typedef void (*helper_pppp)(struct TCGv_ptr_d *, struct TCGv_ptr_d *,
+                            struct TCGv_ptr_d *, struct TCGv_ptr_d *);
+typedef void (*helper_ippp)(struct TCGv_i32_d *, struct TCGv_ptr_d *,
+                            struct TCGv_ptr_d *, struct TCGv_ptr_d *);
+typedef void (*helper_ppip)(struct TCGv_ptr_d *, struct TCGv_ptr_d *,
+                            struct TCGv_i32_d *, struct TCGv_ptr_d *);
+typedef void (*helper_ppp)(struct TCGv_ptr_d *, struct TCGv_ptr_d *,
+                           struct TCGv_ptr_d *);
+typedef void (*helper_ipp)(struct TCGv_i32_d *, struct TCGv_ptr_d *,
+                           struct TCGv_ptr_d *);
+typedef void (*helper_func)(void *, ...);
+
+typedef struct {
+    const char *func;
+    helper_func helper;
+    uint32_t type;
+} FuncHelper;
diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h
index be382bbf77..2e712f1573 100644
--- a/include/tcg/tcg-op-common.h
+++ b/include/tcg/tcg-op-common.h
@@ -903,6 +903,12 @@  void tcg_gen_ld_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset);
 void tcg_gen_st_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset);
 void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t);
 
+/* Host <-> guest conversions */
+void tcg_gen_g2h_i32(TCGv_ptr ret, TCGv_i32 arg);
+void tcg_gen_g2h_i64(TCGv_ptr ret, TCGv_i64 arg);
+void tcg_gen_h2g_i32(TCGv_i32 ret, TCGv_ptr arg);
+void tcg_gen_h2g_i64(TCGv_i64 ret, TCGv_ptr arg);
+
 /* Host pointer ops */
 
 #if UINTPTR_MAX == UINT32_MAX
@@ -938,6 +944,11 @@  static inline void tcg_gen_addi_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t b)
     glue(tcg_gen_addi_,PTR)((NAT)r, (NAT)a, b);
 }
 
+static inline void tcg_gen_subi_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t b)
+{
+    glue(tcg_gen_subi_, PTR)((NAT)r, (NAT)a, b);
+}
+
 static inline void tcg_gen_mov_ptr(TCGv_ptr d, TCGv_ptr s)
 {
     glue(tcg_gen_mov_,PTR)((NAT)d, (NAT)s);
diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
index 0875971719..a4c0f26dc4 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -35,6 +35,9 @@ 
 #include "tcg-target.h"
 #include "tcg/tcg-cond.h"
 #include "tcg/debug-assert.h"
+#ifdef CONFIG_USER_ONLY
+#include "exec/user/guest-base.h"
+#endif
 
 /* XXX: make safe guess about sizes */
 #define MAX_OP_PER_INSTR 266
@@ -1148,4 +1151,10 @@  static inline const TCGOpcode *tcg_swap_vecop_list(const TCGOpcode *n)
 
 bool tcg_can_emit_vecop_list(const TCGOpcode *, TCGType, unsigned);
 
+/* native call */
+void gen_native_call_i32(const char *func_name, TCGv_i32 ret,
+                         TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3);
+void gen_native_call_i64(const char *func_name, TCGv_i64 ret,
+                         TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3);
+
 #endif /* TCG_H */
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index 7aadb37756..8cd39e4ab4 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -29,7 +29,7 @@ 
 #include "exec/translation-block.h"
 #include "exec/plugin-gen.h"
 #include "tcg-internal.h"
-
+#include "native/native-defs.h"
 
 void tcg_gen_op1(TCGOpcode opc, TCGArg a1)
 {
@@ -2852,3 +2852,194 @@  void tcg_gen_lookup_and_goto_ptr(void)
     tcg_gen_op1i(INDEX_op_goto_ptr, tcgv_ptr_arg(ptr));
     tcg_temp_free_ptr(ptr);
 }
+
+#ifdef CONFIG_USER_ONLY
+void tcg_gen_g2h_i32(TCGv_ptr ret, TCGv_i32 arg)
+{
+    TCGv_ptr temp = tcg_temp_new_ptr();
+    tcg_gen_ext_i32_ptr(temp, arg);
+    tcg_gen_addi_ptr(ret, temp, guest_base);
+    tcg_temp_free_ptr(temp);
+}
+
+void tcg_gen_g2h_i64(TCGv_ptr ret, TCGv_i64 arg)
+{
+    TCGv_ptr temp = tcg_temp_new_ptr();
+    tcg_gen_trunc_i64_ptr(temp, arg); /* Not sure */
+    tcg_gen_addi_ptr(ret, temp, guest_base);
+    tcg_temp_free_ptr(temp);
+}
+
+void tcg_gen_h2g_i32(TCGv_i32 ret, TCGv_ptr arg)
+{
+    TCGv_ptr temp = tcg_temp_new_ptr();
+    tcg_gen_subi_ptr(temp, arg, guest_base);
+    tcg_gen_trunc_ptr_i32(ret, temp);
+    tcg_temp_free_ptr(temp);
+}
+
+void tcg_gen_h2g_i64(TCGv_i64 ret, TCGv_ptr arg)
+{
+    TCGv_ptr temp = tcg_temp_new_ptr();
+    tcg_gen_subi_ptr(temp, arg, guest_base);
+    tcg_gen_extu_ptr_i64(ret, temp);
+    tcg_temp_free_ptr(temp);
+}
+
+#else
+void tcg_gen_g2h_i32(TCGv_ptr ret, TCGv_i32 arg)
+{
+}
+void tcg_gen_g2h_i64(TCGv_ptr ret, TCGv_i64 arg)
+{
+}
+void tcg_gen_h2g_i32(TCGv_i32 ret, TCGv_ptr arg)
+{
+}
+void tcg_gen_h2g_i64(TCGv_i64 ret, TCGv_ptr arg)
+{
+}
+#endif
+
+static const FuncHelper func_helper_table[] = {
+    { .func = "memset",
+      .helper = (helper_func)gen_helper_memset,
+      .type = TYPE_AAIP },
+    { .func = "memcpy",
+      .helper = (helper_func)gen_helper_memcpy,
+      .type = TYPE_AAAP },
+    { .func = "strncpy",
+      .helper = (helper_func)gen_helper_strncpy,
+      .type = TYPE_AAAP },
+    { .func = "memcmp",
+      .helper = (helper_func)gen_helper_memcmp,
+      .type = TYPE_IAAP },
+    { .func = "strncmp",
+      .helper = (helper_func)gen_helper_strncmp,
+      .type = TYPE_IAAP },
+    { .func = "strcpy",
+      .helper = (helper_func)gen_helper_strcpy,
+      .type = TYPE_AAA },
+    { .func = "strcat",
+      .helper = (helper_func)gen_helper_strcat,
+      .type = TYPE_AAA },
+    { .func = "strcmp",
+      .helper = (helper_func)gen_helper_strcmp,
+      .type = TYPE_IAA },
+};
+/* p: iptr ; i: i32 ; a: ptr(address) */
+void gen_native_call_i32(const char *func_name, TCGv_i32 ret, TCGv_i32 arg1,
+                         TCGv_i32 arg2, TCGv_i32 arg3)
+{
+    TCGv_ptr arg1_ptr = tcg_temp_new_ptr();
+    TCGv_ptr arg2_ptr = tcg_temp_new_ptr();
+    TCGv_ptr arg3_ptr = tcg_temp_new_ptr();
+    TCGv_ptr ret_ptr = tcg_temp_new_ptr();
+    unsigned int i;
+    for (i = 0; i < sizeof(func_helper_table) / sizeof(FuncHelper); i++) {
+        if (strcmp(func_name, func_helper_table[i].func) == 0) {
+            break;
+        }
+    }
+    g_assert(i < sizeof(func_helper_table) / sizeof(FuncHelper));
+    switch (func_helper_table[i].type) {
+    case TYPE_AAIP:
+        tcg_gen_g2h_i32(arg1_ptr, arg1);
+        tcg_gen_ext_i32_ptr(arg3_ptr, arg3);
+        ((helper_ppip)(func_helper_table[i].helper))(ret_ptr, arg1_ptr, arg2,
+                                                     arg3_ptr);
+        goto ret_ptr;
+    case TYPE_AAAP:
+        tcg_gen_g2h_i32(arg1_ptr, arg1);
+        tcg_gen_g2h_i32(arg2_ptr, arg2);
+        tcg_gen_ext_i32_ptr(arg3_ptr, arg3);
+        ((helper_pppp)(func_helper_table[i].helper))(ret_ptr, arg1_ptr,
+                                                     arg2_ptr, arg3_ptr);
+        goto ret_ptr;
+    case TYPE_IAAP:
+        tcg_gen_g2h_i32(arg1_ptr, arg1);
+        tcg_gen_g2h_i32(arg2_ptr, arg2);
+        tcg_gen_ext_i32_ptr(arg3_ptr, arg3);
+        ((helper_ippp)(func_helper_table[i].helper))(ret, arg1_ptr, arg2_ptr,
+                                                     arg3_ptr);
+        return;
+    case TYPE_AAA:
+        tcg_gen_g2h_i32(arg1_ptr, arg1);
+        tcg_gen_g2h_i32(arg2_ptr, arg2);
+        ((helper_ppp)(func_helper_table[i].helper))(ret_ptr, arg1_ptr,
+                                                    arg2_ptr);
+        goto ret_ptr;
+    case TYPE_IAA:
+        tcg_gen_g2h_i32(arg1_ptr, arg1);
+        tcg_gen_g2h_i32(arg2_ptr, arg2);
+        ((helper_ipp)(func_helper_table[i].helper))(ret, arg1_ptr, arg2_ptr);
+        return;
+    default:
+        g_assert_not_reached();
+    }
+ret_ptr:
+    tcg_gen_h2g_i32(ret, ret_ptr);
+    return;
+}
+
+void gen_native_call_i64(const char *func_name, TCGv_i64 ret, TCGv_i64 arg1,
+                         TCGv_i64 arg2, TCGv_i64 arg3)
+{
+    TCGv_ptr arg1_ptr = tcg_temp_new_ptr();
+    TCGv_ptr arg2_ptr = tcg_temp_new_ptr();
+    TCGv_ptr arg3_ptr = tcg_temp_new_ptr();
+    TCGv_ptr ret_ptr = tcg_temp_new_ptr();
+    TCGv_i32 arg2_i32, ret_i32 = tcg_temp_new_i32();
+    unsigned int i;
+    for (i = 0; i < sizeof(func_helper_table) / sizeof(FuncHelper); i++) {
+        if (strcmp(func_name, func_helper_table[i].func) == 0) {
+            break;
+        }
+    }
+    g_assert(i < sizeof(func_helper_table) / sizeof(FuncHelper));
+
+    switch (func_helper_table[i].type) {
+    case TYPE_AAIP:
+        tcg_gen_g2h_i64(arg1_ptr, arg1);
+        arg2_i32 = tcg_temp_new_i32();
+        tcg_gen_extrl_i64_i32(arg2_i32, arg2);
+        tcg_gen_trunc_i64_ptr(arg3_ptr, arg3);
+        ((helper_ppip)(func_helper_table[i].helper))(ret_ptr, arg1_ptr,
+                                                     arg2_i32, arg3_ptr);
+        goto ret_ptr;
+    case TYPE_AAAP:
+        tcg_gen_g2h_i64(arg1_ptr, arg1);
+        tcg_gen_g2h_i64(arg2_ptr, arg2);
+        tcg_gen_trunc_i64_ptr(arg3_ptr, arg3);
+        ((helper_pppp)(func_helper_table[i].helper))(ret_ptr, arg1_ptr,
+                                                     arg2_ptr, arg3_ptr);
+        goto ret_ptr;
+    case TYPE_IAAP:
+        tcg_gen_g2h_i64(arg1_ptr, arg1);
+        tcg_gen_g2h_i64(arg2_ptr, arg2);
+        tcg_gen_trunc_i64_ptr(arg3_ptr, arg3);
+        ((helper_ippp)(func_helper_table[i].helper))(ret_i32, arg1_ptr,
+                                                     arg2_ptr, arg3_ptr);
+        goto ret_i32;
+    case TYPE_AAA:
+        tcg_gen_g2h_i64(arg1_ptr, arg1);
+        tcg_gen_g2h_i64(arg2_ptr, arg2);
+        ((helper_ppp)(func_helper_table[i].helper))(ret_ptr, arg1_ptr,
+                                                    arg2_ptr);
+        goto ret_ptr;
+    case TYPE_IAA:
+        tcg_gen_g2h_i64(arg1_ptr, arg1);
+        tcg_gen_g2h_i64(arg2_ptr, arg2);
+        ((helper_ipp)(func_helper_table[i].helper))(ret_i32, arg1_ptr,
+                                                    arg2_ptr);
+        goto ret_i32;
+    default:
+        g_assert_not_reached();
+    }
+ret_ptr:
+    tcg_gen_h2g_i64(ret, ret_ptr);
+    return;
+ret_i32:
+    tcg_gen_extu_i32_i64(ret, ret_i32);
+    return;
+}