LibWasm: Implement reference instructions (ref.{null,func,is_null})

This commit is contained in:
Ali Mohammad Pur 2021-06-01 09:48:36 +04:30 committed by Ali Mohammad Pur
parent 7fb458b7c9
commit 56bf80251c
Notes: sideshowbarker 2024-07-18 17:01:23 +09:00
8 changed files with 73 additions and 9 deletions

View file

@ -190,6 +190,12 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
case Wasm::ValueType::Kind::ExternReference:
arguments.append(Wasm::Value(Wasm::ExternAddress { static_cast<u64>(value) }));
break;
case Wasm::ValueType::Kind::NullFunctionReference:
arguments.append(Wasm::Value(Wasm::Value::Null { Wasm::ValueType(Wasm::ValueType::Kind::FunctionReference) }));
break;
case Wasm::ValueType::Kind::NullExternReference:
arguments.append(Wasm::Value(Wasm::Value::Null { Wasm::ValueType(Wasm::ValueType::Kind::ExternReference) }));
break;
}
}
@ -206,6 +212,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
result.values().first().value().visit(
[&](const auto& value) { return_value = JS::Value(static_cast<double>(value)); },
[&](const Wasm::FunctionAddress& index) { return_value = JS::Value(static_cast<double>(index.value())); },
[&](const Wasm::ExternAddress& index) { return_value = JS::Value(static_cast<double>(index.value())); });
[&](const Wasm::ExternAddress& index) { return_value = JS::Value(static_cast<double>(index.value())); },
[&](const Wasm::Value::Null&) { return_value = JS::js_null(); });
return return_value;
}

View file

@ -152,7 +152,8 @@ InstantiationResult AbstractMachine::instantiate(const Module& module, Vector<Ex
result.values().first().value().visit(
[&](const auto& value) { offset = value; },
[&](const FunctionAddress&) { instantiation_result = InstantiationError { "Data segment offset returned an address" }; },
[&](const ExternAddress&) { instantiation_result = InstantiationError { "Data segment offset returned an address" }; });
[&](const ExternAddress&) { instantiation_result = InstantiationError { "Data segment offset returned an address" }; },
[&](const Value::Null&) { instantiation_result = InstantiationError { "Data segment offset returned a null reference" }; });
if (instantiation_result.has_value() && instantiation_result->is_error())
return;
if (main_module_instance.memories().size() <= data.index.value()) {

View file

@ -45,7 +45,11 @@ public:
{
}
using AnyValueType = Variant<i32, i64, float, double, FunctionAddress, ExternAddress>;
struct Null {
ValueType type;
};
using AnyValueType = Variant<i32, i64, float, double, FunctionAddress, ExternAddress, Null>;
explicit Value(AnyValueType value)
: m_value(move(value))
, m_type(ValueType::I32)
@ -62,6 +66,8 @@ public:
m_type = ValueType { ValueType::FunctionReference };
else if (m_value.has<ExternAddress>())
m_type = ValueType { ValueType::ExternReference };
else if (m_value.has<Null>())
m_type = ValueType { m_value.get<Null>().type.kind() == ValueType::ExternReference ? ValueType::NullExternReference : ValueType::NullFunctionReference };
else
VERIFY_NOT_REACHED();
}
@ -90,6 +96,14 @@ public:
case ValueType::Kind::F64:
m_value = bit_cast<double>(raw_value);
break;
case ValueType::Kind::NullFunctionReference:
VERIFY(raw_value == 0);
m_value = Null { ValueType(ValueType::Kind::FunctionReference) };
break;
case ValueType::Kind::NullExternReference:
VERIFY(raw_value == 0);
m_value = Null { ValueType(ValueType::Kind::ExternReference) };
break;
default:
VERIFY_NOT_REACHED();
}
@ -139,6 +153,10 @@ public:
[&](const ExternAddress& address) {
if constexpr (IsSame<T, ExternAddress>)
result = address;
},
[&](const Null& null) {
if constexpr (IsSame<T, Null>)
result = null;
});
return result;
}

View file

@ -85,6 +85,8 @@ void Configuration::dump_stack()
v.value().visit([]<typename T>(const T& v) {
if constexpr (IsIntegral<T> || IsFloatingPoint<T>)
dbgln(" {}", v);
else if constexpr (IsSame<Value::Null, T>)
dbgln(" *null");
else
dbgln(" *{}", v.value());
});
@ -95,6 +97,8 @@ void Configuration::dump_stack()
local.value().visit([]<typename T>(const T& v) {
if constexpr (IsIntegral<T> || IsFloatingPoint<T>)
dbgln(" {}", v);
else if constexpr (IsSame<Value::Null, T>)
dbgln(" *null");
else
dbgln(" *{}", v.value());
});

View file

@ -590,10 +590,29 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
}
case Instructions::table_get.value():
case Instructions::table_set.value():
case Instructions::ref_null.value():
case Instructions::ref_func.value():
case Instructions::ref_is_null.value():
goto unimplemented;
case Instructions::ref_null.value(): {
auto type = instruction.arguments().get<ValueType>();
TRAP_IF_NOT(type.is_reference());
configuration.stack().push(Value(Value::Null { type }));
return;
};
case Instructions::ref_func.value(): {
auto index = instruction.arguments().get<FunctionIndex>().value();
auto& functions = configuration.frame().module().functions();
TRAP_IF_NOT(functions.size() > index);
auto address = functions[index];
configuration.stack().push(Value(ValueType(ValueType::FunctionReference), address.value()));
return;
}
case Instructions::ref_is_null.value(): {
auto top = configuration.stack().peek().get_pointer<Value>();
TRAP_IF_NOT(top);
TRAP_IF_NOT(top->type().is_reference());
auto is_null = top->to<Value::Null>().has_value();
configuration.stack().peek() = Value(ValueType(ValueType::I32), static_cast<u64>(is_null ? 1 : 0));
return;
}
case Instructions::drop.value():
configuration.stack().pop();
return;

View file

@ -170,6 +170,8 @@ public:
F64,
FunctionReference,
ExternReference,
NullFunctionReference,
NullExternReference,
};
explicit ValueType(Kind kind)
@ -177,7 +179,7 @@ public:
{
}
auto is_reference() const { return m_kind == ExternReference || m_kind == FunctionReference; }
auto is_reference() const { return m_kind == ExternReference || m_kind == FunctionReference || m_kind == NullExternReference || m_kind == NullFunctionReference; }
auto is_numeric() const { return !is_reference(); }
auto kind() const { return m_kind; }
@ -198,6 +200,10 @@ public:
return "funcref";
case ExternReference:
return "externref";
case NullFunctionReference:
return "ref.null externref";
case NullExternReference:
return "ref.null funcref";
}
VERIFY_NOT_REACHED();
}

View file

@ -262,7 +262,10 @@ JS::Value to_js_value(Wasm::Value& wasm_value, JS::GlobalObject& global_object)
case Wasm::ValueType::FunctionReference:
// FIXME: What's the name of a function reference that isn't exported?
return create_native_function(wasm_value.to<Wasm::FunctionAddress>().value(), "FIXME_IHaveNoIdeaWhatThisShouldBeCalled", global_object);
case Wasm::ValueType::NullFunctionReference:
return JS::js_null();
case Wasm::ValueType::ExternReference:
case Wasm::ValueType::NullExternReference:
TODO();
}
VERIFY_NOT_REACHED();
@ -304,6 +307,8 @@ Optional<Wasm::Value> to_webassembly_value(JS::Value value, const Wasm::ValueTyp
}
case Wasm::ValueType::FunctionReference:
case Wasm::ValueType::ExternReference:
case Wasm::ValueType::NullFunctionReference:
case Wasm::ValueType::NullExternReference:
TODO();
}

View file

@ -202,9 +202,11 @@ static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPoi
warnln("Returned:");
for (auto& value : result.values()) {
auto str = value.value().visit(
[&](const auto& value) {
[&]<typename T>(const T& value) {
if constexpr (requires { value.value(); })
return String::formatted(" -> addr{} ", value.value());
else if constexpr (IsSame<Wasm::Value::Null, T>)
return String::formatted(" ->addr(null)");
else
return String::formatted(" -> {} ", value);
});
@ -460,9 +462,11 @@ int main(int argc, char* argv[])
warnln("Returned:");
for (auto& value : result.values()) {
value.value().visit(
[&](const auto& value) {
[&]<typename T>(const T& value) {
if constexpr (requires { value.value(); })
out(" -> addr{} ", value.value());
else if constexpr (IsSame<Wasm::Value::Null, T>)
out(" ->addr(null)");
else
out(" -> {} ", value);
});