Fix: AOT link fails for EVERY binary — duplicate memset declaration - #31894
Conversation
master cannot produce a working native binary. Measured on 8084f14: aot-smoke: 0 passed, 8 failed Every case dies the same way: /opt/llvm9/ld: loop.bin.o: in function `phpc_gc_collect_cycles_impl': main:(.text+0x47afe): undefined reference to `memset.1' GcCollectCyclesRuntime::ensureExternal() called module->addFunction() whenever lookupFunction() threw, without first checking getNamedFunction(). LibcExtern adds `memset` to the module and gives it a BODY via implementMemsetBody(), but does not always leave it in the context's function registry — so lookupFunction() throws while the symbol already exists. addFunction() on an existing name does not fail. LLVM silently renames the second one to `memset.1`, which carries no body, and the link ends with an undefined reference from phpc_gc_collect_cycles_impl. Fixed by reusing the existing declaration, matching the getNamedFunction()-first pattern LibcExtern already uses. Verified in php-compiler:22.04-dev: aot-smoke: 8 passed, 0 failed (was 0 passed, 8 failed) Two defects that looked like separate AOT bugs were this one wearing a costume, and both now match Zend exactly: var_dump(7) was abort rc=134 -> int(7) (new Exception)->getMessage() was segfault -> msg=[] One genuine bug survives and is NOT addressed here: an inherited typed property default is not initialized under AOT. class A { public string $p = 'hi'; } class B extends A {} echo (new B)->p; zend: hi aot: Uncaught Error: Typed property A::$p must not be accessed before initialization Filed separately. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Maintainer review (do not merge yet)Verified locally in The Same LLVM rename: One remaining site (not in this diff): // lib/JIT/Builtin/StringSscanfByRef.php ensureRuntimeHelpers()
try {
$context->lookupFunction('memcpy');
} catch (\Throwable) {
$fn = $context->module->addFunction('memcpy', ...); // no getNamedFunction
$context->registerFunction('memcpy', $fn);
}Same Please generalize GHA |
…1910) (#31911) #31894 fixed memset versioning in GcCollectCyclesRuntime only. ObStorageLlvm, OutputRewriteVarsStorage, UrlRewriterApplyRuntime, StringSscanfByRef, and StringZlibJit still called addFunction('memcpy') when lookupFunction threw while LibcExtern already owned the module symbol — LLVM silently versioned to memcpy.N with no body and every default-cache AOT link failed. Add LibcExtern::ensureExternalDecl() (getNamedFunction-first) and delegate peer ensureExternal helpers. Refresh three prelinked helper units that embed the stale memcpy.1 references. Verified in php-compiler:22.04-dev: ./script/aot-smoke.sh → 8 passed, 0 failed (cold, no local helper cache) php bin/compile.php -o /tmp/x test/repro/dom_xpath_relative_aot_31738.php → ok maintainer_gap_dom_contains_null_aot_31791.php AOT → 0\n0 Co-authored-by: PHP Compiler Ext — DOM & XML <agent@purhur.local> Co-authored-by: Cursor <cursoragent@cursor.com>
). (#31985) The unstable garbage reads were probe-reported before #31894 fixed the memset.1 link failure; repro is stable on master now. Lock with VM/AOT compliance, 10× AOT repeat unit test, and differential @differential-repeat: 10 case. Co-authored-by: PurHur <PurHur@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…dJIT module-local (#32273) (#32278) Canonical i8*/size_t ensureMallocFamily stops NestedJIT void*/i64 decls from minting malloc.1 (the #31894/#32122 class). User-script alloc stays MemoryManager __mm__*. Co-authored-by: PurHur <PurHur@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
lookupFunction-miss then addFunction() silently renamed libc snprintf when the symbol already existed in the module, which is the Module.php:180 class from #31894/#32122. Route snprintf through LibcExtern::ensureSnprintf and register getNamedFunction hits for the remaining formatter/GC/parse_url leaves. Co-authored-by: PurHur <PurHur@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
master cannot produce a working native binary. Measured on
8084f14996:Every case dies identically:
Cause
GcCollectCyclesRuntime::ensureExternal()calledmodule->addFunction()wheneverlookupFunction()threw, without first checkinggetNamedFunction():LibcExternaddsmemsetto the module and gives it a body viaimplementMemsetBody(), but does not always leave it in the context's function registry — solookupFunction()throws while the symbol already exists.addFunction()on an existing name does not fail. LLVM silently renames the second one tomemset.1, which carries no body, and the link ends with an undefined reference.Fixed by reusing the existing declaration, matching the
getNamedFunction()-first patternLibcExternalready uses.Verified
php-compiler:22.04-dev,script/aot-smoke.sh:Two defects that looked like independent AOT bugs were this one wearing a costume — both now match Zend exactly:
var_dump(7)int(7)(new Exception)->getMessage()msg=[]Not fixed here
One genuine bug survives — an inherited typed-property default is not initialized under AOT:
Filed separately.
🤖 Generated with Claude Code