Bug 1533932 - Handle prebarriers properly for wasm tables. r=bbouvier

Two problems needed to be fixed:

- JS::Heap<> only has a postbarrier, not a prebarrier also; this is
  what triggered the original error report.  The type in
  table-of-anyref therefore needs to be js::HeapPtr<>, so that we get
  both barriers.  (It can't be GCPtr because the vector storage may be
  moved as the table grows.)

- The API for table.get returns a pointer into underlying vector
  storage or null; if not null, the pointer must be dereferenced to
  obtain the actual value.  Since the vector storage may move during
  GC and grow operations the pointer is really only valid until the
  next operation that might move the vector.  That lifetime
  restriction was not enforced in Ion-generated code and the
  dereference could in principle move far enough during optimization
  for it to become invalid, though in practice it is extremely
  unlikely that this was actually a problem.

  We fix this by flagging the dereference as non-movable, and by
  changing some names and comments to highlight the issue.

Differential Revision: https://phabricator.services.mozilla.com/D23665

--HG--
extra : rebase_source : 67c9d5f4e11b9b2886ce7cc16206a391d97d826e
extra : histedit_source : c9ecc54aab5e3b4c2ab5723b7e0bc1467b6907fe
This commit is contained in:
Lars T Hansen 2019-03-15 13:17:18 +01:00
Родитель 12c0629a98
Коммит f5a09d85ce
6 изменённых файлов: 31 добавлений и 10 удалений

Просмотреть файл

@ -0,0 +1,11 @@
// |jit-test| skip-if: !wasmReftypesEnabled()
// Faulty prebarrier for tables.
gczeal(4, 8);
let ins = wasmEvalText(
`(module
(table (export "t") 10 anyref)
(func (export "set_anyref") (param i32) (param anyref)
(table.set (get_local 0) (get_local 1))))`);
ins.exports.set_anyref(3, {});
ins.exports.set_anyref(3, null);

Просмотреть файл

@ -11932,11 +11932,14 @@ class MWasmDerivedPointer : public MUnaryInstruction,
class MWasmLoadRef : public MUnaryInstruction, public NoTypePolicy::Data {
AliasSet::Flag aliasSet_;
explicit MWasmLoadRef(MDefinition* valueAddr, AliasSet::Flag aliasSet)
explicit MWasmLoadRef(MDefinition* valueAddr, AliasSet::Flag aliasSet,
bool isMovable = true)
: MUnaryInstruction(classOpcode, valueAddr), aliasSet_(aliasSet) {
MOZ_ASSERT(valueAddr->type() == MIRType::Pointer);
setResultType(MIRType::RefOrNull);
setMovable();
if (isMovable) {
setMovable();
}
}
public:

Просмотреть файл

@ -857,6 +857,10 @@ Instance::tableInit(Instance* instance, uint32_t dstOffset, uint32_t srcOffset,
// Ion code has to hold a value that may or may not be a pointer to GC'd
// storage, or where Ion has to pass in a pointer to storage where a return
// value can be written.
//
// Note carefully that the pointer that is returned may not be valid past
// operations that change the size of the table or cause GC work; it is strictly
// to be used to retrieve the return value.
/* static */ void* /* nullptr to signal trap; pointer to table location
otherwise */
@ -868,7 +872,7 @@ Instance::tableGet(Instance* instance, uint32_t index, uint32_t tableIndex) {
JSMSG_WASM_TABLE_OUT_OF_BOUNDS);
return nullptr;
}
return const_cast<void*>(table.getAnyRefLocForCompiledCode(index));
return const_cast<void*>(table.getShortlivedAnyRefLocForCompiledCode(index));
}
/* static */ uint32_t /* infallible */

Просмотреть файл

@ -729,8 +729,11 @@ class FunctionCompiler {
}
MDefinition* derefTableElementPointer(MDefinition* base) {
// Table element storage may be moved by GC operations, so reads from that
// storage are not movable.
MWasmLoadRef* load =
MWasmLoadRef::New(alloc(), base, AliasSet::WasmTableElement);
MWasmLoadRef::New(alloc(), base, AliasSet::WasmTableElement,
/*isMovable=*/ false);
curBlock_->add(load);
return load;
}
@ -3160,8 +3163,8 @@ static bool EmitTableGet(FunctionCompiler& f) {
return false;
}
// The return value here is either null, denoting an error, or a pointer to an
// unmovable location containing a possibly-null ref.
// The return value here is either null, denoting an error, or a short-lived
// pointer to a location containing a possibly-null ref.
MDefinition* result;
if (!f.builtinInstanceMethodCall(SASigTableGet, lineOrBytecode, args,
&result)) {

Просмотреть файл

@ -148,9 +148,9 @@ AnyRef Table::getAnyRef(uint32_t index) const {
return AnyRef::fromJSObject(objects_[index]);
}
const void* Table::getAnyRefLocForCompiledCode(uint32_t index) const {
const void* Table::getShortlivedAnyRefLocForCompiledCode(uint32_t index) const {
MOZ_ASSERT(!isFunction());
return objects_[index].address();
return const_cast<HeapPtr<JSObject*>&>(objects_[index]).unsafeUnbarrieredForTracing();
}
void Table::setAnyFunc(uint32_t index, void* code, const Instance* instance) {

Просмотреть файл

@ -38,7 +38,7 @@ namespace wasm {
// the most appropriate representation for Cell::anyref.
STATIC_ASSERT_ANYREF_IS_JSOBJECT;
typedef GCVector<JS::Heap<JSObject*>, 0, SystemAllocPolicy> TableAnyRefVector;
typedef GCVector<HeapPtr<JSObject*>, 0, SystemAllocPolicy> TableAnyRefVector;
class Table : public ShareableBase<Table> {
using InstanceSet = JS::WeakCache<GCHashSet<
@ -87,7 +87,7 @@ class Table : public ShareableBase<Table> {
void setAnyFunc(uint32_t index, void* code, const Instance* instance);
AnyRef getAnyRef(uint32_t index) const;
const void* getAnyRefLocForCompiledCode(uint32_t index) const;
const void* getShortlivedAnyRefLocForCompiledCode(uint32_t index) const;
void setAnyRef(uint32_t index, AnyRef);
void setNull(uint32_t index);