import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1236321 - Annotate intentional switch fallthroughs to suppress -Wimplicit-fallthrough warnings in js/. r=luke (32d46328ef)
- Bug 1236552 - Odin: handle unfinished AsmJSModuleObject in addSizeOfMisc (r=bbouvier) (dafbd77b10)
- Bug 1229399: Make writing the IR fallible, provide a fallible readingAPI; r=luke (cbc536c3fa)
- Bug 1237272 - Only for Coverity - check arg1, arg2 and arg3 for validity. r=luke (1456e58951)
- Bug 1229399: Split FuncIR into Bytecode/Encoder/Decoder/FuncBytecode; r=luke (9f438b4d5f)
- Bug 1237508 - Odin: make AsmJSModule derive wasm::Module (r=bbouvier) (0186bf908b)
- Bug 1238195 - Switch over some AutoVectorRooters to Rooted<TraceableVector>s and fill in some missing support (r=terrence) (b556fdc27e)
- Bug 1234193 - IsRelazifiableFunction: Return false when we report an error. r=jandem (bd3c33e1e6)
- Bug 1221361: Mark SetARMHwCapFlags as unsafe for fuzzing; r=jolesen (3134febc32)
- Bug 1236564 - Fix various minor issues with getting/setting GC parameters r=terrence (45e251eba7)
- Bug 1235237 - Annotate intentional switch fallthrough to suppress -Wimplicit-fallthrough warning in storage/. r=mak (f81714fdab)
- Bug 1235236 - Annotate intentional switch fallthrough to suppress -Wimplicit-fallthrough warning in modules/libjar/. r=aklotz (f3a210802b)
- Bug 1236324 - Annotate intentional switch fallthroughs to suppress -Wimplicit-fallthrough warnings in toolkit/components/places/. r=mak (f2d09b5041)
- Bug 1238711 - Rename TraceableVector to GCVector; r=sfink Bug 1237153 - Fix gcparam() parameter verification to not allow negative numbers r=terrence (deccfd7f01)
- Bug 1235092 - Part 1: Optimize spread call with rest parameter. r=efaust (e6cc1294d1)
- Bug 1235092 - Part 2: Support allowContentSpread in the optimization for spread call with rest parameter. r=efaust (31c881893d)
- Bug 1235092 - Part 3: Root function in BytecodeEmitter::isRestParameter. r=bustage (ede37f48b6)
- Bug 1233152 - Use PersistentRooted for ParseTask script and sourceObject. r=terrence (d99d9b81fb)
- Bug 1236476: Report out of memory in ExpandErrorArgumentsVA; r=jandem (6a2327222c)
- Bug 1239601 - improve the UniquePtr situation (r=jorendorff) (640322c8c1)
- Bug 1239724: Introduce RegExp registers to non-ion builds; r=arai (f2d837e65b)
- Bug 1137624 - Remove ArrayJoin code duplication, and use a correct alias set. r=jandem (ab8a98a5e3)
- Bug 1237284: Make SIMD names more consistent in MCallOptimize; r=jolesen (d50f74a31e)
- Bug 1238582 - Fix spurious assertion failure in array sort due to over-eager OOM simulation r=jandem (587f4976e5)
- Bug 1235874 - handle null filename in DescribeScriptedCaller (r=sunfish) (b347469108)
- Bug 1239601 - improve the UniquePtr situation (r=jandem) (a8b9f15dcb)
This commit is contained in:
2023-08-04 10:46:07 +08:00
parent c975928fff
commit cc81aca6b2
152 changed files with 3787 additions and 3250 deletions
+399 -345
View File
@@ -76,6 +76,7 @@ wasm::AllocateCode(ExclusiveContext* cx, size_t bytes)
void
CodeDeleter::operator()(uint8_t* p)
{
MOZ_ASSERT(bytes_ != 0);
DeallocateExecutableMemory(p, bytes_, AsmJSPageSize);
}
@@ -462,6 +463,13 @@ CacheableUniquePtr<CharT>::clone(JSContext* cx, CacheableUniquePtr* out) const
return true;
}
template <class CharT>
size_t
CacheableUniquePtr<CharT>::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
{
return mallocSizeOf(this->get());
}
namespace js {
namespace wasm {
template struct CacheableUniquePtr<char>;
@@ -469,24 +477,132 @@ template struct CacheableUniquePtr<char16_t>;
}
}
class Module::AutoMutateCode
size_t
ExportMap::serializedSize() const
{
AutoWritableJitCode awjc_;
AutoFlushICache afc_;
return SerializedVectorSize(exportNames) +
SerializedVectorSize(fieldNames) +
SerializedPodVectorSize(fieldsToExports);
}
public:
AutoMutateCode(JSContext* cx, Module& module, const char* name)
: awjc_(cx->runtime(), module.code(), module.pod.codeBytes_),
afc_(name)
{
AutoFlushICache::setRange(uintptr_t(module.code()), module.pod.codeBytes_);
}
};
uint32_t
Module::totalBytes() const
uint8_t*
ExportMap::serialize(uint8_t* cursor) const
{
return pod.codeBytes_ + pod.globalBytes_;
cursor = SerializeVector(cursor, exportNames);
cursor = SerializeVector(cursor, fieldNames);
cursor = SerializePodVector(cursor, fieldsToExports);
return cursor;
}
const uint8_t*
ExportMap::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
{
(cursor = DeserializeVector(cx, cursor, &exportNames)) &&
(cursor = DeserializeVector(cx, cursor, &fieldNames)) &&
(cursor = DeserializePodVector(cx, cursor, &fieldsToExports));
return cursor;
}
bool
ExportMap::clone(JSContext* cx, ExportMap* map) const
{
return CloneVector(cx, exportNames, &map->exportNames) &&
CloneVector(cx, fieldNames, &map->fieldNames) &&
ClonePodVector(cx, fieldsToExports, &map->fieldsToExports);
}
size_t
ExportMap::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
{
return SizeOfVectorExcludingThis(exportNames, mallocSizeOf) &&
SizeOfVectorExcludingThis(fieldNames, mallocSizeOf) &&
fieldsToExports.sizeOfExcludingThis(mallocSizeOf);
}
size_t
ModuleData::serializedSize() const
{
return sizeof(pod()) +
codeBytes +
SerializedVectorSize(imports) +
SerializedVectorSize(exports) +
SerializedPodVectorSize(heapAccesses) +
SerializedPodVectorSize(codeRanges) +
SerializedPodVectorSize(callSites) +
SerializedVectorSize(funcNames) +
filename.serializedSize() +
displayURL.serializedSize();
}
uint8_t*
ModuleData::serialize(uint8_t* cursor) const
{
cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
cursor = WriteBytes(cursor, code.get(), codeBytes);
cursor = SerializeVector(cursor, imports);
cursor = SerializeVector(cursor, exports);
cursor = SerializePodVector(cursor, heapAccesses);
cursor = SerializePodVector(cursor, codeRanges);
cursor = SerializePodVector(cursor, callSites);
cursor = SerializeVector(cursor, funcNames);
cursor = filename.serialize(cursor);
cursor = displayURL.serialize(cursor);
return cursor;
}
/* static */ const uint8_t*
ModuleData::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
{
cursor = ReadBytes(cursor, &pod(), sizeof(pod()));
code = AllocateCode(cx, totalBytes());
if (!code)
return nullptr;
cursor = ReadBytes(cursor, code.get(), codeBytes);
(cursor = DeserializeVector(cx, cursor, &imports)) &&
(cursor = DeserializeVector(cx, cursor, &exports)) &&
(cursor = DeserializePodVector(cx, cursor, &heapAccesses)) &&
(cursor = DeserializePodVector(cx, cursor, &codeRanges)) &&
(cursor = DeserializePodVector(cx, cursor, &callSites)) &&
(cursor = DeserializeVector(cx, cursor, &funcNames)) &&
(cursor = filename.deserialize(cx, cursor)) &&
(cursor = displayURL.deserialize(cx, cursor));
return cursor;
}
bool
ModuleData::clone(JSContext* cx, ModuleData* out) const
{
out->pod() = pod();
out->code = AllocateCode(cx, totalBytes());
if (!out->code)
return false;
memcpy(out->code.get(), code.get(), codeBytes);
return CloneVector(cx, imports, &out->imports) &&
CloneVector(cx, exports, &out->exports) &&
ClonePodVector(cx, heapAccesses, &out->heapAccesses) &&
ClonePodVector(cx, codeRanges, &out->codeRanges) &&
ClonePodVector(cx, callSites, &out->callSites) &&
CloneVector(cx, funcNames, &out->funcNames) &&
filename.clone(cx, &out->filename) &&
displayURL.clone(cx, &out->displayURL);
}
size_t
ModuleData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
{
// Module::addSizeOfMisc takes care of code and global memory.
return SizeOfVectorExcludingThis(imports, mallocSizeOf) +
SizeOfVectorExcludingThis(exports, mallocSizeOf) +
heapAccesses.sizeOfExcludingThis(mallocSizeOf) +
codeRanges.sizeOfExcludingThis(mallocSizeOf) +
callSites.sizeOfExcludingThis(mallocSizeOf) +
funcNames.sizeOfExcludingThis(mallocSizeOf) +
filename.sizeOfExcludingThis(mallocSizeOf) +
displayURL.sizeOfExcludingThis(mallocSizeOf);
}
uint8_t*
@@ -524,7 +640,7 @@ Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap)
// i.e. ptr > heapLength - data-type-byte-size - offset. data-type-byte-size
// and offset are already included in the addend so we
// just have to add the heap length here.
for (const HeapAccess& access : heapAccesses_) {
for (const HeapAccess& access : module_->heapAccesses) {
if (access.hasLengthCheck())
X86Encoding::AddInt32(access.patchLengthAt(code()), heapLength);
void* addr = access.patchHeapPtrImmAt(code());
@@ -540,14 +656,14 @@ Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap)
// checks at the right places. All accesses that have been recorded are the
// only ones that need bound checks (see also
// CodeGeneratorX64::visitAsmJS{Load,Store,CompareExchange,Exchange,AtomicBinop}Heap)
for (const HeapAccess& access : heapAccesses_) {
for (const HeapAccess& access : module_->heapAccesses) {
// See comment above for x86 codegen.
if (access.hasLengthCheck())
X86Encoding::AddInt32(access.patchLengthAt(code()), heapLength);
}
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
for (const HeapAccess& access : heapAccesses_)
for (const HeapAccess& access : module_->heapAccesses)
Assembler::UpdateBoundsCheck(heapLength, (Instruction*)(access.insnOffset() + code()));
#endif
@@ -567,8 +683,8 @@ Module::despecializeFromHeap(ArrayBufferObjectMaybeShared* heap)
#if defined(JS_CODEGEN_X86)
uint32_t heapLength = heap->byteLength();
uint8_t* ptrBase = heap->dataPointerEither().unwrap(/*safe - used for value*/);
for (unsigned i = 0; i < heapAccesses_.length(); i++) {
const HeapAccess& access = heapAccesses_[i];
for (unsigned i = 0; i < module_->heapAccesses.length(); i++) {
const HeapAccess& access = module_->heapAccesses[i];
if (access.hasLengthCheck())
X86Encoding::AddInt32(access.patchLengthAt(code()), -heapLength);
void* addr = access.patchHeapPtrImmAt(code());
@@ -578,8 +694,8 @@ Module::despecializeFromHeap(ArrayBufferObjectMaybeShared* heap)
}
#elif defined(JS_CODEGEN_X64)
uint32_t heapLength = heap->byteLength();
for (unsigned i = 0; i < heapAccesses_.length(); i++) {
const HeapAccess& access = heapAccesses_[i];
for (unsigned i = 0; i < module_->heapAccesses.length(); i++) {
const HeapAccess& access = module_->heapAccesses[i];
if (access.hasLengthCheck())
X86Encoding::AddInt32(access.patchLengthAt(code()), -heapLength);
}
@@ -594,17 +710,17 @@ Module::sendCodeRangesToProfiler(JSContext* cx)
{
#ifdef JS_ION_PERF
if (PerfFuncEnabled()) {
for (const CodeRange& codeRange : codeRanges_) {
for (const CodeRange& codeRange : module_->codeRanges) {
if (!codeRange.isFunction())
continue;
uintptr_t start = uintptr_t(code() + codeRange.begin());
uintptr_t end = uintptr_t(code() + codeRange.end());
uintptr_t size = end - start;
const char* file = filename_.get();
const char* file = module_->filename.get();
unsigned line = codeRange.funcLineNumber();
unsigned column = 0;
const char* name = funcNames_[codeRange.funcNameIndex()].get();
const char* name = module_->funcNames[codeRange.funcNameIndex()].get();
writePerfSpewerAsmJSFunctionMap(start, size, file, line, column, name);
}
@@ -612,14 +728,14 @@ Module::sendCodeRangesToProfiler(JSContext* cx)
#endif
#ifdef MOZ_VTUNE
if (IsVTuneProfilingActive()) {
for (const CodeRange& codeRange : codeRanges_) {
for (const CodeRange& codeRange : module_->codeRanges) {
if (!codeRange.isFunction())
continue;
uintptr_t start = uintptr_t(code() + codeRange.begin());
uintptr_t end = uintptr_t(code() + codeRange.end());
uintptr_t size = end - start;
const char* name = funcNames_[codeRange.funcNameIndex()].get();
const char* name = module_->funcNames[codeRange.funcNameIndex()].get();
unsigned method_id = iJIT_GetNewMethodID();
if (method_id == 0)
@@ -654,16 +770,16 @@ Module::setProfilingEnabled(JSContext* cx, bool enabled)
// do it now since, once we start sampling, we'll be in a signal-handing
// context where we cannot malloc.
if (enabled) {
if (!funcLabels_.resize(funcNames_.length())) {
if (!funcLabels_.resize(module_->funcNames.length())) {
ReportOutOfMemory(cx);
return false;
}
for (const CodeRange& codeRange : codeRanges_) {
for (const CodeRange& codeRange : module_->codeRanges) {
if (!codeRange.isFunction())
continue;
unsigned lineno = codeRange.funcLineNumber();
const char* name = funcNames_[codeRange.funcNameIndex()].get();
UniqueChars label(JS_smprintf("%s (%s:%u)", name, filename_.get(), lineno));
const char* name = module_->funcNames[codeRange.funcNameIndex()].get();
UniqueChars label(JS_smprintf("%s (%s:%u)", name, module_->filename.get(), lineno));
if (!label) {
ReportOutOfMemory(cx);
return false;
@@ -674,14 +790,16 @@ Module::setProfilingEnabled(JSContext* cx, bool enabled)
funcLabels_.clear();
}
// Patch callsites and returns to execute profiling prologues/epililogues.
// Patch callsites and returns to execute profiling prologues/epilogues.
{
AutoMutateCode amc(cx, *this, "Module::setProfilingEnabled");
AutoWritableJitCode awjc(cx->runtime(), code(), codeBytes());
AutoFlushICache afc("Module::setProfilingEnabled");
AutoFlushICache::setRange(uintptr_t(code()), codeBytes());
for (const CallSite& callSite : callSites_)
for (const CallSite& callSite : module_->callSites)
EnableProfilingPrologue(*this, callSite, enabled);
for (const CodeRange& codeRange : codeRanges_)
for (const CodeRange& codeRange : module_->codeRanges)
EnableProfilingEpilogue(*this, codeRange, enabled);
}
@@ -709,114 +827,70 @@ Module::importToExit(const Import& import)
return *reinterpret_cast<ImportExit*>(globalData() + import.exitGlobalDataOffset());
}
/* static */ Module::CacheablePod
Module::zeroPod()
bool
Module::clone(JSContext* cx, const StaticLinkData& link, Module* out) const
{
CacheablePod pod = {0, 0, 0, HeapUsage::None, false, false, false};
return pod;
MOZ_ASSERT(dynamicallyLinked_);
// The out->module_ field was already cloned and initialized when 'out' was
// constructed. This function should clone the rest.
MOZ_ASSERT(out->module_);
out->isAsmJS_ = isAsmJS_;
out->profilingEnabled_ = profilingEnabled_;
if (!CloneVector(cx, funcLabels_, &out->funcLabels_))
return false;
#ifdef DEBUG
// Put the symbolic links back to -1 so PatchDataWithValueCheck assertions
// in Module::staticallyLink are valid.
for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
void* callee = AddressOf(imm, cx);
const StaticLinkData::OffsetVector& offsets = link.symbolicLinks[imm];
for (uint32_t offset : offsets) {
jit::Assembler::PatchDataWithValueCheck(jit::CodeLocationLabel(out->code() + offset),
jit::PatchedImmPtr((void*)-1),
jit::PatchedImmPtr(callee));
}
}
#endif
// If the copied machine code has been specialized to the heap, it must be
// unspecialized in the copy.
if (usesHeap())
out->despecializeFromHeap(heap_);
return true;
}
void
Module::init()
{
staticallyLinked_ = false;
interrupt_ = nullptr;
outOfBounds_ = nullptr;
dynamicallyLinked_ = false;
Module::Module(UniqueModuleData module, AsmJSBool isAsmJS)
: module_(Move(module)),
isAsmJS_(bool(isAsmJS)),
staticallyLinked_(false),
interrupt_(nullptr),
outOfBounds_(nullptr),
dynamicallyLinked_(false),
profilingEnabled_(false)
{
*(double*)(globalData() + NaN64GlobalDataOffset) = GenericNaN();
*(float*)(globalData() + NaN32GlobalDataOffset) = GenericNaN();
}
// Private constructor used for deserialization and cloning.
Module::Module(const CacheablePod& pod,
UniqueCodePtr code,
ImportVector&& imports,
ExportVector&& exports,
HeapAccessVector&& heapAccesses,
CodeRangeVector&& codeRanges,
CallSiteVector&& callSites,
CacheableCharsVector&& funcNames,
CacheableChars filename,
CacheableTwoByteChars displayURL,
CacheBool loadedFromCache,
ProfilingBool profilingEnabled,
FuncLabelVector&& funcLabels)
: pod(pod),
code_(Move(code)),
imports_(Move(imports)),
exports_(Move(exports)),
heapAccesses_(Move(heapAccesses)),
codeRanges_(Move(codeRanges)),
callSites_(Move(callSites)),
funcNames_(Move(funcNames)),
filename_(Move(filename)),
displayURL_(Move(displayURL)),
loadedFromCache_(loadedFromCache),
profilingEnabled_(profilingEnabled),
funcLabels_(Move(funcLabels))
{
MOZ_ASSERT_IF(!profilingEnabled, funcLabels_.empty());
MOZ_ASSERT_IF(profilingEnabled, funcNames_.length() == funcLabels_.length());
init();
}
// Public constructor for compilation.
Module::Module(CompileArgs args,
uint32_t functionBytes,
uint32_t codeBytes,
uint32_t globalBytes,
HeapUsage heapUsage,
MutedBool mutedErrors,
UniqueCodePtr code,
ImportVector&& imports,
ExportVector&& exports,
HeapAccessVector&& heapAccesses,
CodeRangeVector&& codeRanges,
CallSiteVector&& callSites,
CacheableCharsVector&& funcNames,
CacheableChars filename,
CacheableTwoByteChars displayURL)
: pod(zeroPod()),
code_(Move(code)),
imports_(Move(imports)),
exports_(Move(exports)),
heapAccesses_(Move(heapAccesses)),
codeRanges_(Move(codeRanges)),
callSites_(Move(callSites)),
funcNames_(Move(funcNames)),
filename_(Move(filename)),
displayURL_(Move(displayURL)),
loadedFromCache_(false),
profilingEnabled_(false)
{
// Work around MSVC 2013 bug around {} member initialization.
const_cast<uint32_t&>(pod.functionBytes_) = functionBytes;
const_cast<uint32_t&>(pod.codeBytes_) = codeBytes;
const_cast<uint32_t&>(pod.globalBytes_) = globalBytes;
const_cast<HeapUsage&>(pod.heapUsage_) = heapUsage;
const_cast<bool&>(pod.mutedErrors_) = bool(mutedErrors);
const_cast<bool&>(pod.usesSignalHandlersForOOB_) = args.useSignalHandlersForOOB;
const_cast<bool&>(pod.usesSignalHandlersForInterrupt_) = args.useSignalHandlersForInterrupt;
init();
}
Module::~Module()
{
if (code_) {
for (unsigned i = 0; i < imports_.length(); i++) {
ImportExit& exit = importToExit(imports_[i]);
if (exit.baselineScript)
exit.baselineScript->removeDependentWasmModule(*this, i);
}
for (unsigned i = 0; i < imports().length(); i++) {
ImportExit& exit = importToExit(imports()[i]);
if (exit.baselineScript)
exit.baselineScript->removeDependentWasmModule(*this, i);
}
}
void
Module::trace(JSTracer* trc)
{
for (const Import& import : imports_) {
for (const Import& import : imports()) {
if (importToExit(import).fun)
TraceEdge(trc, &importToExit(import).fun, "wasm function import");
}
@@ -825,25 +899,16 @@ Module::trace(JSTracer* trc)
TraceEdge(trc, &heap_, "wasm buffer");
}
CompileArgs
Module::compileArgs() const
{
CompileArgs args;
args.useSignalHandlersForOOB = pod.usesSignalHandlersForOOB_;
args.useSignalHandlersForInterrupt = pod.usesSignalHandlersForInterrupt_;
return args;
}
bool
Module::containsFunctionPC(void* pc) const
{
return pc >= code() && pc < (code() + pod.functionBytes_);
return pc >= code() && pc < (code() + module_->functionBytes);
}
bool
Module::containsCodePC(void* pc) const
{
return pc >= code() && pc < (code() + pod.codeBytes_);
return pc >= code() && pc < (code() + codeBytes());
}
struct CallSiteRetAddrOffset
@@ -860,13 +925,13 @@ Module::lookupCallSite(void* returnAddress) const
{
uint32_t target = ((uint8_t*)returnAddress) - code();
size_t lowerBound = 0;
size_t upperBound = callSites_.length();
size_t upperBound = module_->callSites.length();
size_t match;
if (!BinarySearch(CallSiteRetAddrOffset(callSites_), lowerBound, upperBound, target, &match))
if (!BinarySearch(CallSiteRetAddrOffset(module_->callSites), lowerBound, upperBound, target, &match))
return nullptr;
return &callSites_[match];
return &module_->callSites[match];
}
const CodeRange*
@@ -874,13 +939,13 @@ Module::lookupCodeRange(void* pc) const
{
CodeRange::PC target((uint8_t*)pc - code());
size_t lowerBound = 0;
size_t upperBound = codeRanges_.length();
size_t upperBound = module_->codeRanges.length();
size_t match;
if (!BinarySearch(codeRanges_, lowerBound, upperBound, target, &match))
if (!BinarySearch(module_->codeRanges, lowerBound, upperBound, target, &match))
return nullptr;
return &codeRanges_[match];
return &module_->codeRanges[match];
}
struct HeapAccessOffset
@@ -899,13 +964,13 @@ Module::lookupHeapAccess(void* pc) const
uint32_t target = ((uint8_t*)pc) - code();
size_t lowerBound = 0;
size_t upperBound = heapAccesses_.length();
size_t upperBound = module_->heapAccesses.length();
size_t match;
if (!BinarySearch(HeapAccessOffset(heapAccesses_), lowerBound, upperBound, target, &match))
if (!BinarySearch(HeapAccessOffset(module_->heapAccesses), lowerBound, upperBound, target, &match))
return nullptr;
return &heapAccesses_[match];
return &module_->heapAccesses[match];
}
bool
@@ -920,7 +985,7 @@ Module::staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData)
JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
MOZ_ASSERT(IsCompilingAsmJS());
AutoFlushICache afc("Module::staticallyLink", /* inhibit = */ true);
AutoFlushICache::setRange(uintptr_t(code()), pod.codeBytes_);
AutoFlushICache::setRange(uintptr_t(code()), codeBytes());
interrupt_ = code() + linkData.pod.interruptOffset;
outOfBounds_ = code() + linkData.pod.outOfBoundsOffset;
@@ -983,7 +1048,7 @@ Module::staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData)
bool
Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
const AutoVectorRooter<JSFunction*>& imports)
Handle<FunctionVector> importArgs)
{
MOZ_ASSERT(staticallyLinked_);
MOZ_ASSERT(!dynamicallyLinked_);
@@ -994,15 +1059,15 @@ Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> hea
JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
MOZ_ASSERT(IsCompilingAsmJS());
AutoFlushICache afc("Module::dynamicallyLink");
AutoFlushICache::setRange(uintptr_t(code()), pod.codeBytes_);
AutoFlushICache::setRange(uintptr_t(code()), codeBytes());
// Initialize imports with actual imported values.
MOZ_ASSERT(imports.length() == imports_.length());
for (size_t i = 0; i < imports_.length(); i++) {
const Import& import = imports_[i];
MOZ_ASSERT(importArgs.length() == imports().length());
for (size_t i = 0; i < imports().length(); i++) {
const Import& import = imports()[i];
ImportExit& exit = importToExit(import);
exit.code = code() + import.interpExitCodeOffset();
exit.fun = imports[i];
exit.fun = importArgs[i];
exit.baselineScript = nullptr;
}
@@ -1011,7 +1076,7 @@ Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> hea
specializeToHeap(heap);
// See AllocateCode comment above.
if (!ExecutableAllocator::makeExecutable(code(), pod.codeBytes_)) {
if (!ExecutableAllocator::makeExecutable(code(), codeBytes())) {
ReportOutOfMemory(cx);
return false;
}
@@ -1020,6 +1085,92 @@ Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> hea
return true;
}
static bool
WasmCall(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction callee(cx, &args.callee().as<JSFunction>());
Module& module = ExportedFunctionToModuleObject(callee)->module();
uint32_t exportIndex = ExportedFunctionToIndex(callee);
return module.callExport(cx, exportIndex, args);
}
static JSFunction*
NewExportedFunction(JSContext* cx, Handle<WasmModuleObject*> moduleObj, const ExportMap& map,
uint32_t exportIndex)
{
unsigned numArgs = moduleObj->module().exports()[exportIndex].sig().args().length();
const char* chars = map.exportNames[exportIndex].get();
RootedAtom name(cx, AtomizeUTF8Chars(cx, chars, strlen(chars)));
if (!name)
return nullptr;
JSFunction* fun = NewNativeConstructor(cx, WasmCall, numArgs, name,
gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
JSFunction::ASMJS_CTOR);
if (!fun)
return nullptr;
fun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj));
fun->setExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
return fun;
}
bool
Module::createExportObject(JSContext* cx, Handle<WasmModuleObject*> moduleObj,
const ExportMap& map, MutableHandleObject exportObj)
{
MOZ_ASSERT(this == &moduleObj->module());
MOZ_ASSERT(map.exportNames.length() == exports().length());
MOZ_ASSERT(map.fieldNames.length() == map.fieldsToExports.length());
for (size_t fieldIndex = 0; fieldIndex < map.fieldNames.length(); fieldIndex++) {
const char* fieldName = map.fieldNames[fieldIndex].get();
if (!*fieldName) {
MOZ_ASSERT_IF(isAsmJS(), exports().length() == 1);
MOZ_ASSERT(!exportObj);
uint32_t exportIndex = map.fieldsToExports[fieldIndex];
exportObj.set(NewExportedFunction(cx, moduleObj, map, exportIndex));
if (!exportObj)
return false;
break;
}
}
Rooted<ValueVector> vals(cx, ValueVector(cx));
for (size_t exportIndex = 0; exportIndex < exports().length(); exportIndex++) {
JSFunction* fun = NewExportedFunction(cx, moduleObj, map, exportIndex);
if (!fun || !vals.append(ObjectValue(*fun)))
return false;
}
if (!exportObj) {
exportObj.set(JS_NewPlainObject(cx));
if (!exportObj)
return false;
}
for (size_t fieldIndex = 0; fieldIndex < map.fieldNames.length(); fieldIndex++) {
const char* fieldName = map.fieldNames[fieldIndex].get();
if (!*fieldName)
continue;
JSAtom* atom = AtomizeUTF8Chars(cx, fieldName, strlen(fieldName));
if (!atom)
return false;
RootedId id(cx, AtomToId(atom));
HandleValue val = vals[map.fieldsToExports[fieldIndex]];
if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
return false;
}
return true;
}
SharedMem<uint8_t*>
Module::heap() const
{
@@ -1043,7 +1194,7 @@ void
Module::deoptimizeImportExit(uint32_t importIndex)
{
MOZ_ASSERT(dynamicallyLinked_);
const Import& import = imports_[importIndex];
const Import& import = imports()[importIndex];
ImportExit& exit = importToExit(import);
exit.code = code() + import.interpExitCodeOffset();
exit.baselineScript = nullptr;
@@ -1054,7 +1205,7 @@ Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
{
MOZ_ASSERT(dynamicallyLinked_);
const Export& exp = exports_[exportIndex];
const Export& exp = exports()[exportIndex];
// Enable/disable profiling in the Module to match the current global
// profiling state. Don't do this if the Module is already active on the
@@ -1190,7 +1341,7 @@ Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Val
{
MOZ_ASSERT(dynamicallyLinked_);
const Import& import = imports_[importIndex];
const Import& import = imports()[importIndex];
RootedValue fval(cx, ObjectValue(*importToExit(import).fun));
if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval))
@@ -1264,211 +1415,114 @@ Module::profilingLabel(uint32_t funcIndex) const
return funcLabels_[funcIndex].get();
}
size_t
Module::serializedSize() const
void
Module::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
{
return sizeof(pod) +
pod.codeBytes_ +
SerializedVectorSize(imports_) +
SerializedVectorSize(exports_) +
SerializedPodVectorSize(heapAccesses_) +
SerializedPodVectorSize(codeRanges_) +
SerializedPodVectorSize(callSites_) +
SerializedVectorSize(funcNames_) +
filename_.serializedSize() +
displayURL_.serializedSize();
*code += codeBytes();
*data += mallocSizeOf(this) +
globalBytes() +
mallocSizeOf(module_.get()) +
module_->sizeOfExcludingThis(mallocSizeOf) +
funcPtrTables_.sizeOfExcludingThis(mallocSizeOf) +
SizeOfVectorExcludingThis(funcLabels_, mallocSizeOf);
}
uint8_t*
Module::serialize(uint8_t* cursor) const
{
MOZ_ASSERT(!profilingEnabled_, "assumed by Module::deserialize");
const Class WasmModuleObject::class_ = {
"WasmModuleObject",
JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK |
JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS),
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* getProperty */
nullptr, /* setProperty */
nullptr, /* enumerate */
nullptr, /* resolve */
nullptr, /* mayResolve */
WasmModuleObject::finalize,
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
WasmModuleObject::trace
};
cursor = WriteBytes(cursor, &pod, sizeof(pod));
cursor = WriteBytes(cursor, code(), pod.codeBytes_);
cursor = SerializeVector(cursor, imports_);
cursor = SerializeVector(cursor, exports_);
cursor = SerializePodVector(cursor, heapAccesses_);
cursor = SerializePodVector(cursor, codeRanges_);
cursor = SerializePodVector(cursor, callSites_);
cursor = SerializeVector(cursor, funcNames_);
cursor = filename_.serialize(cursor);
cursor = displayURL_.serialize(cursor);
return cursor;
bool
WasmModuleObject::hasModule() const
{
MOZ_ASSERT(is<WasmModuleObject>());
return !getReservedSlot(MODULE_SLOT).isUndefined();
}
/* static */ const uint8_t*
Module::deserialize(ExclusiveContext* cx, const uint8_t* cursor, UniqueModule* out)
/* static */ void
WasmModuleObject::finalize(FreeOp* fop, JSObject* obj)
{
CacheablePod pod = zeroPod();
cursor = ReadBytes(cursor, &pod, sizeof(pod));
if (!cursor)
return nullptr;
UniqueCodePtr code = AllocateCode(cx, pod.codeBytes_ + pod.globalBytes_);
if (!code)
return nullptr;
cursor = ReadBytes(cursor, code.get(), pod.codeBytes_);
ImportVector imports;
cursor = DeserializeVector(cx, cursor, &imports);
if (!cursor)
return nullptr;
ExportVector exports;
cursor = DeserializeVector(cx, cursor, &exports);
if (!cursor)
return nullptr;
HeapAccessVector heapAccesses;
cursor = DeserializePodVector(cx, cursor, &heapAccesses);
if (!cursor)
return nullptr;
CodeRangeVector codeRanges;
cursor = DeserializePodVector(cx, cursor, &codeRanges);
if (!cursor)
return nullptr;
CallSiteVector callSites;
cursor = DeserializePodVector(cx, cursor, &callSites);
if (!cursor)
return nullptr;
CacheableCharsVector funcNames;
cursor = DeserializeVector(cx, cursor, &funcNames);
if (!cursor)
return nullptr;
CacheableChars filename;
cursor = filename.deserialize(cx, cursor);
if (!cursor)
return nullptr;
CacheableTwoByteChars displayURL;
cursor = displayURL.deserialize(cx, cursor);
if (!cursor)
return nullptr;
*out = cx->make_unique<Module>(pod,
Move(code),
Move(imports),
Move(exports),
Move(heapAccesses),
Move(codeRanges),
Move(callSites),
Move(funcNames),
Move(filename),
Move(displayURL),
Module::LoadedFromCache,
Module::ProfilingDisabled,
FuncLabelVector());
return cursor;
WasmModuleObject& moduleObj = obj->as<WasmModuleObject>();
if (moduleObj.hasModule())
fop->delete_(&moduleObj.module());
}
Module::UniqueModule
Module::clone(JSContext* cx, const StaticLinkData& linkData) const
/* static */ void
WasmModuleObject::trace(JSTracer* trc, JSObject* obj)
{
MOZ_ASSERT(dynamicallyLinked_);
WasmModuleObject& moduleObj = obj->as<WasmModuleObject>();
if (moduleObj.hasModule())
moduleObj.module().trace(trc);
}
UniqueCodePtr code = AllocateCode(cx, totalBytes());
if (!code)
/* static */ WasmModuleObject*
WasmModuleObject::create(ExclusiveContext* cx)
{
AutoSetNewObjectMetadata metadata(cx);
JSObject* obj = NewObjectWithGivenProto(cx, &WasmModuleObject::class_, nullptr);
if (!obj)
return nullptr;
memcpy(code.get(), this->code(), pod.codeBytes_);
return &obj->as<WasmModuleObject>();
}
#ifdef DEBUG
// Put the symbolic links back to -1 so PatchDataWithValueCheck assertions
// in Module::staticallyLink are valid.
for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
void* callee = AddressOf(imm, cx);
const StaticLinkData::OffsetVector& offsets = linkData.symbolicLinks[imm];
for (uint32_t offset : offsets) {
jit::Assembler::PatchDataWithValueCheck(jit::CodeLocationLabel(code.get() + offset),
jit::PatchedImmPtr((void*)-1),
jit::PatchedImmPtr(callee));
}
}
#endif
bool
WasmModuleObject::init(Module* module)
{
MOZ_ASSERT(is<WasmModuleObject>());
MOZ_ASSERT(!hasModule());
if (!module)
return false;
setReservedSlot(MODULE_SLOT, PrivateValue(module));
return true;
}
ImportVector imports;
if (!CloneVector(cx, imports_, &imports))
return nullptr;
ExportVector exports;
if (!CloneVector(cx, exports_, &exports))
return nullptr;
HeapAccessVector heapAccesses;
if (!ClonePodVector(cx, heapAccesses_, &heapAccesses))
return nullptr;
CodeRangeVector codeRanges;
if (!ClonePodVector(cx, codeRanges_, &codeRanges))
return nullptr;
CallSiteVector callSites;
if (!ClonePodVector(cx, callSites_, &callSites))
return nullptr;
CacheableCharsVector funcNames;
if (!CloneVector(cx, funcNames_, &funcNames))
return nullptr;
CacheableChars filename;
if (!filename_.clone(cx, &filename))
return nullptr;
CacheableTwoByteChars displayURL;
if (!displayURL_.clone(cx, &displayURL))
return nullptr;
FuncLabelVector funcLabels;
if (!CloneVector(cx, funcLabels_, &funcLabels))
return nullptr;
// Must not GC between Module allocation and (successful) return.
auto out = cx->make_unique<Module>(pod,
Move(code),
Move(imports),
Move(exports),
Move(heapAccesses),
Move(codeRanges),
Move(callSites),
Move(funcNames),
Move(filename),
Move(displayURL),
CacheBool::NotLoadedFromCache,
ProfilingBool(profilingEnabled_),
Move(funcLabels));
if (!out)
return nullptr;
// If the copied machine code has been specialized to the heap, it must be
// unspecialized in the copy.
if (usesHeap())
out->despecializeFromHeap(heap_);
if (!out->staticallyLink(cx, linkData))
return nullptr;
return Move(out);
Module&
WasmModuleObject::module() const
{
MOZ_ASSERT(is<WasmModuleObject>());
MOZ_ASSERT(hasModule());
return *(Module*)getReservedSlot(MODULE_SLOT).toPrivate();
}
void
Module::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode, size_t* asmJSModuleData)
WasmModuleObject::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
{
*asmJSModuleCode += pod.codeBytes_;
*asmJSModuleData += mallocSizeOf(this) +
pod.globalBytes_ +
SizeOfVectorExcludingThis(imports_, mallocSizeOf) +
SizeOfVectorExcludingThis(exports_, mallocSizeOf) +
heapAccesses_.sizeOfExcludingThis(mallocSizeOf) +
codeRanges_.sizeOfExcludingThis(mallocSizeOf) +
callSites_.sizeOfExcludingThis(mallocSizeOf) +
funcNames_.sizeOfExcludingThis(mallocSizeOf) +
funcPtrTables_.sizeOfExcludingThis(mallocSizeOf);
if (hasModule())
module().addSizeOfMisc(mallocSizeOf, code, data);
}
bool
wasm::IsExportedFunction(JSFunction* fun)
{
return fun->maybeNative() == WasmCall;
}
WasmModuleObject*
wasm::ExportedFunctionToModuleObject(JSFunction* fun)
{
MOZ_ASSERT(IsExportedFunction(fun));
const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_MODULE_SLOT);
return &v.toObject().as<WasmModuleObject>();
}
uint32_t
wasm::ExportedFunctionToIndex(JSFunction* fun)
{
MOZ_ASSERT(IsExportedFunction(fun));
const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT);
return v.toInt32();
}