diff options
| author | Kenjiro Nakayama <nakayamakenjiro@gmail.com> | 2026-08-13 09:23:31 +0900 |
|---|---|---|
| committer | Kenjiro Nakayama <nakayamakenjiro@gmail.com> | 2026-08-13 11:56:29 +0900 |
| commit | c88ca1f5d3d910d1b1dc9c5f5fe30f261c078e90 (patch) | |
| tree | 6825d7e49ea925852e6df6c1e91f798809d07144 | |
| parent | 3e403cfebd94cbff248e7acac5073a7a123c421b (diff) | |
| download | portable-c88ca1f5d3d910d1b1dc9c5f5fe30f261c078e90.tar.gz portable-c88ca1f5d3d910d1b1dc9c5f5fe30f261c078e90.tar.bz2 portable-c88ca1f5d3d910d1b1dc9c5f5fe30f261c078e90.zip | |
cmake: detect the MSVC ARM64 optimizer bug
Run the reproducer when configuring native MSVC ARM64 builds and
enable the bn_ct_ne_zero() workaround only when the test fails. Retain
the workaround when cross-compiling, where the test cannot be run.
Run the check in the Release configuration and report its result so the
selected code path is visible in the configure log.
| -rw-r--r-- | CMakeLists.txt | 91 | ||||
| -rw-r--r-- | patches/win32_arm64_bn_arch.h.patch | 5 |
2 files changed, 94 insertions, 2 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b15da9..0a59ddc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -522,6 +522,97 @@ else() | |||
| 522 | set(ENABLE_ASM false) | 522 | set(ENABLE_ASM false) |
| 523 | endif() | 523 | endif() |
| 524 | 524 | ||
| 525 | if(MSVC AND HOST_AARCH64 AND NOT CMAKE_CROSSCOMPILING AND | ||
| 526 | CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "(aarch64|arm64|ARM64)") | ||
| 527 | set(BN_CT_TEST_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/bn_ct_test.c") | ||
| 528 | file(WRITE "${BN_CT_TEST_SOURCE}" [=[ | ||
| 529 | #include <inttypes.h> | ||
| 530 | #include <stdint.h> | ||
| 531 | #include <stdio.h> | ||
| 532 | |||
| 533 | typedef uint64_t BN_ULONG; | ||
| 534 | |||
| 535 | #define BN_BITS2 64 | ||
| 536 | |||
| 537 | static __inline int | ||
| 538 | bn_ct_ne_zero(BN_ULONG w) | ||
| 539 | { | ||
| 540 | return (w | ~(w - 1)) >> (BN_BITS2 - 1); | ||
| 541 | } | ||
| 542 | |||
| 543 | static __inline void | ||
| 544 | bn_addw(BN_ULONG a, BN_ULONG b, BN_ULONG *out_r1, BN_ULONG *out_r0) | ||
| 545 | { | ||
| 546 | BN_ULONG c1, c2, r1, r0; | ||
| 547 | |||
| 548 | c1 = a | b; | ||
| 549 | c2 = a & b; | ||
| 550 | r0 = a + b; | ||
| 551 | r1 = ((c1 & ~r0) | c2) >> (BN_BITS2 - 1); | ||
| 552 | |||
| 553 | *out_r1 = r1; | ||
| 554 | *out_r0 = r0; | ||
| 555 | } | ||
| 556 | |||
| 557 | static __inline void | ||
| 558 | bn_addw_addw(BN_ULONG a, BN_ULONG b, BN_ULONG c, BN_ULONG *out_r1, | ||
| 559 | BN_ULONG *out_r0) | ||
| 560 | { | ||
| 561 | BN_ULONG carry, r1, r0; | ||
| 562 | |||
| 563 | bn_addw(a, b, &r1, &r0); | ||
| 564 | bn_addw(r0, c, &carry, &r0); | ||
| 565 | r1 += carry; | ||
| 566 | |||
| 567 | *out_r1 = r1; | ||
| 568 | *out_r0 = r0; | ||
| 569 | } | ||
| 570 | |||
| 571 | static BN_ULONG | ||
| 572 | make_mask(BN_ULONG a, BN_ULONG b, BN_ULONG c, BN_ULONG borrow) | ||
| 573 | { | ||
| 574 | BN_ULONG carry, ignored; | ||
| 575 | |||
| 576 | bn_addw_addw(a, b, c, &carry, &ignored); | ||
| 577 | |||
| 578 | return bn_ct_ne_zero(carry) - borrow; | ||
| 579 | } | ||
| 580 | |||
| 581 | static volatile BN_ULONG input_a = UINT64_MAX; | ||
| 582 | static volatile BN_ULONG input_b = 1; | ||
| 583 | static volatile BN_ULONG input_c = 0; | ||
| 584 | static volatile BN_ULONG input_borrow = 0; | ||
| 585 | |||
| 586 | int | ||
| 587 | main(void) | ||
| 588 | { | ||
| 589 | BN_ULONG actual, expected = 1; | ||
| 590 | |||
| 591 | actual = make_mask(input_a, input_b, input_c, input_borrow); | ||
| 592 | if (actual != expected) { | ||
| 593 | printf("FAIL: expected 0x%016" PRIx64 ", got 0x%016" PRIx64 "\n", | ||
| 594 | expected, actual); | ||
| 595 | return 1; | ||
| 596 | } | ||
| 597 | |||
| 598 | return 0; | ||
| 599 | } | ||
| 600 | ]=]) | ||
| 601 | set(BN_CT_SAVED_CONFIGURATION "${CMAKE_TRY_COMPILE_CONFIGURATION}") | ||
| 602 | set(CMAKE_TRY_COMPILE_CONFIGURATION Release) | ||
| 603 | try_run(BN_CT_RUN_RESULT BN_CT_COMPILED | ||
| 604 | "${CMAKE_CURRENT_BINARY_DIR}" | ||
| 605 | "${BN_CT_TEST_SOURCE}") | ||
| 606 | set(CMAKE_TRY_COMPILE_CONFIGURATION "${BN_CT_SAVED_CONFIGURATION}") | ||
| 607 | if(BN_CT_COMPILED AND BN_CT_RUN_RESULT EQUAL 0) | ||
| 608 | message(STATUS "MSVC ARM64 optimizer check passed; workaround disabled") | ||
| 609 | add_definitions(-DMSVC_ARM64_BN_CT_NE_ZERO_WORKS) | ||
| 610 | else() | ||
| 611 | message(STATUS "MSVC ARM64 optimizer check failed; workaround enabled " | ||
| 612 | "(compiled=${BN_CT_COMPILED}, result=${BN_CT_RUN_RESULT})") | ||
| 613 | endif() | ||
| 614 | endif() | ||
| 615 | |||
| 525 | if(ENABLE_ASM) | 616 | if(ENABLE_ASM) |
| 526 | if(CMAKE_C_COMPILER_ABI STREQUAL "ELF") | 617 | if(CMAKE_C_COMPILER_ABI STREQUAL "ELF") |
| 527 | if(HOST_AARCH64) | 618 | if(HOST_AARCH64) |
diff --git a/patches/win32_arm64_bn_arch.h.patch b/patches/win32_arm64_bn_arch.h.patch index 0d0db42..c8071a8 100644 --- a/patches/win32_arm64_bn_arch.h.patch +++ b/patches/win32_arm64_bn_arch.h.patch | |||
| @@ -1,11 +1,12 @@ | |||
| 1 | --- crypto/bn/arch/aarch64/bn_arch.h.orig | 1 | --- crypto/bn/arch/aarch64/bn_arch.h.orig |
| 2 | +++ crypto/bn/arch/aarch64/bn_arch.h | 2 | +++ crypto/bn/arch/aarch64/bn_arch.h |
| 3 | @@ -20,4 +20,18 @@ | 3 | @@ -20,4 +20,19 @@ |
| 4 | #ifndef HEADER_BN_ARCH_H | 4 | #ifndef HEADER_BN_ARCH_H |
| 5 | #define HEADER_BN_ARCH_H | 5 | #define HEADER_BN_ARCH_H |
| 6 | - | 6 | - |
| 7 | +/* See pull/1355. */ | 7 | +/* See pull/1355. */ |
| 8 | +#if defined(_MSC_VER) && defined(_M_ARM64) && _MSC_FULL_VER < 195136252 | 8 | +#if defined(_MSC_VER) && defined(_M_ARM64) && \ |
| 9 | + !defined(MSVC_ARM64_BN_CT_NE_ZERO_WORKS) | ||
| 9 | + | 10 | + |
| 10 | +#include <intrin.h> | 11 | +#include <intrin.h> |
| 11 | + | 12 | + |
