All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v1 0/3] tcg: add tcg_gen_print helper
@ 2025-12-26 16:51 Chao Liu
  2025-12-26 16:51 ` [RFC PATCH v1 1/3] " Chao Liu
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Chao Liu @ 2025-12-26 16:51 UTC (permalink / raw)
  To: richard.henderson, pbonzini; +Cc: qemu-devel, hust-os-kernel-patches, Chao Liu

Hi,

This series adds a printf-style helper for TCG to simplify debugging TB
translations.

The first patch implements tcg_gen_print plus the runtime support code that
formats the arguments when the helper is invoked.

The second patch updates scripts/checkpatch.pl to warn if a patch still
contains tcg_gen_print() so that developers do not accidentally merge
debug prints.

The final patch documents the helper in docs/devel/tcg-ops.rst.

Usage:

```
    tcg_gen_print("const value = 0x%lx\n"
                  TCG_PRINT_ARG_I64, tcg_constant_i64(0xdead),
                  TCG_PRINT_ARG_END);
```

P.S. I think this debugging feature will be well-received. :)

Finally, happy new year to all ~

Thank,
Chao

Chao Liu (3):
  tcg: add tcg_gen_print helper
  scripts: warn on tcg_gen_print usage to the checkpatch.pl
  docs: add documentation for the tcg_gen_print helper to tcg-ops

 accel/tcg/tcg-runtime.c     | 187 ++++++++++++++++++++++++++++++++++++
 accel/tcg/tcg-runtime.h     |   1 +
 docs/devel/tcg-ops.rst      |  33 +++++++
 include/tcg/tcg-op-common.h |   2 +
 include/tcg/tcg-print.h     |  45 +++++++++
 scripts/checkpatch.pl       |   5 +
 tcg/tcg-op.c                |  89 +++++++++++++++++
 7 files changed, 362 insertions(+)
 create mode 100644 include/tcg/tcg-print.h

--
2.52.0



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [RFC PATCH v1 1/3] tcg: add tcg_gen_print helper
  2025-12-26 16:51 [RFC PATCH v1 0/3] tcg: add tcg_gen_print helper Chao Liu
@ 2025-12-26 16:51 ` Chao Liu
  2025-12-26 16:51 ` [RFC PATCH v1 2/3] scripts: warn on tcg_gen_print usage to the checkpatch.pl Chao Liu
  2025-12-26 16:51 ` [RFC PATCH v1 3/3] docs: add documentation for the tcg_gen_print helper to tcg-ops Chao Liu
  2 siblings, 0 replies; 4+ messages in thread
From: Chao Liu @ 2025-12-26 16:51 UTC (permalink / raw)
  To: richard.henderson, pbonzini; +Cc: qemu-devel, hust-os-kernel-patches, Chao Liu

Add tcg_gen_print() so translators can dump formatted state without
hand writing helpers. The helper builds a typed descriptor for up to
four arguments and emits a runtime call that prints via qemu_printf().

Example:

```
    tcg_gen_print("const value = 0x%lx\n"
                  TCG_PRINT_ARG_I64, tcg_constant_i64(0xdead),
                  TCG_PRINT_ARG_END);
```

Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
---
 accel/tcg/tcg-runtime.c     | 187 ++++++++++++++++++++++++++++++++++++
 accel/tcg/tcg-runtime.h     |   1 +
 include/tcg/tcg-op-common.h |   2 +
 include/tcg/tcg-print.h     |  45 +++++++++
 tcg/tcg-op.c                |  89 +++++++++++++++++
 5 files changed, 324 insertions(+)
 create mode 100644 include/tcg/tcg-print.h

diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c
index fa7ed9739c..e75c1d5702 100644
--- a/accel/tcg/tcg-runtime.c
+++ b/accel/tcg/tcg-runtime.c
@@ -23,9 +23,11 @@
  */
 #include "qemu/osdep.h"
 #include "qemu/host-utils.h"
+#include "qemu/qemu-print.h"
 #include "exec/cpu-common.h"
 #include "exec/helper-proto-common.h"
 #include "accel/tcg/getpc.h"
+#include "tcg/tcg-print.h"
 
 #define HELPER_H  "accel/tcg/tcg-runtime.h"
 #include "exec/helper-info.c.inc"
@@ -148,3 +150,188 @@ void HELPER(exit_atomic)(CPUArchState *env)
 {
     cpu_loop_exit_atomic(env_cpu(env), GETPC());
 }
+
+static void tcg_print_skip_format(const char **pfmt)
+{
+    const char *p = *pfmt;
+
+    while (*p) {
+        char c = *p;
+
+        if (c == 'l' || c == 'h' || c == 'z' || c == 't' ||
+            c == 'j' || c == '*') {
+            p++;
+            continue;
+        }
+        if (strchr("diuoxXp", c) || c == '%') {
+            p++;
+            break;
+        }
+        p++;
+    }
+    *pfmt = p;
+}
+
+static bool tcg_print_emit_arg(GString *out, const char **pfmt,
+                               TCGPrintArgType type, uint64_t value)
+{
+    const char *p = *pfmt;
+    char prefix[32];
+    size_t prelen = 0;
+
+    if (*p == '\0') {
+        tcg_print_skip_format(pfmt);
+        return false;
+    }
+
+    prefix[prelen++] = '%';
+    while (*p) {
+        char c = *p;
+
+        if (c == '*') {
+            tcg_print_skip_format(pfmt);
+            return false;
+        }
+        if (c == 'l' || c == 'h' || c == 'z' || c == 't' || c == 'j') {
+            p++;
+            continue;
+        }
+        if (strchr("diuoxXp", c)) {
+            char fmtbuf[64];
+            size_t len;
+
+            if (prelen >= sizeof(fmtbuf)) {
+                tcg_print_skip_format(pfmt);
+                return false;
+            }
+            memcpy(fmtbuf, prefix, prelen);
+            len = prelen;
+            if (c != 'p' &&
+                (type == TCG_PRINT_ARG_I64 ||
+                (type == TCG_PRINT_ARG_PTR &&
+                 sizeof(uintptr_t) == 8))) {
+                if (len + 2 >= sizeof(fmtbuf)) {
+                    tcg_print_skip_format(pfmt);
+                    return false;
+                }
+                fmtbuf[len++] = 'l';
+                fmtbuf[len++] = 'l';
+            }
+            if (len + 1 >= sizeof(fmtbuf)) {
+                tcg_print_skip_format(pfmt);
+                return false;
+            }
+            fmtbuf[len++] = c;
+            fmtbuf[len] = '\0';
+
+            char tmp[128];
+            bool ok = true;
+
+            switch (c) {
+            case 'd':
+            case 'i':
+                if (type == TCG_PRINT_ARG_I64 ||
+                    (type == TCG_PRINT_ARG_PTR &&
+                     sizeof(uintptr_t) == 8)) {
+                    g_snprintf(tmp, sizeof(tmp), fmtbuf,
+                               (long long)(int64_t)value);
+                } else if (type == TCG_PRINT_ARG_I32 ||
+                           (type == TCG_PRINT_ARG_PTR &&
+                            sizeof(uintptr_t) == 4)) {
+                    g_snprintf(tmp, sizeof(tmp), fmtbuf,
+                               (int)(int32_t)value);
+                } else {
+                    ok = false;
+                }
+                break;
+            case 'u':
+            case 'o':
+            case 'x':
+            case 'X':
+                if (type == TCG_PRINT_ARG_I64 ||
+                    (type == TCG_PRINT_ARG_PTR &&
+                     sizeof(uintptr_t) == 8)) {
+                    g_snprintf(tmp, sizeof(tmp), fmtbuf,
+                               (unsigned long long)value);
+                } else if (type == TCG_PRINT_ARG_I32 ||
+                           (type == TCG_PRINT_ARG_PTR &&
+                            sizeof(uintptr_t) == 4)) {
+                    g_snprintf(tmp, sizeof(tmp), fmtbuf,
+                               (unsigned int)(uint32_t)value);
+                } else {
+                    ok = false;
+                }
+                break;
+            case 'p':
+                g_snprintf(tmp, sizeof(tmp), fmtbuf,
+                           (void *)(uintptr_t)value);
+                break;
+            default:
+                ok = false;
+                break;
+            }
+
+            if (!ok) {
+                tcg_print_skip_format(pfmt);
+                return false;
+            }
+            g_string_append(out, tmp);
+            *pfmt = p + 1;
+            return true;
+        }
+        if (prelen + 1 >= sizeof(prefix)) {
+            tcg_print_skip_format(pfmt);
+            return false;
+        }
+        prefix[prelen++] = c;
+        p++;
+    }
+    tcg_print_skip_format(pfmt);
+    return false;
+}
+
+void HELPER(tcg_print)(void *fmt_ptr, uint32_t desc,
+                       uint64_t v0, uint64_t v1,
+                       uint64_t v2, uint64_t v3,
+                       uint64_t v4)
+{
+    const char *fmt = fmt_ptr;
+    uint64_t values[TCG_PRINT_MAX_ARGS] = { v0, v1, v2, v3, v4 };
+    TCGPrintArgType types[TCG_PRINT_MAX_ARGS];
+    GString *msg = g_string_new(NULL);
+    unsigned count = tcg_print_desc_count(desc);
+    unsigned i;
+    unsigned arg_index = 0;
+
+    g_assert(count <= TCG_PRINT_MAX_ARGS);
+
+    for (i = 0; i < count && i < TCG_PRINT_MAX_ARGS; i++) {
+        types[i] = tcg_print_desc_type(desc, i);
+    }
+
+    while (*fmt) {
+        if (*fmt != '%') {
+            g_string_append_c(msg, *fmt++);
+            continue;
+        }
+        fmt++;
+        if (*fmt == '%') {
+            g_string_append_c(msg, '%');
+            fmt++;
+            continue;
+        }
+        if (arg_index >= count) {
+            tcg_print_skip_format(&fmt);
+            g_string_append(msg, "<missing>");
+            continue;
+        }
+        if (!tcg_print_emit_arg(msg, &fmt, types[arg_index],
+                                 values[arg_index])) {
+            g_string_append(msg, "<fmt?>");
+        }
+        arg_index++;
+    }
+
+    qemu_printf("%s", msg->str);
+    g_string_free(msg, TRUE);
+}
diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h
index 8436599b9f..6b1f0c0b13 100644
--- a/accel/tcg/tcg-runtime.h
+++ b/accel/tcg/tcg-runtime.h
@@ -27,6 +27,7 @@ DEF_HELPER_FLAGS_1(ctpop_i64, TCG_CALL_NO_RWG_SE, i64, i64)
 DEF_HELPER_FLAGS_1(lookup_tb_ptr, TCG_CALL_NO_WG_SE, cptr, env)
 
 DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env)
+DEF_HELPER_FLAGS_7(tcg_print, 0, void, ptr, i32, i64, i64, i64, i64, i64)
 
 #ifndef IN_HELPER_PROTO
 /*
diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h
index f752ef440b..858137cf41 100644
--- a/include/tcg/tcg-op-common.h
+++ b/include/tcg/tcg-op-common.h
@@ -9,6 +9,7 @@
 #define TCG_TCG_OP_COMMON_H
 
 #include "tcg/tcg.h"
+#include "tcg/tcg-print.h"
 #include "exec/helper-proto-common.h"
 #include "exec/helper-gen-common.h"
 
@@ -77,6 +78,7 @@ void tcg_gen_lookup_and_goto_ptr(void);
 
 void tcg_gen_plugin_cb(unsigned from);
 void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo);
+void tcg_gen_print(const char *fmt, ...);
 
 /* 32 bit ops */
 
diff --git a/include/tcg/tcg-print.h b/include/tcg/tcg-print.h
new file mode 100644
index 0000000000..40a2dd9d40
--- /dev/null
+++ b/include/tcg/tcg-print.h
@@ -0,0 +1,45 @@
+/*
+ * Shared definitions for the TCG printf-style helper.
+ *
+ * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef TCG_TCG_PRINT_H
+#define TCG_TCG_PRINT_H
+
+#define TCG_PRINT_MAX_ARGS 5
+
+typedef enum TCGPrintArgType {
+    TCG_PRINT_ARG_END = 0,
+    TCG_PRINT_ARG_I32 = 1,
+    TCG_PRINT_ARG_I64 = 2,
+    TCG_PRINT_ARG_PTR = 3,
+} TCGPrintArgType;
+
+#define TCG_PRINT_DESC_COUNT_MASK 0xF
+#define TCG_PRINT_DESC_SHIFT 4
+#define TCG_PRINT_DESC_BITS_PER_ARG 3
+#define TCG_PRINT_DESC_TYPE_MASK ((1u << TCG_PRINT_DESC_BITS_PER_ARG) - 1)
+
+static inline unsigned tcg_print_desc_count(uint32_t desc)
+{
+    return desc & TCG_PRINT_DESC_COUNT_MASK;
+}
+
+static inline unsigned tcg_print_desc_type(uint32_t desc, unsigned index)
+{
+    return (desc >> (TCG_PRINT_DESC_SHIFT +
+                     index * TCG_PRINT_DESC_BITS_PER_ARG))
+        & TCG_PRINT_DESC_TYPE_MASK;
+}
+
+static inline uint32_t tcg_print_desc_add_type(uint32_t desc, unsigned index,
+                                               TCGPrintArgType type)
+{
+    return desc | ((uint32_t)type & TCG_PRINT_DESC_TYPE_MASK)
+        << (TCG_PRINT_DESC_SHIFT + index * TCG_PRINT_DESC_BITS_PER_ARG);
+}
+
+#endif /* TCG_TCG_PRINT_H */
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index ab7b409be6..44d50c84da 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -323,6 +323,95 @@ void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo)
     tcg_gen_op2(INDEX_op_plugin_mem_cb, 0, tcgv_i64_arg(addr), meminfo);
 }
 
+void tcg_gen_print(const char *fmt, ...)
+{
+    TCGTemp *temps[TCG_PRINT_MAX_ARGS];
+    TCGPrintArgType kinds[TCG_PRINT_MAX_ARGS];
+    TCGv_i64 values[TCG_PRINT_MAX_ARGS];
+    TCGv_i64 to_free[TCG_PRINT_MAX_ARGS];
+    int free_count = 0;
+    int count = 0;
+    va_list ap;
+    uint32_t desc;
+    TCGv_ptr fmt_ptr;
+    TCGv_i32 desc_arg;
+    TCGv_i64 zero;
+    int i;
+
+    QEMU_BUILD_BUG_ON(TCG_PRINT_MAX_ARGS != 5);
+
+    va_start(ap, fmt);
+    for (;;) {
+        int kind = va_arg(ap, int);
+
+        if (kind == TCG_PRINT_ARG_END) {
+            break;
+        }
+        g_assert(count < TCG_PRINT_MAX_ARGS);
+
+        switch (kind) {
+        case TCG_PRINT_ARG_I32:
+            temps[count] = tcgv_i32_temp(va_arg(ap, TCGv_i32));
+            break;
+        case TCG_PRINT_ARG_I64:
+            temps[count] = tcgv_i64_temp(va_arg(ap, TCGv_i64));
+            break;
+        case TCG_PRINT_ARG_PTR:
+            temps[count] = tcgv_ptr_temp(va_arg(ap, TCGv_ptr));
+            break;
+        default:
+            g_assert_not_reached();
+        }
+        kinds[count++] = kind;
+    }
+    va_end(ap);
+
+    desc = count;
+    for (i = 0; i < count; i++) {
+        desc = tcg_print_desc_add_type(desc, i, kinds[i]);
+    }
+
+    fmt_ptr = tcg_constant_ptr(fmt);
+    desc_arg = tcg_constant_i32(desc);
+    zero = tcg_constant_i64(0);
+
+    for (i = 0; i < count; i++) {
+        switch (kinds[i]) {
+        case TCG_PRINT_ARG_I32:
+            values[i] = tcg_temp_ebb_new_i64();
+            tcg_gen_extu_i32_i64(values[i], temp_tcgv_i32(temps[i]));
+            to_free[free_count++] = values[i];
+            break;
+        case TCG_PRINT_ARG_I64:
+            values[i] = temp_tcgv_i64(temps[i]);
+            break;
+        case TCG_PRINT_ARG_PTR:
+#if UINTPTR_MAX == UINT32_MAX
+            values[i] = tcg_temp_ebb_new_i64();
+            tcg_gen_extu_i32_i64(values[i],
+                                 (TCGv_i32)temp_tcgv_ptr(temps[i]));
+            to_free[free_count++] = values[i];
+#else
+            values[i] = temp_tcgv_i64(temps[i]);
+#endif
+            break;
+        default:
+            g_assert_not_reached();
+        }
+    }
+    for (; i < TCG_PRINT_MAX_ARGS; i++) {
+        values[i] = zero;
+    }
+
+    gen_helper_tcg_print(fmt_ptr, desc_arg,
+                         values[0], values[1], values[2], values[3],
+                         values[4]);
+
+    for (i = 0; i < free_count; i++) {
+        tcg_temp_free_i64(to_free[i]);
+    }
+}
+
 /* 32 bit ops */
 
 void tcg_gen_discard_i32(TCGv_i32 arg)
-- 
2.52.0



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [RFC PATCH v1 2/3] scripts: warn on tcg_gen_print usage to the checkpatch.pl
  2025-12-26 16:51 [RFC PATCH v1 0/3] tcg: add tcg_gen_print helper Chao Liu
  2025-12-26 16:51 ` [RFC PATCH v1 1/3] " Chao Liu
@ 2025-12-26 16:51 ` Chao Liu
  2025-12-26 16:51 ` [RFC PATCH v1 3/3] docs: add documentation for the tcg_gen_print helper to tcg-ops Chao Liu
  2 siblings, 0 replies; 4+ messages in thread
From: Chao Liu @ 2025-12-26 16:51 UTC (permalink / raw)
  To: richard.henderson, pbonzini; +Cc: qemu-devel, hust-os-kernel-patches, Chao Liu

Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
---
 scripts/checkpatch.pl | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 3a9557417f..43d3db9f38 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -1985,6 +1985,11 @@ sub process {
 			ERROR("adding a line without newline at end of file\n" . $herecurr);
 		}
 
+# check for tcg_gen_print is present in submitted patches
+		if ($rawline =~ /^\+.*\btcg_gen_print\s*\(/) {
+			WARN("tcg_gen_print(): debug-only, remove all calls before patch submission.\n" . $herecurr);
+		}
+
 # check for RCS/CVS revision markers
 		if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|\b)/) {
 			ERROR("CVS style keyword markers, these will _not_ be updated\n". $herecurr);
-- 
2.52.0



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [RFC PATCH v1 3/3] docs: add documentation for the tcg_gen_print helper to tcg-ops
  2025-12-26 16:51 [RFC PATCH v1 0/3] tcg: add tcg_gen_print helper Chao Liu
  2025-12-26 16:51 ` [RFC PATCH v1 1/3] " Chao Liu
  2025-12-26 16:51 ` [RFC PATCH v1 2/3] scripts: warn on tcg_gen_print usage to the checkpatch.pl Chao Liu
@ 2025-12-26 16:51 ` Chao Liu
  2 siblings, 0 replies; 4+ messages in thread
From: Chao Liu @ 2025-12-26 16:51 UTC (permalink / raw)
  To: richard.henderson, pbonzini; +Cc: qemu-devel, hust-os-kernel-patches, Chao Liu

Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
---
 docs/devel/tcg-ops.rst | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst
index f26b837a30..4b5ee3b560 100644
--- a/docs/devel/tcg-ops.rst
+++ b/docs/devel/tcg-ops.rst
@@ -924,6 +924,39 @@ a constant (e.g. addi for add, movi for mov).
 as some of them may not be available as "real" opcodes. Always use the
 function tcg_gen_xxx(args).
 
+Debug Print Helpers
+--------------------
+
+When debugging decode logic or validating a freshly built TCG sequence, you can
+insert a printf-style TCG operation via ``tcg_gen_print()``. The generated op
+calls the ``tcg_print`` helper when the TB runs and forwards the formatted
+string to ``qemu_printf``. This impacts performance, so enable it only during
+local debugging or under controlled conditions.
+
+The first argument to ``tcg_gen_print`` is a ``printf``-style format string.
+Each subsequent argument must be provided as a pair consisting of a
+``TCGPrintArgType`` enum value (defined in ``tcg/tcg-print.h``) and a TCG
+register or constant of the matching type, and the list ends with
+``TCG_PRINT_ARG_END``. At most ``TCG_PRINT_MAX_ARGS`` (currently 5) dynamic
+arguments are supported; use ``TCG_PRINT_ARG_PTR`` when printing pointers. For
+literal numbers, first create a TCG constant via ``tcg_constant_i32`` or
+``tcg_constant_i64`` before passing it in.
+
+.. code-block:: c
+
+    tcg_gen_print("rd=%d sum=0x%" PRId64 "\n",
+                  TCG_PRINT_ARG_I32, tcg_constant_i32(1),
+                  TCG_PRINT_ARG_I64, cpu_gpr[1],
+                  TCG_PRINT_ARG_END);
+
+When the translated TB executes, the snippet above prints the destination
+register number, and the value written back, which helps observe the runtime
+values of TCG variables quickly.
+
+It is worth noting that ``tcg_gen_print()`` is only recommended for debugging
+purposes. You must remove all instances of it from your patch before
+submitting official code. The ``checkpatch.pl`` script will check your patch
+for any usage of ``tcg_gen_print()`` and throw a warning if detected.
 
 Backend
 =======
-- 
2.52.0



^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2025-12-26 16:52 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-26 16:51 [RFC PATCH v1 0/3] tcg: add tcg_gen_print helper Chao Liu
2025-12-26 16:51 ` [RFC PATCH v1 1/3] " Chao Liu
2025-12-26 16:51 ` [RFC PATCH v1 2/3] scripts: warn on tcg_gen_print usage to the checkpatch.pl Chao Liu
2025-12-26 16:51 ` [RFC PATCH v1 3/3] docs: add documentation for the tcg_gen_print helper to tcg-ops Chao Liu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.