-
Notifications
You must be signed in to change notification settings - Fork 55
[JSC] FTL inline-cache patchpoints must declare fpTempRegister as clobbered #536
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| //@ runDefault("--useConcurrentJIT=0") | ||
| // The IndexedTypedArrayFloat{16,32,64}Load inline cache stubs call AssemblyHelpers::purifyNaN(), | ||
| // which materializes PNaN in MacroAssembler::fpTempRegister (xmm15 on x86-64, q31 on ARM64). | ||
| // FTL lets Air allocate that register, so a double that is live across a generic GetByVal | ||
| // patchpoint could be silently replaced with NaN once the IC attached a typed-array load case. | ||
|
|
||
| function f(arr, idx, d) { | ||
| // Enough doubles live across the access that Air has to use every FP register, including fpTempRegister. | ||
| const a0 = d + 0.5, a1 = d + 1.5, a2 = d + 2.5, a3 = d + 3.5, a4 = d + 4.5, a5 = d + 5.5, a6 = d + 6.5, a7 = d + 7.5; | ||
| const b0 = d * 0.5, b1 = d * 1.5, b2 = d * 2.5, b3 = d * 3.5, b4 = d * 4.5, b5 = d * 5.5, b6 = d * 6.5, b7 = d * 7.5; | ||
| const x = arr[idx]; | ||
| return x + a0 * b0 + a1 * b1 + a2 * b2 + a3 * b3 + a4 * b4 + a5 * b5 + a6 * b6 + a7 * b7 + (a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7) - (b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7); | ||
| } | ||
| noInline(f); | ||
|
|
||
| function expected(idx, d) { | ||
| const a0 = d + 0.5, a1 = d + 1.5, a2 = d + 2.5, a3 = d + 3.5, a4 = d + 4.5, a5 = d + 5.5, a6 = d + 6.5, a7 = d + 7.5; | ||
| const b0 = d * 0.5, b1 = d * 1.5, b2 = d * 2.5, b3 = d * 3.5, b4 = d * 4.5, b5 = d * 5.5, b6 = d * 6.5, b7 = d * 7.5; | ||
| return (idx + 0.25) + a0 * b0 + a1 * b1 + a2 * b2 + a3 * b3 + a4 * b4 + a5 * b5 + a6 * b6 + a7 * b7 + (a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7) - (b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7); | ||
| } | ||
| noInline(expected); | ||
| noFTL(expected); | ||
|
|
||
| for (const TypedArray of [Float64Array, Float32Array, Float16Array]) { | ||
| const ta = new TypedArray(8); | ||
| for (let i = 0; i < 8; i++) | ||
| ta[i] = i + 0.25; | ||
| const other = { 0: 0.25, 1: 1.25, 2: 2.25, 3: 3.25, 4: 4.25, 5: 5.25, 6: 6.25, 7: 7.25 }; | ||
| for (let i = 0; i < 200000; i++) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 (optional) New stress test hardcodes Extended reasoning...JSTests/CLAUDE.md is Verification: nit — The rule violation is real. /home/claude/webkit/JSTests/README.md:17-20 states "New tests are required to adhere to the following rules: ... 2. Use |
||
| const arr = (i & 1023) === 0 ? other : ta; // keep the GetByVal generic so it goes through an IC | ||
| const idx = i & 7; | ||
| const d = (i % 97) * 0.25; | ||
| const r = f(arr, idx, d); | ||
| const e = expected(idx, d); | ||
| if (r !== e) | ||
| throw new Error(TypedArray.name + ": i=" + i + " got " + r + " expected " + e); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: oven-sh/WebKit
Length of output: 558
🌐 Web query:
"ftl-get-by-val-ic-typed-array-float-load-clobbers-fp-temp-register.js" WebKit💡 Result:
The filename "ftl-get-by-val-ic-typed-array-float-load-clobbers-fp-temp-register.js" is a JavaScript microbenchmark file used within the WebKit project (specifically the JavaScriptCore engine) to test and prevent regressions related to floating-point (FP) temporary register clobbering in the FTL (Faster Than Light) JIT compiler [1][2]. In WebKit's FTL JIT, Inline Caches (ICs) are used to speed up property and array accesses. A known technical challenge in this architecture is ensuring that these ICs do not inadvertently overwrite (clobber) registers that the surrounding compiled code expects to remain unchanged, particularly "scratch" or temporary registers like fpTempRegister [1][2]. When the FTL JIT compiles a TypedArray floating-point load, it must ensure that the generated IC code properly manages register usage [1][2]. If an IC improperly clobbers a floating-point temporary register, it can lead to incorrect program execution, such as corrupted floating-point values. Microbenchmarks like the one mentioned are designed to trigger specific edge cases—in this instance, likely involving TypedArray floating-point loads in GetByVal operations—to verify that the compiler's register allocation and IC implementation correctly preserve these critical registers [1][2]. WebKit engineers use these tests to ensure that changes to the JIT (such as transitioning or replacing IC mechanisms) do not introduce such regressions [1][2].
Citations:
🌐 Web query:
site:github.com/WebKit/WebKit "ftl-get-by-val-ic-typed-array-float-load-clobbers-fp-temp-register"💡 Result:
The term "ftl-get-by-val-ic-typed-array-float-load-clobbers-fp-temp-register" appears to refer to a specific issue or historical bug tracking identifier within the JavaScriptCore (JSC) engine of WebKit, related to how the FTL (Faster Than Light) JIT compiler handles floating-point typed array loads [1][2][3]. In the context of JSC's JIT infrastructure, "FTL GetByVal" refers to the optimized compilation of property access operations (e.g., array[index]) [1][4]. When FTL performs these accesses on typed arrays (like Float32Array or Float64Array), it must generate efficient machine code [1][2][3]. Historically, issues have arisen where the generated machine code for these loads inadvertently "clobbered" (overwrote) temporary registers—specifically floating-point (FP) registers—that were being used by the surrounding code [3]. This typically occurred because: 1. Typed array accesses often require a "slow path" (a fallback mechanism) when unexpected conditions are met (e.g., out-of-bounds access or incompatible data types) [1][3][5]. 2. Register allocation in the FTL compiler must be precise [1]. If a specific register was designated as a temporary ("temp") register for floating-point calculations, any operation that unknowingly used that register (clobbered it) would lead to data corruption or incorrect execution results [3]. 3. Updates to the JIT architecture, such as merging loading functionalities or adding new FTL nodes (like MultiGetByVal), have involved rigorous management of register sets and calling conventions to ensure that registers are preserved correctly across these high-performance operations [1][6][7]. While the exact string does not appear as a current public documentation page, it follows the naming convention for internal JIT compiler bugs in WebKit, where developers often use descriptive, hyphenated names to identify specific scenarios where register clobbering occurs during the compilation of typed array loads [1][3]. These issues are addressed by refining the register usage policy in the FTL's
LowerDFGToB3phase, which is responsible for translating the DFG (Data Flow Graph) IR to the B3 (Bare Bones Backend) intermediate representation [1][2][5].Citations:
🌐 Web query:
WebKit changeset 281615 281892 ftl get by val typed array float load clobbers fp temp register💡 Result:
WebKit changesets 281615 and 281892 are part of a series of updates related to modernizing the PutByVal Inline Cache (IC) mechanism in the JavaScriptCore engine [1][2]. While these changes focused on transitioning and replacing IC code to handle property registers more safely—specifically ensuring that ICs do not clobber registers unless properly flushed—the specific issue of a Typed Array float load clobbering an floating-point (FP) temporary register in the FTL JIT is a distinct, though related, category of compiler register management challenge [1][2]. There is no public evidence explicitly linking these specific changesets (281615/281892) to the resolution of an issue where an "FTL get by val typed array float load clobbers fp temp register." These changesets primarily addressed PutByVal IC modernization [1][2]. It is common in the development of the FTL JIT for compiler passes to encounter issues where instructions, such as those generated for typed array loads, incorrectly interfere with temporary registers—particularly when those instructions are generated by LLVM (or B3 in more recent WebKit versions) and must be reconciled with JavaScriptCore's register allocator requirements [3]. For technical investigations into register clobbering within the FTL JIT, developers typically examine: 1. Register pressure and allocation policies within the B3 compiler phase [4][3]. 2. Code generated for typed array operations (e.g., compileGetByVal or similar methods in FTLLowerDFGToB3.cpp) [4][5]. 3. The definition of clobberize rules, which instruct the compiler on which registers are modified by specific operations [4]. If you are investigating a specific regression or bug, it is recommended to search the WebKit Bugzilla for keywords such as "FTL", "register clobber", or the specific typed array type involved (e.g., "Float64Array"), as register management issues are often tracked through individual bug reports [6][7].
Citations:
🏁 Script executed:
Repository: oven-sh/WebKit
Length of output: 558
🌐 Web query:
WebKit GitHub pull request 43579 ftl-get-by-val-ic-typed-array-float-load-clobbers-fp-temp-register.js💡 Result:
The file name "ftl-get-by-val-ic-typed-array-float-load-clobbers-fp-temp-register.js" appears to be a descriptive reference to a specific test case or issue rather than a standard WebKit pull request identifier. WebKit Pull Request
#43579is titled "[JSC] Add MultiGetByVal FTL node" and was merged on April 4, 2025 [1]. This pull request introduced theMultiGetByValnode to the FTL (Faster Than Light) JIT compiler in JavaScriptCore (JSC) to handle multiple array types efficiently by avoiding the "clobber world" side effects associated with generic Inline Cache (IC) lookups [1]. While PR#43579addresses the performance and optimization of TypedArray accesses in the FTL JIT, the specific filename you mentioned likely corresponds to a regression test or a specialized test case created to verify the fix for a register clobbering bug associated with floating-point (FP) operations in TypedArray loads. It is common practice in the WebKit project to name test files descriptively based on the specific behavior or bug they are intended to validate (e.g.,ftl-get-by-val-ic-typed-array-float-load-clobbers-fp-temp-register.js). For authoritative details regarding the changes introduced by PR#43579and any associated test files, you can refer to the official WebKit GitHub repository [1].Citations:
🌐 Web query:
site:github.com/WebKit/WebKit/blob "fpTempRegister" "Float64Array" "GetByVal"💡 Result:
In the context of the JavaScriptCore (JSC) engine used in WebKit, fpTempRegister, Float64Array, and GetByVal refer to distinct components of the Just-In-Time (JIT) compilation infrastructure. These terms do not typically appear together as a single command or API, but rather interact within the engine's implementation of low-level property access. fpTempRegister: This is a designated floating-point temporary register used by the MacroAssembler to perform intermediate operations [1][2]. Because JIT compilers must manage registers carefully, the MacroAssembler defines specific registers (like fpTempRegister) that the compiler can use as scratch space for calculations or data movement without needing to preserve their state across complex operations [1][3]. GetByVal: This is a fundamental bytecode operation (op_get_by_val) in JavaScriptCore used to perform dynamic property access, such as array indexing (e.g., array[index]) [4][5]. When the JIT compiler encounters this operation, it generates efficient machine code (the "fast path") to handle the access, frequently utilizing Inline Caches (ICs) to speed up subsequent accesses of the same type [4][6]. Float64Array: This is a standard JavaScript TypedArray. When the JIT engine processes a GetByVal operation on a Float64Array, it may use specialized machine code sequences to load the floating-point value directly from memory into a register. During the generation of this machine code, the MacroAssembler may utilize an fpTempRegister to temporarily hold data if the operation requires shuffling, conversion, or complex memory addressing [1][3]. In summary, fpTempRegister is a low-level assembler utility, while GetByVal is a higher-level bytecode instruction representing property access, and Float64Array is the data structure being accessed. They intersect when the JIT compiler generates machine code for a Float64Array index operation, using temporary registers to facilitate the load process [4][1].
Citations:
🌐 Web query:
WebKit MacroAssemblerARM64 fpTempRegister q31 B3 register allocator💡 Result:
In the WebKit JavaScriptCore engine, the ARM64 MacroAssembler defines
fpTempRegisterasq31[1][2]. This register serves as a designated floating-point/SIMD scratch register used by the assembler for temporary operations [1][3]. Regarding its relationship with the B3 register allocator: 1. Role in Assembly:fpTempRegister(q31) is explicitly used in MacroAssembler implementations to perform temporary operations, such as those required forcountPopulationor floating-point conversions, where a scratch register is needed to hold intermediate values [1][3]. 2. B3 Register Allocator Interaction: While B3 Air (the JIT's backend IR) performs register allocation to map abstract temporaries (Tmps) to physical registers, it must respect the registers reserved by the MacroAssembler [4][5]. BecausefpTempRegisteris used by the MacroAssembler for scratch operations, it is generally treated as "macro-clobbered" or reserved, meaning the register allocator must avoid using it for long-lived values that would be corrupted by these scratch operations [6]. 3. Register Allocator Context: B3 Air uses several register allocation strategies (including Iterated Register Coalescing and recently introduced Greedy approaches) [7][5]. Regardless of the specific allocator variant, the system relies on precise modeling of register usage—where registers likeq31are marked as clobbered or restricted to ensure they remain available for the JIT's internal low-level operations [6][4].Citations:
🌐 Web query:
WebKit "fpTempRegister" "q31"💡 Result:
In the WebKit project, specifically within the JavaScriptCore engine's ARM64 assembler implementation, fpTempRegister is defined as ARM64Registers::q31 [1][2][3]. This constant is defined in Source/JavaScriptCore/assembler/MacroAssemblerARM64.h [1][2]. It serves as a dedicated temporary register for floating-point operations during machine code generation [1][4]. By using a specific register like q31 as a scratch or temporary register, the assembler can perform intermediate calculations or move data without inadvertently overwriting other registers currently holding live data [1][4]. The usage of such temporary registers is a standard pattern in WebKit's MacroAssembler architecture to facilitate cross-platform JIT compilation; for example, the x86_64 implementation uses xmm15 for the same purpose [4], while other architectures like ARMv7 utilize their own designated registers [5].
Citations:
🌐 Web query:
site:github.com/WebKit/WebKit "macroClobberedFP" q31💡 Result:
In the context of WebKit's JavaScriptCore (JSC) engine, "macroClobberedFP" refers to a set of Floating Point Registers (FPRs) that are clobbered (overwritten) by the MacroAssembler's internal operations [1]. The reference to "q31" indicates a specific register within the ARM64 architecture [2][3]. In JavaScriptCore's MacroAssembler for ARM64, the register q31 is explicitly defined as the temporary floating-point register (fpTempRegister) [3]: static constexpr ARM64Registers::FPRegisterID fpTempRegister = ARM64Registers::q31; Functions like RegisterSet::macroClobberedFPRs utilize this definition to identify registers that are volatile and subject to being overwritten by macro-assembler level operations [1]. This ensures that the JIT compiler is aware of which registers must be saved or are unsafe to use when performing tasks that rely on the MacroAssembler's temporary registers [1]. The register q31 is typically used as a scratch register for these macro-level floating-point operations [3].
Citations:
🌐 Web query:
site:github.com/WebKit/WebKit "fpTempRegister" "macroClobbered"💡 Result:
The terms fpTempRegister and macroClobbered are used within the JavaScriptCore (JSC) engine of WebKit [1][2][3]. fpTempRegister is a designated floating-point temporary register used by the MacroAssembler to perform intermediate operations (such as data movement or calculations) when a scratch register is required [2][4][3]. Because it is used as a temporary scratch space, its contents are volatile and can be overwritten by other operations that require a floating-point scratch register [4][5]. macroClobbered (specifically macroClobberedFPRs) refers to a function or set of metadata used to track which registers, including floating-point registers, are considered clobbered (or modified) by macro-assembler operations [1]. This is critical for the JIT compilers and the register allocator to know which registers must be saved or are no longer safe to use after a specific sequence of instructions [6]. The function RegisterSet::macroClobberedFPRs explicitly includes fpTempRegister (and potentially others like fpTempRegister2, depending on the architecture) in the set of clobbered registers to ensure that the compiler accounts for their volatile nature during register management [1]. In summary, fpTempRegister is a specific register reserved for temporary floating-point tasks [2][3], and macroClobbered mechanisms are the system-level accounting used to track that this register is volatile and unsafe to rely on across operations that might use it as a scratch space [1][6].
Citations:
🌐 Web query:
"const a0 = d + 0.5" WebKit💡 Result:
The expression "const a0 = d + 0.5" appears in the context of the WebKit project, specifically within its JavaScript engine, JavaScriptCore (JSC) [1]. Historically, the pattern
floor(d + 0.5)—often used to implement mathematical rounding—has been utilized within the engine's JIT (Just-In-Time) compilation tiers (such as DFG and FTL) [1]. However, this approach is technically flawed for floating-point numbers becausefloor(d + 0.5)can return an incorrect result whendis the nearest representable double-precision float value just below 0.5 [1]. Consequently, recent updates to the WebKit codebase have involved replacing such manual rounding implementations with more robust methods to ensure correctness [1]. In other areas of the WebKit codebase, developers have also been moving away from manual arithmetic expressions like(a + b) * .5in favor of more expressive and safer alternatives, such asstd::midpoint(a, b)(available since C++20), to avoid potential overflow issues [2]. While the identifiera0is commonly used in computer architecture as a register name (e.g., in RISC-V or older assembly contexts) [3][4], in this specific context within WebKit source code, it represents a variable or temporary value holding a computed result during JavaScript compilation or execution [1].Citations:
🌐 Web query:
"clobbers-fp-temp-register" WebKit💡 Result:
The term clobbers-fp-temp-register is not a standard compiler attribute or recognized keyword in WebKit or GCC. It is likely a misremembered or informal reference to concepts related to register management in the WebKit JavaScriptCore (JSC) assembler or specific compiler warnings regarding Floating Point (FP) registers [1][2]. In the context of WebKit's JavaScriptCore (JSC) assembler: 1. Assembler Architecture Headers: WebKit defines specialized registers for its MacroAssembler on various platforms. For instance, MacroAssemblerARM64.h and MacroAssemblerRISCV64.h define specific FP temporary registers (e.g., fpTempRegister) to be used by the JIT compiler as scratch space during code generation [3][4]. 2. Clobbering: In compiler and JIT development, to clobber a register means that the code modifies the contents of that register without preserving its original value. JSC's JIT infrastructure, particularly in the DFG (Data Flow Graph) JIT, includes a clobberize mechanism to track which registers or memory locations are modified by specific nodes during optimization [5]. 3. SIMD and Callee-Saves: WebKit development has specifically addressed bugs related to FP registers being clobbered when using WebAssembly SIMD. In these scenarios, the JIT must correctly account for which parts of floating-point callee-save registers are volatile or preserved, ensuring the assembler does not incorrectly assume a register is safe to use [6]. In the broader context of compilers like GCC: The phrase likely relates to common warnings regarding FP register preservation. GCC may issue a warning such as "FP registers might be clobbered despite 'interrupt' attribute" when a function marked as an interrupt handler performs operations that affect floating-point state without the compiler having explicit permission or a mechanism to save/restore those registers [1][2]. This is typically resolved by using compiler flags like -mgeneral-regs-only, which restricts the compiler to using only general-purpose registers [1]. If you encountered this specific string in a codebase or error message, it is most likely a custom diagnostic or an informal comment within the source code rather than a formal technical specification.
Citations:
🌐 Web query:
"macroClobberedFPRs" WebKit RegisterSet.cpp source💡 Result:
In the WebKit source code, specifically within Source/JavaScriptCore/jit/RegisterSet.cpp, macroClobberedFPRs is a function that returns a RegisterSet representing the floating-point registers (FPRs) that are clobbered (modified) by macro instructions used in WebKit's Just-In-Time (JIT) compiler [1][2]. When the JIT compiler generates machine code, it often relies on specific "macro" assemblers to perform operations. Some of these operations require temporary registers to store intermediate values; these temporary registers are considered "clobbered" because their original contents are overwritten during the execution of the macro [1]. The macroClobberedFPRs function centralizes the definition of which specific floating-point registers are used as these temporary scratch registers across different CPU architectures (such as X86_64, ARM64, and RISC-V64) [1]. The implementation typically adds architecture-specific temporary registers (e.g., MacroAssembler::fpTempRegister) to the returned RegisterSet, ensuring that the compiler's register allocation logic accounts for these registers being volatile during such operations [1]. In recent versions of WebKit, this logic is managed within the RegisterSet class or its builder components to maintain accurate register usage information [2][3].
Citations:
🌐 Web query:
"q31" "fpTempRegister" "RegisterSet" WebKit💡 Result:
The term fpTempRegister appears within the context of the WebKit project's B3 JIT compiler and its associated AIR (Assembly Intermediate Representation) layer, particularly in files related to register allocation and code generation [1][2][3]. While q31 is not explicitly identified as a standard, standalone public API or concept within available WebKit documentation, the codebase contains various references to registers and register sets within the B3/AIR architecture [1][3]. The B3 JIT compiler uses the RegisterSet class to manage sets of hardware registers during compilation and register allocation processes, and fpTempRegister is used in logic concerning floating-point temporary registers within these systems [2][3]. The search results show these elements appearing in files such as AirAllocateRegistersAndStackAndGenerateCode.cpp and related headers in JavaScriptCore, which are frequently updated as the WebKit engine evolves [1][2][3]. These components are internal implementations of the JavaScript engine and are not typically exposed as high-level, public-facing identifiers for developers outside of the WebKit project itself [1][2][4].
Citations:
🌐 Web query:
WebKit B3 Air register allocation available floating point registers q31 reserved💡 Result:
In the WebKit B3 JIT compiler and its underlying assembly representation (Air), floating-point registers on ARM64 are defined as the set q0 through q31 [1][2]. The register q31 is a valid, available floating-point register within the architecture's register file [1][2]. There is no standard designation of q31 as "reserved" in the context of WebKit's general B3 or Air register allocation [1][2]. The term "q31" likely causes confusion due to the naming collision with "Q31," a common fixed-point data format (31-bit signed fractional) used in digital signal processing (DSP), particularly in ARM's CMSIS-DSP library [3][4][5]. This fixed-point format is unrelated to the q31 hardware register index found in AArch64 [6]. In WebKit's register allocation, registers are generally managed by the B3 compiler's allocator, which tracks live ranges and interference [7][8]. While specific registers may be reserved by the calling convention (such as callee-saved registers or registers used for specific architectural purposes like the stack pointer or thread pointer), q31 is not globally reserved in B3 [1][9][10]. If a specific context or custom patchpoint within WebKit were to reserve q31, it would be an implementation detail of that specific code path rather than a general rule of the B3 register allocator [7][8].
Citations:
Increase ARM64 floating-point register pressure.
The 16 live floating-point values in
a0–a7andb0–b7do not require ARM64 registerq31, which isfpTempRegister. The test can therefore pass without detecting aq31clobber. Add more live floating-point values or assert that ARM64 Air assignsq31to a live value.🤖 Prompt for AI Agents