@samitouri / QOSamiQemu / commits / 94a5b3ff4e

target/hexagon: fix PC not advancing for non-COF TB-ending packets

Add hex_next_PC, a global mirroring CPUHexagonState::next_PC, and ctx->need_next_pc, so that gen_write_new_pc_addr() can write the branch target through hex_next_PC instead of hex_gpr[HEX_REG_PC] when a later unconditional write of PC is expected. gen_end_tb() then commits hex_next_PC into hex_gpr[HEX_REG_PC] at the end of the packet. Previously, non-COF instructions that still end a TB did not advance the PC, since next_PC's value was never written back into the PC register. Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>

Brian Cain committed Jul 16, 2026 at 12:15 UTC 94a5b3ff4e371c8c04c6213e4383722bcafea2a8
3 files changed +49 -19
target/hexagon/genptr.c
+11 -3
@@ -613,14 +613,22 @@ static void gen_write_new_pc_addr(DisasContext *ctx, TCGv addr,
613 tcg_gen_brcondi_tl(cond, pred, 1, pred_false);
614 }
615
616 + /*
617 + * If gen_end_tb() will unconditionally overwrite PC with hex_next_PC
618 + * (because this packet has a predicated COF that may not execute),
619 + * write the branch target there instead of directly into the PC
620 + * global, or the overwrite in gen_end_tb() would clobber it.
621 + */
622 + TCGv pc_wr = ctx->need_next_pc ? hex_next_PC : hex_gpr[HEX_REG_PC];
623 +
624 if (ctx->pkt.pkt_has_multi_cof) {
625 /* If there are multiple branches in a packet, ignore the second one */
618 - tcg_gen_movcond_tl(TCG_COND_NE, hex_gpr[HEX_REG_PC],
626 + tcg_gen_movcond_tl(TCG_COND_NE, pc_wr,
627 ctx->branch_taken, tcg_constant_tl(0),
620 - hex_gpr[HEX_REG_PC], addr);
628 + pc_wr, addr);
629 tcg_gen_movi_tl(ctx->branch_taken, 1);
630 } else {
623 - tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], addr);
631 + tcg_gen_mov_tl(pc_wr, addr);
632 }
633
634 if (cond != TCG_COND_ALWAYS) {
target/hexagon/translate.c
+35 -16
@@ -54,6 +54,7 @@ static const AnalyzeInsn opcode_analyze[XX_LAST_OPCODE] = {
54 TCGv hex_gpr[TOTAL_PER_THREAD_REGS];
55 TCGv hex_pred[NUM_PREGS];
56 TCGv hex_slot_cancelled;
57 +TCGv hex_next_PC;
58 TCGv hex_new_value_usr;
59 TCGv hex_store_addr[STORES_MAX];
60 TCGv_i32 hex_store_width[STORES_MAX];
@@ -184,10 +185,16 @@ static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx,
185 }
186 }
187
188 +static bool need_next_PC(DisasContext *ctx);
189 +
190 static void gen_end_tb(DisasContext *ctx)
191 {
192 gen_exec_counters(ctx);
193
194 + if (ctx->need_next_pc) {
195 + tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC);
196 + }
197 +
198 if (ctx->branch_cond != TCG_COND_NEVER) {
199 if (ctx->branch_cond != TCG_COND_ALWAYS) {
200 TCGLabel *skip = gen_new_label();
@@ -391,17 +398,25 @@ static bool pkt_ends_tb(Packet *pkt)
398
399 static bool need_next_PC(DisasContext *ctx)
400 {
394 - /* Check for conditional control flow or HW loop end */
395 - for (int i = 0; i < ctx->pkt.num_insns; i++) {
396 - uint16_t opcode = ctx->pkt.insn[i].opcode;
397 - if (GET_ATTRIB(opcode, A_CONDEXEC) && GET_ATTRIB(opcode, A_COF)) {
398 - return true;
399 - }
400 - if (GET_ATTRIB(opcode, A_HWLOOP0_END) ||
401 - GET_ATTRIB(opcode, A_HWLOOP1_END)) {
402 - return true;
401 + Packet *pkt = &ctx->pkt;
402 + if (pkt->pkt_has_cof || ctx->pkt_ends_tb) {
403 + for (int i = 0; i < pkt->num_insns; i++) {
404 + uint16_t opcode = pkt->insn[i].opcode;
405 + if ((GET_ATTRIB(opcode, A_CONDEXEC) && GET_ATTRIB(opcode, A_COF)) ||
406 + GET_ATTRIB(opcode, A_HWLOOP0_END) ||
407 + GET_ATTRIB(opcode, A_HWLOOP1_END)) {
408 + return true;
409 + }
410 }
411 }
412 + /*
413 + * We end the TB on some instructions that do not change the flow (for
414 + * other reasons). In these cases, we must set pc too, as the insn won't
415 + * do it themselves.
416 + */
417 + if (ctx->pkt_ends_tb && !check_for_attrib(pkt, A_COF)) {
418 + return true;
419 + }
420 return false;
421 }
422
@@ -637,12 +652,14 @@ static void gen_start_packet(DisasContext *ctx)
652 ctx->branch_taken = NULL;
653 if (ctx->pkt.pkt_has_cof) {
654 ctx->branch_taken = tcg_temp_new();
640 - if (ctx->pkt.pkt_has_multi_cof) {
641 - tcg_gen_movi_tl(ctx->branch_taken, 0);
642 - }
643 - if (need_next_PC(ctx)) {
644 - tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], next_PC);
645 - }
655 + }
656 + if (ctx->pkt.pkt_has_multi_cof) {
657 + tcg_gen_movi_tl(ctx->branch_taken, 0);
658 + }
659 + ctx->pkt_ends_tb = pkt_ends_tb(&ctx->pkt);
660 + ctx->need_next_pc = need_next_PC(ctx);
661 + if (ctx->need_next_pc) {
662 + tcg_gen_movi_tl(hex_next_PC, next_PC);
663 }
664
665 /* Preload the predicated registers into get_result_gpr(ctx, i) */
@@ -1142,7 +1159,7 @@ static void gen_commit_packet(DisasContext *ctx)
1159 ctx->pkt.vhist_insn->generate(ctx);
1160 }
1161
1145 - if (pkt_ends_tb(&ctx->pkt) || ctx->base.is_jmp == DISAS_NORETURN) {
1162 + if (ctx->pkt_ends_tb || ctx->base.is_jmp == DISAS_NORETURN) {
1163 gen_end_tb(ctx);
1164 }
1165 }
@@ -1327,6 +1344,8 @@ void hexagon_translate_init(void)
1344 }
1345 hex_new_value_usr = tcg_global_mem_new(tcg_env,
1346 offsetof(CPUHexagonState, new_value_usr), "new_value_usr");
1347 + hex_next_PC = tcg_global_mem_new(tcg_env,
1348 + offsetof(CPUHexagonState, next_PC), "next_PC");
1349
1350 for (i = 0; i < NUM_PREGS; i++) {
1351 hex_pred[i] = tcg_global_mem_new(tcg_env,
target/hexagon/translate.h
+3
@@ -40,6 +40,7 @@ typedef struct DisasContext {
40 int reg_log_idx;
41 DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS);
42 DECLARE_BITMAP(predicated_regs, TOTAL_PER_THREAD_REGS);
43 + bool pkt_ends_tb;
44 bool implicit_usr_write;
45 #ifndef CONFIG_USER_ONLY
46 int greg_log[GREG_WRITES_MAX];
@@ -75,6 +76,7 @@ typedef struct DisasContext {
76 DECLARE_BITMAP(insn_qregs_read, NUM_QREGS);
77 bool pre_commit;
78 bool need_commit;
79 + bool need_next_pc;
80 TCGCond branch_cond;
81 target_ulong branch_dest;
82 bool is_tight_loop;
@@ -310,6 +312,7 @@ extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS];
312 extern TCGv hex_pred[NUM_PREGS];
313 extern TCGv hex_slot_cancelled;
314 extern TCGv hex_new_value_usr;
315 +extern TCGv hex_next_PC;
316 extern TCGv hex_store_addr[STORES_MAX];
317 extern TCGv_i32 hex_store_width[STORES_MAX];
318 extern TCGv hex_store_val32[STORES_MAX];