From a1852002c7509569eaaedb783925f34350fe0a84 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Mon, 20 May 2024 12:53:04 -0300 Subject: [PATCH 1/6] Hexagon: fix HVX store new At 09a7e7db0f (Hexagon (target/hexagon) Remove uses of op_regs_generated.h.inc, 2024-03-06), we've changed the logic of check_new_value() to use the new pre-calculated packet->insn[...].dest_idx instead of calculating the index on the fly using opcode_reginfo[...]. The dest_idx index is calculated roughly like the following: for reg in iset[tag]["syntax"]: if reg.is_written(): dest_idx = regno break Thus, we take the first register that is writtable. Before that, however, we also used to follow an alphabetical order on the register type: 'd', 'e', 'x', and 'y'. No longer following that makes us select the wrong register index and the HVX store new instruction does not update the memory like expected. Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Reviewed-by: Taylor Simpson Message-Id: Signed-off-by: Brian Cain --- target/hexagon/gen_trans_funcs.py | 9 ++++++--- tests/tcg/hexagon/hvx_misc.c | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/target/hexagon/gen_trans_funcs.py b/target/hexagon/gen_trans_funcs.py index 9f86b4edbd..30f0c73e0c 100755 --- a/target/hexagon/gen_trans_funcs.py +++ b/target/hexagon/gen_trans_funcs.py @@ -89,6 +89,7 @@ def gen_trans_funcs(f): new_read_idx = -1 dest_idx = -1 + dest_idx_reg_id = None has_pred_dest = "false" for regno, (reg_type, reg_id, *_) in enumerate(regs): reg = hex_common.get_register(tag, reg_type, reg_id) @@ -97,9 +98,11 @@ def gen_trans_funcs(f): """)) if reg.is_read() and reg.is_new(): new_read_idx = regno - # dest_idx should be the first destination, so check for -1 - if reg.is_written() and dest_idx == -1: - dest_idx = regno + if reg.is_written(): + # dest_idx should be the first destination alphabetically + if dest_idx_reg_id is None or reg_id < dest_idx_reg_id: + dest_idx = regno + dest_idx_reg_id = reg_id if reg_type == "P" and reg.is_written() and not reg.is_read(): has_pred_dest = "true" diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c index 1fe14b5158..90c3733da0 100644 --- a/tests/tcg/hexagon/hvx_misc.c +++ b/tests/tcg/hexagon/hvx_misc.c @@ -474,6 +474,27 @@ static void test_vcombine(void) check_output_w(__LINE__, BUFSIZE); } +void test_store_new() +{ + asm volatile( + "r0 = #0x12345678\n" + "v0 = vsplat(r0)\n" + "r0 = #0xff00ff00\n" + "v1 = vsplat(r0)\n" + "{\n" + " vdeal(v1,v0,r0)\n" + " vmem(%0) = v0.new\n" + "}\n" + : + : "r"(&output[0]) + : "r0", "v0", "v1", "memory" + ); + for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) { + expect[0].w[i] = 0x12345678; + } + check_output_w(__LINE__, 1); +} + int main() { init_buffers(); @@ -515,6 +536,8 @@ int main() test_vcombine(); + test_store_new(); + puts(err ? "FAIL" : "PASS"); return err ? 1 : 0; } From e1b526f1d86b7c51b97227989b9ba2925cc53069 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Fri, 3 May 2024 13:53:15 -0300 Subject: [PATCH 2/6] Hexagon: add PC alignment check and exception The Hexagon Programmer's Reference Manual says that the exception 0x1e should be raised upon an unaligned program counter. Let's implement that and also add some tests. Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Richard Henderson Reviewed-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <277b7aeda2c717a96d4dde936b3ac77707cb6517.1714755107.git.quic_mathbern@quicinc.com> Signed-off-by: Brian Cain --- linux-user/hexagon/cpu_loop.c | 4 ++ target/hexagon/cpu.h | 7 ++ target/hexagon/cpu_bits.h | 4 ++ target/hexagon/macros.h | 3 - target/hexagon/op_helper.c | 9 ++- tests/tcg/hexagon/Makefile.target | 2 + tests/tcg/hexagon/unaligned_pc.c | 107 ++++++++++++++++++++++++++++++ 7 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 tests/tcg/hexagon/unaligned_pc.c diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 7f1499ed28..d41159e52a 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -60,6 +60,10 @@ void cpu_loop(CPUHexagonState *env) env->gpr[0] = ret; } break; + case HEX_EXCP_PC_NOT_ALIGNED: + force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, + env->gpr[HEX_REG_R31]); + break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 3eef58fe8f..764f3c38cc 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -134,6 +134,10 @@ struct ArchCPU { FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1) +G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, + uint32_t exception, + uintptr_t pc); + static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, uint64_t *cs_base, uint32_t *flags) { @@ -144,6 +148,9 @@ static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1); } *flags = hex_flags; + if (*pc & PCALIGN_MASK) { + hexagon_raise_exception_err(env, HEX_EXCP_PC_NOT_ALIGNED, 0); + } } typedef HexagonCPU ArchCPU; diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index 96fef71729..4279281a71 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -20,9 +20,13 @@ #include "qemu/bitops.h" +#define PCALIGN 4 +#define PCALIGN_MASK (PCALIGN - 1) + #define HEX_EXCP_FETCH_NO_UPAGE 0x012 #define HEX_EXCP_INVALID_PACKET 0x015 #define HEX_EXCP_INVALID_OPCODE 0x015 +#define HEX_EXCP_PC_NOT_ALIGNED 0x01e #define HEX_EXCP_PRIV_NO_UREAD 0x024 #define HEX_EXCP_PRIV_NO_UWRITE 0x025 diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index feb798c6c0..ee3d4c88e7 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -22,9 +22,6 @@ #include "hex_regs.h" #include "reg_fields.h" -#define PCALIGN 4 -#define PCALIGN_MASK (PCALIGN - 1) - #define GET_FIELD(FIELD, REGIN) \ fEXTRACTU_BITS(REGIN, reg_field_info[FIELD].width, \ reg_field_info[FIELD].offset) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index da10ac5847..ae5a605513 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -36,10 +36,9 @@ #define SF_MANTBITS 23 /* Exceptions processing helpers */ -static G_NORETURN -void do_raise_exception_err(CPUHexagonState *env, - uint32_t exception, - uintptr_t pc) +G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, + uint32_t exception, + uintptr_t pc) { CPUState *cs = env_cpu(env); qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception); @@ -49,7 +48,7 @@ void do_raise_exception_err(CPUHexagonState *env, G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp) { - do_raise_exception_err(env, excp, 0); + hexagon_raise_exception_err(env, excp, 0); } void log_store32(CPUHexagonState *env, target_ulong addr, diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index f839b2c0d5..e5182c01d8 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -51,6 +51,7 @@ HEX_TESTS += scatter_gather HEX_TESTS += hvx_misc HEX_TESTS += hvx_histogram HEX_TESTS += invalid-slots +HEX_TESTS += unaligned_pc run-and-check-exception = $(call run-test,$2,$3 2>$2.stderr; \ test $$? -eq 1 && grep -q "exception $(strip $1)" $2.stderr) @@ -107,6 +108,7 @@ overflow: overflow.c hex_test.h preg_alias: preg_alias.c hex_test.h read_write_overlap: read_write_overlap.c hex_test.h reg_mut: reg_mut.c hex_test.h +unaligned_pc: unaligned_pc.c # This test has to be compiled for the -mv67t target usr: usr.c hex_test.h diff --git a/tests/tcg/hexagon/unaligned_pc.c b/tests/tcg/hexagon/unaligned_pc.c new file mode 100644 index 0000000000..e9dc7cb8b5 --- /dev/null +++ b/tests/tcg/hexagon/unaligned_pc.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include + +/* will be changed in signal handler */ +volatile sig_atomic_t completed_tests; +static jmp_buf after_test; +static int nr_tests; + +void __attribute__((naked)) test_return(void) +{ + asm volatile( + "allocframe(#0x8)\n" + "r0 = #0xffffffff\n" + "framekey = r0\n" + "dealloc_return\n" + : + : + : "r0", "r29", "r30", "r31", "framekey"); +} + +void test_endloop(void) +{ + asm volatile( + "loop0(1f, #2)\n" + "1: r0 = #0x3\n" + "sa0 = r0\n" + "{ nop }:endloop0\n" + : + : + : "r0", "sa0", "lc0", "usr"); +} + +asm( + ".pushsection .text.unaligned\n" + ".org 0x3\n" + ".global test_multi_cof_unaligned\n" + "test_multi_cof_unaligned:\n" + " jumpr r31\n" + ".popsection\n" +); + +#define SYS_EXIT 94 + +void test_multi_cof(void) +{ + asm volatile( + "p0 = cmp.eq(r0, r0)\n" + "{\n" + " if (p0) jump test_multi_cof_unaligned\n" + " if (!p0) jump 1f\n" + "}\n" + "1:" + " r0 = #1\n" + " r6 = #%0\n" + " trap0(#1)\n" + : + : "i"(SYS_EXIT) + : "p0", "r0", "r6"); +} + +void sigbus_handler(int signum) +{ + /* retore framekey after test_return */ + asm volatile( + "r0 = #0\n" + "framekey = r0\n" + : + : + : "r0", "framekey"); + printf("Test %d complete\n", completed_tests); + completed_tests++; + siglongjmp(after_test, 1); +} + +void test_done(void) +{ + int err = (completed_tests != nr_tests); + puts(err ? "FAIL" : "PASS"); + exit(err); +} + +typedef void (*test_fn)(void); + +int main() +{ + test_fn tests[] = { test_return, test_endloop, test_multi_cof, test_done }; + nr_tests = (sizeof(tests) / sizeof(tests[0])) - 1; + + struct sigaction sa = { + .sa_sigaction = sigbus_handler, + .sa_flags = SA_SIGINFO + }; + + if (sigaction(SIGBUS, &sa, NULL) < 0) { + perror("sigaction"); + return EXIT_FAILURE; + } + + sigsetjmp(after_test, 1); + tests[completed_tests](); + + /* should never get here */ + puts("FAIL"); + return 1; +} From 49c1f7a472ebff23b4f374a1d5201250f3fdbd76 Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Thu, 23 May 2024 14:58:58 +0200 Subject: [PATCH 3/6] target/hexagon: idef-parser remove unused defines Before switching to GArray/g_string_printf we used fixed size arrays for output buffers and instructions arguments among other things. Macros defining the sizes of these buffers were left behind, remove them. Signed-off-by: Anton Johansson Reviewed-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240523125901.27797-2-anjo@rev.ng> Signed-off-by: Brian Cain --- target/hexagon/idef-parser/idef-parser.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/target/hexagon/idef-parser/idef-parser.h b/target/hexagon/idef-parser/idef-parser.h index 3faa1deecd..8594cbe3a2 100644 --- a/target/hexagon/idef-parser/idef-parser.h +++ b/target/hexagon/idef-parser/idef-parser.h @@ -23,16 +23,6 @@ #include #include -#define TCGV_NAME_SIZE 7 -#define MAX_WRITTEN_REGS 32 -#define OFFSET_STR_LEN 32 -#define ALLOC_LIST_LEN 32 -#define ALLOC_NAME_SIZE 32 -#define INIT_LIST_LEN 32 -#define OUT_BUF_LEN (1024 * 1024) -#define SIGNATURE_BUF_LEN (128 * 1024) -#define HEADER_BUF_LEN (128 * 1024) - /* Variadic macros to wrap the buffer printing functions */ #define EMIT(c, ...) \ do { \ From 348fec2afe9f03b1761caff44ea6290357d87c01 Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Thu, 23 May 2024 14:58:59 +0200 Subject: [PATCH 4/6] target/hexagon: idef-parser remove undefined functions Signed-off-by: Anton Johansson Reviewed-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240523125901.27797-3-anjo@rev.ng> Signed-off-by: Brian Cain --- target/hexagon/idef-parser/parser-helpers.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/target/hexagon/idef-parser/parser-helpers.h b/target/hexagon/idef-parser/parser-helpers.h index 7c58087169..2087d534a9 100644 --- a/target/hexagon/idef-parser/parser-helpers.h +++ b/target/hexagon/idef-parser/parser-helpers.h @@ -143,8 +143,6 @@ void commit(Context *c); #define OUT(c, locp, ...) FOR_EACH((c), (locp), OUT_IMPL, __VA_ARGS__) -const char *cmp_swap(Context *c, YYLTYPE *locp, const char *type); - /** * Temporary values creation */ @@ -236,8 +234,6 @@ HexValue gen_extract_op(Context *c, HexValue *index, HexExtract *extract); -HexValue gen_read_reg(Context *c, YYLTYPE *locp, HexValue *reg); - void gen_write_reg(Context *c, YYLTYPE *locp, HexValue *reg, HexValue *value); void gen_assign(Context *c, @@ -274,13 +270,6 @@ HexValue gen_ctpop_op(Context *c, YYLTYPE *locp, HexValue *src); HexValue gen_rotl(Context *c, YYLTYPE *locp, HexValue *src, HexValue *n); -HexValue gen_deinterleave(Context *c, YYLTYPE *locp, HexValue *mixed); - -HexValue gen_interleave(Context *c, - YYLTYPE *locp, - HexValue *odd, - HexValue *even); - HexValue gen_carry_from_add(Context *c, YYLTYPE *locp, HexValue *op1, @@ -349,8 +338,6 @@ HexValue gen_rvalue_ternary(Context *c, YYLTYPE *locp, HexValue *cond, const char *cond_to_str(TCGCond cond); -void emit_header(Context *c); - void emit_arg(Context *c, YYLTYPE *locp, HexValue *arg); void emit_footer(Context *c); From 95408ad8e24c4364086f185285039e89927dad6c Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Thu, 23 May 2024 14:59:00 +0200 Subject: [PATCH 5/6] target/hexagon: idef-parser fix leak of init_list gen_inst_init_args() is called for instructions using a predicate as an rvalue. Upon first call, the list of arguments which might need initialization init_list is freed to indicate that they have been processed. For instructions without an rvalue predicate, gen_inst_init_args() isn't called and init_list will never be freed. Free init_list from free_instruction() if it hasn't already been freed. A comment in free_instruction is also updated. Signed-off-by: Anton Johansson Reviewed-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240523125901.27797-4-anjo@rev.ng> Signed-off-by: Brian Cain --- target/hexagon/idef-parser/parser-helpers.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index 95f2b43076..c150c308be 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -2121,9 +2121,16 @@ void free_instruction(Context *c) g_string_free(g_array_index(c->inst.strings, GString*, i), TRUE); } g_array_free(c->inst.strings, TRUE); + /* + * Free list of arguments that might need initialization, if they haven't + * already been freed. + */ + if (c->inst.init_list) { + g_array_free(c->inst.init_list, TRUE); + } /* Free INAME token value */ g_string_free(c->inst.name, TRUE); - /* Free variables and registers */ + /* Free declared TCGv variables */ g_array_free(c->inst.allocated, TRUE); /* Initialize instruction-specific portion of the context */ memset(&(c->inst), 0, sizeof(Inst)); From 1967a1ea985299c090dfd3efc1e5323ce60d75df Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Thu, 23 May 2024 14:59:01 +0200 Subject: [PATCH 6/6] target/hexagon: idef-parser simplify predicate init Only predicate instruction arguments need to be initialized by idef-parser. This commit removes registers from the init_list and simplifies gen_inst_init_args() slightly. Signed-off-by: Anton Johansson Reviewed-by: Taylor Simpson Reviewed-by: Brian Cain Message-Id: <20240523125901.27797-5-anjo@rev.ng> Signed-off-by: Brian Cain --- target/hexagon/idef-parser/idef-parser.y | 2 -- target/hexagon/idef-parser/parser-helpers.c | 26 +++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/target/hexagon/idef-parser/idef-parser.y b/target/hexagon/idef-parser/idef-parser.y index cd2612eb8c..9ffb9f9699 100644 --- a/target/hexagon/idef-parser/idef-parser.y +++ b/target/hexagon/idef-parser/idef-parser.y @@ -233,8 +233,6 @@ code : '{' statements '}' argument_decl : REG { emit_arg(c, &@1, &$1); - /* Enqueue register into initialization list */ - g_array_append_val(c->inst.init_list, $1); } | PRED { diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index c150c308be..a7dcd85fe4 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -1652,26 +1652,28 @@ void gen_inst(Context *c, GString *iname) /* - * Initialize declared but uninitialized registers, but only for - * non-conditional instructions + * Initialize declared but uninitialized instruction arguments. Only needed for + * predicate arguments, initialization of registers is handled by the Hexagon + * frontend. */ void gen_inst_init_args(Context *c, YYLTYPE *locp) { + HexValue *val = NULL; + char suffix; + + /* If init_list is NULL arguments have already been initialized */ if (!c->inst.init_list) { return; } for (unsigned i = 0; i < c->inst.init_list->len; i++) { - HexValue *val = &g_array_index(c->inst.init_list, HexValue, i); - if (val->type == REGISTER_ARG) { - /* Nothing to do here */ - } else if (val->type == PREDICATE) { - char suffix = val->is_dotnew ? 'N' : 'V'; - EMIT_HEAD(c, "tcg_gen_movi_i%u(P%c%c, 0);\n", val->bit_width, - val->pred.id, suffix); - } else { - yyassert(c, locp, false, "Invalid arg type!"); - } + val = &g_array_index(c->inst.init_list, HexValue, i); + suffix = val->is_dotnew ? 'N' : 'V'; + yyassert(c, locp, val->type == PREDICATE, + "Only predicates need to be initialized!"); + yyassert(c, locp, val->bit_width == 32, + "Predicates should always be 32 bits"); + EMIT_HEAD(c, "tcg_gen_movi_i32(P%c%c, 0);\n", val->pred.id, suffix); } /* Free argument init list once we have initialized everything */