diff mbox series

[RFC,v3,07/34] Hexagon (target/hexagon) scalar core helpers

Message ID 1597765847-16637-8-git-send-email-tsimpson@quicinc.com (mailing list archive)
State New, archived
Headers show
Series Hexagon patch series | expand

Commit Message

Taylor Simpson Aug. 18, 2020, 3:50 p.m. UTC
The majority of helpers are generated.  Define the helper functions needed
then include the generated file

Signed-off-by: Taylor Simpson <tsimpson@quicinc.com>
---
 target/hexagon/helper.h    |  33 ++++
 target/hexagon/op_helper.c | 368 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 401 insertions(+)
 create mode 100644 target/hexagon/helper.h
 create mode 100644 target/hexagon/op_helper.c

Comments

Richard Henderson Aug. 26, 2020, 2:16 p.m. UTC | #1
On 8/18/20 8:50 AM, Taylor Simpson wrote:
> The majority of helpers are generated.  Define the helper functions needed
> then include the generated file
> 
> Signed-off-by: Taylor Simpson <tsimpson@quicinc.com>
> ---
>  target/hexagon/helper.h    |  33 ++++
>  target/hexagon/op_helper.c | 368 +++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 401 insertions(+)
>  create mode 100644 target/hexagon/helper.h
>  create mode 100644 target/hexagon/op_helper.c
> 
> diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h
> new file mode 100644
> index 0000000..48b1917
> --- /dev/null
> +++ b/target/hexagon/helper.h
> @@ -0,0 +1,33 @@
> +/*
> + *  Copyright(c) 2019-2020 Qualcomm Innovation Center, Inc. All Rights Reserved.
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +DEF_HELPER_2(raise_exception, noreturn, env, i32)
> +DEF_HELPER_1(debug_start_packet, void, env)
> +DEF_HELPER_3(debug_check_store_width, void, env, int, int)
> +DEF_HELPER_3(debug_commit_end, void, env, int, int)
> +DEF_HELPER_3(merge_inflight_store1s, s32, env, s32, s32)
> +DEF_HELPER_3(merge_inflight_store1u, s32, env, s32, s32)
> +DEF_HELPER_3(merge_inflight_store2s, s32, env, s32, s32)
> +DEF_HELPER_3(merge_inflight_store2u, s32, env, s32, s32)
> +DEF_HELPER_3(merge_inflight_store4s, s32, env, s32, s32)
> +DEF_HELPER_3(merge_inflight_store4u, s32, env, s32, s32)
> +DEF_HELPER_3(merge_inflight_store8u, s64, env, s32, s64)

Please use DEF_HELPER_FLAGS_N when possible.

You should be able to know when

(1) No exceptions are possible, and nothing is touched except the inputs.  In
this case use TCG_CALL_NO_RWG_SE.

(2) No exceptions are possible, and nothing in env is touched for which you
have created a tcg variable in hexagon_translate_init().  In this case use
TCG_CALL_NO_RWG.

(3) Exceptions are possible, and no tcg variables are touched on the
non-exceptional path.  In this case use TCG_CALL_NO_WG.

> +static inline void log_pred_write(CPUHexagonState *env, int pnum,
> +                                  target_ulong val)

You might be better off letting the compiler decide about inlining.

> +/*
> + * Handle mem_noshuf
> + *
> + * This occurs when there is a load that might need data forwarded
> + * from an inflight store in slot 1.  Note that the load and store
> + * might have different sizes, so we can't simply compare the
> + * addresses.  We merge only the bytes that overlap (if any).
> + */
> +static int64_t merge_bytes(CPUHexagonState *env, target_ulong load_addr,
> +                           int64_t load_data, uint32_t load_width)
> +{
> +    /* Don't do anything if slot 1 was cancelled */
> +    const int store_slot = 1;
> +    if (env->slot_cancelled & (1 << store_slot)) {
> +        return load_data;
> +    }
> +
> +    int store_width = env->mem_log_stores[store_slot].width;
> +    target_ulong store_addr = env->mem_log_stores[store_slot].va;
> +    union {
> +        uint8_t bytes[8];
> +        uint32_t data32;
> +        uint64_t data64;
> +    } retdata, storedata;

Red flag here.  This is assuming that the host and the target are both
little-endian.

> +    int bigmask = ((-load_width) & (-store_width));
> +    if ((load_addr & bigmask) != (store_addr & bigmask)) {

Huh?  This doesn't detect overlap.  Counter example:

  load_addr = 0, load_width = 4,
  store_addr = 1, store_width = 1.

  bigmask = -4 & -1 -> -4.

  (0 & -4) != (1 & -4) -> 0 != 1


> +    while ((i < load_width) && (j < store_width)) {
> +        retdata.bytes[i] = storedata.bytes[j];
> +        i++;
> +        j++;
> +    }
> +    return retdata.data64;

This most definitely requires host-endian adjustment.

> +/* Helpful for printing intermediate values within instructions */
> +void HELPER(debug_value)(CPUHexagonState *env, int32_t value)
> +{
> +    HEX_DEBUG_LOG("value = 0x%x\n", value);
> +}
> +
> +void HELPER(debug_value_i64)(CPUHexagonState *env, int64_t value)
> +{
> +    HEX_DEBUG_LOG("value_i64 = 0x%lx\n", value);
> +}

I think we need to lose all of this.
There are other ways to debug TCG.


r~
Taylor Simpson Aug. 26, 2020, 11:52 p.m. UTC | #2
> -----Original Message-----
> From: Richard Henderson <richard.henderson@linaro.org>
> Sent: Wednesday, August 26, 2020 8:17 AM
> To: Taylor Simpson <tsimpson@quicinc.com>; qemu-devel@nongnu.org
> Cc: philmd@redhat.com; laurent@vivier.eu; riku.voipio@iki.fi;
> aleksandar.m.mail@gmail.com; ale@rev.ng
> Subject: Re: [RFC PATCH v3 07/34] Hexagon (target/hexagon) scalar core
> helpers
>
> > +DEF_HELPER_3(merge_inflight_store8u, s64, env, s32, s64)
>
> Please use DEF_HELPER_FLAGS_N when possible.

OK

> > +static inline void log_pred_write(CPUHexagonState *env, int pnum,
> > +                                  target_ulong val)
>
> You might be better off letting the compiler decide about inlining.

Isn't "inline" just a hint to the compiler?

> > +    union {
> > +        uint8_t bytes[8];
> > +        uint32_t data32;
> > +        uint64_t data64;
> > +    } retdata, storedata;
>
> Red flag here.  This is assuming that the host and the target are both
> little-endian.

OK

> > +    int bigmask = ((-load_width) & (-store_width));
> > +    if ((load_addr & bigmask) != (store_addr & bigmask)) {
>
> Huh?  This doesn't detect overlap.  Counter example:
>
>   load_addr = 0, load_width = 4,
>   store_addr = 1, store_width = 1.
>
>   bigmask = -4 & -1 -> -4.
>
>   (0 & -4) != (1 & -4) -> 0 != 1

OK

> > +    while ((i < load_width) && (j < store_width)) {
> > +        retdata.bytes[i] = storedata.bytes[j];
> > +        i++;
> > +        j++;
> > +    }
> > +    return retdata.data64;
>
> This most definitely requires host-endian adjustment.

OK

> > +/* Helpful for printing intermediate values within instructions */
> > +void HELPER(debug_value)(CPUHexagonState *env, int32_t value)
> > +{
> > +    HEX_DEBUG_LOG("value = 0x%x\n", value);
> > +}
> > +
> > +void HELPER(debug_value_i64)(CPUHexagonState *env, int64_t value)
> > +{
> > +    HEX_DEBUG_LOG("value_i64 = 0x%lx\n", value);
> > +}
>
> I think we need to lose all of this.
> There are other ways to debug TCG.

OK
diff mbox series

Patch

diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h
new file mode 100644
index 0000000..48b1917
--- /dev/null
+++ b/target/hexagon/helper.h
@@ -0,0 +1,33 @@ 
+/*
+ *  Copyright(c) 2019-2020 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+DEF_HELPER_2(raise_exception, noreturn, env, i32)
+DEF_HELPER_1(debug_start_packet, void, env)
+DEF_HELPER_3(debug_check_store_width, void, env, int, int)
+DEF_HELPER_3(debug_commit_end, void, env, int, int)
+DEF_HELPER_3(merge_inflight_store1s, s32, env, s32, s32)
+DEF_HELPER_3(merge_inflight_store1u, s32, env, s32, s32)
+DEF_HELPER_3(merge_inflight_store2s, s32, env, s32, s32)
+DEF_HELPER_3(merge_inflight_store2u, s32, env, s32, s32)
+DEF_HELPER_3(merge_inflight_store4s, s32, env, s32, s32)
+DEF_HELPER_3(merge_inflight_store4u, s32, env, s32, s32)
+DEF_HELPER_3(merge_inflight_store8u, s64, env, s32, s64)
+
+#include "helper_protos_generated.h"
+
+DEF_HELPER_2(debug_value, void, env, s32)
+DEF_HELPER_2(debug_value_i64, void, env, s64)
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
new file mode 100644
index 0000000..f86d45b
--- /dev/null
+++ b/target/hexagon/op_helper.c
@@ -0,0 +1,368 @@ 
+/*
+ *  Copyright(c) 2019-2020 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <math.h>
+#include "qemu/osdep.h"
+#include "qemu.h"
+#include "exec/helper-proto.h"
+#include "cpu.h"
+#include "internal.h"
+#include "macros.h"
+#include "arch.h"
+#include "fma_emu.h"
+#include "conv_emu.h"
+
+/* Exceptions processing helpers */
+static void QEMU_NORETURN do_raise_exception_err(CPUHexagonState *env,
+                                                 uint32_t exception,
+                                                 uintptr_t pc)
+{
+    CPUState *cs = CPU(hexagon_env_get_cpu(env));
+    qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception);
+    cs->exception_index = exception;
+    cpu_loop_exit_restore(cs, pc);
+}
+
+void HELPER(raise_exception)(CPUHexagonState *env, uint32_t exception)
+{
+    do_raise_exception_err(env, exception, 0);
+}
+
+static inline void log_reg_write(CPUHexagonState *env, int rnum,
+                                 target_ulong val, uint32_t slot)
+{
+    HEX_DEBUG_LOG("log_reg_write[%d] = " TARGET_FMT_ld " (0x" TARGET_FMT_lx ")",
+                  rnum, val, val);
+    if (env->slot_cancelled & (1 << slot)) {
+        HEX_DEBUG_LOG(" CANCELLED");
+    }
+    if (val == env->gpr[rnum]) {
+        HEX_DEBUG_LOG(" NO CHANGE");
+    }
+    HEX_DEBUG_LOG("\n");
+    if (!(env->slot_cancelled & (1 << slot))) {
+        env->new_value[rnum] = val;
+#if HEX_DEBUG
+        /* Do this so HELPER(debug_commit_end) will know */
+        env->reg_written[rnum] = 1;
+#endif
+    }
+}
+
+static __attribute__((unused))
+inline void log_reg_write_pair(CPUHexagonState *env, int rnum,
+                                      int64_t val, uint32_t slot)
+{
+    HEX_DEBUG_LOG("log_reg_write_pair[%d:%d] = %ld\n", rnum + 1, rnum, val);
+    log_reg_write(env, rnum, val & 0xFFFFFFFF, slot);
+    log_reg_write(env, rnum + 1, (val >> 32) & 0xFFFFFFFF, slot);
+}
+
+static inline void log_pred_write(CPUHexagonState *env, int pnum,
+                                  target_ulong val)
+{
+    HEX_DEBUG_LOG("log_pred_write[%d] = " TARGET_FMT_ld
+                  " (0x" TARGET_FMT_lx ")\n",
+                  pnum, val, val);
+
+    /* Multiple writes to the same preg are and'ed together */
+    if (env->pred_written & (1 << pnum)) {
+        env->new_pred_value[pnum] &= val & 0xff;
+    } else {
+        env->new_pred_value[pnum] = val & 0xff;
+        env->pred_written |= 1 << pnum;
+    }
+}
+
+static inline void log_store32(CPUHexagonState *env, target_ulong addr,
+                               target_ulong val, int width, int slot)
+{
+    HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx ", " TARGET_FMT_ld
+                  " [0x" TARGET_FMT_lx "])\n",
+                  width, addr, val, val);
+    env->mem_log_stores[slot].va = addr;
+    env->mem_log_stores[slot].width = width;
+    env->mem_log_stores[slot].data32 = val;
+}
+
+static inline void log_store64(CPUHexagonState *env, target_ulong addr,
+                               int64_t val, int width, int slot)
+{
+    HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx ", %ld [0x%lx])\n",
+                   width, addr, val, val);
+    env->mem_log_stores[slot].va = addr;
+    env->mem_log_stores[slot].width = width;
+    env->mem_log_stores[slot].data64 = val;
+}
+
+static inline void write_new_pc(CPUHexagonState *env, target_ulong addr)
+{
+    HEX_DEBUG_LOG("write_new_pc(0x" TARGET_FMT_lx ")\n", addr);
+
+    /*
+     * If more than one branch is taken in a packet, only the first one
+     * is actually done.
+     */
+    if (env->branch_taken) {
+        HEX_DEBUG_LOG("INFO: multiple branches taken in same packet, "
+                      "ignoring the second one\n");
+    } else {
+        fCHECK_PCALIGN(addr);
+        env->branch_taken = 1;
+        env->next_PC = addr;
+    }
+}
+
+/* Handy place to set a breakpoint */
+void HELPER(debug_start_packet)(CPUHexagonState *env)
+{
+    HEX_DEBUG_LOG("Start packet: pc = 0x" TARGET_FMT_lx "\n",
+                  env->gpr[HEX_REG_PC]);
+
+    int i;
+    for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) {
+        env->reg_written[i] = 0;
+    }
+}
+
+static inline int32_t new_pred_value(CPUHexagonState *env, int pnum)
+{
+    return env->new_pred_value[pnum];
+}
+
+/* Checks for bookkeeping errors between disassembly context and runtime */
+void HELPER(debug_check_store_width)(CPUHexagonState *env, int slot, int check)
+{
+    if (env->mem_log_stores[slot].width != check) {
+        HEX_DEBUG_LOG("ERROR: %d != %d\n",
+                      env->mem_log_stores[slot].width, check);
+        g_assert_not_reached();
+    }
+}
+
+static void print_store(CPUHexagonState *env, int slot)
+{
+    if (!(env->slot_cancelled & (1 << slot))) {
+        size1u_t width = env->mem_log_stores[slot].width;
+        if (width == 1) {
+            size4u_t data = env->mem_log_stores[slot].data32 & 0xff;
+            HEX_DEBUG_LOG("\tmemb[0x" TARGET_FMT_lx "] = %d (0x%02x)\n",
+                          env->mem_log_stores[slot].va, data, data);
+        } else if (width == 2) {
+            size4u_t data = env->mem_log_stores[slot].data32 & 0xffff;
+            HEX_DEBUG_LOG("\tmemh[0x" TARGET_FMT_lx "] = %d (0x%04x)\n",
+                          env->mem_log_stores[slot].va, data, data);
+        } else if (width == 4) {
+            size4u_t data = env->mem_log_stores[slot].data32;
+            HEX_DEBUG_LOG("\tmemw[0x" TARGET_FMT_lx "] = %d (0x%08x)\n",
+                          env->mem_log_stores[slot].va, data, data);
+        } else if (width == 8) {
+            HEX_DEBUG_LOG("\tmemd[0x" TARGET_FMT_lx "] = %lu (0x%016lx)\n",
+                          env->mem_log_stores[slot].va,
+                          env->mem_log_stores[slot].data64,
+                          env->mem_log_stores[slot].data64);
+        } else {
+            HEX_DEBUG_LOG("\tBad store width %d\n", width);
+            g_assert_not_reached();
+        }
+    }
+}
+
+/* This function is a handy place to set a breakpoint */
+void HELPER(debug_commit_end)(CPUHexagonState *env, int has_st0, int has_st1)
+{
+    bool reg_printed = false;
+    bool pred_printed = false;
+    int i;
+
+    HEX_DEBUG_LOG("Packet committed: pc = 0x" TARGET_FMT_lx "\n",
+                  env->this_PC);
+    HEX_DEBUG_LOG("slot_cancelled = %d\n", env->slot_cancelled);
+
+    for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) {
+        if (env->reg_written[i]) {
+            if (!reg_printed) {
+                HEX_DEBUG_LOG("Regs written\n");
+                reg_printed = true;
+            }
+            HEX_DEBUG_LOG("\tr%d = " TARGET_FMT_ld " (0x" TARGET_FMT_lx ")\n",
+                          i, env->new_value[i], env->new_value[i]);
+        }
+    }
+
+    for (i = 0; i < NUM_PREGS; i++) {
+        if (env->pred_written & (1 << i)) {
+            if (!pred_printed) {
+                HEX_DEBUG_LOG("Predicates written\n");
+                pred_printed = true;
+            }
+            HEX_DEBUG_LOG("\tp%d = 0x" TARGET_FMT_lx "\n",
+                          i, env->new_pred_value[i]);
+        }
+    }
+
+    if (has_st0 || has_st1) {
+        HEX_DEBUG_LOG("Stores\n");
+        if (has_st0) {
+            print_store(env, 0);
+        }
+        if (has_st1) {
+            print_store(env, 1);
+        }
+    }
+
+    HEX_DEBUG_LOG("Next PC = 0x%x\n", env->next_PC);
+    HEX_DEBUG_LOG("Exec counters: pkt = " TARGET_FMT_lx
+                  ", insn = " TARGET_FMT_lx
+                  "\n",
+                  env->gpr[HEX_REG_QEMU_PKT_CNT],
+                  env->gpr[HEX_REG_QEMU_INSN_CNT]);
+
+}
+
+/*
+ * Handle mem_noshuf
+ *
+ * This occurs when there is a load that might need data forwarded
+ * from an inflight store in slot 1.  Note that the load and store
+ * might have different sizes, so we can't simply compare the
+ * addresses.  We merge only the bytes that overlap (if any).
+ */
+static int64_t merge_bytes(CPUHexagonState *env, target_ulong load_addr,
+                           int64_t load_data, uint32_t load_width)
+{
+    /* Don't do anything if slot 1 was cancelled */
+    const int store_slot = 1;
+    if (env->slot_cancelled & (1 << store_slot)) {
+        return load_data;
+    }
+
+    int store_width = env->mem_log_stores[store_slot].width;
+    target_ulong store_addr = env->mem_log_stores[store_slot].va;
+    union {
+        uint8_t bytes[8];
+        uint32_t data32;
+        uint64_t data64;
+    } retdata, storedata;
+    int bigmask = ((-load_width) & (-store_width));
+    if ((load_addr & bigmask) != (store_addr & bigmask)) {
+        /* no overlap */
+        return load_data;
+    }
+    retdata.data64 = load_data;
+
+    if (store_width == 1 || store_width == 2 || store_width == 4) {
+        storedata.data32 = env->mem_log_stores[store_slot].data32;
+    } else if (store_width == 8) {
+        storedata.data64 = env->mem_log_stores[store_slot].data64;
+    } else {
+        g_assert_not_reached();
+    }
+    int i, j;
+    i = store_addr & (load_width - 1);
+    j = load_addr & (store_width - 1);
+    while ((i < load_width) && (j < store_width)) {
+        retdata.bytes[i] = storedata.bytes[j];
+        i++;
+        j++;
+    }
+    return retdata.data64;
+}
+
+#define MERGE_INFLIGHT(NAME, RET, IN_TYPE, OUT_TYPE, SIZE) \
+RET HELPER(NAME)(CPUHexagonState *env, int32_t addr, IN_TYPE data) \
+{ \
+    return (OUT_TYPE)merge_bytes(env, addr, data, SIZE); \
+}
+
+MERGE_INFLIGHT(merge_inflight_store1s, int32_t, int32_t,  int8_t,  1)
+MERGE_INFLIGHT(merge_inflight_store1u, int32_t, int32_t, uint8_t,  1)
+MERGE_INFLIGHT(merge_inflight_store2s, int32_t, int32_t,  int16_t, 2)
+MERGE_INFLIGHT(merge_inflight_store2u, int32_t, int32_t, uint16_t, 2)
+MERGE_INFLIGHT(merge_inflight_store4s, int32_t, int32_t,  int32_t, 4)
+MERGE_INFLIGHT(merge_inflight_store4u, int32_t, int32_t, uint32_t, 4)
+MERGE_INFLIGHT(merge_inflight_store8u, int64_t, int64_t,  int64_t, 8)
+
+#define CHECK_NOSHUF(DST, VA, SZ, SIGN) \
+    do { \
+        if (slot == 0 && env->pkt_has_store_s1) { \
+            DST = HELPER(merge_inflight_store##SZ##SIGN)(env, VA, DST); \
+        } \
+    } while (0)
+
+static inline uint8_t mem_load1(CPUHexagonState *env, uint32_t slot,
+                                target_ulong vaddr)
+{
+    uint8_t retval;
+    get_user_u8(retval, vaddr);
+    CHECK_NOSHUF(retval, vaddr, 1, u);
+    return retval;
+}
+
+static inline uint16_t mem_load2(CPUHexagonState *env, uint32_t slot,
+                                 target_ulong vaddr)
+{
+    uint16_t retval;
+    get_user_u16(retval, vaddr);
+    CHECK_NOSHUF(retval, vaddr, 2, u);
+    return retval;
+}
+
+static inline uint32_t mem_load4(CPUHexagonState *env, uint32_t slot,
+                                 target_ulong vaddr)
+{
+    uint32_t retval;
+    get_user_u32(retval, vaddr);
+    CHECK_NOSHUF(retval, vaddr, 4, u);
+    return retval;
+}
+
+static inline uint64_t mem_load8(CPUHexagonState *env, uint32_t slot,
+                                 target_ulong vaddr)
+{
+    uint64_t retval;
+    get_user_u64(retval, vaddr);
+    CHECK_NOSHUF(retval, vaddr, 8, u);
+    return retval;
+}
+
+/* Helpful for printing intermediate values within instructions */
+void HELPER(debug_value)(CPUHexagonState *env, int32_t value)
+{
+    HEX_DEBUG_LOG("value = 0x%x\n", value);
+}
+
+void HELPER(debug_value_i64)(CPUHexagonState *env, int64_t value)
+{
+    HEX_DEBUG_LOG("value_i64 = 0x%lx\n", value);
+}
+
+static void cancel_slot(CPUHexagonState *env, uint32_t slot)
+{
+    HEX_DEBUG_LOG("Slot %d cancelled\n", slot);
+    env->slot_cancelled |= (1 << slot);
+}
+
+/* These macros can be referenced in the generated helper functions */
+#define warn(...) /* Nothing */
+#define fatal(...) g_assert_not_reached();
+
+#define BOGUS_HELPER(tag) \
+    printf("ERROR: bogus helper: " #tag "\n")
+
+#include "helper_funcs_generated.h"
+