mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 05:35:31 +00:00
Bug 1288944 - Baldr: fuse the WasmTableObject InstanceVector into the Table's array (r=bbouvier)
MozReview-Commit-ID: BdP4hd2WX2S
This commit is contained in:
parent
6c3c3d31ea
commit
2d35f7cc8d
@ -350,7 +350,7 @@ Instance::Instance(JSContext* cx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < tables_.length(); i++)
|
for (size_t i = 0; i < tables_.length(); i++)
|
||||||
*addressOfTableBase(i) = tables_[i]->array();
|
*addressOfTableBase(i) = tables_[i]->base();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -407,6 +407,9 @@ Instance::tracePrivate(JSTracer* trc)
|
|||||||
for (const FuncImport& fi : metadata().funcImports)
|
for (const FuncImport& fi : metadata().funcImports)
|
||||||
TraceNullableEdge(trc, &funcImportTls(fi).obj, "wasm import");
|
TraceNullableEdge(trc, &funcImportTls(fi).obj, "wasm import");
|
||||||
|
|
||||||
|
for (const SharedTable& table : tables_)
|
||||||
|
table->trace(trc);
|
||||||
|
|
||||||
TraceNullableEdge(trc, &memory_, "wasm buffer");
|
TraceNullableEdge(trc, &memory_, "wasm buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,6 +504,12 @@ ReadCustomDoubleNaNObject(JSContext* cx, HandleValue v, double* ret)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WasmInstanceObject*
|
||||||
|
Instance::object() const
|
||||||
|
{
|
||||||
|
return object_;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args)
|
Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args)
|
||||||
{
|
{
|
||||||
@ -767,7 +776,7 @@ Instance::ensureProfilingState(JSContext* cx, bool newProfilingEnabled)
|
|||||||
// can contain elements from multiple instances.
|
// can contain elements from multiple instances.
|
||||||
MOZ_ASSERT(metadata().kind == ModuleKind::AsmJS);
|
MOZ_ASSERT(metadata().kind == ModuleKind::AsmJS);
|
||||||
|
|
||||||
void** array = table->array();
|
void** array = table->internalArray();
|
||||||
uint32_t length = table->length();
|
uint32_t length = table->length();
|
||||||
for (size_t i = 0; i < length; i++)
|
for (size_t i = 0; i < length; i++)
|
||||||
UpdateEntry(*code_, newProfilingEnabled, &array[i]);
|
UpdateEntry(*code_, newProfilingEnabled, &array[i]);
|
||||||
|
@ -43,10 +43,6 @@ class Instance
|
|||||||
const UniqueCode code_;
|
const UniqueCode code_;
|
||||||
GCPtrWasmMemoryObject memory_;
|
GCPtrWasmMemoryObject memory_;
|
||||||
SharedTableVector tables_;
|
SharedTableVector tables_;
|
||||||
|
|
||||||
// Thread-local data for code running in this instance.
|
|
||||||
// When threading is supported, we need a TlsData object per thread per
|
|
||||||
// instance.
|
|
||||||
TlsData tlsData_;
|
TlsData tlsData_;
|
||||||
|
|
||||||
// Internal helpers:
|
// Internal helpers:
|
||||||
@ -91,12 +87,13 @@ class Instance
|
|||||||
const SharedTableVector& tables() const { return tables_; }
|
const SharedTableVector& tables() const { return tables_; }
|
||||||
SharedMem<uint8_t*> memoryBase() const;
|
SharedMem<uint8_t*> memoryBase() const;
|
||||||
size_t memoryLength() const;
|
size_t memoryLength() const;
|
||||||
|
TlsData& tlsData() { return tlsData_; }
|
||||||
|
|
||||||
// This method returns a pointer to the GC object that owns this Instance.
|
// This method returns a pointer to the GC object that owns this Instance.
|
||||||
// Instances may be reached via weak edges (e.g., Compartment::instances_)
|
// Instances may be reached via weak edges (e.g., Compartment::instances_)
|
||||||
// so this perform a read-barrier on the returned object.
|
// so this perform a read-barrier on the returned object.
|
||||||
|
|
||||||
WasmInstanceObject* object() const { return object_; }
|
WasmInstanceObject* object() const;
|
||||||
|
|
||||||
// Execute the given export given the JS call arguments, storing the return
|
// Execute the given export given the JS call arguments, storing the return
|
||||||
// value in args.rval.
|
// value in args.rval.
|
||||||
|
@ -849,16 +849,14 @@ WasmTableObject::finalize(FreeOp* fop, JSObject* obj)
|
|||||||
WasmTableObject& tableObj = obj->as<WasmTableObject>();
|
WasmTableObject& tableObj = obj->as<WasmTableObject>();
|
||||||
if (!tableObj.isNewborn())
|
if (!tableObj.isNewborn())
|
||||||
tableObj.table().Release();
|
tableObj.table().Release();
|
||||||
if (tableObj.initialized())
|
|
||||||
fop->delete_(&tableObj.instanceVector());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ void
|
/* static */ void
|
||||||
WasmTableObject::trace(JSTracer* trc, JSObject* obj)
|
WasmTableObject::trace(JSTracer* trc, JSObject* obj)
|
||||||
{
|
{
|
||||||
WasmTableObject& tableObj = obj->as<WasmTableObject>();
|
WasmTableObject& tableObj = obj->as<WasmTableObject>();
|
||||||
if (tableObj.initialized())
|
if (!tableObj.isNewborn())
|
||||||
tableObj.instanceVector().trace(trc);
|
tableObj.table().trace(trc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ WasmTableObject*
|
/* static */ WasmTableObject*
|
||||||
@ -886,40 +884,9 @@ WasmTableObject::create(JSContext* cx, uint32_t length)
|
|||||||
obj->initReservedSlot(TABLE_SLOT, PrivateValue(table.forget().take()));
|
obj->initReservedSlot(TABLE_SLOT, PrivateValue(table.forget().take()));
|
||||||
|
|
||||||
MOZ_ASSERT(!obj->isNewborn());
|
MOZ_ASSERT(!obj->isNewborn());
|
||||||
MOZ_ASSERT(!obj->initialized());
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
WasmTableObject::initialized() const
|
|
||||||
{
|
|
||||||
return !getReservedSlot(INSTANCE_VECTOR_SLOT).isUndefined();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
WasmTableObject::init(JSContext* cx, HandleWasmInstanceObject instanceObj)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!initialized());
|
|
||||||
MOZ_ASSERT(!table().initialized());
|
|
||||||
|
|
||||||
// Ensure initialization is atomic so that the table is never left in an
|
|
||||||
// inconsistent state (where the Table is initialized but the
|
|
||||||
// WasmTableObject is not).
|
|
||||||
|
|
||||||
auto instanceVector = MakeUnique<InstanceVector>();
|
|
||||||
if (!instanceVector || !instanceVector->appendN(instanceObj.get(), table().length())) {
|
|
||||||
ReportOutOfMemory(cx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
initReservedSlot(INSTANCE_VECTOR_SLOT, PrivateValue(instanceVector.release()));
|
|
||||||
table().init(instanceObj->instance().codeSegment());
|
|
||||||
|
|
||||||
MOZ_ASSERT(initialized());
|
|
||||||
MOZ_ASSERT(table().initialized());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
@ -1037,16 +1004,14 @@ WasmTableObject::getImpl(JSContext* cx, const CallArgs& args)
|
|||||||
uint32_t index = uint32_t(indexDbl);
|
uint32_t index = uint32_t(indexDbl);
|
||||||
MOZ_ASSERT(double(index) == indexDbl);
|
MOZ_ASSERT(double(index) == indexDbl);
|
||||||
|
|
||||||
if (!tableObj->initialized()) {
|
if (!table.initialized()) {
|
||||||
args.rval().setNull();
|
args.rval().setNull();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const InstanceVector& instanceVector = tableObj->instanceVector();
|
ExternalTableElem& elem = table.externalArray()[index];
|
||||||
MOZ_ASSERT(instanceVector.length() == table.length());
|
Instance& instance = *elem.tls->instance;
|
||||||
|
const CodeRange* codeRange = instance.code().lookupRange(elem.code);
|
||||||
RootedWasmInstanceObject instanceObj(cx, instanceVector[index]);
|
|
||||||
const CodeRange* codeRange = instanceObj->instance().code().lookupRange(table.array()[index]);
|
|
||||||
|
|
||||||
// A non-function code range means the bad-indirect-call stub, so a null element.
|
// A non-function code range means the bad-indirect-call stub, so a null element.
|
||||||
if (!codeRange || !codeRange->isFunction()) {
|
if (!codeRange || !codeRange->isFunction()) {
|
||||||
@ -1054,6 +1019,7 @@ WasmTableObject::getImpl(JSContext* cx, const CallArgs& args)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RootedWasmInstanceObject instanceObj(cx, instance.object());
|
||||||
RootedFunction fun(cx);
|
RootedFunction fun(cx);
|
||||||
if (!instanceObj->getExportedFunction(cx, instanceObj, codeRange->funcIndex(), &fun))
|
if (!instanceObj->getExportedFunction(cx, instanceObj, codeRange->funcIndex(), &fun))
|
||||||
return false;
|
return false;
|
||||||
@ -1073,7 +1039,7 @@ WasmTableObject::get(JSContext* cx, unsigned argc, Value* vp)
|
|||||||
WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
|
WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
|
||||||
{
|
{
|
||||||
RootedWasmTableObject tableObj(cx, &args.thisv().toObject().as<WasmTableObject>());
|
RootedWasmTableObject tableObj(cx, &args.thisv().toObject().as<WasmTableObject>());
|
||||||
const Table& table = tableObj->table();
|
Table& table = tableObj->table();
|
||||||
|
|
||||||
if (!args.requireAtLeast(cx, "set", 2))
|
if (!args.requireAtLeast(cx, "set", 2))
|
||||||
return false;
|
return false;
|
||||||
@ -1096,20 +1062,15 @@ WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tableObj->initialized()) {
|
if (!table.initialized()) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
args.rval().setUndefined();
|
args.rval().setUndefined();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
RootedWasmInstanceObject instanceObj(cx, ExportedFunctionToInstanceObject(value));
|
table.init(ExportedFunctionToInstance(value));
|
||||||
if (!tableObj->init(cx, instanceObj))
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const InstanceVector& instanceVector = tableObj->instanceVector();
|
|
||||||
MOZ_ASSERT(instanceVector.length() == table.length());
|
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
RootedWasmInstanceObject instanceObj(cx, ExportedFunctionToInstanceObject(value));
|
RootedWasmInstanceObject instanceObj(cx, ExportedFunctionToInstanceObject(value));
|
||||||
uint32_t funcIndex = ExportedFunctionToIndex(value);
|
uint32_t funcIndex = ExportedFunctionToIndex(value);
|
||||||
@ -1120,15 +1081,15 @@ WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
|
|||||||
MOZ_ASSERT(value == f);
|
MOZ_ASSERT(value == f);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!tableObj->setInstance(cx, index, instanceObj))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Instance& instance = instanceObj->instance();
|
Instance& instance = instanceObj->instance();
|
||||||
const FuncExport& funcExport = instance.metadata().lookupFuncExport(funcIndex);
|
const FuncExport& funcExport = instance.metadata().lookupFuncExport(funcIndex);
|
||||||
const CodeRange& codeRange = instance.metadata().codeRanges[funcExport.codeRangeIndex()];
|
const CodeRange& codeRange = instance.metadata().codeRanges[funcExport.codeRangeIndex()];
|
||||||
table.array()[index] = instance.codeSegment().base() + codeRange.funcTableEntry();
|
void* code = instance.codeSegment().base() + codeRange.funcTableEntry();
|
||||||
|
|
||||||
|
if (!table.set(cx, index, code, instance))
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
table.array()[index] = instanceVector[index]->instance().codeSegment().badIndirectCallCode();
|
table.setNull(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
args.rval().setUndefined();
|
args.rval().setUndefined();
|
||||||
@ -1155,28 +1116,6 @@ WasmTableObject::table() const
|
|||||||
return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
|
return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
WasmTableObject::InstanceVector&
|
|
||||||
WasmTableObject::instanceVector() const
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(initialized());
|
|
||||||
return *(InstanceVector*)getReservedSlot(INSTANCE_VECTOR_SLOT).toPrivate();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
WasmTableObject::setInstance(JSContext* cx, uint32_t index, HandleWasmInstanceObject instanceObj)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(initialized());
|
|
||||||
MOZ_ASSERT(instanceObj->instance().codeSegment().containsCodePC(table().array()[index]));
|
|
||||||
|
|
||||||
if (instanceVector()[index] != instanceObj) {
|
|
||||||
JS_ReportError(cx, "cross-module Table import NYI");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
instanceVector()[index] = instanceObj;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// WebAssembly class and static methods
|
// WebAssembly class and static methods
|
||||||
|
|
||||||
|
@ -180,7 +180,6 @@ class WasmMemoryObject : public NativeObject
|
|||||||
class WasmTableObject : public NativeObject
|
class WasmTableObject : public NativeObject
|
||||||
{
|
{
|
||||||
static const unsigned TABLE_SLOT = 0;
|
static const unsigned TABLE_SLOT = 0;
|
||||||
static const unsigned INSTANCE_VECTOR_SLOT = 1;
|
|
||||||
static const ClassOps classOps_;
|
static const ClassOps classOps_;
|
||||||
bool isNewborn() const;
|
bool isNewborn() const;
|
||||||
static void finalize(FreeOp* fop, JSObject* obj);
|
static void finalize(FreeOp* fop, JSObject* obj);
|
||||||
@ -192,29 +191,18 @@ class WasmTableObject : public NativeObject
|
|||||||
static bool setImpl(JSContext* cx, const CallArgs& args);
|
static bool setImpl(JSContext* cx, const CallArgs& args);
|
||||||
static bool set(JSContext* cx, unsigned argc, Value* vp);
|
static bool set(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
// InstanceVector has the same length as the Table and assigns, to each
|
|
||||||
// element, the instance of the exported function stored in that element.
|
|
||||||
using InstanceVector = GCVector<HeapPtr<WasmInstanceObject*>, 0, SystemAllocPolicy>;
|
|
||||||
InstanceVector& instanceVector() const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const unsigned RESERVED_SLOTS = 2;
|
static const unsigned RESERVED_SLOTS = 1;
|
||||||
static const Class class_;
|
static const Class class_;
|
||||||
static const JSPropertySpec properties[];
|
static const JSPropertySpec properties[];
|
||||||
static const JSFunctionSpec methods[];
|
static const JSFunctionSpec methods[];
|
||||||
static bool construct(JSContext*, unsigned, Value*);
|
static bool construct(JSContext*, unsigned, Value*);
|
||||||
|
|
||||||
|
// Note that, after creation, a WasmTableObject's table() is not initialized
|
||||||
|
// and must be initialized before use.
|
||||||
|
|
||||||
static WasmTableObject* create(JSContext* cx, uint32_t length);
|
static WasmTableObject* create(JSContext* cx, uint32_t length);
|
||||||
bool initialized() const;
|
|
||||||
bool init(JSContext* cx, HandleWasmInstanceObject instanceObj);
|
|
||||||
|
|
||||||
// As a global invariant, any time an element of tableObj->table() is
|
|
||||||
// updated to a new exported function, table->setInstance() must be called
|
|
||||||
// to update the instance of that new exported function in the instance
|
|
||||||
// vector.
|
|
||||||
|
|
||||||
wasm::Table& table() const;
|
wasm::Table& table() const;
|
||||||
bool setInstance(JSContext* cx, uint32_t index, HandleWasmInstanceObject instanceObj);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
@ -415,15 +415,10 @@ Module::initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
|
|||||||
Instance& instance = instanceObj->instance();
|
Instance& instance = instanceObj->instance();
|
||||||
const SharedTableVector& tables = instance.tables();
|
const SharedTableVector& tables = instance.tables();
|
||||||
|
|
||||||
// Initialize tables that have a WasmTableObject first, so that this
|
// Ensure all tables are initialized before storing into them.
|
||||||
// initialization can be done atomically.
|
|
||||||
if (tableObj && !tableObj->initialized() && !tableObj->init(cx, instanceObj))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Initialize all remaining Tables that do not have objects.
|
|
||||||
for (const SharedTable& table : tables) {
|
for (const SharedTable& table : tables) {
|
||||||
if (!table->initialized())
|
if (!table->initialized())
|
||||||
table->init(instance.code().segment());
|
table->init(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that all tables have been initialized, write elements.
|
// Now that all tables have been initialized, write elements.
|
||||||
@ -462,14 +457,6 @@ Module::initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tableObj) {
|
|
||||||
MOZ_ASSERT(seg.tableIndex == 0);
|
|
||||||
for (uint32_t i = 0; i < seg.elems.length(); i++) {
|
|
||||||
if (!tableObj->setInstance(cx, offset + i, instanceObj))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If profiling is already enabled in the wasm::Compartment, the new
|
// If profiling is already enabled in the wasm::Compartment, the new
|
||||||
// instance must use the profiling entry for typed functions instead of
|
// instance must use the profiling entry for typed functions instead of
|
||||||
// the default nonProfilingEntry.
|
// the default nonProfilingEntry.
|
||||||
@ -477,10 +464,12 @@ Module::initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
|
|||||||
|
|
||||||
uint8_t* codeBase = instance.codeBase();
|
uint8_t* codeBase = instance.codeBase();
|
||||||
for (uint32_t i = 0; i < seg.elems.length(); i++) {
|
for (uint32_t i = 0; i < seg.elems.length(); i++) {
|
||||||
void* callee = codeBase + seg.elems[i];
|
void* code = codeBase + seg.elems[i];
|
||||||
if (useProfilingEntry)
|
if (useProfilingEntry)
|
||||||
callee = codeBase + instance.code().lookupRange(callee)->funcProfilingEntry();
|
code = codeBase + instance.code().lookupRange(code)->funcProfilingEntry();
|
||||||
table.array()[offset + i] = callee;
|
|
||||||
|
if (!table.set(cx, offset + i, code, instance))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
prevEnd = offset + seg.elems.length();
|
prevEnd = offset + seg.elems.length();
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "jscntxt.h"
|
#include "jscntxt.h"
|
||||||
|
|
||||||
|
#include "asmjs/WasmInstance.h"
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::wasm;
|
using namespace js::wasm;
|
||||||
|
|
||||||
@ -30,27 +32,97 @@ Table::create(JSContext* cx, const TableDesc& desc)
|
|||||||
if (!table)
|
if (!table)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
table->array_.reset(cx->pod_calloc<void*>(desc.initial));
|
// The raw element type of a Table depends on whether it is external: an
|
||||||
if (!table->array_)
|
// external table can contain functions from multiple instances and thus
|
||||||
|
// must store an additional instance pointer in each element.
|
||||||
|
void* array;
|
||||||
|
if (desc.external)
|
||||||
|
array = cx->pod_calloc<ExternalTableElem>(desc.initial);
|
||||||
|
else
|
||||||
|
array = cx->pod_calloc<void*>(desc.initial);
|
||||||
|
if (!array)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
table->array_.reset((uint8_t*)array);
|
||||||
table->kind_ = desc.kind;
|
table->kind_ = desc.kind;
|
||||||
table->length_ = desc.initial;
|
table->length_ = desc.initial;
|
||||||
table->initialized_ = false;
|
table->initialized_ = false;
|
||||||
|
table->external_ = desc.external;
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Table::init(const CodeSegment& codeSegment)
|
Table::init(Instance& instance)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!initialized());
|
MOZ_ASSERT(!initialized());
|
||||||
|
initialized_ = true;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < length_; i++) {
|
void* code = instance.codeSegment().badIndirectCallCode();
|
||||||
MOZ_ASSERT(!array_.get()[i]);
|
if (external_) {
|
||||||
array_.get()[i] = codeSegment.badIndirectCallCode();
|
ExternalTableElem* array = externalArray();
|
||||||
|
TlsData* tls = &instance.tlsData();
|
||||||
|
for (uint32_t i = 0; i < length_; i++) {
|
||||||
|
array[i].code = code;
|
||||||
|
array[i].tls = tls;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
void** array = internalArray();
|
||||||
|
for (uint32_t i = 0; i < length_; i++)
|
||||||
|
array[i] = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Table::trace(JSTracer* trc)
|
||||||
|
{
|
||||||
|
if (!initialized_ || !external_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ExternalTableElem* array = externalArray();
|
||||||
|
for (uint32_t i = 0; i < length_; i++)
|
||||||
|
array[i].tls->instance->trace(trc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void**
|
||||||
|
Table::internalArray() const
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(initialized_);
|
||||||
|
MOZ_ASSERT(!external_);
|
||||||
|
return (void**)array_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalTableElem*
|
||||||
|
Table::externalArray() const
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(initialized_);
|
||||||
|
MOZ_ASSERT(external_);
|
||||||
|
return (ExternalTableElem*)array_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Table::set(JSContext* cx, uint32_t index, void* code, Instance& instance)
|
||||||
|
{
|
||||||
|
if (external_) {
|
||||||
|
ExternalTableElem& elem = externalArray()[index];
|
||||||
|
if (elem.tls->instance != &instance) {
|
||||||
|
JS_ReportError(cx, "cross-module Table import NYI");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem.code = code;
|
||||||
|
} else {
|
||||||
|
internalArray()[index] = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
initialized_ = true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Table::setNull(uint32_t index)
|
||||||
|
{
|
||||||
|
// Only external tables can set elements to null after initialization.
|
||||||
|
ExternalTableElem& elem = externalArray()[index];
|
||||||
|
elem.code = elem.tls->instance->codeSegment().badIndirectCallCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
|
@ -30,24 +30,36 @@ namespace wasm {
|
|||||||
|
|
||||||
class Table : public ShareableBase<Table>
|
class Table : public ShareableBase<Table>
|
||||||
{
|
{
|
||||||
|
UniquePtr<uint8_t[], JS::FreePolicy> array_;
|
||||||
TableKind kind_;
|
TableKind kind_;
|
||||||
UniquePtr<void*> array_;
|
|
||||||
uint32_t length_;
|
uint32_t length_;
|
||||||
bool initialized_;
|
bool initialized_;
|
||||||
|
bool external_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static RefPtr<Table> create(JSContext* cx, const TableDesc& desc);
|
static RefPtr<Table> create(JSContext* cx, const TableDesc& desc);
|
||||||
|
void trace(JSTracer* trc);
|
||||||
|
|
||||||
// These accessors may be used before initialization.
|
// These accessors may be used before initialization.
|
||||||
|
|
||||||
|
bool external() const { return external_; }
|
||||||
bool isTypedFunction() const { return kind_ == TableKind::TypedFunction; }
|
bool isTypedFunction() const { return kind_ == TableKind::TypedFunction; }
|
||||||
void** array() const { return array_.get(); }
|
|
||||||
uint32_t length() const { return length_; }
|
uint32_t length() const { return length_; }
|
||||||
|
uint8_t* base() const { return array_.get(); }
|
||||||
|
|
||||||
// A Table must be initialized before any dependent instance can execute.
|
// A Table must be initialized before any dependent instance can execute.
|
||||||
|
|
||||||
bool initialized() const { return initialized_; }
|
bool initialized() const { return initialized_; }
|
||||||
void init(const CodeSegment& codeSegment);
|
void init(Instance& instance);
|
||||||
|
|
||||||
|
// After initialization, elements must be accessed. All updates must go
|
||||||
|
// through a set() function with the exception of (profiling) updates to the
|
||||||
|
// callee pointer that do not change which logical function is being called.
|
||||||
|
|
||||||
|
void** internalArray() const;
|
||||||
|
ExternalTableElem* externalArray() const;
|
||||||
|
bool set(JSContext* cx, uint32_t index, void* code, Instance& instance);
|
||||||
|
void setNull(uint32_t index);
|
||||||
|
|
||||||
// about:memory reporting:
|
// about:memory reporting:
|
||||||
|
|
||||||
|
@ -1141,6 +1141,10 @@ class CalleeDesc
|
|||||||
MOZ_ASSERT(which_ == WasmTable);
|
MOZ_ASSERT(which_ == WasmTable);
|
||||||
return u.table.desc_.initial;
|
return u.table.desc_.initial;
|
||||||
}
|
}
|
||||||
|
bool wasmTableIsExternal() const {
|
||||||
|
MOZ_ASSERT(which_ == WasmTable);
|
||||||
|
return u.table.desc_.external;
|
||||||
|
}
|
||||||
SigIdDesc wasmTableSigId() const {
|
SigIdDesc wasmTableSigId() const {
|
||||||
MOZ_ASSERT(which_ == WasmTable);
|
MOZ_ASSERT(which_ == WasmTable);
|
||||||
return u.table.sigId_;
|
return u.table.sigId_;
|
||||||
@ -1220,6 +1224,24 @@ struct FuncImportTls
|
|||||||
static_assert(sizeof(GCPtrObject) == sizeof(void*), "for JIT access");
|
static_assert(sizeof(GCPtrObject) == sizeof(void*), "for JIT access");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// When a table can be shared between instances (it is "external"), the internal
|
||||||
|
// representation is an array of ExternalTableElem instead of just an array of
|
||||||
|
// code pointers.
|
||||||
|
|
||||||
|
struct ExternalTableElem
|
||||||
|
{
|
||||||
|
// The code to call when calling this element. The table ABI is the system
|
||||||
|
// ABI with the additional ABI requirements that:
|
||||||
|
// - WasmTlsReg and any pinned registers have been loaded appropriately
|
||||||
|
// - if this is a heterogeneous table that requires a signature check,
|
||||||
|
// WasmTableCallSigReg holds the signature id.
|
||||||
|
void* code;
|
||||||
|
|
||||||
|
// The pointer to the callee's instance's TlsData. This must be loaded into
|
||||||
|
// WasmTlsReg before calling 'code'.
|
||||||
|
TlsData* tls;
|
||||||
|
};
|
||||||
|
|
||||||
// Constants:
|
// Constants:
|
||||||
|
|
||||||
// The WebAssembly spec hard-codes the virtual page size to be 64KiB and
|
// The WebAssembly spec hard-codes the virtual page size to be 64KiB and
|
||||||
|
@ -2756,6 +2756,8 @@ MacroAssembler::wasmCallIndirect(const wasm::CallSiteDesc& desc, const wasm::Cal
|
|||||||
Register index = WasmTableCallIndexReg;
|
Register index = WasmTableCallIndexReg;
|
||||||
|
|
||||||
if (callee.which() == wasm::CalleeDesc::AsmJSTable) {
|
if (callee.which() == wasm::CalleeDesc::AsmJSTable) {
|
||||||
|
// asm.js tables require no signature check, have had their index masked
|
||||||
|
// into range and thus need no bounds check and cannot be external.
|
||||||
loadWasmGlobalPtr(callee.tableGlobalDataOffset(), scratch);
|
loadWasmGlobalPtr(callee.tableGlobalDataOffset(), scratch);
|
||||||
loadPtr(BaseIndex(scratch, index, ScalePointer), scratch);
|
loadPtr(BaseIndex(scratch, index, ScalePointer), scratch);
|
||||||
call(desc, scratch);
|
call(desc, scratch);
|
||||||
@ -2782,8 +2784,23 @@ MacroAssembler::wasmCallIndirect(const wasm::CallSiteDesc& desc, const wasm::Cal
|
|||||||
index, Imm32(callee.wasmTableLength()),
|
index, Imm32(callee.wasmTableLength()),
|
||||||
wasm::JumpTarget::OutOfBounds);
|
wasm::JumpTarget::OutOfBounds);
|
||||||
|
|
||||||
|
// Load the base pointer of the table.
|
||||||
loadWasmGlobalPtr(callee.tableGlobalDataOffset(), scratch);
|
loadWasmGlobalPtr(callee.tableGlobalDataOffset(), scratch);
|
||||||
loadPtr(BaseIndex(scratch, index, ScalePointer), scratch);
|
|
||||||
|
// Load the callee from the table.
|
||||||
|
if (callee.wasmTableIsExternal()) {
|
||||||
|
static_assert(sizeof(wasm::ExternalTableElem) == 8 || sizeof(wasm::ExternalTableElem) == 16,
|
||||||
|
"elements of external tables are two words");
|
||||||
|
if (sizeof(wasm::ExternalTableElem) == 8) {
|
||||||
|
loadPtr(BaseIndex(scratch, index, TimesEight), scratch);
|
||||||
|
} else {
|
||||||
|
lshift32(Imm32(4), index);
|
||||||
|
loadPtr(BaseIndex(scratch, index, TimesOne), scratch);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadPtr(BaseIndex(scratch, index, ScalePointer), scratch);
|
||||||
|
}
|
||||||
|
|
||||||
call(desc, scratch);
|
call(desc, scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user