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

Brian Cain <[email protected]>
Newsgroups org.nongnu.qemu-devel
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.

Reviewed-by: Pierrick Bouvier <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.