LibWasm: Some more performance stuff (#8812)

* wasm: Don't try to print the function results if it traps

* LibWasm: Inline some very hot functions

These are mostly pretty small functions too, and they were about ~10%
of runtime.

* LibWasm+Everywhere: Make the instruction count limit configurable

...and enable it for LibWeb and test-wasm.
Note that `wasm` will not be limited by this.

* LibWasm: Remove a useless use of ScopeGuard

There are no multiple exit paths in that function, so we can just put
the ending logic right at the end of the function instead.
This commit is contained in:
Ali Mohammad Pur 2021-07-17 01:04:37 +04:30 committed by GitHub
parent 3099a6bf2a
commit 35394dbfaa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
Notes: sideshowbarker 2024-07-18 08:54:54 +09:00
8 changed files with 49 additions and 27 deletions

View file

@ -34,6 +34,7 @@ public:
explicit WebAssemblyModule(JS::Object& prototype)
: JS::Object(prototype)
{
m_machine.enable_instruction_count_limit();
}
static Wasm::AbstractMachine& machine() { return m_machine; }

View file

@ -128,6 +128,8 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
module.for_each_section_of_type<GlobalSection>([&](auto& global_section) {
for (auto& entry : global_section.entries()) {
Configuration config { m_store };
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
auxiliary_instance,
Vector<Value> {},
@ -153,6 +155,8 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
Vector<Reference> references;
for (auto& entry : segment.init) {
Configuration config { m_store };
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
main_module_instance,
Vector<Value> {},
@ -204,6 +208,8 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
return IterationDecision::Break;
}
Configuration config { m_store };
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
main_module_instance,
Vector<Value> {},
@ -262,6 +268,8 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
segment.value().visit(
[&](DataSection::Data::Active const& data) {
Configuration config { m_store };
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
main_module_instance,
Vector<Value> {},
@ -439,6 +447,8 @@ Result AbstractMachine::invoke(FunctionAddress address, Vector<Value> arguments)
Result AbstractMachine::invoke(Interpreter& interpreter, FunctionAddress address, Vector<Value> arguments)
{
Configuration configuration { m_store };
if (m_should_limit_instruction_count)
configuration.enable_instruction_count_limit();
return configuration.call(interpreter, address, move(arguments));
}

View file

@ -130,26 +130,26 @@ public:
}
}
Value(Value const& value)
ALWAYS_INLINE Value(Value const& value)
: m_value(AnyValueType { value.m_value })
, m_type(value.m_type)
{
}
Value(Value&& value)
ALWAYS_INLINE Value(Value&& value)
: m_value(move(value.m_value))
, m_type(move(value.m_type))
{
}
Value& operator=(Value&& value)
ALWAYS_INLINE Value& operator=(Value&& value)
{
m_value = move(value.m_value);
m_type = move(value.m_type);
return *this;
}
Value& operator=(Value const& value)
ALWAYS_INLINE Value& operator=(Value const& value)
{
m_value = value.m_value;
m_type = value.m_type;
@ -157,7 +157,7 @@ public:
}
template<typename T>
Optional<T> to()
ALWAYS_INLINE Optional<T> to()
{
Optional<T> result;
m_value.visit(
@ -505,10 +505,13 @@ public:
auto& store() const { return m_store; }
auto& store() { return m_store; }
void enable_instruction_count_limit() { m_should_limit_instruction_count = true; }
private:
Optional<InstantiationError> allocate_all_initial_phase(Module const&, ModuleInstance&, Vector<ExternValue>&, Vector<Value>& global_values);
Optional<InstantiationError> allocate_all_final_phase(Module const&, ModuleInstance&, Vector<Vector<Reference>>& elements);
Store m_store;
bool m_should_limit_instruction_count { false };
};
class Linker {

View file

@ -37,12 +37,15 @@ void BytecodeInterpreter::interpret(Configuration& configuration)
auto& instructions = configuration.frame().expression().instructions();
auto max_ip_value = InstructionPointer { instructions.size() };
auto& current_ip_value = configuration.ip();
auto const should_limit_instruction_count = configuration.should_limit_instruction_count();
u64 executed_instructions = 0;
while (current_ip_value < max_ip_value) {
if (executed_instructions++ >= Constants::max_allowed_executed_instructions_per_call) [[unlikely]] {
m_trap = Trap { "Exceeded maximum allowed number of instructions" };
return;
if (should_limit_instruction_count) {
if (executed_instructions++ >= Constants::max_allowed_executed_instructions_per_call) [[unlikely]] {
m_trap = Trap { "Exceeded maximum allowed number of instructions" };
return;
}
}
auto& instruction = instructions[current_ip_value.value()];
auto old_ip = current_ip_value;
@ -1123,17 +1126,15 @@ void DebuggerBytecodeInterpreter::interpret(Configuration& configuration, Instru
}
}
ScopeGuard guard { [&] {
if (post_interpret_hook) {
auto result = post_interpret_hook(configuration, ip, instruction, *this);
if (!result) {
m_trap = Trap { "Trapped by user request" };
return;
}
}
} };
BytecodeInterpreter::interpret(configuration, ip, instruction);
if (post_interpret_hook) {
auto result = post_interpret_hook(configuration, ip, instruction, *this);
if (!result) {
m_trap = Trap { "Trapped by user request" };
return;
}
}
}
}

View file

@ -50,7 +50,7 @@ protected:
T read_value(ReadonlyBytes data);
Vector<Value> pop_values(Configuration& configuration, size_t count);
bool trap_if_not(bool value, StringView reason)
ALWAYS_INLINE bool trap_if_not(bool value, StringView reason)
{
if (!value)
m_trap = Trap { reason };

View file

@ -61,6 +61,9 @@ public:
Result call(Interpreter&, FunctionAddress, Vector<Value> arguments);
Result execute(Interpreter&);
void enable_instruction_count_limit() { m_should_limit_instruction_count = true; }
bool should_limit_instruction_count() const { return m_should_limit_instruction_count; }
void dump_stack();
private:
@ -69,6 +72,7 @@ private:
Stack m_stack;
size_t m_depth { 0 };
InstructionPointer m_ip;
bool m_should_limit_instruction_count { false };
};
}

View file

@ -25,6 +25,7 @@ namespace Web::Bindings {
WebAssemblyObject::WebAssemblyObject(JS::GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
s_abstract_machine.enable_instruction_count_limit();
}
void WebAssemblyObject::initialize(JS::GlobalObject& global_object)

View file

@ -512,14 +512,16 @@ int main(int argc, char* argv[])
if (debug)
launch_repl();
if (result.is_trap())
warnln("Execution trapped!");
if (!result.values().is_empty())
warnln("Returned:");
for (auto& value : result.values()) {
Wasm::Printer printer { stream };
g_stdout.write(" -> "sv.bytes());
g_printer.print(value);
if (result.is_trap()) {
warnln("Execution trapped: {}", result.trap().reason);
} else {
if (!result.values().is_empty())
warnln("Returned:");
for (auto& value : result.values()) {
Wasm::Printer printer { stream };
g_stdout.write(" -> "sv.bytes());
g_printer.print(value);
}
}
}
}