diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h index 45ee7b46409c7..ccaf6a9d28d6f 100644 --- a/target/hexagon/cpu-param.h +++ b/target/hexagon/cpu-param.h @@ -23,4 +23,9 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 36 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +/* + * Hexagon processors have a strong memory model. + */ +#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL) + #endif diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 0b7fc98f6ce19..9ebed4dd86e14 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -216,8 +216,7 @@ static void hexagon_dump(CPUHexagonState *env, FILE *f, int flags) qemu_fprintf(f, " cs0 = 0x00000000\n"); qemu_fprintf(f, " cs1 = 0x00000000\n"); #else - print_reg(f, env, HEX_REG_CAUSE); - print_reg(f, env, HEX_REG_BADVA); + print_reg(f, env, HEX_SREG_BADVA); print_reg(f, env, HEX_REG_CS0); print_reg(f, env, HEX_REG_CS1); #endif diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 79e60d4bfa1b0..afe4d2808cdd9 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -28,6 +28,7 @@ #define NUM_PREGS 4 #define TOTAL_PER_THREAD_REGS 64 +#define NUM_GPREGS 32 #define SLOTS_MAX 4 #define STORES_MAX 2 @@ -78,6 +79,9 @@ typedef struct CPUArchState { uint8_t slot_cancelled; target_ulong new_value_usr; + target_ulong gpreg_written; + QEMU_BUILD_BUG_MSG(NUM_GPREGS > CHAR_BIT * sizeof(target_ulong), + "Hexagon's CPUArchState.gpreg_written type is too small"); MemLog mem_log_stores[STORES_MAX]; diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index ff596e2a94c98..cc15e33358183 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -26,6 +26,7 @@ enum hex_event { HEX_EVENT_NONE = -1, HEX_EVENT_TRAP0 = 0x008, + HEX_EVENT_PRECISE = 0x2, }; enum hex_cause { @@ -34,6 +35,7 @@ enum hex_cause { HEX_CAUSE_FETCH_NO_UPAGE = 0x012, HEX_CAUSE_INVALID_PACKET = 0x015, HEX_CAUSE_INVALID_OPCODE = 0x015, + HEX_CAUSE_REG_WRITE_CONFLICT = 0x01d, HEX_CAUSE_PC_NOT_ALIGNED = 0x01e, HEX_CAUSE_PRIV_NO_UREAD = 0x024, HEX_CAUSE_PRIV_NO_UWRITE = 0x025, diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 23deba2426f84..1db7f1950f741 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -236,9 +236,9 @@ static void decode_set_insn_attr_fields(Packet *pkt) if (GET_ATTRIB(opcode, A_SCALAR_STORE) && !GET_ATTRIB(opcode, A_MEMSIZE_0B)) { if (pkt->insn[i].slot == 0) { - pkt->pkt_has_store_s0 = true; + pkt->pkt_has_scalar_store_s0 = true; } else { - pkt->pkt_has_store_s1 = true; + pkt->pkt_has_scalar_store_s1 = true; } } } @@ -489,7 +489,6 @@ decode_insns(DisasContext *ctx, Insn *insn, uint32_t encoding) insn->iclass = iclass_bits(encoding); return 1; } - g_assert_not_reached(); } else { uint32_t iclass = get_duplex_iclass(encoding); unsigned int slot0_subinsn = get_slot0_subinsn(encoding); @@ -512,6 +511,11 @@ decode_insns(DisasContext *ctx, Insn *insn, uint32_t encoding) } g_assert_not_reached(); } + /* + * invalid/unrecognized opcode; return 1 and let gen_insn() raise an + * exception when it sees this empty insn. + */ + return 1; } static void decode_add_endloop_insn(Insn *insn, int loopnum) diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py index c1f806ac4b25b..a9c0e27a801ff 100755 --- a/target/hexagon/gen_helper_funcs.py +++ b/target/hexagon/gen_helper_funcs.py @@ -69,7 +69,7 @@ def gen_helper_function(f, tag, tagregs, tagimms): if hex_common.need_slot(tag): if "A_LOAD" in hex_common.attribdict[tag]: f.write(hex_common.code_fmt(f"""\ - bool pkt_has_store_s1 = slotval & 0x1; + bool pkt_has_scalar_store_s1 = slotval & 0x1; """)) f.write(hex_common.code_fmt(f"""\ uint32_t slot = slotval >> 1; diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 2c5e15cfcf6f9..fa312c6fef72d 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -100,6 +100,7 @@ void gen_log_reg_write(DisasContext *ctx, int rnum, TCGv val) gen_masked_reg_write(val, hex_gpr[rnum], reg_mask); tcg_gen_mov_tl(get_result_gpr(ctx, rnum), val); + gen_helper_check_reg_write(tcg_env, tcg_constant_tl(rnum)); } static void gen_log_reg_write_pair(DisasContext *ctx, int rnum, TCGv_i64 val) @@ -113,6 +114,8 @@ static void gen_log_reg_write_pair(DisasContext *ctx, int rnum, TCGv_i64 val) /* High word */ tcg_gen_extrh_i64_i32(val32, val); gen_log_reg_write(ctx, rnum + 1, val32); + + gen_helper_check_reg_write_pair(tcg_env, tcg_constant_tl(rnum)); } TCGv get_result_pred(DisasContext *ctx, int pnum) @@ -395,7 +398,8 @@ static inline void gen_store_conditional8(DisasContext *ctx, #ifndef CONFIG_HEXAGON_IDEF_PARSER static TCGv gen_slotval(DisasContext *ctx) { - int slotval = (ctx->pkt->pkt_has_store_s1 & 1) | (ctx->insn->slot << 1); + int slotval = + (ctx->pkt->pkt_has_scalar_store_s1 & 1) | (ctx->insn->slot << 1); return tcg_constant_tl(slotval); } #endif diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index f8baa599c88cd..5bf072502235f 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h @@ -30,6 +30,10 @@ DEF_HELPER_5(vacsh_val, s64, env, s64, s64, s64, i32) DEF_HELPER_FLAGS_4(vacsh_pred, TCG_CALL_NO_RWG_SE, s32, env, s64, s64, s64) DEF_HELPER_FLAGS_2(cabacdecbin_val, TCG_CALL_NO_RWG_SE, s64, s64, s64) DEF_HELPER_FLAGS_2(cabacdecbin_pred, TCG_CALL_NO_RWG_SE, s32, s64, s64) +DEF_HELPER_2(check_reg_write, void, env, int) +DEF_HELPER_3(check_cond_reg_write, void, env, int, int) +DEF_HELPER_2(check_reg_write_pair, void, env, int) +DEF_HELPER_3(check_cond_reg_write_pair, void, env, int, int) /* Floating point */ DEF_HELPER_2(conv_sf2df, f64, env, f32) diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index 758e5fd12dfed..85eabc98766f6 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -247,8 +247,11 @@ def need_next_PC(tag): def need_pkt_has_multi_cof(tag): - return "A_COF" in attribdict[tag] - + return ( + "A_JUMP" in attribdict[tag] + or "A_CALL" in attribdict[tag] + or "J2_rte" == tag + ) and tag != "J2_hintjumpr" def need_pkt_need_commit(tag): return 'A_IMPLICIT_WRITES_USR' in attribdict[tag] @@ -349,6 +352,12 @@ def helper_arg(self): self.reg_tcg(), f"{self.helper_arg_type()} {self.helper_arg_name()}" ) + def from_subtype(self, subtype): + if subtype == "": + return self + raise Exception( + f"unknown subtype '{subtype}' on generic Register class") + # # Every register is either Single or Pair or Hvx @@ -1070,11 +1079,22 @@ def init_registers(): for reg in new_regs: new_registers[f"{reg.regtype}{reg.regid}"] = reg -def get_register(tag, regtype, regid): - if f"{regtype}{regid}V" in semdict[tag]: - return registers[f"{regtype}{regid}"] - else: - return new_registers[f"{regtype}{regid}"] +def is_new_reg(tag, regid): + if regid[0] in "NO": + return True + return regid[0] == "P" and \ + f"{regid}N" in semdict[tag] and \ + f"{regid}V" not in semdict[tag] + +def get_register(tag, regtype, regid, subtype=""): + regid = f"{regtype}{regid}" + is_new = is_new_reg(tag, regid) + try: + reg = new_registers[regid] if is_new else registers[regid] + except KeyError: + raise Exception(f"Unknown {'new ' if is_new else ''}register {regid}" +\ + f"from '{tag}' with syntax '{semdict[tag]}'") from None + return reg.from_subtype(subtype) def helper_ret_type(tag, regs): ## If there is a scalar result, it is the return type diff --git a/target/hexagon/idef-parser/README.rst b/target/hexagon/idef-parser/README.rst index 7199177ee33e6..235e3debee3c3 100644 --- a/target/hexagon/idef-parser/README.rst +++ b/target/hexagon/idef-parser/README.rst @@ -637,7 +637,7 @@ tinycode for the Hexagon ``add`` instruction :: ---- 00021094 - mov_i32 pkt_has_store_s1,$0x0 + mov_i32 pkt_has_scalar_store_s1,$0x0 add_i32 tmp0,r2,r2 mov_i32 loc2,tmp0 mov_i32 new_r1,loc2 diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index a7dcd85fe43d9..3316c230f8a5f 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -1725,7 +1725,7 @@ void gen_cancel(Context *c, YYLTYPE *locp) void gen_load_cancel(Context *c, YYLTYPE *locp) { - OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_store_s1) {\n"); + OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_scalar_store_s1) {\n"); OUT(c, locp, "ctx->s1_store_processed = false;\n"); OUT(c, locp, "process_store(ctx, 1);\n"); OUT(c, locp, "}\n"); @@ -1750,7 +1750,7 @@ void gen_load(Context *c, YYLTYPE *locp, HexValue *width, /* Lookup the effective address EA */ find_variable(c, locp, ea, ea); - OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_store_s1) {\n"); + OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_scalar_store_s1) {\n"); OUT(c, locp, "probe_noshuf_load(", ea, ", ", width, ", ctx->mem_idx);\n"); OUT(c, locp, "process_store(ctx, 1);\n"); OUT(c, locp, "}\n"); diff --git a/target/hexagon/insn.h b/target/hexagon/insn.h index 24dcf7fe9f385..5d59430da9e12 100644 --- a/target/hexagon/insn.h +++ b/target/hexagon/insn.h @@ -66,8 +66,8 @@ struct Packet { bool pkt_has_dczeroa; - bool pkt_has_store_s0; - bool pkt_has_store_s1; + bool pkt_has_scalar_store_s0; + bool pkt_has_scalar_store_s1; bool pkt_has_hvx; Insn *vhist_insn; diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index ee3d4c88e7bdf..b6e5c8aae251f 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -82,7 +82,7 @@ */ #define CHECK_NOSHUF(VA, SIZE) \ do { \ - if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + if (insn->slot == 0 && ctx->pkt->pkt_has_scalar_store_s1) { \ probe_noshuf_load(VA, SIZE, ctx->mem_idx); \ process_store(ctx, 1); \ } \ @@ -93,11 +93,11 @@ TCGLabel *noshuf_label = gen_new_label(); \ tcg_gen_brcondi_tl(TCG_COND_EQ, PRED, 0, noshuf_label); \ GET_EA; \ - if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + if (insn->slot == 0 && ctx->pkt->pkt_has_scalar_store_s1) { \ probe_noshuf_load(EA, SIZE, ctx->mem_idx); \ } \ gen_set_label(noshuf_label); \ - if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + if (insn->slot == 0 && ctx->pkt->pkt_has_scalar_store_s1) { \ process_store(ctx, 1); \ } \ } while (0) @@ -524,7 +524,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fLOAD(NUM, SIZE, SIGN, EA, DST) \ do { \ - check_noshuf(env, pkt_has_store_s1, slot, EA, SIZE, GETPC()); \ + check_noshuf(env, pkt_has_scalar_store_s1, slot, EA, SIZE, GETPC()); \ DST = (size##SIZE##SIGN##_t)MEM_LOAD##SIZE(env, EA, GETPC()); \ } while (0) #endif diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 6da8db8ea5c59..1d5bc7782a8d4 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -59,6 +59,24 @@ void log_store32(CPUHexagonState *env, target_ulong addr, env->mem_log_stores[slot].data32 = val; } +static void do_check_reg_write(CPUHexagonState *env, int rnum) +{ + target_ulong mask = 1 << rnum; + if (rnum >= NUM_GPREGS) { + return; + } + if ((env->gpreg_written & mask) == 0) { + env->gpreg_written |= mask; + return; + } +#ifdef CONFIG_USER_ONLY + hexagon_raise_exception_err(env, HEX_CAUSE_REG_WRITE_CONFLICT, 0); +#else + env->cause_code = HEX_CAUSE_REG_WRITE_CONFLICT; + hexagon_raise_exception_err(env, HEX_EVENT_PRECISE, 0); +#endif +} + void log_store64(CPUHexagonState *env, target_ulong addr, int64_t val, int width, int slot) { @@ -67,6 +85,32 @@ void log_store64(CPUHexagonState *env, target_ulong addr, env->mem_log_stores[slot].data64 = val; } +void HELPER(check_reg_write)(CPUHexagonState *env, int rnum) +{ + do_check_reg_write(env, rnum); +} + +void HELPER(check_cond_reg_write)(CPUHexagonState *env, int rnum, int skip) +{ + if (!skip) { + do_check_reg_write(env, rnum); + } +} + +void HELPER(check_reg_write_pair)(CPUHexagonState *env, int rnum) +{ + do_check_reg_write(env, rnum); + do_check_reg_write(env, rnum + 1); +} + +void HELPER(check_cond_reg_write_pair)(CPUHexagonState *env, int rnum, int skip) +{ + if (!skip) { + do_check_reg_write(env, rnum); + do_check_reg_write(env, rnum + 1); + } +} + static void commit_store(CPUHexagonState *env, int slot_num, uintptr_t ra) { uint8_t width = env->mem_log_stores[slot_num].width; @@ -463,11 +507,11 @@ void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask) * If the load is in slot 0 and there is a store in slot1 (that * wasn't cancelled), we have to do the store first. */ -static void check_noshuf(CPUHexagonState *env, bool pkt_has_store_s1, +static void check_noshuf(CPUHexagonState *env, bool pkt_has_scalar_store_s1, uint32_t slot, target_ulong vaddr, int size, uintptr_t ra) { - if (slot == 0 && pkt_has_store_s1 && + if (slot == 0 && pkt_has_scalar_store_s1 && ((env->slot_cancelled & (1 << 1)) == 0)) { probe_read(env, vaddr, size, MMU_USER_IDX, ra); commit_store(env, 1, ra); diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index fe7858703c8cb..5b0bb79b66112 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -50,6 +50,7 @@ TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; TCGv hex_pred[NUM_PREGS]; TCGv hex_slot_cancelled; TCGv hex_new_value_usr; +TCGv hex_gpreg_written; TCGv hex_store_addr[STORES_MAX]; TCGv hex_store_width[STORES_MAX]; TCGv hex_store_val32[STORES_MAX]; @@ -234,11 +235,9 @@ static bool check_for_attrib(Packet *pkt, int attrib) static bool need_slot_cancelled(Packet *pkt) { - /* We only need slot_cancelled for conditional store instructions */ for (int i = 0; i < pkt->num_insns; i++) { uint16_t opcode = pkt->insn[i].opcode; - if (GET_ATTRIB(opcode, A_CONDEXEC) && - GET_ATTRIB(opcode, A_SCALAR_STORE)) { + if (GET_ATTRIB(opcode, A_CONDEXEC)) { return true; } } @@ -440,6 +439,7 @@ static void gen_start_packet(DisasContext *ctx) * gen phase, so clear it again. */ bitmap_zero(ctx->pregs_written, NUM_PREGS); + tcg_gen_movi_tl(hex_gpreg_written, 0); /* Initialize the runtime state for packet semantics */ if (need_slot_cancelled(pkt)) { @@ -693,11 +693,11 @@ static void process_store_log(DisasContext *ctx) * the memory accesses overlap. */ Packet *pkt = ctx->pkt; - if (pkt->pkt_has_store_s1) { + if (pkt->pkt_has_scalar_store_s1) { g_assert(!pkt->pkt_has_dczeroa); process_store(ctx, 1); } - if (pkt->pkt_has_store_s0) { + if (pkt->pkt_has_scalar_store_s0) { g_assert(!pkt->pkt_has_dczeroa); process_store(ctx, 0); } @@ -822,8 +822,9 @@ static void gen_commit_packet(DisasContext *ctx) * involved in committing the packet. */ Packet *pkt = ctx->pkt; - bool has_store_s0 = pkt->pkt_has_store_s0; - bool has_store_s1 = (pkt->pkt_has_store_s1 && !ctx->s1_store_processed); + bool has_store_s0 = pkt->pkt_has_scalar_store_s0; + bool has_store_s1 = + (pkt->pkt_has_scalar_store_s1 && !ctx->s1_store_processed); bool has_hvx_store = pkt_has_hvx_store(pkt); if (pkt->pkt_has_dczeroa) { /* @@ -1058,6 +1059,9 @@ void hexagon_translate_init(void) hex_new_value_usr = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, new_value_usr), "new_value_usr"); + hex_gpreg_written = tcg_global_mem_new(tcg_env, + offsetof(CPUHexagonState, gpreg_written), "gpreg_written"); + for (i = 0; i < NUM_PREGS; i++) { hex_pred[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, pred[i]), diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index d251e2233fda7..74ebf81c62e56 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -270,6 +270,7 @@ extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; extern TCGv hex_pred[NUM_PREGS]; extern TCGv hex_slot_cancelled; extern TCGv hex_new_value_usr; +extern TCGv hex_gpreg_written; extern TCGv hex_store_addr[STORES_MAX]; extern TCGv hex_store_width[STORES_MAX]; extern TCGv hex_store_val32[STORES_MAX]; diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index e5182c01d8a0c..2c0e8d9494e7b 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -52,6 +52,7 @@ HEX_TESTS += hvx_misc HEX_TESTS += hvx_histogram HEX_TESTS += invalid-slots HEX_TESTS += unaligned_pc +HEX_TESTS += multiple-writes run-and-check-exception = $(call run-test,$2,$3 2>$2.stderr; \ test $$? -eq 1 && grep -q "exception $(strip $1)" $2.stderr) @@ -59,6 +60,9 @@ run-and-check-exception = $(call run-test,$2,$3 2>$2.stderr; \ run-invalid-slots: invalid-slots $(call run-and-check-exception, 0x15, $@, $(QEMU) $(QEMU_OPTS) $<) +run-multiple-writes: multiple-writes + $(call run-and-check-exception, 0x1d, $@, $(QEMU) $(QEMU_OPTS) $<) + HEX_TESTS += test_abs HEX_TESTS += test_bitcnt HEX_TESTS += test_bitsplit diff --git a/tests/tcg/hexagon/multiple-writes.S b/tests/tcg/hexagon/multiple-writes.S new file mode 100644 index 0000000000000..2e8b240be7d09 --- /dev/null +++ b/tests/tcg/hexagon/multiple-writes.S @@ -0,0 +1,32 @@ +/* + * Copyright(c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + + .text +.global _start +_start: + r2 = #0 + p0 = !cmp.eq(r2,r2) // False + p1 = cmp.eq(r2,r2) // True + + /* + * This instruction will use slot 0 and get cancelled. The cancellation + * state should be cleaned for the next packet. + */ + if (p0) memw(r2) = r2 + + /* + * This packet is crafted so that there are two conditional writes to + * the same register and also an instruction that uses slot 0. The idea + * is that, if the cancellation state is not properly cleaned from the + * previous packet, we may end up considering this packet as cancelled as + * well (due to r1 = #0) and fail to detect the multiple writes to r6. + */ + { + r1 = #0 + if (p1) r6 = r2 + if (!p0) r6 = r2 + } + + jump pass