Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ enum class Type : int32_t {
Void = -0x40, // 0x40
___ = Void, // Convenient for the opcode table in opcode.h
Any = 0, // Not actually specified, but useful for type-checking
Nullref = 1, // Not actually specified, but useful for anyref type-checking
Nullref = 1, // Not actually specified, but used in testing and type-checking
Hostref = 2, // Not actually specified, but used in testing and type-checking
};
typedef std::vector<Type> TypeVector;

Expand Down
2 changes: 1 addition & 1 deletion src/interp/binary-reader-interp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1014,7 +1014,7 @@ wabt::Result BinaryReaderInterp::OnInitExprI64ConstExpr(Index index,

wabt::Result BinaryReaderInterp::OnInitExprRefNull(Index index) {
init_expr_value_.type = Type::Nullref;
init_expr_value_.set_ref({RefType::Func, kInvalidIndex});
init_expr_value_.set_ref({RefType::Null, kInvalidIndex});
return wabt::Result::Ok;
}

Expand Down
47 changes: 30 additions & 17 deletions src/interp/interp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include "src/cast.h"
#include "src/stream.h"
#include "src/type-checker.h"

namespace wabt {
namespace interp {
Expand All @@ -53,6 +54,17 @@ namespace interp {
} \
} while (0)

std::string RefTypeToString(RefType t) {
switch (t) {
case RefType::Null:
return "null";
case RefType::Func:
return "func";
case RefType::Host:
return "host";
}
}

std::string TypedValueToString(const TypedValue& tv) {
switch (tv.type) {
case Type::I32:
Expand All @@ -75,17 +87,20 @@ std::string TypedValueToString(const TypedValue& tv) {
case Type::Nullref:
return StringPrintf("nullref");

case Type::Anyref:
if (tv.get_ref().index == kInvalidIndex)
return StringPrintf("anyref:nullref");
return StringPrintf("anyref:%d(%" PRIindex ")",
static_cast<int>(tv.get_ref().kind),
tv.get_ref().index);
case Type::Hostref:
return StringPrintf("hostref:%" PRIindex, tv.get_ref().index);

case Type::Funcref:
return StringPrintf("funcref:%" PRIindex, tv.get_ref().index);

default:
case Type::Exnref:
return StringPrintf("exnref:%" PRIindex, tv.get_ref().index);

case Type::Func:
case Type::Void:
case Type::Any:
case Type::Anyref:
// These types are not concrete types and should never exist as a value
WABT_UNREACHABLE;
}
}
Expand Down Expand Up @@ -1072,8 +1087,6 @@ Result Thread::TableInit(const uint8_t** pc) {

Result Thread::TableSet(const uint8_t** pc) {
Table* table = ReadTable(pc);
// We currently only support tables of Funcref.
assert(table->elem_type == Type::Funcref);
Ref ref = Pop<Ref>();
uint32_t index = Pop<uint32_t>();
TRAP_IF(index >= table->size(), TableAccessOutOfBounds);
Expand All @@ -1083,7 +1096,6 @@ Result Thread::TableSet(const uint8_t** pc) {

Result Thread::TableGet(const uint8_t** pc) {
Table* table = ReadTable(pc);
assert(table->elem_type == Type::Funcref);
uint32_t index = Pop<uint32_t>();
TRAP_IF(index >= table->size(), TableAccessOutOfBounds);
Ref ref = static_cast<Ref>(table->entries[index]);
Expand Down Expand Up @@ -1716,10 +1728,8 @@ Result Thread::CallHost(HostFunc* func) {
TypedValues params(num_params);
TypedValues results(num_results);

for (size_t i = num_params; i > 0; --i) {
params[i - 1].value = Pop();
params[i - 1].type = sig->param_types[i - 1];
}
for (size_t i = num_params; i > 0; --i)
params[i - 1] = {sig->param_types[i - 1], Pop()};

for (size_t i = 0; i < num_results; ++i) {
results[i].type = sig->result_types[i];
Expand All @@ -1731,7 +1741,9 @@ Result Thread::CallHost(HostFunc* func) {

TRAP_IF(results.size() != num_results, HostResultTypeMismatch);
for (size_t i = 0; i < num_results; ++i) {
TRAP_IF(results[i].type != sig->result_types[i], HostResultTypeMismatch);
TRAP_IF(TypeChecker::CheckType(results[i].type, sig->result_types[i]) !=
wabt::Result::Ok,
HostResultTypeMismatch);
CHECK_TRAP(Push(results[i].value));
}

Expand Down Expand Up @@ -1821,7 +1833,7 @@ Result Thread::Run(int num_instructions) {
case Opcode::GlobalSet: {
Index index = ReadU32(&pc);
assert(index < env_->globals_.size());
env_->globals_[index].typed_value.value = Pop();
env_->globals_[index].typed_value = {env_->globals_[index].type, Pop()};
break;
}

Expand Down Expand Up @@ -3698,7 +3710,8 @@ Result Executor::PushArgs(const FuncSignature* sig, const TypedValues& args) {
}

for (size_t i = 0; i < sig->param_types.size(); ++i) {
if (sig->param_types[i] != args[i].type) {
if (TypeChecker::CheckType(args[i].type, sig->param_types[i]) !=
wabt::Result::Ok) {
return Result::ArgumentTypeMismatch;
}

Expand Down
42 changes: 38 additions & 4 deletions src/interp/interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ struct FuncSignature {
enum class RefType {
Func,
Null,
Host
Host,
};

struct Ref {
RefType kind;
Index index; // Only meaningful for RefType::FUNC
Index index; // Not meaningful for RefType::Null
};

struct Table {
Expand Down Expand Up @@ -146,6 +146,9 @@ struct ElemSegment {
bool dropped = false;
};

// Opaque handle to a host object.
struct HostObject {};

// ValueTypeRep converts from one type to its representation on the
// stack. For example, float -> uint32_t. See Value below.
template <typename T>
Expand Down Expand Up @@ -175,10 +178,32 @@ union Value {
struct TypedValue {
TypedValue() {}
explicit TypedValue(Type type) : type(type) {}
TypedValue(Type type, const Value& value) : type(type), value(value) {}
TypedValue(Type basetype, const Value& value) : type(basetype), value(value) {
SetConcreteType();
}

void SetConcreteType() {
// Anyref is an abstract type. The actual type is stored in value.
if (type == Type::Anyref) {
switch (value.ref.kind) {
case RefType::Func:
type = Type::Funcref;
break;
case RefType::Null:
type = Type::Nullref;
break;
case RefType::Host:
type = Type::Hostref;
break;
}
}
}

void SetZero() { ZeroMemory(value); }
void set_ref(Ref x) { value.ref = x; }
void set_ref(Ref x) {
value.ref = x;
SetConcreteType();
}
void set_i32(uint32_t x) { value.i32 = x; }
void set_i64(uint64_t x) { value.i64 = x; }
void set_f32(float x) { memcpy(&value.f32_bits, &x, sizeof(x)); }
Expand Down Expand Up @@ -396,6 +421,7 @@ class Environment {
Index GetDataSegmentCount() const { return data_segments_.size(); }
Index GetElemSegmentCount() const { return elem_segments_.size(); }
Index GetModuleCount() const { return modules_.size(); }
Index GetHostCount() const { return host_objects_.size(); }

Index GetLastModuleIndex() const {
return modules_.empty() ? kInvalidIndex : modules_.size() - 1;
Expand Down Expand Up @@ -486,6 +512,12 @@ class Environment {
return modules_.back().get();
}

template <typename... Args>
HostObject* EmplaceBackHostObject(Args&&... args) {
host_objects_.emplace_back(std::forward<Args>(args)...);
return &host_objects_.back();
}

template <typename... Args>
void EmplaceModuleBinding(Args&&... args) {
module_bindings_.emplace(std::forward<Args>(args)...);
Expand Down Expand Up @@ -522,6 +554,7 @@ class Environment {
std::vector<Global> globals_;
std::vector<DataSegment> data_segments_;
std::vector<ElemSegment> elem_segments_;
std::vector<HostObject> host_objects_;
std::unique_ptr<OutputBuffer> istream_;
BindingHash module_bindings_;
BindingHash registered_module_bindings_;
Expand Down Expand Up @@ -691,6 +724,7 @@ bool IsCanonicalNan(uint64_t f64_bits);
bool IsArithmeticNan(uint32_t f32_bits);
bool IsArithmeticNan(uint64_t f64_bits);

std::string RefTypeToString(RefType t);
std::string TypedValueToString(const TypedValue&);
const char* ResultToString(Result);

Expand Down
13 changes: 11 additions & 2 deletions src/type-checker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,23 @@ Result TypeChecker::CheckTypeStackEnd(const char* desc) {
return result;
}

static bool IsRefType(Type t) {
return t == Type::Anyref || t == Type::Funcref || t == Type::Nullref ||
t == Type::Hostref;
}

static bool IsNullableRefType(Type t) {
return t == Type::Anyref || t == Type::Funcref || t == Type::Hostref;
}

Result TypeChecker::CheckType(Type actual, Type expected) {
if (expected == actual || expected == Type::Any || actual == Type::Any) {
return Result::Ok;
}
if (expected == Type::Anyref && (actual == Type::Funcref || actual == Type::Nullref)) {
if (expected == Type::Anyref && IsRefType(actual)) {
return Result::Ok;
}
if ((expected == Type::Funcref || expected == Type::Anyref) && actual == Type::Nullref) {
if (IsNullableRefType(expected) && actual == Type::Nullref) {
return Result::Ok;
}
return Result::Error;
Expand Down
4 changes: 2 additions & 2 deletions test/interp/reference-types.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@
)
)
(;; STDOUT ;;;
ref_null() => anyref:nullref
ref_null() => nullref
ref_is_null() => i32:1
ref_func() => funcref:1
table_set() =>
table_get() => i32:0
global_set() => anyref:0(1)
global_set() => funcref:1
;;; STDOUT ;;)