Commit 7cd8a0a0a47 for php.net

commit 7cd8a0a0a471b12bd2940fbb3b1406d9d77ec911
Author: Marc <m@pyc.ac>
Date:   Mon Apr 20 21:20:59 2026 +0700

    [Windows] Improve clang-cl support (#21618)

    * fix compiling (intl) C++ extensions --with-toolset=clang

    * guard the double extern "C" { ... } for clang-only

    * don't error on warnings with clang

    * enable compiler intrinsics for clang too

    * make --enable-compiler-intrinsics=no same as disabled

    * add clang windows x64 ZTS test

    * remove check if it's clang

    * debloat logs from common warning

    * @shivammathur feedback

    * remove CI changes

    * rebase on https://github.com/php/php-src/pull/21630

    * fix default to asan => false

    * bring back explicit error

    * fix typo

diff --git a/.github/matrix.php b/.github/matrix.php
index dec8c7d249c..18c2ef1269b 100644
--- a/.github/matrix.php
+++ b/.github/matrix.php
@@ -144,12 +144,15 @@ function select_jobs($repository, $trigger, $nightly, $labels, $php_version, $re
         $jobs['SOLARIS'] = true;
     }
     if ($all_jobs || !$no_jobs || $test_windows) {
-        $jobs['WINDOWS']['matrix'] = $all_variations
-            ? ['include' => [
-                ['asan' => true, 'opcache' => true, 'x64' => true, 'zts' => true],
-                ['asan' => false, 'opcache' => false, 'x64' => false, 'zts' => false],
-            ]]
-            : ['include' => [['asan' => false, 'opcache' => true, 'x64' => true, 'zts' => true]]];
+        $matrix = [['asan' => false, 'opcache' => true, 'x64' => true, 'zts' => true]];
+        if ($all_variations) {
+            $matrix[] = ['asan' => true, 'opcache' => true, 'x64' => true, 'zts' => true];
+            $matrix[] = ['asan' => false, 'opcache' => false, 'x64' => false, 'zts' => false];
+            if (version_compare($php_version, '8.5', '>=')) {
+                $matrix[] = ['asan' => false, 'opcache' => true, 'x64' => true, 'zts' => true, 'clang' => true];
+            }
+        }
+        $jobs['WINDOWS']['matrix'] = ['include' => $matrix];
         $jobs['WINDOWS']['config'] = version_compare($php_version, '8.4', '>=')
             ? ['vs_crt_version' => 'vs17']
             : ['vs_crt_version' => 'vs16'];
diff --git a/.github/scripts/windows/build_task.bat b/.github/scripts/windows/build_task.bat
index b6547945184..1177cef3be4 100644
--- a/.github/scripts/windows/build_task.bat
+++ b/.github/scripts/windows/build_task.bat
@@ -26,12 +26,18 @@ if %errorlevel% neq 0 exit /b 3
 if "%THREAD_SAFE%" equ "0" set ADD_CONF=%ADD_CONF% --disable-zts
 if "%INTRINSICS%" neq "" set ADD_CONF=%ADD_CONF% --enable-native-intrinsics=%INTRINSICS%
 if "%ASAN%" equ "1" set ADD_CONF=%ADD_CONF% --enable-sanitizer --enable-debug-pack
+if "%CLANG_TOOLSET%" equ "1" set ADD_CONF=%ADD_CONF% --with-toolset=clang

 rem C4018: comparison: signed/unsigned mismatch
 rem C4146: unary minus operator applied to unsigned type
 rem C4244: type conversion, possible loss of data
 rem C4267: 'size_t' type conversion, possible loss of data
-set CFLAGS=/W3 /WX /wd4018 /wd4146 /wd4244 /wd4267
+if "%CLANG_TOOLSET%" equ "1" (
+	rem Clang is much stricter than MSVC, produces too many warnings that would fail the build with /WX
+	set CFLAGS=/W3 /wd4018 /wd4146 /wd4244 /wd4267
+) else (
+	set CFLAGS=/W3 /WX /wd4018 /wd4146 /wd4244 /wd4267
+)

 cmd /c configure.bat ^
 	--enable-snapshot-build ^
diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml
index 90bcafd1630..4905dcb9ccb 100644
--- a/.github/workflows/test-suite.yml
+++ b/.github/workflows/test-suite.yml
@@ -940,7 +940,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix: ${{ fromJson(inputs.branch).jobs.WINDOWS.matrix }}
-    name: "WINDOWS_${{ matrix.x64 && 'X64' || 'X86' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}${{ matrix.asan && '_ASAN' || ''}}"
+    name: "WINDOWS_${{ matrix.x64 && 'X64' || 'X86' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}${{ matrix.asan && '_ASAN' || ''}}${{ matrix.clang && '_CLANG' || ''}}"
     runs-on: windows-2022
     env:
       PHP_BUILD_CACHE_BASE_DIR: C:\build-cache
@@ -954,6 +954,7 @@ jobs:
       PARALLEL: -j2
       OPCACHE: "${{ matrix.opcache && '1' || '0' }}"
       ASAN: "${{ matrix.asan && '1' || '0' }}"
+      CLANG_TOOLSET: "${{ matrix.clang && '1' || '0' }}"
     steps:
       - name: git config
         run: git config --global core.autocrlf false && git config --global core.eol lf
diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h
index 80d6cbad044..ea13552c837 100644
--- a/TSRM/TSRM.h
+++ b/TSRM/TSRM.h
@@ -175,9 +175,14 @@ TSRM_API bool tsrm_is_managed_thread(void);
 #define TSRMG_BULK_STATIC(id, type)	((type) (*((void ***) TSRMLS_CACHE))[TSRM_UNSHUFFLE_RSRC_ID(id)])
 #define TSRMG_FAST_STATIC(offset, type, element)	(TSRMG_FAST_BULK_STATIC(offset, type)->element)
 #define TSRMG_FAST_BULK_STATIC(offset, type)	((type) (((char*) TSRMLS_CACHE)+(offset)))
+#ifdef __cplusplus
+#define TSRMLS_MAIN_CACHE_EXTERN() extern "C" { extern TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR; }
+#define TSRMLS_CACHE_EXTERN() extern "C" { extern TSRM_TLS void *TSRMLS_CACHE; }
+#else
 #define TSRMLS_MAIN_CACHE_EXTERN() extern TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR;
-#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;
 #define TSRMLS_CACHE_EXTERN() extern TSRM_TLS void *TSRMLS_CACHE;
+#endif
+#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;
 #define TSRMLS_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE = NULL;
 #define TSRMLS_CACHE_UPDATE() TSRMLS_CACHE = tsrm_get_ls_cache()
 #define TSRMLS_CACHE _tsrm_ls_cache
diff --git a/win32/build/confutils.js b/win32/build/confutils.js
index 36484e71f8f..fd1e9ce0be1 100644
--- a/win32/build/confutils.js
+++ b/win32/build/confutils.js
@@ -3361,7 +3361,7 @@ function toolset_setup_common_cflags()

 		ADD_FLAG("CFLAGS", "/Zc:wchar_t");
 	} else if (CLANG_TOOLSET) {
-		ADD_FLAG("CFLAGS", "-Wno-deprecated-declarations");
+		ADD_FLAG("CFLAGS", "-Wno-deprecated-declarations -Wno-microsoft-enum-forward-reference");
 		if (TARGET_ARCH == 'x86') {
 			ADD_FLAG('CFLAGS', '-m32');
 		} else {
@@ -3395,24 +3395,25 @@ function toolset_setup_intrinsic_cflags()
 	/* From oldest to newest. */
 	var scale = new Array("sse", "sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "avx", "avx2", "avx512");

-	if (VS_TOOLSET) {
-		if ("disabled" == PHP_NATIVE_INTRINSICS) {
-			ERROR("Can't enable intrinsics, --with-codegen-arch passed with an incompatible option. ")
-		}
+	if ("disabled" == PHP_NATIVE_INTRINSICS) {
+        ERROR("Can't enable intrinsics, --with-codegen-arch passed with an incompatible option. ")
+	}

-		if (TARGET_ARCH == 'arm64') {
-			/* arm64 supports neon */
-			configure_subst.Add("PHP_SIMD_SCALE", 'NEON');
-			/* all officially supported arm64 cpu supports crc32 (TODO: to be confirmed) */
-			AC_DEFINE('HAVE_ARCH64_CRC32', 1);
-			return;
-		}
+	if (TARGET_ARCH == 'arm64') {
+		/* arm64 supports neon */
+		configure_subst.Add("PHP_SIMD_SCALE", 'NEON');
+		/* all officially supported arm64 cpu supports crc32 (TODO: to be confirmed) */
+		AC_DEFINE('HAVE_ARCH64_CRC32', 1);
+		return;
+	}

-		if ("no" == PHP_NATIVE_INTRINSICS || "yes" == PHP_NATIVE_INTRINSICS) {
-			PHP_NATIVE_INTRINSICS = default_enabled;
-		}
+    // if --enable-native-intrisics is not specified, it's "no" - enable default
+	if ("no" == PHP_NATIVE_INTRINSICS || "yes" == PHP_NATIVE_INTRINSICS) {
+		PHP_NATIVE_INTRINSICS = default_enabled;
+	}

-		if ("all" == PHP_NATIVE_INTRINSICS) {
+	if ("all" == PHP_NATIVE_INTRINSICS) {
+		if (VS_TOOLSET) {
 			var list = (new VBArray(avail.Keys())).toArray();

 			for (var i in list) {
@@ -3421,46 +3422,61 @@ function toolset_setup_intrinsic_cflags()

 			/* All means all. __AVX__, __AVX2__, and __AVX512*__ are defined by compiler. */
 			ADD_FLAG("CFLAGS","/arch:AVX512");
-			configure_subst.Add("PHP_SIMD_SCALE", "AVX512");
-		} else {
-			var list = PHP_NATIVE_INTRINSICS.split(",");
-			var j = 0;
-			for (var k = 0; k < scale.length; k++) {
-				for (var i = 0; i < list.length; i++) {
-					var it = list[i].toLowerCase();
-					if (scale[k] == it) {
-						j = k > j ? k : j;
-					} else if (!avail.Exists(it) && "avx512" != it && "avx2" != it && "avx" != it) {
-						WARNING("Unknown intrinsic name '" + it + "' ignored");
-					}
-				}
-			}
-			if (TARGET_ARCH == 'x86') {
-				/* SSE2 is currently the default on 32-bit. It could change later,
-					for now no need to pass it. But, if SSE only was chosen,
-					/arch:SSE is required. */
-				if ("sse" == scale[j]) {
-					ADD_FLAG("CFLAGS","/arch:SSE");
+		} else if (CLANG_TOOLSET) {
+			ADD_FLAG("CFLAGS","-mavx512f -mavx512cd -mavx512bw -mavx512dq -mavx512vl");
+		}
+		configure_subst.Add("PHP_SIMD_SCALE", "AVX512");
+	} else {
+		var list = PHP_NATIVE_INTRINSICS.split(",");
+		var j = 0;
+		for (var k = 0; k < scale.length; k++) {
+			for (var i = 0; i < list.length; i++) {
+				var it = list[i].toLowerCase();
+				if (scale[k] == it) {
+					j = k > j ? k : j;
+				} else if (!avail.Exists(it) && "avx512" != it && "avx2" != it && "avx" != it) {
+					WARNING("Unknown intrinsic name '" + it + "' ignored");
 				}
 			}
-			configure_subst.Add("PHP_SIMD_SCALE", scale[j].toUpperCase());
-			/* There is no explicit way to enable intrinsics between SSE3 and SSE4.2.
-				The declared macros therefore won't affect the code generation,
-				but will enable the guarded code parts. */
-			if ("avx512" == scale[j]) {
-				ADD_FLAG("CFLAGS","/arch:AVX512");
-				j -= 3;
-			} else if ("avx2" == scale[j]) {
-				ADD_FLAG("CFLAGS","/arch:AVX2");
-				j -= 2;
-			} else if ("avx" == scale[j]) {
-				ADD_FLAG("CFLAGS","/arch:AVX");
-				j -= 1;
-			}
+		}
+		if (TARGET_ARCH == 'x86') {
+			/* SSE2 is currently the default on 32-bit. It could change later,
+				for now no need to pass it. But, if SSE only was chosen,
+				/arch:SSE is required. */
+			if ("sse" == scale[j]) {
+				ADD_FLAG("CFLAGS", VS_TOOLSET ? "/arch:SSE" : "-msse");
+			}
+		}
+		configure_subst.Add("PHP_SIMD_SCALE", scale[j].toUpperCase());
+		if ("avx512" == scale[j]) {
+			ADD_FLAG("CFLAGS", VS_TOOLSET ? "/arch:AVX512" : "-mavx512f -mavx512cd -mavx512bw -mavx512dq -mavx512vl");
+			j -= 3;
+		} else if ("avx2" == scale[j]) {
+			ADD_FLAG("CFLAGS", VS_TOOLSET ? "/arch:AVX2" : "-mavx2");
+			j -= 2;
+		} else if ("avx" == scale[j]) {
+			ADD_FLAG("CFLAGS", VS_TOOLSET ? "/arch:AVX" : "-mavx");
+			j -= 1;
+		}
+		if (VS_TOOLSET) {
+			/* MSVC has no explicit way to enable intrinsics between SSE3 and SSE4.2.
+			   The declared macros won't affect code generation, but will enable
+			   the guarded code parts. */
 			for (var i = 0; i <= j; i++) {
 				var it = scale[i];
 				AC_DEFINE(avail.Item(it), 1);
 			}
+		} else if (CLANG_TOOLSET) {
+			/* clang supports -m flags for each SSE level and auto-defines
+			   the corresponding __SSE*__ macros. Pass the highest requested
+			   level; clang implicitly enables all lower levels. */
+			var clang_flag_map = {
+				"sse": "-msse", "sse2": "-msse2", "sse3": "-msse3",
+				"ssse3": "-mssse3", "sse4.1": "-msse4.1", "sse4.2": "-msse4.2"
+			};
+			if (clang_flag_map[scale[j]]) {
+				ADD_FLAG("CFLAGS", clang_flag_map[scale[j]]);
+			}
 		}
 	}
 }