-
Notifications
You must be signed in to change notification settings - Fork 779
Windows arm64: enable building with MSVC #2673
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -143,7 +143,7 @@ jobs: | |
|
|
||
| - run: rustup toolchain install --no-self-update --profile=minimal 1.66.0 | ||
|
|
||
| - run: echo "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\Llvm\x64\bin" >> $GITHUB_PATH | ||
| - run: echo "VSINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2022\Enterprise" >> $GITHUB_ENV | ||
| shell: bash | ||
|
|
||
| - run: sh mk/package.sh | ||
|
|
@@ -362,20 +362,16 @@ jobs: | |
| with: | ||
| persist-credentials: false | ||
|
|
||
| # From https://github.com/actions/partner-runner-images/issues/77 | ||
| - if: ${{ contains(matrix.host_os, 'windows') && contains(matrix.host_os, 'arm') }} | ||
| run: | | ||
| curl -LOs https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe | ||
| ./rustup-init.exe -y --default-toolchain none --no-modify-path | ||
| echo "$USERPROFILE/.cargo/bin" >> "$GITHUB_PATH" | ||
| shell: sh | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for pointing this out; see PR #2675. |
||
|
|
||
| - run: mk/install-build-tools.sh +${{ matrix.rust_channel }} --target=${{ matrix.target }} | ||
| shell: sh | ||
|
|
||
| - if: ${{ contains(matrix.host_os, 'windows') && contains(matrix.target, '86') }} | ||
| run: ./mk/install-build-tools.ps1 | ||
|
|
||
| - if: ${{ contains(matrix.host_os, 'windows') }} | ||
| run: echo "VSINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2022\Enterprise" >> $GITHUB_ENV | ||
| shell: bash | ||
|
|
||
| - if: ${{ matrix.xcode_version != '' }} | ||
| run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode_version }}.app | ||
|
|
||
|
|
@@ -491,11 +487,6 @@ jobs: | |
| - if: ${{ contains(matrix.host_os, 'windows') && contains(matrix.target, '86') }} | ||
| run: ./mk/install-build-tools.ps1 | ||
|
|
||
| - if: ${{ matrix.target == 'aarch64-pc-windows-msvc' && !contains(matrix.host_os, 'arm') }} | ||
| run: | | ||
| echo "C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin" >> $GITHUB_PATH | ||
| shell: bash | ||
|
|
||
| - if: ${{ !contains(matrix.host_os, 'windows') }} | ||
| run: | | ||
| mk/cargo.sh +${{ matrix.rust_channel }} test --lib --tests -vv --target=${{ matrix.target }} ${{ matrix.cargo_options }} ${{ matrix.features }} ${{ matrix.mode }} ${{ matrix.cargo_test_options }} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,6 +83,7 @@ mod env { | |
| define_env! { pub OUT_DIR: SetByCargo } | ||
| define_env! { pub PERL_EXECUTABLE: RerunIfChanged } | ||
| define_env! { pub RING_PREGENERATE_ASM: RerunIfChanged } | ||
| define_env! { pub VSINSTALLDIR: RerunIfChanged } | ||
| } | ||
|
|
||
| const X86: &str = "x86"; | ||
|
|
@@ -272,6 +273,9 @@ impl AsmTarget { | |
| fn use_nasm(&self) -> bool { | ||
| [WIN32N, NASM].contains(&self.perlasm_format) | ||
| } | ||
| fn use_clang_win(&self) -> bool { | ||
| self.perlasm_format == "win64" | ||
| } | ||
| } | ||
|
|
||
| /// Operating systems that have the same ABI as Linux on every architecture | ||
|
|
@@ -427,13 +431,17 @@ fn generate_sources_and_preassemble<'a>( | |
| let perlasm_src_dsts = perlasm_src_dsts(out_dir, asm_target); | ||
| perlasm(&perl_exe, &perlasm_src_dsts, asm_target, c_root_dir); | ||
|
|
||
| if asm_target.use_nasm() { | ||
| // Package pregenerated object files in addition to pregenerated | ||
| // assembly language source files, so that the user doesn't need | ||
| // to install the assembler. | ||
| // Package pregenerated object files in addition to pregenerated | ||
| // assembly language source files, so that the user doesn't need | ||
| // to install the assembler (NASM on x64/86 Windows and MSVC's Clang on arm64 Windows). | ||
| if asm_target.use_nasm() || asm_target.use_clang_win() { | ||
| let srcs = asm_srcs(perlasm_src_dsts); | ||
| for src in srcs { | ||
| nasm(&src, asm_target.arch, out_dir, out_dir, c_root_dir); | ||
| if asm_target.use_nasm() { | ||
| nasm(&src, asm_target.arch, out_dir, out_dir, c_root_dir); | ||
| } else { | ||
| clang_win(&src, asm_target.arch, out_dir, out_dir); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -466,8 +474,8 @@ fn build_c_code( | |
|
|
||
| let asm_srcs = asm_srcs(perlasm_src_dsts); | ||
|
|
||
| if asm_target.use_nasm() { | ||
| // Nasm was already used to generate the object files, so use them instead of | ||
| if asm_target.use_nasm() || asm_target.use_clang_win() { | ||
| // Nasm or Clang was already used to generate the object files, so use them instead of | ||
| // assembling. | ||
| let obj_srcs = asm_srcs | ||
| .iter() | ||
|
|
@@ -489,7 +497,7 @@ fn build_c_code( | |
| // We don't (and can't) use any .S on Windows since MSVC and NASM can't assemble | ||
| // them. | ||
| if extension == "S" | ||
| && (target.arch == X86_64 || target.arch == X86) | ||
| && (target.arch == X86_64 || target.arch == X86 || target.arch == AARCH64) | ||
| && target.os == WINDOWS | ||
| { | ||
| return false; | ||
|
|
@@ -593,13 +601,6 @@ fn obj_path(out_dir: &Path, src: &Path) -> PathBuf { | |
|
|
||
| fn configure_cc(c: &mut cc::Build, target: &Target, c_root_dir: &Path, include_dir: &Path) { | ||
| let compiler = c.get_compiler(); | ||
| // FIXME: On Windows AArch64 we currently must use Clang to compile C code | ||
| let compiler = if target.os == WINDOWS && target.arch == AARCH64 && !compiler.is_like_clang() { | ||
| let _ = c.compiler("clang"); | ||
| c.get_compiler() | ||
| } else { | ||
| compiler | ||
| }; | ||
|
|
||
| let _ = c.include(c_root_dir.join("include")); | ||
| let _ = c.include(include_dir); | ||
|
|
@@ -669,6 +670,45 @@ fn nasm(file: &Path, arch: &str, include_dir: &Path, out_dir: &Path, c_root_dir: | |
| run_command(c); | ||
| } | ||
|
|
||
| fn clang_win(file: &Path, arch: &str, include_dir: &Path, out_dir: &Path) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think instead of this, it would be better to create two separate libraries: one for assembly code, and one for C code. Then configure the |
||
| // TODO use the CC API instead of the hardcoded path below. | ||
| // Needs https://github.com/rust-lang/cc-rs/pull/1506 to be merged. | ||
| // cc::windows_registry::find(asm_target, "clang.exe"); | ||
|
|
||
| let out_file = obj_path(out_dir, file); | ||
| // Use Clang that's bundled with MSVC explicitly. | ||
| // This is necessary because the `cc` crate uses the system Clang by default, | ||
| // which is not compatible with MSVC. | ||
| let vs_path = env::var_os(&env::VSINSTALLDIR).unwrap(); | ||
| let clang_path = PathBuf::from(vs_path) | ||
| .join("VC") | ||
| .join("Tools") | ||
| .join("Llvm") | ||
| .join("bin"); | ||
|
|
||
| // Check if path exists | ||
| if !clang_path.exists() { | ||
| panic!("Clang path does not exist: {}", clang_path.display()); | ||
| } | ||
|
|
||
| let mut c = Command::new(clang_path.join("clang.exe")); | ||
|
|
||
| // Should be called like this: | ||
| // clang -target aarch64-windows -c chacha-armv8-win64.S -o chacha-armv8-win64.o -I../include -I./ | ||
| let _ = c | ||
| .arg("-target") | ||
| .arg(format!("{arch}-pc-windows-msvc")) | ||
| .arg("-c") | ||
| .arg(file) | ||
| .arg("-o") | ||
| .arg(out_file.to_str().expect("Invalid path")) | ||
| .arg("-I") | ||
| .arg("include/") | ||
| .arg("-I") | ||
| .arg(include_dir); | ||
| run_command(c); | ||
| } | ||
|
|
||
| fn run_command_with_args(command_name: &Path, args: &[OsString]) { | ||
| let mut cmd = Command::new(command_name); | ||
| let _ = cmd.args(args); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,11 +18,15 @@ | |
|
|
||
| #include <ring-core/base.h> | ||
|
|
||
| #if defined(OPENSSL_X86_64) && defined(_MSC_VER) && !defined(__clang__) | ||
| #if defined(_MSC_VER) && defined(OPENSSL_64_BIT) && !defined(__clang__) | ||
| #pragma warning(push, 3) | ||
| #include <intrin.h> | ||
| #pragma warning(pop) | ||
| #if defined(OPENSSL_X86_64) | ||
| #pragma intrinsic(_umul128) | ||
| #elif defined(OPENSSL_AARCH64) | ||
| #pragma intrinsic(__umulh) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why BoringSSL avoids doing this. My guess is they aren't testing AArch64 Windows builds using MSVC, but instead forcing the use of clang.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
They actually do use it, looking at the most recent version of
They do build arm64 Windows using both MSVC and Clang in their CI. They even seem to prefer MSVC over Clang:
I've just configured an example pipeline in GitHub Actions to build BoringSSL using both MSVC and Clang on arm64. The build succeeds using both compilers. Note that with MSVC, |
||
| #endif | ||
| #endif | ||
|
|
||
| #include "../../internal.h" | ||
|
|
@@ -132,6 +136,10 @@ static inline void bn_umult_lohi(BN_ULONG *low_out, BN_ULONG *high_out, | |
| BN_ULONG a, BN_ULONG b) { | ||
| #if defined(OPENSSL_X86_64) && defined(_MSC_VER) && !defined(__clang__) | ||
| *low_out = _umul128(a, b, high_out); | ||
| #elif defined(OPENSSL_AARCH64) && defined(_MSC_VER) && !defined(__clang__) | ||
| /* Use __umulh intrinsic for 64x64->128 bit multiplication on MSVC ARM64 */ | ||
| *low_out = a * b; | ||
| *high_out = __umulh(a, b); | ||
| #else | ||
| BN_ULLONG result = (BN_ULLONG)a * b; | ||
| *low_out = (BN_ULONG)result; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,7 +34,7 @@ | |
| #error "MSVC 2015 Update 2 or later is required." | ||
| #endif | ||
| typedef uint8_t Carry; | ||
| #if LIMB_BITS == 64 | ||
| #if LIMB_BITS == 64 && defined(OPENSSL_X86_64) | ||
| #pragma intrinsic(_addcarry_u64, _subborrow_u64) | ||
| #define RING_CORE_ADDCARRY_INTRINSIC _addcarry_u64 | ||
| #define RING_CORE_SUBBORROW_INTRINSIC _subborrow_u64 | ||
|
|
@@ -60,6 +60,13 @@ static inline Carry limb_adc(Limb *r, Limb a, Limb b, Carry carry_in) { | |
| Carry ret; | ||
| #if defined(RING_CORE_ADDCARRY_INTRINSIC) | ||
| ret = RING_CORE_ADDCARRY_INTRINSIC(carry_in, a, b, r); | ||
| #elif defined(OPENSSL_AARCH64) && defined(_MSC_VER) && !defined(__clang__) | ||
| /* Manual 64-bit addition for MSVC ARM64 (no __uint128_t) */ | ||
| Limb sum = a + carry_in; | ||
| Carry carry1 = (sum < a) ? 1 : 0; /* carry from a + carry_in */ | ||
| *r = sum + b; | ||
| Carry carry2 = (*r < sum) ? 1 : 0; /* carry from sum + b */ | ||
| ret = carry1 | carry2; | ||
|
Comment on lines
+63
to
+69
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the lack of
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I cannot accept PRs of unknown provenance.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Upstream BoringSSL has some changes that we should bring in related to this. But, again, it looks like they are just avoiding MSVC on AArch64. |
||
| #else | ||
| DoubleLimb x = (DoubleLimb)a + b + carry_in; | ||
| *r = (Limb)x; | ||
|
|
@@ -74,6 +81,10 @@ static inline Carry limb_add(Limb *r, Limb a, Limb b) { | |
| Carry ret; | ||
| #if defined(RING_CORE_ADDCARRY_INTRINSIC) | ||
| ret = RING_CORE_ADDCARRY_INTRINSIC(0, a, b, r); | ||
| #elif defined(OPENSSL_AARCH64) && defined(_MSC_VER) && !defined(__clang__) | ||
| /* Manual 64-bit addition for MSVC ARM64 (no __uint128_t) */ | ||
| *r = a + b; | ||
| ret = (*r < a) ? 1 : 0; /* carry if result overflowed */ | ||
| #else | ||
| DoubleLimb x = (DoubleLimb)a + b; | ||
| *r = (Limb)x; | ||
|
|
@@ -90,6 +101,13 @@ static inline Carry limb_sbb(Limb *r, Limb a, Limb b, Carry borrow_in) { | |
| Carry ret; | ||
| #if defined(RING_CORE_SUBBORROW_INTRINSIC) | ||
| ret = RING_CORE_SUBBORROW_INTRINSIC(borrow_in, a, b, r); | ||
| #elif defined(OPENSSL_AARCH64) && defined(_MSC_VER) && !defined(__clang__) | ||
| /* Manual 64-bit subtraction for MSVC ARM64 (no __uint128_t) */ | ||
| Limb temp = a - borrow_in; | ||
| Carry borrow1 = (temp > a) ? 1 : 0; /* borrow from a - borrow_in */ | ||
| *r = temp - b; | ||
| Carry borrow2 = (*r > temp) ? 1 : 0; /* borrow from temp - b */ | ||
| ret = borrow1 | borrow2; | ||
| #else | ||
| DoubleLimb x = (DoubleLimb)a - b - borrow_in; | ||
| *r = (Limb)x; | ||
|
|
@@ -104,6 +122,10 @@ static inline Carry limb_sub(Limb *r, Limb a, Limb b) { | |
| Carry ret; | ||
| #if defined(RING_CORE_SUBBORROW_INTRINSIC) | ||
| ret = RING_CORE_SUBBORROW_INTRINSIC(0, a, b, r); | ||
| #elif defined(OPENSSL_AARCH64) && defined(_MSC_VER) && !defined(__clang__) | ||
| /* Manual 64-bit subtraction for MSVC ARM64 (no __uint128_t) */ | ||
| *r = a - b; | ||
| ret = (*r > a) ? 1 : 0; /* borrow if result underflowed */ | ||
| #else | ||
| DoubleLimb x = (DoubleLimb)a - b; | ||
| *r = (Limb)x; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't be needed anymore once rust-lang/cc-rs#1506 gets merged. It will then be possible to get the path to Visual Studio's
clang.exelike this:Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's wait for the cc-rs changes. Then we can increase our cc-rs version dependency and rely on it to find this for us.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cc-rs change that I think we need is rust-lang/cc-rs#1367.