Skip to content

Latest commit

 

History

History
56 lines (40 loc) · 3.8 KB

internal.md

File metadata and controls

56 lines (40 loc) · 3.8 KB

Flags

General

There is an internal check V8 does in the bytecode to check if the compiled V8 flags and the used V8 flags are the same, and because the flags used in bytecode module and Client JS could mismatch, they are manually overwritten, to make sure that the bytecode actually runs in Client JS without V8 rejecting it.

Make sure to not add any V8 flags that change any compiler behaviour, as this might break the bytecode, if its only added on one side

Updating the flags hash

To update the flags hash because the V8 flags used in Client JS have changed, update the V8 flags used by the module temporarily to exactly those used by Client JS, then use the following code to display the new flags hash: (In compiler/compiler.cpp)

uint32_t flagsHash;
memcpy(&flagsHash, bytecodeResult.data() + flagsHashOffset, sizeof(flagsHash));
logger->Log("Currently used flags hash: " + std::to_string(flagsHash));

Flag caveats

Due to a lot of testing and investigation into the V8 source code, there were some interesting flag caveats found, that are important to mention here:

--no-lazy

This flag must be set when trying to run precompiled bytecode, because otherwise the bytecode compiler will not generate the bytecode for the whole file, and instead only compile functions that are called in the top level code. Normally, the V8 engine will lazily compile these functions from their source code to their bytecode representation when they are called, but because the client does not have the source code, it is not able to do these lazy compilations.

Thus, we need to disable lazy compilation completely.

Disabling lazy compilation results in more memory consumption, but no performance decrease, because only the baseline compiler is affected, not TurboFan

--no-flush-bytecode

This flag must be set on the clientside, because if this flag is not disabled, then V8 will try to garbage collect bytecode that has not been executed recently, but as with the --no-lazy flag, once the bytecode is disposed, the V8 engine has no way of creating that bytecode again, because it does not have the source code, to create the bytecode from. This causes random errors in JS code because V8 is unable to create valid bytecode for the function, causing it to throw syntax errors.

This results in a small increase in memory consumption, because the V8 engine can't garbage collect the bytecode, so the whole bytecode for all scripting resources will remain in memory forever.

This memory increase is not a meaningful amount, a relatively big script will have around 1-2 megabytes of bytecode

Updating the V8 version

The bytecode module needs to always use the same version as the version used in Client JS, fortunately we store the V8 binary we use on the alt:V CDN, so this module downloads it from there. The V8 version should always be in sync of that, but we need to make sure we make a new release of the bytecode module when we update the V8 version, so that the module is working again.

Format

The bytecode we send to the client has exactly 5 bytes of magic bytes at the front, these bytes are ALTBC to identify the data as alt:V bytecode, when we write it to file. After that there are 4 bytes that form a 4-byte integer that corresponds to the size of the original source code (this is needed because V8 sometimes uses the source code buffer, when compiling functions, and if the buffer is too small, it crashes) that will be used to create a string to pass to the script compiler with the length of the original source code. To read this bytecode, we need to check for these 5 magic bytes at the front, if they match, remove them and the 4 bytes from the source code length from the buffer, and the remaining buffer is the full bytecode generated by V8, which we can then use to instantiate the script.