blob: 139f9c8184e7d881ec781aeb52c43148d398012c [file] [log] [blame]
Avi Drissman468e51b62022-09-13 20:47:011// Copyright 2017 The Chromium Authors
Ross McIlroy45e9500a2018-03-27 17:06:022// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "gin/gin_features.h"
Michael Lippautzdc5c8632024-04-23 16:02:416
Camillo Bruni5e5376f2021-04-01 19:15:097#include "base/metrics/field_trial_params.h"
Ross McIlroy45e9500a2018-03-27 17:06:028
9namespace features {
10
Dominik Inführb13c75a92022-02-18 11:15:5611// Enable code space compaction when finalizing a full GC with stack.
Daniel Chengaab4b01b2022-09-20 14:28:3812BASE_FEATURE(kV8CompactCodeSpaceWithStack,
Leszek Swirski7a6cb1252024-06-19 15:37:3313 ("V8CompactCodeSpaceWithStack"),
14 kFeatureDefaultStateControlledByV8);
Michael Lippautz471f75f22021-11-12 15:30:1115
16// Enable compaction when finalizing a full GC with stack.
Daniel Chengaab4b01b2022-09-20 14:28:3817BASE_FEATURE(kV8CompactWithStack,
Leszek Swirski7a6cb1252024-06-19 15:37:3318 ("V8CompactWithStack"),
19 kFeatureDefaultStateControlledByV8);
Michael Lippautz471f75f22021-11-12 15:30:1120
Benoît Lizé787cd3a2024-05-17 14:47:4621// Decommit (rather than discard) pooled pages.
22BASE_FEATURE(kV8DecommitPooledPages,
Leszek Swirski7a6cb1252024-06-19 15:37:3323 ("DecommitPooledPages"),
24 kFeatureDefaultStateControlledByV8);
Benoît Lizé787cd3a2024-05-17 14:47:4625
Ross McIlroy45e9500a2018-03-27 17:06:0226// Enables optimization of JavaScript in V8.
Daniel Chengaab4b01b2022-09-20 14:28:3827BASE_FEATURE(kV8OptimizeJavascript,
Leszek Swirski7a6cb1252024-06-19 15:37:3328 ("V8OptimizeJavascript"),
29 kFeatureDefaultStateControlledByV8);
Ross McIlroy45e9500a2018-03-27 17:06:0230
Ross McIlroyf4100322019-02-04 11:02:4631// Enables flushing of JS bytecode in V8.
Daniel Chengaab4b01b2022-09-20 14:28:3832BASE_FEATURE(kV8FlushBytecode,
Leszek Swirski7a6cb1252024-06-19 15:37:3333 ("V8FlushBytecode"),
34 kFeatureDefaultStateControlledByV8);
Toon Verwaest60185fc2022-07-27 10:40:3035const base::FeatureParam<int> kV8FlushBytecodeOldAge{
36 &kV8FlushBytecode, "V8FlushBytecodeOldAge", 5};
Ross McIlroyf4100322019-02-04 11:02:4637
Leszek Swirski0eb5dea2021-09-07 11:14:3338// Enables flushing of baseline code in V8.
Daniel Chengaab4b01b2022-09-20 14:28:3839BASE_FEATURE(kV8FlushBaselineCode,
Leszek Swirski7a6cb1252024-06-19 15:37:3340 ("V8FlushBaselineCode"),
41 kFeatureDefaultStateControlledByV8);
Leszek Swirski0eb5dea2021-09-07 11:14:3342
Dominik Inführaa5f5112023-06-07 21:07:3543// Enables code flushing based on tab visibility.
44BASE_FEATURE(kV8FlushCodeBasedOnTabVisibility,
Leszek Swirski7a6cb1252024-06-19 15:37:3345 ("V8FlushCodeBasedOnTabVisibility"),
46 kFeatureDefaultStateControlledByV8);
Dominik Inführaa5f5112023-06-07 21:07:3547
48// Enables code flushing based on time.
49BASE_FEATURE(kV8FlushCodeBasedOnTime,
Leszek Swirski7a6cb1252024-06-19 15:37:3350 ("V8FlushCodeBasedOnTime"),
51 kFeatureDefaultStateControlledByV8);
Dominik Inführaa5f5112023-06-07 21:07:3552const base::FeatureParam<int> kV8FlushCodeOldTime{&kV8FlushCodeBasedOnTime,
53 "V8FlushCodeOldTime", 30};
54
Leszek Swirskide933c192020-04-03 17:30:5355// Enables finalizing streaming JS compilations on a background thread.
Daniel Chengaab4b01b2022-09-20 14:28:3856BASE_FEATURE(kV8OffThreadFinalization,
Leszek Swirski7a6cb1252024-06-19 15:37:3357 ("V8OffThreadFinalization"),
58 kFeatureDefaultStateControlledByV8);
Leszek Swirskide933c192020-04-03 17:30:5359
Mythri A258ae112019-06-05 11:41:1060// Enables lazy feedback allocation in V8.
Daniel Chengaab4b01b2022-09-20 14:28:3861BASE_FEATURE(kV8LazyFeedbackAllocation,
Leszek Swirski7a6cb1252024-06-19 15:37:3362 ("V8LazyFeedbackAllocation"),
63 kFeatureDefaultStateControlledByV8);
Mythri A258ae112019-06-05 11:41:1064
Ulan Degenbaev1e41d4d2020-07-08 12:19:5465// Enables per-context marking worklists in V8 GC.
Daniel Chengaab4b01b2022-09-20 14:28:3866BASE_FEATURE(kV8PerContextMarkingWorklist,
Leszek Swirski7a6cb1252024-06-19 15:37:3367 ("V8PerContextMarkingWorklist"),
68 kFeatureDefaultStateControlledByV8);
Ulan Degenbaev916bc13e2020-07-07 15:07:4869
Ulan Degenbaev1e41d4d2020-07-08 12:19:5470// Enables flushing of the instruction cache for the embedded blob.
Daniel Chengaab4b01b2022-09-20 14:28:3871BASE_FEATURE(kV8FlushEmbeddedBlobICache,
Leszek Swirski7a6cb1252024-06-19 15:37:3372 ("V8FlushEmbeddedBlobICache"),
73 kFeatureDefaultStateControlledByV8);
Ulan Degenbaev1e41d4d2020-07-08 12:19:5474
75// Enables reduced number of concurrent marking tasks.
Daniel Chengaab4b01b2022-09-20 14:28:3876BASE_FEATURE(kV8ReduceConcurrentMarkingTasks,
Leszek Swirski7a6cb1252024-06-19 15:37:3377 ("V8ReduceConcurrentMarkingTasks"),
78 kFeatureDefaultStateControlledByV8);
Ulan Degenbaev1e41d4d2020-07-08 12:19:5479
Michael Lippautza4077272020-07-22 14:24:1780// Disables reclaiming of unmodified wrappers objects.
Daniel Chengaab4b01b2022-09-20 14:28:3881BASE_FEATURE(kV8NoReclaimUnmodifiedWrappers,
Leszek Swirski7a6cb1252024-06-19 15:37:3382 ("V8NoReclaimUnmodifiedWrappers"),
83 kFeatureDefaultStateControlledByV8);
Michael Lippautza4077272020-07-22 14:24:1784
pthier02d48fbc2021-09-27 11:50:1185// Enables W^X code memory protection in V8.
86// This is enabled in V8 by default. To test the performance impact, we are
87// going to disable this feature in a finch experiment.
Daniel Chengaab4b01b2022-09-20 14:28:3888BASE_FEATURE(kV8CodeMemoryWriteProtection,
Leszek Swirski7a6cb1252024-06-19 15:37:3389 ("V8CodeMemoryWriteProtection"),
90 kFeatureDefaultStateControlledByV8);
pthier02d48fbc2021-09-27 11:50:1191
Maya Lekova776d92092021-01-08 11:11:2392// Enables fallback to a breadth-first regexp engine on excessive backtracking.
Daniel Chengaab4b01b2022-09-20 14:28:3893BASE_FEATURE(kV8ExperimentalRegexpEngine,
94 "V8ExperimentalRegexpEngine",
Leszek Swirski7a6cb1252024-06-19 15:37:3395 kFeatureDefaultStateControlledByV8);
Maya Lekova776d92092021-01-08 11:11:2396
Etienne Pierre-doray0589f222024-09-16 15:44:4997// Enable accounting for external memory limits as part of global limits in v8
98// Heap.
99BASE_FEATURE(kV8ExternalMemoryAccountedInGlobalLimit,
100 "V8ExternalMemoryAccountedInGlobalLimit",
101 kFeatureDefaultStateControlledByV8);
102
Camillo Brunic8566b862023-02-16 09:10:48103// Enables the Turbofan compiler.
Leszek Swirski7a6cb1252024-06-19 15:37:33104BASE_FEATURE(kV8Turbofan, ("V8Turbofan"), kFeatureDefaultStateControlledByV8);
Camillo Brunic8566b862023-02-16 09:10:48105
Nico Hartmannc87b89e2023-06-14 14:29:06106// Enables Turbofan's new compiler IR Turboshaft.
Leszek Swirski7a6cb1252024-06-19 15:37:33107BASE_FEATURE(kV8Turboshaft,
108 ("V8Turboshaft"),
109 kFeatureDefaultStateControlledByV8);
Nico Hartmannc87b89e2023-06-14 14:29:06110
Nico Hartmannd88debc2023-10-25 16:48:30111// Enable running instruction selection on Turboshaft IR directly.
112BASE_FEATURE(kV8TurboshaftInstructionSelection,
Leszek Swirski7a6cb1252024-06-19 15:37:33113 ("V8TurboshaftInstructionSelection"),
114 kFeatureDefaultStateControlledByV8);
Nico Hartmannd88debc2023-10-25 16:48:30115
Victor Gomes6f7554462023-07-17 15:22:37116// Enables Maglev compiler. Note that this only sets the V8 flag when
117// manually overridden; otherwise it defers to whatever the V8 default is.
Leszek Swirski7a6cb1252024-06-19 15:37:33118BASE_FEATURE(kV8Maglev, ("V8Maglev"), kFeatureDefaultStateControlledByV8);
Etienne Pierre-doray21b714df2024-03-21 12:56:16119BASE_FEATURE(kV8ConcurrentMaglevHighPriorityThreads,
Leszek Swirski7a6cb1252024-06-19 15:37:33120 ("V8ConcurrentMaglevHighPriorityThreads"),
121 kFeatureDefaultStateControlledByV8);
Ross McIlroybeaa73b2021-02-03 10:27:34122
Dominik Inführa17f285d2025-06-30 07:55:58123BASE_FEATURE(kV8HighEndAndroid,
124 "V8HighEndAndroid",
125 kFeatureDefaultStateControlledByV8);
126
127const base::FeatureParam<int> kV8HighEndAndroidMemoryThreshold{
128 &kV8HighEndAndroid, "V8HighEndAndroidMemoryThreshold", 8};
129
Dominik Inführ390e7472023-08-15 07:33:39130BASE_FEATURE(kV8MemoryReducer,
131 "V8MemoryReducer",
132 base::FEATURE_DISABLED_BY_DEFAULT);
133
134const base::FeatureParam<int> kV8MemoryReducerGCCount{
135 &kV8MemoryReducer, "V8MemoryReducerGCCount", 3};
136
Dominik Inführ607eb712025-03-24 16:10:28137BASE_FEATURE(kV8PreconfigureOldGen,
138 "V8PreconfigureOldGen",
139 kFeatureDefaultStateControlledByV8);
140
141const base::FeatureParam<int> kV8PreconfigureOldGenSize{
142 &kV8PreconfigureOldGen, "V8PreconfigureOldGenSize", 32};
143
Omer Katze72b2982023-05-02 14:43:49144// Enables MinorMC young generation garbage collector.
Leszek Swirski7a6cb1252024-06-19 15:37:33145BASE_FEATURE(kV8MinorMS, ("V8MinorMS"), kFeatureDefaultStateControlledByV8);
Omer Katze72b2982023-05-02 14:43:49146
Omer Katzdd11ac172024-07-12 10:59:23147BASE_FEATURE(kV8ScavengerHigherCapacity,
148 "V8ScavengerHigherCapacity",
149 base::FEATURE_DISABLED_BY_DEFAULT);
150
151const base::FeatureParam<int> kV8ScavengerMaxCapacity{
152 &kV8ScavengerHigherCapacity, "V8ScavengerMaxCapacity", 16};
153
Leszek Swirskif68e123f2021-07-22 18:23:39154// Enables Sparkplug compiler. Note that this only sets the V8 flag when
155// manually overridden; otherwise it defers to whatever the V8 default is.
Leszek Swirski7a6cb1252024-06-19 15:37:33156BASE_FEATURE(kV8Sparkplug, ("V8Sparkplug"), kFeatureDefaultStateControlledByV8);
Leszek Swirskiaac0b192021-03-03 17:07:13157
Victor Gomes409ef5a2021-12-02 09:47:35158// Enables the concurrent Sparkplug compiler.
Daniel Chengaab4b01b2022-09-20 14:28:38159BASE_FEATURE(kV8ConcurrentSparkplug,
Leszek Swirski7a6cb1252024-06-19 15:37:33160 ("V8ConcurrentSparkplug"),
161 kFeatureDefaultStateControlledByV8);
Victor Gomes409ef5a2021-12-02 09:47:35162const base::FeatureParam<int> kV8ConcurrentSparkplugMaxThreads{
163 &kV8ConcurrentSparkplug, "V8ConcurrentSparkplugMaxThreads", 0};
Daniel Chengaab4b01b2022-09-20 14:28:38164BASE_FEATURE(kV8ConcurrentSparkplugHighPriorityThreads,
Leszek Swirski7a6cb1252024-06-19 15:37:33165 ("V8ConcurrentSparkplugHighPriorityThreads"),
166 kFeatureDefaultStateControlledByV8);
Leszek Swirski4cc40362021-06-25 16:22:04167// Makes sure the experimental Sparkplug compiler is only enabled if short
168// builtin calls are enabled too.
Daniel Chengaab4b01b2022-09-20 14:28:38169BASE_FEATURE(kV8SparkplugNeedsShortBuiltinCalls,
Leszek Swirski7a6cb1252024-06-19 15:37:33170 ("V8SparkplugNeedsShortBuiltinCalls"),
171 kFeatureDefaultStateControlledByV8);
Leszek Swirski4cc40362021-06-25 16:22:04172
Leszek Swirski25757112024-04-09 14:53:31173// Enables batch compilation for Sparkplug (baseline) compilation.
174BASE_FEATURE(kV8BaselineBatchCompilation,
Leszek Swirski7a6cb1252024-06-19 15:37:33175 ("V8BaselineBatchCompilation"),
176 kFeatureDefaultStateControlledByV8);
Leszek Swirski25757112024-04-09 14:53:31177
[email protected]bda533ac2021-03-29 12:14:12178// Enables short builtin calls feature.
Daniel Chengaab4b01b2022-09-20 14:28:38179BASE_FEATURE(kV8ShortBuiltinCalls,
Leszek Swirski7a6cb1252024-06-19 15:37:33180 ("V8ShortBuiltinCalls"),
181 kFeatureDefaultStateControlledByV8);
[email protected]bda533ac2021-03-29 12:14:12182
Maya Lekova232168d2021-01-19 13:43:39183// Enables fast API calls in TurboFan.
Daniel Chengaab4b01b2022-09-20 14:28:38184BASE_FEATURE(kV8TurboFastApiCalls,
Leszek Swirski7a6cb1252024-06-19 15:37:33185 ("V8TurboFastApiCalls"),
186 kFeatureDefaultStateControlledByV8);
Maya Lekova232168d2021-01-19 13:43:39187
Camillo Bruni3a3f7ff2023-02-01 14:35:38188// Enables faster DOM methods for megamorphic ICs
Leszek Swirski7a6cb1252024-06-19 15:37:33189BASE_FEATURE(kV8MegaDomIC, ("V8MegaDomIC"), kFeatureDefaultStateControlledByV8);
Camillo Bruni3a3f7ff2023-02-01 14:35:38190
Olivier Flückigerd772e5d2024-06-07 07:21:25191// Faster object cloning
192BASE_FEATURE(kV8SideStepTransitions,
Leszek Swirski7a6cb1252024-06-19 15:37:33193 ("V8SideStepTransitions"),
194 kFeatureDefaultStateControlledByV8);
Olivier Flückigerd772e5d2024-06-07 07:21:25195
Dominik Inführ99a23d32023-08-02 15:40:29196// Avoids background threads for GC if isolate is in background.
197BASE_FEATURE(kV8SingleThreadedGCInBackground,
Leszek Swirski7a6cb1252024-06-19 15:37:33198 ("V8SingleThreadedGCInBackground"),
199 kFeatureDefaultStateControlledByV8);
Dominik Inführ99a23d32023-08-02 15:40:29200
Dominik Inführ06fa6672024-05-28 16:55:14201BASE_FEATURE(kV8SingleThreadedGCInBackgroundParallelPause,
Leszek Swirski7a6cb1252024-06-19 15:37:33202 ("V8SingleThreadedGCInBackgroundParallelPause"),
203 kFeatureDefaultStateControlledByV8);
Dominik Inführ06fa6672024-05-28 16:55:14204
205BASE_FEATURE(kV8SingleThreadedGCInBackgroundNoIncrementalMarking,
206 "V8SingleThreadedGCInBackgroundNoIncrementalMarking",
Leszek Swirski7a6cb1252024-06-19 15:37:33207 kFeatureDefaultStateControlledByV8);
Dominik Inführ06fa6672024-05-28 16:55:14208
Camillo Bruni262a21a2021-08-04 14:36:49209// Enables slow histograms that provide detailed information at increased
210// runtime overheads.
Daniel Chengaab4b01b2022-09-20 14:28:38211BASE_FEATURE(kV8SlowHistograms,
Leszek Swirski7a6cb1252024-06-19 15:37:33212 ("V8SlowHistograms"),
213 kFeatureDefaultStateControlledByV8);
Camillo Brunie04a51e52021-10-04 17:45:09214// Multiple finch experiments might use slow-histograms. We introduce
215// separate feature flags to circumvent finch limitations.
Daniel Chengaab4b01b2022-09-20 14:28:38216BASE_FEATURE(kV8SlowHistogramsCodeMemoryWriteProtection,
217 "V8SlowHistogramsCodeMemoryWriteProtection",
218 base::FEATURE_DISABLED_BY_DEFAULT);
Daniel Lehmannc9370852024-03-18 12:55:50219BASE_FEATURE(kV8SlowHistogramsIntelJCCErratumMitigation,
220 "V8SlowHistogramsIntelJCCErratumMitigation",
221 base::FEATURE_DISABLED_BY_DEFAULT);
Daniel Chengaab4b01b2022-09-20 14:28:38222BASE_FEATURE(kV8SlowHistogramsSparkplug,
223 "V8SlowHistogramsSparkplug",
224 base::FEATURE_DISABLED_BY_DEFAULT);
225BASE_FEATURE(kV8SlowHistogramsSparkplugAndroid,
226 "V8SlowHistogramsSparkplugAndroid",
227 base::FEATURE_DISABLED_BY_DEFAULT);
Camillo Brunic8566b862023-02-16 09:10:48228BASE_FEATURE(kV8SlowHistogramsNoTurbofan,
229 "V8SlowHistogramsNoTurbofan",
230 base::FEATURE_DISABLED_BY_DEFAULT);
Camillo Bruni262a21a2021-08-04 14:36:49231
Daniel Chengaab4b01b2022-09-20 14:28:38232BASE_FEATURE(kV8DelayMemoryReducer,
233 "V8DelayMemoryReducer",
François Doraye38ec6c2022-12-13 17:12:40234 base::FEATURE_ENABLED_BY_DEFAULT);
Etienne Pierre-doray87b4b84b2022-08-09 20:04:48235const base::FeatureParam<base::TimeDelta> kV8MemoryReducerStartDelay{
François Doraye38ec6c2022-12-13 17:12:40236 &kV8DelayMemoryReducer, "delay", base::Seconds(30)};
Etienne Pierre-doray87b4b84b2022-08-09 20:04:48237
Etienne Pierre-doray89f75072023-05-13 15:08:44238BASE_FEATURE(kV8ConcurrentMarkingHighPriorityThreads,
Leszek Swirski7a6cb1252024-06-19 15:37:33239 ("V8ConcurrentMarkingHighPriorityThreads"),
240 kFeatureDefaultStateControlledByV8);
Etienne Pierre-doray89f75072023-05-13 15:08:44241
Scott Violet744f9f872022-12-15 20:24:51242BASE_FEATURE(kV8UseLibmTrigFunctions,
Leszek Swirski7a6cb1252024-06-19 15:37:33243 ("V8UseLibmTrigFunctions"),
244 kFeatureDefaultStateControlledByV8);
Scott Violet744f9f872022-12-15 20:24:51245
Simon Zünd09d366b2024-03-21 14:53:37246BASE_FEATURE(kV8UseOriginalMessageForStackTrace,
Leszek Swirski7a6cb1252024-06-19 15:37:33247 ("V8UseOriginalMessageForStackTrace"),
248 kFeatureDefaultStateControlledByV8);
Simon Zünd09d366b2024-03-21 14:53:37249
Etienne Pierre-doray3976b762024-08-27 17:47:50250BASE_FEATURE(kV8IncrementalMarkingStartUserVisible,
251 ("V8IncrementalMarkingStartUserVisible"),
252 kFeatureDefaultStateControlledByV8);
253
Michael Lippautzdc5c8632024-04-23 16:02:41254BASE_FEATURE(kV8IdleGcOnContextDisposal,
Leszek Swirski7a6cb1252024-06-19 15:37:33255 ("V8IdleGcOnContextDisposal"),
256 kFeatureDefaultStateControlledByV8);
Michael Lippautzdc5c8632024-04-23 16:02:41257
Shu-yu Guo07bdfb42023-05-11 21:01:54258// Elide redundant TDZ hole checks in bytecode. This only sets the V8 flag when
259// manually overridden.
260BASE_FEATURE(kV8IgnitionElideRedundantTdzChecks,
Leszek Swirski7a6cb1252024-06-19 15:37:33261 ("V8IgnitionElideRedundantTdzChecks"),
262 kFeatureDefaultStateControlledByV8);
Shu-yu Guo07bdfb42023-05-11 21:01:54263
Daniel Lehmann456950c2024-02-09 14:05:54264// Add additional alignment for some jumps in generated x64 code, to mitigate
265// the performance impact of the Intel JCC erratum (https://crbug.com/v8/14225).
266// Currently disabled by default in V8, but adding here temporarily to test
267// real-world performance impact via a Finch experiment.
268BASE_FEATURE(kV8IntelJCCErratumMitigation,
Leszek Swirski7a6cb1252024-06-19 15:37:33269 ("V8IntelJCCErratumMitigation"),
270 kFeatureDefaultStateControlledByV8);
Daniel Lehmann456950c2024-02-09 14:05:54271
Shu-yu Guo61ae6bfa2022-11-04 15:13:31272// JavaScript language features.
273
Shu-yu Guo924f2732023-12-20 23:32:14274// Enables the RegExp modifiers proposal.
275BASE_FEATURE(kJavaScriptRegExpModifiers,
Leszek Swirski7a6cb1252024-06-19 15:37:33276 ("JavaScriptRegExpModifiers"),
277 kFeatureDefaultStateControlledByV8);
Shu-yu Guo924f2732023-12-20 23:32:14278
Nicolò Ribaudocc6f95722024-01-09 16:44:57279// Enables the `with` syntax for the Import Attributes proposal.
280BASE_FEATURE(kJavaScriptImportAttributes,
Leszek Swirski7a6cb1252024-06-19 15:37:33281 ("JavaScriptImportAttributes"),
282 kFeatureDefaultStateControlledByV8);
Nicolò Ribaudocc6f95722024-01-09 16:44:57283
Shu-yu Guobf2f7aa2024-01-31 19:01:55284// Enables the RegExp duplicate named capture groups proposal.
285BASE_FEATURE(kJavaScriptRegExpDuplicateNamedGroups,
Leszek Swirski7a6cb1252024-06-19 15:37:33286 ("JavaScriptRegExpDuplicateNamedGroups"),
287 kFeatureDefaultStateControlledByV8);
Shu-yu Guobf2f7aa2024-01-31 19:01:55288
Shu-yu Guo160b2ae2024-07-10 18:02:59289// Enables the Promise.try proposal.
290BASE_FEATURE(kJavaScriptPromiseTry,
291 ("JavaScriptPromiseTry"),
292 kFeatureDefaultStateControlledByV8);
293
Daniel Lehmannd60d88d12025-06-26 12:05:39294// WebAssembly features (currently none).
Daniel Lehmann4205b352024-07-15 11:01:49295
Ross McIlroy45e9500a2018-03-27 17:06:02296} // namespace features