Skip to content

Make HPTT buildable with MSVC - #5

Merged
IvanaGyro merged 7 commits into
mainfrom
msvc-build
Jul 30, 2026
Merged

Make HPTT buildable with MSVC#5
IvanaGyro merged 7 commits into
mainfrom
msvc-build

Conversation

@IvanaGyro

@IvanaGyro IvanaGyro commented Jul 29, 2026

Copy link
Copy Markdown
Member

Makes HPTT configure and build with MSVC 2022 + Ninja. Addresses Cytnx-dev/Cytnx#1112.

Verified locally against cl 19.44.35228 (VS 2022 BuildTools 14.44.35207), CMake 4.4.1, Ninja 1.13.2.

The build failures, in order of discovery

# Symptom Fix
1 ninja: error: build.ninja:187: multiple rules generate hptt.lib ARCHIVE_OUTPUT_NAME hptt_dyn on the shared target only
2 __restrict__ rejected HPTT_RESTRICT -> __restrict on MSVC
3 INLINE undefined __forceinline for _MSC_VER
4 C2131 on variable-length arrays std::vector
5 -mavx rejected /arch:AVX
6 DLL exports nothing, no import library produced WINDOWS_EXPORT_ALL_SYMBOLS
7 hptt.h unparseable by consumers (_Complex) declared only where _Complex exists

Item 7 was not in the issue list. cTensorTranspose / zTensorTranspose are declared with the C99 _Complex specifier; MSVC has no _Complex in C++ mode, where <complex.h> resolves to the STL header and forwards to <complex>. None of HPTT's own sources include hptt.h, so this breaks only consumers of the installed headers — which is exactly how Cytnx uses HPTT.

On the artifact naming

The issue offered two shapes. This PR takes the narrower one: rename only the import library. The DLL stays hptt.dll and the static archive stays hptt.lib; only the colliding artifact becomes hptt_dyn.lib. ARCHIVE_OUTPUT_NAME applies only to DLL-platform shared libraries, so libhptt.a / libhptt.so / libhptt.dylib are untouched.

HPTT_BUILD_SHARED

New option, default ON, so standalone builds are unchanged. A parent that links only hptt_static can set it OFF and skip the second full compile of every source. HPTT_TARGETS collects the enabled targets so export(TARGETS) and install(TARGETS) follow the option.

OpenMP: /openmp is sufficient — no /openmp:llvm

The issue's plan implies OpenMP 3.x is needed. It isn't, and I checked before assuming:

  • HPTT_DUPLICATE_2 — the only collapse(2) user — is never called anywhere in the tree.
  • Every #pragma omp parallel for (transpose.cpp:840, transpose.cpp:1941, utils.cpp:81) uses an int induction variable, so OpenMP 2.0's signed-index restriction (C3016) never bites.

All four sources compile clean under plain /openmp. This matters beyond tidiness: /openmp:llvm would require redistributing libomp140.x86_64.dll, whereas /openmp's vcomp140.dll already ships in System32. No source or CMake change was needed here.

Verification

Every configuration below was built and run:

Configuration Result
Ninja + MSVC, shared ON (default) pass — hptt.lib, hptt.dll, hptt_dyn.lib
HPTT_BUILD_SHARED=OFF pass — hptt.lib only, no DLL
install + find_package(hptt CONFIG) consumer, static pass
same, linking hptt::hptt_dyn against the DLL pass
add_subdirectory + static-only (Cytnx's integration mode) pass
ENABLE_AVX=ON pass, /arch:AVX present in compile_commands.json
Visual Studio 17 2022 generator, x64 pass

Correctness was checked with a consumer that transposes random tensors against a naive index-arithmetic reference: all four instantiated scalar types, 2D through 5D, non-trivial permutations, beta != 0, complex alpha/beta, and 1 and 4 threads. Every case matched bit-exactly (max|B-Bref| = 0), against both the static library and the DLL.

Cross-platform verification

The CI added in #6 is based on this branch, so its run covers these commits. All six jobs pass: linux-x64, linux-arm64, macos-x64, macos-arm64, windows-x64, windows-arm64 (https://github.com/Cytnx-dev/hptt/actions/runs/30481654763). Each job builds HPTT and then runs the correctness consumer against both the build tree and an install tree.

One caveat: linux-arm64 is green only with an additional fix that lives in #6, not here. HPTT applies -march=x86-64 on every architecture, so GCC on aarch64 rejects it (unknown value 'x86-64' for '-march') before any of this branch's changes matter. That is a pre-existing bug unrelated to MSVC, so it is fixed there rather than in this PR. The other five platforms validate this branch as it stands.

Pre-existing warnings, left alone

MSVC emits C4267/C4305/C4244 narrowing warnings and C4068 for the Intel-only #pragma vector nontemporal. All predate this PR and none block the build.

IvanaGyro and others added 7 commits July 30, 2026 02:21
`__restrict__` and `__attribute__((always_inline))` are GNU spellings.
MSVC accepts neither: it spells the restrict qualifier `__restrict` and
forced inlining `__forceinline`, so every declaration carrying one of
these failed to parse under cl.

Add HPTT_RESTRICT to hptt_types.h and use it in place of `__restrict__`
throughout transpose.h and transpose.cpp, and give macros.h an INLINE
definition for _MSC_VER.

HPTT_RESTRICT lives in hptt_types.h rather than macros.h because
transpose.h -- a public, installed header -- applies it to the A_ and B_
data members, and macros.h also defines the unprefixed `INLINE`, which
should not leak into consumers' translation units.

clang-cl defines _MSC_VER but understands the GNU spelling, so it is
excluded from the MSVC branch.

Co-Authored-By: Claude <noreply@anthropic.com>
The Transpose constructor sized four int buffers by the runtime `dim`
argument, and getBestLoopOrder() sized its cost matrix by `dim_*dim_`.
Variable-length arrays are a C99 feature that C++ never adopted; GCC and
Clang accept them as an extension, MSVC rejects them with C2131.

Use std::vector for all five and pass .data() at the call sites that
expect a raw pointer.

Co-Authored-By: Claude <noreply@anthropic.com>
cTensorTranspose() and zTensorTranspose() are declared with the C99
`_Complex` type specifier. MSVC does not support `_Complex` when
compiling C++: in C++ mode <complex.h> resolves to the STL header, which
forwards to <complex> and never declares the UCRT's _Fcomplex /
_Dcomplex. cl therefore rejects both declarations with C2146/C3646/C2062.

Nothing in HPTT's own sources includes hptt.h, so this only breaks
consumers of the installed headers -- but for those it makes the public
header unusable on MSVC entirely.

There is no portable spelling for these two signatures, so guard them on
compilers that understand `_Complex`. clang-cl defines _MSC_VER but does
understand it, so it keeps the declarations.

Co-Authored-By: Claude <noreply@anthropic.com>
ENABLE_AVX appended the GCC/Clang flag -mavx unconditionally. cl does not
accept it; the MSVC equivalent is /arch:AVX. Select per compiler.

ENABLE_ARM appended -mfpu=neon, which is an ARM32 flag with no MSVC
counterpart -- on MSVC ARM64 Advanced SIMD is part of the baseline ISA
and /arch: only selects newer extensions. Skip the flag there and keep
defining HPTT_ARCH_ARM, which is what selects the Neon kernels and
REGISTER_BITS=128.

Co-Authored-By: Claude <noreply@anthropic.com>
hptt_dyn was built unconditionally. A parent project that embeds HPTT via
add_subdirectory() and links only hptt_static still paid for a second
full compile of every source, and on Windows it also inherited the
shared target's artifact-naming constraints for a library it never uses.

Add HPTT_BUILD_SHARED (default ON, so standalone builds are unchanged)
and create hptt_dyn only when it is set. Collect the enabled targets in
HPTT_TARGETS so export(TARGETS) and install(TARGETS) both follow the
option without naming hptt_dyn directly.

Co-Authored-By: Claude <noreply@anthropic.com>
No declaration in HPTT is annotated with __declspec(dllexport), so on
Windows the DLL exported nothing. The linker then emitted no import
library at all, which leaves install(TARGETS hptt_dyn ARCHIVE ...) with
no file to install and gives consumers nothing to link against.

Set WINDOWS_EXPORT_ALL_SYMBOLS on hptt_dyn. CMake generates the .def file
from the object files, which covers the four explicit Transpose
instantiations and the create_plan() overloads. The property is ignored
on non-Windows platforms.

Co-Authored-By: Claude <noreply@anthropic.com>
hptt_configure_target() sets OUTPUT_NAME to `hptt` for both targets. On
Windows a static library and a DLL's import library are both ARCHIVE
artifacts named <OUTPUT_NAME>.lib, so the two targets claimed the same
output path. The Ninja generator rejects the resulting build graph with

    ninja: error: build.ninja:187: multiple rules generate hptt.lib

Set ARCHIVE_OUTPUT_NAME on hptt_dyn only. The DLL keeps the name
hptt.dll and the static archive keeps hptt.lib; just the import library
becomes hptt_dyn.lib. ARCHIVE_OUTPUT_NAME applies only to DLL-platform
shared libraries, so libhptt.a, libhptt.so and libhptt.dylib are
unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant