@samitouri / QOSamiQemu / commits / 8f5bf4fa0a

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

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 <pierrick.bouvier@oss.qualcomm.com> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>

Brian Cain committed Aug 5, 2026 at 07:02 UTC 8f5bf4fa0aa2f5648f016b8265d86d4b05b42307
5 files changed +86
target/hexagon/gen_tcg_funcs.py
+15
@@ -75,6 +75,21 @@ def gen_tcg_func(f, tag, regs, imms):
75 f.write(f" int {hex_common.imm_name(immlett)} = insn->immed[{i}];\n")
76
77 if hex_common.is_idef_parser_enabled(tag):
78 + gpr_operands = [
79 + hex_common.get_register(tag, regtype, regid)
80 + for regtype, regid in regs
81 + if hex_common.get_register(tag, regtype, regid).may_alias_gpr()
82 + ]
83 + dests = [reg for reg in gpr_operands if reg.is_written()]
84 + for reg in gpr_operands:
85 + if reg.is_written() or not reg.is_read():
86 + continue
87 + src = reg.reg_tcg()
88 + for dest in dests:
89 + f.write(hex_common.code_fmt(f"""\
90 + {src} = gen_unalias_gpr_src({src}, {dest.reg_tcg()});
91 + """))
92 +
93 declared = []
94 ## Handle registers
95 for regtype, regid in regs:
target/hexagon/genptr.c
+11
@@ -91,6 +91,17 @@ TCGv get_result_gpr(DisasContext *ctx, int rnum)
91 }
92 }
93
94 +TCGv gen_unalias_gpr_src(TCGv src, TCGv dst)
95 +{
96 + if (src != dst) {
97 + return src;
98 + }
99 +
100 + TCGv tmp = tcg_temp_new();
101 + tcg_gen_mov_tl(tmp, src);
102 + return tmp;
103 +}
104 +
105 static TCGv_i64 get_result_gpr_pair(DisasContext *ctx, int rnum)
106 {
107 TCGv_i64 result = tcg_temp_new_i64();
target/hexagon/genptr.h
+1
@@ -36,6 +36,7 @@ void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, uint32_t slot);
36 TCGv gen_read_reg(TCGv result, int num);
37 TCGv gen_read_preg(TCGv pred, uint8_t num);
38 TCGv get_result_gpr(DisasContext *ctx, int rnum);
39 +TCGv gen_unalias_gpr_src(TCGv src, TCGv dst);
40 TCGv get_result_pred(DisasContext *ctx, int pnum);
41 void gen_pred_write(DisasContext *ctx, int pnum, TCGv val);
42 void gen_set_usr_field(DisasContext *ctx, int field, TCGv val);
target/hexagon/hex_common.py
+12
@@ -388,6 +388,8 @@ class Register:
388 """))
389 def idef_arg(self, declared):
390 declared.append(self.reg_tcg())
391 + def may_alias_gpr(self):
392 + return False
393 def helper_arg(self):
394 return HelperArg(
395 self.helper_proto_type(),
@@ -495,6 +497,8 @@ class ReadWrite:
497 return False
498
499 class GprDest(Register, Single, Dest):
500 + def may_alias_gpr(self):
501 + return True
502 def decl_tcg(self, f, tag, regno):
503 self.decl_reg_num(f, regno)
504 f.write(code_fmt(f"""\
@@ -510,6 +514,8 @@ class GprDest(Register, Single, Dest):
514 """))
515
516 class GprSource(Register, Single, OldSource):
517 + def may_alias_gpr(self):
518 + return True
519 def decl_tcg(self, f, tag, regno):
520 self.decl_reg_num(f, regno)
521 f.write(code_fmt(f"""\
@@ -531,6 +537,8 @@ class GprNewSource(Register, Single, NewSource):
537 """))
538
539 class GprReadWrite(Register, Single, ReadWrite):
540 + def may_alias_gpr(self):
541 + return True
542 def decl_tcg(self, f, tag, regno):
543 self.decl_reg_num(f, regno)
544 f.write(code_fmt(f"""\
@@ -557,6 +565,8 @@ class GprReadWrite(Register, Single, ReadWrite):
565 """))
566
567 class ControlDest(Register, Single, Dest):
568 + def may_alias_gpr(self):
569 + return True
570 def decl_reg_num(self, f, regno):
571 f.write(code_fmt(f"""\
572 const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_SA0;
@@ -593,6 +603,8 @@ class ControlSource(Register, Single, OldSource):
603 """))
604
605 class ModifierSource(Register, Single, OldSource):
606 + def may_alias_gpr(self):
607 + return True
608 def decl_reg_num(self, f, regno):
609 f.write(code_fmt(f"""\
610 const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_M0;
tests/tcg/hexagon/read_write_overlap.c
+47
@@ -115,12 +115,59 @@ static void test_swiz(void)
115 check32(swiz(0x11223344), 0x44332211);
116 }
117
118 +#define CMPY(NAME, ASM) \
119 +static inline uint32_t NAME##_rd_eq_rs(uint32_t x, uint32_t y) \
120 +{ \
121 + uint32_t res; \
122 + asm("r7 = %1\n\t" \
123 + ASM("r7", "%2") "\n\t" \
124 + "%0 = r7\n\t" \
125 + : "=r"(res) : "r"(x), "r"(y) : "r7"); \
126 + return res; \
127 +} \
128 +static inline uint32_t NAME##_rd_eq_rt(uint32_t x, uint32_t y) \
129 +{ \
130 + uint32_t res; \
131 + asm("r7 = %2\n\t" \
132 + ASM("%1", "r7") "\n\t" \
133 + "%0 = r7\n\t" \
134 + : "=r"(res) : "r"(x), "r"(y) : "r7"); \
135 + return res; \
136 +}
137 +
138 +#define CMPY_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "):rnd:sat"
139 +#define CMPY_S1_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "):<<1:rnd:sat"
140 +#define CMPYC_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "*):rnd:sat"
141 +#define CMPYC_S1_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "*):<<1:rnd:sat"
142 +
143 +CMPY(cmpyrs_s0, CMPY_RND_SAT)
144 +CMPY(cmpyrs_s1, CMPY_S1_RND_SAT)
145 +CMPY(cmpyrsc_s0, CMPYC_RND_SAT)
146 +CMPY(cmpyrsc_s1, CMPYC_S1_RND_SAT)
147 +
148 +static void test_cmpy(void)
149 +{
150 + check32(cmpyrs_s0_rd_eq_rs(0x32195ce2, 0xef862430), 0x011b105b);
151 + check32(cmpyrs_s0_rd_eq_rt(0x32195ce2, 0xef862430), 0x011b105b);
152 + check32(cmpyrs_s1_rd_eq_rs(0x32195ce2, 0xef862430), 0x023520b5);
153 + check32(cmpyrs_s1_rd_eq_rt(0x32195ce2, 0xef862430), 0x023520b5);
154 + check32(cmpyrsc_s0_rd_eq_rs(0x32195ce2, 0xef862430), 0x0d0f09e8);
155 + check32(cmpyrsc_s0_rd_eq_rt(0x32195ce2, 0xef862430), 0x0d0f09e8);
156 + check32(cmpyrsc_s1_rd_eq_rs(0x32195ce2, 0xef862430), 0x1a1f13d0);
157 + check32(cmpyrsc_s1_rd_eq_rt(0x32195ce2, 0xef862430), 0x1a1f13d0);
158 +
159 + /* Both halves saturate */
160 + check32(cmpyrs_s1_rd_eq_rs(0x80008000, 0x80008000), 0x7fff0000);
161 + check32(cmpyrsc_s1_rd_eq_rs(0x7fff8001, 0x80017fff), 0x00008000);
162 +}
163 +
164 int main()
165 {
166 test_insert();
167 test_insert_rp();
168 test_asr_r_svw_trun();
169 test_swiz();
170 + test_cmpy();
171
172 puts(err ? "FAIL" : "PASS");
173 return err ? EXIT_FAILURE : EXIT_SUCCESS;