[static-roots] Build infrastructure for static roots

Add gen-static-roots.py to conveniently re-generate the static roots
table when it needs changing.

Additionally, ensure the first read-only page is allocated as first page
during mksnapshot, to move static roots closer to start.

Bug: v8:13466
Change-Id: Ie72b64d0ad0dd3e5fccd3b41e8ed00a4a55a0033
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4096481
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Auto-Submit: Olivier Flückiger <olivf@chromium.org>
Commit-Queue: Olivier Flückiger <olivf@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84844}
This commit is contained in:
Olivier Flückiger 2022-12-13 17:01:28 +00:00 committed by V8 LUCI CQ
parent 4677d3ba1b
commit f6eab3830d
10 changed files with 193 additions and 11 deletions

View File

@ -132,6 +132,8 @@ v8_flag(name = "v8_enable_snapshot_code_comments")
v8_flag(name = "v8_enable_snapshot_native_code_counters")
v8_flag(name = "v8_enable_static_roots")
v8_flag(name = "v8_enable_trace_maps")
v8_flag(name = "v8_enable_v8_checks")
@ -316,6 +318,7 @@ v8_config(
"v8_enable_slow_dchecks": "ENABLE_SLOW_DCHECKS",
"v8_enable_runtime_call_stats": "V8_RUNTIME_CALL_STATS",
"v8_enable_snapshot_native_code_counters": "V8_SNAPSHOT_NATIVE_CODE_COUNTERS",
"v8_enable_static_roots": "V8_STATIC_ROOTS",
"v8_enable_trace_maps": "V8_TRACE_MAPS",
"v8_enable_v8_checks": "V8_ENABLE_CHECKS",
"v8_enable_verify_csa": "ENABLE_VERIFY_CSA",

View File

@ -120,6 +120,9 @@ declare_args() {
# as per the --native-code-counters flag.
v8_enable_snapshot_native_code_counters = ""
# Use pre-generated static root pointer values from static-roots.h.
v8_enable_static_roots = false
# Enable code-generation-time checking of types in the CodeStubAssembler.
v8_enable_verify_csa = false
@ -535,6 +538,22 @@ if (v8_enable_sandbox == "") {
v8_enable_external_code_space && target_os != "fuchsia"
}
if (v8_enable_static_roots == "") {
# Static roots are only valid for builds with pointer compression and a
# shared ro heap. Also, non-wasm and non-i18n builds have fewer read-only
# roots.
v8_enable_static_roots =
v8_enable_pointer_compression && v8_enable_shared_ro_heap &&
v8_enable_pointer_compression_shared_cage && v8_enable_webassembly &&
v8_enable_i18n_support
}
assert(!v8_enable_static_roots ||
(v8_enable_pointer_compression && v8_enable_shared_ro_heap &&
v8_enable_pointer_compression_shared_cage &&
v8_enable_webassembly && v8_enable_i18n_support),
"Trying to enable static roots in a configuration that is not supported")
assert(!v8_disable_write_barriers || v8_enable_single_generation,
"Disabling write barriers works only with single generation")
@ -1060,6 +1079,9 @@ config("features") {
if (v8_enable_pointer_compression_8gb) {
defines += [ "V8_COMPRESS_POINTERS_8GB" ]
}
if (v8_enable_static_roots) {
defines += [ "V8_STATIC_ROOTS" ]
}
if (v8_use_zlib) {
defines += [ "V8_USE_ZLIB" ]
}

View File

@ -122,13 +122,8 @@ namespace internal {
#define V8_CAN_CREATE_SHARED_HEAP_BOOL false
#endif
// Disabling WASM or INTL invalidates the contents of static-roots.h
#if defined(V8_SHARED_RO_HEAP) && defined(V8_COMPRESS_POINTERS) && \
defined(V8_COMPRESS_POINTERS_IN_SHARED_CAGE) && V8_ENABLE_WEBASSEMBLY && \
defined(V8_INTL_SUPPORT)
// TODO(olivf, v8:13466): Add build infra to conveniently re-regenerate the
// static roots table and then enable it.
#define V8_STATIC_ROOTS_BOOL false
#if defined(V8_STATIC_ROOTS)
#define V8_STATIC_ROOTS_BOOL true
#else
#define V8_STATIC_ROOTS_BOOL false
#endif

View File

@ -4156,16 +4156,22 @@ VirtualMemoryCage* Isolate::GetPtrComprCodeCageForTesting() {
// then called with --static-roots to re-regenerate the static-roots.h file.
void Isolate::VerifyStaticRoots() {
#if V8_STATIC_ROOTS_BOOL
static_assert(ReadOnlyHeap::IsReadOnlySpaceShared(),
"Static read only roots are only supported when there is one "
"shared read only space per cage");
#define STATIC_ROOTS_FAILED_MSG \
"Run `tools/dev/gen-static-roots.py` to update static-roots.h."
static_assert(static_cast<int>(RootIndex::kReadOnlyRootsCount) ==
StaticReadOnlyRootsPointerTable.size(),
STATIC_ROOTS_FAILED_MSG);
auto& roots = roots_table();
CHECK_EQ(static_cast<int>(RootIndex::kReadOnlyRootsCount),
StaticReadOnlyRootsPointerTable.size());
RootIndex idx = RootIndex::kFirstReadOnlyRoot;
ReadOnlyPage* first_page = read_only_heap()->read_only_space()->pages()[0];
for (Tagged_t cmp_ptr : StaticReadOnlyRootsPointerTable) {
Address the_root = roots[idx];
Address ptr =
V8HeapCompressionScheme::DecompressTaggedPointer(cage_base(), cmp_ptr);
CHECK_EQ(the_root, ptr);
CHECK_WITH_MSG(the_root == ptr, STATIC_ROOTS_FAILED_MSG);
// All roots must fit on first page, since only this page is guaranteed to
// have a stable offset from the cage base. If this ever changes we need
// to load more pages with predictable offset at
@ -4173,6 +4179,7 @@ void Isolate::VerifyStaticRoots() {
CHECK(first_page->Contains(the_root));
++idx;
}
#undef STATIC_ROOTS_FAILED_MSG
#endif // V8_STATIC_ROOTS_BOOL
}
@ -4522,7 +4529,7 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data,
can_rehash);
startup_deserializer.DeserializeIntoIsolate();
}
if (DEBUG_BOOL) VerifyStaticRoots();
if (DEBUG_BOOL || create_heap_objects) VerifyStaticRoots();
load_stub_cache_->Initialize();
store_stub_cache_->Initialize();
interpreter_->Initialize();

View File

@ -99,6 +99,9 @@ void ReadOnlyHeap::SetUp(Isolate* isolate,
artifacts = InitializeSharedReadOnlyArtifacts();
ro_heap = CreateInitalHeapForBootstrapping(isolate, artifacts);
// Ensure the first read-only page ends up first in the cage.
ro_heap->read_only_space()->EnsurePage();
artifacts->VerifyChecksum(read_only_snapshot_data, true);
}
} else {

View File

@ -558,6 +558,11 @@ void ReadOnlySpace::FreeLinearAllocationArea() {
limit_ = kNullAddress;
}
void ReadOnlySpace::EnsurePage() {
if (pages_.empty()) EnsureSpaceForAllocation(1);
CHECK(!pages_.empty());
}
void ReadOnlySpace::EnsureSpaceForAllocation(int size_in_bytes) {
if (top_ + size_in_bytes <= limit_) {
return;

View File

@ -237,6 +237,9 @@ class ReadOnlySpace : public BaseSpace {
void InitFromMemoryDump(Isolate* isolate, SnapshotByteSource* source);
// Ensure the read only space has at least one allocated page
void EnsurePage();
protected:
friend class SingleCopyReadOnlyArtifacts;

View File

@ -15,6 +15,9 @@ namespace v8 {
namespace internal {
void StaticRootsTableGen::write(Isolate* isolate, const char* file) {
CHECK_WITH_MSG(!V8_STATIC_ROOTS_BOOL,
"--static-roots is only supported in builds with "
"v8_enable_static_roots disabled");
CHECK(file);
static_assert(static_cast<int>(RootIndex::kFirstReadOnlyRoot) == 0);
@ -31,8 +34,19 @@ void StaticRootsTableGen::write(Isolate* isolate, const char* file) {
<< "#define V8_ROOTS_STATIC_ROOTS_H_\n"
<< "\n"
<< "#include \"src/common/globals.h\"\n"
<< "\n"
<< "#if V8_STATIC_ROOTS_BOOL\n"
<< "\n"
<< "// Disabling Wasm or Intl invalidates the contents of "
"static-roots.h.\n"
<< "// TODO(olivf): To support static roots for multiple build "
"configurations we\n"
<< "// will need to generate target specific versions of "
"this "
"file.\n"
<< "static_assert(V8_ENABLE_WEBASSEMBLY);\n"
<< "static_assert(V8_INTL_SUPPORT);\n"
<< "\n"
<< "namespace v8 {\n"
<< "namespace internal {\n"
<< "\n"

126
tools/dev/gen-static-roots.py Executable file
View File

@ -0,0 +1,126 @@
#!/usr/bin/env python3
# Copyright 2022 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can
# be found in the LICENSE file.
import subprocess
import argparse
import os
import filecmp
import tempfile
import shutil
from pathlib import Path
# Detect if we have goma
def _Which(cmd):
for path in os.environ["PATH"].split(os.pathsep):
if os.path.exists(os.path.join(path, cmd)):
return os.path.join(path, cmd)
return None
def DetectGoma():
if os.environ.get("GOMA_DIR"):
return os.environ.get("GOMA_DIR")
if os.environ.get("GOMADIR"):
return os.environ.get("GOMADIR")
# There is a copy of goma in depot_tools, but it might not be in use on
# this machine.
goma = _Which("goma_ctl")
if goma is None:
return None
cipd_bin = os.path.join(os.path.dirname(goma), ".cipd_bin")
if not os.path.exists(cipd_bin):
return None
goma_auth = os.path.expanduser("~/.goma_client_oauth2_config")
if not os.path.exists(goma_auth):
return None
return cipd_bin
GOMADIR = DetectGoma()
IS_GOMA_MACHINE = GOMADIR is not None
USE_GOMA = "true" if IS_GOMA_MACHINE else "false"
# List of all supported build configurations for static roots
STATIC_ROOT_CONFIGURATIONS = {
"ptr-cmpr-wasm-intl": {
"target":
"src/roots/static-roots.h",
"gn_args":
f"""\
is_debug = false
use_goma = {USE_GOMA}
v8_enable_static_roots = false
v8_enable_pointer_compression = true
v8_enable_shared_ro_heap = true
v8_enable_pointer_compression_shared_cage = true
v8_enable_webassembly = true
v8_enable_i18n_support = true
"""
},
}
# Parse args
parser = argparse.ArgumentParser(description='Generates static-roots.h.')
parser.add_argument(
'--configuration',
choices=STATIC_ROOT_CONFIGURATIONS.keys(),
action='extend',
default='ptr-cmpr-wasm-intl',
nargs='*',
help="""Build configuration. Refers to a set of configurations with
identical static-roots.h. Currently there is only one supported configuration.
Future configurations will need to generate multiple target files.""")
parser.add_argument(
'--out',
default=Path('out'),
required=False,
type=Path,
help='target build directory')
args = parser.parse_args()
# Some helpers
def run(cmd, **kwargs):
print(f"# CMD: {cmd} {kwargs}")
return subprocess.run(cmd, **kwargs, check=True)
def build(path, gn_args):
if not path.exists():
path.mkdir(parents=True, exist_ok=True)
with (path / "args.gn").open("w") as f:
f.write(gn_args)
run(["gn", "gen", path])
run(["autoninja", "-C", path, "mksnapshot"])
return path.absolute()
# Generate all requested static root headers
v8_path = Path(__file__).parents[2]
changed = False
for target in [args.configuration]:
build_dir = args.out / f"gen-static-roots.{target}"
config = STATIC_ROOT_CONFIGURATIONS[target]
gn_args = config["gn_args"]
build_path = build(build_dir, gn_args)
out_file = Path(tempfile.gettempdir()) / f"static-roots-{target}.h"
run([build_path / "mksnapshot", "--static-roots", out_file])
target_file = v8_path / config["target"]
if not filecmp.cmp(out_file, target_file):
shutil.move(out_file, target_file)
changed = True
if changed:
exit(1)

View File

@ -384,6 +384,10 @@ class Config(object):
return_code, output = _CallWithOutput("autoninja -C %s %s" %
(path, targets))
if return_code != 0 and "FAILED:" in output and "snapshot_blob" in output:
if "gen-static-roots.py" in output:
_Notify("V8 build requires your attention",
"Please re-generate static roots...")
return return_code
csa_trap = re.compile("Specify option( --csa-trap-on-node=[^ ]*)")
match = csa_trap.search(output)
extra_opt = match.group(1) if match else ""