[PATCH] target/hexagon: don't let an idef-parser dest clobber its own source

Brian Cain <[email protected]> Wed, 5 Aug 2026 07:02:00 -0700
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
The idef-parser emitters write the destination in place, so when a packet
is short-circuited and get_result_gpr() returns hex_gpr[] itself, an
instruction naming one register as both source and destination reads back
a value it already overwrote.

`Rd32=cmpy(Rs32,Rt32):<<1:rnd:sat` with Rs == Rd is an example.

Give a source reg that aliases a destination its own copy of the
register value.

Signed-off-by: Brian Cain <[email protected]>
---
 target/hexagon/genptr.h                |  1 +
 target/hexagon/genptr.c                | 11 ++++++
 tests/tcg/hexagon/read_write_overlap.c | 47 ++++++++++++++++++++++++++
 target/hexagon/gen_tcg_funcs.py        | 15 ++++++++
 target/hexagon/hex_common.py           | 12 +++++++
 5 files changed, 86 insertions(+)

diff --git a/target/hexagon/genptr.h b/target/hexagon/genptr.h
index 45ee038ca94..5d096774102 100644
--- a/target/hexagon/genptr.h
+++ b/target/hexagon/genptr.h
@@ -36,6 +36,7 @@ void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, uint32_t slot);
 TCGv gen_read_reg(TCGv result, int num);
 TCGv gen_read_preg(TCGv pred, uint8_t num);
 TCGv get_result_gpr(DisasContext *ctx, int rnum);
+TCGv gen_unalias_gpr_src(TCGv src, TCGv dst);
 TCGv get_result_pred(DisasContext *ctx, int pnum);
 void gen_pred_write(DisasContext *ctx, int pnum, TCGv val);
 void gen_set_usr_field(DisasContext *ctx, int field, TCGv val);
diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c
index 3f310377097..12813f48bf9 100644
--- a/target/hexagon/genptr.c
+++ b/target/hexagon/genptr.c
@@ -91,6 +91,17 @@ TCGv get_result_gpr(DisasContext *ctx, int rnum)
     }
 }
 
+TCGv gen_unalias_gpr_src(TCGv src, TCGv dst)
+{
+    if (src != dst) {
+        return src;
+    }
+
+    TCGv tmp = tcg_temp_new();
+    tcg_gen_mov_tl(tmp, src);
+    return tmp;
+}
+
 static TCGv_i64 get_result_gpr_pair(DisasContext *ctx, int rnum)
 {
     TCGv_i64 result = tcg_temp_new_i64();
diff --git a/tests/tcg/hexagon/read_write_overlap.c b/tests/tcg/hexagon/read_write_overlap.c
index 95c54ccd63c..7eaf75f545a 100644
--- a/tests/tcg/hexagon/read_write_overlap.c
+++ b/tests/tcg/hexagon/read_write_overlap.c
@@ -115,12 +115,59 @@ static void test_swiz(void)
     check32(swiz(0x11223344), 0x44332211);
 }
 
+#define CMPY(NAME, ASM) \
+static inline uint32_t NAME##_rd_eq_rs(uint32_t x, uint32_t y) \
+{ \
+    uint32_t res; \
+    asm("r7 = %1\n\t" \
+        ASM("r7", "%2") "\n\t" \
+        "%0 = r7\n\t" \
+        : "=r"(res) : "r"(x), "r"(y) : "r7"); \
+    return res; \
+} \
+static inline uint32_t NAME##_rd_eq_rt(uint32_t x, uint32_t y) \
+{ \
+    uint32_t res; \
+    asm("r7 = %2\n\t" \
+        ASM("%1", "r7") "\n\t" \
+        "%0 = r7\n\t" \
+        : "=r"(res) : "r"(x), "r"(y) : "r7"); \
+    return res; \
+}
+
+#define CMPY_RND_SAT(RS, RT)     "r7 = cmpy(" RS "," RT "):rnd:sat"
+#define CMPY_S1_RND_SAT(RS, RT)  "r7 = cmpy(" RS "," RT "):<<1:rnd:sat"
+#define CMPYC_RND_SAT(RS, RT)    "r7 = cmpy(" RS "," RT "*):rnd:sat"
+#define CMPYC_S1_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "*):<<1:rnd:sat"
+
+CMPY(cmpyrs_s0, CMPY_RND_SAT)
+CMPY(cmpyrs_s1, CMPY_S1_RND_SAT)
+CMPY(cmpyrsc_s0, CMPYC_RND_SAT)
+CMPY(cmpyrsc_s1, CMPYC_S1_RND_SAT)
+
+static void test_cmpy(void)
+{
+    check32(cmpyrs_s0_rd_eq_rs(0x32195ce2, 0xef862430), 0x011b105b);
+    check32(cmpyrs_s0_rd_eq_rt(0x32195ce2, 0xef862430), 0x011b105b);
+    check32(cmpyrs_s1_rd_eq_rs(0x32195ce2, 0xef862430), 0x023520b5);
+    check32(cmpyrs_s1_rd_eq_rt(0x32195ce2, 0xef862430), 0x023520b5);
+    check32(cmpyrsc_s0_rd_eq_rs(0x32195ce2, 0xef862430), 0x0d0f09e8);
+    check32(cmpyrsc_s0_rd_eq_rt(0x32195ce2, 0xef862430), 0x0d0f09e8);
+    check32(cmpyrsc_s1_rd_eq_rs(0x32195ce2, 0xef862430), 0x1a1f13d0);
+    check32(cmpyrsc_s1_rd_eq_rt(0x32195ce2, 0xef862430), 0x1a1f13d0);
+
+    /* Both halves saturate */
+    check32(cmpyrs_s1_rd_eq_rs(0x80008000, 0x80008000), 0x7fff0000);
+    check32(cmpyrsc_s1_rd_eq_rs(0x7fff8001, 0x80017fff), 0x00008000);
+}
+
 int main()
 {
     test_insert();
     test_insert_rp();
     test_asr_r_svw_trun();
     test_swiz();
+    test_cmpy();
 
     puts(err ? "FAIL" : "PASS");
     return err ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py
index 6d5d99cee3a..43e68077495 100755
--- a/target/hexagon/gen_tcg_funcs.py
+++ b/target/hexagon/gen_tcg_funcs.py
@@ -75,6 +75,21 @@ def gen_tcg_func(f, tag, regs, imms):
         f.write(f"    int {hex_common.imm_name(immlett)} = insn->immed[{i}];\n")
 
     if hex_common.is_idef_parser_enabled(tag):
+        gpr_operands = [
+            hex_common.get_register(tag, regtype, regid)
+            for regtype, regid in regs
+            if hex_common.get_register(tag, regtype, regid).may_alias_gpr()
+        ]
+        dests = [reg for reg in gpr_operands if reg.is_written()]
+        for reg in gpr_operands:
+            if reg.is_written() or not reg.is_read():
+                continue
+            src = reg.reg_tcg()
+            for dest in dests:
+                f.write(hex_common.code_fmt(f"""\
+                    {src} = gen_unalias_gpr_src({src}, {dest.reg_tcg()});
+                """))
+
         declared = []
         ## Handle registers
         for regtype, regid in regs:
diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py
index d91a653c3d4..09a025ba13e 100755
--- a/target/hexagon/hex_common.py
+++ b/target/hexagon/hex_common.py
@@ -388,6 +388,8 @@ def decl_reg_num(self, f, regno):
         """))
     def idef_arg(self, declared):
         declared.append(self.reg_tcg())
+    def may_alias_gpr(self):
+        return False
     def helper_arg(self):
         return HelperArg(
             self.helper_proto_type(),
@@ -495,6 +497,8 @@ def is_new(self):
         return False
 
 class GprDest(Register, Single, Dest):
+    def may_alias_gpr(self):
+        return True
     def decl_tcg(self, f, tag, regno):
         self.decl_reg_num(f, regno)
         f.write(code_fmt(f"""\
@@ -510,6 +514,8 @@ def analyze_write(self, f, tag, regno):
         """))
 
 class GprSource(Register, Single, OldSource):
+    def may_alias_gpr(self):
+        return True
     def decl_tcg(self, f, tag, regno):
         self.decl_reg_num(f, regno)
         f.write(code_fmt(f"""\
@@ -531,6 +537,8 @@ def analyze_read(self, f, regno):
         """))
 
 class GprReadWrite(Register, Single, ReadWrite):
+    def may_alias_gpr(self):
+        return True
     def decl_tcg(self, f, tag, regno):
         self.decl_reg_num(f, regno)
         f.write(code_fmt(f"""\
@@ -557,6 +565,8 @@ def analyze_write(self, f, tag, regno):
         """))
 
 class ControlDest(Register, Single, Dest):
+    def may_alias_gpr(self):
+        return True
     def decl_reg_num(self, f, regno):
         f.write(code_fmt(f"""\
             const int {self.reg_num} = insn->regno[{regno}]  + HEX_REG_SA0;
@@ -593,6 +603,8 @@ def analyze_read(self, f, regno):
         """))
 
 class ModifierSource(Register, Single, OldSource):
+    def may_alias_gpr(self):
+        return True
     def decl_reg_num(self, f, regno):
         f.write(code_fmt(f"""\
             const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_M0;
-- 
2.34.1