Instead of storing a u32 index into a cache vector and looking up the cache at runtime through a chain of dependent loads (load Executable*, load vector data pointer, multiply index, add), store the actual cache pointer as a u64 directly in the instruction stream. A fixup pass (Executable::fixup_cache_pointers()) runs after Executable construction in both the Rust and C++ pipelines, walking the bytecode and replacing each index with the corresponding pointer. The cache pointer type is encoded in Bytecode.def (e.g. PropertyLookupCache*, GlobalVariableCache*) so the fixup switch is auto-generated by the Python Op code generator, making it impossible to forget updating the fixup when adding new cached instructions. This eliminates 3-4 dependent loads on every inline cache access in both the C++ interpreter and the assembly interpreter.
15 lines
No EOL
747 B
Text
15 lines
No EOL
747 B
Text
JS bytecode executable ""
|
|
[ 0] 0: GetLexicalEnvironment dst:reg4
|
|
[ 8] GetGlobal dst:reg6, identifier:outer
|
|
[ 20] Call dst:reg5, callee:reg6, this_value:Undefined, outer
|
|
[ 40] End value:reg5
|
|
|
|
JS bytecode executable "outer"
|
|
[ 0] 0: GetLexicalEnvironment dst:reg4
|
|
[ 8] CreateVariable identifier:inner, is_immutable:false, is_global:false, is_strict:false
|
|
[ 18] InitializeVariableBinding identifier:inner, src:Undefined
|
|
[ 30] NewFunction dst:reg5, shared_function_data_index:0
|
|
[ 48] SetVariableBinding identifier:inner, src:reg5
|
|
[ 60] GetGlobal dst:reg6, identifier:Number
|
|
[ 78] CallConstruct dst:reg5, callee:reg6, Number, arguments:[Int32(42)]
|
|
[ 98] Return value:reg5 |