diff --git a/scripts/test_latx_hbr_audit.py b/scripts/test_latx_hbr_audit.py index e7dc25a907..f5b747dbe3 100644 --- a/scripts/test_latx_hbr_audit.py +++ b/scripts/test_latx_hbr_audit.py @@ -20,13 +20,13 @@ class HbrAuditTest(unittest.TestCase): def test_repository_inventory(self): result = latx_hbr_audit.audit(REPO_ROOT) - self.assertEqual(result["counts"]["translator_supported"], 1098) - self.assertEqual(result["counts"]["shbr_relevant"], 774) - self.assertEqual(result["counts"]["opcode_specific"], 774) + self.assertEqual(result["counts"]["translator_supported"], 1101) + self.assertEqual(result["counts"]["shbr_relevant"], 777) + self.assertEqual(result["counts"]["opcode_specific"], 777) self.assertEqual(result["counts"]["conservative_unmodeled"], 0) - self.assertEqual(len(result["rows"]), 774) - self.assertEqual(len(result["promoted_opcode_specific"]), 618) - self.assertEqual(result["generated_opcode_specific"], 519) + self.assertEqual(len(result["rows"]), 777) + self.assertEqual(len(result["promoted_opcode_specific"]), 621) + self.assertEqual(result["generated_opcode_specific"], 522) gather = next( row for row in result["rows"] if row["mnemonic"] == "VGATHERDPD" ) @@ -36,13 +36,21 @@ def test_repository_inventory(self): ) self.assertEqual( result["translator_mnemonic_sha256"], - "32bdf9f0e48c0b73ac1f3f0e914d2af621540ffd7c6ccebb86d059c14137250d", + "f8b9761a04c987ad075b4986d1dec5aab06a2f9c6dd992c3adcba6881a25a2a8", ) xrstor = next( row for row in result["rows"] if row["mnemonic"] == "XRSTOR" ) self.assertEqual(xrstor["policy"], "implicit-read-write-all") self.assertIn("preserve path", xrstor["high32"]["reads"]) + xsave64 = next( + row for row in result["rows"] if row["mnemonic"] == "XSAVE64" + ) + self.assertEqual(xsave64["policy"], "implicit-read-all") + xrstor64 = next( + row for row in result["rows"] if row["mnemonic"] == "XRSTOR64" + ) + self.assertEqual(xrstor64["policy"], "implicit-read-write-all") def test_commented_hbr_case_is_ignored(self): models = latx_hbr_audit.parse_hbr_models( @@ -115,7 +123,7 @@ def test_single_domain_model_fails(self): def test_exact_family_rules(self): data = json.loads(INVENTORY.read_text(encoding="utf-8")) rules = generate_latx_hbr_semantics.exact_rules(data) - self.assertEqual(len(rules), 519) + self.assertEqual(len(rules), 522) self.assertEqual( rules["VFMADD132SS"], {"high32": "DEST", "high64": "DEST"}, diff --git a/scripts/test_latx_hbr_metrics.py b/scripts/test_latx_hbr_metrics.py index 77f164e18c..c64e517741 100644 --- a/scripts/test_latx_hbr_metrics.py +++ b/scripts/test_latx_hbr_metrics.py @@ -14,9 +14,9 @@ class ShbrMetricsTests(unittest.TestCase): def test_current_metrics(self): result = latx_hbr_metrics.collect(REPO_ROOT) - self.assertEqual(result["coverage"]["precise"], 774) + self.assertEqual(result["coverage"]["precise"], 777) self.assertEqual(result["coverage"]["conservative"], 0) - self.assertEqual(result["coverage"]["relevant"], 774) + self.assertEqual(result["coverage"]["relevant"], 777) self.assertEqual(result["generation_site_counts"]["SHBR_ON_32"], 12) self.assertEqual(result["generation_site_counts"]["SHBR_ON_64"], 10) self.assertEqual(result["minimum_removed_per_full_hit"]["lasx"], 22) diff --git a/target/i386/latx/optimization/hbr-xmm-semantics.inc b/target/i386/latx/optimization/hbr-xmm-semantics.inc index be7ca9f8cc..5c5063dd43 100644 --- a/target/i386/latx/optimization/hbr-xmm-semantics.inc +++ b/target/i386/latx/optimization/hbr-xmm-semantics.inc @@ -517,5 +517,8 @@ [WRAP(VUNPCKLPS)] = { SHBR_RULE_ACCESS, SHBR_RULE_OTHER }, [WRAP(VZEROALL)] = { SHBR_RULE_IMPLICIT, SHBR_RULE_IMPLICIT }, [WRAP(XRSTOR)] = { SHBR_RULE_IMPLICIT, SHBR_RULE_IMPLICIT }, + [WRAP(XRSTOR64)] = { SHBR_RULE_IMPLICIT, SHBR_RULE_IMPLICIT }, [WRAP(XSAVE)] = { SHBR_RULE_IMPLICIT, SHBR_RULE_IMPLICIT }, + [WRAP(XSAVE64)] = { SHBR_RULE_IMPLICIT, SHBR_RULE_IMPLICIT }, [WRAP(XSAVEOPT)] = { SHBR_RULE_IMPLICIT, SHBR_RULE_IMPLICIT }, + [WRAP(XSAVEOPT64)] = { SHBR_RULE_IMPLICIT, SHBR_RULE_IMPLICIT }, diff --git a/target/i386/latx/optimization/hbr-xmm-semantics.json b/target/i386/latx/optimization/hbr-xmm-semantics.json index fdedaf7deb..b44d5f60bf 100644 --- a/target/i386/latx/optimization/hbr-xmm-semantics.json +++ b/target/i386/latx/optimization/hbr-xmm-semantics.json @@ -18,12 +18,12 @@ "note": "Intel SDM is authoritative; Intel XED validates decoded operand access and Felix Cloutier is a per-family navigation aid." }, "expected_counts": { - "translator_supported": 1098, - "opcode_specific": 774, + "translator_supported": 1101, + "opcode_specific": 777, "conservative_unmodeled": 0, - "strict_xmm_scope": 766, + "strict_xmm_scope": 769, "ymm_alias_only": 8, - "shbr_relevant": 774 + "shbr_relevant": 777 }, "promoted_opcode_specific": [ "VADDPD", @@ -126,7 +126,7 @@ "VPSUBW", "VPXOR" ], - "translator_mnemonic_sha256": "32bdf9f0e48c0b73ac1f3f0e914d2af621540ffd7c6ccebb86d059c14137250d", + "translator_mnemonic_sha256": "f8b9761a04c987ad075b4986d1dec5aab06a2f9c6dd992c3adcba6881a25a2a8", "form_resolution": { "key": "mnemonic plus encoding family", "runtime_fields": [ @@ -244,8 +244,11 @@ "SQRTPD": {"high32": "ACCESS_READ", "high64": "ACCESS_READ"}, "SQRTPS": {"high32": "ACCESS_READ", "high64": "ACCESS_READ"}, "XRSTOR": {"high32": "IMPLICIT", "high64": "IMPLICIT"}, + "XRSTOR64": {"high32": "IMPLICIT", "high64": "IMPLICIT"}, "XSAVE": {"high32": "IMPLICIT", "high64": "IMPLICIT"}, + "XSAVE64": {"high32": "IMPLICIT", "high64": "IMPLICIT"}, "XSAVEOPT": {"high32": "IMPLICIT", "high64": "IMPLICIT"}, + "XSAVEOPT64": {"high32": "IMPLICIT", "high64": "IMPLICIT"}, "VBROADCASTF128": {"high32": "BROADCAST", "high64": "BROADCAST"}, "VBROADCASTI128": {"high32": "BROADCAST", "high64": "BROADCAST"}, "VBROADCASTSD": {"high32": "BROADCAST", "high64": "BROADCAST"}, @@ -505,12 +508,24 @@ "all-xmm" ] }, + "XSAVE64": { + "policy": "implicit-read-all", + "implicit": [ + "all-xmm" + ] + }, "XSAVEOPT": { "policy": "implicit-read-all", "implicit": [ "all-xmm" ] }, + "XSAVEOPT64": { + "policy": "implicit-read-all", + "implicit": [ + "all-xmm" + ] + }, "FXRSTOR": { "policy": "implicit-write-all", "implicit": [ @@ -530,6 +545,13 @@ ], "condition": "SSE state is restored only when EDX:EAX requests XSTATE_SSE" }, + "XRSTOR64": { + "policy": "implicit-read-write-all", + "implicit": [ + "all-xmm" + ], + "condition": "SSE state is restored only when EDX:EAX requests XSTATE_SSE" + }, "VZEROALL": { "policy": "implicit-write-all", "implicit": [ @@ -572,7 +594,7 @@ "unmodeled_groups": [ { "name": "legacy_unmodeled", - "expected_count": 141, + "expected_count": 144, "form_scope": "legacy-or-non-vex", "policy": "conservative-explicit", "mnemonics": [ @@ -715,8 +737,11 @@ "SQRTPD", "SQRTPS", "XRSTOR", + "XRSTOR64", "XSAVE", - "XSAVEOPT" + "XSAVE64", + "XSAVEOPT", + "XSAVEOPT64" ] }, { diff --git a/target/i386/latx/optimization/hbr.c b/target/i386/latx/optimization/hbr.c index 1f6c618f84..e1a8380e91 100644 --- a/target/i386/latx/optimization/hbr.c +++ b/target/i386/latx/optimization/hbr.c @@ -1884,7 +1884,9 @@ static bool apply_implicit_semantic(TranslationBlock *tb, IR1_INST *ir1, case WRAP(FXSAVE): case WRAP(FXSAVE64): case WRAP(XSAVE): + case WRAP(XSAVE64): case WRAP(XSAVEOPT): + case WRAP(XSAVEOPT64): ir1->shbr_read |= SHBR_XMM_ALL; for (int i = 0; i < XMM_NUM; i++) { ir1->xmm_use |= xmm[i]; @@ -1898,6 +1900,7 @@ static bool apply_implicit_semantic(TranslationBlock *tb, IR1_INST *ir1, } return true; case WRAP(XRSTOR): + case WRAP(XRSTOR64): /* XRSTOR preserves XMM when EDX:EAX does not request SSE state. */ ir1->shbr_read |= SHBR_XMM_ALL; ir1->shbr_def |= SHBR_XMM_ALL; @@ -1932,10 +1935,13 @@ static bool has_implicit_semantic(IR1_INST *ir1) case WRAP(FXSAVE): case WRAP(FXSAVE64): case WRAP(XSAVE): + case WRAP(XSAVE64): case WRAP(XSAVEOPT): + case WRAP(XSAVEOPT64): case WRAP(FXRSTOR): case WRAP(FXRSTOR64): case WRAP(XRSTOR): + case WRAP(XRSTOR64): case WRAP(VZEROALL): case WRAP(BLENDVPD): case WRAP(BLENDVPS): diff --git a/target/i386/latx/translator/translate.c b/target/i386/latx/translator/translate.c index 83d1c3a625..7d6b364694 100644 --- a/target/i386/latx/translator/translate.c +++ b/target/i386/latx/translator/translate.c @@ -1771,8 +1771,11 @@ static bool (*translate_functions[])(IR1_INST *) = { TRANS_FUNC_GEN(XGETBV, xgetbv_wrap), TRANS_FUNC_GEN(XSETBV, xsetbv_wrap), TRANS_FUNC_GEN(XSAVE, xsave_wrap), + TRANS_FUNC_GEN(XSAVE64, xsave_wrap), TRANS_FUNC_GEN(XSAVEOPT, xsaveopt_wrap), + TRANS_FUNC_GEN(XSAVEOPT64, xsaveopt_wrap), TRANS_FUNC_GEN(XRSTOR, xrstor_wrap), + TRANS_FUNC_GEN(XRSTOR64, xrstor_wrap), TRANS_FUNC_GEN(VPBROADCASTD, vpbroadcastd), //TRANS_FUNC_GEN(VPMAXSD, vpmaxsd), //TRANS_FUNC_GEN(VPMINSD, vpminsd), @@ -2414,8 +2417,11 @@ static SoftFPURegionClass softfpu_region_class(IR1_OPCODE opcode) case dt_X86_INS_XGETBV: case dt_X86_INS_XSETBV: case dt_X86_INS_XSAVE: + case dt_X86_INS_XSAVE64: case dt_X86_INS_XSAVEOPT: + case dt_X86_INS_XSAVEOPT64: case dt_X86_INS_XRSTOR: + case dt_X86_INS_XRSTOR64: #endif return SOFTFPU_REGION_REQUIRED; case dt_X86_INS_FILD: diff --git a/tests/integration/registrations/avx/meson.build b/tests/integration/registrations/avx/meson.build index b4c9589dba..2f889ca69d 100644 --- a/tests/integration/registrations/avx/meson.build +++ b/tests/integration/registrations/avx/meson.build @@ -12,6 +12,12 @@ if 'x86_64-linux-user' in target_dirs 'args': [emulators['latx-x86_64'], files('../../sse41-ymmh-preserve.asm')], 'timeout': 120, }] + latx_integration_tests += [{ + 'name': 'test-xsave64-roundtrip', + 'runner': find_program('../../test-avx-regressions.py'), + 'args': [emulators['latx-x86_64'], files('../../xsave64-roundtrip.S')], + 'timeout': 120, + }] latx_integration_tests += [{ 'name': 'test-avx-compare-jcc', 'runner': find_program('../../test-avx-regressions.py'), diff --git a/tests/integration/xsave64-roundtrip.S b/tests/integration/xsave64-roundtrip.S new file mode 100644 index 0000000000..2331368f64 --- /dev/null +++ b/tests/integration/xsave64-roundtrip.S @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * x86-64 XSAVE64/XRSTOR64 must save and restore the non-volatile XMM + * registers. Wine's syscall dispatcher relies on this across win32u calls; + * a missing XSAVE64 translation corrupts dialog sizes and other state held + * in XMM6-XMM15. + */ +.intel_syntax noprefix +.global _start +.section .text +_start: + lea rbx, [area] + add rbx, 63 + and rbx, -64 + + movdqu xmm6, [pat6] + movdqu xmm7, [pat7] + + mov eax, 3 + xor edx, edx + xsave64 [rbx] + + mov rax, [rbx + 0x100] + cmp rax, [pat6] + jne fail + mov rax, [rbx + 0x108] + cmp rax, [pat6 + 8] + jne fail + mov rax, [rbx + 0x110] + cmp rax, [pat7] + jne fail + mov rax, [rbx + 0x118] + cmp rax, [pat7 + 8] + jne fail + + movdqu xmm6, [pat8] + movdqu xmm7, [pat9] + + mov eax, 3 + xor edx, edx + xrstor64 [rbx] + + movdqu [actual], xmm6 + movdqu [actual + 16], xmm7 + mov rax, [actual] + cmp rax, [pat6] + jne fail + mov rax, [actual + 8] + cmp rax, [pat6 + 8] + jne fail + mov rax, [actual + 16] + cmp rax, [pat7] + jne fail + mov rax, [actual + 24] + cmp rax, [pat7 + 8] + jne fail + + xor edi, edi + jmp exit +fail: + mov edi, 1 +exit: + mov eax, 60 + syscall + +.section .rodata +.p2align 4 +pat6: .quad 0x1111111111111111, 0x2222222222222222 +pat7: .quad 0x3333333333333333, 0x4444444444444444 +pat8: .quad 0x5555555555555555, 0x6666666666666666 +pat9: .quad 0x7777777777777777, 0x8888888888888888 + +.section .bss +.p2align 6 +area: .skip 2048 +actual: .skip 32