Skip to content

Commit 2a010c7

Browse files
committed
fix(windows-clang): survive real-world spc rc18 behavior; verify against a full local run
Fixes found by running the lane end to end locally with stock spc.exe v3.0.0-pgo-rc18 (PHP 8.5.7, LLVM 22.1.8) and building ePHPm against the resulting SDK: - configure: force PHP_TOOLSET = clang in the config.w32 body instead of flipping the declaration default. spc passes --disable-all, and conf_process_args resets every unseen configure arg to no (-> vs), so a default flip silently produces an MSVC/CALL artifact. Also keep the injected comment free of the literal ARG_WITH/ARG_ENABLE strings: buildconf's preamble extractor greps for them and would copy comment text into configure.js as broken JS. - spc build: repeat --dl-custom-local. The build phase runs its own downloader pass and otherwise re-resolves php-src, silently replacing the patched tree with the stock tarball. - VM-kind gate: the probe member is Zend\zend_execute.obj (zend_vm_execute.h is #included by zend_execute.c; there is no zend_vm_execute.obj). - Package: strip HAVE_PRESERVE_NONE and the PHP_HAVE_BUILTIN_* defines from BOTH staged config headers (php_config.h and config.w32.h - zend_config.h includes the latter). With them present, rust-bindgen panics on libclang calling convention 20 (preserve_none) and MSVC consumers emit unlinkable __builtin_expect calls. Stripped, consumers get the same portable CALL-view the MSVC-lane headers give while the engine runs TAILCALL internally - verified: ePHPm links and runs the clang SDK with zero source changes, 1.6-1.7x faster on CPU-bound PHP than the MSVC SDK build.
1 parent f2dc5eb commit 2a010c7

1 file changed

Lines changed: 51 additions & 6 deletions

File tree

.github/workflows/build.yml

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,7 +1287,10 @@ jobs:
12871287
# a pre-patched php-src via --custom-local (source is used in place,
12881288
# never re-extracted — see spc's ArtifactExtractor). Two patches, both
12891289
# hard-fail if the anchor text has drifted:
1290-
# 1. win32/build/config.w32: default toolset "vs" -> "clang".
1290+
# 1. win32/build/config.w32: force PHP_TOOLSET = "clang" right
1291+
# before toolset_option_handle(). An ARG_WITH default flip does
1292+
# NOT work: spc passes --disable-all, and conf_process_args
1293+
# resets every unseen configure arg to "no" (-> "vs").
12911294
# 2. win32/build/config.w32: define HAVE_PRESERVE_NONE inside the
12921295
# CLANG_TOOLSET block (clang >= 19, x64). zend_portability.h
12931296
# detects musttail on its own; with both defined,
@@ -1299,12 +1302,16 @@ jobs:
12991302
$cfg = "php-src-clang\win32\build\config.w32"
13001303
$content = Get-Content $cfg -Raw
13011304
1302-
$anchor1 = '"is Visual Studio. Use others at your own risk.", "vs");'
1305+
$anchor1 = 'toolset_option_handle();'
13031306
if (-not $content.Contains($anchor1)) {
1304-
Write-Error "config.w32 toolset-default anchor not found - php-src build system changed, refusing to build (would silently produce an MSVC/CALL artifact)"
1307+
Write-Error "config.w32 toolset anchor not found - php-src build system changed, refusing to build (would silently produce an MSVC/CALL artifact)"
13051308
exit 1
13061309
}
1307-
$content = $content.Replace($anchor1, '"is Visual Studio. Use others at your own risk.", "clang");')
1310+
# NB: the comment must not contain the literal string ARG_WITH or
1311+
# ARG_ENABLE — buildconf's preamble extractor greps for those and
1312+
# would copy the comment line into configure.js as broken JS.
1313+
$force = "/* Injected by php-sdk's windows-x86_64-clang lane: --disable-all resets`n every unseen configure arg to `"no`", so a declaration-default can never`n select the toolset. Force it before the handler runs. */`nPHP_TOOLSET = `"clang`";`ntoolset_option_handle();"
1314+
$content = $content.Replace($anchor1, $force)
13081315
13091316
$anchor2 = 'AC_DEFINE("PHP_HAVE_BUILTIN_SMULLL_OVERFLOW", 1, "Define to 1 if the compiler supports ''__builtin_smulll_overflow''.");'
13101317
if (-not $content.Contains($anchor2)) {
@@ -1394,8 +1401,13 @@ jobs:
13941401
if ($spDir -and (Test-Path "$($spDir.FullName)\perl\bin\perl.exe")) {
13951402
$env:PATH = "$($spDir.FullName)\perl\bin;$env:PATH"
13961403
}
1404+
# --dl-custom-local must be repeated here: the build phase runs its
1405+
# own downloader pass (--dl-*) and re-resolves php-src without it,
1406+
# silently replacing the patched tree with the stock tarball.
1407+
$phpSrc = "$PWD\php-src-clang"
13971408
spc build ${{ env.WIN_PHP_EXTENSIONS }} `
13981409
--dl-with-php=${{ inputs.php_version }} `
1410+
--dl-custom-local "php-src:$phpSrc" `
13991411
--build-embed `
14001412
--enable-zts `
14011413
--no-strip `
@@ -1412,9 +1424,11 @@ jobs:
14121424
$lib = Get-ChildItem -Recurse -Filter "php8embed.lib" | Select-Object -First 1
14131425
if (-not $lib) { Write-Error "php8embed.lib not found"; exit 1 }
14141426
1427+
# zend_vm_kind() lives in zend_execute.obj (Zend/zend_vm_execute.h
1428+
# is #included by zend_execute.c — there is no zend_vm_execute.obj).
14151429
$vcvars = "C:\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
1416-
$obj = cmd /c "call `"$vcvars`" >nul 2>&1 && lib /nologo /list `"$($lib.FullName)`"" | Select-String "zend_vm_execute.obj" | Select-Object -First 1
1417-
if (-not $obj) { Write-Error "zend_vm_execute.obj not found in php8embed.lib"; exit 1 }
1430+
$obj = cmd /c "call `"$vcvars`" >nul 2>&1 && lib /nologo /list `"$($lib.FullName)`"" | Select-String "\\Zend\\zend_execute.obj" | Select-Object -First 1
1431+
if (-not $obj) { Write-Error "Zend\zend_execute.obj not found in php8embed.lib"; exit 1 }
14181432
$member = $obj.Line.Trim()
14191433
Write-Host "==> Extracting $member"
14201434
cmd /c "call `"$vcvars`" >nul 2>&1 && lib /nologo `"/extract:$member`" /out:zend_vm_execute_probe.obj `"$($lib.FullName)`""
@@ -1499,6 +1513,37 @@ jobs:
14991513
$extHeaderCount = (Get-ChildItem -Path $extDest -Recurse -Filter "*.h").Count
15001514
Write-Host "==> Staged $extHeaderCount ext/*/*.h headers"
15011515
1516+
# Strip HAVE_PRESERVE_NONE from the STAGED php_config.h. The built
1517+
# lib runs the TAILCALL VM internally (the gate above proves it),
1518+
# but the preserve_none opcode-handler calling convention is not
1519+
# representable by MSVC consumers (__attribute__ is a no-op path:
1520+
# they compile the CALL-view types) nor by bindgen (libclang
1521+
# calling convention 20 panics rust-bindgen 0.72 — found building
1522+
# ePHPm). The embed API surface does not expose handler pointers,
1523+
# so the portable CALL-view header is safe for every consumer and
1524+
# keeps clang- and MSVC-consumer views identical.
1525+
# Both staged copies: php_config.h AND config.w32.h (Zend's
1526+
# zend_config.h includes the latter — stripping only one is not
1527+
# enough; found via a second bindgen panic).
1528+
foreach ($configHeader in @("sdk/include/php/main/php_config.h", "sdk/include/php/main/config.w32.h")) {
1529+
$cfgContent = Get-Content $configHeader -Raw
1530+
if ($cfgContent -notmatch '(?m)^#define HAVE_PRESERVE_NONE 1\r?\n') {
1531+
Write-Error "HAVE_PRESERVE_NONE not found in $configHeader - did the TAILCALL configure define go missing?"
1532+
exit 1
1533+
}
1534+
$cfgContent = $cfgContent -replace '(?m)^#define HAVE_PRESERVE_NONE 1\r?\n', "/* HAVE_PRESERVE_NONE intentionally removed from the staged SDK header (see the php-sdk windows-x86_64-clang lane). */`n"
1535+
# Same treatment for the clang-only __builtin_* capability defines
1536+
# (config.w32's CLANG_TOOLSET block AC_DEFINEs them): Zend headers
1537+
# gate STATIC INLINE fast paths on these, so an MSVC consumer
1538+
# compiling the SDK headers emits calls to __builtin_expect /
1539+
# __builtin_*_overflow it can never link (LNK2001 — found
1540+
# building ePHPm). MSVC consumers get the same fallback paths the
1541+
# MSVC-lane headers give them.
1542+
$cfgContent = $cfgContent -replace '(?m)^#define (PHP_HAVE_BUILTIN_\w+) 1\r?\n', "/* `$1 removed for MSVC consumers (clang-only builtin; see windows-x86_64-clang lane) */`n"
1543+
Set-Content -Path $configHeader -Value $cfgContent -Encoding ascii -NoNewline
1544+
Write-Host "==> Stripped clang-only defines from $configHeader"
1545+
}
1546+
15021547
# Version guard (see MSVC lane for the incident this prevents).
15031548
$versionHeader = "sdk/include/php/main/php_version.h"
15041549
$match = Select-String -Path $versionHeader -Pattern '#define PHP_VERSION "([^"]+)"'

0 commit comments

Comments
 (0)