[packages/ldc] - rel 3, sync with git for llvm 22 compatibility
baggins
baggins at pld-linux.org
Sat Apr 18 22:39:25 CEST 2026
commit 0cb953558f29825500e177b8a703b59ff88ff4a5
Author: Jan Rękorajski <baggins at pld-linux.org>
Date: Sun Apr 19 00:38:49 2026 +0200
- rel 3, sync with git for llvm 22 compatibility
git.patch | 10578 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ldc.spec | 6 +-
2 files changed, 10582 insertions(+), 2 deletions(-)
---
diff --git a/ldc.spec b/ldc.spec
index 3c63c10..9b1bc61 100644
--- a/ldc.spec
+++ b/ldc.spec
@@ -6,7 +6,7 @@
%bcond_without geany # geany autocompletion support
%bcond_with jit # dynamic compilation support (JIT) (LLVM 18/19 only)
-%define rel 2
+%define rel 3
%define bootstrap_version 1.42.0
Summary: LLVM D Compiler
@@ -26,6 +26,7 @@ Source3: macros.%{name}
Patch0: %{name}-include-path.patch
Patch1: %{name}-no-default-rpath.patch
Patch2: %{name}-dmd.patch
+Patch3: git.patch
URL: https://github.com/ldc-developers/ldc
# for llvm < 16
#BuildRequires: SPIRV-LLVM-Translator-devel
@@ -40,7 +41,7 @@ BuildRequires: libconfig-devel
BuildRequires: libedit-devel
BuildRequires: libstdc++-devel
BuildRequires: llvm-devel >= 15.0
-BuildRequires: llvm-devel < 22
+BuildRequires: llvm-devel < 23
BuildRequires: rpm-build >= 4.6
BuildRequires: rpmbuild(macros) >= 2.047
BuildRequires: tar >= 1:1.22
@@ -127,6 +128,7 @@ Obsługa automatycznego dopełniania dla biblioteki Phobos w IDE geany.
%patch -P0 -p1
%patch -P1 -p1
%patch -P2 -p1
+%patch -P3 -p1
%if %{with geany}
# temp geany config directory for allow geany to generate tags
diff --git a/git.patch b/git.patch
new file mode 100644
index 0000000..5675698
--- /dev/null
+++ b/git.patch
@@ -0,0 +1,10578 @@
+diff --git a/CHANGELOG.md b/CHANGELOG.md
+index 647eb518f6..d9f19defa1 100644
+--- a/CHANGELOG.md
++++ b/CHANGELOG.md
+@@ -1,8 +1,13 @@
+ # LDC master
+
+ #### Big news
++- Support for [LLVM 22](https://releases.llvm.org/22.1.0/docs/ReleaseNotes.html). The prebuilt packages use v22.1.2. (#5097, #5102)
++- Minimum LLVM version raised to 18. (#5094)
++- DMD-style inline assembly: `asm { naked; }` is now much less of a special case wrt. codegen - such naked functions are now emitted as if `asm { naked; }` was replaced with the `@naked` function UDA. IR-wise, they are not emitted as module-level asm blobs anymore, but regular IR functions. This should lift a few former `asm { naked; }`-specific restrictions and fixed at least one LTO issue. (#5041)
++- Predefined version `LDC_LLVM_*` now only contains the LLVM major version, i.e., former `version (LDC_LLVM_1801)` with LLVM v18.1 is now `version (LDC_LLVM_18)`. Use `ldc.intrinsics.LLVM_version` for backwards compatibility if really needed. (#5109)
+
+ #### Platform support
++- Supports LLVM 18 - 22.
+
+ #### Bug fixes
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 81ca3effa7..83584dcae8 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -29,14 +29,13 @@ include(BuildDExecutable)
+ # Locate LLVM.
+ #
+
+-find_package(LLVM 15.0 REQUIRED
++find_package(LLVM 18.0 REQUIRED
+ all-targets analysis asmparser asmprinter bitreader bitwriter codegen core
+ debuginfodwarf debuginfomsf debuginfopdb demangle
+ instcombine ipo instrumentation irreader libdriver linker lto mc
+ mcdisassembler mcparser objcarcopts object option profiledata scalaropts
+ selectiondag support tablegen target transformutils vectorize
+ windowsdriver windowsmanifest symbolize ${EXTRA_LLVM_MODULES})
+-math(EXPR LDC_LLVM_VER ${LLVM_VERSION_MAJOR}*100+${LLVM_VERSION_MINOR})
+ message(STATUS "Using LLVM Version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}")
+ # Remove LLVMTableGen library from list of libraries
+ string(REGEX MATCH "[^;]*LLVMTableGen[^;]*" LLVM_TABLEGEN_LIBRARY "${LLVM_LIBRARIES}")
+@@ -49,7 +48,7 @@ endforeach()
+
+ # Set MLIR support variables if it is found.
+ # FIXME: LLVM 14+ (`mlir::OwningModuleRef` replacement)
+-if(NOT LDC_WITH_MLIR STREQUAL "OFF" AND LDC_LLVM_VER LESS 1400)
++if(NOT LDC_WITH_MLIR STREQUAL "OFF" AND LLVM_VERSION_MAJOR LESS 14)
+ include(FindMLIR)
+ if(MLIR_FOUND)
+ message(STATUS "-- Building LDC with MLIR support (${MLIR_ROOT_DIR})")
+@@ -61,46 +60,6 @@ if(NOT LDC_WITH_MLIR STREQUAL "OFF" AND LDC_LLVM_VER LESS 1400)
+ endif()
+ endif()
+
+-# Check and adapt for LLVMSPIRVLib (Khronos SPIRV-LLVM-Translator)
+-set(LLVM_SPIRV_FOUND OFF)
+-if (LDC_LLVM_VER LESS 1600)
+-if(MSVC)
+- if(EXISTS "${LLVM_LIBRARY_DIRS}/LLVMSPIRVLib.lib")
+- set(LLVM_SPIRV_FOUND ON)
+- set(LLVM_LIBRARIES "${LLVM_LIBRARY_DIRS}/LLVMSPIRVLib.lib" ${LLVM_LIBRARIES})
+- endif()
+-else()
+- if((EXISTS "${LLVM_LIBRARY_DIRS}/libLLVMSPIRVLib.a") OR
+- (EXISTS "${LLVM_LIBRARY_DIRS}/libLLVMSPIRVLib.so") OR
+- (EXISTS "${LLVM_LIBRARY_DIRS}/libLLVMSPIRVLib.dylib"))
+- set(LLVM_SPIRV_FOUND ON)
+- set(LLVM_LIBRARIES -lLLVMSPIRVLib ${LLVM_LIBRARIES})
+- endif()
+-endif()
+-if(NOT LLVM_SPIRV_FOUND)
+- find_package(PkgConfig)
+- if(PkgConfig_FOUND)
+- if(MSVC)
+- # make pkg-config use -LC:\path\to\build\LLVMSPIRVLib.lib not -L-lLLVMSPIRVLib
+- set(PKG_CONFIG_EXECUTABLE "${PKG_CONFIG_EXECUTABLE} --msvc-syntax")
+- endif()
+- pkg_check_modules(LLVM_SPIRV LLVMSPIRVLib)
+- if(LLVM_SPIRV_FOUND)
+- set(LLVM_SPIRV_FOUND ON) # translate 1 to ON
+- include_directories(${LLVM_SPIRV_INCLUDE_DIRS})
+- else()
+- set(LLVM_SPIRV_FOUND OFF)
+- endif()
+- endif()
+-endif()
+-if(LLVM_SPIRV_FOUND)
+- message(STATUS "-- Building LDC with SPIR-V support")
+- add_definitions("-DLDC_LLVM_SUPPORTED_TARGET_SPIRV=1")
+-else()
+- message(STATUS "-- Building LDC without SPIR-V support: not found")
+-endif()
+-endif()
+-
+ #
+ # Get info about used Linux distribution.
+ #
+@@ -137,6 +96,9 @@ set(ALTERNATIVE_MALLOC_O "" CACHE STRING "If specified, adds ALTERNATIVE_M
+ # Most linux distributions have a policy of not bundling dependencies like zlib
+ set(PHOBOS_SYSTEM_ZLIB OFF CACHE BOOL "Use system zlib instead of Phobos' vendored version")
+
++# Force use of softfloat for compiler
++option(LDC_FORCE_SOFTFLOAT_REAL "Force LDC to be built with 80-bit real in software" OFF)
++
+ if(D_VERSION EQUAL 1)
+ message(FATAL_ERROR "D version 1 is no longer supported.
+ Please consider using D version 2 or checkout the 'd1' git branch for the last version supporting D version 1.")
+@@ -175,6 +137,7 @@ if(NOT MSVC_IDE)
+ endif()
+
+ if(MSVC)
++ set(LDC_FORCE_SOFTFLOAT_REAL ON)
+ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ message(STATUS "Let D host compiler output 64-bit object files")
+ append("-m64" DFLAGS_BASE)
+@@ -206,6 +169,11 @@ if(MSVC)
+ endforeach()
+ endif()
+
++if (LDC_FORCE_SOFTFLOAT_REAL)
++ append("-version=LDC_real_softfloat" DFLAGS_LDC)
++ append("-DLDC_real_softfloat" LDC_CXXFLAGS)
++endif()
++
+ # Use separate compiler flags for the frontend and for the LDC-specific parts,
+ # as enabling warnings on the DMD frontend only leads to a lot of clutter in
+ # the output (LLVM_CXXFLAGS sometimes already includes -Wall).
+@@ -435,10 +403,8 @@ include(HandleLTOPGOBuildOptions)
+ set(LDC_DYNAMIC_COMPILE "AUTO" CACHE STRING "Support dynamic compilation (ON|OFF). Enabled by default; not supported for LLVM < 18.")
+ option(LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES "Use custom LDC passes in jit" ON)
+ if(LDC_DYNAMIC_COMPILE STREQUAL "AUTO")
+- if(LDC_LLVM_VER LESS 1800)
+- set(LDC_DYNAMIC_COMPILE OFF)
+ # FIXME: dynamic-compile regressions with LLVM 20
+- elseif(LDC_LLVM_VER LESS 2000)
++ if(LLVM_VERSION_MAJOR LESS 20)
+ set(LDC_DYNAMIC_COMPILE ON)
+ else()
+ set(LDC_DYNAMIC_COMPILE OFF)
+@@ -475,13 +441,11 @@ append("-J${PROJECT_SOURCE_DIR}/dmd/res" DFLAGS_LDC)
+
+ append("-version=IN_LLVM" DFLAGS_LDC)
+ append("-DIN_LLVM" LDC_CXXFLAGS)
+-append("-DOPAQUE_VTBLS" LDC_CXXFLAGS)
+ # Predefine LDC_INSTALL_PREFIX as raw string literal, requiring shell + CMake escaping.
+ # E.g., for CMAKE_INSTALL_PREFIX=`C:\dir with space`:
+ # g++ "-DLDC_INSTALL_PREFIX=R\"(C:\dir with space)\"" ...
+ # => LDC_INSTALL_PREFIX defined as `R"(C:\dir with space)"`
+ append("\"-DLDC_INSTALL_PREFIX=R\\\"(${CMAKE_INSTALL_PREFIX})\\\"\"" LDC_CXXFLAGS)
+-append("-DLDC_LLVM_VER=${LDC_LLVM_VER}" LDC_CXXFLAGS)
+ append("\"-DLDC_LIBDIR_SUFFIX=R\\\"(${LIB_SUFFIX})\\\"\"" LDC_CXXFLAGS)
+ append("-DLDC_HOST_${D_COMPILER_ID}=1" LDC_CXXFLAGS)
+ append("-DLDC_HOST_FE_VER=${D_COMPILER_FE_VERSION}" LDC_CXXFLAGS)
+@@ -580,9 +544,6 @@ if(WIN32)
+ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ target_link_libraries(${LDC_LIB} dl)
+ endif()
+-if(LLVM_SPIRV_FOUND)
+- target_link_libraries(${LDC_LIB} ${LLVM_SPIRV_LIBRARIES} ${LLVM_SPIRV_LDFLAGS})
+-endif()
+
+ set(LDC_EXE_FULL ${PROJECT_BINARY_DIR}/bin/${LDC_EXE_NAME}${CMAKE_EXECUTABLE_SUFFIX})
+ set(LDMD_EXE_FULL ${PROJECT_BINARY_DIR}/bin/${LDMD_EXE_NAME}${CMAKE_EXECUTABLE_SUFFIX})
+@@ -601,19 +562,17 @@ if(MSVC)
+ endif()
+ endif()
+ if(LDC_WITH_LLD)
+- if(MSVC)
+- list(APPEND LDC_LINKERFLAG_LIST LLVMSymbolize.lib)
+- else()
+- set(LDC_LINKERFLAG_LIST -lLLVMSymbolize ${LDC_LINKERFLAG_LIST})
++ if(LLVM_VERSION_MAJOR GREATER 21)
++ if(MSVC)
++ list(APPEND LDC_LINKERFLAG_LIST LLVMDTLTO.lib)
++ else()
++ set(LDC_LINKERFLAG_LIST -lLLVMDTLTO ${LDC_LINKERFLAG_LIST})
++ endif()
+ endif()
+ if(MSVC)
+- list(APPEND LDC_LINKERFLAG_LIST lldMinGW.lib lldCOFF.lib lldELF.lib lldMachO.lib lldWasm.lib lldCommon.lib)
++ list(APPEND LDC_LINKERFLAG_LIST lldMinGW.lib lldCOFF.lib lldELF.lib lldMachO.lib lldWasm.lib lldCommon.lib LLVMSymbolize.lib)
+ else()
+- set(LDC_LINKERFLAG_LIST -llldMinGW -llldCOFF -llldELF -llldMachO -llldWasm -llldCommon ${LDC_LINKERFLAG_LIST})
+- endif()
+- if(APPLE)
+- # LLD 13.0.0 on Mac needs libxar
+- list(APPEND LDC_LINKERFLAG_LIST -lxar)
++ set(LDC_LINKERFLAG_LIST -llldMinGW -llldCOFF -llldELF -llldMachO -llldWasm -llldCommon -lLLVMSymbolize ${LDC_LINKERFLAG_LIST})
+ endif()
+ if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
+ # FreeBSD LLVM port links to zstd, but does not convey this information via CMake
+@@ -799,11 +758,7 @@ endif()
+ # another directory.
+ set(COMPILER_RT_BASE_DIR "${LLVM_LIBRARY_DIRS}/clang" CACHE PATH "Base path of compiler-rt libraries. If they in are /usr/lib/clang/17/lib/linux/libclang_rt* you should set this value to /usr/lib/clang")
+ set(COMPILER_RT_LIBDIR "${COMPILER_RT_BASE_DIR}")
+-if(LDC_LLVM_VER LESS 1600)
+- set(COMPILER_RT_LIBDIR "${COMPILER_RT_LIBDIR}/${LLVM_VERSION_BASE_STRING}")
+-else()
+- set(COMPILER_RT_LIBDIR "${COMPILER_RT_LIBDIR}/${LLVM_VERSION_MAJOR}")
+-endif()
++set(COMPILER_RT_LIBDIR "${COMPILER_RT_LIBDIR}/${LLVM_VERSION_MAJOR}")
+ set(COMPILER_RT_LIBDIR "${COMPILER_RT_LIBDIR}/lib")
+ if(APPLE)
+ set(COMPILER_RT_LIBDIR "${COMPILER_RT_LIBDIR}/darwin")
+@@ -881,7 +836,7 @@ if (LDC_INSTALL_LLVM_RUNTIME_LIBS)
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(compilerrt_arch_suffix "i386")
+ endif()
+- if(LDC_LLVM_VER LESS 2000)
++ if(LLVM_VERSION_MAJOR LESS 20)
+ copy_compilerrt_lib("clang_rt.asan-${compilerrt_arch_suffix}.lib" "ldc_rt.asan.lib" FALSE)
+ copy_compilerrt_lib("clang_rt.lsan-${compilerrt_arch_suffix}.lib" "ldc_rt.lsan.lib" FALSE)
+ else()
+@@ -956,12 +911,6 @@ add_subdirectory(tools)
+
+ set(switches)
+
+-# LLVM 16: Disable function specializations by default.
+-# They cause miscompiles of e.g. the frontend for some targets (macOS x86_64 and Windows x64).
+-if(LDC_LLVM_VER GREATER 1599 AND LDC_LLVM_VER LESS 1700)
+- list(APPEND switches "-func-specialization-size-threshold=1000000000")
+-endif()
+-
+ list(APPEND switches "-defaultlib=phobos2-ldc,druntime-ldc")
+
+ makeConfSection(NAME "30-compiler" SECTION "default"
+diff --git a/README.md b/README.md
+index 9d8b8e568b..5076bf12f7 100644
+--- a/README.md
++++ b/README.md
+@@ -75,7 +75,7 @@ libraries is available on the project wiki for
+ [Windows](https://wiki.dlang.org/Building_and_hacking_LDC_on_Windows_using_MSVC).
+
+ If you have a working C++/D build environment, CMake, and a recent LLVM
+-version (≥ 15) available, there should be no big surprises. Do not
++version (≥ 18) available, there should be no big surprises. Do not
+ forget to make sure the Phobos submodule is up to date:
+
+ $ cd ldc
+diff --git a/cmake/Modules/FindLLVM.cmake b/cmake/Modules/FindLLVM.cmake
+index 75fa1eee29..018011e8d0 100644
+--- a/cmake/Modules/FindLLVM.cmake
++++ b/cmake/Modules/FindLLVM.cmake
+@@ -36,9 +36,6 @@ set(llvm_config_names llvm-config-21.1 llvm-config211 llvm-config-21
+ llvm-config-20.1 llvm-config201 llvm-config-20
+ llvm-config-19.1 llvm-config191 llvm-config-19
+ llvm-config-18.1 llvm-config181 llvm-config-18
+- llvm-config-17.0 llvm-config170 llvm-config-17
+- llvm-config-16.0 llvm-config160 llvm-config-16
+- llvm-config-15.0 llvm-config150 llvm-config-15
+ llvm-config)
+ find_program(LLVM_CONFIG
+ NAMES ${llvm_config_names}
+@@ -51,14 +48,10 @@ if(APPLE)
+ NAMES ${llvm_config_names}
+ PATHS /opt/local/libexec/llvm-21/bin
+ /opt/local/libexec/llvm-20/bin /opt/local/libexec/llvm-19/bin
+- /opt/local/libexec/llvm-18/bin /opt/local/libexec/llvm-17/bin
+- /opt/local/libexec/llvm-16/bin /opt/local/libexec/llvm-15/bin
+- /opt/local/libexec/llvm/bin
++ /opt/local/libexec/llvm-18/bin /opt/local/libexec/llvm/bin
+ /usr/local/opt/llvm at 21/bin
+ /usr/local/opt/llvm at 20/bin /usr/local/opt/llvm at 19/bin
+- /usr/local/opt/llvm at 18/bin /usr/local/opt/llvm at 17/bin
+- /usr/local/opt/llvm at 16/bin /usr/local/opt/llvm at 15/bin
+- /usr/local/opt/llvm/bin
++ /usr/local/opt/llvm at 18/bin /usr/local/opt/llvm/bin
+ NO_DEFAULT_PATH)
+ endif()
+
+diff --git a/dmd/declaration.h b/dmd/declaration.h
+index 7533348374..5c4562aab9 100644
+--- a/dmd/declaration.h
++++ b/dmd/declaration.h
+@@ -581,6 +581,9 @@ public:
+ VarDeclaration *v_argptr; // '_argptr' variable
+ VarDeclarations *parameters; // Array of VarDeclaration's for parameters
+ DsymbolTable *labtab; // statement label symbol table
++#if IN_LLVM
++ Identifiers *asmLabels; // identifiers of labels defined in DMD-style inline assembly
++#endif
+ Dsymbol *overnext; // next in overload list
+ FuncDeclaration *overnext0; // next in overload list (only used during IFTI)
+ Loc endloc; // location of closing curly bracket
+diff --git a/dmd/func.d b/dmd/func.d
+index 6709e32ebb..e39e233eae 100644
+--- a/dmd/func.d
++++ b/dmd/func.d
+@@ -256,6 +256,10 @@ version (IN_LLVM)
+ VarDeclaration v_argptr; /// '_argptr' variable
+ VarDeclarations* parameters; /// Array of VarDeclaration's for parameters
+ DsymbolTable labtab; /// statement label symbol table
++version (IN_LLVM)
++{
++ Identifiers* asmLabels; /// identifiers of labels defined in DMD-style inline assembly
++}
+ Dsymbol overnext; /// next in overload list
+ FuncDeclaration overnext0; /// next in overload list (only used during IFTI)
+ Loc endloc; /// location of closing curly bracket
+diff --git a/dmd/globals.h b/dmd/globals.h
+index 9d4bf185a5..bad8e9c410 100644
+--- a/dmd/globals.h
++++ b/dmd/globals.h
+@@ -17,11 +17,7 @@
+ #include "compiler.h"
+
+ #if IN_LLVM
+-#if LDC_LLVM_VER < 1700
+-#include "llvm/ADT/Triple.h"
+-#else
+ #include "llvm/TargetParser/Triple.h"
+-#endif
+
+ enum OUTPUTFLAG
+ {
+diff --git a/dmd/root/ctfloat.d b/dmd/root/ctfloat.d
+index 2996d7cd60..2ca9e20527 100644
+--- a/dmd/root/ctfloat.d
++++ b/dmd/root/ctfloat.d
+@@ -47,7 +47,8 @@ extern (C++) struct CTFloat
+ version (GNU)
+ enum yl2x_supported = false;
+ else
+- enum yl2x_supported = __traits(compiles, core.math.yl2x(1.0L, 2.0L));
++ enum yl2x_supported = is(real_t == real) &&
++ __traits(compiles, core.math.yl2x(1.0L, 2.0L));
+ enum yl2xp1_supported = yl2x_supported;
+
+ static void yl2x(const real_t* x, const real_t* y, real_t* res) // IN_LLVM: impure because of log2
+diff --git a/dmd/root/longdouble.d b/dmd/root/longdouble.d
+index 0bbd27a39e..9da7f09e06 100644
+--- a/dmd/root/longdouble.d
++++ b/dmd/root/longdouble.d
+@@ -11,8 +11,14 @@
+
+ module dmd.root.longdouble;
+
+-version (CRuntime_Microsoft)
++version (LDC_real_softfloat)
+ {
++ version = EnableSoftfloat;
++ alias longdouble = longdouble_soft;
++}
++else version (CRuntime_Microsoft)
++{
++ version = EnableSoftfloat;
+ static if (real.sizeof > 8)
+ alias longdouble = real;
+ else
+@@ -23,7 +29,7 @@ else
+
+ // longdouble_soft needed when building the backend with
+ // Visual C or the frontend with LDC on Windows
+-version (CRuntime_Microsoft):
++version (EnableSoftfloat):
+ extern (C++):
+ nothrow:
+ @nogc:
+@@ -71,7 +77,7 @@ bool initFPU()
+ return true;
+ }
+
+-version(unittest) version(CRuntime_Microsoft)
++version(unittest)
+ extern(D) shared static this()
+ {
+ initFPU(); // otherwise not guaranteed to be run before pure unittest below
+diff --git a/dmd/root/longdouble.h b/dmd/root/longdouble.h
+index 150b9f55e1..251be1d0f3 100644
+--- a/dmd/root/longdouble.h
++++ b/dmd/root/longdouble.h
+@@ -11,7 +11,13 @@
+
+ #pragma once
+
+-#if !_MSC_VER // has native 10 byte doubles
++#if defined(LDC_real_softfloat)
++#define USE_LONGDOUBLE_SOFTFLOAT 1
++#elif defined(_MSC_VER)
++#define USE_LONGDOUBLE_SOFTFLOAT 1
++#endif
++
++#if !USE_LONGDOUBLE_SOFTFLOAT // has native 10 byte doubles
+ #include <stdio.h>
+ typedef long double longdouble;
+ typedef volatile long double volatile_longdouble;
+diff --git a/dmd/statementsem.d b/dmd/statementsem.d
+index 987979bb88..a5be77972f 100644
+--- a/dmd/statementsem.d
++++ b/dmd/statementsem.d
+@@ -3695,6 +3695,13 @@ version (IN_LLVM)
+ if (auto ls = s.isLabelStatement())
+ {
+ sc.func.searchLabel(ls.ident, ls.loc);
++
++version (IN_LLVM)
++{
++ if (!sc.func.asmLabels)
++ sc.func.asmLabels = new Identifiers();
++ sc.func.asmLabels.push(ls.ident);
++}
+ }
+ }
+ }
+diff --git a/driver/archiver.cpp b/driver/archiver.cpp
+index 14c91d810c..15725a947d 100644
+--- a/driver/archiver.cpp
++++ b/driver/archiver.cpp
+@@ -14,13 +14,8 @@
+ #include "driver/cl_options.h"
+ #include "driver/tool.h"
+ #include "gen/logger.h"
+-#if LDC_LLVM_VER < 1700
+-#include "llvm/ADT/Triple.h"
+-#include "llvm/Support/Host.h"
+-#else
+ #include "llvm/TargetParser/Host.h"
+ #include "llvm/TargetParser/Triple.h"
+-#endif
+ #include "llvm/Object/Archive.h"
+ #include "llvm/Object/ArchiveWriter.h"
+ #include "llvm/Object/MachO.h"
+@@ -45,11 +40,7 @@ namespace llvm_ar {
+ StringRef ArchiveName;
+ std::vector<const char *> Members;
+
+-#if LDC_LLVM_VER < 1800
+-bool Symtab = true;
+-#else
+ llvm::SymtabWritingMode Symtab = llvm::SymtabWritingMode::NormalSymtab;
+-#endif
+ bool Deterministic = true;
+ bool Thin = false;
+
+diff --git a/driver/args.cpp b/driver/args.cpp
+index 2485dab7dd..2ce4210a12 100644
+--- a/driver/args.cpp
++++ b/driver/args.cpp
+@@ -170,11 +170,7 @@ int executeAndWait(std::vector<const char *> fullArgs,
+ }
+
+ const std::vector<llvm::StringRef> argv = toRefsVector(fullArgs);
+-#if LDC_LLVM_VER < 1600
+- auto envVars = llvm::None;
+-#else
+ auto envVars = std::nullopt;
+-#endif
+
+ return llvm::sys::ExecuteAndWait(argv[0], argv, envVars, {}, 0, 0, errorMsg);
+ }
+diff --git a/driver/cache.cpp b/driver/cache.cpp
+index 9e9278847d..2556ef2422 100644
+--- a/driver/cache.cpp
++++ b/driver/cache.cpp
+@@ -309,30 +309,14 @@ void outputIR2ObjRelevantCmdlineArgs(llvm::raw_ostream &hash_os) {
+ hash_os << opts::getFeaturesStr();
+ hash_os << opts::floatABI;
+ const auto relocModel = opts::getRelocModel();
+-#if LDC_LLVM_VER >= 1600
+ if (relocModel.has_value())
+ hash_os << relocModel.value();
+-#else
+- if (relocModel.hasValue())
+- hash_os << relocModel.getValue();
+-#endif
+ const auto codeModel = opts::getCodeModel();
+-#if LDC_LLVM_VER >= 1600
+ if (codeModel.has_value())
+ hash_os << codeModel.value();
+-#else
+- if (codeModel.hasValue())
+- hash_os << codeModel.getValue();
+-#endif
+-
+ const auto framePointerUsage = opts::framePointerUsage();
+-#if LDC_LLVM_VER >= 1600
+ if (framePointerUsage.has_value())
+ hash_os << static_cast<int>(framePointerUsage.value());
+-#else
+- if (framePointerUsage.hasValue())
+- hash_os << static_cast<int>(framePointerUsage.getValue());
+-#endif
+ }
+
+ // Output to `hash_os` all environment flags that influence object code output
+diff --git a/driver/cl_options-llvm.cpp b/driver/cl_options-llvm.cpp
+index 036e0bfa4f..168fab6adb 100644
+--- a/driver/cl_options-llvm.cpp
++++ b/driver/cl_options-llvm.cpp
+@@ -10,12 +10,8 @@
+ #include "driver/cl_options-llvm.h"
+
+ #if LDC_WITH_LLD
+-#if LDC_LLVM_VER < 1700
+-#include "llvm/ADT/Triple.h"
+-#else
+ #include "llvm/TargetParser/Triple.h"
+ #endif
+-#endif
+
+ // Pull in command-line options and helper functions from special LLVM header
+ // shared by multiple LLVM tools.
+@@ -35,17 +31,17 @@ std::string getArchStr() {
+ return codegen::getMArch();
+ }
+
+-Optional<Reloc::Model> getRelocModel() {
++std::optional<Reloc::Model> getRelocModel() {
+ return codegen::getExplicitRelocModel();
+ }
+
+-Optional<CodeModel::Model> getCodeModel() {
++std::optional<CodeModel::Model> getCodeModel() {
+ return codegen::getExplicitCodeModel();
+ }
+
+ using FPK = llvm::FramePointerKind;
+
+-llvm::Optional<FPK> framePointerUsage() {
++std::optional<FPK> framePointerUsage() {
+ // Defaults to `FP::None`; no way to check if set explicitly by user except
+ // indirectly via setFunctionAttributes()...
+ return codegen::getFramePointerUsage();
+@@ -89,11 +85,11 @@ TargetOptions initTargetOptionsFromCodeGenFlags() {
+ return ::opts::InitTargetOptionsFromCodeGenFlags(llvm::Triple());
+ }
+
+-Optional<Reloc::Model> getRelocModelFromCMModel() {
++std::optional<Reloc::Model> getRelocModelFromCMModel() {
+ return ::opts::getRelocModel();
+ }
+
+-Optional<CodeModel::Model> getCodeModelFromCMModel() {
++std::optional<CodeModel::Model> getCodeModelFromCMModel() {
+ return ::opts::getCodeModel();
+ }
+
+diff --git a/driver/cl_options-llvm.h b/driver/cl_options-llvm.h
+index 2f39b2401a..69b54359dc 100644
+--- a/driver/cl_options-llvm.h
++++ b/driver/cl_options-llvm.h
+@@ -9,17 +9,10 @@
+
+ #pragma once
+
+-#if LDC_LLVM_VER < 1700
+-#include "llvm/ADT/Optional.h"
+-#else
+-#include <optional>
+-namespace llvm {
+-template <typename T> using Optional = std::optional<T>;
+-}
+-#endif
+ #include "llvm/Support/CommandLine.h"
+ #include "llvm/Support/CodeGen.h"
+ #include "llvm/Target/TargetOptions.h"
++#include <optional>
+
+ namespace llvm {
+ class Function;
+@@ -29,9 +22,9 @@ class Triple;
+ namespace opts {
+
+ std::string getArchStr();
+-llvm::Optional<llvm::Reloc::Model> getRelocModel();
+-llvm::Optional<llvm::CodeModel::Model> getCodeModel();
+-llvm::Optional<llvm::FramePointerKind> framePointerUsage();
++std::optional<llvm::Reloc::Model> getRelocModel();
++std::optional<llvm::CodeModel::Model> getCodeModel();
++std::optional<llvm::FramePointerKind> framePointerUsage();
+
+ bool disableRedZone();
+ bool printTargetFeaturesHelp();
+diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp
+index 5b155a6797..c101cf199c 100644
+--- a/driver/cl_options.cpp
++++ b/driver/cl_options.cpp
+@@ -20,6 +20,12 @@ namespace opts {
+ // This vector is filled by parseCommandLine in main.cpp.
+ llvm::SmallVector<const char *, 32> allArguments;
+
++cl::opt<bool> noBuiltIn(
++ "fno-builtin",
++ cl::desc("Disable implicit builtin knowledge of functions"),
++ cl::ZeroOrMore
++);
++
+ cl::OptionCategory linkingCategory("Linking options");
+
+ // Positional options first, in order:
+@@ -577,7 +583,7 @@ llvm::FastMathFlags defaultFMF;
+ void setDefaultMathOptions(llvm::TargetOptions &targetOptions) {
+ if (fFastMath) {
+ defaultFMF.setFast();
+-#if LDC_LLVM_VER < 2200
++#if LLVM_VERSION_MAJOR < 22
+ targetOptions.UnsafeFPMath = true;
+ #endif
+ }
+@@ -788,12 +794,15 @@ static cl::extrahelp
+ /// The clashing LLVM options are suffixed with "llvm-" and hidden from the
+ /// -help output.
+ void createClashingOptions() {
+- llvm::StringMap<cl::Option *> &map = cl::getRegisteredOptions();
+-
++ auto &map = cl::getRegisteredOptions();
+ auto renameAndHide = [&map](const char *from, const char *to) {
+ auto i = map.find(from);
+ if (i != map.end()) {
++#if LLVM_VERSION_MAJOR >= 22
++ cl::Option *opt = i->second;
++#else
+ cl::Option *opt = i->getValue();
++#endif
+ map.erase(i);
+ if (to) {
+ opt->setArgStr(to);
+@@ -847,7 +856,8 @@ void hideLLVMOptions() {
+ "amdgpu-enable-global-sgpr-addr", "amdgpu-enable-merge-m0",
+ "amdgpu-enable-power-sched", "amdgpu-igrouplp",
+ "amdgpu-indirect-call-specialization-threshold",
+- "amdgpu-kernarg-preload-count", "amdgpu-module-splitting-max-depth",
++ "amdgpu-kernarg-preload", "amdgpu-kernarg-preload-count",
++ "amdgpu-module-splitting-max-depth",
+ "amdgpu-promote-alloca-to-vector-limit",
+ "amdgpu-promote-alloca-to-vector-max-regs",
+ "amdgpu-promote-alloca-to-vector-vgpr-ratio",
+@@ -857,9 +867,11 @@ void hideLLVMOptions() {
+ "argext-abi-check", "arm-add-build-attributes",
+ "arm-implicit-it", "asm-instrumentation", "asm-show-inst",
+ "atomic-counter-update-promoted", "atomic-first-counter",
+- "basic-block-address-map", "basic-block-sections",
++ "basic-block-address-map", "basic-block-section-match-infer",
++ "basic-block-sections",
+ "basicblock-sections", "bounds-checking-single-trap",
+- "bounds-checking-unique-traps", "bpf-stack-size", "cfg-hide-cold-paths",
++ "bounds-checking-unique-traps", "bpf-stack-size",
++ "call-graph-section", "cfg-hide-cold-paths",
+ "cfg-hide-deoptimize-paths", "cfg-hide-unreachable-paths",
+ "check-functions-filter", "code-model", "conditional-counter-update",
+ "cost-kind", "cppfname", "cppfor", "cppgen", "crel",
+@@ -867,18 +879,21 @@ void hideLLVMOptions() {
+ "cvp-dont-process-adds", "debug-counter", "debug-entry-values",
+ "debugger-tune", "debugify-atoms", "debugify-func-limit",
+ "debugify-level", "debugify-quiet", "debug-info-correlate",
+- "denormal-fp-math", "denormal-fp-math-f32",
++ "denormal-fp-math", "denormal-fp-math-f32", "devirtualize-speculatively",
+ "disable-auto-upgrade-debug-info", "disable-debug-info-verifier",
+ "disable-i2p-p2i-opt",
+ "disable-objc-arc-checkforcfghazards", "disable-promote-alloca-to-lds",
+- "disable-promote-alloca-to-vector", "disable-slp-vectorization",
++ "disable-promote-alloca-to-vector", "disable-qfp-opt",
++ "disable-qfp-opt-mul", "disable-slp-vectorization",
+ "disable-spill-fusing",
+- "do-counter-promotion", "dot-cfg-mssa", "dwarf64", "emit-call-site-info",
++ "do-counter-promotion", "dot-cfg-mssa", "dwarf64",
++ "elide-all-zero-branch-weights", "emit-bb-hash", "emit-call-site-info",
+ "emit-compact-unwind-non-canonical", "emit-dwarf-unwind",
+ "emit-gnuas-syntax-on-zos", "emscripten-cxx-exceptions-allowed",
+ "emscripten-cxx-exceptions-whitelist",
+ "emulated-tls", "enable-approx-func-fp-math", "enable-correct-eh-support",
+ "enable-cse-in-irtranslator", "enable-cse-in-legalizer",
++ "enable-devirtualize-speculatively",
+ "enable-emscripten-cxx-exceptions", "enable-emscripten-sjlj",
+ "enable-fp-mad", "enable-gvn-hoist", "enable-gvn-memdep",
+ "enable-gvn-memoryssa",
+@@ -889,7 +904,8 @@ void hideLLVMOptions() {
+ "enable-no-nans-fp-math", "enable-no-signed-zeros-fp-math",
+ "enable-no-trapping-fp-math", "enable-objc-arc-annotations",
+ "enable-objc-arc-opts", "enable-pgo-force-function-attrs",
+- "enable-pie", "enable-scoped-noalias",
++ "enable-pie", "enable-poison-reuse-guard",
++ "enable-scoped-noalias",
+ "enable-split-backedge-in-load-pre", "enable-split-loopiv-heuristic",
+ "enable-tbaa", "enable-tlsdesc", "enable-unsafe-fp-math",
+ "enable-vtable-profile-use", "enable-vtable-value-profiling",
+@@ -901,8 +917,8 @@ void hideLLVMOptions() {
+ "force-dwarf-frame-section", "force-opaque-pointers",
+ "force-tail-folding-style",
+ "fs-profile-debug-bw-threshold", "fs-profile-debug-prob-diff-threshold",
+- "generate-merged-base-profiles",
+- "gpsize", "hash-based-counter-split", "hexagon-add-build-attributes",
++ "generate-merged-base-profiles", "gpsize", "gsframe",
++ "hash-based-counter-split", "hexagon-add-build-attributes",
+ "hexagon-rdf-limit", "hot-cold-split", "hwasan-percentile-cutoff-hot",
+ "hwasan-random-rate", "ignore-xcoff-visibility",
+ "imp-null-check-page-size", "imp-null-max-insts-to-consider",
+@@ -914,7 +930,8 @@ void hideLLVMOptions() {
+ "instcombine-negator-max-depth", "instcombine-unsafe-select-transform",
+ "instrprof-atomic-counter-update-all", "internalize-public-api-file",
+ "internalize-public-api-list", "intrinsic-cost-strategy",
+- "ir2vec-arg-weight", "ir2vec-opc-weight", "ir2vec-type-weight",
++ "ir2vec-arg-weight", "ir2vec-kind", "ir2vec-opc-weight",
++ "ir2vec-type-weight",
+ "ir2vec-vocab-path", "iterative-counter-promotion",
+ "join-liveintervals", "jump-table-type", "large-data-threshold",
+ "limit-float-precision", "lint-abort-on-error", "loongarch-use-aa",
+@@ -927,7 +944,10 @@ void hideLLVMOptions() {
+ "memop-size-large", "memop-size-range", "merror-missing-parenthesis",
+ "merror-noncontigious-register", "mfuture-regs", "mhvx",
+ "mips-compact-branches", "mips16-constant-islands", "mips16-hard-float",
+- "mir-strip-debugify-only", "misexpect-tolerance", "mlsm", "mno-compound",
++ "mir-strip-debugify-only",
++ "mir2vec-common-operand-weight", "mir2vec-kind", "mir2vec-opc-weight",
++ "mir2vec-reg-operand-weight", "mir2vec-vocab-path",
++ "misexpect-tolerance", "mlsm", "mno-compound",
+ "mno-fixup", "mno-ldc1-sdc1", "mno-pairing",
+ "ms-secure-hotpatch-functions-file", "ms-secure-hotpatch-functions-list",
+ "mwarn-missing-parenthesis",
+@@ -943,10 +963,15 @@ void hideLLVMOptions() {
+ "polly-dump-after", "polly-dump-after-file", "polly-dump-before",
+ "polly-dump-before-file", "pre-RA-sched", "print-after-all",
+ "print-before-all", "print-machineinstrs", "print-module-scope",
+- "print-pipeline-passes", "profile-correlate",
++ "print-pipeline-passes",
++ "profcheck-annotate-select", "profcheck-default-function-entry-count",
++ "profcheck-default-select-false-weight",
++ "profcheck-default-select-true-weight", "profcheck-weights-for-test",
++ "profile-correlate",
+ "profile-estimator-loop-weight", "profile-estimator-loop-weight",
+ "profile-file", "profile-info-file", "profile-verifier-noassert",
+- "promote-alloca-vector-loop-user-weight", "pseudo-probe-for-profiling",
++ "promote-alloca-vector-loop-user-weight", "propeller-infer-threshold",
++ "pseudo-probe-for-profiling",
+ "r600-ir-structurize", "rdf-dump", "rdf-limit", "recip", "regalloc",
+ "relax-elf-relocations", "remarks-section", "rewrite-map-file",
+ "riscv-add-build-attributes", "riscv-use-aa", "rng-seed",
+@@ -961,11 +986,12 @@ void hideLLVMOptions() {
+ "skip-ret-exit-block",
+ "speculative-counter-promotion-max-exiting",
+ "speculative-counter-promotion-to-loop", "spiller", "spirv-debug",
++ "spirv-emit-op-names",
+ "spirv-erase-cl-md", "spirv-ext", "spirv-lower-const-expr",
+ "spirv-mem2reg",
+ "spirv-no-deref-attr", "spirv-text", "spirv-verify-regularize-passes",
+- "split-machine-functions", "spv-dump-deps",
+- "spv-emit-nonsemantic-debug-info",
++ "split-machine-functions", "spv-allow-unknown-intrinsics",
++ "spv-dump-deps", "spv-emit-nonsemantic-debug-info",
+ "spv-lower-saddwithoverflow-validate", "spvbool-validate",
+ "spvmemmove-validate", "stack-alignment", "stack-protector-guard",
+ "stack-protector-guard-offset", "stack-protector-guard-reg",
+@@ -1016,7 +1042,8 @@ void hideLLVMOptions() {
+ "trap-func",
+ "W"};
+
+- llvm::StringMap<cl::Option *> &map = cl::getRegisteredOptions();
++ auto &map = cl::getRegisteredOptions();
++
+ for (const auto name : hiddenOptions) {
+ // Check if option exists first for resilience against LLVM changes
+ // between versions.
+diff --git a/driver/cl_options.h b/driver/cl_options.h
+index 964874bcce..481b83adcc 100644
+--- a/driver/cl_options.h
++++ b/driver/cl_options.h
+@@ -41,6 +41,7 @@ extern cl::OptionCategory linkingCategory;
+ egrep -e '^(cl::|#if|#e)' gen/cl_options.cpp \
+ | sed -re 's/^(cl::.*)\(.*$/ extern \1;/'
+ */
++extern cl::opt<bool> noBuiltIn;
+ extern cl::list<std::string> fileList;
+ extern cl::list<std::string> runargs;
+ extern cl::opt<bool> invokedByLDMD;
+diff --git a/driver/cl_options_instrumentation.cpp b/driver/cl_options_instrumentation.cpp
+index 325f71383d..c8c0a07a77 100644
+--- a/driver/cl_options_instrumentation.cpp
++++ b/driver/cl_options_instrumentation.cpp
+@@ -18,11 +18,7 @@
+ #include "dmd/errors.h"
+ #include "dmd/globals.h"
+ #include "gen/to_string.h"
+-#if LDC_LLVM_VER < 1700
+-#include "llvm/ADT/Triple.h"
+-#else
+ #include "llvm/TargetParser/Triple.h"
+-#endif
+
+ namespace {
+ namespace cl = llvm::cl;
+diff --git a/driver/cl_options_sanitizers.h b/driver/cl_options_sanitizers.h
+index e1eaa3c934..423322e7fd 100644
+--- a/driver/cl_options_sanitizers.h
++++ b/driver/cl_options_sanitizers.h
+@@ -14,7 +14,7 @@
+ #pragma once
+
+ #include "driver/cl_helpers.h"
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ #include "llvm/Transforms/Utils/Instrumentation.h"
+ #else
+ #include "llvm/Transforms/Instrumentation.h"
+diff --git a/driver/codegenerator.cpp b/driver/codegenerator.cpp
+index 04fdeee4a6..6ec8044eb4 100644
+--- a/driver/codegenerator.cpp
++++ b/driver/codegenerator.cpp
+@@ -37,7 +37,7 @@
+ #include "mlir/IR/MLIRContext.h"
+ #endif
+
+-#if LDC_LLVM_VER < 2200
++#if LLVM_VERSION_MAJOR < 22
+ namespace llvm {
+ using LLVMRemarkFileHandle = std::unique_ptr<llvm::ToolOutputFile>;
+ }
+@@ -218,7 +218,7 @@ void CodeGenerator::prepareLLModule(Module *m) {
+ // name, as it should not collide with a symbol name used somewhere in the
+ // module.
+ ir_ = new IRState(m->srcfile.toChars(), context_);
+-#if LDC_LLVM_VER >= 2100
++#if LLVM_VERSION_MAJOR >= 21
+ ir_->module.setTargetTriple(*global.params.targetTriple);
+ #else
+ ir_->module.setTargetTriple(global.params.targetTriple->str());
+diff --git a/driver/cpreprocessor.cpp b/driver/cpreprocessor.cpp
+index fff0e5d223..d4b579d99e 100644
+--- a/driver/cpreprocessor.cpp
++++ b/driver/cpreprocessor.cpp
+@@ -99,7 +99,6 @@ FileName runCPreprocessor(FileName csrcfile, Loc loc, OutBuffer &defines) {
+ // propagate the target to the preprocessor
+ args.push_back("--target=" + triple.getTriple());
+
+-#if LDC_LLVM_VER >= 1800 // getAllProcessorFeatures was introduced in this version
+ // propagate all enabled/disabled features to the preprocessor
+ const auto &subTarget = gTargetMachine->getMCSubtargetInfo();
+ const auto &featureBits = subTarget->getFeatureBits();
+@@ -114,7 +113,6 @@ FileName runCPreprocessor(FileName csrcfile, Loc loc, OutBuffer &defines) {
+ args.push_back(featureString.str().str());
+ featureString.clear();
+ }
+-#endif
+
+ // print macro definitions (clang-cl doesn't support /PD - use clang's
+ // -dD)
+diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp
+index d3ad82b346..93cb97a492 100644
+--- a/driver/linker-gcc.cpp
++++ b/driver/linker-gcc.cpp
+@@ -27,14 +27,12 @@
+
+ #if LDC_WITH_LLD
+ #include "lld/Common/Driver.h"
+-#if LDC_LLVM_VER >= 1700
+ LLD_HAS_DRIVER(coff)
+ LLD_HAS_DRIVER(elf)
+ LLD_HAS_DRIVER(mingw)
+ LLD_HAS_DRIVER(macho)
+ LLD_HAS_DRIVER(wasm)
+ #endif
+-#endif
+
+ //////////////////////////////////////////////////////////////////////////////
+
+@@ -161,11 +159,6 @@ void ArgsBuilder::addLTOGoldPluginFlags(bool requirePlugin) {
+ addLdFlag("-plugin-opt=-function-sections");
+ if (TO.DataSections)
+ addLdFlag("-plugin-opt=-data-sections");
+-
+-#if LDC_LLVM_VER >= 1600 && LDC_LLVM_VER < 1700
+- // LLVM 16: disable function specializations by default
+- addLdFlag("-plugin-opt=-func-specialization-size-threshold=1000000000");
+-#endif
+ }
+
+ // Returns an empty string when libLTO.dylib was not specified nor found.
+@@ -196,11 +189,6 @@ void ArgsBuilder::addDarwinLTOFlags() {
+ std::string dylibPath = getLTOdylibPath();
+ if (!dylibPath.empty()) {
+ addLdFlag("-lto_library", dylibPath);
+-
+-#if LDC_LLVM_VER >= 1600 && LDC_LLVM_VER < 1700
+- // LLVM 16: disable function specializations by default
+- addLdFlag("-mllvm", "-func-specialization-size-threshold=1000000000");
+-#endif
+ }
+ }
+
+@@ -624,20 +612,11 @@ void ArgsBuilder::build(llvm::StringRef outputPath,
+ }
+
+ const auto explicitPlatformLibs = getExplicitPlatformLibs();
+-#if LDC_LLVM_VER >= 1600
+ if (explicitPlatformLibs.has_value()) {
+ for (const auto &name : explicitPlatformLibs.value()) {
+ args.push_back("-l" + name);
+ }
+- }
+-#else
+- if (explicitPlatformLibs.hasValue()) {
+- for (const auto &name : explicitPlatformLibs.getValue()) {
+- args.push_back("-l" + name);
+- }
+- }
+-#endif
+- else {
++ } else {
+ addDefaultPlatformLibs();
+ }
+
+@@ -656,10 +635,6 @@ void ArgsBuilder::addLinker() {
+ //////////////////////////////////////////////////////////////////////////////
+
+ void ArgsBuilder::addUserSwitches() {
+-#if LDC_LLVM_VER >= 1800
+- #define startswith starts_with
+-#endif
+-
+ // additional linker and cc switches (preserve order across both lists)
+ for (unsigned ilink = 0, icc = 0;;) {
+ unsigned linkpos = ilink < opts::linkerSwitches.size()
+@@ -676,9 +651,9 @@ void ArgsBuilder::addUserSwitches() {
+ // Options starting with `-Wl,`, -shared or -static are not handled by
+ // the linker and must be passed to the driver.
+ auto str = llvm::StringRef(p);
+- if (!(str.startswith("-l") || str.startswith("-L") ||
+- str.startswith("-Wl,") || str.startswith("-shared") ||
+- str.startswith("-static"))) {
++ if (!(str.starts_with("-l") || str.starts_with("-L") ||
++ str.starts_with("-Wl,") || str.starts_with("-shared") ||
++ str.starts_with("-static"))) {
+ args.push_back("-Xlinker");
+ }
+ args.push_back(p);
+@@ -688,10 +663,6 @@ void ArgsBuilder::addUserSwitches() {
+ break;
+ }
+ }
+-
+-#if LDC_LLVM_VER >= 1800
+- #undef startswith
+-#endif
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+diff --git a/driver/linker-msvc.cpp b/driver/linker-msvc.cpp
+index ad86ebabdf..931ccc3bb5 100644
+--- a/driver/linker-msvc.cpp
++++ b/driver/linker-msvc.cpp
+@@ -22,10 +22,8 @@
+
+ #if LDC_WITH_LLD
+ #include "lld/Common/Driver.h"
+-#if LDC_LLVM_VER >= 1700
+ LLD_HAS_DRIVER(coff)
+ #endif
+-#endif
+
+ //////////////////////////////////////////////////////////////////////////////
+
+@@ -39,22 +37,15 @@ void addMscrtLibs(bool useInternalToolchain, std::vector<std::string> &args) {
+ // We need the vcruntime lib for druntime's exception handling (ldc.eh_msvc).
+ // Pick one of the 4 variants matching the selected main UCRT lib.
+
+-#if LDC_LLVM_VER >= 1700
+-#define contains_lower contains_insensitive
+-#define endswith_lower ends_with_insensitive
+-#else
+-#define contains_lower contains_insensitive
+-#define endswith_lower endswith_insensitive
+-#endif
+ if (useInternalToolchain) {
+- assert(mscrtlibName.contains_lower("vcruntime"));
++ assert(mscrtlibName.contains_insensitive("vcruntime"));
+ return;
+ }
+
+- const bool isStatic = mscrtlibName.contains_lower("libcmt");
++ const bool isStatic = mscrtlibName.contains_insensitive("libcmt");
+
+- const bool isDebug =
+- mscrtlibName.endswith_lower("d") || mscrtlibName.endswith_lower("d.lib");
++ const bool isDebug = mscrtlibName.ends_with_insensitive("d") ||
++ mscrtlibName.ends_with_insensitive("d.lib");
+
+ const llvm::StringRef prefix = isStatic ? "lib" : "";
+ const llvm::StringRef suffix = isDebug ? "d" : "";
+@@ -77,9 +68,9 @@ void addSanitizerLibs(bool useInternalToolchain,
+ std::vector<std::string> &args) {
+ if (opts::isSanitizerEnabled(opts::AddressSanitizer)) {
+ args.push_back("ldc_rt.asan.lib");
+-#if LDC_LLVM_VER >= 2000 // extra library since LLVM 20
++#if LLVM_VERSION_MAJOR >= 20 // extra library since LLVM 20
+ const bool linkStaticCRT =
+- getMscrtLibName(&useInternalToolchain).contains_lower("libcmt");
++ getMscrtLibName(&useInternalToolchain).contains_insensitive("libcmt");
+ args.push_back((llvm::Twine("ldc_rt.asan_") +
+ (linkStaticCRT ? "static" : "dynamic") +
+ "_runtime_thunk.lib")
+@@ -108,7 +99,8 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath,
+
+ const bool forceMSVC = env::has(L"LDC_VSDIR_FORCE");
+ const bool useInternalToolchain =
+- (!forceMSVC && getExplicitMscrtLibName().contains_lower("vcruntime")) ||
++ (!forceMSVC &&
++ getExplicitMscrtLibName().contains_insensitive("vcruntime")) ||
+ !msvcEnv.setup();
+
+ if (forceMSVC && useInternalToolchain) {
+@@ -243,22 +235,12 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath,
+ }
+
+ auto explicitPlatformLibs = getExplicitPlatformLibs();
+-#if LDC_LLVM_VER >= 1600
+ if (explicitPlatformLibs.has_value()) {
+ for (auto &lib : explicitPlatformLibs.value()) {
+ args.push_back(llvm::sys::path::has_extension(lib) ? std::move(lib)
+ : lib + ".lib");
+ }
+- }
+-#else
+- if (explicitPlatformLibs.hasValue()) {
+- for (auto &lib : explicitPlatformLibs.getValue()) {
+- args.push_back(llvm::sys::path::has_extension(lib) ? std::move(lib)
+- : lib + ".lib");
+- }
+- }
+-#endif
+- else {
++ } else {
+ // default platform libs
+ // TODO check which libaries are necessary
+ args.insert(args.end(),
+diff --git a/driver/linker.cpp b/driver/linker.cpp
+index 65e0a099e0..44495896ee 100644
+--- a/driver/linker.cpp
++++ b/driver/linker.cpp
+@@ -192,14 +192,10 @@ static std::vector<std::string> getDefaultLibNames() {
+
+ //////////////////////////////////////////////////////////////////////////////
+
+-llvm::Optional<std::vector<std::string>> getExplicitPlatformLibs() {
++std::optional<std::vector<std::string>> getExplicitPlatformLibs() {
+ if (platformLib.getNumOccurrences() > 0)
+ return parseLibNames(platformLib);
+-#if LDC_LLVM_VER < 1600
+- return llvm::None;
+-#else
+ return std::nullopt;
+-#endif
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+diff --git a/driver/linker.h b/driver/linker.h
+index 28362d8652..4f584f8359 100644
+--- a/driver/linker.h
++++ b/driver/linker.h
+@@ -42,11 +42,7 @@ bool linkAgainstSharedDefaultLibs();
+ /**
+ * Returns the -platformlib library names, if specified.
+ */
+-#if LDC_LLVM_VER < 1600
+-llvm::Optional<std::vector<std::string>> getExplicitPlatformLibs();
+-#else
+ std::optional<std::vector<std::string>> getExplicitPlatformLibs();
+-#endif
+
+ /**
+ * Returns the value of -mscrtlib.
+diff --git a/driver/main.cpp b/driver/main.cpp
+index e1619dc0cb..df3637f55c 100644
+--- a/driver/main.cpp
++++ b/driver/main.cpp
+@@ -57,11 +57,7 @@
+ #include "llvm/LinkAllIR.h"
+ #include "llvm/LinkAllPasses.h"
+ #include "llvm/Support/FileSystem.h"
+-#if LDC_LLVM_VER >= 1700
+ #include "llvm/TargetParser/Host.h"
+-#else
+-#include "llvm/Support/Host.h"
+-#endif
+ #include "llvm/Support/ManagedStatic.h"
+ #include "llvm/Support/Path.h"
+ #include "llvm/Support/StringSaver.h"
+@@ -189,17 +185,13 @@ void tryParse(const llvm::SmallVectorImpl<const char *> &args, size_t i,
+ }
+
+ bool tryParseLowmem(const llvm::SmallVectorImpl<const char *> &args) {
+-#if LDC_LLVM_VER >= 1800
+- #define startswith starts_with
+-#endif
+-
+ bool lowmem = false;
+ for (size_t i = 1; i < args.size(); ++i) {
+ if (args::isRunArg(args[i]))
+ break;
+
+ llvm::StringRef arg = args[i];
+- if (arg.startswith("-lowmem") || arg.startswith("--lowmem")) {
++ if (arg.starts_with("-lowmem") || arg.starts_with("--lowmem")) {
+ auto remainder = arg.substr(arg[1] == '-' ? 8 : 7);
+ if (remainder.empty()) {
+ lowmem = true;
+@@ -210,10 +202,6 @@ bool tryParseLowmem(const llvm::SmallVectorImpl<const char *> &args) {
+ }
+ }
+ return lowmem;
+-
+-#if LDC_LLVM_VER >= 1800
+- #undef startswith
+-#endif
+ }
+
+ const char *
+@@ -322,7 +310,11 @@ void parseCommandLine(Strings &sourceFiles) {
+ if (auto target = lookupTarget("", triple, errMsg)) {
+ llvm::errs() << "Targeting " << target->getName() << ". ";
+ // this prints the available CPUs and features of the target to stderr...
++#if LLVM_VERSION_MAJOR >= 22
++ target->createMCSubtargetInfo(triple, "help", "");
++#else
+ target->createMCSubtargetInfo(cfg_triple, "help", "");
++#endif
+ } else {
+ error(Loc(), "%s", errMsg.c_str());
+ fatal();
+@@ -574,13 +566,6 @@ void parseCommandLine(Strings &sourceFiles) {
+
+ global.params.dihdr.fullOutput = opts::hdrKeepAllBodies;
+ global.params.disableRedZone = opts::disableRedZone();
+-
+- // enforce opaque IR pointers
+-#if LDC_LLVM_VER >= 1700
+- // supports opaque IR pointers only
+-#else
+- getGlobalContext().setOpaquePointers(true);
+-#endif
+ }
+
+ void initializePasses() {
+@@ -590,28 +575,13 @@ void initializePasses() {
+ initializeCore(Registry);
+ initializeTransformUtils(Registry);
+ initializeScalarOpts(Registry);
+-#if LDC_LLVM_VER < 1600
+- initializeObjCARCOpts(Registry);
+-#endif
+ initializeVectorization(Registry);
+ initializeInstCombine(Registry);
+-#if LDC_LLVM_VER < 1600
+- initializeAggressiveInstCombine(Registry);
+-#endif
+ initializeIPO(Registry);
+-#if LDC_LLVM_VER < 1600
+- initializeInstrumentation(Registry);
+-#endif
+ initializeAnalysis(Registry);
+ initializeCodeGen(Registry);
+ initializeGlobalISel(Registry);
+ initializeTarget(Registry);
+-
+-#if LDC_LLVM_VER < 1700
+-// Initialize passes not included above
+- initializeRewriteSymbolsLegacyPassPass(Registry);
+- initializeSjLjEHPreparePass(Registry);
+-#endif
+ }
+
+ /// Register the MIPS ABI.
+@@ -774,7 +744,6 @@ void registerPredefinedTargetVersions() {
+ case llvm::Triple::wasm64:
+ VersionCondition::addPredefinedGlobalIdent("WebAssembly");
+ break;
+-#if LDC_LLVM_VER >= 1600
+ case llvm::Triple::loongarch32:
+ VersionCondition::addPredefinedGlobalIdent("LoongArch32");
+ registerPredefinedFloatABI("LoongArch_SoftFloat", "LoongArch_HardFloat");
+@@ -786,7 +755,6 @@ void registerPredefinedTargetVersions() {
+ case llvm::Triple::xtensa:
+ VersionCondition::addPredefinedGlobalIdent("Xtensa");
+ break;
+-#endif // LDC_LLVM_VER >= 1600
+ default:
+ warning(Loc(), "unknown target CPU architecture: %s",
+ triple.getArchName().str().c_str());
+@@ -1064,7 +1032,7 @@ void registerPredefinedVersions() {
+ // Expose LLVM version to runtime
+ #define STR(x) #x
+ #define XSTR(x) STR(x)
+- VersionCondition::addPredefinedGlobalIdent("LDC_LLVM_" XSTR(LDC_LLVM_VER));
++ VersionCondition::addPredefinedGlobalIdent("LDC_LLVM_" XSTR(LLVM_VERSION_MAJOR));
+ #undef XSTR
+ #undef STR
+ }
+@@ -1181,15 +1149,9 @@ int cppmain() {
+ }
+
+ auto relocModel = getRelocModel();
+-#if LDC_LLVM_VER >= 1600
+ if (global.params.dll && !relocModel.has_value()) {
+ relocModel = llvm::Reloc::PIC_;
+ }
+-#else
+- if (global.params.dll && !relocModel.hasValue()) {
+- relocModel = llvm::Reloc::PIC_;
+- }
+-#endif
+
+ fixupTripleEnv(mTargetTriple);
+
+diff --git a/driver/plugins.cpp b/driver/plugins.cpp
+index 1356b4c5fd..b6baa8dfba 100644
+--- a/driver/plugins.cpp
++++ b/driver/plugins.cpp
+@@ -25,7 +25,11 @@
+ #include "llvm/Support/CommandLine.h"
+ #include "llvm/Support/DynamicLibrary.h"
+ #include "llvm/ADT/SmallVector.h"
++#if LLVM_VERSION_MAJOR >= 22
++#include "llvm/Plugins/PassPlugin.h"
++#else
+ #include "llvm/Passes/PassPlugin.h"
++#endif
+ #include "llvm/Support/Error.h"
+
+ #include "driver/cl_options.h"
+diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp
+index 0afc279bcc..6f85efd660 100644
+--- a/driver/targetmachine.cpp
++++ b/driver/targetmachine.cpp
+@@ -21,21 +21,12 @@
+ #include "gen/logger.h"
+ #include "llvm/ADT/StringExtras.h"
+ #include "llvm/ADT/StringSwitch.h"
+-#if LDC_LLVM_VER < 1700
+-#include "llvm/ADT/Triple.h"
+-#include "llvm/MC/SubtargetFeature.h"
+-#include "llvm/Support/Host.h"
+-#include "llvm/Support/TargetParser.h"
+-#include "llvm/Support/AArch64TargetParser.h"
+-#include "llvm/Support/ARMTargetParser.h"
+-#else
+ #include "llvm/TargetParser/AArch64TargetParser.h"
+ #include "llvm/TargetParser/ARMTargetParser.h"
+ #include "llvm/TargetParser/Host.h"
+ #include "llvm/TargetParser/SubtargetFeature.h"
+ #include "llvm/TargetParser/TargetParser.h"
+ #include "llvm/TargetParser/Triple.h"
+-#endif
+ #include "llvm/IR/Module.h"
+ #include "llvm/MC/MCObjectFileInfo.h"
+ #include "llvm/Support/CommandLine.h"
+@@ -46,10 +37,6 @@
+
+ #include "gen/optimizer.h"
+
+-#if LDC_LLVM_VER >= 1800
+-#define startswith starts_with
+-#endif
+-
+ #ifdef LDC_LLVM_SUPPORTS_MACHO_DWARF_LINE_AS_REGULAR_SECTION
+ // LDC-LLVM >= 20:
+ // On Darwin, emit __debug_line section in __DWARF segment as regular
+@@ -87,65 +74,63 @@ const char *getABI(const llvm::Triple &triple, const llvm::SmallVectorImpl<llvm:
+ switch (triple.getArch()) {
+ case llvm::Triple::arm:
+ case llvm::Triple::armeb:
+- if (ABIName.startswith("aapcs"))
++ if (ABIName.starts_with("aapcs"))
+ return "aapcs";
+- if (ABIName.startswith("eabi"))
++ if (ABIName.starts_with("eabi"))
+ return "apcs";
+ break;
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+- if (ABIName.startswith("o32"))
++ if (ABIName.starts_with("o32"))
+ return "o32";
+- if (ABIName.startswith("n32"))
++ if (ABIName.starts_with("n32"))
+ return "n32";
+- if (ABIName.startswith("n64"))
++ if (ABIName.starts_with("n64"))
+ return "n64";
+- if (ABIName.startswith("eabi"))
++ if (ABIName.starts_with("eabi"))
+ return "eabi";
+ break;
+ case llvm::Triple::ppc64:
+ case llvm::Triple::ppc64le:
+- if (ABIName.startswith("elfv1"))
++ if (ABIName.starts_with("elfv1"))
+ return "elfv1";
+- if (ABIName.startswith("elfv2"))
++ if (ABIName.starts_with("elfv2"))
+ return "elfv2";
+ break;
+ case llvm::Triple::riscv64:
+- if (ABIName.startswith("lp64f"))
++ if (ABIName.starts_with("lp64f"))
+ return "lp64f";
+- if (ABIName.startswith("lp64d"))
++ if (ABIName.starts_with("lp64d"))
+ return "lp64d";
+- if (ABIName.startswith("lp64"))
++ if (ABIName.starts_with("lp64"))
+ return "lp64";
+ break;
+ case llvm::Triple::riscv32:
+- if (ABIName.startswith("ilp32f"))
++ if (ABIName.starts_with("ilp32f"))
+ return "ilp32f";
+- if (ABIName.startswith("ilp32d"))
++ if (ABIName.starts_with("ilp32d"))
+ return "ilp32d";
+- if (ABIName.startswith("ilp32"))
++ if (ABIName.starts_with("ilp32"))
+ return "ilp32";
+ break;
+-#if LDC_LLVM_VER >= 1600
+ case llvm::Triple::loongarch32:
+- if (ABIName.startswith("ilp32s"))
++ if (ABIName.starts_with("ilp32s"))
+ return "ilp32s";
+- if (ABIName.startswith("ilp32f"))
++ if (ABIName.starts_with("ilp32f"))
+ return "ilp32f";
+- if (ABIName.startswith("ilp32d"))
++ if (ABIName.starts_with("ilp32d"))
+ return "ilp32d";
+ break;
+ case llvm::Triple::loongarch64:
+- if (ABIName.startswith("lp64f"))
++ if (ABIName.starts_with("lp64f"))
+ return "lp64f";
+- if (ABIName.startswith("lp64d"))
++ if (ABIName.starts_with("lp64d"))
+ return "lp64d";
+- if (ABIName.startswith("lp64s"))
++ if (ABIName.starts_with("lp64s"))
+ return "lp64s";
+ break;
+-#endif // LDC_LLVM_VER >= 1600
+ default:
+ break;
+ }
+@@ -169,7 +154,6 @@ const char *getABI(const llvm::Triple &triple, const llvm::SmallVectorImpl<llvm:
+ return "lp64";
+ case llvm::Triple::riscv32:
+ return "ilp32";
+-#if LDC_LLVM_VER >= 1600
+ case llvm::Triple::loongarch32:
+ if (isFeatureEnabled(features, "d"))
+ return "ilp32d";
+@@ -182,7 +166,6 @@ const char *getABI(const llvm::Triple &triple, const llvm::SmallVectorImpl<llvm:
+ if (isFeatureEnabled(features, "f"))
+ return "lp64f";
+ return "lp64d";
+-#endif // LDC_LLVM_VER >= 1600
+ default:
+ return "";
+ }
+@@ -222,22 +205,22 @@ static std::string getX86TargetCPU(const llvm::Triple &triple) {
+ if (triple.isArch64Bit()) {
+ return "x86-64";
+ }
+- if (triple.getOSName().startswith("haiku")) {
++ if (triple.getOSName().starts_with("haiku")) {
+ return "i586";
+ }
+- if (triple.getOSName().startswith("openbsd")) {
++ if (triple.getOSName().starts_with("openbsd")) {
+ return "i486";
+ }
+- if (triple.getOSName().startswith("freebsd")) {
++ if (triple.getOSName().starts_with("freebsd")) {
+ return "i486";
+ }
+- if (triple.getOSName().startswith("netbsd")) {
++ if (triple.getOSName().starts_with("netbsd")) {
+ return "i486";
+ }
+- if (triple.getOSName().startswith("openbsd")) {
++ if (triple.getOSName().starts_with("openbsd")) {
+ return "i486";
+ }
+- if (triple.getOSName().startswith("dragonfly")) {
++ if (triple.getOSName().starts_with("dragonfly")) {
+ return "i486";
+ }
+
+@@ -263,12 +246,6 @@ static std::string getARMTargetCPU(const llvm::Triple &triple) {
+ }
+
+ static std::string getAArch64TargetCPU(const llvm::Triple &triple) {
+-#if LDC_LLVM_VER < 1600
+- auto defaultCPU = llvm::AArch64::getDefaultCPU(triple.getArchName());
+- if (!defaultCPU.empty())
+- return std::string(defaultCPU);
+-#endif
+-
+ return "generic";
+ }
+
+@@ -309,18 +286,32 @@ static std::string getTargetCPU(const llvm::Triple &triple) {
+ return getRiscv32TargetCPU(triple);
+ case llvm::Triple::riscv64:
+ return getRiscv64TargetCPU(triple);
+-#if LDC_LLVM_VER >= 1600
+ case llvm::Triple::loongarch32:
+ return getLoongArch32TargetCPU(triple);
+ case llvm::Triple::loongarch64:
+ return getLoongArch64TargetCPU(triple);
+-#endif // LDC_LLVM_VER >= 1600
+ }
+ }
+
+ static const char *getLLVMArchSuffixForARM(llvm::StringRef CPU) {
+ return llvm::StringSwitch<const char *>(CPU)
+ .Case("strongarm", "v4")
++#if LLVM_VERSION_MAJOR >= 22
++ .Cases({"arm7tdmi", "arm7tdmi-s", "arm710t"}, "v4t")
++ .Cases({"arm720t", "arm9", "arm9tdmi"}, "v4t")
++ .Cases({"arm920", "arm920t", "arm922t"}, "v4t")
++ .Cases({"arm940t", "ep9312"}, "v4t")
++ .Cases({"arm10tdmi", "arm1020t"}, "v5")
++ .Cases({"arm9e", "arm926ej-s", "arm946e-s"}, "v5e")
++ .Cases({"arm966e-s", "arm968e-s", "arm10e"}, "v5e")
++ .Cases({"arm1020e", "arm1022e", "xscale", "iwmmxt"}, "v5e")
++ .Cases({"arm1136j-s", "arm1136jf-s", "arm1176jz-s"}, "v6")
++ .Cases({"arm1176jzf-s", "mpcorenovfp", "mpcore"}, "v6")
++ .Cases({"arm1156t2-s", "arm1156t2f-s"}, "v6t2")
++ .Cases({"cortex-a5", "cortex-a7", "cortex-a8"}, "v7")
++ .Cases({"cortex-a9", "cortex-a12", "cortex-a15"}, "v7")
++ .Cases({"cortex-r4", "cortex-r5"}, "v7r")
++#else
+ .Cases("arm7tdmi", "arm7tdmi-s", "arm710t", "v4t")
+ .Cases("arm720t", "arm9", "arm9tdmi", "v4t")
+ .Cases("arm920", "arm920t", "arm922t", "v4t")
+@@ -335,6 +326,7 @@ static const char *getLLVMArchSuffixForARM(llvm::StringRef CPU) {
+ .Cases("cortex-a5", "cortex-a7", "cortex-a8", "v7")
+ .Cases("cortex-a9", "cortex-a12", "cortex-a15", "v7")
+ .Cases("cortex-r4", "cortex-r5", "v7r")
++#endif
+ .Case("cortex-m0", "v6m")
+ .Case("cortex-m3", "v7m")
+ .Case("cortex-m4", "v7em")
+@@ -349,8 +341,8 @@ static FloatABI::Type getARMFloatABI(const llvm::Triple &triple,
+ const char *llvmArchSuffix) {
+ if (triple.isOSDarwin()) {
+ // Darwin defaults to "softfp" for v6 and v7.
+- if (llvm::StringRef(llvmArchSuffix).startswith("v6") ||
+- llvm::StringRef(llvmArchSuffix).startswith("v7")) {
++ if (llvm::StringRef(llvmArchSuffix).starts_with("v6") ||
++ llvm::StringRef(llvmArchSuffix).starts_with("v7")) {
+ return FloatABI::SoftFP;
+ }
+ return FloatABI::Soft;
+@@ -361,9 +353,9 @@ static FloatABI::Type getARMFloatABI(const llvm::Triple &triple,
+ return FloatABI::Soft;
+ }
+
+- if (triple.getVendorName().startswith("hardfloat"))
++ if (triple.getVendorName().starts_with("hardfloat"))
+ return FloatABI::Hard;
+- if (triple.getVendorName().startswith("softfloat"))
++ if (triple.getVendorName().starts_with("softfloat"))
+ return FloatABI::SoftFP;
+
+ switch (triple.getEnvironment()) {
+@@ -375,7 +367,7 @@ static FloatABI::Type getARMFloatABI(const llvm::Triple &triple,
+ // EABI is always AAPCS, and if it was not marked 'hard', it's softfp
+ return FloatABI::SoftFP;
+ case llvm::Triple::Android: {
+- if (llvm::StringRef(llvmArchSuffix).startswith("v7")) {
++ if (llvm::StringRef(llvmArchSuffix).starts_with("v7")) {
+ return FloatABI::SoftFP;
+ }
+ return FloatABI::Soft;
+@@ -425,7 +417,11 @@ const llvm::Target *lookupTarget(const std::string &arch, llvm::Triple &triple,
+ }
+ } else {
+ std::string tempError;
++#if LLVM_VERSION_MAJOR >= 22
++ target = llvm::TargetRegistry::lookupTarget(triple, tempError);
++#else
+ target = llvm::TargetRegistry::lookupTarget(triple.getTriple(), tempError);
++#endif
+ if (!target) {
+ errorMsg = "unable to get target for '" + triple.getTriple() +
+ "', see -version and -mtriple.";
+@@ -440,8 +436,8 @@ createTargetMachine(const std::string targetTriple, const std::string arch,
+ std::string cpu, const std::string featuresString,
+ const ExplicitBitness::Type bitness,
+ FloatABI::Type &floatABI,
+- llvm::Optional<llvm::Reloc::Model> relocModel,
+- llvm::Optional<llvm::CodeModel::Model> codeModel,
++ std::optional<llvm::Reloc::Model> relocModel,
++ std::optional<llvm::CodeModel::Model> codeModel,
+ const llvm::CodeGenOptLevel codeGenOptLevel,
+ const bool noLinkerStripDead) {
+ // Determine target triple. If the user didn't explicitly specify one, use
+@@ -543,19 +539,12 @@ createTargetMachine(const std::string targetTriple, const std::string arch,
+
+ // For LoongArch 64-bit target default to la64 if nothing has been selected
+ // All current LoongArch targets have 64-bit floating point registers.
+-#if LDC_LLVM_VER >= 1600
+ if (triple.getArch() == llvm::Triple::loongarch64 && features.empty()) {
+ features = {"+d"};
+ }
+-#endif
+
+ // Handle cases where LLVM picks wrong default relocModel
+-#if LDC_LLVM_VER >= 1600
+- if (relocModel.has_value()) {}
+-#else
+- if (relocModel.hasValue()) {}
+-#endif
+- else {
++ if (!relocModel.has_value()) {
+ if (triple.isOSDarwin()) {
+ // Darwin defaults to PIC (and as of 10.7.5/LLVM 3.1-3.3, TLS use leads
+ // to crashes for non-PIC code). LLVM doesn't handle this.
+@@ -636,11 +625,6 @@ createTargetMachine(const std::string targetTriple, const std::string arch,
+ // EmuTLS).
+ if (triple.getEnvironment() == llvm::Triple::Android) {
+ targetOptions.EmulatedTLS = false;
+-#if LDC_LLVM_VER < 1700
+- // Removed in this commit:
+- // https://github.com/llvm/llvm-project/commit/0d333bf0e3aa37e2e6ae211e3aa80631c3e01b85
+- targetOptions.ExplicitEmulatedTLS = true;
+-#endif
+ }
+
+ const std::string finalFeaturesString =
+@@ -653,7 +637,7 @@ createTargetMachine(const std::string targetTriple, const std::string arch,
+ }
+
+ return target->createTargetMachine(
+-#if LDC_LLVM_VER >= 2100
++#if LLVM_VERSION_MAJOR >= 21
+ triple,
+ #else
+ triple.str(),
+diff --git a/driver/targetmachine.h b/driver/targetmachine.h
+index 3bfa567e4f..e0c21ee140 100644
+--- a/driver/targetmachine.h
++++ b/driver/targetmachine.h
+@@ -14,24 +14,12 @@
+
+ #pragma once
+
+-#if LDC_LLVM_VER < 1700
+-#include "llvm/ADT/Optional.h"
+-#else
+-#include <optional>
+-namespace llvm {
+-template <typename T> using Optional = std::optional<T>;
+-}
+-#endif
+ #include "llvm/ADT/SmallVector.h"
+ #include "llvm/ADT/StringRef.h"
+ #include "llvm/Support/CodeGen.h"
++#include <optional>
+ #include <string>
+ #include <vector>
+-namespace llvm {
+-#if LDC_LLVM_VER < 1800
+-using CodeGenOptLevel = llvm::CodeGenOpt::Level;
+-#endif
+-}
+
+ namespace ExplicitBitness {
+ enum Type { None, M32, M64 };
+@@ -69,8 +57,8 @@ llvm::TargetMachine *
+ createTargetMachine(std::string targetTriple, std::string arch, std::string cpu,
+ std::string featuresString, ExplicitBitness::Type bitness,
+ FloatABI::Type &floatABI,
+- llvm::Optional<llvm::Reloc::Model> relocModel,
+- llvm::Optional<llvm::CodeModel::Model> codeModel,
++ std::optional<llvm::Reloc::Model> relocModel,
++ std::optional<llvm::CodeModel::Model> codeModel,
+ llvm::CodeGenOptLevel codeGenOptLevel,
+ bool noLinkerStripDead);
+
+diff --git a/driver/toobj.cpp b/driver/toobj.cpp
+index f9789452f4..211d8d88f4 100644
+--- a/driver/toobj.cpp
++++ b/driver/toobj.cpp
+@@ -37,34 +37,14 @@
+ #include "llvm/CodeGen/TargetSubtargetInfo.h"
+ #include "llvm/Transforms/Utils/Cloning.h"
+ #include "llvm/IR/Module.h"
+-#ifdef LDC_LLVM_SUPPORTED_TARGET_SPIRV
+-#if LDC_LLVM_VER < 1600
+-#include "LLVMSPIRVLib/LLVMSPIRVLib.h"
+-#endif
+-#endif
+ #include <cstddef>
+ #include <fstream>
+
+-using CodeGenFileType = llvm::CodeGenFileType;
+-
+-#if LDC_LLVM_VER >= 1800
+-constexpr llvm::CodeGenFileType CGFT_AssemblyFile = CodeGenFileType::AssemblyFile;
+-constexpr llvm::CodeGenFileType CGFT_ObjectFile = CodeGenFileType::ObjectFile;
+-#endif
+-
+-#if LDC_LLVM_VER < 1700
+-static llvm::cl::opt<bool>
+- NoIntegratedAssembler("no-integrated-as", llvm::cl::ZeroOrMore,
+- llvm::cl::Hidden,
+- llvm::cl::desc("Disable integrated assembler"));
+-#else
+ namespace llvm {
+ namespace codegen {
+ bool getDisableIntegratedAS();
+ }
+ }
+-#define NoIntegratedAssembler llvm::codegen::getDisableIntegratedAS()
+-#endif
+
+ namespace {
+
+@@ -81,21 +61,13 @@ void runDLLImportRelocationPass(llvm::TargetMachine &Target, llvm::Module &m) {
+ // based on llc code, University of Illinois Open Source License
+ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m,
+ const char *filename,
+- CodeGenFileType fileType) {
++ llvm::CodeGenFileType fileType) {
+ using namespace llvm;
+
+ const ComputeBackend::Type cb = getComputeTargetType(&m);
+
+ if (cb == ComputeBackend::SPIRV) {
+-#ifdef LDC_LLVM_SUPPORTED_TARGET_SPIRV
+-#if LDC_LLVM_VER < 1600
+- IF_LOG Logger::println("running createSPIRVWriterPass()");
+- std::ofstream out(filename, std::ofstream::binary);
+- llvm::createSPIRVWriterPass(out)->runOnModule(m);
+- IF_LOG Logger::println("Success.");
+- return;
+-#endif
+-#else
++#ifndef LDC_LLVM_SUPPORTED_TARGET_SPIRV
+ error(Loc(), "Trying to target SPIRV, but LDC is not built to do so!");
+ return;
+ #endif
+@@ -132,11 +104,8 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m,
+ nullptr, // DWO output file
+ // Always generate assembly for ptx as it is an assembly format
+ // The PTX backend fails if we pass anything else.
+- (cb == ComputeBackend::NVPTX) ? CGFT_AssemblyFile : fileType
+-#if LDC_LLVM_VER < 1700
+- , codeGenOptLevel()
+-#endif
+- )) {
++ (cb == ComputeBackend::NVPTX) ? CodeGenFileType::AssemblyFile
++ : fileType)) {
+ llvm_unreachable("no support for asm output");
+ }
+
+@@ -300,14 +269,14 @@ public:
+ void writeObjectFile(llvm::Module *m, const char *filename) {
+ IF_LOG Logger::println("Writing object file to: %s", filename);
+ codegenModule(*gTargetMachine, *m, filename,
+- CGFT_ObjectFile);
++ llvm::CodeGenFileType::ObjectFile);
+ }
+
+ bool shouldAssembleExternally() {
+ // There is no integrated assembler on AIX because XCOFF is not supported.
+ // Starting with LLVM 3.5 the integrated assembler can be used with MinGW.
+ return global.params.output_o &&
+- (NoIntegratedAssembler ||
++ (llvm::codegen::getDisableIntegratedAS() ||
+ global.params.targetTriple->getOS() == llvm::Triple::AIX);
+ }
+
+@@ -479,10 +448,10 @@ void writeModule(llvm::Module *m, const char *filename) {
+ // to avoid running 'addPassesToEmitFile' passes twice on same module
+ auto clonedModule = llvm::CloneModule(*m);
+ codegenModule(*gTargetMachine, *clonedModule, spath.c_str(),
+- CGFT_AssemblyFile);
++ llvm::CodeGenFileType::AssemblyFile);
+ } else {
+ codegenModule(*gTargetMachine, *m, spath.c_str(),
+- CGFT_AssemblyFile);
++ llvm::CodeGenFileType::AssemblyFile);
+ }
+
+ if (assembleExternally) {
+diff --git a/driver/tool.cpp b/driver/tool.cpp
+index e64ec96cbd..31ff31cf83 100644
+--- a/driver/tool.cpp
++++ b/driver/tool.cpp
+@@ -20,12 +20,7 @@
+ #include "llvm/Support/MemoryBuffer.h"
+ #include "llvm/Support/Path.h"
+ #include "llvm/Target/TargetMachine.h"
+-
+-#if LDC_LLVM_VER >= 1600
+ #include "llvm/TargetParser/Host.h"
+-#else
+-#include "llvm/Support/Host.h"
+-#endif
+
+ #ifdef _WIN32
+ #include <Windows.h>
+@@ -214,12 +209,10 @@ void appendTargetArgsForGcc(std::vector<std::string> &args) {
+ case Triple::nvptx64:
+ args.push_back(triple.isArch64Bit() ? "-m64" : "-m32");
+ return;
+-#if LDC_LLVM_VER >= 1600
+ // LoongArch does not use -m32/-m64 and uses -mabi=.
+ case Triple::loongarch64:
+ args.emplace_back(triple.isArch64Bit() ? "-mabi=lp64d" : "-mabi=ilp32d");
+ return;
+-#endif // LDC_LLVM_VER >= 1600
+ // MIPS does not have -m32/-m64 but requires -mabi=.
+ case Triple::mips64:
+ case Triple::mips64el:
+diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp
+index eab1ca5ca1..5e5a1baa20 100644
+--- a/gen/abi/abi.cpp
++++ b/gen/abi/abi.cpp
+@@ -158,11 +158,7 @@ llvm::CallingConv::ID TargetABI::callingConv(FuncDeclaration *fdecl) {
+ void TargetABI::setUnwindTableKind(llvm::Function *fn) {
+ llvm::UWTableKind kind = defaultUnwindTableKind();
+ if (kind != llvm::UWTableKind::None) {
+-#if LDC_LLVM_VER >= 1600
+ fn->setUWTableKind(kind);
+-#else
+- fn->setUWTableKind(kind);
+-#endif
+ }
+ }
+
+@@ -285,10 +281,8 @@ TargetABI *TargetABI::getTarget() {
+ case llvm::Triple::thumb:
+ case llvm::Triple::thumbeb:
+ return getArmTargetABI();
+-#if LDC_LLVM_VER >= 1600
+ case llvm::Triple::loongarch64:
+ return getLoongArch64TargetABI();
+-#endif // LDC_LLVM_VER >= 1600
+ case llvm::Triple::wasm32:
+ case llvm::Triple::wasm64:
+ return getWasmTargetABI();
+diff --git a/gen/abi/generic.h b/gen/abi/generic.h
+index 6458d28b8b..4def40c790 100644
+--- a/gen/abi/generic.h
++++ b/gen/abi/generic.h
+@@ -260,7 +260,7 @@ struct IndirectByvalRewrite : ABIRewrite {
+ auto &attrs = arg.attrs;
+ attrs.clear();
+ attrs.addAttribute(LLAttribute::NoAlias);
+-#if LDC_LLVM_VER >= 2100
++#if LLVM_VERSION_MAJOR >= 21
+ attrs.addCapturesAttr(llvm::CaptureInfo::none());
+ #else
+ attrs.addAttribute(LLAttribute::NoCapture);
+diff --git a/gen/abi/nvptx.cpp b/gen/abi/nvptx.cpp
+index cd81b6261f..31cf01d946 100644
+--- a/gen/abi/nvptx.cpp
++++ b/gen/abi/nvptx.cpp
+@@ -38,7 +38,7 @@ struct NVPTXTargetABI : TargetABI {
+ return;
+
+ Type *ty = arg.type->toBasetype();
+- llvm::Optional<DcomputePointer> ptr;
++ std::optional<DcomputePointer> ptr;
+ if (ty->ty == TY::Tstruct &&
+ (ptr = toDcomputePointer(static_cast<TypeStruct *>(ty)->sym))) {
+ pointerRewite.applyTo(arg);
+diff --git a/gen/abi/spirv.cpp b/gen/abi/spirv.cpp
+index 60a3eada37..df8a487acc 100644
+--- a/gen/abi/spirv.cpp
++++ b/gen/abi/spirv.cpp
+@@ -44,7 +44,7 @@ struct SPIRVTargetABI : TargetABI {
+ return;
+
+ Type *ty = arg.type->toBasetype();
+- llvm::Optional<DcomputePointer> ptr;
++ std::optional<DcomputePointer> ptr;
+ if (ty->ty == TY::Tstruct &&
+ (ptr = toDcomputePointer(static_cast<TypeStruct *>(ty)->sym))) {
+ pointerRewite.applyTo(arg);
+diff --git a/gen/arrays.cpp b/gen/arrays.cpp
+index 8179679d10..a27c4338e6 100644
+--- a/gen/arrays.cpp
++++ b/gen/arrays.cpp
+@@ -533,11 +533,7 @@ llvm::Constant *arrayLiteralToConst(IRState *p, ArrayLiteralExp *ale) {
+ // extend i1 to i8
+ if (val->getType()->isIntegerTy(1)) {
+ LLType *I8PtrTy = LLType::getInt8Ty(p->context());
+-#if LDC_LLVM_VER < 1800
+- val = llvm::ConstantExpr::getZExt(val, I8PtrTy);
+-#else
+ val = llvm::ConstantFoldCastOperand(llvm::Instruction::ZExt, val, I8PtrTy, *gDataLayout);
+-#endif
+ }
+ if (!elementType) {
+ elementType = val->getType();
+diff --git a/gen/asm-gcc.cpp b/gen/asm-gcc.cpp
+index 9df3c55c2e..60026c6f98 100644
+--- a/gen/asm-gcc.cpp
++++ b/gen/asm-gcc.cpp
+@@ -63,10 +63,6 @@ class ConstraintsBuilder {
+ // Appends a constraint string expression with an optional prefix.
+ // Returns true if the string describes an indirect operand.
+ bool append(Expression *e, char prefix = 0) {
+-#if LDC_LLVM_VER >= 1800
+- #define startswith starts_with
+-#endif
+-
+ auto se = e->isStringExp();
+ assert(se);
+ llvm::StringRef code = peekString(se);
+@@ -81,10 +77,10 @@ class ConstraintsBuilder {
+
+ // commit any modifier and strip from `code`
+ bool isIndirect = false;
+- if (code.startswith("&")) { // early clobber
++ if (code.starts_with("&")) { // early clobber
+ str << '&';
+ code = code.substr(1);
+- } else if (code.startswith("*")) { // indirect in/output
++ } else if (code.starts_with("*")) { // indirect in/output
+ isIndirect = true; // delay the commit
+ code = code.substr(1);
+ }
+@@ -97,10 +93,6 @@ class ConstraintsBuilder {
+ str << ',';
+
+ return isIndirect;
+-
+-#if LDC_LLVM_VER >= 1800
+- #undef startswith
+-#endif
+ }
+
+ // Might set `isIndirect` to true (but never resets to false).
+diff --git a/gen/asm-x86.h b/gen/asm-x86.h
+index eecd1a8b65..2104b4155c 100644
+--- a/gen/asm-x86.h
++++ b/gen/asm-x86.h
+@@ -2340,57 +2340,15 @@ struct AsmProcessor {
+ AsmCode *asmcode, AsmArgMode mode = Mode_Input) {
+ using namespace dmd;
+
+- if (sc->func->isNaked()) {
+- switch (type) {
+- case Arg_Integer:
+- if (e->type->isUnsigned()) {
+- insnTemplate << "$" << e->toUInteger();
+- } else {
++ if (type == Arg_Integer) {
++ if (e->type->isUnsigned()) {
++ insnTemplate << "$$" << e->toUInteger();
++ } else {
+ #ifndef ASM_X86_64
+- insnTemplate << "$" << static_cast<sinteger_t>(e->toInteger());
++ insnTemplate << "$$" << static_cast<sinteger_t>(e->toInteger());
+ #else
+- insnTemplate << "$" << e->toInteger();
++ insnTemplate << "$$" << e->toInteger();
+ #endif
+- }
+- break;
+-
+- case Arg_Pointer:
+- error(stmt->loc, "unsupported pointer reference to `%s` in naked asm",
+- e->toChars());
+- break;
+-
+- case Arg_Memory:
+- // Peel off one layer of explicitly taking the address, if present.
+- if (auto ae = e->isAddrExp()) {
+- e = ae->e1;
+- }
+-
+- if (auto v = e->isVarExp()) {
+- if (VarDeclaration *vd = v->var->isVarDeclaration()) {
+- if (!vd->isDataseg()) {
+- error(stmt->loc, "only global variables can be referenced by "
+- "identifier in naked asm");
+- break;
+- }
+-
+- // print out the mangle
+- if (prependExtraUnderscore(vd->resolvedLinkage())) {
+- insnTemplate << "_";
+- }
+- OutBuffer buf;
+- mangleToBuffer(vd, buf);
+- insnTemplate << buf.peekChars();
+- getIrGlobal(vd, true)->nakedUse = true;
+- break;
+- }
+- }
+- error(stmt->loc, "unsupported memory reference to `%s` in naked asm",
+- e->toChars());
+- break;
+-
+- default:
+- llvm_unreachable("Unsupported argument in asm.");
+- break;
+ }
+ } else {
+ insnTemplate << fmt << "<<" << (mode == Mode_Input ? "in" : "out")
+@@ -2401,13 +2359,6 @@ struct AsmProcessor {
+ void addOperand2(const char *fmtpre, const char *fmtpost, AsmArgType type,
+ Expression *e, AsmCode *asmcode,
+ AsmArgMode mode = Mode_Input) {
+- if (sc->func->isNaked()) {
+- // taken from above
+- error(stmt->loc, "only global variables can be referenced by identifier in "
+- "naked asm");
+- return;
+- }
+-
+ insnTemplate << fmtpre << "<<" << (mode == Mode_Input ? "in" : "out")
+ << ">>" << fmtpost;
+ asmcode->args.push_back(AsmArg(type, e, mode));
+@@ -3078,11 +3029,9 @@ struct AsmProcessor {
+ // If we subtract 8 from our const-displacement, then we can use the `H` modifier to ensure
+ // that we always end up with a valid syntax for a memory operand with an offset.
+ // So, we do just that when we have const-displacement in play.
+- // (Only for non-naked asm, as this isn't an issue for naked asm.)
+ //
+ // See also: https://lists.llvm.org/pipermail/llvm-dev/2017-August/116244.html
+- const auto forceLeadingDisplacement = hasConstDisplacement && !sc->func->isNaked();
+- if (forceLeadingDisplacement) {
++ if (hasConstDisplacement) {
+ // Subtract 8 from our const-displacement, and prepare to add the 8 from the `H` modifier.
+ insnTemplate << "-8+";
+ }
+@@ -3092,14 +3041,11 @@ struct AsmProcessor {
+ use_star = false;
+ }
+
+- if (!sc->func->isNaked()) // no addrexp in naked asm please :)
+- {
+- Type *tt = pointerTo(e->type);
+- e = createAddrExp(Loc(), e);
+- e->type = tt;
+- }
++ Type *tt = pointerTo(e->type);
++ e = createAddrExp(Loc(), e);
++ e->type = tt;
+
+- if (forceLeadingDisplacement) {
++ if (hasConstDisplacement) {
+ // We have a const-displacement in play, so we add the `H` modifier, as described earlier.
+ insnTemplate << "${" << "<<" << (mode == Mode_Input ? "in" : "out")
+ << asmcode->args.size() << ">>" << ":H}";
+diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp
+index 12d364d012..b5c868a844 100644
+--- a/gen/asmstmt.cpp
++++ b/gen/asmstmt.cpp
+@@ -532,9 +532,14 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) {
+ }
+ Identifier *const ident = targetLabel->ident;
+
+- // if internal, no special handling is necessary, skip
+- if (llvm::any_of(asmblock->internalLabels,
+- [ident](Identifier *i) { return i->equals(ident); })) {
++ // if the label is defined in inline asm, we can jump to it directly
++ if (fd->isNaked() // in naked DMD-style inline-asm functions, all D labels
++ // become assembly labels too (and the extra forwarding
++ // code would mess with the stack due to an i32 alloca)
++ || (fd->asmLabels &&
++ llvm::any_of(*fd->asmLabels, [ident](Identifier *i) {
++ return i->equals(ident);
++ }))) {
+ continue;
+ }
+
+@@ -784,21 +789,3 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) {
+ p->ir->SetInsertPoint(bb);
+ }
+ }
+-
+-//////////////////////////////////////////////////////////////////////////////
+-
+-void AsmStatement_toNakedIR(InlineAsmStatement *stmt, IRState *irs) {
+- IF_LOG Logger::println("InlineAsmStatement::toNakedIR(): %s",
+- stmt->loc.toChars());
+- LOG_SCOPE;
+-
+- // is there code?
+- if (!stmt->asmcode) {
+- return;
+- }
+- AsmCode *code = static_cast<AsmCode *>(stmt->asmcode);
+-
+- // build asm stmt
+- replace_func_name(irs, code->insnTemplate);
+- irs->nakedAsm << "\t" << code->insnTemplate << std::endl;
+-}
+diff --git a/gen/ctfloat.cpp b/gen/ctfloat.cpp
+index ef033c018d..9ce8ad72d7 100644
+--- a/gen/ctfloat.cpp
++++ b/gen/ctfloat.cpp
+@@ -83,7 +83,11 @@ void CTFloat::toAPFloat(const real_t src, APFloat &dst) {
+ u.fp = src;
+
+ const unsigned sizeInBits = APFloat::getSizeInBits(*apSemantics);
++#if LLVM_VERSION_MAJOR >= 22
++ const APInt bits = APInt(sizeInBits, u.bits);
++#else
+ const APInt bits = APInt(sizeInBits, numUint64Parts, u.bits);
++#endif
+
+ dst = APFloat(*apSemantics, bits);
+ }
+diff --git a/gen/dcompute/druntime.cpp b/gen/dcompute/druntime.cpp
+index 0f2c79bd4c..829185481c 100644
+--- a/gen/dcompute/druntime.cpp
++++ b/gen/dcompute/druntime.cpp
+@@ -43,17 +43,13 @@ bool isFromLDC_OpenCL(Dsymbol *sym) {
+ return isFromLDC_Mod(sym,Id::opencl);
+ }
+
+-llvm::Optional<DcomputePointer> toDcomputePointer(StructDeclaration *sd) {
++std::optional<DcomputePointer> toDcomputePointer(StructDeclaration *sd) {
+ if (sd->ident != Id::dcPointer || !isFromLDC_DCompute(sd)) {
+-#if LDC_LLVM_VER < 1600
+- return llvm::Optional<DcomputePointer>(llvm::None);
+-#else
+ return std::optional<DcomputePointer>(std::nullopt);
+-#endif
+ }
+
+ TemplateInstance *ti = sd->isInstantiated();
+ int addrspace = isExpression((*ti->tiargs)[0])->toInteger();
+ Type *type = isType((*ti->tiargs)[1]);
+- return llvm::Optional<DcomputePointer>(DcomputePointer(addrspace, type));
++ return std::optional<DcomputePointer>(DcomputePointer(addrspace, type));
+ }
+diff --git a/gen/dcompute/druntime.h b/gen/dcompute/druntime.h
+index 6ff3b2c8a8..673eb24951 100644
+--- a/gen/dcompute/druntime.h
++++ b/gen/dcompute/druntime.h
+@@ -18,14 +18,7 @@
+ #include "gen/irstate.h"
+ #include "gen/llvm.h"
+ #include "gen/tollvm.h"
+-#if LDC_LLVM_VER < 1700
+-#include "llvm/ADT/Optional.h"
+-#else
+ #include <optional>
+-namespace llvm {
+-template <typename T> using Optional = std::optional<T>;
+-}
+-#endif
+
+ class Dsymbol;
+ class Type;
+@@ -45,4 +38,4 @@ struct DcomputePointer {
+ return LLPointerType::get(getGlobalContext(), as);
+ }
+ };
+-llvm::Optional<DcomputePointer> toDcomputePointer(StructDeclaration *sd);
++std::optional<DcomputePointer> toDcomputePointer(StructDeclaration *sd);
+diff --git a/gen/dcompute/targetCUDA.cpp b/gen/dcompute/targetCUDA.cpp
+index ffd2711acc..9861cb2764 100644
+--- a/gen/dcompute/targetCUDA.cpp
++++ b/gen/dcompute/targetCUDA.cpp
+@@ -43,7 +43,7 @@ public:
+ llvm::Reloc::Static, llvm::CodeModel::Medium, codeGenOptLevel(), false);
+
+ _ir = new IRState("dcomputeTargetCUDA", ctx);
+-#if LDC_LLVM_VER >= 2100
++#if LLVM_VERSION_MAJOR >= 21
+ _ir->module.setTargetTriple(llvm::Triple(tripleString));
+ #else
+ _ir->module.setTargetTriple(tripleString);
+diff --git a/gen/dcompute/targetOCL.cpp b/gen/dcompute/targetOCL.cpp
+index b93850c95e..e0fa37310e 100644
+--- a/gen/dcompute/targetOCL.cpp
++++ b/gen/dcompute/targetOCL.cpp
+@@ -24,7 +24,7 @@
+ #include <string>
+
+ // from SPIRVInternal.h
+-#if LDC_LLVM_VER < 1900
++#if LLVM_VERSION_MAJOR < 19
+ # define SPIR_TARGETTRIPLE32 "spir-unknown-unknown"
+ # define SPIR_TARGETTRIPLE64 "spir64-unknown-unknown"
+ # define SPIR_DATALAYOUT32 \
+@@ -42,7 +42,7 @@
+ #else // LLVM 19+
+ # define SPIR_TARGETTRIPLE32 "spirv-unknown-unknown"
+ # define SPIR_TARGETTRIPLE64 "spirv64-unknown-unknown"
+-# if LDC_LLVM_VER < 2000
++# if LLVM_VERSION_MAJOR < 20
+ # define SPIR_DATALAYOUT32 \
+ "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64" \
+ "-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
+@@ -75,14 +75,13 @@ public:
+ _ir = new IRState("dcomputeTargetOCL", ctx);
+ std::string targTripleStr = is64 ? SPIR_TARGETTRIPLE64
+ : SPIR_TARGETTRIPLE32;
+-#if LDC_LLVM_VER >= 2100
++#if LLVM_VERSION_MAJOR >= 21
+ llvm::Triple targTriple = llvm::Triple(targTripleStr);
+ #else
+ std::string targTriple = targTripleStr;
+ #endif
+ _ir->module.setTargetTriple(targTriple);
+
+-#if LDC_LLVM_VER >= 1600
+ auto floatABI = ::FloatABI::Hard;
+ targetMachine = createTargetMachine(
+ targTripleStr,
+@@ -90,7 +89,6 @@ public:
+ "", {},
+ is64 ? ExplicitBitness::M64 : ExplicitBitness::M32, floatABI,
+ llvm::Reloc::Static, llvm::CodeModel::Medium, codeGenOptLevel(), false);
+-#endif
+ _ir->module.setDataLayout(is64 ? SPIR_DATALAYOUT64 : SPIR_DATALAYOUT32);
+ _ir->dcomputetarget = this;
+ }
+@@ -200,7 +198,7 @@ public:
+ void decodeTypes(std::array<llvm::SmallVector<llvm::Metadata *, 8>,count_KernArgMD>& attrs,
+ VarDeclaration *v)
+ {
+- llvm::Optional<DcomputePointer> ptr;
++ std::optional<DcomputePointer> ptr;
+ std::string typeQuals;
+ std::string baseTyName;
+ std::string tyName;
+diff --git a/gen/declarations.cpp b/gen/declarations.cpp
+index bdadc84c61..0e43a20ff6 100644
+--- a/gen/declarations.cpp
++++ b/gen/declarations.cpp
+@@ -412,24 +412,19 @@ public:
+ void visit(PragmaDeclaration *decl) override {
+ const auto &triple = *global.params.targetTriple;
+
+-#if LDC_LLVM_VER >= 1800
+- #define endswith ends_with
+- #define startswith starts_with
+-#endif
+-
+ if (decl->ident == Id::lib) {
+ assert(!irs->dcomputetarget);
+ llvm::StringRef name = getPragmaStringArg(decl);
+
+ if (triple.isWindowsGNUEnvironment()) {
+- if (name.endswith(".lib")) {
++ if (name.ends_with(".lib")) {
+ // On MinGW, strip the .lib suffix, if any, to improve compatibility
+ // with code written for DMD (we pass the name to GCC via -l, just as
+ // on Posix).
+ name = name.drop_back(4);
+ }
+
+- if (name.startswith("shell32")) {
++ if (name.starts_with("shell32")) {
+ // Another DMD compatibility kludge: Ignore
+ // pragma(lib, "shell32.lib"), it is implicitly provided by
+ // MinGW.
+@@ -438,10 +433,10 @@ public:
+ }
+
+ if (triple.isWindowsMSVCEnvironment()) {
+- if (name.endswith(".a")) {
++ if (name.ends_with(".a")) {
+ name = name.drop_back(2);
+ }
+- if (name.endswith(".lib")) {
++ if (name.ends_with(".lib")) {
+ name = name.drop_back(4);
+ }
+
+@@ -450,7 +445,7 @@ public:
+ std::string arg = ("/DEFAULTLIB:\"" + name + "\"").str();
+ gIR->addLinkerOption(llvm::StringRef(arg));
+ } else {
+- const bool isStaticLib = name.endswith(".a");
++ const bool isStaticLib = name.ends_with(".a");
+ const size_t nameLen = name.size();
+
+ char *arg = nullptr;
+@@ -490,11 +485,6 @@ public:
+ }
+ }
+ visit(static_cast<AttribDeclaration *>(decl));
+-
+-#if LDC_LLVM_VER >= 1800
+- #undef endswith
+- #undef startswith
+-#endif
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp
+index 21b176ef00..df1328ecdb 100644
+--- a/gen/dibuilder.cpp
++++ b/gen/dibuilder.cpp
+@@ -47,13 +47,6 @@ using namespace dmd;
+ namespace cl = llvm::cl;
+ using LLMetadata = llvm::Metadata;
+
+-#if LDC_LLVM_VER >= 1600
+-namespace llvm {
+- template <typename T> using Optional = std::optional<T>;
+- inline constexpr std::nullopt_t None = std::nullopt;
+-}
+-#endif
+-
+ static cl::opt<cl::boolOrDefault> emitColumnInfo(
+ "gcolumn-info", cl::ZeroOrMore, cl::Hidden,
+ cl::desc("Include column numbers in line debug infos. Defaults to "
+@@ -426,7 +419,7 @@ DIType DIBuilder::CreateEnumType(TypeEnum *type) {
+ DIType DIBuilder::CreatePointerType(TypePointer *type) {
+ // TODO: The addressspace is important for dcompute targets. See e.g.
+ // https://www.mail-archive.com/dwarf-discuss@lists.dwarfstd.org/msg00326.html
+- const llvm::Optional<unsigned> DWARFAddressSpace = llvm::None;
++ const std::optional<unsigned> DWARFAddressSpace;
+
+ const auto name = processDIName(type->toPrettyChars(true));
+
+@@ -672,10 +665,7 @@ DIType DIBuilder::CreateCompositeType(Type *t) {
+ ret = DBuilder.createClassType(
+ scope, name, file, lineNum, sizeInBits, alignmentInBits,
+ classOffsetInBits, DIFlags::FlagZero, derivedFrom, elemsArray,
+-#if LDC_LLVM_VER >= 1800
+- runtimeLang,
+-#endif
+- vtableHolder, templateParams, uniqueIdentifier);
++ runtimeLang, vtableHolder, templateParams, uniqueIdentifier);
+ } else {
+ ret = DBuilder.createStructType(scope, name, file, lineNum, sizeInBits,
+ alignmentInBits, DIFlags::FlagZero,
+@@ -799,7 +789,7 @@ DISubroutineType DIBuilder::CreateFunctionType(Type *type,
+ DIType ditype = DBuilder.createReferenceType(
+ llvm::dwarf::DW_TAG_pointer_type, pointeeType, target.ptrsize * 8);
+ ditype = DBuilder.createObjectPointerType(ditype
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ , /* Implicit */ true
+ #endif
+ );
+@@ -819,11 +809,7 @@ DISubroutineType DIBuilder::CreateFunctionType(Type *type,
+ }
+
+ DISubroutineType DIBuilder::CreateEmptyFunctionType() {
+-#if LDC_LLVM_VER >= 2100
+ auto paramsArray = DBuilder.getOrCreateTypeArray({});
+-#else
+- auto paramsArray = DBuilder.getOrCreateTypeArray(llvm::None);
+-#endif
+ return DBuilder.createSubroutineType(paramsArray);
+ }
+
+@@ -869,7 +855,7 @@ DIType DIBuilder::CreateTypeDescription(Type *t, bool voidToUbyte) {
+ // display null as void*
+ return DBuilder.createPointerType(
+ CreateTypeDescription(Type::tvoid), target.ptrsize * 8, 0,
+- /* DWARFAddressSpace */ llvm::None, "typeof(null)");
++ /* DWARFAddressSpace */ std::nullopt, "typeof(null)");
+ }
+ if (auto te = t->isTypeEnum())
+ return CreateEnumType(te);
+@@ -892,7 +878,7 @@ DIType DIBuilder::CreateTypeDescription(Type *t, bool voidToUbyte) {
+ const auto name =
+ (tc->sym->toPrettyChars(true) + llvm::StringRef("*")).str();
+ return DBuilder.createPointerType(aggregateDIType, target.ptrsize * 8, 0,
+- llvm::None, processDIName(name));
++ std::nullopt, processDIName(name));
+ }
+ if (auto tf = t->isTypeFunction())
+ return CreateFunctionType(tf);
+@@ -1229,7 +1215,7 @@ void DIBuilder::EmitValue(llvm::Value *val, VarDeclaration *vd) {
+ auto instr = DBuilder.insertDbgValueIntrinsic(
+ val, debugVariable, DBuilder.createExpression(),
+ IR->ir->getCurrentDebugLocation(), IR->scopebb());
+-#if LDC_LLVM_VER >= 1900
++#if LLVM_VERSION_MAJOR >= 19
+ llvm::cast<llvm::DbgRecord *>
+ #endif
+ (instr)->setDebugLoc(IR->ir->getCurrentDebugLocation());
+diff --git a/gen/functions.cpp b/gen/functions.cpp
+index 3c3e6d3d35..b1e106f6bc 100644
+--- a/gen/functions.cpp
++++ b/gen/functions.cpp
+@@ -773,10 +773,8 @@ static LinkageWithCOMDAT lowerFuncLinkage(FuncDeclaration *fdecl) {
+ return LinkageWithCOMDAT(LLGlobalValue::ExternalLinkage, false);
+ }
+
+- // A body-less declaration always needs to be marked as external in LLVM
+- // (also e.g. naked template functions which would otherwise be weak_odr,
+- // but where the definition is in module-level inline asm).
+- if (!fdecl->fbody || fdecl->isNaked()) {
++ // A body-less declaration always needs to be marked as external in LLVM.
++ if (!fdecl->fbody) {
+ return LinkageWithCOMDAT(LLGlobalValue::ExternalLinkage, false);
+ }
+
+@@ -910,10 +908,6 @@ bool eraseDummyAfterReturnBB(llvm::BasicBlock *bb) {
+ * to be found.
+ */
+ void emulateWeakAnyLinkageForMSVC(IrFunction *irFunc, LINK linkage) {
+-#if LDC_LLVM_VER >= 1800
+- #define startswith starts_with
+-#endif
+-
+ LLFunction *func = irFunc->getLLVMFunc();
+
+ const bool isWin32 = global.params.targetTriple->isArch32Bit();
+@@ -930,18 +924,18 @@ void emulateWeakAnyLinkageForMSVC(IrFunction *irFunc, LINK linkage) {
+
+ std::string finalWeakMangle = finalMangle.str();
+ if (linkage == LINK::cpp) {
+- assert(finalMangle.startswith("?"));
++ assert(finalMangle.starts_with("?"));
+ // prepend `__weak_` to first identifier
+- size_t offset = finalMangle.startswith("??$") ? 3 : 1;
++ size_t offset = finalMangle.starts_with("??$") ? 3 : 1;
+ finalWeakMangle.insert(offset, "__weak_");
+ } else if (linkage == LINK::d) {
+ const size_t offset = isWin32 ? 1 : 0;
+- assert(finalMangle.substr(offset).startswith("_D"));
++ assert(finalMangle.substr(offset).starts_with("_D"));
+ // prepend a `__weak` package
+ finalWeakMangle.insert(offset + 2, "6__weak");
+ } else {
+ // prepend `__weak_`
+- const size_t offset = isWin32 && finalMangle.startswith("_") ? 1 : 0;
++ const size_t offset = isWin32 && finalMangle.starts_with("_") ? 1 : 0;
+ finalWeakMangle.insert(offset, "__weak_");
+ }
+
+@@ -965,10 +959,6 @@ void emulateWeakAnyLinkageForMSVC(IrFunction *irFunc, LINK linkage) {
+ // declaration
+ irFunc->setLLVMFunc(newFunc);
+ func->replaceNonMetadataUsesWith(newFunc);
+-
+-#if LDC_LLVM_VER >= 1800
+- #undef startswith
+-#endif
+ }
+
+ } // anonymous namespace
+@@ -1127,12 +1117,6 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
+ gIR->funcGenStates.pop_back();
+ };
+
+- // if this function is naked, we take over right away! no standard processing!
+- if (fd->isNaked()) {
+- DtoDefineNakedFunction(fd);
+- return;
+- }
+-
+ SCOPE_EXIT {
+ if (irFunc->isDynamicCompiled()) {
+ defineDynamicCompiledFunction(gIR, irFunc);
+@@ -1172,6 +1156,10 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
+ func->addFnAttr(LLAttribute::SanitizeAddress);
+ }
+
++ if (opts::isSanitizerEnabled(opts::FuzzSanitizer)) {
++ func->addFnAttr(LLAttribute::OptForFuzzing);
++ }
++
+ if (opts::isSanitizerEnabled(opts::MemorySanitizer & noSanitizeMask)) {
+ func->addFnAttr(LLAttribute::SanitizeMemory);
+ }
+@@ -1190,7 +1178,9 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
+ if (opts::isUsingSampleBasedPGOProfile()) {
+ func->addFnAttr("use-sample-profile");
+ }
+-
++ if (opts::noBuiltIn) {
++ func->addFnAttr("no-builtins");
++ }
+ if (fd->hasInlineAsm()) {
+ // disable frame-pointer-elimination for functions with DMD-style inline asm
+ func->addFnAttr("frame-pointer", "all");
+@@ -1210,18 +1200,13 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
+ gIR->ir->setFastMathFlags(irFunc->FMF);
+ gIR->DBuilder.EmitFuncStart(fd);
+
+- // @naked: emit body and return, no prologue/epilogue
+- if (func->hasFnAttribute(llvm::Attribute::Naked)) {
+- Statement_toIR(fd->fbody, gIR);
+- const bool wasDummy = eraseDummyAfterReturnBB(gIR->scopebb());
+- if (!wasDummy && !gIR->scopereturned()) {
+- // this is what clang does to prevent LLVM complaining about
+- // non-terminated function
+- gIR->ir->CreateUnreachable();
+- }
+- return;
++ if (fd->isNaked()) { // DMD-style `asm { naked; }`
++ func->addFnAttr(llvm::Attribute::Naked);
+ }
+
++ // @naked: emit body and return, no prologue/epilogue
++ const bool isNaked = func->hasFnAttribute(llvm::Attribute::Naked);
++
+ // create temporary allocas block
+ funcGen.allocasBlock =
+ llvm::BasicBlock::Create(gIR->context(), "allocas", func);
+@@ -1229,12 +1214,12 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
+ emitInstrumentationFnEnter(fd);
+
+ if (global.params.trace && fd->emitInstrumentation && !fd->isCMain() &&
+- !fd->isNaked()) {
++ !isNaked) {
+ emitDMDStyleFunctionTrace(*gIR, fd, funcGen);
+ }
+
+ // give the 'this' parameter (an lvalue) storage and debug info
+- if (irFty.arg_this) {
++ if (!isNaked && irFty.arg_this) {
+ LLValue *thisvar = irFunc->thisArg;
+ assert(thisvar);
+
+@@ -1256,7 +1241,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
+ }
+
+ // define all explicit parameters
+- if (fd->parameters)
++ if (!isNaked && fd->parameters)
+ defineParameters(irFty, *fd->parameters);
+
+ // Initialize PGO state for this function
+@@ -1322,7 +1307,10 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
+
+ // pass the previous block into this block
+ gIR->DBuilder.EmitStopPoint(fd->endloc);
+- if (func->getReturnType() == LLType::getVoidTy(gIR->context())) {
++
++ if (isNaked) {
++ gIR->ir->CreateUnreachable();
++ } else if (func->getReturnType() == LLType::getVoidTy(gIR->context())) {
+ gIR->ir->CreateRetVoid();
+ } else if (isAnyMainFunction(fd)) {
+ gIR->ir->CreateRet(LLConstant::getNullValue(func->getReturnType()));
+@@ -1335,12 +1323,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
+ }
+
+ // move allocas from temporary block to the start of the function
+-#if LDC_LLVM_VER >= 1600
+ beginbb->splice(beginbb->begin(), funcGen.allocasBlock);
+-#else
+- beginbb->getInstList().splice(beginbb->begin(),
+- funcGen.allocasBlock->getInstList());
+-#endif
+ funcGen.allocasBlock->eraseFromParent();
+ funcGen.allocasBlock = nullptr;
+
+diff --git a/gen/functions.h b/gen/functions.h
+index 5dcfa7032d..609da95620 100644
+--- a/gen/functions.h
++++ b/gen/functions.h
+@@ -40,7 +40,6 @@ void DtoResolveFunction(FuncDeclaration *fdecl);
+ void DtoDeclareFunction(FuncDeclaration *fdecl);
+ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally = false);
+
+-void DtoDefineNakedFunction(FuncDeclaration *fd);
+ void emitABIReturnAsmStmt(IRAsmBlock *asmblock, Loc loc,
+ FuncDeclaration *fdecl);
+
+diff --git a/gen/irstate.cpp b/gen/irstate.cpp
+index 6834025129..364e825763 100644
+--- a/gen/irstate.cpp
++++ b/gen/irstate.cpp
+@@ -45,14 +45,14 @@ IrFunction *IRState::func() { return &funcGen().irFunc; }
+
+ llvm::Function *IRState::topfunc() { return func()->getLLVMFunc(); }
+
+-#if LDC_LLVM_VER >= 1900
++#if LLVM_VERSION_MAJOR >= 19
+ llvm::BasicBlock::iterator IRState::nextAllocaPos() {
+ #else
+ llvm::BasicBlock *IRState::nextAllocaPos() {
+ #endif
+ return funcGen()
+ .allocasBlock
+-#if LDC_LLVM_VER >= 1900
++#if LLVM_VERSION_MAJOR >= 19
+ ->end()
+ #endif
+ ;
+@@ -203,9 +203,7 @@ LLGlobalVariable *
+ getCachedStringLiteralImpl(llvm::Module &module,
+ llvm::StringMap<LLGlobalVariable *> &cache,
+ llvm::StringRef key,
+-#if LDC_LLVM_VER >= 1700
+ std::optional< unsigned > addrspace,
+-#endif
+ F initFactory) {
+ auto iter = cache.find(key);
+ if (iter != cache.end()) {
+@@ -214,14 +212,9 @@ getCachedStringLiteralImpl(llvm::Module &module,
+
+ LLConstant *constant = initFactory();
+
+- auto gvar =
+- new LLGlobalVariable(module, constant->getType(), true,
+- LLGlobalValue::PrivateLinkage, constant,
+- ".str", nullptr, llvm::GlobalValue::NotThreadLocal
+-#if LDC_LLVM_VER >= 1700
+- , addrspace
+-#endif
+- );
++ auto gvar = new LLGlobalVariable(
++ module, constant->getType(), true, LLGlobalValue::PrivateLinkage,
++ constant, ".str", nullptr, llvm::GlobalValue::NotThreadLocal, addrspace);
+ gvar->setUnnamedAddr(LLGlobalValue::UnnamedAddr::Global);
+
+ cache[key] = gvar;
+@@ -261,30 +254,18 @@ LLGlobalVariable *IRState::getCachedStringLiteral(StringExp *se) {
+ const llvm::StringRef key(reinterpret_cast<const char *>(keyData.ptr),
+ keyData.length);
+
+- return getCachedStringLiteralImpl(module, *cache, key,
+-#if LDC_LLVM_VER >= 1700
+- std::nullopt,
+-#endif
+- [se]() {
++ return getCachedStringLiteralImpl(module, *cache, key, std::nullopt, [se]() {
+ // null-terminate
+ return buildStringLiteralConstant(se, se->len + 1);
+ });
+ }
+
+-LLGlobalVariable *IRState::getCachedStringLiteral(llvm::StringRef s
+-#if LDC_LLVM_VER >= 1700
+- ,std::optional< unsigned > addrspace
+-#endif
+- ) {
+- return getCachedStringLiteralImpl(module,
+- cachedStringLiterals,
+- s,
+-#if LDC_LLVM_VER >= 1700
+- addrspace,
+-#endif
+- [&]() {
+- return llvm::ConstantDataArray::getString(context(), s, true);
+- });
++LLGlobalVariable *
++IRState::getCachedStringLiteral(llvm::StringRef s,
++ std::optional<unsigned> addrspace) {
++ return getCachedStringLiteralImpl(
++ module, cachedStringLiterals, s, addrspace,
++ [&]() { return llvm::ConstantDataArray::getString(context(), s, true); });
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+diff --git a/gen/irstate.h b/gen/irstate.h
+index 25e963dce9..0e8983a9f7 100644
+--- a/gen/irstate.h
++++ b/gen/irstate.h
+@@ -24,10 +24,10 @@
+ #include "llvm/ProfileData/InstrProfReader.h"
+ #include <deque>
+ #include <memory>
++#include <optional>
+ #include <set>
+ #include <sstream>
+ #include <vector>
+-#include <optional>
+
+ namespace llvm {
+ class LLVMContext;
+@@ -94,9 +94,6 @@ struct IRAsmBlock {
+ std::set<std::string> clobs;
+ size_t outputcount;
+
+- // stores the labels within the asm block
+- std::vector<Identifier *> internalLabels;
+-
+ CompoundAsmStatement *asmBlock;
+ LLType *retty;
+ unsigned retn;
+@@ -156,7 +153,7 @@ public:
+ IrFunction *func();
+ llvm::Function *topfunc();
+
+-#if LDC_LLVM_VER >= 1900
++#if LLVM_VERSION_MAJOR >= 19
+ llvm::BasicBlock::iterator nextAllocaPos();
+ #else
+ llvm::BasicBlock *nextAllocaPos();
+@@ -220,7 +217,6 @@ public:
+
+ // for inline asm
+ IRAsmBlock *asmBlock = nullptr;
+- std::ostringstream nakedAsm;
+
+ // Globals to pin in the llvm.used array to make sure they are not
+ // eliminated.
+@@ -254,11 +250,9 @@ public:
+ // calls with a StringExp with matching data will return the same variable.
+ // Exception: ulong[]-typed hex strings (not null-terminated either).
+ llvm::GlobalVariable *getCachedStringLiteral(StringExp *se);
+- llvm::GlobalVariable *getCachedStringLiteral(llvm::StringRef s
+-#if LDC_LLVM_VER >= 1700
+- ,std::optional< unsigned > = std::nullopt
+-#endif
+- );
++ llvm::GlobalVariable *
++ getCachedStringLiteral(llvm::StringRef s,
++ std::optional<unsigned> = std::nullopt);
+
+ // List of functions with cpu or features attributes overriden by user
+ std::vector<IrFunction *> targetCpuOrFeaturesOverridden;
+diff --git a/gen/llvm.h b/gen/llvm.h
+index 45f051aa93..0070894b8b 100644
+--- a/gen/llvm.h
++++ b/gen/llvm.h
+@@ -35,10 +35,10 @@ using llvm::APFloat;
+ using llvm::APInt;
+ using llvm::IRBuilder;
+
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ #define GET_INTRINSIC_DECL(_X, _TY) \
+ (llvm::Intrinsic::getOrInsertDeclaration(&gIR->module, llvm::Intrinsic::_X, _TY))
+-#elif LDC_LLVM_VER >= 1900
++#elif LLVM_VERSION_MAJOR >= 19
+ #define GET_INTRINSIC_DECL(_X, _TY) \
+ (llvm::Intrinsic::getDeclaration(&gIR->module, llvm::Intrinsic::_X, _TY))
+ #else
+diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp
+index 0771ab54ca..83d72dcc55 100644
+--- a/gen/llvmhelpers.cpp
++++ b/gen/llvmhelpers.cpp
+@@ -1132,11 +1132,7 @@ LLConstant *DtoConstExpInit(Loc loc, Type *targetType, Expression *exp) {
+ // extend i1 to i8
+ if (llType->isIntegerTy(1)) {
+ llType = LLType::getInt8Ty(gIR->context());
+-#if LDC_LLVM_VER < 1800
+- val = llvm::ConstantExpr::getZExt(val, llType);
+-#else
+ val = llvm::ConstantFoldCastOperand(llvm::Instruction::ZExt, val, llType, *gDataLayout);
+-#endif
+ }
+
+ if (llType == targetLLType)
+@@ -1196,11 +1192,7 @@ LLConstant *DtoConstExpInit(Loc loc, Type *targetType, Expression *exp) {
+ "On initializer integer type mismatch, the target should be wider "
+ "than the source.");
+
+-#if LDC_LLVM_VER < 1800
+- return llvm::ConstantExpr::getZExtOrBitCast(val, target);
+-#else
+ return llvm::ConstantFoldCastOperand(llvm::Instruction::ZExt, val, target, *gDataLayout);
+-#endif
+ }
+
+ Logger::println("Unhandled type mismatch, giving up.");
+diff --git a/gen/modules.cpp b/gen/modules.cpp
+index 569859e468..41fcc777b3 100644
+--- a/gen/modules.cpp
++++ b/gen/modules.cpp
+@@ -50,9 +50,7 @@
+ #include "llvm/Support/FileSystem.h"
+ #include "llvm/Support/Path.h"
+ #include "llvm/Transforms/Utils/ModuleUtils.h"
+-#if LDC_LLVM_VER >= 1700
+ #include "llvm/Support/VirtualFileSystem.h"
+-#endif
+
+ #if _AIX || __sun
+ #include <alloca.h>
+@@ -340,13 +338,8 @@ void loadInstrProfileData(IRState *irs) {
+ IF_LOG Logger::println("Read profile data from %s",
+ global.params.datafileInstrProf);
+
+- auto readerOrErr =
+- llvm::IndexedInstrProfReader::create(global.params.datafileInstrProf
+-#if LDC_LLVM_VER >= 1700
+- ,
+- *llvm::vfs::getRealFileSystem()
+-#endif
+- );
++ auto readerOrErr = llvm::IndexedInstrProfReader::create(
++ global.params.datafileInstrProf, *llvm::vfs::getRealFileSystem());
+ if (auto E = readerOrErr.takeError()) {
+ handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EI) {
+ error(irs->dmodule->loc, "Could not read profile file '%s': %s",
+diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp
+index d17cd3c677..815eb5d058 100644
+--- a/gen/ms-cxx-helper.cpp
++++ b/gen/ms-cxx-helper.cpp
+@@ -113,11 +113,7 @@ void cloneBlocks(const std::vector<llvm::BasicBlock *> &srcblocks,
+ if (!newInst)
+ newInst = Inst->clone();
+
+-#if LDC_LLVM_VER < 1600
+- nbb->getInstList().push_back(newInst);
+-#else
+ newInst->insertInto(nbb, nbb->end());
+-#endif
+
+ VMap[Inst] = newInst; // Add instruction map to value.
+ if (unwindTo)
+diff --git a/gen/naked.cpp b/gen/naked.cpp
+index 5e5b3ea653..e63ed83d55 100644
+--- a/gen/naked.cpp
++++ b/gen/naked.cpp
+@@ -27,228 +27,6 @@
+
+ using namespace dmd;
+
+-////////////////////////////////////////////////////////////////////////////////
+-// FIXME: Integrate these functions
+-void AsmStatement_toNakedIR(InlineAsmStatement *stmt, IRState *irs);
+-
+-////////////////////////////////////////////////////////////////////////////////
+-
+-class ToNakedIRVisitor : public Visitor {
+- IRState *irs;
+-
+-public:
+- explicit ToNakedIRVisitor(IRState *irs) : irs(irs) {}
+-
+- //////////////////////////////////////////////////////////////////////////
+-
+- // Import all functions from class Visitor
+- using Visitor::visit;
+-
+- //////////////////////////////////////////////////////////////////////////
+-
+- void visit(Statement *stmt) override {
+- error(stmt->loc, "Statement not allowed in naked function");
+- }
+-
+- //////////////////////////////////////////////////////////////////////////
+-
+- void visit(InlineAsmStatement *stmt) override {
+- AsmStatement_toNakedIR(stmt, irs);
+- }
+-
+- //////////////////////////////////////////////////////////////////////////
+-
+- void visit(CompoundStatement *stmt) override {
+- IF_LOG Logger::println("CompoundStatement::toNakedIR(): %s",
+- stmt->loc.toChars());
+- LOG_SCOPE;
+-
+- if (stmt->statements) {
+- for (auto s : *stmt->statements) {
+- if (s) {
+- s->accept(this);
+- }
+- }
+- }
+- }
+-
+- //////////////////////////////////////////////////////////////////////////
+-
+- void visit(ExpStatement *stmt) override {
+- IF_LOG Logger::println("ExpStatement::toNakedIR(): %s",
+- stmt->loc.toChars());
+- LOG_SCOPE;
+-
+- // This happens only if there is a ; at the end:
+- // asm { naked; ... };
+- // Is this a legal AST?
+- if (!stmt->exp) {
+- return;
+- }
+-
+- // only expstmt supported in declarations
+- if (!stmt->exp || stmt->exp->op != EXP::declaration) {
+- visit(static_cast<Statement *>(stmt));
+- return;
+- }
+-
+- DeclarationExp *d = static_cast<DeclarationExp *>(stmt->exp);
+- VarDeclaration *vd = d->declaration->isVarDeclaration();
+- FuncDeclaration *fd = d->declaration->isFuncDeclaration();
+- EnumDeclaration *ed = d->declaration->isEnumDeclaration();
+-
+- // and only static variable/function declaration
+- // no locals or nested stuffies!
+- if (!vd && !fd && !ed) {
+- visit(static_cast<Statement *>(stmt));
+- return;
+- }
+- if (vd && !(vd->storage_class & (STCstatic | STCmanifest))) {
+- error(vd->loc, "non-static variable `%s` not allowed in naked function",
+- vd->toChars());
+- return;
+- }
+- if (fd && !fd->isStatic()) {
+- error(fd->loc,
+- "non-static nested function `%s` not allowed in naked function",
+- fd->toChars());
+- return;
+- }
+- // enum decls should always be safe
+-
+- // make sure the symbols gets processed
+- // TODO: codegen() here is likely incorrect
+- Declaration_codegen(d->declaration, irs);
+- }
+-
+- //////////////////////////////////////////////////////////////////////////
+-
+- void visit(LabelStatement *stmt) override {
+- IF_LOG Logger::println("LabelStatement::toNakedIR(): %s",
+- stmt->loc.toChars());
+- LOG_SCOPE;
+-
+- printLabelName(irs->nakedAsm, mangleExact(irs->func()->decl),
+- stmt->ident->toChars());
+- irs->nakedAsm << ":";
+-
+- if (stmt->statement) {
+- stmt->statement->accept(this);
+- }
+- }
+-};
+-
+-////////////////////////////////////////////////////////////////////////////////
+-
+-void DtoDefineNakedFunction(FuncDeclaration *fd) {
+- IF_LOG Logger::println("DtoDefineNakedFunction(%s)", mangleExact(fd));
+- LOG_SCOPE;
+-
+- // we need to do special processing on the body, since we only want
+- // to allow actual inline asm blocks to reach the final asm output
+-
+- std::ostringstream &asmstr = gIR->nakedAsm;
+-
+- // build function header
+-
+- // FIXME: could we perhaps use llvm asmwriter to give us these details ?
+-
+- const char *mangle = mangleExact(fd);
+- std::string fullmangle; // buffer only
+-
+- const auto &triple = *global.params.targetTriple;
+- bool const isWin = triple.isOSWindows();
+- bool const isDarwin = triple.isOSDarwin();
+-
+- // osx is different
+- // also mangling has an extra underscore prefixed
+- if (isDarwin) {
+- fullmangle += '_';
+- fullmangle += mangle;
+- mangle = fullmangle.c_str();
+-
+- asmstr << "\t.section\t__TEXT,__text,regular,pure_instructions"
+- << std::endl;
+- asmstr << "\t.globl\t" << mangle << std::endl;
+- if (fd->isInstantiated()) {
+- asmstr << "\t.weak_definition\t" << mangle << std::endl;
+- }
+- asmstr << "\t.p2align\t4, 0x90" << std::endl;
+- asmstr << mangle << ":" << std::endl;
+- }
+- // Windows is different
+- else if (isWin) {
+- // mangled names starting with '?' (MSVC++ symbols) apparently need quoting
+- if (mangle[0] == '?') {
+- fullmangle += '"';
+- fullmangle += mangle;
+- fullmangle += '"';
+- mangle = fullmangle.c_str();
+- } else if (triple.isArch32Bit()) {
+- // prepend extra underscore for Windows x86
+- fullmangle += '_';
+- fullmangle += mangle;
+- mangle = fullmangle.c_str();
+- }
+-
+- asmstr << "\t.def\t" << mangle << ";" << std::endl;
+- // hard code these two numbers for now since gas ignores .scl and llvm
+- // is defaulting to .type 32 for everything I have seen
+- asmstr << "\t.scl\t2;" << std::endl;
+- asmstr << "\t.type\t32;" << std::endl;
+- asmstr << "\t.endef" << std::endl;
+-
+- if (fd->isInstantiated()) {
+- asmstr << "\t.section\t.text,\"xr\",discard," << mangle << std::endl;
+- } else {
+- asmstr << "\t.text" << std::endl;
+- }
+- asmstr << "\t.globl\t" << mangle << std::endl;
+- asmstr << "\t.p2align\t4, 0x90" << std::endl;
+- asmstr << mangle << ":" << std::endl;
+- } else {
+- if (fd->isInstantiated()) {
+- asmstr << "\t.section\t.text." << mangle << ",\"axG\", at progbits,"
+- << mangle << ",comdat" << std::endl;
+- asmstr << "\t.weak\t" << mangle << std::endl;
+- } else {
+- asmstr << "\t.text" << std::endl;
+- asmstr << "\t.globl\t" << mangle << std::endl;
+- }
+- asmstr << "\t.p2align\t4, 0x90" << std::endl;
+- asmstr << "\t.type\t" << mangle << ", at function" << std::endl;
+- asmstr << mangle << ":" << std::endl;
+- }
+-
+- // emit body
+- ToNakedIRVisitor v(gIR);
+- fd->fbody->accept(&v);
+-
+- // We could have generated new errors in toNakedIR(), but we are in codegen
+- // already so we have to abort here.
+- if (global.errors) {
+- fatal();
+- }
+-
+- // emit size after body
+- // llvm does this on linux, but not on osx or Win
+- if (!(isWin || isDarwin)) {
+- asmstr << "\t.size\t" << mangle << ", .-" << mangle << std::endl
+- << std::endl;
+- }
+-
+- gIR->module.appendModuleInlineAsm(asmstr.str());
+- asmstr.str("");
+-
+- if (global.params.dllexport ||
+- (global.params.targetTriple->isOSWindows() && fd->isExport())) {
+- // Embed a linker switch telling the MS linker to export the naked function.
+- // This mimics the effect of the dllexport attribute for regular functions.
+- const auto linkerSwitch = std::string("/EXPORT:") + mangle;
+- gIR->addLinkerOption(llvm::StringRef(linkerSwitch));
+- }
+-}
+-
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void emitABIReturnAsmStmt(IRAsmBlock *asmblock, Loc loc,
+@@ -436,7 +214,7 @@ DValue *DtoInlineAsmExpr(Loc loc, FuncDeclaration *fd,
+ LLSmallVector<LLValue *, 8> operands;
+ LLSmallVector<LLType *, 8> indirectTypes;
+ operands.reserve(n);
+-
++
+ Type *returnType = fd->type->nextOf();
+ const size_t cisize = constraintInfo.size();
+ const size_t minRequired = n + (returnType->ty == TY::Tvoid ? 0 : 1);
+diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp
+index 7029959452..e418774715 100644
+--- a/gen/optimizer.cpp
++++ b/gen/optimizer.cpp
+@@ -36,11 +36,7 @@
+ #include "driver/targetmachine.h"
+ #endif
+
+-#if LDC_LLVM_VER < 1700
+-#include "llvm/ADT/Triple.h"
+-#else
+ #include "llvm/TargetParser/Triple.h"
+-#endif
+ #include "llvm/Analysis/InlineCost.h"
+ #include "llvm/Analysis/TargetLibraryInfo.h"
+ #include "llvm/Analysis/TargetTransformInfo.h"
+@@ -52,15 +48,12 @@
+ #include "llvm/LinkAllPasses.h"
+ #include "llvm/Support/CommandLine.h"
+ #include "llvm/Target/TargetMachine.h"
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ #include "llvm/Transforms/Utils/Instrumentation.h"
+ #else
+ #include "llvm/Transforms/Instrumentation.h"
+ #endif
+ #include "llvm/Transforms/IPO.h"
+-#if LDC_LLVM_VER < 1700
+-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+-#endif
+ #include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
+ #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
+ #include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
+@@ -212,7 +205,7 @@ static OptimizationLevel getOptimizationLevel(){
+ #ifndef IN_JITRT
+ static void addAddressSanitizerPasses(ModulePassManager &mpm,
+ OptimizationLevel level
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ ,
+ ThinOrFullLTOPhase
+ #endif
+@@ -223,11 +216,7 @@ static void addAddressSanitizerPasses(ModulePassManager &mpm,
+ aso.UseAfterScope = true;
+ aso.UseAfterReturn = opts::fSanitizeAddressUseAfterReturn;
+
+-#if LDC_LLVM_VER >= 1600
+ mpm.addPass(AddressSanitizerPass(aso));
+-#else
+- mpm.addPass(ModuleAddressSanitizerPass(aso));
+-#endif
+ }
+
+ static void addMemorySanitizerPass(ModulePassManager &mpm,
+@@ -236,13 +225,8 @@ static void addMemorySanitizerPass(ModulePassManager &mpm,
+ int trackOrigins = fSanitizeMemoryTrackOrigins;
+ bool recover = opts::isSanitizerRecoveryEnabled(opts::MemorySanitizer);
+ bool kernel = false;
+-#if LDC_LLVM_VER >= 1600
+ mpm.addPass(MemorySanitizerPass(
+ MemorySanitizerOptions{trackOrigins, recover, kernel}));
+-#else
+- fpm.addPass(MemorySanitizerPass(
+- MemorySanitizerOptions{trackOrigins, recover, kernel}));
+-#endif
+
+ // MemorySanitizer inserts complex instrumentation that mostly follows
+ // the logic of the original code, but operates on "shadow" values.
+@@ -260,7 +244,7 @@ static void addMemorySanitizerPass(ModulePassManager &mpm,
+ }
+ static void addThreadSanitizerPass(ModulePassManager &mpm,
+ OptimizationLevel level
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ ,
+ ThinOrFullLTOPhase
+ #endif
+@@ -271,18 +255,13 @@ static void addThreadSanitizerPass(ModulePassManager &mpm,
+
+ static void addSanitizerCoveragePass(ModulePassManager &mpm,
+ OptimizationLevel level
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ ,
+ ThinOrFullLTOPhase
+ #endif
+ ) {
+-#if LDC_LLVM_VER >= 1600
+ mpm.addPass(SanitizerCoveragePass(
+ opts::getSanitizerCoverageOptions()));
+-#else
+- mpm.addPass(ModuleSanitizerCoveragePass(
+- opts::getSanitizerCoverageOptions()));
+-#endif
+ }
+ // Adds PGO instrumentation generation and use passes.
+ static void addPGOPasses(ModulePassManager &mpm, OptimizationLevel level) {
+@@ -291,13 +270,7 @@ static void addPGOPasses(ModulePassManager &mpm, OptimizationLevel level) {
+ options.NoRedZone = global.params.disableRedZone;
+ if (global.params.datafileInstrProf)
+ options.InstrProfileOutput = global.params.datafileInstrProf;
+- mpm.addPass(
+-#if LDC_LLVM_VER < 1800
+- InstrProfiling(options)
+-#else
+- InstrProfilingLoweringPass(options)
+-#endif // LDC_LLVM_VER < 1800
+- );
++ mpm.addPass(InstrProfilingLoweringPass(options));
+ } else if (opts::isUsingASTBasedPGOProfile()) {
+ // We are generating code with PGO profile information available.
+ // Do indirect call promotion from -O1
+@@ -310,7 +283,7 @@ static void addPGOPasses(ModulePassManager &mpm, OptimizationLevel level) {
+
+ static void addStripExternalsPass(ModulePassManager &mpm,
+ OptimizationLevel level
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ ,
+ ThinOrFullLTOPhase
+ #endif
+@@ -327,7 +300,7 @@ static void addStripExternalsPass(ModulePassManager &mpm,
+
+ static void addSimplifyDRuntimeCallsPass(ModulePassManager &mpm,
+ OptimizationLevel level
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ ,
+ ThinOrFullLTOPhase
+ #endif
+@@ -342,7 +315,7 @@ static void addSimplifyDRuntimeCallsPass(ModulePassManager &mpm,
+
+ static void addGarbageCollect2StackPass(ModulePassManager &mpm,
+ OptimizationLevel level
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ ,
+ ThinOrFullLTOPhase
+ #endif
+@@ -356,58 +329,48 @@ static void addGarbageCollect2StackPass(ModulePassManager &mpm,
+ }
+
+ #ifndef IN_JITRT
+-static llvm::Optional<PGOOptions> getPGOOptions() {
++static std::optional<PGOOptions> getPGOOptions() {
+ // FIXME: Do we have these anywhere?
+ bool debugInfoForProfiling = false;
+ bool pseudoProbeForProfiling = false;
+ if (opts::isInstrumentingForIRBasedPGO()) {
+ return PGOOptions(
+ global.params.datafileInstrProf, "", "",
+-#if LDC_LLVM_VER >= 1700
+ "" /*MemoryProfileUsePath*/,
+-#if LDC_LLVM_VER < 2200
++#if LLVM_VERSION_MAJOR < 22
+ llvm::vfs::getRealFileSystem(),
+-#endif
+ #endif
+ PGOOptions::PGOAction::IRInstr, PGOOptions::CSPGOAction::NoCSAction,
+-#if LDC_LLVM_VER >= 1900
++#if LLVM_VERSION_MAJOR >= 19
+ PGOOptions::ColdFuncOpt::Default,
+ #endif
+ debugInfoForProfiling, pseudoProbeForProfiling);
+ } else if (opts::isUsingIRBasedPGOProfile()) {
+ return PGOOptions(
+ global.params.datafileInstrProf, "", "",
+-#if LDC_LLVM_VER >= 1700
+ "" /*MemoryProfileUsePath*/,
+-#if LDC_LLVM_VER < 2200
++#if LLVM_VERSION_MAJOR < 22
+ llvm::vfs::getRealFileSystem(),
+-#endif
+ #endif
+ PGOOptions::PGOAction::IRUse, PGOOptions::CSPGOAction::NoCSAction,
+-#if LDC_LLVM_VER >= 1900
++#if LLVM_VERSION_MAJOR >= 19
+ PGOOptions::ColdFuncOpt::Default,
+ #endif
+ debugInfoForProfiling, pseudoProbeForProfiling);
+ } else if (opts::isUsingSampleBasedPGOProfile()) {
+ return PGOOptions(
+ global.params.datafileInstrProf, "", "",
+-#if LDC_LLVM_VER >= 1700
+ "" /*MemoryProfileUsePath*/,
+-#if LDC_LLVM_VER < 2200
++#if LLVM_VERSION_MAJOR < 22
+ llvm::vfs::getRealFileSystem(),
+-#endif
+ #endif
+ PGOOptions::PGOAction::SampleUse, PGOOptions::CSPGOAction::NoCSAction,
+-#if LDC_LLVM_VER >= 1900
++#if LLVM_VERSION_MAJOR >= 19
+ PGOOptions::ColdFuncOpt::Default,
+ #endif
+ debugInfoForProfiling, pseudoProbeForProfiling);
+ }
+-#if LDC_LLVM_VER < 1600
+- return None;
+-#else
+ return std::nullopt;
+-#endif
+ }
+ #endif // !IN_JITRT
+
+@@ -472,17 +435,9 @@ void runOptimizationPasses(llvm::Module *M, llvm::TargetMachine *TM) {
+ bool debugLogging = false;
+ ppo.Indent = false;
+ ppo.SkipAnalyses = false;
+-#if LDC_LLVM_VER < 1600
+- StandardInstrumentations si(debugLogging, /*VerifyEach=*/false, ppo);
+-#else
+ StandardInstrumentations si(M->getContext(), debugLogging, /*VerifyEach=*/false, ppo);
+-#endif
+
+-#if LDC_LLVM_VER < 1700
+- si.registerCallbacks(pic, &fam);
+-#else
+ si.registerCallbacks(pic, &mam);
+-#endif
+
+ PassBuilder pb(TM, getPipelineTuningOptions(optLevelVal, sizeLevelVal),
+ #ifdef IN_JITRT
+@@ -566,14 +521,14 @@ void runOptimizationPasses(llvm::Module *M, llvm::TargetMachine *TM) {
+
+ if (optLevelVal == 0) {
+ #ifdef IN_JITRT
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ const ThinOrFullLTOPhase ltoPrelink = ThinOrFullLTOPhase::None;
+ #else
+ const bool ltoPrelink = false;
+-#endif // LDC_LLVM_VER >= 2000
++#endif
+ mpm = pb.buildO0DefaultPipeline(level, ltoPrelink);
+-#else
+-#if LDC_LLVM_VER >= 2000
++#else // !IN_JITRT
++#if LLVM_VERSION_MAJOR >= 20
+ const ThinOrFullLTOPhase ltoPrelink =
+ opts::isUsingLTO()
+ ? (opts::isUsingThinLTO() ? ThinOrFullLTOPhase::ThinLTOPreLink
+@@ -581,15 +536,13 @@ void runOptimizationPasses(llvm::Module *M, llvm::TargetMachine *TM) {
+ : ThinOrFullLTOPhase::None;
+ #else
+ const bool ltoPrelink = opts::isUsingLTO();
+-#endif // LDC_LLVM_VER >= 2000
++#endif
+ mpm = pb.buildO0DefaultPipeline(level, ltoPrelink);
+-#if LDC_LLVM_VER >= 1700
+ } else if (opts::ltoFatObjects && opts::isUsingLTO()) {
+ mpm = pb.buildFatLTODefaultPipeline(level,
+ opts::isUsingThinLTO(),
+ opts::isUsingThinLTO()
+ );
+-#endif
+ } else if (opts::isUsingThinLTO()) {
+ mpm = pb.buildThinLTOPreLinkDefaultPipeline(level);
+ } else if (opts::isUsingLTO()) {
+diff --git a/gen/optimizer.h b/gen/optimizer.h
+index 8ba3521a28..688b654069 100644
+--- a/gen/optimizer.h
++++ b/gen/optimizer.h
+@@ -19,12 +19,6 @@
+
+ #include "llvm/Support/CommandLine.h"
+
+-namespace llvm {
+-#if LDC_LLVM_VER < 1800
+-using CodeGenOptLevel = llvm::CodeGenOpt::Level;
+-#endif
+-}
+-
+ namespace llvm {
+ class raw_ostream;
+ }
+diff --git a/gen/passes/DLLImportRelocation.cpp b/gen/passes/DLLImportRelocation.cpp
+index d2dc6c8369..288f71cbfd 100644
+--- a/gen/passes/DLLImportRelocation.cpp
++++ b/gen/passes/DLLImportRelocation.cpp
+@@ -272,11 +272,7 @@ bool DLLImportRelocation::run(Module &m) {
+ Impl impl(m);
+ bool hasChanged = false;
+
+-#if LDC_LLVM_VER >= 1700
+ for (GlobalVariable &global : m.globals()) {
+-#else
+- for (GlobalVariable &global : m.getGlobalList()) {
+-#endif
+ // TODO: thread-local globals would need to be initialized in a separate TLS
+ // ctor
+ if (!global.hasInitializer() || global.isThreadLocal())
+diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp
+index 4c9b82c0c8..a788789c86 100644
+--- a/gen/passes/GarbageCollect2Stack.cpp
++++ b/gen/passes/GarbageCollect2Stack.cpp
+@@ -24,7 +24,7 @@
+ #include "llvm/ADT/Statistic.h"
+ #include "llvm/ADT/StringMap.h"
+ #include "llvm/ADT/StringSwitch.h"
+-#if LDC_LLVM_VER >= 2100
++#if LLVM_VERSION_MAJOR >= 21
+ #include "llvm/IR/AbstractCallSite.h"
+ #endif
+ #include "llvm/IR/Constants.h"
+@@ -100,7 +100,7 @@ Value* FunctionInfo::promote(CallBase *CB, IRBuilder<> &B, const G2StackAnalysis
+ // FIXME: set alignment on alloca?
+ return new AllocaInst(
+ Ty, BB.getModule()->getDataLayout().getAllocaAddrSpace(), ".nongc_mem",
+-#if LDC_LLVM_VER >= 1900
++#if LLVM_VERSION_MAJOR >= 19
+ BB.begin()
+ #else
+ &(*BB.begin())
+@@ -352,7 +352,7 @@ static void RemoveCall(CallBase *CB, const G2StackAnalysis &A) {
+ // the dominator tree here.
+ if (auto Invoke = dyn_cast<InvokeInst>(static_cast<Instruction *>(CB))) {
+ BranchInst::Create(Invoke->getNormalDest(),
+-#if LDC_LLVM_VER >= 1900
++#if LLVM_VERSION_MAJOR >= 19
+ Invoke->getIterator()
+ #else
+ Invoke
+@@ -363,7 +363,7 @@ static void RemoveCall(CallBase *CB, const G2StackAnalysis &A) {
+
+ // Remove the runtime call.
+ if (A.CGNode) {
+-#if LDC_LLVM_VER >= 2100
++#if LLVM_VERSION_MAJOR >= 21
+ //FIXME: Look into using `LazyCallGraph` and the new pass manager
+ for (auto I = A.CGNode->begin(); ; I++) {
+ assert(I != A.CGNode->end() && "Cannot find callsite to remove!");
+@@ -755,7 +755,7 @@ bool isSafeToStackAllocate(BasicBlock::iterator Alloc, Value *V,
+ auto B = CB->arg_begin(), E = CB->arg_end();
+ for (auto A = B; A != E; ++A) {
+ if (A->get() == V) {
+-#if LDC_LLVM_VER >= 2100
++#if LLVM_VERSION_MAJOR >= 21
+ if (!(CB->paramHasAttr(A - B, llvm::Attribute::AttrKind::Captures) &&
+ capturesNothing(
+ CB->getParamAttr(A - B, llvm::Attribute::AttrKind::Captures)
+diff --git a/gen/passes/SimplifyDRuntimeCalls.cpp b/gen/passes/SimplifyDRuntimeCalls.cpp
+index 9dd672f20b..fd698cfeb9 100644
+--- a/gen/passes/SimplifyDRuntimeCalls.cpp
++++ b/gen/passes/SimplifyDRuntimeCalls.cpp
+@@ -160,7 +160,7 @@ Value *ArraySliceCopyOpt::CallOptimizer(Function *Callee, CallInst *CI,
+ Sz = (Int->getValue() * ElemSz->getValue()).getZExtValue();
+ }
+
+-#if LDC_LLVM_VER >= 2100
++#if LLVM_VERSION_MAJOR >= 21
+ llvm::LocationSize Sz2 =
+ (Sz == llvm::MemoryLocation::UnknownSize)
+ ? llvm::LocationSize::beforeOrAfterPointer()
+diff --git a/gen/pgo_ASTbased.cpp b/gen/pgo_ASTbased.cpp
+index 723cabdd95..c1937b5539 100644
+--- a/gen/pgo_ASTbased.cpp
++++ b/gen/pgo_ASTbased.cpp
+@@ -38,13 +38,17 @@ llvm::cl::opt<bool, false, opts::FlagParser<bool>> enablePGOIndirectCalls(
+ "pgo-indirect-calls", llvm::cl::ZeroOrMore, llvm::cl::Hidden,
+ llvm::cl::desc("(*) Enable PGO of indirect calls"),
+ llvm::cl::init(true));
+-}
+
+-#if LDC_LLVM_VER >= 1800
+-namespace llvm::support {
+- const auto little = llvm::endianness::little;
+-}
++uint64_t inLittleEndian(uint64_t x) {
++#if LLVM_VERSION_MAJOR >= 22
++ return llvm::support::endian::byte_swap<uint64_t>(x,
++ llvm::endianness::little);
++#else
++ return llvm::support::endian::byte_swap<uint64_t, llvm::endianness::little>(
++ x);
+ #endif
++}
++} // anonymous namespace
+
+ /// \brief Stable hasher for PGO region counters.
+ ///
+@@ -121,8 +125,7 @@ public:
+
+ // Pass through MD5 if enough work has built up.
+ if (Count && Count % NumTypesPerWord == 0) {
+- using namespace llvm::support;
+- uint64_t Swapped = endian::byte_swap<uint64_t, little>(Working);
++ uint64_t Swapped = inLittleEndian(Working);
+ MD5.update(llvm::ArrayRef<uint8_t>((uint8_t *)&Swapped, sizeof(Swapped)));
+ Working = 0;
+ }
+@@ -142,8 +145,7 @@ public:
+
+ // Check for remaining work in Working.
+ if (Working) {
+- using namespace llvm::support;
+- uint64_t Swapped = endian::byte_swap<uint64_t, little>(Working);
++ uint64_t Swapped = inLittleEndian(Working);
+ MD5.update(llvm::ArrayRef<uint8_t>((uint8_t *)&Swapped, sizeof(Swapped)));
+ }
+
+@@ -924,11 +926,7 @@ void CodeGenPGO::loadRegionCounts(llvm::IndexedInstrProfReader *PGOReader,
+ auto EC = RecordExpected.takeError();
+
+ if (EC) {
+-#if LDC_LLVM_VER >= 1700
+ auto IPE = std::get<0>(llvm::InstrProfError::take(std::move(EC)));
+-#else
+- auto IPE = llvm::InstrProfError::take(std::move(EC));
+-#endif
+ if (IPE == llvm::instrprof_error::unknown_function) {
+ IF_LOG Logger::println("No profile data for function: %s",
+ FuncName.c_str());
+diff --git a/gen/runtime.cpp b/gen/runtime.cpp
+index 5d3cbb6ebf..7ac04957e1 100644
+--- a/gen/runtime.cpp
++++ b/gen/runtime.cpp
+@@ -32,9 +32,7 @@
+ #include "ir/irtypefunction.h"
+ #include "llvm/Bitcode/BitcodeWriter.h"
+ #include "llvm/IR/Attributes.h"
+-#if LDC_LLVM_VER >= 1600
+ #include "llvm/Support/ModRef.h"
+-#endif
+ #include "llvm/IR/Module.h"
+ #include "llvm/Support/CommandLine.h"
+ #include "llvm/Support/MemoryBuffer.h"
+@@ -511,15 +509,9 @@ static void buildRuntimeModule() {
+ // `readonly`
+ {
+ auto addReadOnly = [&](AttrSet& a) {
+-#if LDC_LLVM_VER >= 1600
+ a = a.merge(AttrSet(llvm::AttributeList().addFnAttribute(
+ context, llvm::Attribute::getWithMemoryEffects(
+ context, llvm::MemoryEffects::readOnly()))));
+-#else
+- llvm::AttrBuilder ab(context);
+- ab.addAttribute(llvm::Attribute::ReadOnly);
+- a = a.addToFunction(ab);
+-#endif
+ };
+ addReadOnly(Attr_ReadOnly);
+ addReadOnly(Attr_ReadOnly_NoUnwind);
+@@ -542,7 +534,7 @@ static void buildRuntimeModule() {
+ {
+ auto addCapturesNone = [&](int extra, AttrSet& a) {
+ llvm::AttrBuilder ab(context);
+-#if LDC_LLVM_VER >= 2100
++#if LLVM_VERSION_MAJOR >= 21
+ ab.addCapturesAttr(llvm::CaptureInfo::none());
+ #else
+ ab.addAttribute(llvm::Attribute::NoCapture);
+diff --git a/gen/semantic-dcompute.cpp b/gen/semantic-dcompute.cpp
+index 5fe5f64e16..1cf3fb4843 100644
+--- a/gen/semantic-dcompute.cpp
++++ b/gen/semantic-dcompute.cpp
+@@ -92,6 +92,9 @@ struct DComputeSemanticAnalyser : public StoppableVisitor {
+ return;
+ }
+
++ if (!decl->type)
++ return;
++
+ if (decl->type->ty == TY::Taarray) {
+ error(decl->loc, "associative arrays not allowed in `@compute` code");
+ stop = true;
+diff --git a/gen/statements.cpp b/gen/statements.cpp
+index 1bdd3cef30..004fba0656 100644
+--- a/gen/statements.cpp
++++ b/gen/statements.cpp
+@@ -431,7 +431,11 @@ public:
+ if (llvm::ConstantInt *const_val = llvm::dyn_cast<llvm::ConstantInt>(cond_val)) {
+ Statement *executed = stmt->ifbody;
+ Statement *skipped = stmt->elsebody;
++#if LLVM_VERSION_MAJOR >= 21
++ if (const_val->isZero()) {
++#else
+ if (const_val->isZeroValue()) {
++#endif
+ std::swap(executed, skipped);
+ }
+ if (!containsLabel(skipped)) {
+@@ -441,7 +445,11 @@ public:
+ irs->DBuilder.EmitBlockStart(executed->loc);
+ }
+ // True condition, the branch is taken so emit counter increment.
++#if LLVM_VERSION_MAJOR >= 21
++ if (!const_val->isZero()) {
++#else
+ if (!const_val->isZeroValue()) {
++#endif
+ PGO.emitCounterIncrement(stmt);
+ }
+ if (executed) {
+@@ -1542,17 +1550,21 @@ public:
+ auto &PGO = irs->funcGen().pgo;
+ PGO.setCurrentStmt(stmt);
+
++ auto fd = irs->func()->decl;
++
++ const auto getFullAsmLabelString = [stmt, fd]() {
++ std::stringstream s;
++ printLabelName(s, mangleExact(fd), stmt->ident->toChars());
++ s << ":";
++ return s.str();
++ };
++
+ // if it's an inline asm label, we don't create a basicblock, just emit it
+ // in the asm
+ if (irs->asmBlock) {
+ auto a = new IRAsmStmt;
+- std::stringstream label;
+- printLabelName(label, mangleExact(irs->func()->decl),
+- stmt->ident->toChars());
+- label << ":";
+- a->code = label.str();
++ a->code = getFullAsmLabelString();
+ irs->asmBlock->s.push_back(a);
+- irs->asmBlock->internalLabels.push_back(stmt->ident);
+
+ // disable inlining
+ irs->func()->setNeverInline();
+@@ -1566,6 +1578,13 @@ public:
+ }
+
+ irs->ir->SetInsertPoint(labelBB);
++
++ // in a naked DMD-style inline-asm function, create an assembly label too
++ // (so that inline-asm can jump to it directly)
++ if (fd->isNaked()) {
++ DtoInlineAsmExpr(stmt->loc, getFullAsmLabelString(), "", {}, {},
++ irs->ir->getVoidTy());
++ }
+ }
+
+ PGO.emitCounterIncrement(stmt);
+diff --git a/gen/target.cpp b/gen/target.cpp
+index 7f77923a39..16622a9922 100644
+--- a/gen/target.cpp
++++ b/gen/target.cpp
+@@ -83,10 +83,8 @@ llvm::Type *getRealType(const llvm::Triple &triple) {
+
+ case Triple::riscv32:
+ case Triple::riscv64:
+-#if LDC_LLVM_VER >= 1600
+ case Triple::loongarch32:
+ case Triple::loongarch64:
+-#endif // LDC_LLVM_VER >= 1600
+ return LLType::getFP128Ty(ctx);
+
+ case Triple::wasm32:
+diff --git a/gen/tocall.cpp b/gen/tocall.cpp
+index edc5ef837c..c1b0bc1b3c 100644
+--- a/gen/tocall.cpp
++++ b/gen/tocall.cpp
+@@ -1045,7 +1045,7 @@ DValue *DtoCallFunction(Loc loc, Type *resulttype, DValue *fnval,
+ if (cf->isIntrinsic()) { // override intrinsic attrs
+ attrlist =
+ llvm::Intrinsic::getAttributes(gIR->context(), cf->getIntrinsicID()
+-#if LDC_LLVM_VER >= 2100
++#if LLVM_VERSION_MAJOR >= 21
+ ,cf->getFunctionType()
+ #endif
+ );
+diff --git a/gen/toconstelem.cpp b/gen/toconstelem.cpp
+index c32f6f4a7b..9ded6dddc0 100644
+--- a/gen/toconstelem.cpp
++++ b/gen/toconstelem.cpp
+@@ -522,11 +522,7 @@ public:
+ // extend i1 to i8
+ if (c->getType()->isIntegerTy(1)) {
+ LLType *I8PtrTy = LLType::getInt8Ty(p->context());
+-#if LDC_LLVM_VER < 1800
+- c = llvm::ConstantExpr::getZExt(c, I8PtrTy);
+-#else
+ c = llvm::ConstantFoldCastOperand(llvm::Instruction::ZExt, c, I8PtrTy, *gDataLayout);
+-#endif
+ }
+ varInits[e->sd->fields[i]] = c;
+ }
+diff --git a/gen/toir.cpp b/gen/toir.cpp
+index 0a22d1b0d5..e746c6bbcd 100644
+--- a/gen/toir.cpp
++++ b/gen/toir.cpp
+@@ -1016,11 +1016,7 @@ public:
+ }
+
+ static llvm::PointerType * getWithSamePointeeType(llvm::PointerType *p, unsigned addressSpace) {
+-#if LDC_LLVM_VER >= 1700
+ return llvm::PointerType::get(p->getContext(), addressSpace);
+-#else
+- return llvm::PointerType::getWithSamePointeeType(p, addressSpace);
+-#endif
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+diff --git a/gen/trycatchfinally.cpp b/gen/trycatchfinally.cpp
+index b486228874..510e0c0ac6 100644
+--- a/gen/trycatchfinally.cpp
++++ b/gen/trycatchfinally.cpp
+@@ -323,7 +323,7 @@ CleanupScope::CleanupScope(llvm::BasicBlock *beginBlock,
+ }
+
+ namespace {
+-#if LDC_LLVM_VER >= 1900
++#if LLVM_VERSION_MAJOR >= 19
+ llvm::BasicBlock::iterator getTerminatorPos(llvm::BasicBlock *bb) {
+ auto it = bb->rbegin().getReverse();
+ assert(it->isTerminator());
+diff --git a/gen/uda.cpp b/gen/uda.cpp
+index a89c2c1e75..63f84e5699 100644
+--- a/gen/uda.cpp
++++ b/gen/uda.cpp
+@@ -210,11 +210,7 @@ void applyAttrAllocSize(StructLiteralExp *sle, IrFunction *irFunc) {
+ if (numArgIdx >= 0) {
+ builder.addAllocSizeAttr(llvmSizeIdx, llvmNumIdx);
+ } else {
+-#if LDC_LLVM_VER < 1600
+- builder.addAllocSizeAttr(llvmSizeIdx, llvm::Optional<unsigned>());
+-#else
+ builder.addAllocSizeAttr(llvmSizeIdx, std::optional<unsigned>());
+-#endif
+ }
+
+ llvm::Function *func = irFunc->getLLVMFunc();
+@@ -307,10 +303,6 @@ void applyAttrTarget(StructLiteralExp *sle, llvm::Function *func,
+ // The current implementation here does not do any checking of the specified
+ // string and simply passes all to llvm.
+
+-#if LDC_LLVM_VER >= 1800
+- #define startswith starts_with
+-#endif
+-
+ checkStructElems(sle, {Type::tstring});
+ llvm::StringRef targetspec = getFirstElemString(sle);
+
+@@ -338,20 +330,20 @@ void applyAttrTarget(StructLiteralExp *sle, llvm::Function *func,
+ if (s.empty())
+ continue;
+
+- if (s.startswith("arch=")) {
++ if (s.starts_with("arch=")) {
+ // TODO: be smarter than overwriting the previous arch= setting
+ CPU = s.drop_front(5);
+ continue;
+ }
+- if (s.startswith("tune=")) {
++ if (s.starts_with("tune=")) {
+ // clang 3.8 ignores tune= too
+ continue;
+ }
+- if (s.startswith("fpmath=")) {
++ if (s.starts_with("fpmath=")) {
+ // TODO: implementation; clang 3.8 ignores fpmath= too
+ continue;
+ }
+- if (s.startswith("no-")) {
++ if (s.starts_with("no-")) {
+ std::string f = (std::string("-") + s.drop_front(3)).str();
+ features.emplace_back(std::move(f));
+ continue;
+@@ -370,10 +362,6 @@ void applyAttrTarget(StructLiteralExp *sle, llvm::Function *func,
+ llvm::join(features.begin(), features.end(), ","));
+ irFunc->targetFeaturesOverridden = true;
+ }
+-
+-#if LDC_LLVM_VER >= 1800
+- #undef startswith
+-#endif
+ }
+
+ void applyAttrAssumeUsed(IRState &irs, StructLiteralExp *sle,
+@@ -438,11 +426,7 @@ bool parseCallingConvention(llvm::StringRef name,
+ .Case("intel_ocl_bicc", llvm::CallingConv::Intel_OCL_BI)
+ .Case("x86_64_sysvcc", llvm::CallingConv::X86_64_SysV)
+ .Case("win64cc", llvm::CallingConv::Win64)
+-#if LDC_LLVM_VER >= 1800
+ .Case("webkit_jscc", llvm::CallingConv::WASM_EmscriptenInvoke)
+-#else
+- .Case("webkit_jscc", llvm::CallingConv::WebKit_JS)
+-#endif
+ .Case("anyregcc", llvm::CallingConv::AnyReg)
+ .Case("preserve_mostcc", llvm::CallingConv::PreserveMost)
+ .Case("preserve_allcc", llvm::CallingConv::PreserveAll)
+@@ -450,13 +434,8 @@ bool parseCallingConvention(llvm::StringRef name,
+ .Case("swiftcc", llvm::CallingConv::Swift)
+ .Case("swifttailcc", llvm::CallingConv::SwiftTail)
+ .Case("x86_intrcc", llvm::CallingConv::X86_INTR)
+-#if LDC_LLVM_VER >= 1700
+ .Case("hhvmcc", llvm::CallingConv::DUMMY_HHVM)
+ .Case("hhvm_ccc", llvm::CallingConv::DUMMY_HHVM_C)
+-#else
+- .Case("hhvmcc", llvm::CallingConv::HHVM)
+- .Case("hhvm_ccc", llvm::CallingConv::HHVM_C)
+-#endif
+ .Case("cxx_fast_tlscc", llvm::CallingConv::CXX_FAST_TLS)
+ .Case("amdgpu_vs", llvm::CallingConv::AMDGPU_VS)
+ .Case("amdgpu_gfx", llvm::CallingConv::AMDGPU_Gfx)
+diff --git a/gen/variable_lifetime.cpp b/gen/variable_lifetime.cpp
+index ed0b772dbf..717760881e 100644
+--- a/gen/variable_lifetime.cpp
++++ b/gen/variable_lifetime.cpp
+@@ -53,7 +53,7 @@ void LocalVariableLifetimeAnnotator::addLocalVariable(llvm::AllocaInst *address,
+
+ // Emit lifetime start
+ irs.CreateCallOrInvoke(getLLVMLifetimeStartFn(),
+-#if LDC_LLVM_VER >= 2200
++#if LLVM_VERSION_MAJOR >= 22
+ {address},
+ #else
+ {size, address},
+@@ -67,7 +67,7 @@ void LocalVariableLifetimeAnnotator::popScope() {
+ return;
+
+ for (const auto &var : scopes.back().variables) {
+-#if LDC_LLVM_VER < 2200
++#if LLVM_VERSION_MAJOR < 22
+ auto size = var.first;
+ #endif
+ auto address = var.second;
+@@ -75,7 +75,7 @@ void LocalVariableLifetimeAnnotator::popScope() {
+ assert(address);
+
+ irs.CreateCallOrInvoke(getLLVMLifetimeEndFn(),
+-#if LDC_LLVM_VER >= 2200
++#if LLVM_VERSION_MAJOR >= 22
+ {address},
+ #else
+ {size, address},
+@@ -92,7 +92,7 @@ llvm::Function *LocalVariableLifetimeAnnotator::getLLVMLifetimeStartFn() {
+ return lifetimeStartFunction;
+
+ lifetimeStartFunction = llvm::Intrinsic::
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ getOrInsertDeclaration(
+ #else
+ getDeclaration(
+@@ -108,7 +108,7 @@ llvm::Function *LocalVariableLifetimeAnnotator::getLLVMLifetimeEndFn() {
+ return lifetimeEndFunction;
+
+ lifetimeEndFunction = llvm::Intrinsic::
+-#if LDC_LLVM_VER >= 2000
++#if LLVM_VERSION_MAJOR >= 20
+ getOrInsertDeclaration(
+ #else
+ getDeclaration(
+diff --git a/ir/iraggr.cpp b/ir/iraggr.cpp
+index 28d2ddddd2..94275dafce 100644
+--- a/ir/iraggr.cpp
++++ b/ir/iraggr.cpp
+@@ -63,10 +63,6 @@ bool IrAggr::useDLLImport() const {
+ //////////////////////////////////////////////////////////////////////////////
+
+ LLGlobalVariable *IrAggr::getInitSymbol(bool define) {
+-#if LDC_LLVM_VER >= 1800
+- #define startswith starts_with
+-#endif
+-
+ if (!init) {
+ const auto irMangle = getIRMangledInitSymbolName(aggrdecl);
+
+@@ -74,7 +70,7 @@ LLGlobalVariable *IrAggr::getInitSymbol(bool define) {
+ // special.
+ auto cd = aggrdecl->isClassDeclaration();
+ const bool isBuiltinTypeInfo =
+- cd && llvm::StringRef(cd->ident->toChars()).startswith("TypeInfo_");
++ cd && llvm::StringRef(cd->ident->toChars()).starts_with("TypeInfo_");
+
+ // Only declare the symbol if it isn't yet, otherwise the init symbol of
+ // built-in TypeInfos may clash with an existing base-typed forward
+@@ -108,10 +104,6 @@ LLGlobalVariable *IrAggr::getInitSymbol(bool define) {
+ }
+
+ return init;
+-
+-#if LDC_LLVM_VER >= 1800
+- #undef startswith
+-#endif
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+diff --git a/ir/irtypestruct.cpp b/ir/irtypestruct.cpp
+index cac45683e0..644c196021 100644
+--- a/ir/irtypestruct.cpp
++++ b/ir/irtypestruct.cpp
+@@ -72,7 +72,7 @@ IrTypeStruct *IrTypeStruct::get(StructDeclaration *sd) {
+
+ // For ldc.dcomptetypes.Pointer!(uint n,T),
+ // emit { T addrspace(gIR->dcomputetarget->mapping[n])* }
+- llvm::Optional<DcomputePointer> p;
++ std::optional<DcomputePointer> p;
+
+ if (gIR->dcomputetarget && (p = toDcomputePointer(sd))) {
+ // Translate the virtual dcompute address space into the real one for
+diff --git a/runtime/druntime/src/__importc_builtins.di b/runtime/druntime/src/__importc_builtins.di
+index 476852cd82..3c870c1842 100644
+--- a/runtime/druntime/src/__importc_builtins.di
++++ b/runtime/druntime/src/__importc_builtins.di
+@@ -12,6 +12,10 @@
+
+ module __builtins;
+
++import core.stdc.config : c_long, c_ulong;
++import core.checkedint : adds, subs, muls;
++
++
+ /* gcc relies on internal __builtin_xxxx functions and templates to
+ * accomplish <stdarg.h>. D does the same thing with templates in core.stdc.stdarg.
+ * Here, we redirect the gcc builtin declarations to the equivalent
+@@ -101,6 +105,11 @@ version (DigitalMars)
+ alias __builtin_fabsf = imported!"core.stdc.math".fabsf;
+ alias __builtin_fabsl = imported!"core.stdc.math".fabsl;
+
++ alias __builtin_memcmp = imported!"core.stdc.string".memcmp;
++ alias __builtin_memcpy = imported!"core.stdc.string".memcpy;
++ alias __builtin_memmove = imported!"core.stdc.string".memmove;
++ alias __builtin_memset = imported!"core.stdc.string".memset;
++
+ ushort __builtin_bswap16()(ushort value)
+ {
+ return cast(ushort) (((value >> 8) & 0xFF) | ((value << 8) & 0xFF00U));
+@@ -161,6 +170,11 @@ else version (LDC)
+ alias __builtin_fabsf = imported!"ldc.intrinsics".llvm_fabs!float;
+ alias __builtin_fabsl = imported!"ldc.intrinsics".llvm_fabs!real;
+
++ alias __builtin_memcmp = imported!"core.stdc.string".memcmp;
++ alias __builtin_memcpy = imported!"ldc.intrinsics".llvm_memcpy!size_t;
++ alias __builtin_memmove = imported!"ldc.intrinsics".llvm_memmove!size_t;
++ alias __builtin_memset = imported!"ldc.intrinsics".llvm_memset!size_t;
++
+ alias __builtin_bswap16 = imported!"ldc.intrinsics".llvm_bswap!ushort;
+ alias __builtin_bswap32 = imported!"ldc.intrinsics".llvm_bswap!uint;
+ alias __builtin_bswap64 = imported!"ldc.intrinsics".llvm_bswap!ulong;
+@@ -172,7 +186,8 @@ else version (LDC)
+
+ alias __uint128_t = imported!"core.int128".Cent;
+
+- alias __builtin_alloca = imported!"core.stdc.stdlib".alloca;
++ pragma(LDC_alloca)
++ extern(C) void* __builtin_alloca(size_t size) nothrow @nogc pure;
+
+ // gcc builtins:
+
+@@ -200,3 +215,233 @@ version (CRuntime_Glibc) version (AArch64)
+ alias __Float32x4_t = __vector(float[4]);
+ alias __Float64x2_t = __vector(double[2]);
+ }
++
++// https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html
++
++private bool overflowOp(alias op, T1, T2, T3)(T1 a, T2 b, ref T3 res)
++{
++ bool overflow = false;
++ res = op(a, b, overflow);
++ return overflow;
++}
++
++private T builtin_opc(alias op, T)(T a, T b, T carry_in, ref T carry_out)
++{
++ carry_out = op(a, b, a) | op(a, carry_in, a);
++ return a;
++}
++
++pragma(inline, true)
++{
++ bool __builtin_add_overflow(T1, T2, T3)(T1 a, T2 b, T3* res) => overflowOp!(adds, T1, T2, T3)(a, b, *res);
++ bool __builtin_sub_overflow(T1, T2, T3)(T1 a, T2 b, T3* res) => overflowOp!(subs, T1, T2, T3)(a, b, *res);
++ bool __builtin_mul_overflow(T1, T2, T3)(T1 a, T2 b, T3* res) => overflowOp!(muls, T1, T2, T3)(a, b, *res);
++ bool __builtin_add_overflow_p(T1, T2, T3)(T1 a, T2 b, T3 c) => overflowOp!(adds, T1, T2, T3)(a, b, res);
++ bool __builtin_sub_overflow_p(T1, T2, T3)(T1 a, T2 b, T3 c) => overflowOp!(subs, T1, T2, T3)(a, b, res);
++ bool __builtin_mul_overflow_p(T1, T2, T3)(T1 a, T2 b, T3 c) => overflowOp!(muls, T1, T2, T3)(a, b, res);
++ bool __builtin_sadd_overflow ()(int a, int b, int* res) => overflowOp!(adds, int , int , int )(a, b, *res);
++ bool __builtin_saddl_overflow ()(c_long a, c_long b, c_long* res) => overflowOp!(adds, c_long , c_long , c_long )(a, b, *res);
++ bool __builtin_saddll_overflow()(long a, long b, long* res) => overflowOp!(adds, long , long , long )(a, b, *res);
++ bool __builtin_uadd_overflow ()(uint a, uint b, uint* res) => overflowOp!(adds, uint , uint , uint )(a, b, *res);
++ bool __builtin_uaddl_overflow ()(c_ulong a, c_ulong b, c_ulong* res) => overflowOp!(adds, c_ulong, c_ulong, c_ulong)(a, b, *res);
++ bool __builtin_uaddll_overflow()(ulong a, ulong b, ulong* res) => overflowOp!(adds, ulong , ulong , ulong )(a, b, *res);
++ bool __builtin_ssub_overflow ()(int a, int b, int* res) => overflowOp!(subs, int , int , int )(a, b, *res);
++ bool __builtin_ssubl_overflow ()(c_long a, c_long b, c_long* res) => overflowOp!(subs, c_long , c_long , c_long )(a, b, *res);
++ bool __builtin_ssubll_overflow()(long a, long b, long* res) => overflowOp!(subs, long , long , long )(a, b, *res);
++ bool __builtin_usub_overflow ()(uint a, uint b, uint* res) => overflowOp!(subs, uint , uint , uint )(a, b, *res);
++ bool __builtin_usubl_overflow ()(c_ulong a, c_ulong b, c_ulong* res) => overflowOp!(subs, c_ulong, c_ulong, c_ulong)(a, b, *res);
++ bool __builtin_usubll_overflow()(ulong a, ulong b, ulong* res) => overflowOp!(subs, ulong , ulong , ulong )(a, b, *res);
++ bool __builtin_smul_overflow ()(int a, int b, int* res) => overflowOp!(muls, int , int , int )(a, b, *res);
++ bool __builtin_smull_overflow ()(c_long a, c_long b, c_long* res) => overflowOp!(muls, c_long , c_long , c_long )(a, b, *res);
++ bool __builtin_smulll_overflow()(long a, long b, long* res) => overflowOp!(muls, long , long , long )(a, b, *res);
++ bool __builtin_umul_overflow ()(uint a, uint b, uint* res) => overflowOp!(muls, uint , uint , uint )(a, b, *res);
++ bool __builtin_umull_overflow ()(c_ulong a, c_ulong b, c_ulong* res) => overflowOp!(muls, c_ulong, c_ulong, c_ulong)(a, b, *res);
++ bool __builtin_umulll_overflow()(ulong a, ulong b, ulong* res) => overflowOp!(muls, ulong , ulong , ulong )(a, b, *res);
++
++ uint __builtin_addc ()(uint a, uint b, uint carry_in, uint* carry_out) => builtin_opc!(adds, uint )(a, b, carry_in, *carry_out);
++ ulong __builtin_addcl ()(ulong a, ulong b, uint carry_in, ulong* carry_out) => builtin_opc!(adds, ulong)(a, b, carry_in, *carry_out);
++ ulong __builtin_addcll()(ulong a, ulong b, ulong carry_in, ulong* carry_out) => builtin_opc!(adds, ulong)(a, b, carry_in, *carry_out);
++ uint __builtin_subc ()(uint a, uint b, uint carry_in, uint* carry_out) => builtin_opc!(subs, uint )(a, b, carry_in, *carry_out);
++ ulong __builtin_subcl ()(ulong a, ulong b, uint carry_in, ulong* carry_out) => builtin_opc!(subs, ulong)(a, b, carry_in, *carry_out);
++ ulong __builtin_subcll()(ulong a, ulong b, ulong carry_in, ulong* carry_out) => builtin_opc!(subs, ulong)(a, b, carry_in, *carry_out);
++}
++
++unittest {
++ int r1;
++ assert(__builtin_sadd_overflow(2147483647, 1, &r1) == true);
++ assert(__builtin_sadd_overflow(1, 1, &r1) == false);
++
++ assert(__builtin_ssub_overflow(-2147483648, 1, &r1) == true);
++ assert(__builtin_ssub_overflow(5, 3, &r1) == false);
++
++ assert(__builtin_smul_overflow(2000000000, 2, &r1) == true);
++ assert(__builtin_smul_overflow(10, 20, &r1) == false);
++
++ uint ur;
++ assert(__builtin_uadd_overflow(0xFFFFFFFFu, 1u, &ur) == true);
++ assert(__builtin_uadd_overflow(10u, 20u, &ur) == false);
++
++ assert(__builtin_usub_overflow(0u, 1u, &ur) == true);
++ assert(__builtin_usub_overflow(20u, 10u, &ur) == false);
++
++ assert(__builtin_umul_overflow(0xFFFFFFFFu, 2u, &ur) == true);
++ assert(__builtin_umul_overflow(10u, 20u, &ur) == false);
++
++ uint carry;
++ uint rr = __builtin_addc(1u, 1u, 0u, &carry);
++ assert(rr == 2);
++ assert(carry == 0);
++
++ rr = __builtin_addc(0xFFFFFFFFu, 1u, 0u, &carry);
++ assert(carry == 1);
++
++ rr = __builtin_subc(1u, 1u, 0u, &carry);
++ assert(rr == 0);
++ assert(carry == 0);
++
++ rr = __builtin_subc(0u, 1u, 0u, &carry);
++ assert(carry == 1);
++}
++
++private U signbit(T, U)(T x)
++{
++ T arg = x;
++ return cast(U)arg >> (T.sizeof * 8 - 1);
++}
++
++pragma(inline, true)
++{
++ // https://gcc.gnu.org/onlinedocs/gcc/Bit-Operation-Builtins.html
++ import core.bitop : popcnt, bsr, bsf, rol, ror;
++ private int clz(T)(T x) => bsr(x) ^ ((int.sizeof * 8)-1);
++
++ int __builtin_clz()(uint x) => clz!uint(x);
++ int __builtin_clzl()(c_ulong x) => clz!c_ulong(x);
++ int __builtin_clzll()(ulong x) => clz!ulong(x);
++ int __builtin_clzg(T)(T arg) => clz(arg);
++
++ int __builtin_ctz()(uint x) => bsf(x);
++ int __builtin_ctzl()(c_ulong x) => bsf(x);
++ int __builtin_ctzll()(ulong x) => bsf(x);
++ int __builtin_ctzg(T)(T arg) => bsf(arg);
++
++ int __builtin_clrsb()(int x) => signbit!(int, uint)(x) ? clz!uint(~x) - 1 : clz!uint(x) -1;
++ int __builtin_clrsbl()(c_long x) => signbit!(c_long, c_ulong)(x) ? clz!c_ulong(~x) - 1 : clz!c_ulong(x) -1;
++ int __builtin_clrsbll()(long x) => signbit!(long, ulong)(x) ? clz!ulong(~x) - 1 : clz!ulong(x) -1;
++ int __builtin_clrsbg(T, U)(T arg) => signbit!(T, U)(arg) ? clz!U(~x) - 1 : clz!U(x) -1;
++
++ int __builtin_ffs()(int x) => x ? bsf(x) + 1 : 0;
++ int __builtin_ffsl()(c_long x) => x ? bsf(x) + 1 : 0;
++ int __builtin_ffsll()(long x) => x ? bsf(x) + 1 : 0;
++ int __builtin_ffsg(T)(T arg) => arg ? bsf(arg) + 1 : 0;
++
++ int __builtin_popcount()(uint x) => popcnt(x);
++ int __builtin_popcountl()(c_ulong x) => popcnt(x);
++ int __builtin_popcountll()(ulong x) => popcnt(x);
++ int __builtin_popcountg(T)(T arg) => popcnt(arg);
++
++ int __builtin_parity()(uint x) => popcnt(x) % 2;
++ int __builtin_parityl()(c_ulong) => popcnt(x) % 2;
++ int __builtin_parityll()(ulong) => popcnt(x) % 2;
++ int __builtin_parityg(T)(T arg) => popcnt(arg) % 2;
++
++ T __builtin_stdc_bit_ceil(T)(T arg) => arg <= 1 ? T(1) : T(2) << (T.sizeof * 8 - 1 - clz(arg - 1));
++ T __builtin_stdc_bit_floor(T)(T arg) => arg == 0 ? T(0) : T(1) << (T.sizeof * 8 - 1 - clz(arg));
++ uint __builtin_stdc_bit_width(T)(T arg) => T.sizeof * 8 - clz(arg);
++ uint __builtin_stdc_count_ones (T)(T arg) => popcnt(arg);
++ uint __builtin_stdc_count_zeros(T)(T arg) => popcnt(cast(T) ~arg);
++ uint __builtin_stdc_first_leading_one (T)(T arg) => clz( arg) + 1U;
++ uint __builtin_stdc_first_leading_zero (T)(T arg) => clz(~arg) + 1U;
++ uint __builtin_stdc_first_trailing_one (T)(T arg) => ctz( arg) + 1U;
++ uint __builtin_stdc_first_trailing_zero(T)(T arg) => ctz(~arg) + 1U;
++ uint __builtin_stdc_has_single_bit(T)(T arg) => popcnt(arg) == 1;
++ T1 __builtin_stdc_rotate_left (T1, T2)(T1 arg1, T2 arg2) => roL(arg1, arg2);
++ T1 __builtin_stdc_rotate_right(T1, T2)(T1 arg1, T2 arg2) => ror(arg1, arg2);
++}
++
++unittest
<Skipped 6647 lines>
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/packages/ldc.git/commitdiff/0cb953558f29825500e177b8a703b59ff88ff4a5
More information about the pld-cvs-commit
mailing list