diff --git a/common.gypi b/common.gypi index 63301644b949f0..73dfecc46b879e 100644 --- a/common.gypi +++ b/common.gypi @@ -443,6 +443,9 @@ ['v8_enable_pointer_compression == 1', { 'defines': ['V8_COMPRESS_POINTERS'], }], + ['v8_enable_pointer_compression == 1 and v8_enable_pointer_compression_shared_cage != 1', { + 'defines': ['V8_COMPRESS_POINTERS_IN_MULTIPLE_CAGES'], + }], ['v8_enable_pointer_compression_shared_cage == 1', { 'defines': ['V8_COMPRESS_POINTERS_IN_SHARED_CAGE'], }], diff --git a/configure.py b/configure.py index 5a05a1fdcd496c..a752897e90aa25 100755 --- a/configure.py +++ b/configure.py @@ -646,6 +646,12 @@ default=None, help='[Experimental] Enable V8 pointer compression (limits max heap to 4GB and breaks ABI compatibility)') +parser.add_argument('--experimental-pointer-compression-shared-cage', + action='store_true', + dest='pointer_compression_shared_cage', + default=None, + help='[Experimental] Use V8 pointer compression with shared cage (requires --experimental-enable-pointer-compression)') + parser.add_argument('--v8-options', action='store', dest='v8_options', @@ -1789,7 +1795,10 @@ def configure_v8(o, configs): # Note that enabling pointer compression without enabling sandbox is unsupported by V8, # so this can be broken at any time. o['variables']['v8_enable_sandbox'] = 0 - o['variables']['v8_enable_pointer_compression_shared_cage'] = 1 if options.enable_pointer_compression else 0 + # We set v8_enable_pointer_compression_shared_cage to 0 always, even when + # pointer compression is enabled so that we don't accidentally enable shared + # cage mode when pointer compression is on. + o['variables']['v8_enable_pointer_compression_shared_cage'] = 1 if options.pointer_compression_shared_cage else 0 o['variables']['v8_enable_external_code_space'] = 1 if options.enable_pointer_compression else 0 o['variables']['v8_enable_31bit_smis_on_64bit_arch'] = 1 if options.enable_pointer_compression else 0 o['variables']['v8_enable_extensible_ro_snapshot'] = 0 diff --git a/src/api/embed_helpers.cc b/src/api/embed_helpers.cc index f6ad46dae3db9a..c1bcb3948c4cf6 100644 --- a/src/api/embed_helpers.cc +++ b/src/api/embed_helpers.cc @@ -1,6 +1,7 @@ #include "debug_utils-inl.h" #include "env-inl.h" #include "node.h" +#include "node_internals.h" #include "node_snapshot_builder.h" using v8::Context; @@ -127,7 +128,7 @@ CommonEnvironmentSetup::CommonEnvironmentSetup( if (flags & Flags::kIsForSnapshotting) { // The isolate must be registered before the SnapshotCreator initializes the // isolate, so that the memory reducer can be initialized. - isolate = impl_->isolate = Isolate::Allocate(); + isolate = impl_->isolate = Isolate::Allocate(GetOrCreateIsolateGroup()); platform->RegisterIsolate(isolate, loop); impl_->snapshot_creator.emplace(isolate, params); diff --git a/src/api/environment.cc b/src/api/environment.cc index b806e0e4af70d0..4f0d0e5e7f4ded 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -37,6 +37,7 @@ using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Isolate; +using v8::IsolateGroup; using v8::Just; using v8::JustVoid; using v8::Local; @@ -304,6 +305,17 @@ void SetIsolateUpForNode(v8::Isolate* isolate) { SetIsolateUpForNode(isolate, settings); } +// +IsolateGroup GetOrCreateIsolateGroup() { + // When pointer compression is disabled, we cannot create new groups, + // in which case we'll always return the default. + if (IsolateGroup::CanCreateNewGroups()) { + return IsolateGroup::Create(); + } + + return IsolateGroup::GetDefault(); +} + // TODO(joyeecheung): we may want to expose this, but then we need to be // careful about what we override in the params. Isolate* NewIsolate(Isolate::CreateParams* params, @@ -311,7 +323,8 @@ Isolate* NewIsolate(Isolate::CreateParams* params, MultiIsolatePlatform* platform, const SnapshotData* snapshot_data, const IsolateSettings& settings) { - Isolate* isolate = Isolate::Allocate(); + IsolateGroup group = GetOrCreateIsolateGroup(); + Isolate* isolate = Isolate::Allocate(group); if (isolate == nullptr) return nullptr; if (snapshot_data != nullptr) { diff --git a/src/node_internals.h b/src/node_internals.h index 978994ee1c9c3a..6ec17b35cae6c6 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -56,6 +56,9 @@ extern uint64_t node_start_time; // Forward declaration class Environment; +static constexpr uint64_t kMaxPointerCompressionHeap = uint64_t{1} + << 32; // 4 GiB + // Convert a struct sockaddr to a { address: '1.2.3.4', port: 1234 } JS object. // Sets address and port properties on the info object and returns it. // If |info| is omitted, a new object is returned. @@ -342,6 +345,20 @@ void TraceEnvVar(Environment* env, v8::Local key); void DefineZlibConstants(v8::Local target); + +// If creating new v8::IsolateGroup instance is supported, this returns a +// new instance. Otherwise, it returns the default instance. +// +// An IsolateGroup is a collection of Isolates that share the same underlying +// pointer cage when pointer compression is enabled. When pointer compression is +// disabled, there is a default IsolateGroup that is used for all isolates, and +// when pointer compression is enabled, all isolates in the app share the +// same pointer cage by default that is limited a maximum of 4GB, not counting +// array buffers and off-heap storage. Multiple IsolateGroups can be used to +// work around the 4GB limit, but each group reserves a range of virtual memory +// addresses, so this should be used with care. +v8::IsolateGroup GetOrCreateIsolateGroup(); + v8::Isolate* NewIsolate(v8::Isolate::CreateParams* params, uv_loop_t* event_loop, MultiIsolatePlatform* platform, diff --git a/src/node_options.cc b/src/node_options.cc index 2d9ccba7fe4feb..5ee878e97d44d9 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -128,7 +128,15 @@ void PerIsolateOptions::HandleMaxOldSpaceSizePercentage( } // Get available memory in bytes +#ifdef V8_COMPRESS_POINTERS + // When pointer compression is enabled, V8 uses a 4 GiB heap limit. + // We'll use the smaller of that or the total system memory as + // reported by uv. + uint64_t total_memory = + std::min(uv_get_total_memory(), kMaxPointerCompressionHeap); // 4 GiB +#else uint64_t total_memory = uv_get_total_memory(); +#endif uint64_t constrained_memory = uv_get_constrained_memory(); // Use constrained memory if available, otherwise use total memory diff --git a/test/parallel/test-max-old-space-size-percentage.js b/test/parallel/test-max-old-space-size-percentage.js index d4da10b3012662..b84344ddb3988d 100644 --- a/test/parallel/test-max-old-space-size-percentage.js +++ b/test/parallel/test-max-old-space-size-percentage.js @@ -122,7 +122,11 @@ assert( ); // Validate heap sizes against system memory -const totalMemoryMB = Math.floor(os.totalmem() / 1024 / 1024); +// When pointer compression is enabled, the maximum total memory is 4 GB +const totalmem = Math.floor(os.totalmem() / 1024 / 1024); +const totalMemoryMB = process.config.variables.v8_enable_pointer_compression ? + Math.min(4096, totalmem) : + totalmem; const uint64Max = 2 ** 64 - 1; const constrainedMemory = process.constrainedMemory(); const constrainedMemoryMB = Math.floor(constrainedMemory / 1024 / 1024); diff --git a/tools/v8_gypfiles/features.gypi b/tools/v8_gypfiles/features.gypi index 462af7d2581985..263ac8213f0aa2 100644 --- a/tools/v8_gypfiles/features.gypi +++ b/tools/v8_gypfiles/features.gypi @@ -355,6 +355,9 @@ ['v8_enable_pointer_compression==1', { 'defines': ['V8_COMPRESS_POINTERS'], }], + ['v8_enable_pointer_compression==1 and v8_enable_pointer_compression_shared_cage!=1', { + 'defines': ['V8_COMPRESS_POINTERS_IN_MULTIPLE_CAGES'], + }], ['v8_enable_pointer_compression_shared_cage==1', { 'defines': ['V8_COMPRESS_POINTERS_IN_SHARED_CAGE'], }],