Commit ab6d5ed5 authored by Ben L. Titzer's avatar Ben L. Titzer Committed by Commit Bot

Reland "[wasm] Add feature counter for threads and shared memory"

This is a reland of b10a967f

Original change's description:
> [wasm] Add feature counter for threads and shared memory
> 
> This adds a feature counter for WASM shared memory (i.e. the presence
> of the "shared" bit in a WASM module's memory section) and the usage
> of WASM threads opcodes (i.e. wake/wait and atomics).
> 
> This CL also plumbs the WasmFeatures through the compilation pipeline
> to detect features as functions are being compiled.
> 
> R=ahaas@chromium.org, ulan@chromium.org
> BUG=chromium:868844
> 
> Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
> Change-Id: I854f932d3adb16e4fd87196fe2a193950295b856
> Reviewed-on: https://chromium-review.googlesource.com/1186329
> Reviewed-by: Andreas Haas <ahaas@chromium.org>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Commit-Queue: Ben Titzer <titzer@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#55337}

Bug: chromium:868844
Change-Id: Iac3a38d80fa71aadd7147704669a8fd671ecfae8
Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Reviewed-on: https://chromium-review.googlesource.com/1186343
Commit-Queue: Ben Titzer <titzer@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55363}
parent 50e142c9
......@@ -5031,7 +5031,8 @@ TurbofanWasmCompilationUnit::TurbofanWasmCompilationUnit(
TurbofanWasmCompilationUnit::~TurbofanWasmCompilationUnit() = default;
SourcePositionTable* TurbofanWasmCompilationUnit::BuildGraphForWasmFunction(
double* decode_ms, MachineGraph* mcgraph, NodeOriginTable* node_origins) {
wasm::WasmFeatures* detected, double* decode_ms, MachineGraph* mcgraph,
NodeOriginTable* node_origins) {
base::ElapsedTimer decode_timer;
if (FLAG_trace_wasm_decode_time) {
decode_timer.Start();
......@@ -5042,14 +5043,10 @@ SourcePositionTable* TurbofanWasmCompilationUnit::BuildGraphForWasmFunction(
new (mcgraph->zone()) SourcePositionTable(mcgraph->graph());
WasmGraphBuilder builder(wasm_unit_->env_, mcgraph->zone(), mcgraph,
wasm_unit_->func_body_.sig, source_position_table);
// TODO(titzer): gather detected features into a per-module location
// in order to increment an embedder feature use count.
wasm::WasmFeatures unused_detected_features;
graph_construction_result_ = wasm::BuildTFGraph(
wasm_unit_->wasm_engine_->allocator(),
wasm_unit_->native_module_->enabled_features(), wasm_unit_->env_->module,
&builder, &unused_detected_features, wasm_unit_->func_body_,
node_origins);
&builder, detected, wasm_unit_->func_body_, node_origins);
if (graph_construction_result_.failed()) {
if (FLAG_trace_wasm_compiler) {
StdoutStream{} << "Compilation failed: "
......@@ -5102,7 +5099,8 @@ Vector<const char> GetDebugName(Zone* zone, wasm::WasmName name, int index) {
} // namespace
void TurbofanWasmCompilationUnit::ExecuteCompilation() {
void TurbofanWasmCompilationUnit::ExecuteCompilation(
wasm::WasmFeatures* detected) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
"ExecuteTurbofanCompilation");
double decode_ms = 0;
......@@ -5134,7 +5132,7 @@ void TurbofanWasmCompilationUnit::ExecuteCompilation() {
NodeOriginTable(mcgraph->graph())
: nullptr;
SourcePositionTable* source_positions =
BuildGraphForWasmFunction(&decode_ms, mcgraph, node_origins);
BuildGraphForWasmFunction(detected, &decode_ms, mcgraph, node_origins);
if (graph_construction_result_.failed()) {
ok_ = false;
......
......@@ -40,6 +40,7 @@ struct DecodeStruct;
typedef compiler::Node TFNode;
typedef compiler::MachineGraph TFGraph;
class WasmCode;
struct WasmFeatures;
} // namespace wasm
namespace compiler {
......@@ -49,11 +50,12 @@ class TurbofanWasmCompilationUnit {
explicit TurbofanWasmCompilationUnit(wasm::WasmCompilationUnit* wasm_unit);
~TurbofanWasmCompilationUnit();
SourcePositionTable* BuildGraphForWasmFunction(double* decode_ms,
SourcePositionTable* BuildGraphForWasmFunction(wasm::WasmFeatures* detected,
double* decode_ms,
MachineGraph* mcgraph,
NodeOriginTable* node_origins);
void ExecuteCompilation();
void ExecuteCompilation(wasm::WasmFeatures* detected);
wasm::WasmCode* FinishCompilation(wasm::ErrorThrower*);
......
......@@ -1835,7 +1835,7 @@ class LiftoffCompiler {
} // namespace
bool LiftoffCompilationUnit::ExecuteCompilation() {
bool LiftoffCompilationUnit::ExecuteCompilation(WasmFeatures* detected) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
"ExecuteLiftoffCompilation");
base::ElapsedTimer compile_timer;
......@@ -1850,11 +1850,9 @@ bool LiftoffCompilationUnit::ExecuteCompilation() {
compiler::GetWasmCallDescriptor(&zone, wasm_unit_->func_body_.sig);
base::Optional<TimedHistogramScope> liftoff_compile_time_scope(
base::in_place, wasm_unit_->counters_->liftoff_compile_time());
WasmFeatures unused_detected_features;
WasmFullDecoder<Decoder::kValidate, LiftoffCompiler> decoder(
&zone, module, wasm_unit_->native_module_->enabled_features(),
&unused_detected_features, wasm_unit_->func_body_, call_descriptor,
wasm_unit_->env_, &zone);
&zone, module, wasm_unit_->native_module_->enabled_features(), detected,
wasm_unit_->func_body_, call_descriptor, wasm_unit_->env_, &zone);
decoder.Decode();
liftoff_compile_time_scope.reset();
LiftoffCompiler* compiler = &decoder.interface();
......
......@@ -11,6 +11,7 @@ namespace v8 {
namespace internal {
namespace wasm {
struct WasmFeatures;
class ErrorThrower;
class WasmCode;
class WasmCompilationUnit;
......@@ -20,7 +21,7 @@ class LiftoffCompilationUnit final {
explicit LiftoffCompilationUnit(WasmCompilationUnit* wasm_unit)
: wasm_unit_(wasm_unit) {}
bool ExecuteCompilation();
bool ExecuteCompilation(WasmFeatures* detected);
WasmCode* FinishCompilation(ErrorThrower*);
private:
......
......@@ -73,7 +73,7 @@ WasmCompilationUnit::WasmCompilationUnit(WasmEngine* wasm_engine,
// {TurbofanWasmCompilationUnit} can be opaque in the header file.
WasmCompilationUnit::~WasmCompilationUnit() {}
void WasmCompilationUnit::ExecuteCompilation() {
void WasmCompilationUnit::ExecuteCompilation(WasmFeatures* detected) {
auto size_histogram = SELECT_WASM_COUNTER(counters_, env_->module->origin,
wasm, function_size_bytes);
size_histogram->AddSample(
......@@ -89,12 +89,12 @@ void WasmCompilationUnit::ExecuteCompilation() {
switch (mode_) {
case ExecutionTier::kBaseline:
if (liftoff_unit_->ExecuteCompilation()) break;
if (liftoff_unit_->ExecuteCompilation(detected)) break;
// Otherwise, fall back to turbofan.
SwitchMode(ExecutionTier::kOptimized);
V8_FALLTHROUGH;
case ExecutionTier::kOptimized:
turbofan_unit_->ExecuteCompilation();
turbofan_unit_->ExecuteCompilation(detected);
break;
case ExecutionTier::kInterpreter:
UNREACHABLE(); // TODO(titzer): compile interpreter entry stub.
......@@ -145,8 +145,9 @@ void WasmCompilationUnit::SwitchMode(ExecutionTier new_mode) {
// static
WasmCode* WasmCompilationUnit::CompileWasmFunction(
NativeModule* native_module, ErrorThrower* thrower, Isolate* isolate,
ModuleEnv* env, const WasmFunction* function, ExecutionTier mode) {
Isolate* isolate, NativeModule* native_module, WasmFeatures* detected,
ErrorThrower* thrower, ModuleEnv* env, const WasmFunction* function,
ExecutionTier mode) {
ModuleWireBytes wire_bytes(native_module->wire_bytes());
FunctionBody function_body{function->sig, function->code.offset(),
wire_bytes.start() + function->code.offset(),
......@@ -156,7 +157,7 @@ WasmCode* WasmCompilationUnit::CompileWasmFunction(
function_body,
wire_bytes.GetNameOrNull(function, env->module),
function->func_index, isolate->counters(), mode);
unit.ExecuteCompilation();
unit.ExecuteCompilation(detected);
return unit.FinishCompilation(thrower);
}
......
......@@ -91,12 +91,12 @@ class WasmCompilationUnit final {
~WasmCompilationUnit();
void ExecuteCompilation();
void ExecuteCompilation(WasmFeatures* detected);
WasmCode* FinishCompilation(ErrorThrower* thrower);
static WasmCode* CompileWasmFunction(
NativeModule* native_module, ErrorThrower* thrower, Isolate* isolate,
ModuleEnv* env, const WasmFunction* function,
Isolate* isolate, NativeModule* native_module, WasmFeatures* detected,
ErrorThrower* thrower, ModuleEnv* env, const WasmFunction* function,
ExecutionTier = GetDefaultExecutionTier());
NativeModule* native_module() const { return native_module_; }
......
......@@ -90,7 +90,8 @@ class CompilationState {
void ScheduleUnitForFinishing(std::unique_ptr<WasmCompilationUnit> unit,
ExecutionTier mode);
void OnBackgroundTaskStopped();
void OnBackgroundTaskStopped(const WasmFeatures& detected);
void PublishDetectedFeatures(Isolate* isolate, const WasmFeatures& detected);
void RestartBackgroundTasks(size_t max = std::numeric_limits<size_t>::max());
// Only one foreground thread (finisher) is allowed to run at a time.
// {SetFinisherIsRunning} returns whether the flag changed its state.
......@@ -113,6 +114,7 @@ class CompilationState {
WasmEngine* wasm_engine() const { return wasm_engine_; }
CompileMode compile_mode() const { return compile_mode_; }
ModuleEnv* module_env() { return &module_env_; }
WasmFeatures* detected_features() { return &detected_features_; }
private:
void NotifyOnEvent(CompilationEvent event, ErrorThrower* thrower);
......@@ -148,6 +150,10 @@ class CompilationState {
std::vector<std::unique_ptr<WasmCompilationUnit>> baseline_finish_units_;
std::vector<std::unique_ptr<WasmCompilationUnit>> tiering_finish_units_;
// Features detected to be used in this module. Features can be detected
// as a module is being compiled.
WasmFeatures detected_features_ = kNoWasmFeatures;
// End of fields protected by {mutex_}.
//////////////////////////////////////////////////////////////////////////////
......@@ -166,6 +172,12 @@ class CompilationState {
namespace {
void UpdateFeatureUseCounts(Isolate* isolate, const WasmFeatures& detected) {
if (detected.threads) {
isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmThreadOpcodes);
}
}
class JSToWasmWrapperCache {
public:
Handle<Code> GetOrCompileJSToWasmWrapper(Isolate* isolate,
......@@ -359,7 +371,8 @@ WasmCode* LazyCompileFunction(Isolate* isolate, NativeModule* native_module,
ErrorThrower thrower(isolate, "WasmLazyCompile");
WasmCompilationUnit unit(isolate->wasm_engine(), module_env, native_module,
body, func_name, func_index, isolate->counters());
unit.ExecuteCompilation();
unit.ExecuteCompilation(
native_module->compilation_state()->detected_features());
WasmCode* wasm_code = unit.FinishCompilation(&thrower);
if (WasmCode::ShouldBeLogged(isolate)) wasm_code->LogCode(isolate);
......@@ -500,7 +513,8 @@ class CompilationUnitBuilder {
// foreground and background threads). The no_finisher_callback is called
// within the result_mutex_ lock when no finishing task is running, i.e. when
// the finisher_is_running_ flag is not set.
bool FetchAndExecuteCompilationUnit(CompilationState* compilation_state) {
bool FetchAndExecuteCompilationUnit(CompilationState* compilation_state,
WasmFeatures* detected) {
DisallowHeapAccess no_heap_access;
std::unique_ptr<WasmCompilationUnit> unit =
......@@ -513,7 +527,7 @@ bool FetchAndExecuteCompilationUnit(CompilationState* compilation_state) {
// later as soon as Liftoff can compile any function. Then, we can directly
// access {unit->mode()} within {ScheduleUnitForFinishing()}.
ExecutionTier mode = unit->mode();
unit->ExecuteCompilation();
unit->ExecuteCompilation(detected);
compilation_state->ScheduleUnitForFinishing(std::move(unit), mode);
return true;
......@@ -614,8 +628,10 @@ void CompileInParallel(Isolate* isolate, NativeModule* native_module,
// result is enqueued in {baseline_finish_units_}.
// The foreground task bypasses waiting on memory threshold, because
// its results will immediately be converted to code (below).
while (FetchAndExecuteCompilationUnit(compilation_state) &&
!compilation_state->baseline_compilation_finished()) {
WasmFeatures detected_features;
while (
FetchAndExecuteCompilationUnit(compilation_state, &detected_features) &&
!compilation_state->baseline_compilation_finished()) {
// 2.b) If {baseline_finish_units_} contains a compilation unit, the main
// thread dequeues it and finishes the compilation unit. Compilation
// units are finished concurrently to the background threads to save
......@@ -636,6 +652,9 @@ void CompileInParallel(Isolate* isolate, NativeModule* native_module,
if (compilation_state->baseline_compilation_finished()) break;
}
// Publish features from the foreground and background tasks.
compilation_state->PublishDetectedFeatures(isolate, detected_features);
// 4) If tiering-compilation is enabled, we need to set the finisher
// to false, such that the background threads will spawn a foreground
// thread to finish the top-tier compilation units.
......@@ -652,13 +671,14 @@ void CompileSequentially(Isolate* isolate, NativeModule* native_module,
ModuleWireBytes wire_bytes(native_module->wire_bytes());
const WasmModule* module = module_env->module;
WasmFeatures detected = kNoWasmFeatures;
for (uint32_t i = 0; i < module->functions.size(); ++i) {
const WasmFunction& func = module->functions[i];
if (func.imported) continue; // Imports are compiled at instantiation time.
// Compile the function.
WasmCode* code = WasmCompilationUnit::CompileWasmFunction(
native_module, thrower, isolate, module_env, &func);
isolate, native_module, &detected, thrower, module_env, &func);
if (code == nullptr) {
TruncatedUserString<> name(wire_bytes.GetName(&func, module));
thrower->CompileError("Compilation of #%d:%.*s failed.", i, name.length(),
......@@ -666,6 +686,7 @@ void CompileSequentially(Isolate* isolate, NativeModule* native_module,
break;
}
}
UpdateFeatureUseCounts(isolate, detected);
}
void ValidateSequentially(Isolate* isolate, NativeModule* native_module,
......@@ -831,15 +852,17 @@ class BackgroundCompileTask : public CancelableTask {
// The number of currently running background tasks is reduced in
// {OnBackgroundTaskStopped}.
while (!compilation_state_->failed()) {
if (!FetchAndExecuteCompilationUnit(compilation_state_)) {
if (!FetchAndExecuteCompilationUnit(compilation_state_,
&detected_features_)) {
break;
}
}
compilation_state_->OnBackgroundTaskStopped();
compilation_state_->OnBackgroundTaskStopped(detected_features_);
}
private:
CompilationState* compilation_state_;
WasmFeatures detected_features_ = kNoWasmFeatures;
};
} // namespace
......@@ -851,6 +874,12 @@ MaybeHandle<WasmModuleObject> CompileToModuleObject(
const WasmModule* wasm_module = module.get();
TimedHistogramScope wasm_compile_module_time_scope(SELECT_WASM_COUNTER(
isolate->counters(), wasm_module->origin, wasm_compile, module_time));
// Embedder usage count for declared shared memories.
if (wasm_module->has_shared_memory) {
isolate->CountUsage(v8::Isolate::UseCounterFeature::kWasmSharedMemory);
}
// TODO(6792): No longer needed once WebAssembly code is off heap. Use
// base::Optional to be able to close the scope before notifying the debugger.
base::Optional<CodeSpaceMemoryModificationScope> modification_scope(
......@@ -2191,6 +2220,11 @@ void AsyncCompileJob::FinishCompile() {
// Log the code within the generated module for profiling.
native_module_->LogWasmCodes(isolate_);
// We can only update the feature counts once the entire compile is done.
auto compilation_state = native_module_->compilation_state();
compilation_state->PublishDetectedFeatures(
isolate_, *compilation_state->detected_features());
// TODO(wasm): compiling wrappers should be made async as well.
DoSync<CompileWrappers>();
}
......@@ -2376,6 +2410,12 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
// is done.
job_->background_task_manager_.CancelAndWait();
// Embedder usage count for declared shared memories.
if (job_->module_->has_shared_memory) {
job_->isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kWasmSharedMemory);
}
// Create heap objects for script and module bytes to be stored in the
// module object. Asm.js is not compiled asynchronously.
Handle<Script> script = CreateWasmScript(job_->isolate_, job_->wire_bytes_);
......@@ -2876,10 +2916,21 @@ void CompilationState::ScheduleUnitForFinishing(
}
}
void CompilationState::OnBackgroundTaskStopped() {
void CompilationState::OnBackgroundTaskStopped(const WasmFeatures& detected) {
base::LockGuard<base::Mutex> guard(&mutex_);
DCHECK_LE(1, num_background_tasks_);
--num_background_tasks_;
UnionFeaturesInto(&detected_features_, detected);
}
void CompilationState::PublishDetectedFeatures(Isolate* isolate,
const WasmFeatures& detected) {
// Notifying the isolate of the feature counts must take place under
// the mutex, because even if we have finished baseline compilation,
// tiering compilations may still occur in the background.
base::LockGuard<base::Mutex> guard(&mutex_);
UnionFeaturesInto(&detected_features_, detected);
UpdateFeatureUseCounts(isolate, detected_features_);
}
void CompilationState::RestartBackgroundTasks(size_t max) {
......
......@@ -171,8 +171,10 @@ std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
bool WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
uint32_t function_index, ExecutionTier tier) {
ErrorThrower thrower(isolate, "Manually requested tier up");
// Note we assume that "one-off" compilations can discard detected features.
WasmFeatures detected = kNoWasmFeatures;
WasmCode* ret = WasmCompilationUnit::CompileWasmFunction(
native_module, &thrower, isolate,
isolate, native_module, &detected, &thrower,
GetModuleEnv(native_module->compilation_state()),
&native_module->module()->functions[function_index], tier);
return ret != nullptr;
......
......@@ -13,11 +13,11 @@ namespace wasm {
#define COMMA ,
#define SPACE
#define DO_UNION(feat, desc, val) dst->feat |= src->feat;
#define DO_UNION(feat, desc, val) dst->feat |= src.feat;
#define FLAG_REF(feat, desc, val) FLAG_experimental_wasm_##feat
void UnionFeaturesInto(WasmFeatures* dst, WasmFeatures* src) {
FOREACH_WASM_FEATURE(DO_UNION, SPACE)
void UnionFeaturesInto(WasmFeatures* dst, const WasmFeatures& src) {
FOREACH_WASM_FEATURE(DO_UNION, SPACE);
}
WasmFeatures WasmFeaturesFromFlags() {
......
......@@ -57,7 +57,8 @@ V8_EXPORT_PRIVATE WasmFeatures WasmFeaturesFromFlags();
// Precondition: A valid context must be set in {isolate->context()}.
V8_EXPORT_PRIVATE WasmFeatures WasmFeaturesFromIsolate(Isolate* isolate);
V8_EXPORT_PRIVATE void UnionFeaturesInto(WasmFeatures* dst, WasmFeatures* src);
V8_EXPORT_PRIVATE void UnionFeaturesInto(WasmFeatures* dst,
const WasmFeatures& src);
} // namespace wasm
} // namespace internal
......
......@@ -352,8 +352,9 @@ TEST(SharedEngineRunThreadedTierUp) {
HandleScope scope(isolate.isolate());
Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
ErrorThrower thrower(isolate.isolate(), "Forced Tier Up");
WasmFeatures detected = kNoWasmFeatures;
WasmCompilationUnit::CompileWasmFunction(
module.get(), &thrower, isolate.isolate(),
isolate.isolate(), module.get(), &detected, &thrower,
GetModuleEnv(module->compilation_state()),
&module->module()->functions[0], ExecutionTier::kOptimized);
CHECK_EQ(23, isolate.Run(instance));
......
......@@ -432,7 +432,8 @@ void WasmFunctionCompiler::Build(const byte* start, const byte* end) {
WasmCompilationUnit unit(isolate()->wasm_engine(), &module_env, native_module,
func_body, func_name, function_->func_index,
isolate()->counters(), tier);
unit.ExecuteCompilation();
WasmFeatures unused_detected_features;
unit.ExecuteCompilation(&unused_detected_features);
WasmCode* wasm_code = unit.FinishCompilation(&thrower);
if (WasmCode::ShouldBeLogged(isolate())) {
wasm_code->LogCode(isolate());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment