diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 57f25697fb..96f77bbdaf 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1915,7 +1915,8 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_PSE36, .features[FEAT_1_ECX] = #ifdef CONFIG_LATX_AVX_OPT - CPUID_EXT_AVX | CPUID_EXT_FMA | CPUID_EXT_XSAVE | + CPUID_EXT_AVX | CPUID_EXT_FMA | CPUID_EXT_F16C | + CPUID_EXT_XSAVE | #endif/*CONFIG_LATX_AVX_OPT*/ #ifdef CONFIG_LATX_SSSE3_SSE4 CPUID_EXT_AES | CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | @@ -1926,7 +1927,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .features[FEAT_8000_0001_EDX] = CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, .features[FEAT_8000_0001_ECX] = - CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM, + CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | CPUID_EXT3_ABM, #ifdef CONFIG_LATX_AVX_OPT .features[FEAT_7_0_EBX] = CPUID_7_0_EBX_HLE | @@ -2065,7 +2066,8 @@ static X86CPUDefinition builtin_x86_defs[] = { PPRO_FEATURES, .features[FEAT_1_ECX] = #ifdef CONFIG_LATX_AVX_OPT - CPUID_EXT_AVX | CPUID_EXT_FMA | CPUID_EXT_XSAVE | + CPUID_EXT_AVX | CPUID_EXT_FMA | CPUID_EXT_F16C | + CPUID_EXT_XSAVE | #endif/*CONFIG_LATX_AVX_OPT*/ #ifdef CONFIG_LATX_SSSE3_SSE4 CPUID_EXT_AES | CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | @@ -2074,7 +2076,7 @@ static X86CPUDefinition builtin_x86_defs[] = { #endif CPUID_EXT_SSE3, .features[FEAT_8000_0001_ECX] = - CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM, + CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | CPUID_EXT3_ABM, #ifdef CONFIG_LATX_AVX_OPT .features[FEAT_7_0_EBX] = CPUID_7_0_EBX_HLE | @@ -7581,6 +7583,7 @@ static void x86_cpu_register_types(void) if(!option_avx_cpuid){ uint64_t feat_1_ecx_mask = ~(CPUID_EXT_AVX | CPUID_EXT_FMA | + CPUID_EXT_F16C | CPUID_EXT_XSAVE); uint64_t feat_7_0_ebx_mask = ~(CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_BMI1 | diff --git a/tests/integration/avx-cpu-features.S b/tests/integration/avx-cpu-features.S new file mode 100644 index 0000000000..afa74f341b --- /dev/null +++ b/tests/integration/avx-cpu-features.S @@ -0,0 +1,84 @@ +.intel_syntax noprefix +.section .text +.global _start + +_start: + mov eax, 1 + cpuid + bt ecx, 29 + jnc .Lfail_f16c + + mov eax, 0x80000001 + cpuid + bt ecx, 5 + jnc .Lfail_lzcnt + + xor eax, eax + lzcnt eax, eax + jnc .Lfail_lzcnt_flags + jz .Lfail_lzcnt_flags + cmp eax, 32 + jne .Lfail_lzcnt_value + + mov eax, 0x80000000 + lzcnt eax, eax + jc .Lfail_lzcnt_flags + jnz .Lfail_lzcnt_flags + test eax, eax + jne .Lfail_lzcnt_value + + mov eax, 0x100 + lzcnt ax, ax + cmp ax, 7 + jne .Lfail_lzcnt_value + + mov rax, 1 + lzcnt rax, rax + cmp rax, 63 + jne .Lfail_lzcnt_value + + vmovq xmm0, qword ptr [rip + .Lhalf] + vcvtph2ps xmm1, xmm0 + vmovdqu xmm2, xmmword ptr [rip + .Lfloat] + vpcmpeqd xmm1, xmm1, xmm2 + vpmovmskb eax, xmm1 + cmp eax, 0xffff + jne .Lfail_f16c_value + + vcvtps2ph xmm3, xmm2, 0 + vmovq rax, xmm3 + cmp rax, qword ptr [rip + .Lhalf] + jne .Lfail_f16c_roundtrip + + xor edi, edi + jmp .Lexit + +.Lfail_f16c: + mov edi, 1 + jmp .Lexit +.Lfail_lzcnt: + mov edi, 2 + jmp .Lexit +.Lfail_lzcnt_value: + mov edi, 3 + jmp .Lexit +.Lfail_f16c_value: + mov edi, 4 + jmp .Lexit +.Lfail_f16c_roundtrip: + mov edi, 5 + jmp .Lexit +.Lfail_lzcnt_flags: + mov edi, 6 +.Lexit: + mov eax, 60 + syscall + +.section .rodata +.p2align 4 +.Lhalf: + .short 0x3c00, 0xc000, 0x3800, 0x0000 +.Lfloat: + .long 0x3f800000, 0xc0000000, 0x3f000000, 0x00000000 + +.section .note.GNU-stack,"",@progbits diff --git a/tests/integration/meson.build b/tests/integration/meson.build index be47597268..847ece9915 100644 --- a/tests/integration/meson.build +++ b/tests/integration/meson.build @@ -1,5 +1,7 @@ latx_integration_tests = [] +subdir('registrations/cpu') + if host_machine.cpu_family() == 'loongarch64' subdir('registrations/x11-kzt') subdir('registrations/sandbox') diff --git a/tests/integration/registrations/cpu/meson.build b/tests/integration/registrations/cpu/meson.build new file mode 100644 index 0000000000..8c9fd155d3 --- /dev/null +++ b/tests/integration/registrations/cpu/meson.build @@ -0,0 +1,12 @@ +if 'x86_64-linux-user' in target_dirs and \ + 'CONFIG_LATX_AVX_OPT' in config_host + latx_integration_tests += [{ + 'name': 'test-avx-cpu-features', + 'runner': find_program('../../test-avx-cpu-features.sh'), + 'args': [ + emulators['latx-x86_64'], + files('../../avx-cpu-features.S'), + ], + 'timeout': 120, + }] +endif diff --git a/tests/integration/test-avx-cpu-features.sh b/tests/integration/test-avx-cpu-features.sh new file mode 100755 index 0000000000..30e339b68c --- /dev/null +++ b/tests/integration/test-avx-cpu-features.sh @@ -0,0 +1,43 @@ +#!/bin/sh +set -eu + +emulator=$1 +source_file=$2 +workdir=$(mktemp -d) +trap 'rm -rf "$workdir"' EXIT HUP INT TERM + +if command -v clang-19 >/dev/null 2>&1; then + clang=clang-19 +elif command -v clang >/dev/null 2>&1; then + clang=clang +else + echo "SKIP: clang is required to build the x86_64 guest" + exit 77 +fi + +"$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none "$source_file" -o "$workdir/avx-cpu-features" + +LATX_AOT=0 LATX_TU=0 "$emulator" "$workdir/avx-cpu-features" + +aot_home="$workdir/aot" +mkdir -p "$aot_home" +HOME="$aot_home" LATX_AOT=1 LATX_TU=1 \ + "$emulator" "$workdir/avx-cpu-features" + +aot_file= +for _ in $(seq 1 100); do + aot_file=$(find "$aot_home/.cache/latx" -type f -name '*.aot2' \ + -size +0c -print -quit 2>/dev/null || true) + [ -n "$aot_file" ] && break + sleep 0.1 +done +if [ -z "$aot_file" ]; then + echo "FAIL: no non-empty AOT file generated" >&2 + exit 1 +fi + +HOME="$aot_home" LATX_AOT=1 LATX_TU=1 \ + "$emulator" "$workdir/avx-cpu-features" + +echo "PASS: F16C/LZCNT CPUID and instructions JIT/cold-AOT/hot-AOT"