* add help option for -audio and -audiodev

* another missing memory barrier for dirty pages
 * target/i386: Raise #GP on unaligned m128 accesses
 * coverity fixes + improvements to components
 * add MMX and 3DNow! tests
 * SSE4a fixes
 * target/i386: TCG translation cleanups
 * update qboot submodule
 -----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmMocZcUHHBib256aW5p
 QHJlZGhhdC5jb20ACgkQv/vSX3jHroNV7Qf+NEoB8R0ug+ClMRe1Qqt8FXEd0eXE
 nT19q4rOWfmW4/L+wI6gpxhbxrxOuLwoZ8YvD8c6rQAdexMoHoeTvA1PAca4zZTo
 ISmW3bXsoHN2uGLPz4CKhjKBLCANtDkh3EWCwRFkLSRCLSRDhKPrG1Ue3fOgQ6GO
 riROcxbyYzvU/4uefSW+xG/Im9gftNF6occZZ59LrK7Xd8kwlb+E+EdsmzFw5f8O
 Q9irVQ8pX9ZM4BK2KiT16nZ0uSRwJqSJKbLI670nUEsj1jQCIgU3srgZHjAIvoir
 yivDs6oktgS/HkPD5CQoTX+fVDgEDM1TTF6P8r7uJopPXpzz+AHswfSJmg==
 =RVCS
 -----END PGP SIGNATURE-----

Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging

* add help option for -audio and -audiodev
* another missing memory barrier for dirty pages
* target/i386: Raise #GP on unaligned m128 accesses
* coverity fixes + improvements to components
* add MMX and 3DNow! tests
* SSE4a fixes
* target/i386: TCG translation cleanups
* update qboot submodule

# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmMocZcUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroNV7Qf+NEoB8R0ug+ClMRe1Qqt8FXEd0eXE
# nT19q4rOWfmW4/L+wI6gpxhbxrxOuLwoZ8YvD8c6rQAdexMoHoeTvA1PAca4zZTo
# ISmW3bXsoHN2uGLPz4CKhjKBLCANtDkh3EWCwRFkLSRCLSRDhKPrG1Ue3fOgQ6GO
# riROcxbyYzvU/4uefSW+xG/Im9gftNF6occZZ59LrK7Xd8kwlb+E+EdsmzFw5f8O
# Q9irVQ8pX9ZM4BK2KiT16nZ0uSRwJqSJKbLI670nUEsj1jQCIgU3srgZHjAIvoir
# yivDs6oktgS/HkPD5CQoTX+fVDgEDM1TTF6P8r7uJopPXpzz+AHswfSJmg==
# =RVCS
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 19 Sep 2022 09:41:43 EDT
# gpg:                using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg:                issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full]
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>" [full]
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (21 commits)
  qboot: update to latest submodule
  build: remove extra parentheses causing missing rebuilds
  target/i386: introduce insn_get_addr
  target/i386: REPZ and REPNZ are mutually exclusive
  target/i386: fix INSERTQ implementation
  target/i386: correctly mask SSE4a bit indices in register operands
  audio: add help option for -audio and -audiodev
  tests/tcg: remove old SSE tests
  tests/tcg: refine MMX support in SSE tests
  tests/tcg: i386: add MMX and 3DNow! tests
  tests/tcg: i386: fix typos in 3DNow! instructions
  tests: unit: add NULL-pointer check
  tests: test-qga: close socket on failure to connect
  tests: unit: simplify test-visitor-serialization list tests
  smbios: sanitize type from external type before checking have_fields_bitmap
  coverity: put NUBus under m68k component
  coverity: add new RISC-V component
  spapr_pci: fix leak in spapr_phb_vfio_get_loc_code
  kvm: fix memory leak on failure to read stats descriptors
  target/i386: Raise #GP on unaligned m128 accesses when required.
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2022-09-20 16:22:26 -04:00
commit 1b3a3383df
29 changed files with 870 additions and 744 deletions

View File

@ -145,7 +145,7 @@ NINJAFLAGS = $(if $V,-v) $(if $(MAKE.n), -n) $(if $(MAKE.k), -k0) \
$(filter-out -j, $(lastword -j1 $(filter -l% -j%, $(MAKEFLAGS)))) \ $(filter-out -j, $(lastword -j1 $(filter -l% -j%, $(MAKEFLAGS)))) \
-d keepdepfile -d keepdepfile
ninja-cmd-goals = $(or $(MAKECMDGOALS), all) ninja-cmd-goals = $(or $(MAKECMDGOALS), all)
ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g)))) ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g))
makefile-targets := build.ninja ctags TAGS cscope dist clean uninstall makefile-targets := build.ninja ctags TAGS cscope dist clean uninstall
# "ninja -t targets" also lists all prerequisites. If build system # "ninja -t targets" also lists all prerequisites. If build system

View File

@ -728,7 +728,23 @@ static bool dirty_gfn_is_dirtied(struct kvm_dirty_gfn *gfn)
static void dirty_gfn_set_collected(struct kvm_dirty_gfn *gfn) static void dirty_gfn_set_collected(struct kvm_dirty_gfn *gfn)
{ {
gfn->flags = KVM_DIRTY_GFN_F_RESET; /*
* Use a store-release so that the CPU that executes KVM_RESET_DIRTY_RINGS
* sees the full content of the ring:
*
* CPU0 CPU1 CPU2
* ------------------------------------------------------------------------------
* fill gfn0
* store-rel flags for gfn0
* load-acq flags for gfn0
* store-rel RESET for gfn0
* ioctl(RESET_RINGS)
* load-acq flags for gfn0
* check if flags have RESET
*
* The synchronization goes from CPU2 to CPU0 to CPU1.
*/
qatomic_store_release(&gfn->flags, KVM_DIRTY_GFN_F_RESET);
} }
/* /*
@ -3892,7 +3908,7 @@ exit:
typedef struct StatsDescriptors { typedef struct StatsDescriptors {
const char *ident; /* cache key, currently the StatsTarget */ const char *ident; /* cache key, currently the StatsTarget */
struct kvm_stats_desc *kvm_stats_desc; struct kvm_stats_desc *kvm_stats_desc;
struct kvm_stats_header *kvm_stats_header; struct kvm_stats_header kvm_stats_header;
QTAILQ_ENTRY(StatsDescriptors) next; QTAILQ_ENTRY(StatsDescriptors) next;
} StatsDescriptors; } StatsDescriptors;
@ -3923,7 +3939,7 @@ static StatsDescriptors *find_stats_descriptors(StatsTarget target, int stats_fd
descriptors = g_new0(StatsDescriptors, 1); descriptors = g_new0(StatsDescriptors, 1);
/* Read stats header */ /* Read stats header */
kvm_stats_header = g_malloc(sizeof(*kvm_stats_header)); kvm_stats_header = &descriptors->kvm_stats_header;
ret = read(stats_fd, kvm_stats_header, sizeof(*kvm_stats_header)); ret = read(stats_fd, kvm_stats_header, sizeof(*kvm_stats_header));
if (ret != sizeof(*kvm_stats_header)) { if (ret != sizeof(*kvm_stats_header)) {
error_setg(errp, "KVM stats: failed to read stats header: " error_setg(errp, "KVM stats: failed to read stats header: "
@ -3948,7 +3964,6 @@ static StatsDescriptors *find_stats_descriptors(StatsTarget target, int stats_fd
g_free(kvm_stats_desc); g_free(kvm_stats_desc);
return NULL; return NULL;
} }
descriptors->kvm_stats_header = kvm_stats_header;
descriptors->kvm_stats_desc = kvm_stats_desc; descriptors->kvm_stats_desc = kvm_stats_desc;
descriptors->ident = ident; descriptors->ident = ident;
QTAILQ_INSERT_TAIL(&stats_descriptors, descriptors, next); QTAILQ_INSERT_TAIL(&stats_descriptors, descriptors, next);
@ -3973,7 +3988,7 @@ static void query_stats(StatsResultList **result, StatsTarget target,
return; return;
} }
kvm_stats_header = descriptors->kvm_stats_header; kvm_stats_header = &descriptors->kvm_stats_header;
kvm_stats_desc = descriptors->kvm_stats_desc; kvm_stats_desc = descriptors->kvm_stats_desc;
size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size; size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size;
@ -4038,7 +4053,7 @@ static void query_stats_schema(StatsSchemaList **result, StatsTarget target,
return; return;
} }
kvm_stats_header = descriptors->kvm_stats_header; kvm_stats_header = &descriptors->kvm_stats_header;
kvm_stats_desc = descriptors->kvm_stats_desc; kvm_stats_desc = descriptors->kvm_stats_desc;
size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size; size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size;

View File

@ -32,6 +32,7 @@
#include "qapi/qapi-visit-audio.h" #include "qapi/qapi-visit-audio.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/help_option.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "sysemu/replay.h" #include "sysemu/replay.h"
#include "sysemu/runstate.h" #include "sysemu/runstate.h"
@ -2101,10 +2102,28 @@ static void audio_validate_opts(Audiodev *dev, Error **errp)
} }
} }
void audio_help(void)
{
int i;
printf("Available audio drivers:\n");
for (i = 0; i < AUDIODEV_DRIVER__MAX; i++) {
audio_driver *driver = audio_driver_lookup(AudiodevDriver_str(i));
if (driver) {
printf("%s\n", driver->name);
}
}
}
void audio_parse_option(const char *opt) void audio_parse_option(const char *opt)
{ {
Audiodev *dev = NULL; Audiodev *dev = NULL;
if (is_help_option(opt)) {
audio_help();
exit(EXIT_SUCCESS);
}
Visitor *v = qobject_input_visitor_new_str(opt, "driver", &error_fatal); Visitor *v = qobject_input_visitor_new_str(opt, "driver", &error_fatal);
visit_type_Audiodev(v, NULL, &dev, &error_fatal); visit_type_Audiodev(v, NULL, &dev, &error_fatal);
visit_free(v); visit_free(v);

View File

@ -171,6 +171,7 @@ void audio_sample_from_uint64(void *samples, int pos,
void audio_define(Audiodev *audio); void audio_define(Audiodev *audio);
void audio_parse_option(const char *opt); void audio_parse_option(const char *opt);
bool audio_init_audiodevs(void); bool audio_init_audiodevs(void);
void audio_help(void);
void audio_legacy_help(void); void audio_legacy_help(void);
AudioState *audio_state_by_name(const char *name); AudioState *audio_state_by_name(const char *name);

View File

@ -800,6 +800,7 @@ static char *spapr_phb_vfio_get_loc_code(SpaprPhbState *sphb, PCIDevice *pdev)
} }
/* Construct and read from host device tree the loc-code */ /* Construct and read from host device tree the loc-code */
g_free(path);
path = g_strdup_printf("/proc/device-tree%s/ibm,loc-code", devspec); path = g_strdup_printf("/proc/device-tree%s/ibm,loc-code", devspec);
if (!g_file_get_contents(path, &buf, NULL, NULL)) { if (!g_file_get_contents(path, &buf, NULL, NULL)) {
return NULL; return NULL;

View File

@ -1205,13 +1205,15 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
return; return;
} }
if (test_bit(header->type, have_fields_bitmap)) { if (header->type <= SMBIOS_MAX_TYPE) {
error_setg(errp, if (test_bit(header->type, have_fields_bitmap)) {
"can't load type %d struct, fields already specified!", error_setg(errp,
header->type); "can't load type %d struct, fields already specified!",
return; header->type);
return;
}
set_bit(header->type, have_binfile_bitmap);
} }
set_bit(header->type, have_binfile_bitmap);
if (header->type == 4) { if (header->type == 4) {
smbios_type4_count++; smbios_type4_count++;

View File

@ -704,10 +704,11 @@ SRST
``-audio [driver=]driver,model=value[,prop[=value][,...]]`` ``-audio [driver=]driver,model=value[,prop[=value][,...]]``
This option is a shortcut for configuring both the guest audio This option is a shortcut for configuring both the guest audio
hardware and the host audio backend in one go. hardware and the host audio backend in one go.
The host backend options are the same as with the corresponding The driver option is the same as with the corresponding ``-audiodev`` option below.
``-audiodev`` options below. The guest hardware model can be set with The guest hardware model can be set with ``model=modelname``.
``model=modelname``. Use ``model=help`` to list the available device
types. Use ``driver=help`` to list the available drivers,
and ``model=help`` to list the available device types.
The following two example do exactly the same, to show how ``-audio`` The following two example do exactly the same, to show how ``-audio``
can be used to shorten the command line length: can be used to shorten the command line length:
@ -721,6 +722,7 @@ ERST
DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
"-audiodev [driver=]driver,id=id[,prop[=value][,...]]\n" "-audiodev [driver=]driver,id=id[,prop[=value][,...]]\n"
" specifies the audio backend to use\n" " specifies the audio backend to use\n"
" Use ``-audiodev help`` to list the available drivers\n"
" id= identifier of the backend\n" " id= identifier of the backend\n"
" timer-period= timer period in microseconds\n" " timer-period= timer period in microseconds\n"
" in|out.mixing-engine= use mixing engine to mix streams inside QEMU\n" " in|out.mixing-engine= use mixing engine to mix streams inside QEMU\n"

@ -1 +1 @@
Subproject commit a5300c4949b8d4de2d34bedfaed66793f48ec948 Subproject commit 8ca302e86d685fa05b16e2b208888243da319941

View File

@ -22,7 +22,7 @@ i386
~ (/qemu)?((/include)?/hw/i386/.*|/target/i386/.*|/hw/intc/[^/]*apic[^/]*\.c) ~ (/qemu)?((/include)?/hw/i386/.*|/target/i386/.*|/hw/intc/[^/]*apic[^/]*\.c)
m68k m68k
~ (/qemu)?((/include)?/hw/m68k/.*|/target/m68k/.*|(/include)?/hw(/.*)?/mcf.*) ~ (/qemu)?((/include)?/hw/m68k/.*|/target/m68k/.*|(/include)?/hw(/.*)?/mcf.*|(/include)?/hw/nubus/.*)
microblaze microblaze
~ (/qemu)?((/include)?/hw/microblaze/.*|/target/microblaze/.*) ~ (/qemu)?((/include)?/hw/microblaze/.*|/target/microblaze/.*)
@ -146,3 +146,6 @@ tests
loongarch loongarch
~ (/qemu)?((/include)?/hw/(loongarch/.*|.*/loongarch.*)|/target/loongarch/.*) ~ (/qemu)?((/include)?/hw/(loongarch/.*|.*/loongarch.*)|/target/loongarch/.*)
riscv
~ (/qemu)?((/include)?/hw/riscv/.*|/target/riscv/.*|/hw/.*/(riscv_|ibex_|sifive_).*)

View File

@ -2842,11 +2842,16 @@ void qemu_init(int argc, char **argv, char **envp)
audio_parse_option(optarg); audio_parse_option(optarg);
break; break;
case QEMU_OPTION_audio: { case QEMU_OPTION_audio: {
QDict *dict = keyval_parse(optarg, "driver", NULL, &error_fatal); bool help;
char *model; char *model;
Audiodev *dev = NULL; Audiodev *dev = NULL;
Visitor *v; Visitor *v;
QDict *dict = keyval_parse(optarg, "driver", &help, &error_fatal);
if (help || (qdict_haskey(dict, "driver") &&
is_help_option(qdict_get_str(dict, "driver")))) {
audio_help();
exit(EXIT_SUCCESS);
}
if (!qdict_haskey(dict, "id")) { if (!qdict_haskey(dict, "id")) {
qdict_put_str(dict, "id", "audiodev0"); qdict_put_str(dict, "id", "audiodev0");
} }

View File

@ -926,7 +926,7 @@ static inline uint64_t helper_extrq(uint64_t src, int shift, int len)
void helper_extrq_r(CPUX86State *env, ZMMReg *d, ZMMReg *s) void helper_extrq_r(CPUX86State *env, ZMMReg *d, ZMMReg *s)
{ {
d->ZMM_Q(0) = helper_extrq(d->ZMM_Q(0), s->ZMM_B(1), s->ZMM_B(0)); d->ZMM_Q(0) = helper_extrq(d->ZMM_Q(0), s->ZMM_B(1) & 63, s->ZMM_B(0) & 63);
} }
void helper_extrq_i(CPUX86State *env, ZMMReg *d, int index, int length) void helper_extrq_i(CPUX86State *env, ZMMReg *d, int index, int length)
@ -934,7 +934,7 @@ void helper_extrq_i(CPUX86State *env, ZMMReg *d, int index, int length)
d->ZMM_Q(0) = helper_extrq(d->ZMM_Q(0), index, length); d->ZMM_Q(0) = helper_extrq(d->ZMM_Q(0), index, length);
} }
static inline uint64_t helper_insertq(uint64_t src, int shift, int len) static inline uint64_t helper_insertq(uint64_t dest, uint64_t src, int shift, int len)
{ {
uint64_t mask; uint64_t mask;
@ -943,17 +943,17 @@ static inline uint64_t helper_insertq(uint64_t src, int shift, int len)
} else { } else {
mask = (1ULL << len) - 1; mask = (1ULL << len) - 1;
} }
return (src & ~(mask << shift)) | ((src & mask) << shift); return (dest & ~(mask << shift)) | ((src & mask) << shift);
} }
void helper_insertq_r(CPUX86State *env, ZMMReg *d, ZMMReg *s) void helper_insertq_r(CPUX86State *env, ZMMReg *d, ZMMReg *s)
{ {
d->ZMM_Q(0) = helper_insertq(s->ZMM_Q(0), s->ZMM_B(9), s->ZMM_B(8)); d->ZMM_Q(0) = helper_insertq(d->ZMM_Q(0), s->ZMM_Q(0), s->ZMM_B(9) & 63, s->ZMM_B(8) & 63);
} }
void helper_insertq_i(CPUX86State *env, ZMMReg *d, int index, int length) void helper_insertq_i(CPUX86State *env, ZMMReg *d, ZMMReg *s, int index, int length)
{ {
d->ZMM_Q(0) = helper_insertq(d->ZMM_Q(0), index, length); d->ZMM_Q(0) = helper_insertq(d->ZMM_Q(0), s->ZMM_Q(0), index, length);
} }
#endif #endif

View File

@ -193,7 +193,7 @@ DEF_HELPER_3(rcpss, void, env, ZMMReg, ZMMReg)
DEF_HELPER_3(extrq_r, void, env, ZMMReg, ZMMReg) DEF_HELPER_3(extrq_r, void, env, ZMMReg, ZMMReg)
DEF_HELPER_4(extrq_i, void, env, ZMMReg, int, int) DEF_HELPER_4(extrq_i, void, env, ZMMReg, int, int)
DEF_HELPER_3(insertq_r, void, env, ZMMReg, ZMMReg) DEF_HELPER_3(insertq_r, void, env, ZMMReg, ZMMReg)
DEF_HELPER_4(insertq_i, void, env, ZMMReg, int, int) DEF_HELPER_5(insertq_i, void, env, ZMMReg, ZMMReg, int, int)
DEF_HELPER_3(glue(haddps, SUFFIX), void, env, ZMMReg, ZMMReg) DEF_HELPER_3(glue(haddps, SUFFIX), void, env, ZMMReg, ZMMReg)
DEF_HELPER_3(glue(haddpd, SUFFIX), void, env, ZMMReg, ZMMReg) DEF_HELPER_3(glue(haddpd, SUFFIX), void, env, ZMMReg, ZMMReg)
DEF_HELPER_3(glue(hsubps, SUFFIX), void, env, ZMMReg, ZMMReg) DEF_HELPER_3(glue(hsubps, SUFFIX), void, env, ZMMReg, ZMMReg)

View File

@ -140,3 +140,16 @@ G_NORETURN void raise_exception_ra(CPUX86State *env, int exception_index,
{ {
raise_interrupt2(env, exception_index, 0, 0, 0, retaddr); raise_interrupt2(env, exception_index, 0, 0, 0, retaddr);
} }
G_NORETURN void handle_unaligned_access(CPUX86State *env, vaddr vaddr,
MMUAccessType access_type,
uintptr_t retaddr)
{
/*
* Unaligned accesses are currently only triggered by SSE/AVX
* instructions that impose alignment requirements on memory
* operands. These instructions raise #GP(0) upon accessing an
* unaligned address.
*/
raise_exception_ra(env, EXCP0D_GPF, retaddr);
}

View File

@ -42,17 +42,6 @@ void x86_cpu_do_interrupt(CPUState *cpu);
bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req); bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req);
#endif #endif
/* helper.c */
#ifdef CONFIG_USER_ONLY
void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr,
MMUAccessType access_type,
bool maperr, uintptr_t ra);
#else
bool x86_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
#endif
void breakpoint_handler(CPUState *cs); void breakpoint_handler(CPUState *cs);
/* n must be a constant to be efficient */ /* n must be a constant to be efficient */
@ -78,6 +67,23 @@ G_NORETURN void raise_exception_err_ra(CPUX86State *env, int exception_index,
int error_code, uintptr_t retaddr); int error_code, uintptr_t retaddr);
G_NORETURN void raise_interrupt(CPUX86State *nenv, int intno, int is_int, G_NORETURN void raise_interrupt(CPUX86State *nenv, int intno, int is_int,
int error_code, int next_eip_addend); int error_code, int next_eip_addend);
G_NORETURN void handle_unaligned_access(CPUX86State *env, vaddr vaddr,
MMUAccessType access_type,
uintptr_t retaddr);
#ifdef CONFIG_USER_ONLY
void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr,
MMUAccessType access_type,
bool maperr, uintptr_t ra);
void x86_cpu_record_sigbus(CPUState *cs, vaddr addr,
MMUAccessType access_type, uintptr_t ra);
#else
bool x86_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
G_NORETURN void x86_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr);
#endif
/* cc_helper.c */ /* cc_helper.c */
extern const uint8_t parity_table[256]; extern const uint8_t parity_table[256];

View File

@ -439,3 +439,11 @@ bool x86_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
} }
return true; return true;
} }
G_NORETURN void x86_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr)
{
X86CPU *cpu = X86_CPU(cs);
handle_unaligned_access(&cpu->env, vaddr, access_type, retaddr);
}

View File

@ -75,10 +75,12 @@ static const struct TCGCPUOps x86_tcg_ops = {
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
.fake_user_interrupt = x86_cpu_do_interrupt, .fake_user_interrupt = x86_cpu_do_interrupt,
.record_sigsegv = x86_cpu_record_sigsegv, .record_sigsegv = x86_cpu_record_sigsegv,
.record_sigbus = x86_cpu_record_sigbus,
#else #else
.tlb_fill = x86_cpu_tlb_fill, .tlb_fill = x86_cpu_tlb_fill,
.do_interrupt = x86_cpu_do_interrupt, .do_interrupt = x86_cpu_do_interrupt,
.cpu_exec_interrupt = x86_cpu_exec_interrupt, .cpu_exec_interrupt = x86_cpu_exec_interrupt,
.do_unaligned_access = x86_cpu_do_unaligned_access,
.debug_excp_handler = breakpoint_handler, .debug_excp_handler = breakpoint_handler,
.debug_check_breakpoint = x86_debug_check_breakpoint, .debug_check_breakpoint = x86_debug_check_breakpoint,
#endif /* !CONFIG_USER_ONLY */ #endif /* !CONFIG_USER_ONLY */

View File

@ -2289,6 +2289,31 @@ static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm,
} }
} }
static target_ulong insn_get_addr(CPUX86State *env, DisasContext *s, MemOp ot)
{
target_ulong ret;
switch (ot) {
case MO_8:
ret = x86_ldub_code(env, s);
break;
case MO_16:
ret = x86_lduw_code(env, s);
break;
case MO_32:
ret = x86_ldl_code(env, s);
break;
#ifdef TARGET_X86_64
case MO_64:
ret = x86_ldq_code(env, s);
break;
#endif
default:
g_assert_not_reached();
}
return ret;
}
static inline uint32_t insn_get(CPUX86State *env, DisasContext *s, MemOp ot) static inline uint32_t insn_get(CPUX86State *env, DisasContext *s, MemOp ot)
{ {
uint32_t ret; uint32_t ret;
@ -2738,21 +2763,23 @@ static inline void gen_stq_env_A0(DisasContext *s, int offset)
tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ);
} }
static inline void gen_ldo_env_A0(DisasContext *s, int offset) static inline void gen_ldo_env_A0(DisasContext *s, int offset, bool align)
{ {
int mem_index = s->mem_index; int mem_index = s->mem_index;
tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, mem_index, MO_LEUQ); tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, mem_index,
MO_LEUQ | (align ? MO_ALIGN_16 : 0));
tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0)));
tcg_gen_addi_tl(s->tmp0, s->A0, 8); tcg_gen_addi_tl(s->tmp0, s->A0, 8);
tcg_gen_qemu_ld_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ); tcg_gen_qemu_ld_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ);
tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1))); tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1)));
} }
static inline void gen_sto_env_A0(DisasContext *s, int offset) static inline void gen_sto_env_A0(DisasContext *s, int offset, bool align)
{ {
int mem_index = s->mem_index; int mem_index = s->mem_index;
tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0)));
tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, mem_index, MO_LEUQ); tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, mem_index,
MO_LEUQ | (align ? MO_ALIGN_16 : 0));
tcg_gen_addi_tl(s->tmp0, s->A0, 8); tcg_gen_addi_tl(s->tmp0, s->A0, 8);
tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1))); tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1)));
tcg_gen_qemu_st_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ); tcg_gen_qemu_st_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ);
@ -3131,7 +3158,7 @@ static const struct SSEOpHelper_table6 sse_op_table6[256] = {
[0x25] = UNARY_OP(pmovsxdq, SSE41, SSE_OPF_MMX), [0x25] = UNARY_OP(pmovsxdq, SSE41, SSE_OPF_MMX),
[0x28] = BINARY_OP(pmuldq, SSE41, SSE_OPF_MMX), [0x28] = BINARY_OP(pmuldq, SSE41, SSE_OPF_MMX),
[0x29] = BINARY_OP(pcmpeqq, SSE41, SSE_OPF_MMX), [0x29] = BINARY_OP(pcmpeqq, SSE41, SSE_OPF_MMX),
[0x2a] = SPECIAL_OP(SSE41), /* movntqda */ [0x2a] = SPECIAL_OP(SSE41), /* movntdqa */
[0x2b] = BINARY_OP(packusdw, SSE41, SSE_OPF_MMX), [0x2b] = BINARY_OP(packusdw, SSE41, SSE_OPF_MMX),
[0x30] = UNARY_OP(pmovzxbw, SSE41, SSE_OPF_MMX), [0x30] = UNARY_OP(pmovzxbw, SSE41, SSE_OPF_MMX),
[0x31] = UNARY_OP(pmovzxbd, SSE41, SSE_OPF_MMX), [0x31] = UNARY_OP(pmovzxbd, SSE41, SSE_OPF_MMX),
@ -3294,17 +3321,17 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break; break;
case 0x1e7: /* movntdq */ case 0x1e7: /* movntdq */
case 0x02b: /* movntps */ case 0x02b: /* movntps */
case 0x12b: /* movntps */ case 0x12b: /* movntpd */
if (mod == 3) if (mod == 3)
goto illegal_op; goto illegal_op;
gen_lea_modrm(env, s, modrm); gen_lea_modrm(env, s, modrm);
gen_sto_env_A0(s, ZMM_OFFSET(reg)); gen_sto_env_A0(s, ZMM_OFFSET(reg), true);
break; break;
case 0x3f0: /* lddqu */ case 0x3f0: /* lddqu */
if (mod == 3) if (mod == 3)
goto illegal_op; goto illegal_op;
gen_lea_modrm(env, s, modrm); gen_lea_modrm(env, s, modrm);
gen_ldo_env_A0(s, ZMM_OFFSET(reg)); gen_ldo_env_A0(s, ZMM_OFFSET(reg), false);
break; break;
case 0x22b: /* movntss */ case 0x22b: /* movntss */
case 0x32b: /* movntsd */ case 0x32b: /* movntsd */
@ -3373,7 +3400,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x26f: /* movdqu xmm, ea */ case 0x26f: /* movdqu xmm, ea */
if (mod != 3) { if (mod != 3) {
gen_lea_modrm(env, s, modrm); gen_lea_modrm(env, s, modrm);
gen_ldo_env_A0(s, ZMM_OFFSET(reg)); gen_ldo_env_A0(s, ZMM_OFFSET(reg),
/* movaps, movapd, movdqa */
b == 0x028 || b == 0x128 || b == 0x16f);
} else { } else {
rm = (modrm & 7) | REX_B(s); rm = (modrm & 7) | REX_B(s);
gen_op_movo(s, ZMM_OFFSET(reg), ZMM_OFFSET(rm)); gen_op_movo(s, ZMM_OFFSET(reg), ZMM_OFFSET(rm));
@ -3432,7 +3461,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x212: /* movsldup */ case 0x212: /* movsldup */
if (mod != 3) { if (mod != 3) {
gen_lea_modrm(env, s, modrm); gen_lea_modrm(env, s, modrm);
gen_ldo_env_A0(s, ZMM_OFFSET(reg)); gen_ldo_env_A0(s, ZMM_OFFSET(reg), true);
} else { } else {
rm = (modrm & 7) | REX_B(s); rm = (modrm & 7) | REX_B(s);
gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)), gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)),
@ -3474,7 +3503,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x216: /* movshdup */ case 0x216: /* movshdup */
if (mod != 3) { if (mod != 3) {
gen_lea_modrm(env, s, modrm); gen_lea_modrm(env, s, modrm);
gen_ldo_env_A0(s, ZMM_OFFSET(reg)); gen_ldo_env_A0(s, ZMM_OFFSET(reg), true);
} else { } else {
rm = (modrm & 7) | REX_B(s); rm = (modrm & 7) | REX_B(s);
gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1)), gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1)),
@ -3502,10 +3531,20 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
gen_helper_extrq_i(cpu_env, s->ptr0, gen_helper_extrq_i(cpu_env, s->ptr0,
tcg_const_i32(bit_index), tcg_const_i32(bit_index),
tcg_const_i32(field_length)); tcg_const_i32(field_length));
else else {
gen_helper_insertq_i(cpu_env, s->ptr0, if (mod != 3) {
gen_lea_modrm(env, s, modrm);
op2_offset = offsetof(CPUX86State, xmm_t0);
gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.ZMM_D(0)));
} else {
rm = (modrm & 7) | REX_B(s);
op2_offset = ZMM_OFFSET(rm);
}
tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset);
gen_helper_insertq_i(cpu_env, s->ptr0, s->ptr1,
tcg_const_i32(bit_index), tcg_const_i32(bit_index),
tcg_const_i32(field_length)); tcg_const_i32(field_length));
}
} }
break; break;
case 0x7e: /* movd ea, mm */ case 0x7e: /* movd ea, mm */
@ -3568,7 +3607,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x27f: /* movdqu ea, xmm */ case 0x27f: /* movdqu ea, xmm */
if (mod != 3) { if (mod != 3) {
gen_lea_modrm(env, s, modrm); gen_lea_modrm(env, s, modrm);
gen_sto_env_A0(s, ZMM_OFFSET(reg)); gen_sto_env_A0(s, ZMM_OFFSET(reg),
/* movaps, movapd, movdqa */
b == 0x029 || b == 0x129 || b == 0x17f);
} else { } else {
rm = (modrm & 7) | REX_B(s); rm = (modrm & 7) | REX_B(s);
gen_op_movo(s, ZMM_OFFSET(rm), ZMM_OFFSET(reg)); gen_op_movo(s, ZMM_OFFSET(rm), ZMM_OFFSET(reg));
@ -3724,7 +3765,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
if (mod != 3) { if (mod != 3) {
gen_lea_modrm(env, s, modrm); gen_lea_modrm(env, s, modrm);
op2_offset = offsetof(CPUX86State,xmm_t0); op2_offset = offsetof(CPUX86State,xmm_t0);
gen_ldo_env_A0(s, op2_offset); /* FIXME: should be 64-bit access if b1 == 0. */
gen_ldo_env_A0(s, op2_offset, !!b1);
} else { } else {
rm = (modrm & 7) | REX_B(s); rm = (modrm & 7) | REX_B(s);
op2_offset = ZMM_OFFSET(rm); op2_offset = ZMM_OFFSET(rm);
@ -3913,11 +3955,11 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
tcg_gen_st16_tl(s->tmp0, cpu_env, op2_offset + tcg_gen_st16_tl(s->tmp0, cpu_env, op2_offset +
offsetof(ZMMReg, ZMM_W(0))); offsetof(ZMMReg, ZMM_W(0)));
break; break;
case 0x2a: /* movntqda */ case 0x2a: /* movntdqa */
gen_ldo_env_A0(s, op1_offset); gen_ldo_env_A0(s, op1_offset, true);
return; return;
default: default:
gen_ldo_env_A0(s, op2_offset); gen_ldo_env_A0(s, op2_offset, true);
} }
} }
if (!op6->fn[b1].op1) { if (!op6->fn[b1].op1) {
@ -4499,7 +4541,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
} else { } else {
op2_offset = offsetof(CPUX86State, xmm_t0); op2_offset = offsetof(CPUX86State, xmm_t0);
gen_lea_modrm(env, s, modrm); gen_lea_modrm(env, s, modrm);
gen_ldo_env_A0(s, op2_offset); gen_ldo_env_A0(s, op2_offset, true);
} }
val = x86_ldub_code(env, s); val = x86_ldub_code(env, s);
@ -4606,7 +4648,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break; break;
default: default:
/* 128 bit access */ /* 128 bit access */
gen_ldo_env_A0(s, op2_offset); gen_ldo_env_A0(s, op2_offset, true);
break; break;
} }
} else { } else {
@ -4716,9 +4758,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu)
switch (b) { switch (b) {
case 0xf3: case 0xf3:
prefixes |= PREFIX_REPZ; prefixes |= PREFIX_REPZ;
prefixes &= ~PREFIX_REPNZ;
goto next_byte; goto next_byte;
case 0xf2: case 0xf2:
prefixes |= PREFIX_REPNZ; prefixes |= PREFIX_REPNZ;
prefixes &= ~PREFIX_REPZ;
goto next_byte; goto next_byte;
case 0xf0: case 0xf0:
prefixes |= PREFIX_LOCK; prefixes |= PREFIX_LOCK;
@ -5832,16 +5876,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu)
target_ulong offset_addr; target_ulong offset_addr;
ot = mo_b_d(b, dflag); ot = mo_b_d(b, dflag);
switch (s->aflag) { offset_addr = insn_get_addr(env, s, s->aflag);
#ifdef TARGET_X86_64
case MO_64:
offset_addr = x86_ldq_code(env, s);
break;
#endif
default:
offset_addr = insn_get(env, s, s->aflag);
break;
}
tcg_gen_movi_tl(s->A0, offset_addr); tcg_gen_movi_tl(s->A0, offset_addr);
gen_add_A0_ds_seg(s); gen_add_A0_ds_seg(s);
if ((b & 2) == 0) { if ((b & 2) == 0) {

View File

@ -48,3 +48,10 @@ void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr,
cpu_loop_exit_restore(cs, ra); cpu_loop_exit_restore(cs, ra);
} }
void x86_cpu_record_sigbus(CPUState *cs, vaddr addr,
MMUAccessType access_type, uintptr_t ra)
{
X86CPU *cpu = X86_CPU(cs);
handle_unaligned_access(&cpu->env, addr, access_type, ra);
}

View File

@ -7,8 +7,8 @@ VPATH += $(I386_SRC)
I386_SRCS=$(notdir $(wildcard $(I386_SRC)/*.c)) I386_SRCS=$(notdir $(wildcard $(I386_SRC)/*.c))
ALL_X86_TESTS=$(I386_SRCS:.c=) ALL_X86_TESTS=$(I386_SRCS:.c=)
SKIP_I386_TESTS=test-i386-ssse3 test-avx SKIP_I386_TESTS=test-i386-ssse3 test-avx test-3dnow test-mmx
X86_64_TESTS:=$(filter test-i386-bmi2 test-i386-ssse3 test-avx, $(ALL_X86_TESTS)) X86_64_TESTS:=$(filter test-i386-bmi2 $(SKIP_I386_TESTS), $(ALL_X86_TESTS))
test-i386-sse-exceptions: CFLAGS += -msse4.1 -mfpmath=sse test-i386-sse-exceptions: CFLAGS += -msse4.1 -mfpmath=sse
run-test-i386-sse-exceptions: QEMU_OPTS += -cpu max run-test-i386-sse-exceptions: QEMU_OPTS += -cpu max
@ -82,9 +82,27 @@ run-plugin-sha512-sse-with-%: QEMU_OPTS+=-cpu max
TESTS+=sha512-sse TESTS+=sha512-sse
CLEANFILES += test-avx.h CLEANFILES += test-avx.h test-mmx.h test-3dnow.h
test-3dnow.h: test-mmx.py x86.csv
$(PYTHON) $(I386_SRC)/test-mmx.py $(I386_SRC)/x86.csv $@ 3DNOW
test-mmx.h: test-mmx.py x86.csv
$(PYTHON) $(I386_SRC)/test-mmx.py $(I386_SRC)/x86.csv $@ MMX SSE SSE2 SSE3 SSSE3
test-avx.h: test-avx.py x86.csv test-avx.h: test-avx.py x86.csv
$(PYTHON) $(I386_SRC)/test-avx.py $(I386_SRC)/x86.csv $@ $(PYTHON) $(I386_SRC)/test-avx.py $(I386_SRC)/x86.csv $@
test-3dnow: CFLAGS += -masm=intel -O -I.
run-test-3dnow: QEMU_OPTS += -cpu max
run-plugin-test-3dnow: QEMU_OPTS += -cpu max
test-3dnow: test-3dnow.h
test-mmx: CFLAGS += -masm=intel -O -I.
run-test-mmx: QEMU_OPTS += -cpu max
run-plugin-test-mmx: QEMU_OPTS += -cpu max
test-mmx: test-mmx.h
test-avx: CFLAGS += -masm=intel -O -I. test-avx: CFLAGS += -masm=intel -O -I.
run-test-avx: QEMU_OPTS += -cpu max
run-plugin-test-avx: QEMU_OPTS += -cpu max
test-avx: test-avx.h test-avx: test-avx.h

View File

@ -0,0 +1,3 @@
#define EMMS "femms"
#define TEST_FILE "test-3dnow.h"
#include "test-mmx.c"

View File

@ -7,7 +7,6 @@ import sys
from fnmatch import fnmatch from fnmatch import fnmatch
archs = [ archs = [
# TODO: MMX?
"SSE", "SSE2", "SSE3", "SSSE3", "SSE4_1", "SSE4_2", "SSE", "SSE2", "SSE3", "SSSE3", "SSE4_1", "SSE4_2",
] ]
@ -104,7 +103,11 @@ class XMMArg():
class MMArg(): class MMArg():
isxmm = True isxmm = True
ismem = False # TODO def __init__(self, mw):
if mw not in [0, 32, 64]:
raise Exception("Bad mem width: %s" % mw)
self.mw = mw
self.ismem = mw != 0
def regstr(self, n): def regstr(self, n):
return "mm%d" % (n & 7) return "mm%d" % (n & 7)
@ -170,6 +173,9 @@ class ArgMem():
def regstr(self, n): def regstr(self, n):
return mem_w(self.w) return mem_w(self.w)
class SkipInstruction(Exception):
pass
def ArgGenerator(arg, op): def ArgGenerator(arg, op):
if arg[:3] == 'xmm' or arg[:3] == "ymm": if arg[:3] == 'xmm' or arg[:3] == "ymm":
if "/" in arg: if "/" in arg:
@ -180,7 +186,13 @@ def ArgGenerator(arg, op):
else: else:
return XMMArg(arg[0], 0); return XMMArg(arg[0], 0);
elif arg[:2] == 'mm': elif arg[:2] == 'mm':
return MMArg(); if "/" in arg:
r, m = arg.split('/')
if (m[0] != 'm'):
raise Exception("Expected /m: %s", arg)
return MMArg(int(m[1:]));
else:
return MMArg(0);
elif arg[:4] == 'imm8': elif arg[:4] == 'imm8':
return ArgImm8u(op); return ArgImm8u(op);
elif arg == '<XMM0>': elif arg == '<XMM0>':
@ -218,8 +230,12 @@ class InsnGenerator:
try: try:
self.args = list(ArgGenerator(a, op) for a in args) self.args = list(ArgGenerator(a, op) for a in args)
if not any((x.isxmm for x in self.args)):
raise SkipInstruction
if len(self.args) > 0 and self.args[-1] is None: if len(self.args) > 0 and self.args[-1] is None:
self.args = self.args[:-1] self.args = self.args[:-1]
except SkipInstruction:
raise
except Exception as e: except Exception as e:
raise Exception("Bad arg %s: %s" % (op, e)) raise Exception("Bad arg %s: %s" % (op, e))
@ -340,10 +356,13 @@ def main():
continue continue
cpuid = row[6] cpuid = row[6]
if cpuid in archs: if cpuid in archs:
g = InsnGenerator(insn[0], insn[1:]) try:
for insn in g.gen(): g = InsnGenerator(insn[0], insn[1:])
outf.write('TEST(%d, "%s", %s)\n' % (n, insn, g.optype)) for insn in g.gen():
n += 1 outf.write('TEST(%d, "%s", %s)\n' % (n, insn, g.optype))
n += 1
except SkipInstruction:
pass
outf.write("#undef TEST\n") outf.write("#undef TEST\n")
csvfile.close() csvfile.close()

View File

@ -34,15 +34,8 @@
#endif #endif
//#define LINUX_VM86_IOPL_FIX //#define LINUX_VM86_IOPL_FIX
//#define TEST_P4_FLAGS //#define TEST_P4_FLAGS
#ifdef __SSE__
#define TEST_SSE
#define TEST_CMOV 1 #define TEST_CMOV 1
#define TEST_FCOMI 1 #define TEST_FCOMI 1
#else
#undef TEST_SSE
#define TEST_CMOV 1
#define TEST_FCOMI 1
#endif
#if defined(__x86_64__) #if defined(__x86_64__)
#define FMT64X "%016lx" #define FMT64X "%016lx"
@ -2104,568 +2097,6 @@ static void test_enter(void)
TEST_ENTER("w", uint16_t, 31); TEST_ENTER("w", uint16_t, 31);
} }
#ifdef TEST_SSE
typedef int __m64 __attribute__ ((vector_size(8)));
typedef float __m128 __attribute__ ((vector_size(16)));
typedef union {
double d[2];
float s[4];
uint32_t l[4];
uint64_t q[2];
__m128 dq;
} XMMReg;
static uint64_t __attribute__((aligned(16))) test_values[4][2] = {
{ 0x456723c698694873, 0xdc515cff944a58ec },
{ 0x1f297ccd58bad7ab, 0x41f21efba9e3e146 },
{ 0x007c62c2085427f8, 0x231be9e8cde7438d },
{ 0x0f76255a085427f8, 0xc233e9e8c4c9439a },
};
#define SSE_OP(op)\
{\
asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
#op,\
a.q[1], a.q[0],\
b.q[1], b.q[0],\
r.q[1], r.q[0]);\
}
#define SSE_OP2(op)\
{\
int i;\
for(i=0;i<2;i++) {\
a.q[0] = test_values[2*i][0];\
a.q[1] = test_values[2*i][1];\
b.q[0] = test_values[2*i+1][0];\
b.q[1] = test_values[2*i+1][1];\
SSE_OP(op);\
}\
}
#define MMX_OP2(op)\
{\
int i;\
for(i=0;i<2;i++) {\
a.q[0] = test_values[2*i][0];\
b.q[0] = test_values[2*i+1][0];\
asm volatile (#op " %2, %0" : "=y" (r.q[0]) : "0" (a.q[0]), "y" (b.q[0]));\
printf("%-9s: a=" FMT64X " b=" FMT64X " r=" FMT64X "\n",\
#op,\
a.q[0],\
b.q[0],\
r.q[0]);\
}\
SSE_OP2(op);\
}
#define SHUF_OP(op, ib)\
{\
a.q[0] = test_values[0][0];\
a.q[1] = test_values[0][1];\
b.q[0] = test_values[1][0];\
b.q[1] = test_values[1][1];\
asm volatile (#op " $" #ib ", %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
#op,\
a.q[1], a.q[0],\
b.q[1], b.q[0],\
ib,\
r.q[1], r.q[0]);\
}
#define PSHUF_OP(op, ib)\
{\
int i;\
for(i=0;i<2;i++) {\
a.q[0] = test_values[2*i][0];\
a.q[1] = test_values[2*i][1];\
asm volatile (#op " $" #ib ", %1, %0" : "=x" (r.dq) : "x" (a.dq));\
printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
#op,\
a.q[1], a.q[0],\
ib,\
r.q[1], r.q[0]);\
}\
}
#define SHIFT_IM(op, ib)\
{\
int i;\
for(i=0;i<2;i++) {\
a.q[0] = test_values[2*i][0];\
a.q[1] = test_values[2*i][1];\
asm volatile (#op " $" #ib ", %0" : "=x" (r.dq) : "0" (a.dq));\
printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
#op,\
a.q[1], a.q[0],\
ib,\
r.q[1], r.q[0]);\
}\
}
#define SHIFT_OP(op, ib)\
{\
int i;\
SHIFT_IM(op, ib);\
for(i=0;i<2;i++) {\
a.q[0] = test_values[2*i][0];\
a.q[1] = test_values[2*i][1];\
b.q[0] = ib;\
b.q[1] = 0;\
asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
#op,\
a.q[1], a.q[0],\
b.q[1], b.q[0],\
r.q[1], r.q[0]);\
}\
}
#define MOVMSK(op)\
{\
int i, reg;\
for(i=0;i<2;i++) {\
a.q[0] = test_values[2*i][0];\
a.q[1] = test_values[2*i][1];\
asm volatile (#op " %1, %0" : "=r" (reg) : "x" (a.dq));\
printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\
#op,\
a.q[1], a.q[0],\
reg);\
}\
}
#define SSE_OPS(a) \
SSE_OP(a ## ps);\
SSE_OP(a ## ss);
#define SSE_OPD(a) \
SSE_OP(a ## pd);\
SSE_OP(a ## sd);
#define SSE_COMI(op, field)\
{\
unsigned long eflags;\
XMMReg a, b;\
a.field[0] = a1;\
b.field[0] = b1;\
asm volatile (#op " %2, %1\n"\
"pushf\n"\
"pop %0\n"\
: "=rm" (eflags)\
: "x" (a.dq), "x" (b.dq));\
printf("%-9s: a=%f b=%f cc=%04lx\n",\
#op, a1, b1,\
eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\
}
void test_sse_comi(double a1, double b1)
{
SSE_COMI(ucomiss, s);
SSE_COMI(ucomisd, d);
SSE_COMI(comiss, s);
SSE_COMI(comisd, d);
}
#define CVT_OP_XMM(op)\
{\
asm volatile (#op " %1, %0" : "=x" (r.dq) : "x" (a.dq));\
printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
#op,\
a.q[1], a.q[0],\
r.q[1], r.q[0]);\
}
/* Force %xmm0 usage to avoid the case where both register index are 0
to test instruction decoding more extensively */
#define CVT_OP_XMM2MMX(op)\
{\
asm volatile (#op " %1, %0" : "=y" (r.q[0]) : "x" (a.dq) \
: "%xmm0"); \
asm volatile("emms\n"); \
printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "\n",\
#op,\
a.q[1], a.q[0],\
r.q[0]);\
}
#define CVT_OP_MMX2XMM(op)\
{\
asm volatile (#op " %1, %0" : "=x" (r.dq) : "y" (a.q[0]));\
asm volatile("emms\n"); \
printf("%-9s: a=" FMT64X " r=" FMT64X "" FMT64X "\n",\
#op,\
a.q[0],\
r.q[1], r.q[0]);\
}
#define CVT_OP_REG2XMM(op)\
{\
asm volatile (#op " %1, %0" : "=x" (r.dq) : "r" (a.l[0]));\
printf("%-9s: a=%08x r=" FMT64X "" FMT64X "\n",\
#op,\
a.l[0],\
r.q[1], r.q[0]);\
}
#define CVT_OP_XMM2REG(op)\
{\
asm volatile (#op " %1, %0" : "=r" (r.l[0]) : "x" (a.dq));\
printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\
#op,\
a.q[1], a.q[0],\
r.l[0]);\
}
struct fpxstate {
uint16_t fpuc;
uint16_t fpus;
uint16_t fptag;
uint16_t fop;
uint32_t fpuip;
uint16_t cs_sel;
uint16_t dummy0;
uint32_t fpudp;
uint16_t ds_sel;
uint16_t dummy1;
uint32_t mxcsr;
uint32_t mxcsr_mask;
uint8_t fpregs1[8 * 16];
uint8_t xmm_regs[8 * 16];
uint8_t dummy2[224];
};
static struct fpxstate fpx_state __attribute__((aligned(16)));
static struct fpxstate fpx_state2 __attribute__((aligned(16)));
void test_fxsave(void)
{
struct fpxstate *fp = &fpx_state;
struct fpxstate *fp2 = &fpx_state2;
int i, nb_xmm;
XMMReg a, b;
a.q[0] = test_values[0][0];
a.q[1] = test_values[0][1];
b.q[0] = test_values[1][0];
b.q[1] = test_values[1][1];
asm("movdqa %2, %%xmm0\n"
"movdqa %3, %%xmm7\n"
#if defined(__x86_64__)
"movdqa %2, %%xmm15\n"
#endif
" fld1\n"
" fldpi\n"
" fldln2\n"
" fxsave %0\n"
" fxrstor %0\n"
" fxsave %1\n"
" fninit\n"
: "=m" (*(uint32_t *)fp2), "=m" (*(uint32_t *)fp)
: "m" (a), "m" (b));
printf("fpuc=%04x\n", fp->fpuc);
printf("fpus=%04x\n", fp->fpus);
printf("fptag=%04x\n", fp->fptag);
for(i = 0; i < 3; i++) {
printf("ST%d: " FMT64X " %04x\n",
i,
*(uint64_t *)&fp->fpregs1[i * 16],
*(uint16_t *)&fp->fpregs1[i * 16 + 8]);
}
printf("mxcsr=%08x\n", fp->mxcsr & 0x1f80);
#if defined(__x86_64__)
nb_xmm = 16;
#else
nb_xmm = 8;
#endif
for(i = 0; i < nb_xmm; i++) {
printf("xmm%d: " FMT64X "" FMT64X "\n",
i,
*(uint64_t *)&fp->xmm_regs[i * 16],
*(uint64_t *)&fp->xmm_regs[i * 16 + 8]);
}
}
void test_sse(void)
{
XMMReg r, a, b;
int i;
MMX_OP2(punpcklbw);
MMX_OP2(punpcklwd);
MMX_OP2(punpckldq);
MMX_OP2(packsswb);
MMX_OP2(pcmpgtb);
MMX_OP2(pcmpgtw);
MMX_OP2(pcmpgtd);
MMX_OP2(packuswb);
MMX_OP2(punpckhbw);
MMX_OP2(punpckhwd);
MMX_OP2(punpckhdq);
MMX_OP2(packssdw);
MMX_OP2(pcmpeqb);
MMX_OP2(pcmpeqw);
MMX_OP2(pcmpeqd);
MMX_OP2(paddq);
MMX_OP2(pmullw);
MMX_OP2(psubusb);
MMX_OP2(psubusw);
MMX_OP2(pminub);
MMX_OP2(pand);
MMX_OP2(paddusb);
MMX_OP2(paddusw);
MMX_OP2(pmaxub);
MMX_OP2(pandn);
MMX_OP2(pmulhuw);
MMX_OP2(pmulhw);
MMX_OP2(psubsb);
MMX_OP2(psubsw);
MMX_OP2(pminsw);
MMX_OP2(por);
MMX_OP2(paddsb);
MMX_OP2(paddsw);
MMX_OP2(pmaxsw);
MMX_OP2(pxor);
MMX_OP2(pmuludq);
MMX_OP2(pmaddwd);
MMX_OP2(psadbw);
MMX_OP2(psubb);
MMX_OP2(psubw);
MMX_OP2(psubd);
MMX_OP2(psubq);
MMX_OP2(paddb);
MMX_OP2(paddw);
MMX_OP2(paddd);
MMX_OP2(pavgb);
MMX_OP2(pavgw);
asm volatile ("pinsrw $1, %1, %0" : "=y" (r.q[0]) : "r" (0x12345678));
printf("%-9s: r=" FMT64X "\n", "pinsrw", r.q[0]);
asm volatile ("pinsrw $5, %1, %0" : "=x" (r.dq) : "r" (0x12345678));
printf("%-9s: r=" FMT64X "" FMT64X "\n", "pinsrw", r.q[1], r.q[0]);
a.q[0] = test_values[0][0];
a.q[1] = test_values[0][1];
asm volatile ("pextrw $1, %1, %0" : "=r" (r.l[0]) : "y" (a.q[0]));
printf("%-9s: r=%08x\n", "pextrw", r.l[0]);
asm volatile ("pextrw $5, %1, %0" : "=r" (r.l[0]) : "x" (a.dq));
printf("%-9s: r=%08x\n", "pextrw", r.l[0]);
asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "y" (a.q[0]));
printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]);
asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "x" (a.dq));
printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]);
{
r.q[0] = -1;
r.q[1] = -1;
a.q[0] = test_values[0][0];
a.q[1] = test_values[0][1];
b.q[0] = test_values[1][0];
b.q[1] = test_values[1][1];
asm volatile("maskmovq %1, %0" :
: "y" (a.q[0]), "y" (b.q[0]), "D" (&r)
: "memory");
printf("%-9s: r=" FMT64X " a=" FMT64X " b=" FMT64X "\n",
"maskmov",
r.q[0],
a.q[0],
b.q[0]);
asm volatile("maskmovdqu %1, %0" :
: "x" (a.dq), "x" (b.dq), "D" (&r)
: "memory");
printf("%-9s: r=" FMT64X "" FMT64X " a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X "\n",
"maskmov",
r.q[1], r.q[0],
a.q[1], a.q[0],
b.q[1], b.q[0]);
}
asm volatile ("emms");
SSE_OP2(punpcklqdq);
SSE_OP2(punpckhqdq);
SSE_OP2(andps);
SSE_OP2(andpd);
SSE_OP2(andnps);
SSE_OP2(andnpd);
SSE_OP2(orps);
SSE_OP2(orpd);
SSE_OP2(xorps);
SSE_OP2(xorpd);
SSE_OP2(unpcklps);
SSE_OP2(unpcklpd);
SSE_OP2(unpckhps);
SSE_OP2(unpckhpd);
SHUF_OP(shufps, 0x78);
SHUF_OP(shufpd, 0x02);
PSHUF_OP(pshufd, 0x78);
PSHUF_OP(pshuflw, 0x78);
PSHUF_OP(pshufhw, 0x78);
SHIFT_OP(psrlw, 7);
SHIFT_OP(psrlw, 16);
SHIFT_OP(psraw, 7);
SHIFT_OP(psraw, 16);
SHIFT_OP(psllw, 7);
SHIFT_OP(psllw, 16);
SHIFT_OP(psrld, 7);
SHIFT_OP(psrld, 32);
SHIFT_OP(psrad, 7);
SHIFT_OP(psrad, 32);
SHIFT_OP(pslld, 7);
SHIFT_OP(pslld, 32);
SHIFT_OP(psrlq, 7);
SHIFT_OP(psrlq, 32);
SHIFT_OP(psllq, 7);
SHIFT_OP(psllq, 32);
SHIFT_IM(psrldq, 16);
SHIFT_IM(psrldq, 7);
SHIFT_IM(pslldq, 16);
SHIFT_IM(pslldq, 7);
MOVMSK(movmskps);
MOVMSK(movmskpd);
/* FPU specific ops */
{
uint32_t mxcsr;
asm volatile("stmxcsr %0" : "=m" (mxcsr));
printf("mxcsr=%08x\n", mxcsr & 0x1f80);
asm volatile("ldmxcsr %0" : : "m" (mxcsr));
}
test_sse_comi(2, -1);
test_sse_comi(2, 2);
test_sse_comi(2, 3);
test_sse_comi(2, q_nan.d);
test_sse_comi(q_nan.d, -1);
for(i = 0; i < 2; i++) {
a.s[0] = 2.7;
a.s[1] = 3.4;
a.s[2] = 4;
a.s[3] = -6.3;
b.s[0] = 45.7;
b.s[1] = 353.4;
b.s[2] = 4;
b.s[3] = 56.3;
if (i == 1) {
a.s[0] = q_nan.d;
b.s[3] = q_nan.d;
}
SSE_OPS(add);
SSE_OPS(mul);
SSE_OPS(sub);
SSE_OPS(min);
SSE_OPS(div);
SSE_OPS(max);
SSE_OPS(sqrt);
SSE_OPS(cmpeq);
SSE_OPS(cmplt);
SSE_OPS(cmple);
SSE_OPS(cmpunord);
SSE_OPS(cmpneq);
SSE_OPS(cmpnlt);
SSE_OPS(cmpnle);
SSE_OPS(cmpord);
a.d[0] = 2.7;
a.d[1] = -3.4;
b.d[0] = 45.7;
b.d[1] = -53.4;
if (i == 1) {
a.d[0] = q_nan.d;
b.d[1] = q_nan.d;
}
SSE_OPD(add);
SSE_OPD(mul);
SSE_OPD(sub);
SSE_OPD(min);
SSE_OPD(div);
SSE_OPD(max);
SSE_OPD(sqrt);
SSE_OPD(cmpeq);
SSE_OPD(cmplt);
SSE_OPD(cmple);
SSE_OPD(cmpunord);
SSE_OPD(cmpneq);
SSE_OPD(cmpnlt);
SSE_OPD(cmpnle);
SSE_OPD(cmpord);
}
/* float to float/int */
a.s[0] = 2.7;
a.s[1] = 3.4;
a.s[2] = 4;
a.s[3] = -6.3;
CVT_OP_XMM(cvtps2pd);
CVT_OP_XMM(cvtss2sd);
CVT_OP_XMM2MMX(cvtps2pi);
CVT_OP_XMM2MMX(cvttps2pi);
CVT_OP_XMM2REG(cvtss2si);
CVT_OP_XMM2REG(cvttss2si);
CVT_OP_XMM(cvtps2dq);
CVT_OP_XMM(cvttps2dq);
a.d[0] = 2.6;
a.d[1] = -3.4;
CVT_OP_XMM(cvtpd2ps);
CVT_OP_XMM(cvtsd2ss);
CVT_OP_XMM2MMX(cvtpd2pi);
CVT_OP_XMM2MMX(cvttpd2pi);
CVT_OP_XMM2REG(cvtsd2si);
CVT_OP_XMM2REG(cvttsd2si);
CVT_OP_XMM(cvtpd2dq);
CVT_OP_XMM(cvttpd2dq);
/* sse/mmx moves */
CVT_OP_XMM2MMX(movdq2q);
CVT_OP_MMX2XMM(movq2dq);
/* int to float */
a.l[0] = -6;
a.l[1] = 2;
a.l[2] = 100;
a.l[3] = -60000;
CVT_OP_MMX2XMM(cvtpi2ps);
CVT_OP_MMX2XMM(cvtpi2pd);
CVT_OP_REG2XMM(cvtsi2ss);
CVT_OP_REG2XMM(cvtsi2sd);
CVT_OP_XMM(cvtdq2ps);
CVT_OP_XMM(cvtdq2pd);
/* XXX: test PNI insns */
#if 0
SSE_OP2(movshdup);
#endif
asm volatile ("emms");
}
#endif
#define TEST_CONV_RAX(op)\ #define TEST_CONV_RAX(op)\
{\ {\
unsigned long a, r;\ unsigned long a, r;\
@ -2756,9 +2187,5 @@ int main(int argc, char **argv)
#endif #endif
test_enter(); test_enter();
test_conv(); test_conv();
#ifdef TEST_SSE
test_sse();
test_fxsave();
#endif
return 0; return 0;
} }

315
tests/tcg/i386/test-mmx.c Normal file
View File

@ -0,0 +1,315 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifndef TEST_FILE
#define TEST_FILE "test-mmx.h"
#endif
#ifndef EMMS
#define EMMS "emms"
#endif
typedef void (*testfn)(void);
typedef struct {
uint64_t q0, q1;
} __attribute__((aligned(16))) v2di;
typedef struct {
uint64_t mm[8];
v2di xmm[8];
uint64_t r[16];
uint64_t flags;
uint32_t ff;
uint64_t pad;
v2di mem[4];
v2di mem0[4];
} reg_state;
typedef struct {
int n;
testfn fn;
const char *s;
reg_state *init;
} TestDef;
reg_state initI;
reg_state initF32;
reg_state initF64;
static void dump_mmx(int n, const uint64_t *r, int ff)
{
if (ff == 32) {
float v[2];
memcpy(v, r, sizeof(v));
printf("MM%d = %016lx %8g %8g\n", n, *r, v[1], v[0]);
} else {
printf("MM%d = %016lx\n", n, *r);
}
}
static void dump_xmm(const char *name, int n, const v2di *r, int ff)
{
printf("%s%d = %016lx %016lx\n",
name, n, r->q1, r->q0);
if (ff == 32) {
float v[4];
memcpy(v, r, sizeof(v));
printf(" %8g %8g %8g %8g\n",
v[3], v[2], v[1], v[0]);
}
}
static void dump_regs(reg_state *s, int ff)
{
int i;
for (i = 0; i < 8; i++) {
dump_mmx(i, &s->mm[i], ff);
}
for (i = 0; i < 4; i++) {
dump_xmm("mem", i, &s->mem0[i], 0);
}
}
static void compare_state(const reg_state *a, const reg_state *b)
{
int i;
for (i = 0; i < 8; i++) {
if (a->mm[i] != b->mm[i]) {
printf("MM%d = %016lx\n", i, b->mm[i]);
}
}
for (i = 0; i < 16; i++) {
if (a->r[i] != b->r[i]) {
printf("r%d = %016lx\n", i, b->r[i]);
}
}
for (i = 0; i < 8; i++) {
if (memcmp(&a->xmm[i], &b->xmm[i], 8)) {
dump_xmm("xmm", i, &b->xmm[i], a->ff);
}
}
for (i = 0; i < 4; i++) {
if (memcmp(&a->mem0[i], &a->mem[i], 16)) {
dump_xmm("mem", i, &a->mem[i], a->ff);
}
}
if (a->flags != b->flags) {
printf("FLAGS = %016lx\n", b->flags);
}
}
#define LOADMM(r, o) "movq " #r ", " #o "[%0]\n\t"
#define LOADXMM(r, o) "movdqa " #r ", " #o "[%0]\n\t"
#define STOREMM(r, o) "movq " #o "[%1], " #r "\n\t"
#define STOREXMM(r, o) "movdqa " #o "[%1], " #r "\n\t"
#define MMREG(F) \
F(mm0, 0x00) \
F(mm1, 0x08) \
F(mm2, 0x10) \
F(mm3, 0x18) \
F(mm4, 0x20) \
F(mm5, 0x28) \
F(mm6, 0x30) \
F(mm7, 0x38)
#define XMMREG(F) \
F(xmm0, 0x040) \
F(xmm1, 0x050) \
F(xmm2, 0x060) \
F(xmm3, 0x070) \
F(xmm4, 0x080) \
F(xmm5, 0x090) \
F(xmm6, 0x0a0) \
F(xmm7, 0x0b0)
#define LOADREG(r, o) "mov " #r ", " #o "[rax]\n\t"
#define STOREREG(r, o) "mov " #o "[rax], " #r "\n\t"
#define REG(F) \
F(rbx, 0xc8) \
F(rcx, 0xd0) \
F(rdx, 0xd8) \
F(rsi, 0xe0) \
F(rdi, 0xe8) \
F(r8, 0x100) \
F(r9, 0x108) \
F(r10, 0x110) \
F(r11, 0x118) \
F(r12, 0x120) \
F(r13, 0x128) \
F(r14, 0x130) \
F(r15, 0x138) \
static void run_test(const TestDef *t)
{
reg_state result;
reg_state *init = t->init;
memcpy(init->mem, init->mem0, sizeof(init->mem));
printf("%5d %s\n", t->n, t->s);
asm volatile(
MMREG(LOADMM)
XMMREG(LOADXMM)
"sub rsp, 128\n\t"
"push rax\n\t"
"push rbx\n\t"
"push rcx\n\t"
"push rdx\n\t"
"push %1\n\t"
"push %2\n\t"
"mov rax, %0\n\t"
"pushf\n\t"
"pop rbx\n\t"
"shr rbx, 8\n\t"
"shl rbx, 8\n\t"
"mov rcx, 0x140[rax]\n\t"
"and rcx, 0xff\n\t"
"or rbx, rcx\n\t"
"push rbx\n\t"
"popf\n\t"
REG(LOADREG)
"mov rax, 0xc0[rax]\n\t"
"call [rsp]\n\t"
"mov [rsp], rax\n\t"
"mov rax, 8[rsp]\n\t"
REG(STOREREG)
"mov rbx, [rsp]\n\t"
"mov 0xc0[rax], rbx\n\t"
"mov rbx, 0\n\t"
"mov 0xf0[rax], rbx\n\t"
"mov 0xf8[rax], rbx\n\t"
"pushf\n\t"
"pop rbx\n\t"
"and rbx, 0xff\n\t"
"mov 0x140[rax], rbx\n\t"
"add rsp, 16\n\t"
"pop rdx\n\t"
"pop rcx\n\t"
"pop rbx\n\t"
"pop rax\n\t"
"add rsp, 128\n\t"
MMREG(STOREMM)
EMMS "\n\t"
XMMREG(STOREXMM)
: : "r"(init), "r"(&result), "r"(t->fn)
: "memory", "cc",
"rsi", "rdi",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5",
"xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11",
"xmm12", "xmm13", "xmm14", "xmm15"
);
compare_state(init, &result);
}
#define TEST(n, cmd, type) \
static void __attribute__((naked)) test_##n(void) \
{ \
asm volatile(cmd); \
asm volatile("ret"); \
}
#include TEST_FILE
static const TestDef test_table[] = {
#define TEST(n, cmd, type) {n, test_##n, cmd, &init##type},
#include TEST_FILE
{-1, NULL, "", NULL}
};
static void run_all(void)
{
const TestDef *t;
for (t = test_table; t->fn; t++) {
run_test(t);
}
}
#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))
float val_f32[] = {2.0, -1.0, 4.8, 0.8, 3, -42.0, 5e6, 7.5, 8.3};
uint64_t val_i64[] = {
0x3d6b3b6a9e4118f2lu, 0x355ae76d2774d78clu,
0xd851c54a56bf1f29lu, 0x4a84d1d50bf4c4fflu,
0x5826475e2c5fd799lu, 0xfd32edc01243f5e9lu,
};
v2di deadbeef = {0xa5a5a5a5deadbeefull, 0xa5a5a5a5deadbeefull};
void init_f32reg(uint64_t *r)
{
static int n;
float v[2];
int i;
for (i = 0; i < 2; i++) {
v[i] = val_f32[n++];
if (n == ARRAY_LEN(val_f32)) {
n = 0;
}
}
memcpy(r, v, sizeof(*r));
}
void init_intreg(uint64_t *r)
{
static uint64_t mask;
static int n;
*r = val_i64[n] ^ mask;
n++;
if (n == ARRAY_LEN(val_i64)) {
n = 0;
mask *= 0x104C11DB7;
}
}
static void init_all(reg_state *s)
{
int i;
for (i = 0; i < 16; i++) {
init_intreg(&s->r[i]);
}
s->r[3] = (uint64_t)&s->mem[0]; /* rdx */
s->r[5] = (uint64_t)&s->mem[2]; /* rdi */
s->r[6] = 0;
s->r[7] = 0;
s->flags = 2;
for (i = 0; i < 8; i++) {
s->xmm[i] = deadbeef;
memcpy(&s->mm[i], &s->xmm[i], sizeof(s->mm[i]));
}
for (i = 0; i < 2; i++) {
s->mem0[i] = deadbeef;
}
}
int main(int argc, char *argv[])
{
init_all(&initI);
init_intreg(&initI.mm[5]);
init_intreg(&initI.mm[6]);
init_intreg(&initI.mm[7]);
init_intreg(&initI.mem0[1].q0);
init_intreg(&initI.mem0[1].q1);
printf("Int:\n");
dump_regs(&initI, 0);
init_all(&initF32);
init_f32reg(&initF32.mm[5]);
init_f32reg(&initF32.mm[6]);
init_f32reg(&initF32.mm[7]);
init_f32reg(&initF32.mem0[1].q0);
init_f32reg(&initF32.mem0[1].q1);
initF32.ff = 32;
printf("F32:\n");
dump_regs(&initF32, 32);
if (argc > 1) {
int n = atoi(argv[1]);
run_test(&test_table[n]);
} else {
run_all();
}
return 0;
}

244
tests/tcg/i386/test-mmx.py Executable file
View File

@ -0,0 +1,244 @@
#! /usr/bin/env python3
# Generate test-avx.h from x86.csv
import csv
import sys
from fnmatch import fnmatch
ignore = set(["EMMS", "FEMMS", "FISTTP",
"LDMXCSR", "VLDMXCSR", "STMXCSR", "VSTMXCSR"])
imask = {
'PALIGNR': 0x3f,
'PEXTRB': 0x0f,
'PEXTRW': 0x07,
'PEXTRD': 0x03,
'PEXTRQ': 0x01,
'PINSRB': 0x0f,
'PINSRW': 0x07,
'PINSRD': 0x03,
'PINSRQ': 0x01,
'PSHUF[DW]': 0xff,
'PSHUF[LH]W': 0xff,
'PS[LR][AL][WDQ]': 0x3f,
}
def strip_comments(x):
for l in x:
if l != '' and l[0] != '#':
yield l
def reg_w(w):
if w == 8:
return 'al'
elif w == 16:
return 'ax'
elif w == 32:
return 'eax'
elif w == 64:
return 'rax'
raise Exception("bad reg_w %d" % w)
def mem_w(w):
if w == 8:
t = "BYTE"
elif w == 16:
t = "WORD"
elif w == 32:
t = "DWORD"
elif w == 64:
t = "QWORD"
else:
raise Exception()
return t + " PTR 32[rdx]"
class MMArg():
isxmm = True
def __init__(self, mw):
if mw not in [0, 32, 64]:
raise Exception("Bad /m width: %s" % w)
self.mw = mw
self.ismem = mw != 0
def regstr(self, n):
if n < 0:
return mem_w(self.mw)
else:
return "mm%d" % (n, )
def match(op, pattern):
return fnmatch(op, pattern)
class ArgImm8u():
isxmm = False
ismem = False
def __init__(self, op):
for k, v in imask.items():
if match(op, k):
self.mask = imask[k];
return
raise Exception("Unknown immediate")
def vals(self):
mask = self.mask
yield 0
n = 0
while n != mask:
n += 1
while (n & ~mask) != 0:
n += (n & ~mask)
yield n
class ArgRM():
isxmm = False
def __init__(self, rw, mw):
if rw not in [8, 16, 32, 64]:
raise Exception("Bad r/w width: %s" % w)
if mw not in [0, 8, 16, 32, 64]:
raise Exception("Bad r/w width: %s" % w)
self.rw = rw
self.mw = mw
self.ismem = mw != 0
def regstr(self, n):
if n < 0:
return mem_w(self.mw)
else:
return reg_w(self.rw)
class ArgMem():
isxmm = False
ismem = True
def __init__(self, w):
if w not in [8, 16, 32, 64, 128, 256]:
raise Exception("Bad mem width: %s" % w)
self.w = w
def regstr(self, n):
return mem_w(self.w)
class SkipInstruction(Exception):
pass
def ArgGenerator(arg, op):
if arg[:2] == 'mm':
if "/" in arg:
r, m = arg.split('/')
if (m[0] != 'm'):
raise Exception("Expected /m: %s", arg)
return MMArg(int(m[1:]));
else:
return MMArg(0);
elif arg[:4] == 'imm8':
return ArgImm8u(op);
elif arg[0] == 'r':
if '/m' in arg:
r, m = arg.split('/')
if (m[0] != 'm'):
raise Exception("Expected /m: %s", arg)
mw = int(m[1:])
if r == 'r':
rw = mw
else:
rw = int(r[1:])
return ArgRM(rw, mw)
return ArgRM(int(arg[1:]), 0);
elif arg[0] == 'm':
return ArgMem(int(arg[1:]))
else:
raise SkipInstruction
class InsnGenerator:
def __init__(self, op, args):
self.op = op
if op[0:2] == "PF":
self.optype = 'F32'
else:
self.optype = 'I'
try:
self.args = list(ArgGenerator(a, op) for a in args)
if len(self.args) > 0 and self.args[-1] is None:
self.args = self.args[:-1]
except SkipInstruction:
raise
except Exception as e:
raise Exception("Bad arg %s: %s" % (op, e))
def gen(self):
regs = (5, 6, 7)
dest = 4
nreg = len(self.args)
if nreg == 0:
yield self.op
return
if isinstance(self.args[-1], ArgImm8u):
nreg -= 1
immarg = self.args[-1]
else:
immarg = None
memarg = -1
for n, arg in enumerate(self.args):
if arg.ismem:
memarg = n
if nreg == 1:
regset = [(regs[0],)]
if memarg == 0:
regset += [(-1,)]
elif nreg == 2:
regset = [
(regs[0], regs[1]),
(regs[0], regs[0]),
]
if memarg == 0:
regset += [(-1, regs[0])]
elif memarg == 1:
regset += [(dest, -1)]
else:
raise Exception("Too many regs: %s(%d)" % (self.op, nreg))
for regv in regset:
argstr = []
for i in range(nreg):
arg = self.args[i]
argstr.append(arg.regstr(regv[i]))
if immarg is None:
yield self.op + ' ' + ','.join(argstr)
else:
for immval in immarg.vals():
yield self.op + ' ' + ','.join(argstr) + ',' + str(immval)
def split0(s):
if s == '':
return []
return s.split(',')
def main():
n = 0
if len(sys.argv) <= 3:
print("Usage: test-mmx.py x86.csv test-mmx.h CPUID...")
exit(1)
csvfile = open(sys.argv[1], 'r', newline='')
archs = sys.argv[3:]
with open(sys.argv[2], "w") as outf:
outf.write("// Generated by test-mmx.py. Do not edit.\n")
for row in csv.reader(strip_comments(csvfile)):
insn = row[0].replace(',', '').split()
if insn[0] in ignore:
continue
cpuid = row[6]
if cpuid in archs:
try:
g = InsnGenerator(insn[0], insn[1:])
for insn in g.gen():
outf.write('TEST(%d, "%s", %s)\n' % (n, insn, g.optype))
n += 1
except SkipInstruction:
pass
outf.write("#undef TEST\n")
csvfile.close()
if __name__ == "__main__":
main()

View File

@ -1469,16 +1469,16 @@
"PFCMPEQ mm1, mm2/m64","PFCMPEQ mm2/m64, mm1","pfcmpeq mm2/m64, mm1","0F 0F B0 /r","V","V","3DNOW","amd","rw,r","","" "PFCMPEQ mm1, mm2/m64","PFCMPEQ mm2/m64, mm1","pfcmpeq mm2/m64, mm1","0F 0F B0 /r","V","V","3DNOW","amd","rw,r","",""
"PFCMPGE mm1, mm2/m64","PFCMPGE mm2/m64, mm1","pfcmpge mm2/m64, mm1","0F 0F 90 /r","V","V","3DNOW","amd","rw,r","","" "PFCMPGE mm1, mm2/m64","PFCMPGE mm2/m64, mm1","pfcmpge mm2/m64, mm1","0F 0F 90 /r","V","V","3DNOW","amd","rw,r","",""
"PFCMPGT mm1, mm2/m64","PFCMPGT mm2/m64, mm1","pfcmpgt mm2/m64, mm1","0F 0F A0 /r","V","V","3DNOW","amd","rw,r","","" "PFCMPGT mm1, mm2/m64","PFCMPGT mm2/m64, mm1","pfcmpgt mm2/m64, mm1","0F 0F A0 /r","V","V","3DNOW","amd","rw,r","",""
"PFCPIT1 mm1, mm2/m64","PFCPIT1 mm2/m64, mm1","pfcpit1 mm2/m64, mm1","0F 0F A6 /r","V","V","3DNOW","amd","rw,r","",""
"PFMAX mm1, mm2/m64","PFMAX mm2/m64, mm1","pfmax mm2/m64, mm1","0F 0F A4 /r","V","V","3DNOW","amd","rw,r","","" "PFMAX mm1, mm2/m64","PFMAX mm2/m64, mm1","pfmax mm2/m64, mm1","0F 0F A4 /r","V","V","3DNOW","amd","rw,r","",""
"PFMIN mm1, mm2/m64","PFMIN mm2/m64, mm1","pfmin mm2/m64, mm1","0F 0F 94 /r","V","V","3DNOW","amd","rw,r","","" "PFMIN mm1, mm2/m64","PFMIN mm2/m64, mm1","pfmin mm2/m64, mm1","0F 0F 94 /r","V","V","3DNOW","amd","rw,r","",""
"PFMUL mm1, mm2/m64","PFMUL mm2/m64, mm1","pfmul mm2/m64, mm1","0F 0F B4 /r","V","V","3DNOW","amd","rw,r","","" "PFMUL mm1, mm2/m64","PFMUL mm2/m64, mm1","pfmul mm2/m64, mm1","0F 0F B4 /r","V","V","3DNOW","amd","rw,r","",""
"PFNACC mm1, mm2/m64","PFNACC mm2/m64, mm1","pfnacc mm2/m64, mm1","0F 0F 8A /r","V","V","3DNOW","amd","rw,r","","" "PFNACC mm1, mm2/m64","PFNACC mm2/m64, mm1","pfnacc mm2/m64, mm1","0F 0F 8A /r","V","V","3DNOW","amd","rw,r","",""
"PFPNACC mm1, mm2/m64","PFPNACC mm2/m64, mm1","pfpnacc mm2/m64, mm1","0F 0F 8E /r","V","V","3DNOW","amd","rw,r","","" "PFPNACC mm1, mm2/m64","PFPNACC mm2/m64, mm1","pfpnacc mm2/m64, mm1","0F 0F 8E /r","V","V","3DNOW","amd","rw,r","",""
"PFRCP mm1, mm2/m64","PFRCP mm2/m64, mm1","pfrcp mm2/m64, mm1","0F 0F 96 /r","V","V","3DNOW","amd","rw,r","","" "PFRCP mm1, mm2/m64","PFRCP mm2/m64, mm1","pfrcp mm2/m64, mm1","0F 0F 96 /r","V","V","3DNOW","amd","rw,r","",""
"PFRCPIT1 mm1, mm2/m64","PFRCPIT1 mm2/m64, mm1","pfrcpit1 mm2/m64, mm1","0F 0F A6 /r","V","V","3DNOW","amd","rw,r","",""
"PFRCPIT2 mm1, mm2/m64","PFRCPIT2 mm2/m64, mm1","pfrcpit2 mm2/m64, mm1","0F 0F B6 /r","V","V","3DNOW","amd","rw,r","","" "PFRCPIT2 mm1, mm2/m64","PFRCPIT2 mm2/m64, mm1","pfrcpit2 mm2/m64, mm1","0F 0F B6 /r","V","V","3DNOW","amd","rw,r","",""
"PFRSQIT1 mm1, mm2/m64","PFRSQIT1 mm2/m64, mm1","pfrsqit1 mm2/m64, mm1","0F 0F A7 /r","V","V","3DNOW","amd","rw,r","","" "PFRSQIT1 mm1, mm2/m64","PFRSQIT1 mm2/m64, mm1","pfrsqit1 mm2/m64, mm1","0F 0F A7 /r","V","V","3DNOW","amd","rw,r","",""
"PFSQRT mm1, mm2/m64","PFSQRT mm2/m64, mm1","pfsqrt mm2/m64, mm1","0F 0F 97 /r","V","V","3DNOW","amd","rw,r","","" "PFRSQRT mm1, mm2/m64","PFRSQRT mm2/m64, mm1","pfrsqrt mm2/m64, mm1","0F 0F 97 /r","V","V","3DNOW","amd","rw,r","",""
"PFSUB mm1, mm2/m64","PFSUB mm2/m64, mm1","pfsub mm2/m64, mm1","0F 0F 9A /r","V","V","3DNOW","amd","rw,r","","" "PFSUB mm1, mm2/m64","PFSUB mm2/m64, mm1","pfsub mm2/m64, mm1","0F 0F 9A /r","V","V","3DNOW","amd","rw,r","",""
"PFSUBR mm1, mm2/m64","PFSUBR mm2/m64, mm1","pfsubr mm2/m64, mm1","0F 0F AA /r","V","V","3DNOW","amd","rw,r","","" "PFSUBR mm1, mm2/m64","PFSUBR mm2/m64, mm1","pfsubr mm2/m64, mm1","0F 0F AA /r","V","V","3DNOW","amd","rw,r","",""
"PHADDD mm1, mm2/m64","PHADDD mm2/m64, mm1","phaddd mm2/m64, mm1","0F 38 02 /r","V","V","SSSE3","","rw,r","","" "PHADDD mm1, mm2/m64","PHADDD mm2/m64, mm1","phaddd mm2/m64, mm1","0F 38 02 /r","V","V","SSSE3","","rw,r","",""

Can't render this file because it is too large.

View File

@ -17,7 +17,6 @@ TESTS=$(MULTIARCH_TESTS)
endif endif
run-test-i386-ssse3: QEMU_OPTS += -cpu max run-test-i386-ssse3: QEMU_OPTS += -cpu max
run-test-avx: QEMU_OPTS += -cpu max
run-plugin-test-i386-ssse3-%: QEMU_OPTS += -cpu max run-plugin-test-i386-ssse3-%: QEMU_OPTS += -cpu max
test-x86_64: LDFLAGS+=-lm -lc test-x86_64: LDFLAGS+=-lm -lc

View File

@ -504,7 +504,7 @@ static void qdict_crumple_test_empty(void)
src = qdict_new(); src = qdict_new();
dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); dst = qobject_to(QDict, qdict_crumple(src, &error_abort));
g_assert(dst);
g_assert_cmpint(qdict_size(dst), ==, 0); g_assert_cmpint(qdict_size(dst), ==, 0);
qobject_unref(src); qobject_unref(src);

View File

@ -32,6 +32,7 @@ static int connect_qga(char *path)
g_usleep(G_USEC_PER_SEC); g_usleep(G_USEC_PER_SEC);
} }
if (i++ == 10) { if (i++ == 10) {
close(s);
return -1; return -1;
} }
} while (ret == -1); } while (ret == -1);

View File

@ -427,131 +427,117 @@ static void test_primitive_lists(gconstpointer opaque)
ops->deserialize((void **)&pl_copy_ptr, serialize_data, ops->deserialize((void **)&pl_copy_ptr, serialize_data,
visit_primitive_list, &error_abort); visit_primitive_list, &error_abort);
i = 0;
switch (pl_copy.type) {
case PTYPE_STRING:
cur_head = pl_copy.value.strings;
break;
case PTYPE_INTEGER:
cur_head = pl_copy.value.integers;
break;
case PTYPE_S8:
cur_head = pl_copy.value.s8_integers;
break;
case PTYPE_S16:
cur_head = pl_copy.value.s16_integers;
break;
case PTYPE_S32:
cur_head = pl_copy.value.s32_integers;
break;
case PTYPE_S64:
cur_head = pl_copy.value.s64_integers;
break;
case PTYPE_U8:
cur_head = pl_copy.value.u8_integers;
break;
case PTYPE_U16:
cur_head = pl_copy.value.u16_integers;
break;
case PTYPE_U32:
cur_head = pl_copy.value.u32_integers;
break;
case PTYPE_U64:
cur_head = pl_copy.value.u64_integers;
break;
case PTYPE_NUMBER:
cur_head = pl_copy.value.numbers;
break;
case PTYPE_BOOLEAN:
cur_head = pl_copy.value.booleans;
break;
default:
g_assert_not_reached();
}
/* compare our deserialized list of primitives to the original */ /* compare our deserialized list of primitives to the original */
do { i = 0;
while (cur_head) {
switch (pl_copy.type) { switch (pl_copy.type) {
case PTYPE_STRING: { case PTYPE_STRING: {
strList *ptr; strList *ptr = cur_head;
if (cur_head) { cur_head = ptr->next;
ptr = cur_head;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.strings;
}
g_assert_cmpstr(pt->value.string, ==, ptr->value); g_assert_cmpstr(pt->value.string, ==, ptr->value);
break; break;
} }
case PTYPE_INTEGER: { case PTYPE_INTEGER: {
intList *ptr; intList *ptr = cur_head;
if (cur_head) { cur_head = ptr->next;
ptr = cur_head;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.integers;
}
g_assert_cmpint(pt->value.integer, ==, ptr->value); g_assert_cmpint(pt->value.integer, ==, ptr->value);
break; break;
} }
case PTYPE_S8: { case PTYPE_S8: {
int8List *ptr; int8List *ptr = cur_head;
if (cur_head) { cur_head = ptr->next;
ptr = cur_head;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.s8_integers;
}
g_assert_cmpint(pt->value.s8, ==, ptr->value); g_assert_cmpint(pt->value.s8, ==, ptr->value);
break; break;
} }
case PTYPE_S16: { case PTYPE_S16: {
int16List *ptr; int16List *ptr = cur_head;
if (cur_head) { cur_head = ptr->next;
ptr = cur_head;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.s16_integers;
}
g_assert_cmpint(pt->value.s16, ==, ptr->value); g_assert_cmpint(pt->value.s16, ==, ptr->value);
break; break;
} }
case PTYPE_S32: { case PTYPE_S32: {
int32List *ptr; int32List *ptr = cur_head;
if (cur_head) { cur_head = ptr->next;
ptr = cur_head;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.s32_integers;
}
g_assert_cmpint(pt->value.s32, ==, ptr->value); g_assert_cmpint(pt->value.s32, ==, ptr->value);
break; break;
} }
case PTYPE_S64: { case PTYPE_S64: {
int64List *ptr; int64List *ptr = cur_head;
if (cur_head) { cur_head = ptr->next;
ptr = cur_head;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.s64_integers;
}
g_assert_cmpint(pt->value.s64, ==, ptr->value); g_assert_cmpint(pt->value.s64, ==, ptr->value);
break; break;
} }
case PTYPE_U8: { case PTYPE_U8: {
uint8List *ptr; uint8List *ptr = cur_head;
if (cur_head) { cur_head = ptr->next;
ptr = cur_head;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.u8_integers;
}
g_assert_cmpint(pt->value.u8, ==, ptr->value); g_assert_cmpint(pt->value.u8, ==, ptr->value);
break; break;
} }
case PTYPE_U16: { case PTYPE_U16: {
uint16List *ptr; uint16List *ptr = cur_head;
if (cur_head) { cur_head = ptr->next;
ptr = cur_head;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.u16_integers;
}
g_assert_cmpint(pt->value.u16, ==, ptr->value); g_assert_cmpint(pt->value.u16, ==, ptr->value);
break; break;
} }
case PTYPE_U32: { case PTYPE_U32: {
uint32List *ptr; uint32List *ptr = cur_head;
if (cur_head) { cur_head = ptr->next;
ptr = cur_head;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.u32_integers;
}
g_assert_cmpint(pt->value.u32, ==, ptr->value); g_assert_cmpint(pt->value.u32, ==, ptr->value);
break; break;
} }
case PTYPE_U64: { case PTYPE_U64: {
uint64List *ptr; uint64List *ptr = cur_head;
if (cur_head) { cur_head = ptr->next;
ptr = cur_head;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.u64_integers;
}
g_assert_cmpint(pt->value.u64, ==, ptr->value); g_assert_cmpint(pt->value.u64, ==, ptr->value);
break; break;
} }
case PTYPE_NUMBER: { case PTYPE_NUMBER: {
numberList *ptr;
GString *double_expected = g_string_new(""); GString *double_expected = g_string_new("");
GString *double_actual = g_string_new(""); GString *double_actual = g_string_new("");
if (cur_head) { numberList *ptr = cur_head;
ptr = cur_head; cur_head = ptr->next;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.numbers;
}
/* we serialize with %f for our reference visitors, so rather than /* we serialize with %f for our reference visitors, so rather than
* fuzzy floating math to test "equality", just compare the * fuzzy floating math to test "equality", just compare the
* formatted values * formatted values
@ -564,13 +550,8 @@ static void test_primitive_lists(gconstpointer opaque)
break; break;
} }
case PTYPE_BOOLEAN: { case PTYPE_BOOLEAN: {
boolList *ptr; boolList *ptr = cur_head;
if (cur_head) { cur_head = ptr->next;
ptr = cur_head;
cur_head = ptr->next;
} else {
cur_head = ptr = pl_copy.value.booleans;
}
g_assert_cmpint(!!pt->value.boolean, ==, !!ptr->value); g_assert_cmpint(!!pt->value.boolean, ==, !!ptr->value);
break; break;
} }
@ -578,9 +559,9 @@ static void test_primitive_lists(gconstpointer opaque)
g_assert_not_reached(); g_assert_not_reached();
} }
i++; i++;
} while (cur_head); }
g_assert_cmpint(i, ==, 33); g_assert_cmpint(i, ==, 32);
ops->cleanup(serialize_data); ops->cleanup(serialize_data);
dealloc_helper(&pl, visit_primitive_list, &error_abort); dealloc_helper(&pl, visit_primitive_list, &error_abort);