Introduces simple end-to-end tests of helper-to-tcg of functions the
translator is expected to handle, any translation failure will result in
a test failure. More test cases to come.
Signed-off-by: Anton Johansson <anjo@rev.ng>
---
subprojects/helper-to-tcg/meson.build | 2 +
subprojects/helper-to-tcg/tests/cpustate.c | 45 +++++++
subprojects/helper-to-tcg/tests/ldst.c | 17 +++
subprojects/helper-to-tcg/tests/meson.build | 24 ++++
subprojects/helper-to-tcg/tests/scalar.c | 15 +++
.../helper-to-tcg/tests/tcg-global-mappings.h | 115 ++++++++++++++++++
subprojects/helper-to-tcg/tests/vector.c | 26 ++++
7 files changed, 244 insertions(+)
create mode 100644 subprojects/helper-to-tcg/tests/cpustate.c
create mode 100644 subprojects/helper-to-tcg/tests/ldst.c
create mode 100644 subprojects/helper-to-tcg/tests/meson.build
create mode 100644 subprojects/helper-to-tcg/tests/scalar.c
create mode 100644 subprojects/helper-to-tcg/tests/tcg-global-mappings.h
create mode 100644 subprojects/helper-to-tcg/tests/vector.c
diff --git a/subprojects/helper-to-tcg/meson.build b/subprojects/helper-to-tcg/meson.build
index 4f045eb1da..e09f121e18 100644
--- a/subprojects/helper-to-tcg/meson.build
+++ b/subprojects/helper-to-tcg/meson.build
@@ -80,3 +80,5 @@ pipeline = executable('helper-to-tcg', sources,
include_directories: ['passes', './', 'include'] + [incdir],
link_args: [ldflags] + [libs] + [syslibs],
cpp_args: cpp_args)
+
+subdir('tests')
diff --git a/subprojects/helper-to-tcg/tests/cpustate.c b/subprojects/helper-to-tcg/tests/cpustate.c
new file mode 100644
index 0000000000..79205da75e
--- /dev/null
+++ b/subprojects/helper-to-tcg/tests/cpustate.c
@@ -0,0 +1,45 @@
+#include <stdint.h>
+#include "tcg-global-mappings.h"
+
+typedef struct SpecialData {
+ uint32_t a;
+ uint32_t unmapped_field;
+} SpecialData;
+
+typedef struct CPUArchState {
+ uint32_t regs[32];
+ uint32_t unmapped_field;
+ SpecialData data[8];
+ uint32_t mapped_field;
+} CPUArchState;
+
+/* Dummy struct, in QEMU this would correspond to TCGv_i32 in tcg.h */
+typedef struct TCGv_i32 {} TCGv_i32;
+/* Global TCGv's representing CPU state */
+TCGv_i32 tcg_regs[32];
+TCGv_i32 tcg_a[8];
+TCGv_i32 tcg_field;
+
+cpu_tcg_mapping mappings[] = {
+ CPU_TCG_MAP_ARRAY(CPUArchState, tcg_regs, regs, NULL),
+ CPU_TCG_MAP_ARRAY_OF_STRUCTS(CPUArchState, tcg_a, data, a, NULL),
+ CPU_TCG_MAP(CPUArchState, tcg_field, mapped_field, NULL),
+};
+
+__attribute__((annotate ("immediate: 1")))
+uint32_t helper_reg(CPUArchState *env, uint32_t i) {
+ return env->regs[i];
+}
+
+__attribute__((annotate ("immediate: 1")))
+uint32_t helper_data_a(CPUArchState *env, uint32_t i) {
+ return env->data[i].a;
+}
+
+uint32_t helper_single_mapped(CPUArchState *env) {
+ return env->mapped_field;
+}
+
+uint32_t helper_unmapped(CPUArchState *env) {
+ return env->unmapped_field;
+}
diff --git a/subprojects/helper-to-tcg/tests/ldst.c b/subprojects/helper-to-tcg/tests/ldst.c
new file mode 100644
index 0000000000..44d32d0875
--- /dev/null
+++ b/subprojects/helper-to-tcg/tests/ldst.c
@@ -0,0 +1,17 @@
+#include <stdint.h>
+
+/* Opaque CPU state type, will be mapped to tcg_env */
+struct CPUArchState;
+typedef struct CPUArchState CPUArchState;
+
+/* Prototype of QEMU helper guest load/store functions, see exec/cpu_ldst.h */
+uint32_t cpu_ldub_data(CPUArchState *, uint32_t ptr);
+void cpu_stb_data(CPUArchState *, uint32_t ptr, uint32_t data);
+
+uint32_t helper_ld8(CPUArchState *env, uint32_t addr) {
+ return cpu_ldub_data(env, addr);
+}
+
+void helper_st8(CPUArchState *env, uint32_t addr, uint32_t data) {
+ return cpu_stb_data(env, addr, data);
+}
diff --git a/subprojects/helper-to-tcg/tests/meson.build b/subprojects/helper-to-tcg/tests/meson.build
new file mode 100644
index 0000000000..e7b9329c82
--- /dev/null
+++ b/subprojects/helper-to-tcg/tests/meson.build
@@ -0,0 +1,24 @@
+sources = [
+ 'scalar.c',
+ 'vector.c',
+ 'ldst.c',
+ 'cpustate.c',
+]
+
+
+foreach s : sources
+ name = s.split('.')[0]
+ name_ll = name + '.ll'
+ ll = custom_target(name_ll,
+ input: s,
+ output: name_ll,
+ command: [clang, '-O0', '-Xclang', '-disable-O0-optnone',
+ '-S', '-emit-llvm', '-o', '@OUTPUT@', '@INPUT@']
+ )
+ test(name, pipeline,
+ args: [ll,
+ '--mmu-index-function=tb_mmu_index',
+ '--tcg-global-mappings=mappings',
+ '--translate-all-helpers'],
+ suite: 'end-to-end')
+endforeach
diff --git a/subprojects/helper-to-tcg/tests/scalar.c b/subprojects/helper-to-tcg/tests/scalar.c
new file mode 100644
index 0000000000..09af72371d
--- /dev/null
+++ b/subprojects/helper-to-tcg/tests/scalar.c
@@ -0,0 +1,15 @@
+#include <stdint.h>
+
+/* Simple arithmetic */
+uint32_t helper_add(uint32_t a, uint32_t b) {
+ return a + b;
+}
+
+/* Control flow reducable to conditinal move */
+uint32_t helper_cmov(uint32_t c0, uint32_t c1, uint32_t a, uint32_t b) {
+ if (c0 < c1) {
+ return a;
+ } else {
+ return b;
+ }
+}
diff --git a/subprojects/helper-to-tcg/tests/tcg-global-mappings.h b/subprojects/helper-to-tcg/tests/tcg-global-mappings.h
new file mode 100644
index 0000000000..fed3577bcf
--- /dev/null
+++ b/subprojects/helper-to-tcg/tests/tcg-global-mappings.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright(c) 2024 rev.ng Labs Srl. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TCG_GLOBAL_MAP_H
+#define TCG_GLOBAL_MAP_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define _stringify(STR) #STR
+#define stringify(STR) _stringify(TR)
+
+/**
+ * cpu_tcg_mapping: Declarative mapping of offsets into a struct to global
+ * TCGvs. Parseable by LLVM-based tools.
+ * @tcg_var_name: String name of the TCGv to use as destination of the mapping.
+ * @tcg_var_base_address: Address of the above TCGv.
+ * @cpu_var_names: Array of printable names of TCGvs, used when calling
+ * tcg_global_mem_new from init_cpu_tcg_mappings.
+ * @cpu_var_base_offset: Base offset of field in the source struct.
+ * @cpu_var_size: Size of field in the source struct, if the field is an array,
+ * this holds the size of the element type.
+ * @cpu_var_stride: Stride between array elements in the source struct. This
+ * can be greater than the element size when mapping a field
+ * in an array of structs.
+ * @number_of_elements: Number of elements of array in the source struct.
+ */
+typedef struct cpu_tcg_mapping {
+ const char *tcg_var_name;
+ void *tcg_var_base_address;
+
+ const char *const *cpu_var_names;
+ size_t cpu_var_base_offset;
+ size_t cpu_var_size;
+ size_t cpu_var_stride;
+
+ size_t number_of_elements;
+} cpu_tcg_mapping;
+
+#define STRUCT_SIZEOF_FIELD(S, member) sizeof(((S *)0)->member)
+
+#define STRUCT_ARRAY_SIZE(S, array) \
+ (STRUCT_SIZEOF_FIELD(S, array) / STRUCT_SIZEOF_FIELD(S, array[0]))
+
+/*
+ * Following are a few macros that aid in constructing
+ * `cpu_tcg_mapping`s for a few common cases.
+ */
+
+/* Map between single CPU register and to TCG global */
+#define CPU_TCG_MAP(struct_type, tcg_var, cpu_var, name_str) \
+ (cpu_tcg_mapping) \
+ { \
+ .tcg_var_name = stringify(tcg_var), .tcg_var_base_address = &tcg_var, \
+ .cpu_var_names = (const char *[]){name_str}, \
+ .cpu_var_base_offset = offsetof(struct_type, cpu_var), \
+ .cpu_var_size = STRUCT_SIZEOF_FIELD(struct_type, cpu_var), \
+ .cpu_var_stride = 0, .number_of_elements = 1, \
+ }
+
+/* Map between array of CPU registers and array of TCG globals. */
+#define CPU_TCG_MAP_ARRAY(struct_type, tcg_var, cpu_var, names) \
+ (cpu_tcg_mapping) \
+ { \
+ .tcg_var_name = #tcg_var, .tcg_var_base_address = tcg_var, \
+ .cpu_var_names = names, \
+ .cpu_var_base_offset = offsetof(struct_type, cpu_var), \
+ .cpu_var_size = STRUCT_SIZEOF_FIELD(struct_type, cpu_var[0]), \
+ .cpu_var_stride = STRUCT_SIZEOF_FIELD(struct_type, cpu_var[0]), \
+ .number_of_elements = STRUCT_ARRAY_SIZE(struct_type, cpu_var), \
+ }
+
+/*
+ * Map between single member in an array of structs to an array
+ * of TCG globals, e.g. maps
+ *
+ * cpu_state.array_of_structs[i].member
+ *
+ * to
+ *
+ * tcg_global_member[i]
+ */
+#define CPU_TCG_MAP_ARRAY_OF_STRUCTS(struct_type, tcg_var, cpu_struct, \
+ cpu_var, names) \
+ (cpu_tcg_mapping) \
+ { \
+ .tcg_var_name = #tcg_var, .tcg_var_base_address = tcg_var, \
+ .cpu_var_names = names, \
+ .cpu_var_base_offset = offsetof(struct_type, cpu_struct[0].cpu_var), \
+ .cpu_var_size = \
+ STRUCT_SIZEOF_FIELD(struct_type, cpu_struct[0].cpu_var), \
+ .cpu_var_stride = STRUCT_SIZEOF_FIELD(struct_type, cpu_struct[0]), \
+ .number_of_elements = STRUCT_ARRAY_SIZE(struct_type, cpu_struct), \
+ }
+
+extern cpu_tcg_mapping tcg_global_mappings[];
+extern size_t tcg_global_mapping_count;
+
+void init_cpu_tcg_mappings(cpu_tcg_mapping *mappings, size_t size);
+
+#endif /* TCG_GLOBAL_MAP_H */
diff --git a/subprojects/helper-to-tcg/tests/vector.c b/subprojects/helper-to-tcg/tests/vector.c
new file mode 100644
index 0000000000..c40f63b60d
--- /dev/null
+++ b/subprojects/helper-to-tcg/tests/vector.c
@@ -0,0 +1,26 @@
+#include <stdint.h>
+
+__attribute__((annotate("ptr-to-offset: 0"))) void
+helper_vec_splat_reg(void *restrict d, uint8_t imm)
+{
+ for (int i = 0; i < 32; ++i) {
+ ((uint8_t *)d)[i] = imm;
+ }
+}
+
+__attribute__((annotate("immediate: 1")))
+__attribute__((annotate("ptr-to-offset: 0"))) void
+helper_vec_splat_imm(void *restrict d, uint8_t imm)
+{
+ for (int i = 0; i < 32; ++i) {
+ ((uint8_t *)d)[i] = imm;
+ }
+}
+
+__attribute__((annotate("ptr-to-offset: 0, 1, 2"))) void
+helper_vec_add(void *restrict d, void *restrict a, void *restrict b)
+{
+ for (int i = 0; i < 32; ++i) {
+ ((uint8_t *)d)[i] = ((uint8_t *)a)[i] + ((uint8_t *)b)[i];
+ }
+}
--
2.45.2