October 15th, 2026
0 reactions

MSVC Build Tools Version 14.52 Release Candidate Now Available

Software Engineering Manager

We are pleased to announce the availability of the release candidate for Microsoft C++ (MSVC) Build Tools version 14.52. With this release, C++23 is ready for production use and available through the /std:c++23 compiler option; see below for details. This is the last MSVC v14.52 preview before it moves to stable status and full support. You can download it from the Visual Studio 2026 Insiders Channel. As this is the release candidate, it is now the default toolset installed with C++ workloads in this channel. To check if it’s installed simply make sure one or both of these components are checked in the installer:

  1. MSVC Build Tools for x64/x86 (Latest)
  2. MSVC Build Tools for ARM64/ARM64EC (Latest)

The version number printed by cl.exe and link.exe will be at least 14.52.37025 / 19.52.37025.

We appreciate any feedback that you may have. Please report any issues on Visual Studio Developer Community so we can address them by the time we move out of preview.

Version 14.52 is expected to go stable in November and will ship with the 18.12 release of Visual Studio (Stable Channel).

This post summarizes the work done by the MSVC team between the 14.51 and 14.52 releases. This encompasses changes across the compiler frontend, backend, linker, standard library, and related tools.

C++ standards conformance

  • C++23 language mode: Use /std:c++23 to select C++23 language rules. The existing /std:c++23preview spelling still works and is an alias for /std:c++23. The preview form is now deprecated and will be removed in a future update.
  • Immediate escalation: C++23 immediate escalation (P2564R3) lets eligible constexpr functions and lambdas become immediate when their use requires it. It is enabled in C++23 and later modes; C++20 builds can opt in with /Zc:immediateEscalation.
  • Unicode identifiers: C++23 and C23 modes recognize the mathematical compatibility identifier ranges from P3658R1.
  • Constexpr allocation in modules: Module interfaces can represent constexpr functions that contain new T[n] expressions.
  • Preprocessor behavior: _Pragma works in more macro-expansion cases and is supported under /E.
  • Constexpr cmath: Support for C++ compile-time cmath functions remains off-by-default under /std:c++23 and may be enabled via the /Zc:cmath switch. Please see https://devblogs.microsoft.com/cppblog/msvc-c23-constexpr-cmath-with-llvm-libc/ for additional details.

C++/CLI, C, and language tooling

  • /clr is now available in C++23 and later modes.
  • The opt-in /Zc:functionToObjectPtr switch rejects implicit conversions from function pointers to void*.
  • The compiler now accepts selected constexpr constructor-template instantiations involving virtual base classes. Constructing those virtual bases during constant evaluation remains unsupported, so this is limited to the declaration checks for a constexpr function.
  • In C code, C4815 and C4816 now warn when a structure is allocated without storage for its flexible array member or when a structure with a flexible array member is passed by value.
  • The Linux C++ IntelliSense parser maps C++20 and C++23 standard options to the corresponding language modes.
  • Argument-dependent lookup now handles more names that are not exported from a module, and nested template and module instantiation is less prone to failure.
  • Module serialization preserves class data members and template information more consistently. Type merging also works better when several modules share a header unit.
  • Header units preserve macro whitespace so stringized macros survive a round trip. Module processing also handles types first defined in the global module fragment more reliably.
  • Math intrinsics and math headers now work more consistently with modules and constant evaluation.
  • Caret diagnostics use less memory, and several diagnostics reproduce the original source expression more accurately.
  • Preprocessor diagnostics better identify malformed and nested constant expressions.
  • The compiler handles invalid type-trait inputs and malformed function-parameter scopes more consistently.
  • The compiler warns when overriding a deprecated non-pure virtual function.

Code generation and optimization

Inlining:

  • LTCG now considers which fields of a small return object the caller reads, so inlining can discard work that computes unused fields.
  • Whole-program optimization can favor an inline when doing so exposes parts of a local object and makes unused stores removable.
  • A large outlined loop body can now inline through a small wrapper called from a loop.
  • With PGO, small size-optimized virtual-call targets can be speculatively inlined when the profile, size, and growth limits allow it.
  • The inliner follows function-pointer values through aggregate assignments, preserving more indirect-call opportunities in template adapters.
  • Type-based devirtualization now works alongside profile-guided inlining. Hot/cold information also does more to prevent cold code from being pulled into hot callers.

Loop and scalar optimization:

  • The compiler can move more invariant loads out of loops after proving that each load executes on every iteration and remains safe across aliased pointers.
  • Equality-tested loops shed more loop-carried work, and strength reduction handles more wrapping integer induction expressions.
  • When three or more multiplications share a divisor, the optimizer can compute one reciprocal and reuse it instead of restoring repeated divisions.
  • Copy propagation through aliased fields is on by default and understands known-size memcpy and memset operations.
  • x64 conditional selection handles more nonzero constants and safe aliased operands.
  • Unsigned comparisons and induction-variable ranges now establish more nonzero, ordering, and non-overflow facts for later optimization.
  • Scalar conditional expressions gain more value-equivalence, incoming-value, multiply, and shift simplifications.
  • Calls use restrict arguments more precisely when determining memory interference, exposing more optimization around those calls.
  • The x86 optimizer performs more local and cross-block copy propagation.
  • The compiler recognizes more rotate idioms and dynamic bit-field extraction controls, and removes redundant POPCNT zero extension.
  • Small-aggregate optimization removes padding-only assignments, decomposes qualifying whole-struct loads, and forwards eligible loads through local memcpy operations.
  • Constant conversions can fold without /fp:fast when the transformation is otherwise valid.
  • Earlier loop-invariant motion and broader expression reassociation expose more work to later optimization.
  • Larger functions reuse more repeated expressions. Min/max expressions, mixed-signedness compare diamonds, and repeated vector-element extraction also simplify in more cases.
  • More indirect calls to noexcept functions become tail calls. Static constant arrays can also disappear when their storage is unnecessary.
  • Loop unswitching handles more nested-loop cases before vectorization, while straight-line vectorization handles more merged values.
  • Jump chains and induction variables simplify in more cases without losing useful predicates or signedness information.
  • The compiler emits less attribute data, scans exception regions fewer times, bounds pathological dead-use searches, and processes large chained function expressions in linear rather than quadratic time.

Vectorization:

  • On x64 and ARM64, the compiler vectorizes more code that selects values through branches.
  • Integer reductions such as sum += a[i] - b[i] can be reassociated into a form the loop vectorizer recognizes.
  • On x64 and ARM64, vector loads and stores reach copy propagation and scalar replacement earlier. Full-width vectors remain intact when splitting them would discard useful vector operations.
  • The vectorizer recognizes more adjacent-address patterns and is more willing to pack user-written SIMD values when the result can remain in a vector register.
  • A parameter-derived pointer advanced by a loop is recognized as non-aliasing a local array in more cases, avoiding an unnecessary runtime overlap check.
  • The loop vectorizer handles qualifying memory-only swaps and multi-stream copies, including std::swap on arrays of trivial types.
  • More loop-control expressions shared by a following conditional simplify after the loop.

ARM64

  • ARM64 now shares address calculations across related loads and stores, including array-of-structures loops based on function parameters. This removes repeated arithmetic and creates more opportunities for paired loads and stores.
  • Known NEON constants can be built without unnecessary data loads. Scalar loads, broadcasts, and lane inserts also fold into NEON load instructions in more cases.
  • The compiler combines more NEON construction and lane operations, removes unused lane inserts, and eliminates redundant mask comparisons.
  • The loop vectorizer now handles additional floor, ceil, trunc, and round operations.
  • Tail-call decisions are now made per call, so one ineligible call no longer blocks another eligible call in the same function.
  • ARM64 and ARM64EC inlining accounts for floating-point and vector register pressure. This preserves profitable native ARM64 inlines while avoiding excessive spills on restricted ARM64EC targets.
  • Native ARM64 SVE2 supports code generation and disassembly for lane-indexed svmullb and svmullt intrinsics. Execution requires SVE2-capable hardware.
  • LUTI2 and LUTI4 lookup-table support includes BF16 forms. Floating-point absolute minimum/maximum instructions and Common Short Sequence Compression operations are also supported. Each family requires the corresponding processor feature.
  • ARM64_FPMR names the FPMR register for _ReadStatusReg and _WriteStatusReg; armasm64 and the disassembler recognize the same name. Execution requires FEAT_FPMR hardware and OS permission.
  • Native ARM64 supports __preserve_none and [[msvc::musttail]], including specialized interpreter dispatch. The __preserve_none calling convention is C-only and is not supported on ARM64EC.
  • With /feature:sme, the compiler accepts SME attributes and ZA-state types on supported external declarations. Eligible scalar noexcept calls can perform the required streaming-mode transitions, including selected function-pointer, reference, whole-program, and Control Flow Guard cases. Streaming function definitions, ZA-bearing calls, unsupported pointer forms, and vector or aggregate arguments remain unsupported. ARM64EC is excluded.
  • The native ARM64 CRT supplies the AAPCS64 SME state-management routines for TPIDR2, ZA, and ZT0. Availability depends on processor and operating-system state support; ARM64EC is excluded.
  • On ARM64, the /Zc:cmath math runtime now supports the floating-point environment. This initial implementation reports math errors through floating-point exceptions rather than errno.
  • ARM64 stack layout can reuse slots for locals in mutually exclusive switch cases, greatly reducing the frame size of large switches.
  • Stateless SVE all-true predicates can move out of loops when the usual safety and profitability checks allow it.
  • Proven-dead switch edges are removed before switch expansion, exposing more control-flow and value optimizations.
  • Bitfield operations use narrower immediate forms where possible.
  • Register assignment preserves more adjacent load pairs and makes better spill/rematerialization choices for frequently used values.
  • Used-lane tracking now crosses more scalar floating-point operations, exposing dead vector work.
  • The compiler recognizes more vector permutations, per-lane rotations, and vector bit-clear immediate forms.
  • SVE comparisons and the MATCH/NMATCH/WHILE families reuse condition flags more effectively. NEON-to-SVE bridge moves, upper-lane float extraction, and by-element multiply addressing also produce shorter sequences.

x64 and x86

  • Speed-optimized x64 builds have more strategies for fixed-length memcmp. Eligible copies larger than 256 bytes use the runtime copy path instead of a large inline sequence.
  • x64 removes more redundant zero extensions before jump-table address calculations and lowers unsigned division by a constant power of two to a shift.
  • Constant f64x2 assignments can combine so a later vector operation folds to one 128-bit constant move.
  • 32-bit x86 builds can inline fmin, fmax, and copysign; ARM64 can inline copysign.
  • AVX2 and AVX-512 targets can build wider vectors from smaller register values without a memory round trip.
  • AVX-512 code generation uses suitable VEX moves for intrinsic operands and folds eligible single-use masked arithmetic followed by a blend into one merge-masked operation.
  • Builds using /feature:APX gain more arithmetic and conditional-selection forms, fewer redundant moves and comparisons, and APX-aware unrolling and strength-reduction decisions for scalar integer loops.
  • APX support now spans the ABI, assembler, disassembler, and debugging tools. R30 and R31 are nonvolatile, register assignment treats them as killed across setjmp, MASM and the disassembler handle eligible extended-register encodings, and DIA reads APX register context.
  • APX-enabled x64 code generation can fold eligible operands and makes better spill, recomputation, and fold choices.
  • Unwind V3 support now spans code generation, linking, ML64, DIA, and binary instrumentation. It covers chained runtime-function records, large prologs and epilogs, .PUSHFRAME/.POPFRAME, and stack walking. ML64 requires /unwindv3; compiler emission is on by default only for APX functions, and stack walking requires a V3-aware operating system and debugger.
  • x64 recognizes more condition-code-safe INC/DEC forms, including eligible atomic fetch-add sequences.
  • AVX2 intrinsic lowering removes unnecessary scalar-to-vector moves in qualifying _mm_set_sd and _mm256_set1_pd paths.
  • APX code generation includes more new-destination and memory-operand forms, shorter LEA replacements for eligible INC/DEC and SUB sequences, and broader shift, compare, conditional-compare, conditional-move, and constant-propagation support.
  • All-bits-set vector constants and functions containing paired register operations receive better code generation.
  • Shift-range analysis models the hardware shift-count mask, and adjacent two-element double stores combine more effectively.
  • Under /O1 and /Os, additional pre-allocation optimizations and size-aware instruction costs reduce code size.
  • RDPRU uses its required fixed-register constraints.

Standard Library

See the STL Changelog – MSVC Build Tools 14.52 for a complete list of all of the changes in this release, grouped by features, LWG issue resolutions, bug fixes, performance improvements, etc. Some highlights:

  • The STL is C++23 feature complete now that P0533R9 “constexpr For <cmath> And <cstdlib>” has been implemented.
    • This is initially supported for MSVC only, under /std:c++23 /Zc:cmath. We expect to make /std:c++23 imply /Zc:cmath in a future release, but it is currently opt-in. Clang and EDG support will be enabled when the necessary compiler machinery becomes available.
    • For more info, read MSVC C++23: constexpr cmath with LLVM Libc by Cody Miller (MSVC compiler front-end dev) and Bringing Correctly Rounded Math to Production with LLVM-libc by Michael Jones and Tue Ly (LLVM-libc maintainers at Google).
    • And under /std:c++latest /Zc:cmath, we’ve mostly implemented C++26 P1383R2 ‘s constexpr additions for <cmath>. (No <complex> changes yet.) A few functions are not yet implemented by LLVM-libc (see LLVM libc math status ) and accordingly are not yet constexpr. For double, the functions acosh, asinh, atanh, cosh, sinh, tanh, and erf are missing. For float and double, the functions erfc, lgamma, and tgamma are missing. (MSVC uses 64-bit long double, so we don’t need a third implementation of each function.) As soon as they become available, we’ll activate them as constexpr in future releases.
  • 19 LWG issue resolutions, which are bugfixes in the Standard.
  • Improved <regex> performance in several scenarios.
    • Special thanks to Julian Müller (@muellerj2 on GitHub) for this ongoing correctness and performance overhaul of <regex>.
  • Added ARM64 SVE vectorized implementations of several STL algorithms.
    • Also fixed regressions and improved performance in some of the ARM64/ARM64EC Neon vectorized implementations that were previously added.
    • Special thanks to Hari Limaye at Arm (@hazzlim on GitHub) for this ongoing vectorization work.

AddressSanitizer

  • Improved runtime performance by removing duplicated quarantine work
  • Use MSDIS (MSVC disassembler) as a fallback instruction-length decoder for interception, reducing “unrecognized instruction” failures. This is on by default and can be disabled via ASAN_OPTIONS=windows_use_msdis_decoder=false . To trace what instructions were intercepted by MSDIS instead of the manual instruction decoder, set verbosity=3 and search for logs prefixed with “MSDIS instruction decoder:”.
  • Runtime performance improvements including heap address caching, more efficient memory-mapped heap checks, and faster allocator flag assignments, resulting in significant performance gains across multiple runtime scenarios.

Build tools and diagnostics

  • The C++ frontend accepts more valid designated-initialization forms.
  • The C++ frontend accepts more deeply nested initializers, and the legacy preprocessor allows larger macro buffers.
  • Code Analysis SARIF output includes rule names, descriptions, and help links by default, plus tags where available.
  • LNK2022 and LNK4227 name the relevant metadata item. LNK4323 explains a missing ARM64EC/native object pair.
  • Incremental linking trims excess growth space from program-database files.
  • The disassembly library recognizes MIPS32 and MIPS64 Release 6 instructions and ARM64 matrix-state start/stop instructions.
  • /analyze:log:format:sarifandxml writes both Code Analysis log formats.
  • LNK4321 identifies duplicate C++ module exports while generating an import library.
  • C5089 identifies the experimental combination of /fsanitize=address with /clr, including /clr:netcore.
  • In C++, two new level-4 warnings diagnose implicit and explicit conversions between HRESULT/SCODE and bool-like types. They are off by default and do not apply to C.
  • The opt-in /PDB4GBSTREAMS option raises the size limit for the PDB global/public symbol-record stream to roughly 4 GiB. Other PDB streams retain their existing limits.
  • MASM and the disassembly library recognize the x86 int1/ICEBP instruction (0xF1).
  • LTCG emits C4744 when extern declarations disagree on type.
  • PE reproducibility improves because the image hash no longer includes the PDB age.
  • Tag-based Code Analysis warning groups simplify ruleset authoring.
  • Debugger clients can release a program-database file handle without discarding loaded data, allowing the file to be rebuilt sooner. Debug information also merges module and header-unit types more reliably.
  • Debug records can identify alternate object names, and type-record processing performs fewer allocations and improves throughput on large programs.
  • Linker and assembler diagnostics are clearer, and malformed object files, libraries, and module metadata are handled more defensively.

Community Tickets fixed

These public Developer Community tickets were fixed or completed during this development interval, including changes later serviced to MSVC 14.51.

Share your feedback

Thank you for the reports and reproductions that helped us investigate these issues. Please report problems on Developer Community , including a small reproducer, the compiler options, and the target architecture.

Category

Author

Eric Brumer
Software Engineering Manager

Software engineering manager on the Visual C++ compiler back-end team. I lead the machine-independent optimization team, as well as code generation security, including sanitizers.

4 comments

Sort by :
  • Antanas Uršulis

    Hi! Would you be able to confirm if the following issue will be fixed?
    ATL COM_INTERFACE_ENTRY_NOINTERFACE triggers C2440 converting nullptr to DWORD_PTR
    It’s related to the “ATL/MFC uses invalid nullptr reinterpret_cast (14.52 preview)” one that’s mention in the long list in this post, but this is different. I think it would be a shame if 14.52 shipped with broken ATL sources.

    • Stephan T. Lavavej - MSFTMicrosoft employee

      Yes, I’ve already merged the fix, it just didn’t get into the Release Candidate. As I just posted in that DevCom bug:

      The MSVC Compiler 19.52.37025 RC was built on 2026-09-26, while my backported fix MSVC-PR-788954 “[14.52] Fix more ATL nullptr compiler error regressions” was merged to the rel/14.52 release branch on 2026-10-06. So it’ll be in the General Availability release.

    • Stephan T. Lavavej - MSFTMicrosoft employee

      That’s on me to review – I will try to get to it soon. It’s unlikely that it’ll meet the bar for backporting to 14.52, though.