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(+)
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 <brian.cain@oss.qualcomm.com>
---
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
On 8/5/2026 7:02 AM, Brian Cain wrote: > 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 <brian.cain@oss.qualcomm.com> > --- > 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(+) > Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
© 2016 - 2026 Red Hat, Inc.