Merge inbound to mozilla-central. a=merge

This commit is contained in:
Gurzau Raul 2018-01-25 19:02:48 +02:00
Родитель 0cc3f8ba82 49c692983a
Коммит 062e1cf551
62 изменённых файлов: 2612 добавлений и 1181 удалений

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

@ -402,7 +402,7 @@ ChromeUtils::Import(const GlobalObject& aGlobal,
}
JS::Rooted<JS::Value> retval(cx);
nsresult rv = moduleloader->Import(registryLocation, targetObj, cx,
nsresult rv = moduleloader->ImportInto(registryLocation, targetObj, cx,
optionalArgc, &retval);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
@ -421,6 +421,149 @@ ChromeUtils::Import(const GlobalObject& aGlobal,
aRetval.set(&retval.toObject());
}
namespace module_getter {
static const size_t SLOT_ID = 0;
static const size_t SLOT_URI = 1;
static bool
ExtractArgs(JSContext* aCx, JS::CallArgs& aArgs,
JS::MutableHandle<JSObject*> aCallee,
JS::MutableHandle<JSObject*> aThisObj,
JS::MutableHandle<jsid> aId)
{
aCallee.set(&aArgs.callee());
JS::Handle<JS::Value> thisv = aArgs.thisv();
if (!thisv.isObject()) {
JS_ReportErrorASCII(aCx, "Invalid target object");
return false;
}
aThisObj.set(&thisv.toObject());
JS::Rooted<JS::Value> id(aCx, js::GetFunctionNativeReserved(aCallee, SLOT_ID));
MOZ_ALWAYS_TRUE(JS_ValueToId(aCx, id, aId));
return true;
}
static bool
ModuleGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
JS::Rooted<JSObject*> callee(aCx);
JS::Rooted<JSObject*> thisObj(aCx);
JS::Rooted<jsid> id(aCx);
if (!ExtractArgs(aCx, args, &callee, &thisObj, &id)) {
return false;
}
JS::Rooted<JSString*> moduleURI(
aCx, js::GetFunctionNativeReserved(callee, SLOT_URI).toString());
JSAutoByteString bytes;
if (!bytes.encodeUtf8(aCx, moduleURI)) {
return false;
}
nsDependentCString uri(bytes.ptr());
RefPtr<mozJSComponentLoader> moduleloader = mozJSComponentLoader::Get();
MOZ_ASSERT(moduleloader);
JS::Rooted<JSObject*> moduleGlobal(aCx);
JS::Rooted<JSObject*> moduleExports(aCx);
nsresult rv = moduleloader->Import(aCx, uri, &moduleGlobal, &moduleExports);
if (NS_FAILED(rv)) {
Throw(aCx, rv);
return false;
}
JS::RootedValue value(aCx);
{
JSAutoCompartment ac(aCx, moduleExports);
if (!JS_GetPropertyById(aCx, moduleExports, id, &value)) {
return false;
}
}
if (!JS_WrapValue(aCx, &value) ||
!JS_DefinePropertyById(aCx, thisObj, id, value,
JSPROP_ENUMERATE)) {
return false;
}
args.rval().set(value);
return true;
}
static bool
ModuleSetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
JS::Rooted<JSObject*> callee(aCx);
JS::Rooted<JSObject*> thisObj(aCx);
JS::Rooted<jsid> id(aCx);
if (!ExtractArgs(aCx, args, &callee, &thisObj, &id)) {
return false;
}
return JS_DefinePropertyById(aCx, thisObj, id, args.get(0),
JSPROP_ENUMERATE);
}
static bool
DefineGetter(JSContext* aCx,
JS::Handle<JSObject*> aTarget,
const nsAString& aId,
const nsAString& aResourceURI)
{
JS::RootedValue uri(aCx);
JS::RootedValue idValue(aCx);
JS::Rooted<jsid> id(aCx);
if (!xpc::NonVoidStringToJsval(aCx, aResourceURI, &uri) ||
!xpc::NonVoidStringToJsval(aCx, aId, &idValue) ||
!JS_ValueToId(aCx, idValue, &id)) {
return false;
}
idValue = js::IdToValue(id);
JS::Rooted<JSObject*> getter(aCx, JS_GetFunctionObject(
js::NewFunctionByIdWithReserved(aCx, ModuleGetter, 0, 0, id)));
JS::Rooted<JSObject*> setter(aCx, JS_GetFunctionObject(
js::NewFunctionByIdWithReserved(aCx, ModuleSetter, 0, 0, id)));
if (!getter || !setter) {
JS_ReportOutOfMemory(aCx);
return false;
}
js::SetFunctionNativeReserved(getter, SLOT_ID, idValue);
js::SetFunctionNativeReserved(setter, SLOT_ID, idValue);
js::SetFunctionNativeReserved(getter, SLOT_URI, uri);
return JS_DefinePropertyById(aCx, aTarget, id,
JS_DATA_TO_FUNC_PTR(JSNative, getter.get()),
JS_DATA_TO_FUNC_PTR(JSNative, setter.get()),
JSPROP_GETTER | JSPROP_SETTER | JSPROP_ENUMERATE);
}
} // namespace module_getter
/* static */ void
ChromeUtils::DefineModuleGetter(const GlobalObject& global,
JS::Handle<JSObject*> target,
const nsAString& id,
const nsAString& resourceURI,
ErrorResult& aRv)
{
if (!module_getter::DefineGetter(global.Context(), target, id, resourceURI)) {
aRv.NoteJSContextException(global.Context());
}
}
/* static */ void
ChromeUtils::OriginAttributesToSuffix(dom::GlobalObject& aGlobal,
const dom::OriginAttributesDictionary& aAttrs,

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

@ -160,6 +160,12 @@ public:
const Optional<JS::Handle<JSObject*>>& aTargetObj,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv);
static void DefineModuleGetter(const GlobalObject& global,
JS::Handle<JSObject*> target,
const nsAString& id,
const nsAString& resourceURI,
ErrorResult& aRv);
};
} // namespace dom

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

@ -2375,7 +2375,7 @@ PluginModuleChild::NPN_ReleaseObject(NPObject* aNPObj)
PluginInstanceChild* instance = PluginScriptableObjectChild::GetInstanceForNPObject(aNPObj);
if (!instance) {
NS_ERROR("Releasing object not in mObjectMap?");
// The PluginInstanceChild was destroyed
return;
}

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

@ -1273,6 +1273,11 @@ PluginScriptableObjectChild::UnregisterObject(NPObject* aObject)
PluginScriptableObjectChild::GetInstanceForNPObject(NPObject* aObject)
{
AssertPluginThread();
if (!sObjectMap) {
// All PluginInstanceChilds have been destroyed
return nullptr;
}
NPObjectData* d = sObjectMap->GetEntry(aObject);
if (!d) {
return nullptr;

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

@ -261,6 +261,39 @@ partial namespace ChromeUtils {
*/
[Throws]
object import(DOMString aResourceURI, optional object? aTargetObj);
/**
* Defines a property on the given target which lazily imports a JavaScript
* module when accessed.
*
* The first time the property is accessed, the module at the given URL is
* imported, and the property is replaced with the module's exported symbol
* of the same name.
*
* Some points to note when using this utility:
*
* - The cached module export is always stored on the `this` object that was
* used to access the getter. This means that if you define the lazy
* getter on a prototype, the module will be re-imported every time the
* property is accessed on a new instance.
*
* - The getter property may be overwritten by simple assignment, but as
* with imports, the new property value is always defined on the `this`
* object that the setter is called with.
*
* - If the module import fails, the getter will throw an error, and the
* property will not be replaced. Each subsequent attempt to access the
* getter will attempt to re-import the object, which will likely continue
* to result in errors.
*
* @param target The target object on which to define the property.
* @param id The name of the property to define, and of the symbol to
* import.
* @param resourceURI The resource URI of the module, as passed to
* ChromeUtils.import.
*/
[Throws]
void defineModuleGetter(object target, DOMString id, DOMString resourceURI);
};
/**

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

@ -184,7 +184,7 @@ def jit_disasm_arm(ion_enabled, simulator, target, debug):
if not ion_enabled:
return
if simulator:
if simulator and debug:
if getattr(simulator, 'arm', None):
return True
@ -194,6 +194,21 @@ def jit_disasm_arm(ion_enabled, simulator, target, debug):
set_config('JS_DISASM_ARM', jit_disasm_arm)
set_define('JS_DISASM_ARM', jit_disasm_arm)
@depends('--enable-ion', simulator, target, moz_debug)
def jit_disasm_arm64(ion_enabled, simulator, target, debug):
if not ion_enabled:
return
if simulator and debug:
if getattr(simulator, 'arm64', None):
return True
if target.cpu == 'aarch64' and debug:
return True
set_config('JS_DISASM_ARM64', jit_disasm_arm64)
set_define('JS_DISASM_ARM64', jit_disasm_arm64)
# Profiling
# =======================================================
js_option('--enable-instruments', env='MOZ_INSTRUMENTS',

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

@ -265,6 +265,11 @@ JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data, uint32_t vers
JS::MutableHandleValue vp,
const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
/**
* Note: If the scope is DifferentProcess then the cloneDataPolicy must deny
* shared-memory objects, or an error will be signaled if a shared memory object
* is seen.
*/
JS_PUBLIC_API(bool)
JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, JSStructuredCloneData* data,
JS::StructuredCloneScope scope,

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

@ -5363,7 +5363,9 @@ gc::ZealModeHelpText),
" to specify whether SharedArrayBuffers may be serialized.\n"
" 'scope' - SameProcessSameThread, SameProcessDifferentThread, or\n"
" DifferentProcess. Determines how some values will be serialized.\n"
" Clone buffers may only be deserialized with a compatible scope."),
" Clone buffers may only be deserialized with a compatible scope.\n"
" NOTE - For DifferentProcess, must also set SharedArrayBuffer:'deny'\n"
" if data contains any shared memory object."),
JS_FN_HELP("deserialize", Deserialize, 1, 0,
"deserialize(clonebuffer[, opts])",

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

@ -119,6 +119,11 @@ module = wasmEvalText(`(module
)`, { globals: {x: 42} }).exports;
assertEq(module.getter(), 42);
// Adapt to ongoing experiment with WebAssembly.Global.
// assertEq() will not trigger @@toPrimitive, so we must have a cast here.
if (typeof WebAssembly.Global === "function")
assertEq(Number(module.value), 42);
else
assertEq(module.value, 42);
// Can only import numbers (no implicit coercions).
@ -174,8 +179,14 @@ module = wasmEvalText(`(module
(export "defined" global 1)
)`, { globals: {x: 42} }).exports;
// See comment earlier about WebAssembly.Global
if (typeof WebAssembly.Global === "function") {
assertEq(Number(module.imported), 42);
assertEq(Number(module.defined), 1337);
} else {
assertEq(module.imported, 42);
assertEq(module.defined, 1337);
}
// Initializer expressions can reference an imported immutable global.
wasmFailValidateText(`(module (global f32 (f32.const 13.37)) (global i32 (get_global 0)))`, /must reference a global immutable import/);
@ -212,11 +223,19 @@ function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = asse
assertFunc(module.get0(), coercion(initialValue));
assertFunc(module.get1(), coercion(initialValue));
// See comment earlier about WebAssembly.Global
if (typeof WebAssembly.Global === "function")
assertFunc(Number(module.global_imm), coercion(initialValue));
else
assertFunc(module.global_imm, coercion(initialValue));
assertEq(module.set1(coercion(nextValue)), undefined);
assertFunc(module.get1(), coercion(nextValue));
assertFunc(module.get0(), coercion(initialValue));
// See comment earlier about WebAssembly.Global
if (typeof WebAssembly.Global === "function")
assertFunc(Number(module.global_imm), coercion(initialValue));
else
assertFunc(module.global_imm, coercion(initialValue));
assertFunc(module.get_cst(), coercion(initialValue));
@ -270,3 +289,64 @@ wasmAssert(`(module
dv.setFloat32(0, module.nan32, true);
assertEq(dv.getUint32(0, true), 0x7fc00000);
}
// WebAssembly.Global experiment
if (typeof WebAssembly.Global === "function") {
// These types should work:
assertEq(new WebAssembly.Global({type: "i32"}) instanceof WebAssembly.Global, true);
assertEq(new WebAssembly.Global({type: "f32"}) instanceof WebAssembly.Global, true);
assertEq(new WebAssembly.Global({type: "f64"}) instanceof WebAssembly.Global, true);
// These types should not work:
assertErrorMessage(() => new WebAssembly.Global({type: "i64"}),
TypeError,
/bad type for a WebAssembly.Global/);
assertErrorMessage(() => new WebAssembly.Global({}),
TypeError,
/bad type for a WebAssembly.Global/);
assertErrorMessage(() => new WebAssembly.Global({type: "fnord"}),
TypeError,
/bad type for a WebAssembly.Global/);
assertErrorMessage(() => new WebAssembly.Global(),
TypeError,
/WebAssembly.Global requires more than 0 arguments/);
// Coercion of init value; ".value" accessor
assertEq((new WebAssembly.Global({type: "i32", value: 3.14})).value, 3);
assertEq((new WebAssembly.Global({type: "f32", value: { valueOf: () => 33.5 }})).value, 33.5);
// Misc internal conversions
let g = new WebAssembly.Global({type: "i32", value: 42});
// @@toPrimitive
assertEq(g - 5, 37);
assertEq(String(g), "42");
// @@toStringTag
assertEq(g.toString(), "[object WebAssembly.Global]");
// An exported global should appear as a WebAssembly.Global instance:
let i =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module (global (export "g") i32 (i32.const 42)))`)));
assertEq(typeof i.exports.g, "object");
assertEq(i.exports.g instanceof WebAssembly.Global, true);
// An exported global can be imported into another instance even if
// it is an object:
let j =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(global (import "" "g") i32)
(func (export "f") (result i32)
(get_global 0)))`)),
{ "": { "g": i.exports.g }});
// And when it is then accessed it has the right value:
assertEq(j.exports.f(), 42);
}

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

@ -222,6 +222,15 @@ function get(instance, name) {
if (instance.isError())
return instance;
// Experimental API change. We try to export WebAssembly.Global instances,
// not primitive values. In that case the Number() cast is necessary here
// to convert the Global to a value: the harness examines types carefully
// and will not trigger the @@toPrimitive hook on Global, unlike most user
// code.
if (typeof WebAssembly.Global === "function")
return ValueResult(Number(instance.value.exports[name]));
return ValueResult(instance.value.exports[name]);
}

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

@ -1415,6 +1415,25 @@ class MacroAssembler : public MacroAssemblerSpecific
FloatRegister floatTemp)
DEFINED_ON(x86, x64);
public:
// ========================================================================
// Convert floating point.
// temp required on x86 and x64; must be undefined on mips64.
void convertUInt64ToFloat32(Register64 src, FloatRegister dest, Register temp)
DEFINED_ON(arm64, mips64, x64, x86);
void convertInt64ToFloat32(Register64 src, FloatRegister dest)
DEFINED_ON(arm64, mips64, x64, x86);
bool convertUInt64ToDoubleNeedsTemp() PER_ARCH;
// temp required when convertUInt64ToDoubleNeedsTemp() returns true.
void convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp) PER_ARCH;
void convertInt64ToDouble(Register64 src, FloatRegister dest)
DEFINED_ON(arm64, mips64, x64, x86);
public:
// ========================================================================
// wasm support
@ -1506,9 +1525,22 @@ class MacroAssembler : public MacroAssemblerSpecific
wasm::BytecodeOffset off, Label* rejoin)
DEFINED_ON(x86_shared);
void wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble)
DEFINED_ON(arm64, x86, x64);
void wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble)
DEFINED_ON(arm64, x86, x64);
void outOfLineWasmTruncateDoubleToInt64(FloatRegister input, bool isUnsigned,
wasm::BytecodeOffset off, Label* rejoin)
DEFINED_ON(x86_shared);
void wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble)
DEFINED_ON(arm64, x86, x64);
void wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble)
DEFINED_ON(arm64, x86, x64);
void outOfLineWasmTruncateFloat32ToInt64(FloatRegister input, bool isUnsigned,
wasm::BytecodeOffset off, Label* rejoin)
DEFINED_ON(x86_shared);

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

@ -10,9 +10,6 @@
#include "mozilla/MathAlgorithms.h"
#include "jscompartment.h"
#ifdef JS_DISASM_ARM
#include "jsprf.h"
#endif
#include "jsutil.h"
#include "gc/Marking.h"
@ -27,6 +24,9 @@ using namespace js::jit;
using mozilla::CountLeadingZeroes32;
using LabelDoc = DisassemblerSpew::LabelDoc;
using LiteralDoc = DisassemblerSpew::LiteralDoc;
void dbg_break() {}
// The ABIArgGenerator is used for making system ABI calls and for inter-wasm
@ -1440,241 +1440,6 @@ Assembler::bytesNeeded() const
dataRelocationTableBytes();
}
#ifdef JS_DISASM_ARM
void
Assembler::spewInst(Instruction* i)
{
disasm::NameConverter converter;
disasm::Disassembler dasm(converter);
disasm::EmbeddedVector<char, disasm::ReasonableBufferSize> buffer;
uint8_t* loc = reinterpret_cast<uint8_t*>(const_cast<uint32_t*>(i->raw()));
dasm.InstructionDecode(buffer, loc);
printf(" %08x %s\n", reinterpret_cast<uint32_t>(loc), buffer.start());
}
// Labels are named as they are encountered by adding names to a
// table, using the Label address as the key. This is made tricky by
// the (memory for) Label objects being reused, but reused label
// objects are recognizable from being marked as not used or not
// bound. See spewResolve().
//
// In a number of cases there is no information about the target, and
// we just end up printing "patchable constant load to PC". This is
// true especially for jumps to bailout handlers (which have no
// names). See spewData() and its callers. In some cases (loop back
// edges) some information about the intended target may be propagated
// from higher levels, and if so it's printed here.
void
Assembler::spew(Instruction* i)
{
if (spewDisabled() || !i)
return;
disasm::NameConverter converter;
disasm::Disassembler dasm(converter);
disasm::EmbeddedVector<char, disasm::ReasonableBufferSize> buffer;
uint8_t* loc = reinterpret_cast<uint8_t*>(const_cast<uint32_t*>(i->raw()));
dasm.InstructionDecode(buffer, loc);
spew(" %08x %s", reinterpret_cast<uint32_t>(loc), buffer.start());
}
void
Assembler::spewTarget(Label* target)
{
if (spewDisabled())
return;
spew(" -> %d%s", spewResolve(target), !target->bound() ? "f" : "");
}
// If a target label is known, always print that and do not attempt to
// disassemble the branch operands, as they will often be encoding
// metainformation (pointers for a chain of jump instructions), and
// not actual branch targets.
void
Assembler::spewBranch(Instruction* i, Label* target /* may be nullptr */)
{
if (spewDisabled() || !i)
return;
disasm::NameConverter converter;
disasm::Disassembler dasm(converter);
disasm::EmbeddedVector<char, disasm::ReasonableBufferSize> buffer;
uint8_t* loc = reinterpret_cast<uint8_t*>(const_cast<uint32_t*>(i->raw()));
dasm.InstructionDecode(buffer, loc);
char labelBuf[128];
labelBuf[0] = 0;
if (!target)
snprintf(labelBuf, sizeof(labelBuf), " -> (link-time target)");
if (InstBranchImm::IsTHIS(*i)) {
InstBranchImm* bimm = InstBranchImm::AsTHIS(*i);
BOffImm destOff;
bimm->extractImm(&destOff);
if (destOff.isInvalid() || target) {
// The target information in the instruction is likely garbage, so remove it.
// The target label will in any case be printed if we have it.
//
// The format of the instruction disassembly is [0-9a-f]{8}\s+\S+\s+.*,
// where the \S+ string is the opcode. Strip everything after the opcode,
// and attach the label if we have it.
int i;
for ( i=8 ; i < buffer.length() && buffer[i] == ' ' ; i++ )
;
for ( ; i < buffer.length() && buffer[i] != ' ' ; i++ )
;
buffer[i] = 0;
if (target) {
snprintf(labelBuf, sizeof(labelBuf), " -> %d%s", spewResolve(target),
!target->bound() ? "f" : "");
target = nullptr;
}
}
}
spew(" %08x %s%s", reinterpret_cast<uint32_t>(loc), buffer.start(), labelBuf);
if (target)
spewTarget(target);
}
void
Assembler::spewLabel(Label* l)
{
if (spewDisabled())
return;
spew(" %d:", spewResolve(l));
}
void
Assembler::spewRetarget(Label* label, Label* target)
{
if (spewDisabled())
return;
spew(" %d: .retarget -> %d%s",
spewResolve(label), spewResolve(target), !target->bound() ? "f" : "");
}
void
Assembler::spewData(BufferOffset addr, size_t numInstr, bool loadToPC)
{
if (spewDisabled())
return;
Instruction* inst = m_buffer.getInstOrNull(addr);
if (!inst)
return;
uint32_t *instr = reinterpret_cast<uint32_t*>(inst);
for ( size_t k=0 ; k < numInstr ; k++ ) {
spew(" %08x %08x (patchable constant load%s)",
reinterpret_cast<uint32_t>(instr+k), *(instr+k), loadToPC ? " to PC" : "");
}
}
bool
Assembler::spewDisabled()
{
return !(JitSpewEnabled(JitSpew_Codegen) || printer_);
}
void
Assembler::spew(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
spew(fmt, args);
va_end(args);
}
void
Assembler::spew(const char* fmt, va_list va)
{
if (printer_) {
printer_->vprintf(fmt, va);
printer_->put("\n");
}
js::jit::JitSpewVA(js::jit::JitSpew_Codegen, fmt, va);
}
uint32_t
Assembler::spewResolve(Label* l)
{
// Note, spewResolve will sometimes return 0 when it is triggered
// by the profiler and not by a full disassembly, since in that
// case a label can be used or bound but not previously have been
// defined.
return l->used() || l->bound() ? spewProbe(l) : spewDefine(l);
}
uint32_t
Assembler::spewProbe(Label* l)
{
uint32_t key = reinterpret_cast<uint32_t>(l);
uint32_t value = 0;
spewNodes_.lookup(key, &value);
return value;
}
uint32_t
Assembler::spewDefine(Label* l)
{
uint32_t key = reinterpret_cast<uint32_t>(l);
spewNodes_.remove(key);
uint32_t value = spewNext_++;
if (!spewNodes_.add(key, value))
return 0;
return value;
}
Assembler::SpewNodes::~SpewNodes()
{
Node* p = nodes;
while (p) {
Node* victim = p;
p = p->next;
js_free(victim);
}
}
bool
Assembler::SpewNodes::lookup(uint32_t key, uint32_t* value)
{
for ( Node* p = nodes ; p ; p = p->next ) {
if (p->key == key) {
*value = p->value;
return true;
}
}
return false;
}
bool
Assembler::SpewNodes::add(uint32_t key, uint32_t value)
{
Node* node = (Node*)js_malloc(sizeof(Node));
if (!node)
return false;
node->key = key;
node->value = value;
node->next = nodes;
nodes = node;
return true;
}
bool
Assembler::SpewNodes::remove(uint32_t key)
{
for ( Node* p = nodes, *pp = nullptr ; p ; pp = p, p = p->next ) {
if (p->key == key) {
if (pp)
pp->next = p->next;
else
nodes = p->next;
js_free(p);
return true;
}
}
return false;
}
#endif // JS_DISASM_ARM
// Allocate memory for a branch instruction, it will be overwritten
// subsequently and should not be disassembled.
@ -2136,14 +1901,22 @@ Assembler::as_dtm(LoadStore ls, Register rn, uint32_t mask,
}
BufferOffset
Assembler::allocEntry(size_t numInst, unsigned numPoolEntries,
uint8_t* inst, uint8_t* data, ARMBuffer::PoolEntry* pe,
bool loadToPC)
Assembler::allocLiteralLoadEntry(size_t numInst, unsigned numPoolEntries,
PoolHintPun& php, uint8_t* data,
const LiteralDoc& doc,
ARMBuffer::PoolEntry* pe, bool loadToPC)
{
uint8_t* inst = (uint8_t*)&php.raw;
MOZ_ASSERT(inst);
MOZ_ASSERT(numInst == 1); // Or fix the disassembly
BufferOffset offs = m_buffer.allocEntry(numInst, numPoolEntries, inst, data, pe);
propagateOOM(offs.assigned());
#ifdef JS_DISASM_ARM
spewData(offs, numInst, loadToPC);
Instruction* instruction = m_buffer.getInstOrNull(offs);
if (instruction)
spewLiteralLoad(php, loadToPC, instruction, doc);
#endif
return offs;
}
@ -2156,7 +1929,11 @@ Assembler::as_Imm32Pool(Register dest, uint32_t value, Condition c)
{
PoolHintPun php;
php.phd.init(0, c, PoolHintData::PoolDTR, dest);
BufferOffset offs = allocEntry(1, 1, (uint8_t*)&php.raw, (uint8_t*)&value, nullptr, dest == pc);
BufferOffset offs = allocLiteralLoadEntry(1, 1, php,
(uint8_t*)&value,
LiteralDoc(value),
nullptr,
dest == pc);
return offs;
}
@ -2169,12 +1946,15 @@ Assembler::WritePoolEntry(Instruction* addr, Condition c, uint32_t data)
}
BufferOffset
Assembler::as_BranchPool(uint32_t value, RepatchLabel* label, ARMBuffer::PoolEntry* pe, Condition c,
Label* documentation)
Assembler::as_BranchPool(uint32_t value, RepatchLabel* label,
const LabelDoc& documentation,
ARMBuffer::PoolEntry* pe, Condition c)
{
PoolHintPun php;
php.phd.init(0, c, PoolHintData::PoolBranch, pc);
BufferOffset ret = allocEntry(1, 1, (uint8_t*)&php.raw, (uint8_t*)&value, pe,
BufferOffset ret = allocLiteralLoadEntry(1, 1, php, (uint8_t*)&value,
LiteralDoc(),
pe,
/* loadToPC = */ true);
// If this label is already bound, then immediately replace the stub load
// with a correct branch.
@ -2190,8 +1970,7 @@ Assembler::as_BranchPool(uint32_t value, RepatchLabel* label, ARMBuffer::PoolEnt
label->use(ret.getOffset());
}
#ifdef JS_DISASM_ARM
if (documentation)
spewTarget(documentation);
spew_.spewRef(documentation);
#endif
return ret;
}
@ -2202,7 +1981,7 @@ Assembler::as_FImm64Pool(VFPRegister dest, double d, Condition c)
MOZ_ASSERT(dest.isDouble());
PoolHintPun php;
php.phd.init(0, c, PoolHintData::PoolVDTR, dest);
return allocEntry(1, 2, (uint8_t*)&php.raw, (uint8_t*)&d);
return allocLiteralLoadEntry(1, 2, php, (uint8_t*)&d, LiteralDoc(d));
}
BufferOffset
@ -2214,7 +1993,7 @@ Assembler::as_FImm32Pool(VFPRegister dest, float f, Condition c)
MOZ_ASSERT(dest.isSingle());
PoolHintPun php;
php.phd.init(0, c, PoolHintData::PoolVDTR, dest);
return allocEntry(1, 1, (uint8_t*)&php.raw, (uint8_t*)&f);
return allocLiteralLoadEntry(1, 1, php, (uint8_t*)&f, LiteralDoc(f));
}
// Pool callbacks stuff:
@ -2407,8 +2186,7 @@ Assembler::WritePoolGuard(BufferOffset branch, Instruction* dest, BufferOffset a
BufferOffset
Assembler::as_b(BOffImm off, Condition c, Label* documentation)
{
BufferOffset ret = writeBranchInst(((int)c) | OpB | off.encode(), documentation);
return ret;
return writeBranchInst(((int)c) | OpB | off.encode(), refLabel(documentation));
}
BufferOffset
@ -2422,7 +2200,7 @@ Assembler::as_b(Label* l, Condition c)
as_b(BufferOffset(l).diffB<BOffImm>(ret), c, ret);
#ifdef JS_DISASM_ARM
spewBranch(m_buffer.getInstOrNull(ret), l);
spewBranch(m_buffer.getInstOrNull(ret), refLabel(l));
#endif
return ret;
}
@ -2486,7 +2264,7 @@ Assembler::as_blx(Register r, Condition c)
BufferOffset
Assembler::as_bl(BOffImm off, Condition c, Label* documentation)
{
return writeBranchInst(((int)c) | OpBl | off.encode(), documentation);
return writeBranchInst(((int)c) | OpBl | off.encode(), refLabel(documentation));
}
BufferOffset
@ -2506,7 +2284,7 @@ Assembler::as_bl(Label* l, Condition c)
as_bl(offset, c, ret);
#ifdef JS_DISASM_ARM
spewBranch(m_buffer.getInstOrNull(ret), l);
spewBranch(m_buffer.getInstOrNull(ret), refLabel(l));
#endif
return ret;
}
@ -2855,7 +2633,7 @@ void
Assembler::bind(Label* label, BufferOffset boff)
{
#ifdef JS_DISASM_ARM
spewLabel(label);
spew_.spewBind(label);
#endif
if (oom()) {
// Ensure we always bind the label. This matches what we do on
@ -2941,7 +2719,7 @@ void
Assembler::retarget(Label* label, Label* target)
{
#ifdef JS_DISASM_ARM
spewRetarget(label, target);
spew_.spewRetarget(label, target);
#endif
if (label->used() && !oom()) {
if (target->bound()) {
@ -3491,3 +3269,166 @@ SecondScratchRegisterScope::SecondScratchRegisterScope(MacroAssembler &masm)
: AutoRegisterScope(masm, masm.getSecondScratchReg())
{
}
#ifdef JS_DISASM_ARM
/* static */ void
Assembler::disassembleInstruction(const Instruction* i, DisasmBuffer& buffer)
{
disasm::NameConverter converter;
disasm::Disassembler dasm(converter);
uint8_t* loc = reinterpret_cast<uint8_t*>(const_cast<uint32_t*>(i->raw()));
dasm.InstructionDecode(buffer, loc);
}
void
Assembler::initDisassembler()
{
// The line is normally laid out like this:
//
// xxxxxxxx ldr r, op ; comment
//
// where xx...x is the instruction bit pattern.
//
// Labels are laid out by themselves to line up with the instructions above
// and below:
//
// nnnn:
//
// Branch targets are normally on the same line as the branch instruction,
// but when they cannot be they will be on a line by themselves, indented
// significantly:
//
// -> label
spew_.setLabelIndent(" "); // 10
spew_.setTargetIndent(" "); // 20
}
void
Assembler::finishDisassembler()
{
spew_.spewOrphans();
}
// Labels are named as they are encountered by adding names to a
// table, using the Label address as the key. This is made tricky by
// the (memory for) Label objects being reused, but reused label
// objects are recognizable from being marked as not used or not
// bound. See spew_.refLabel().
//
// In a number of cases there is no information about the target, and
// we just end up printing "patchable constant load to PC". This is
// true especially for jumps to bailout handlers (which have no
// names). See allocLiteralLoadEntry() and its callers. In some cases
// (loop back edges) some information about the intended target may be
// propagated from higher levels, and if so it's printed here.
void
Assembler::spew(Instruction* i)
{
if (spew_.isDisabled() || !i)
return;
DisasmBuffer buffer;
disassembleInstruction(i, buffer);
spew_.spew("%s", buffer.start());
}
// If a target label is known, always print that and do not attempt to
// disassemble the branch operands, as they will often be encoding
// metainformation (pointers for a chain of jump instructions), and
// not actual branch targets.
void
Assembler::spewBranch(Instruction* i, const LabelDoc& target)
{
if (spew_.isDisabled() || !i)
return;
DisasmBuffer buffer;
disassembleInstruction(i, buffer);
char labelBuf[128];
labelBuf[0] = 0;
bool haveTarget = target.valid;
if (!haveTarget)
snprintf(labelBuf, sizeof(labelBuf), " -> (link-time target)");
if (InstBranchImm::IsTHIS(*i)) {
InstBranchImm* bimm = InstBranchImm::AsTHIS(*i);
BOffImm destOff;
bimm->extractImm(&destOff);
if (destOff.isInvalid() || haveTarget) {
// The target information in the instruction is likely garbage, so remove it.
// The target label will in any case be printed if we have it.
//
// The format of the instruction disassembly is [0-9a-f]{8}\s+\S+\s+.*,
// where the \S+ string is the opcode. Strip everything after the opcode,
// and attach the label if we have it.
int i;
for ( i=8 ; i < buffer.length() && buffer[i] == ' ' ; i++ )
;
for ( ; i < buffer.length() && buffer[i] != ' ' ; i++ )
;
buffer[i] = 0;
if (haveTarget) {
snprintf(labelBuf, sizeof(labelBuf), " -> %d%s", target.doc,
!target.bound ? "f" : "");
haveTarget = false;
}
}
}
spew_.spew("%s%s", buffer.start(), labelBuf);
if (haveTarget)
spew_.spewRef(target);
}
void
Assembler::spewLiteralLoad(PoolHintPun& php, bool loadToPC, const Instruction* i,
const LiteralDoc& doc)
{
if (spew_.isDisabled())
return;
char litbuf[2048];
spew_.formatLiteral(doc, litbuf, sizeof(litbuf));
// See patchConstantPoolLoad, above. We assemble the instruction into a
// buffer with a zero offset, as documentation, but the offset will be
// patched later.
uint32_t inst;
PoolHintData& data = php.phd;
switch (php.phd.getLoadType()) {
case PoolHintData::PoolDTR:
Assembler::as_dtr_patch(IsLoad, 32, Offset, data.getReg(),
DTRAddr(pc, DtrOffImm(0)),
data.getCond(), &inst);
break;
case PoolHintData::PoolBranch:
if (data.isValidPoolHint()) {
Assembler::as_dtr_patch(IsLoad, 32, Offset, pc,
DTRAddr(pc, DtrOffImm(0)),
data.getCond(), &inst);
}
break;
case PoolHintData::PoolVDTR:
Assembler::as_vdtr_patch(IsLoad, data.getVFPReg(), VFPAddr(pc, VFPOffImm(0)),
data.getCond(), &inst);
break;
default:
MOZ_CRASH();
}
DisasmBuffer buffer;
disasm::NameConverter converter;
disasm::Disassembler dasm(converter);
dasm.InstructionDecode(buffer, reinterpret_cast<uint8_t*>(&inst));
spew_.spew("%s ; .const %s", buffer.start(), litbuf);
}
#endif // JS_DISASM_ARM

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

@ -12,15 +12,22 @@
#include "mozilla/MathAlgorithms.h"
#include "jit/arm/Architecture-arm.h"
#include "jit/arm/disasm/Disasm-arm.h"
#include "jit/CompactBuffer.h"
#include "jit/IonCode.h"
#include "jit/JitCompartment.h"
#include "jit/shared/Assembler-shared.h"
#include "jit/shared/Disassembler-shared.h"
#include "jit/shared/IonAssemblerBufferWithConstantPools.h"
union PoolHintPun;
namespace js {
namespace jit {
using LiteralDoc = DisassemblerSpew::LiteralDoc;
using LabelDoc = DisassemblerSpew::LabelDoc;
// NOTE: there are duplicates in this list! Sometimes we want to specifically
// refer to the link register as a link register (bl lr is much clearer than bl
// r14). HOWEVER, this register can easily be a gpr when it is not busy holding
@ -1243,8 +1250,10 @@ class Assembler : public AssemblerShared
protected:
// Shim around AssemblerBufferWithConstantPools::allocEntry.
BufferOffset allocEntry(size_t numInst, unsigned numPoolEntries,
uint8_t* inst, uint8_t* data, ARMBuffer::PoolEntry* pe = nullptr,
BufferOffset allocLiteralLoadEntry(size_t numInst, unsigned numPoolEntries,
PoolHintPun& php, uint8_t* data,
const LiteralDoc& doc = LiteralDoc(),
ARMBuffer::PoolEntry* pe = nullptr,
bool loadToPC = false);
Instruction* editSrc(BufferOffset bo) {
@ -1252,13 +1261,16 @@ class Assembler : public AssemblerShared
}
#ifdef JS_DISASM_ARM
static void spewInst(Instruction* i);
typedef disasm::EmbeddedVector<char, disasm::ReasonableBufferSize> DisasmBuffer;
static void disassembleInstruction(const Instruction* i, DisasmBuffer& buffer);
void initDisassembler();
void finishDisassembler();
void spew(Instruction* i);
void spewBranch(Instruction* i, Label* target);
void spewData(BufferOffset addr, size_t numInstr, bool loadToPC);
void spewLabel(Label* label);
void spewRetarget(Label* label, Label* target);
void spewTarget(Label* l);
void spewBranch(Instruction* i, const LabelDoc& target);
void spewLiteralLoad(PoolHintPun& php, bool loadToPC, const Instruction* offs,
const LiteralDoc& doc);
#endif
public:
@ -1296,35 +1308,7 @@ class Assembler : public AssemblerShared
ARMBuffer m_buffer;
#ifdef JS_DISASM_ARM
private:
class SpewNodes {
struct Node {
uint32_t key;
uint32_t value;
Node* next;
};
Node* nodes;
public:
SpewNodes() : nodes(nullptr) {}
~SpewNodes();
bool lookup(uint32_t key, uint32_t* value);
bool add(uint32_t key, uint32_t value);
bool remove(uint32_t key);
};
SpewNodes spewNodes_;
uint32_t spewNext_;
Sprinter* printer_;
bool spewDisabled();
uint32_t spewResolve(Label* l);
uint32_t spewProbe(Label* l);
uint32_t spewDefine(Label* l);
void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
void spew(const char* fmt, va_list args) MOZ_FORMAT_PRINTF(2, 0);
DisassemblerSpew spew_;
#endif
public:
@ -1332,14 +1316,20 @@ class Assembler : public AssemblerShared
// For the nopFill use a branch to the next instruction: 0xeaffffff.
Assembler()
: m_buffer(1, 1, 8, GetPoolMaxOffset(), 8, 0xe320f000, 0xeaffffff, GetNopFill()),
#ifdef JS_DISASM_ARM
spewNext_(1000),
printer_(nullptr),
#endif
isFinished(false),
dtmActive(false),
dtmCond(Always)
{ }
{
#ifdef JS_DISASM_ARM
initDisassembler();
#endif
}
~Assembler() {
#ifdef JS_DISASM_ARM
finishDisassembler();
#endif
}
// We need to wait until an AutoJitContextAlloc is created by the
// MacroAssembler, before allocating any space.
@ -1397,7 +1387,7 @@ class Assembler : public AssemblerShared
void setPrinter(Sprinter* sp) {
#ifdef JS_DISASM_ARM
printer_ = sp;
spew_.setPrinter(sp);
#endif
}
@ -1407,6 +1397,16 @@ class Assembler : public AssemblerShared
private:
bool isFinished;
protected:
LabelDoc refLabel(const Label* label) {
#ifdef JS_DISASM_ARM
return spew_.refLabel(label);
#else
return LabelDoc();
#endif
}
public:
void finish();
bool appendRawCode(const uint8_t* code, size_t numBytes);
@ -1436,7 +1436,8 @@ class Assembler : public AssemblerShared
// As above, but also mark the instruction as a branch. Very hot, inlined
// for performance
MOZ_ALWAYS_INLINE BufferOffset writeBranchInst(uint32_t x, Label* documentation = nullptr) {
MOZ_ALWAYS_INLINE BufferOffset
writeBranchInst(uint32_t x, const LabelDoc& documentation) {
BufferOffset offs = m_buffer.putInt(x);
#ifdef JS_DISASM_ARM
spewBranch(m_buffer.getInstOrNull(offs), documentation);
@ -1559,8 +1560,8 @@ class Assembler : public AssemblerShared
BufferOffset as_Imm32Pool(Register dest, uint32_t value, Condition c = Always);
// Make a patchable jump that can target the entire 32 bit address space.
BufferOffset as_BranchPool(uint32_t value, RepatchLabel* label,
ARMBuffer::PoolEntry* pe = nullptr, Condition c = Always,
Label* documentation = nullptr);
const LabelDoc& documentation,
ARMBuffer::PoolEntry* pe = nullptr, Condition c = Always);
// Load a 64 bit floating point immediate from a pool into a register.
BufferOffset as_FImm64Pool(VFPRegister dest, double value, Condition c = Always);
@ -1769,7 +1770,7 @@ class Assembler : public AssemblerShared
void comment(const char* msg) {
#ifdef JS_DISASM_ARM
spew("; %s", msg);
spew_.spew("; %s", msg);
#endif
}

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

@ -96,29 +96,6 @@ MacroAssemblerARM::convertUInt32ToDouble(Register src, FloatRegister dest_)
static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
bool
MacroAssemblerARMCompat::convertUInt64ToDoubleNeedsTemp()
{
return false;
}
void
MacroAssemblerARMCompat::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
{
MOZ_ASSERT(temp == Register::Invalid());
ScratchDoubleScope scratchDouble(asMasm());
convertUInt32ToDouble(src.high, dest);
{
ScratchRegisterScope scratch(asMasm());
movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), scratch);
ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble);
}
asMasm().mulDouble(scratchDouble, dest);
convertUInt32ToDouble(src.low, scratchDouble);
asMasm().addDouble(scratchDouble, dest);
}
void
MacroAssemblerARM::convertUInt32ToFloat32(Register src, FloatRegister dest_)
{
@ -4152,7 +4129,7 @@ CodeOffsetJump
MacroAssemblerARMCompat::jumpWithPatch(RepatchLabel* label, Condition cond, Label* documentation)
{
ARMBuffer::PoolEntry pe;
BufferOffset bo = as_BranchPool(0xdeadbeef, label, &pe, cond, documentation);
BufferOffset bo = as_BranchPool(0xdeadbeef, label, refLabel(documentation), &pe, cond);
// Fill in a new CodeOffset with both the load and the pool entry that the
// instruction loads from.
CodeOffsetJump ret(bo.getOffset(), pe.index());
@ -5620,6 +5597,31 @@ MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, Regist
AtomicFetchOp64(*this, sync, op, value, mem, temp, output);
}
// ========================================================================
// Convert floating point.
bool
MacroAssembler::convertUInt64ToDoubleNeedsTemp()
{
return false;
}
void
MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
{
MOZ_ASSERT(temp == Register::Invalid());
ScratchDoubleScope scratchDouble(*this);
convertUInt32ToDouble(src.high, dest);
{
ScratchRegisterScope scratch(*this);
movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), scratch);
ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble);
}
mulDouble(scratchDouble, dest);
convertUInt32ToDouble(src.low, scratchDouble);
addDouble(scratchDouble, dest);
}
//}}} check_macroassembler_style

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

@ -1161,9 +1161,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
void cmpPtr(const Address& lhs, ImmGCPtr rhs);
void cmpPtr(const Address& lhs, Imm32 rhs);
static bool convertUInt64ToDoubleNeedsTemp();
void convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp);
void setStackArg(Register reg, uint32_t arg);
void breakpoint();

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

@ -65,6 +65,33 @@ __aeabi_uidivmod(int x, int y)
namespace js {
namespace jit {
// For decoding load-exclusive and store-exclusive instructions.
namespace excl {
// Bit positions.
enum {
ExclusiveOpHi = 24, // Hi bit of opcode field
ExclusiveOpLo = 23, // Lo bit of opcode field
ExclusiveSizeHi = 22, // Hi bit of operand size field
ExclusiveSizeLo = 21, // Lo bit of operand size field
ExclusiveLoad = 20 // Bit indicating load
};
// Opcode bits for exclusive instructions.
enum {
ExclusiveOpcode = 3
};
// Operand size, Bits(ExclusiveSizeHi,ExclusiveSizeLo).
enum {
ExclusiveWord = 0,
ExclusiveDouble = 1,
ExclusiveByte = 2,
ExclusiveHalf = 3
};
}
// Load/store multiple addressing mode.
enum BlockAddrMode {
// Alias modes for comparison when writeback does not matter.
@ -413,6 +440,7 @@ Simulator::Destroy(Simulator* sim)
void
Simulator::disassemble(SimInstruction* instr, size_t n)
{
#ifdef JS_DISASM_ARM
disasm::NameConverter converter;
disasm::Disassembler dasm(converter);
disasm::EmbeddedVector<char, disasm::ReasonableBufferSize> buffer;
@ -422,6 +450,7 @@ Simulator::disassemble(SimInstruction* instr, size_t n)
fprintf(stderr, " 0x%08x %s\n", uint32_t(instr), buffer.start());
instr = reinterpret_cast<SimInstruction*>(reinterpret_cast<uint8_t*>(instr) + 4);
}
#endif
}
void
@ -3104,17 +3133,17 @@ Simulator::decodeType01(SimInstruction* instr)
MOZ_CRASH();
}
} else {
if (instr->bits(disasm::ExclusiveOpHi, disasm::ExclusiveOpLo) == disasm::ExclusiveOpcode) {
if (instr->bits(excl::ExclusiveOpHi, excl::ExclusiveOpLo) == excl::ExclusiveOpcode) {
// Load-exclusive / store-exclusive.
if (instr->bit(disasm::ExclusiveLoad)) {
if (instr->bit(excl::ExclusiveLoad)) {
int rn = instr->rnValue();
int rt = instr->rtValue();
int32_t address = get_register(rn);
switch (instr->bits(disasm::ExclusiveSizeHi, disasm::ExclusiveSizeLo)) {
case disasm::ExclusiveWord:
switch (instr->bits(excl::ExclusiveSizeHi, excl::ExclusiveSizeLo)) {
case excl::ExclusiveWord:
set_register(rt, readExW(address, instr));
break;
case disasm::ExclusiveDouble: {
case excl::ExclusiveDouble: {
MOZ_ASSERT((rt % 2) == 0);
int32_t hibits;
int32_t lobits = readExDW(address, &hibits);
@ -3122,10 +3151,10 @@ Simulator::decodeType01(SimInstruction* instr)
set_register(rt+1, hibits);
break;
}
case disasm::ExclusiveByte:
case excl::ExclusiveByte:
set_register(rt, readExBU(address));
break;
case disasm::ExclusiveHalf:
case excl::ExclusiveHalf:
set_register(rt, readExHU(address, instr));
break;
}
@ -3136,20 +3165,20 @@ Simulator::decodeType01(SimInstruction* instr)
int32_t address = get_register(rn);
int32_t value = get_register(rt);
int32_t result = 0;
switch (instr->bits(disasm::ExclusiveSizeHi, disasm::ExclusiveSizeLo)) {
case disasm::ExclusiveWord:
switch (instr->bits(excl::ExclusiveSizeHi, excl::ExclusiveSizeLo)) {
case excl::ExclusiveWord:
result = writeExW(address, value, instr);
break;
case disasm::ExclusiveDouble: {
case excl::ExclusiveDouble: {
MOZ_ASSERT((rt % 2) == 0);
int32_t value2 = get_register(rt+1);
result = writeExDW(address, value, value2);
break;
}
case disasm::ExclusiveByte:
case excl::ExclusiveByte:
result = writeExB(address, (uint8_t)value);
break;
case disasm::ExclusiveHalf:
case excl::ExclusiveHalf:
result = writeExH(address, (uint16_t)value, instr);
break;
}

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

@ -16,6 +16,7 @@
#include "jit/arm64/Architecture-arm64.h"
#include "jit/arm64/MacroAssembler-arm64.h"
#include "jit/arm64/vixl/Disasm-vixl.h"
#include "jit/ExecutableAllocator.h"
#include "jit/JitCompartment.h"
@ -167,19 +168,20 @@ Assembler::executableCopy(uint8_t* buffer, bool flushICache)
}
BufferOffset
Assembler::immPool(ARMRegister dest, uint8_t* value, vixl::LoadLiteralOp op, ARMBuffer::PoolEntry* pe)
Assembler::immPool(ARMRegister dest, uint8_t* value, vixl::LoadLiteralOp op,
const LiteralDoc& doc, ARMBuffer::PoolEntry* pe)
{
uint32_t inst = op | Rt(dest);
const size_t numInst = 1;
const unsigned sizeOfPoolEntryInBytes = 4;
const unsigned numPoolEntries = sizeof(value) / sizeOfPoolEntryInBytes;
return allocEntry(numInst, numPoolEntries, (uint8_t*)&inst, value, pe);
return allocLiteralLoadEntry(numInst, numPoolEntries, (uint8_t*)&inst, value, doc, pe);
}
BufferOffset
Assembler::immPool64(ARMRegister dest, uint64_t value, ARMBuffer::PoolEntry* pe)
{
return immPool(dest, (uint8_t*)&value, vixl::LDR_x_lit, pe);
return immPool(dest, (uint8_t*)&value, vixl::LDR_x_lit, LiteralDoc(value), pe);
}
BufferOffset
@ -189,29 +191,34 @@ Assembler::immPool64Branch(RepatchLabel* label, ARMBuffer::PoolEntry* pe, Condit
}
BufferOffset
Assembler::fImmPool(ARMFPRegister dest, uint8_t* value, vixl::LoadLiteralOp op)
Assembler::fImmPool(ARMFPRegister dest, uint8_t* value, vixl::LoadLiteralOp op,
const LiteralDoc& doc)
{
uint32_t inst = op | Rt(dest);
const size_t numInst = 1;
const unsigned sizeOfPoolEntryInBits = 32;
const unsigned numPoolEntries = dest.size() / sizeOfPoolEntryInBits;
return allocEntry(numInst, numPoolEntries, (uint8_t*)&inst, value);
return allocLiteralLoadEntry(numInst, numPoolEntries, (uint8_t*)&inst, value, doc);
}
BufferOffset
Assembler::fImmPool64(ARMFPRegister dest, double value)
{
return fImmPool(dest, (uint8_t*)&value, vixl::LDR_d_lit);
return fImmPool(dest, (uint8_t*)&value, vixl::LDR_d_lit, LiteralDoc(value));
}
BufferOffset
Assembler::fImmPool32(ARMFPRegister dest, float value)
{
return fImmPool(dest, (uint8_t*)&value, vixl::LDR_s_lit);
return fImmPool(dest, (uint8_t*)&value, vixl::LDR_s_lit, LiteralDoc(value));
}
void
Assembler::bind(Label* label, BufferOffset targetOffset)
{
#ifdef JS_DISASM_ARM64
spew_.spewBind(label);
#endif
// Nothing has seen the label yet: just mark the location.
// If we've run out of memory, don't attempt to modify the buffer which may
// not be there. Just mark the label as bound to the (possibly bogus)
@ -634,6 +641,9 @@ Assembler::FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader
void
Assembler::retarget(Label* label, Label* target)
{
#ifdef JS_DISASM_ARM64
spew_.spewRetarget(label, target);
#endif
if (label->used()) {
if (target->bound()) {
bind(label, BufferOffset(target));

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

@ -10,6 +10,7 @@
#include "jit/arm64/vixl/Assembler-vixl.h"
#include "jit/JitCompartment.h"
#include "jit/shared/Disassembler-shared.h"
namespace js {
namespace jit {
@ -20,6 +21,9 @@ typedef vixl::FPRegister ARMFPRegister;
using vixl::ARMBuffer;
using vixl::Instruction;
using LabelDoc = DisassemblerSpew::LabelDoc;
using LiteralDoc = DisassemblerSpew::LiteralDoc;
static const uint32_t AlignmentAtPrologue = 0;
static const uint32_t AlignmentMidPrologue = 8;
static const Scale ScalePointer = TimesEight;
@ -205,10 +209,11 @@ class Assembler : public vixl::Assembler
void executableCopy(uint8_t* buffer, bool flushICache = true);
BufferOffset immPool(ARMRegister dest, uint8_t* value, vixl::LoadLiteralOp op,
ARMBuffer::PoolEntry* pe = nullptr);
const LiteralDoc& doc, ARMBuffer::PoolEntry* pe = nullptr);
BufferOffset immPool64(ARMRegister dest, uint64_t value, ARMBuffer::PoolEntry* pe = nullptr);
BufferOffset immPool64Branch(RepatchLabel* label, ARMBuffer::PoolEntry* pe, vixl::Condition c);
BufferOffset fImmPool(ARMFPRegister dest, uint8_t* value, vixl::LoadLiteralOp op);
BufferOffset fImmPool(ARMFPRegister dest, uint8_t* value, vixl::LoadLiteralOp op,
const LiteralDoc& doc);
BufferOffset fImmPool64(ARMFPRegister dest, double value);
BufferOffset fImmPool32(ARMFPRegister dest, float value);
@ -267,8 +272,9 @@ class Assembler : public vixl::Assembler
}
void comment(const char* msg) {
// This is not implemented because setPrinter() is not implemented.
// TODO spew("; %s", msg);
#ifdef JS_DISASM_ARM64
spew_.spew("; %s", msg);
#endif
}
int actualIndex(int curOffset) {
@ -278,7 +284,11 @@ class Assembler : public vixl::Assembler
static uint8_t* PatchableJumpAddress(JitCode* code, uint32_t index) {
return code->raw() + index;
}
void setPrinter(Sprinter* sp) {
#ifdef JS_DISASM_ARM64
spew_.setPrinter(sp);
#endif
}
static bool SupportsFloatingPoint() { return true; }

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

@ -81,7 +81,7 @@ MacroAssemblerCompat::movePatchablePtr(ImmPtr ptr, Register dest)
// Scratch space for generating the load instruction.
//
// allocEntry() will use InsertIndexIntoTag() to store a temporary
// allocLiteralLoadEntry() will use InsertIndexIntoTag() to store a temporary
// index to the corresponding PoolEntry in the instruction itself.
//
// That index will be fixed up later when finishPool()
@ -94,7 +94,7 @@ MacroAssemblerCompat::movePatchablePtr(ImmPtr ptr, Register dest)
// Add the entry to the pool, fix up the LDR imm19 offset,
// and add the completed instruction to the buffer.
return allocEntry(numInst, numPoolEntries, (uint8_t*)&instructionScratch,
return allocLiteralLoadEntry(numInst, numPoolEntries, (uint8_t*)&instructionScratch,
literalAddr);
}
@ -107,7 +107,7 @@ MacroAssemblerCompat::movePatchablePtr(ImmWord ptr, Register dest)
// Scratch space for generating the load instruction.
//
// allocEntry() will use InsertIndexIntoTag() to store a temporary
// allocLiteralLoadEntry() will use InsertIndexIntoTag() to store a temporary
// index to the corresponding PoolEntry in the instruction itself.
//
// That index will be fixed up later when finishPool()
@ -120,7 +120,7 @@ MacroAssemblerCompat::movePatchablePtr(ImmWord ptr, Register dest)
// Add the entry to the pool, fix up the LDR imm19 offset,
// and add the completed instruction to the buffer.
return allocEntry(numInst, numPoolEntries, (uint8_t*)&instructionScratch,
return allocLiteralLoadEntry(numInst, numPoolEntries, (uint8_t*)&instructionScratch,
literalAddr);
}
@ -882,6 +882,69 @@ MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, Register output,
MOZ_CRASH("NYI");
}
void
MacroAssembler::wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble)
{
MOZ_CRASH("NYI");
}
void
MacroAssembler::wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble)
{
MOZ_CRASH("NYI");
}
void
MacroAssembler::wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble)
{
MOZ_CRASH("NYI");
}
void
MacroAssembler::wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble)
{
MOZ_CRASH("NYI");
}
// ========================================================================
// Convert floating point.
bool
MacroAssembler::convertUInt64ToDoubleNeedsTemp()
{
return false;
}
void
MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
{
MOZ_ASSERT(temp == Register::Invalid());
Ucvtf(ARMFPRegister(dest, 64), ARMRegister(src.reg, 64));
}
void
MacroAssembler::convertInt64ToDouble(Register64 src, FloatRegister dest)
{
Scvtf(ARMFPRegister(dest, 64), ARMRegister(src.reg, 64));
}
void
MacroAssembler::convertUInt64ToFloat32(Register64 src, FloatRegister dest, Register temp)
{
MOZ_ASSERT(temp == Register::Invalid());
Ucvtf(ARMFPRegister(dest, 32), ARMRegister(src.reg, 64));
}
void
MacroAssembler::convertInt64ToFloat32(Register64 src, FloatRegister dest)
{
Scvtf(ARMFPRegister(dest, 32), ARMRegister(src.reg, 64));
}
// ========================================================================
// Primitive atomic operations.

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

@ -671,7 +671,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
}
void jump(TrampolinePtr code) {
syncStackPtr();
BufferOffset loc = b(-1); // The jump target will be patched by executableCopy().
BufferOffset loc = b(-1, LabelDoc()); // The jump target will be patched by executableCopy().
addPendingJump(loc, ImmPtr(code.value), Relocation::HARDCODED);
}
void jump(RepatchLabel* label) {
@ -1266,13 +1266,17 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
}
void branch(JitCode* target) {
syncStackPtr();
BufferOffset loc = b(-1); // The jump target will be patched by executableCopy().
BufferOffset loc = b(-1, LabelDoc()); // The jump target will be patched by executableCopy().
addPendingJump(loc, ImmPtr(target->raw()), Relocation::JITCODE);
}
CodeOffsetJump jumpWithPatch(RepatchLabel* label, Condition cond = Always,
Label* documentation = nullptr)
CodeOffsetJump jumpWithPatch(RepatchLabel* label, Condition cond = Always, Label* documentation = nullptr)
{
#ifdef JS_DISASM_ARM64
LabelDoc doc = spew_.refLabel(documentation);
#else
LabelDoc doc;
#endif
ARMBuffer::PoolEntry pe;
BufferOffset load_bo;
BufferOffset branch_bo;
@ -1288,16 +1292,16 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
if (cond != Always) {
Label notTaken;
B(&notTaken, Assembler::InvertCondition(cond));
branch_bo = b(-1);
branch_bo = b(-1, doc);
bind(&notTaken);
} else {
nop();
branch_bo = b(-1);
branch_bo = b(-1, doc);
}
label->use(branch_bo.getOffset());
return CodeOffsetJump(load_bo.getOffset(), pe.index());
}
CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation = nullptr) {
CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation) {
return jumpWithPatch(label, Always, documentation);
}
@ -1986,15 +1990,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
vixl::MacroAssembler::Ret(vixl::lr);
}
bool convertUInt64ToDoubleNeedsTemp() {
return false;
}
void convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp) {
MOZ_ASSERT(temp == Register::Invalid());
Ucvtf(ARMFPRegister(dest, 64), ARMRegister(src.reg, 64));
}
void clampCheck(Register r, Label* handleNotAnInt) {
MOZ_CRASH("clampCheck");
}

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

@ -35,6 +35,7 @@
#include "jit/JitSpewer.h"
#include "jit/shared/Assembler-shared.h"
#include "jit/shared/Disassembler-shared.h"
#include "jit/shared/IonAssemblerBufferWithConstantPools.h"
namespace vixl {
@ -43,6 +44,9 @@ using js::jit::BufferOffset;
using js::jit::Label;
using js::jit::Address;
using js::jit::BaseIndex;
using js::jit::DisassemblerSpew;
using LabelDoc = DisassemblerSpew::LabelDoc;
typedef uint64_t RegList;
static const int kRegListSizeInBits = sizeof(RegList) * 8;
@ -971,32 +975,32 @@ class Assembler : public MozBaseAssembler {
BufferOffset b(Label* label, Condition cond);
// Unconditional branch to PC offset.
BufferOffset b(int imm26);
BufferOffset b(int imm26, const LabelDoc& doc);
static void b(Instruction* at, int imm26);
// Conditional branch to PC offset.
BufferOffset b(int imm19, Condition cond);
BufferOffset b(int imm19, Condition cond, const LabelDoc& doc);
static void b(Instruction*at, int imm19, Condition cond);
// Branch with link to label.
void bl(Label* label);
// Branch with link to PC offset.
void bl(int imm26);
void bl(int imm26, const LabelDoc& doc);
static void bl(Instruction* at, int imm26);
// Compare and branch to label if zero.
void cbz(const Register& rt, Label* label);
// Compare and branch to PC offset if zero.
void cbz(const Register& rt, int imm19);
void cbz(const Register& rt, int imm19, const LabelDoc& doc);
static void cbz(Instruction* at, const Register& rt, int imm19);
// Compare and branch to label if not zero.
void cbnz(const Register& rt, Label* label);
// Compare and branch to PC offset if not zero.
void cbnz(const Register& rt, int imm19);
void cbnz(const Register& rt, int imm19, const LabelDoc& doc);
static void cbnz(Instruction* at, const Register& rt, int imm19);
// Table lookup from one register.
@ -1055,14 +1059,14 @@ class Assembler : public MozBaseAssembler {
void tbz(const Register& rt, unsigned bit_pos, Label* label);
// Test bit and branch to PC offset if zero.
void tbz(const Register& rt, unsigned bit_pos, int imm14);
void tbz(const Register& rt, unsigned bit_pos, int imm14, const LabelDoc& doc);
static void tbz(Instruction* at, const Register& rt, unsigned bit_pos, int imm14);
// Test bit and branch to label if not zero.
void tbnz(const Register& rt, unsigned bit_pos, Label* label);
// Test bit and branch to PC offset if not zero.
void tbnz(const Register& rt, unsigned bit_pos, int imm14);
void tbnz(const Register& rt, unsigned bit_pos, int imm14, const LabelDoc& doc);
static void tbnz(Instruction* at, const Register& rt, unsigned bit_pos, int imm14);
// Address calculation instructions.
@ -1073,14 +1077,14 @@ class Assembler : public MozBaseAssembler {
void adr(const Register& rd, Label* label);
// Calculate the address of a PC offset.
void adr(const Register& rd, int imm21);
void adr(const Register& rd, int imm21, const LabelDoc& doc);
static void adr(Instruction* at, const Register& rd, int imm21);
// Calculate the page address of a label.
void adrp(const Register& rd, Label* label);
// Calculate the page address of a PC offset.
void adrp(const Register& rd, int imm21);
void adrp(const Register& rd, int imm21, const LabelDoc& doc);
static void adrp(Instruction* at, const Register& rd, int imm21);
// Data Processing instructions.

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

@ -2751,10 +2751,14 @@ void Disassembler::Format(const Instruction* instr, const char* mnemonic,
const char* format) {
VIXL_ASSERT(mnemonic != NULL);
ResetOutput();
uint32_t pos = buffer_pos_;
Substitute(instr, mnemonic);
if (format != NULL) {
uint32_t spaces = buffer_pos_ - pos < 8 ? 8 - (buffer_pos_ - pos) : 1;
while (spaces--) {
VIXL_ASSERT(buffer_pos_ < buffer_size_);
buffer_[buffer_pos_++] = ' ';
}
Substitute(instr, format);
}
VIXL_ASSERT(buffer_pos_ < buffer_size_);
@ -3485,4 +3489,13 @@ void PrintDisassembler::ProcessOutput(const Instruction* instr) {
GetOutput());
}
void DisassembleInstruction(char* buffer, size_t bufsize, const Instruction* instr)
{
vixl::Disassembler disasm(buffer, bufsize-1);
vixl::Decoder decoder;
decoder.AppendVisitor(&disasm);
decoder.Decode(instr);
buffer[bufsize-1] = 0; // Just to be safe
}
} // namespace vixl

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

@ -172,6 +172,9 @@ class PrintDisassembler: public Disassembler {
private:
FILE *stream_;
};
void DisassembleInstruction(char* buffer, size_t bufsize, const Instruction* instr);
} // namespace vixl
#endif // VIXL_A64_DISASM_A64_H

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

@ -31,6 +31,7 @@
namespace vixl {
using LabelDoc = js::jit::DisassemblerSpew::LabelDoc;
// Assembler
void Assembler::FinalizeCode() {
@ -196,8 +197,8 @@ ptrdiff_t MozBaseAssembler::LinkAndGetPageOffsetTo(BufferOffset branch, Label* l
return LinkAndGetOffsetTo(branch, UncondBranchRangeType, kPageSizeLog2, label);
}
BufferOffset Assembler::b(int imm26) {
return EmitBranch(B | ImmUncondBranch(imm26));
BufferOffset Assembler::b(int imm26, const LabelDoc& doc) {
return EmitBranch(B | ImmUncondBranch(imm26), doc);
}
@ -206,8 +207,8 @@ void Assembler::b(Instruction* at, int imm26) {
}
BufferOffset Assembler::b(int imm19, Condition cond) {
return EmitBranch(B_cond | ImmCondBranch(imm19) | cond);
BufferOffset Assembler::b(int imm19, Condition cond, const LabelDoc& doc) {
return EmitBranch(B_cond | ImmCondBranch(imm19) | cond, doc);
}
@ -218,13 +219,15 @@ void Assembler::b(Instruction* at, int imm19, Condition cond) {
BufferOffset Assembler::b(Label* label) {
// Encode the relative offset from the inserted branch to the label.
return b(LinkAndGetInstructionOffsetTo(nextInstrOffset(), UncondBranchRangeType, label));
LabelDoc doc = refLabel(label);
return b(LinkAndGetInstructionOffsetTo(nextInstrOffset(), UncondBranchRangeType, label), doc);
}
BufferOffset Assembler::b(Label* label, Condition cond) {
// Encode the relative offset from the inserted branch to the label.
return b(LinkAndGetInstructionOffsetTo(nextInstrOffset(), CondBranchRangeType, label), cond);
LabelDoc doc = refLabel(label);
return b(LinkAndGetInstructionOffsetTo(nextInstrOffset(), CondBranchRangeType, label), cond, doc);
}
void Assembler::br(Instruction* at, const Register& xn) {
@ -241,8 +244,8 @@ void Assembler::blr(Instruction* at, const Register& xn) {
}
void Assembler::bl(int imm26) {
EmitBranch(BL | ImmUncondBranch(imm26));
void Assembler::bl(int imm26, const LabelDoc& doc) {
EmitBranch(BL | ImmUncondBranch(imm26), doc);
}
@ -253,12 +256,13 @@ void Assembler::bl(Instruction* at, int imm26) {
void Assembler::bl(Label* label) {
// Encode the relative offset from the inserted branch to the label.
return bl(LinkAndGetInstructionOffsetTo(nextInstrOffset(), UncondBranchRangeType, label));
LabelDoc doc = refLabel(label);
return bl(LinkAndGetInstructionOffsetTo(nextInstrOffset(), UncondBranchRangeType, label), doc);
}
void Assembler::cbz(const Register& rt, int imm19) {
EmitBranch(SF(rt) | CBZ | ImmCmpBranch(imm19) | Rt(rt));
void Assembler::cbz(const Register& rt, int imm19, const LabelDoc& doc) {
EmitBranch(SF(rt) | CBZ | ImmCmpBranch(imm19) | Rt(rt), doc);
}
@ -269,12 +273,13 @@ void Assembler::cbz(Instruction* at, const Register& rt, int imm19) {
void Assembler::cbz(const Register& rt, Label* label) {
// Encode the relative offset from the inserted branch to the label.
return cbz(rt, LinkAndGetInstructionOffsetTo(nextInstrOffset(), CondBranchRangeType, label));
LabelDoc doc = refLabel(label);
return cbz(rt, LinkAndGetInstructionOffsetTo(nextInstrOffset(), CondBranchRangeType, label), doc);
}
void Assembler::cbnz(const Register& rt, int imm19) {
EmitBranch(SF(rt) | CBNZ | ImmCmpBranch(imm19) | Rt(rt));
void Assembler::cbnz(const Register& rt, int imm19, const LabelDoc& doc) {
EmitBranch(SF(rt) | CBNZ | ImmCmpBranch(imm19) | Rt(rt), doc);
}
@ -285,13 +290,14 @@ void Assembler::cbnz(Instruction* at, const Register& rt, int imm19) {
void Assembler::cbnz(const Register& rt, Label* label) {
// Encode the relative offset from the inserted branch to the label.
return cbnz(rt, LinkAndGetInstructionOffsetTo(nextInstrOffset(), CondBranchRangeType, label));
LabelDoc doc = refLabel(label);
return cbnz(rt, LinkAndGetInstructionOffsetTo(nextInstrOffset(), CondBranchRangeType, label), doc);
}
void Assembler::tbz(const Register& rt, unsigned bit_pos, int imm14) {
void Assembler::tbz(const Register& rt, unsigned bit_pos, int imm14, const LabelDoc& doc) {
VIXL_ASSERT(rt.Is64Bits() || (rt.Is32Bits() && (bit_pos < kWRegSize)));
EmitBranch(TBZ | ImmTestBranchBit(bit_pos) | ImmTestBranch(imm14) | Rt(rt));
EmitBranch(TBZ | ImmTestBranchBit(bit_pos) | ImmTestBranch(imm14) | Rt(rt), doc);
}
@ -303,13 +309,14 @@ void Assembler::tbz(Instruction* at, const Register& rt, unsigned bit_pos, int i
void Assembler::tbz(const Register& rt, unsigned bit_pos, Label* label) {
// Encode the relative offset from the inserted branch to the label.
return tbz(rt, bit_pos, LinkAndGetInstructionOffsetTo(nextInstrOffset(), TestBranchRangeType, label));
LabelDoc doc = refLabel(label);
return tbz(rt, bit_pos, LinkAndGetInstructionOffsetTo(nextInstrOffset(), TestBranchRangeType, label), doc);
}
void Assembler::tbnz(const Register& rt, unsigned bit_pos, int imm14) {
void Assembler::tbnz(const Register& rt, unsigned bit_pos, int imm14, const LabelDoc& doc) {
VIXL_ASSERT(rt.Is64Bits() || (rt.Is32Bits() && (bit_pos < kWRegSize)));
EmitBranch(TBNZ | ImmTestBranchBit(bit_pos) | ImmTestBranch(imm14) | Rt(rt));
EmitBranch(TBNZ | ImmTestBranchBit(bit_pos) | ImmTestBranch(imm14) | Rt(rt), doc);
}
@ -321,13 +328,14 @@ void Assembler::tbnz(Instruction* at, const Register& rt, unsigned bit_pos, int
void Assembler::tbnz(const Register& rt, unsigned bit_pos, Label* label) {
// Encode the relative offset from the inserted branch to the label.
return tbnz(rt, bit_pos, LinkAndGetInstructionOffsetTo(nextInstrOffset(), TestBranchRangeType, label));
LabelDoc doc = refLabel(label);
return tbnz(rt, bit_pos, LinkAndGetInstructionOffsetTo(nextInstrOffset(), TestBranchRangeType, label), doc);
}
void Assembler::adr(const Register& rd, int imm21) {
void Assembler::adr(const Register& rd, int imm21, const LabelDoc& doc) {
VIXL_ASSERT(rd.Is64Bits());
EmitBranch(ADR | ImmPCRelAddress(imm21) | Rd(rd));
EmitBranch(ADR | ImmPCRelAddress(imm21) | Rd(rd), doc);
}
@ -339,13 +347,14 @@ void Assembler::adr(Instruction* at, const Register& rd, int imm21) {
void Assembler::adr(const Register& rd, Label* label) {
// Encode the relative offset from the inserted adr to the label.
return adr(rd, LinkAndGetByteOffsetTo(nextInstrOffset(), label));
LabelDoc doc = refLabel(label);
return adr(rd, LinkAndGetByteOffsetTo(nextInstrOffset(), label), doc);
}
void Assembler::adrp(const Register& rd, int imm21) {
void Assembler::adrp(const Register& rd, int imm21, const LabelDoc& doc) {
VIXL_ASSERT(rd.Is64Bits());
EmitBranch(ADRP | ImmPCRelAddress(imm21) | Rd(rd));
EmitBranch(ADRP | ImmPCRelAddress(imm21) | Rd(rd), doc);
}
@ -358,7 +367,8 @@ void Assembler::adrp(Instruction* at, const Register& rd, int imm21) {
void Assembler::adrp(const Register& rd, Label* label) {
VIXL_ASSERT(AllowPageOffsetDependentCode());
// Encode the relative offset from the inserted adr to the label.
return adrp(rd, LinkAndGetPageOffsetTo(nextInstrOffset(), label));
LabelDoc doc = refLabel(label);
return adrp(rd, LinkAndGetPageOffsetTo(nextInstrOffset(), label), doc);
}

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

@ -31,13 +31,21 @@
#include "jit/arm64/vixl/Instructions-vixl.h"
#include "jit/shared/Assembler-shared.h"
#include "jit/shared/Disassembler-shared.h"
#include "jit/shared/IonAssemblerBufferWithConstantPools.h"
namespace vixl {
using js::jit::BufferOffset;
using js::jit::DisassemblerSpew;
using LabelDoc = DisassemblerSpew::LabelDoc;
using LiteralDoc = DisassemblerSpew::LiteralDoc;
#ifdef JS_DISASM_ARM64
void DisassembleInstruction(char* buffer, size_t bufsize, const Instruction* instr);
#endif
class MozBaseAssembler;
typedef js::jit::AssemblerBufferWithConstantPools<1024, 4, Instruction, MozBaseAssembler,
@ -55,6 +63,12 @@ class MozBaseAssembler : public js::jit::AssemblerShared {
static const uint32_t BufferNopFillInstruction = HINT | (31 << Rt_offset);
static const unsigned BufferNumDebugNopsToInsert = 0;
#ifdef JS_DISASM_ARM64
static constexpr const char* const InstrIndent = " ";
static constexpr const char* const LabelIndent = " ";
static constexpr const char* const TargetIndent = " ";
#endif
public:
MozBaseAssembler()
: armbuffer_(BufferGuardSize,
@ -65,7 +79,18 @@ class MozBaseAssembler : public js::jit::AssemblerShared {
BufferAlignmentFillInstruction,
BufferNopFillInstruction,
BufferNumDebugNopsToInsert)
{ }
{
#ifdef JS_DISASM_ARM64
spew_.setLabelIndent(LabelIndent);
spew_.setTargetIndent(TargetIndent);
#endif
}
~MozBaseAssembler()
{
#ifdef JS_DISASM_ARM64
spew_.spewOrphans();
#endif
}
public:
// Helper function for use with the ARMBuffer.
@ -102,26 +127,121 @@ class MozBaseAssembler : public js::jit::AssemblerShared {
// Allocate memory in the buffer by forwarding to armbuffer_.
// Propagate OOM errors.
BufferOffset allocEntry(size_t numInst, unsigned numPoolEntries,
BufferOffset allocLiteralLoadEntry(size_t numInst, unsigned numPoolEntries,
uint8_t* inst, uint8_t* data,
const LiteralDoc& doc = LiteralDoc(),
ARMBuffer::PoolEntry* pe = nullptr)
{
MOZ_ASSERT(inst);
MOZ_ASSERT(numInst == 1); /* If not, then fix disassembly */
BufferOffset offset = armbuffer_.allocEntry(numInst, numPoolEntries, inst,
data, pe);
propagateOOM(offset.assigned());
#ifdef JS_DISASM_ARM64
Instruction* instruction = armbuffer_.getInstOrNull(offset);
if (instruction)
spewLiteralLoad(reinterpret_cast<vixl::Instruction*>(instruction), doc);
#endif
return offset;
}
#ifdef JS_DISASM_ARM64
DisassemblerSpew spew_;
void spew(const vixl::Instruction* instr) {
if (spew_.isDisabled() || !instr)
return;
char buffer[2048];
DisassembleInstruction(buffer, sizeof(buffer), instr);
spew_.spew("%08" PRIx32 "%s%s", instr->InstructionBits(), InstrIndent, buffer);
}
void spewBranch(const vixl::Instruction* instr, const LabelDoc& target) {
if (spew_.isDisabled() || !instr)
return;
char buffer[2048];
DisassembleInstruction(buffer, sizeof(buffer), instr);
char labelBuf[128];
labelBuf[0] = 0;
bool hasTarget = target.valid;
if (!hasTarget)
snprintf(labelBuf, sizeof(labelBuf), "-> (link-time target)");
if (instr->IsImmBranch() && hasTarget) {
// The target information in the instruction is likely garbage, so remove it.
// The target label will in any case be printed if we have it.
//
// The format of the instruction disassembly is /.*#.*/. Strip the # and later.
size_t i;
const size_t BUFLEN = sizeof(buffer)-1;
for ( i=0 ; i < BUFLEN && buffer[i] && buffer[i] != '#' ; i++ )
;
buffer[i] = 0;
snprintf(labelBuf, sizeof(labelBuf), "-> %d%s", target.doc, !target.bound ? "f" : "");
hasTarget = false;
}
spew_.spew("%08" PRIx32 "%s%s%s", instr->InstructionBits(), InstrIndent, buffer, labelBuf);
if (hasTarget)
spew_.spewRef(target);
}
void spewLiteralLoad(const vixl::Instruction* instr, const LiteralDoc& doc) {
if (spew_.isDisabled() || !instr)
return;
char buffer[2048];
DisassembleInstruction(buffer, sizeof(buffer), instr);
char litbuf[2048];
spew_.formatLiteral(doc, litbuf, sizeof(litbuf));
// The instruction will have the form /^.*pc\+0/ followed by junk that we
// don't need; try to strip it.
char *probe = strstr(buffer, "pc+0");
if (probe)
*(probe + 4) = 0;
spew_.spew("%08" PRIx32 "%s%s ; .const %s", instr->InstructionBits(), InstrIndent, buffer, litbuf);
}
LabelDoc refLabel(Label* label) {
if (spew_.isDisabled())
return LabelDoc();
return spew_.refLabel(label);
}
#else
LabelDoc refLabel(js::jit::Label*) {
return LabelDoc();
}
#endif
// Emit the instruction, returning its offset.
BufferOffset Emit(Instr instruction, bool isBranch = false) {
JS_STATIC_ASSERT(sizeof(instruction) == kInstructionSize);
// TODO: isBranch is obsolete and should be removed.
(void)isBranch;
return armbuffer_.putInt(*(uint32_t*)(&instruction));
BufferOffset offs = armbuffer_.putInt(*(uint32_t*)(&instruction));
#ifdef JS_DISASM_ARM64
if (!isBranch)
spew(armbuffer_.getInstOrNull(offs));
#endif
return offs;
}
BufferOffset EmitBranch(Instr instruction) {
return Emit(instruction, true);
BufferOffset EmitBranch(Instr instruction, const LabelDoc& doc) {
BufferOffset offs = Emit(instruction, true);
#ifdef JS_DISASM_ARM64
spewBranch(armbuffer_.getInstOrNull(offs), doc);
#endif
return offs;
}
public:

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

@ -77,25 +77,6 @@ MacroAssemblerMIPSCompat::convertUInt32ToDouble(Register src, FloatRegister dest
as_addd(dest, dest, SecondScratchDoubleReg);
}
static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
bool
MacroAssemblerMIPSCompat::convertUInt64ToDoubleNeedsTemp()
{
return false;
}
void
MacroAssemblerMIPSCompat::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
{
MOZ_ASSERT(temp == Register::Invalid());
convertUInt32ToDouble(src.high, dest);
loadConstantDouble(TO_DOUBLE_HIGH_SCALE, ScratchDoubleReg);
asMasm().mulDouble(ScratchDoubleReg, dest);
convertUInt32ToDouble(src.low, ScratchDoubleReg);
asMasm().addDouble(ScratchDoubleReg, dest);
}
void
MacroAssemblerMIPSCompat::convertUInt32ToFloat32(Register src, FloatRegister dest)
{
@ -2506,4 +2487,26 @@ MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output
bind(&done);
}
// ========================================================================
// Convert floating point.
static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
bool
MacroAssembler::convertUInt64ToDoubleNeedsTemp()
{
return false;
}
void
MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
{
MOZ_ASSERT(temp == Register::Invalid());
convertUInt32ToDouble(src.high, dest);
loadConstantDouble(TO_DOUBLE_HIGH_SCALE, ScratchDoubleReg);
mulDouble(ScratchDoubleReg, dest);
convertUInt32ToDouble(src.low, ScratchDoubleReg);
addDouble(ScratchDoubleReg, dest);
}
//}}} check_macroassembler_style

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

@ -1000,9 +1000,6 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
moveToDoubleHi(zero, reg);
}
static bool convertUInt64ToDoubleNeedsTemp();
void convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp);
void breakpoint();
void checkStackAlignment();

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

@ -743,12 +743,12 @@ CodeGeneratorMIPS64::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir)
if (outputType == MIRType::Double) {
if (lir->mir()->isUnsigned())
masm.convertUInt64ToDouble(input, output);
masm.convertUInt64ToDouble(input, output, Register::Invalid());
else
masm.convertInt64ToDouble(input, output);
} else {
if (lir->mir()->isUnsigned())
masm.convertUInt64ToFloat32(input, output);
masm.convertUInt64ToFloat32(input, output, Register::Invalid());
else
masm.convertInt64ToFloat32(input, output);
}

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

@ -74,20 +74,6 @@ MacroAssemblerMIPS64Compat::convertUInt32ToDouble(Register src, FloatRegister de
as_addd(dest, dest, SecondScratchDoubleReg);
}
void
MacroAssemblerMIPS64Compat::convertInt64ToDouble(Register src, FloatRegister dest)
{
as_dmtc1(src, dest);
as_cvtdl(dest, dest);
}
void
MacroAssemblerMIPS64Compat::convertInt64ToFloat32(Register src, FloatRegister dest)
{
as_dmtc1(src, dest);
as_cvtsl(dest, dest);
}
void
MacroAssemblerMIPS64Compat::convertUInt64ToDouble(Register src, FloatRegister dest)
{
@ -112,42 +98,6 @@ MacroAssemblerMIPS64Compat::convertUInt64ToDouble(Register src, FloatRegister de
bind(&done);
}
void
MacroAssemblerMIPS64Compat::convertUInt64ToFloat32(Register src, FloatRegister dest)
{
Label positive, done;
ma_b(src, src, &positive, NotSigned, ShortJump);
MOZ_ASSERT(src!= ScratchRegister);
MOZ_ASSERT(src!= SecondScratchReg);
ma_and(ScratchRegister, src, Imm32(1));
ma_dsrl(SecondScratchReg, src, Imm32(1));
ma_or(ScratchRegister, SecondScratchReg);
as_dmtc1(ScratchRegister, dest);
as_cvtsl(dest, dest);
asMasm().addFloat32(dest, dest);
ma_b(&done, ShortJump);
bind(&positive);
as_dmtc1(src, dest);
as_cvtsl(dest, dest);
bind(&done);
}
bool
MacroAssemblerMIPS64Compat::convertUInt64ToDoubleNeedsTemp()
{
return false;
}
void
MacroAssemblerMIPS64Compat::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
{
convertUInt64ToDouble(src.reg, dest);
}
void
MacroAssemblerMIPS64Compat::convertUInt32ToFloat32(Register src, FloatRegister dest)
{
@ -2604,4 +2554,61 @@ MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output
}
// ========================================================================
// Convert floating point.
void
MacroAssembler::convertInt64ToDouble(Register64 src, FloatRegister dest)
{
as_dmtc1(src.reg, dest);
as_cvtdl(dest, dest);
}
void
MacroAssembler::convertInt64ToFloat32(Register64 src, FloatRegister dest)
{
as_dmtc1(src.reg, dest);
as_cvtsl(dest, dest);
}
bool
MacroAssembler::convertUInt64ToDoubleNeedsTemp()
{
return false;
}
void
MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
{
MOZ_ASSERT(temp == Register::Invalid());
MacroAssemblerSpecific::convertUInt64ToDouble(src.reg, dest);
}
void
MacroAssembler::convertUInt64ToFloat32(Register64 src_, FloatRegister dest, Register temp)
{
MOZ_ASSERT(temp == Register::Invalid());
Register src = src_.reg;
Label positive, done;
ma_b(src, src, &positive, NotSigned, ShortJump);
MOZ_ASSERT(src!= ScratchRegister);
MOZ_ASSERT(src!= SecondScratchReg);
ma_and(ScratchRegister, src, Imm32(1));
ma_dsrl(SecondScratchReg, src, Imm32(1));
ma_or(ScratchRegister, SecondScratchReg);
as_dmtc1(ScratchRegister, dest);
as_cvtsl(dest, dest);
addFloat32(dest, dest);
ma_b(&done, ShortJump);
bind(&positive);
as_dmtc1(src, dest);
as_cvtsl(dest, dest);
bind(&done);
}
//}}} check_macroassembler_style

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

@ -982,14 +982,7 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64
moveToDouble(zero, reg);
}
void convertInt64ToDouble(Register src, FloatRegister dest);
void convertInt64ToFloat32(Register src, FloatRegister dest);
void convertUInt64ToDouble(Register src, FloatRegister dest);
void convertUInt64ToFloat32(Register src, FloatRegister dest);
static bool convertUInt64ToDoubleNeedsTemp();
void convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp);
void breakpoint();

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

@ -342,8 +342,6 @@ class MacroAssemblerNone : public Assembler
template <typename T> void convertInt32ToDouble(T, FloatRegister) { MOZ_CRASH(); }
void convertFloat32ToDouble(FloatRegister, FloatRegister) { MOZ_CRASH(); }
static bool convertUInt64ToDoubleNeedsTemp() { MOZ_CRASH(); }
void convertUInt64ToDouble(Register64, FloatRegister, Register) { MOZ_CRASH(); }
void boolValueToDouble(ValueOperand, FloatRegister) { MOZ_CRASH(); }
void boolValueToFloat32(ValueOperand, FloatRegister) { MOZ_CRASH(); }

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

@ -0,0 +1,275 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/shared/Disassembler-shared.h"
#include "jsprf.h"
#include "jit/JitSpewer.h"
using namespace js::jit;
#ifdef JS_DISASM_SUPPORTED
// See comments in spew(), below.
mozilla::Atomic<uint32_t> DisassemblerSpew::live_(0);
#endif
DisassemblerSpew::DisassemblerSpew()
: printer_(nullptr)
#ifdef JS_DISASM_SUPPORTED
,
labelIndent_(""),
targetIndent_(""),
spewNext_(1000),
nodes_(nullptr)
#endif
{
#ifdef JS_DISASM_SUPPORTED
live_++;
#endif
}
DisassemblerSpew::~DisassemblerSpew()
{
#ifdef JS_DISASM_SUPPORTED
Node* p = nodes_;
while (p) {
Node* victim = p;
p = p->next;
js_free(victim);
}
live_--;
#endif
}
void
DisassemblerSpew::setPrinter(Sprinter* printer)
{
printer_ = printer;
}
bool
DisassemblerSpew::isDisabled()
{
return !(JitSpewEnabled(JitSpew_Codegen) || printer_);
}
void
DisassemblerSpew::spew(const char* fmt, ...)
{
// Nested assemblers are handled by prefixing the output with '>..> ' where
// the number of '>' is the nesting level, and the outermost assembler is
// taken as being at nesting level zero (and does not require the trailing
// space character). This markup disambiguates eg the output of an IC
// compilation that happens as a subtask of a normal compilation from the
// output of the normal compilation.
//
// We track the nesting level globally, on the assumption that anyone
// wanting to look at disassembly is running with --no-threads. If this
// turns out to be wrong then live_ can be made thread-local.
#ifdef JS_DISASM_SUPPORTED
char fmt2[1024];
MOZ_RELEASE_ASSERT(sizeof(fmt2) >= strlen(fmt) + live_ + 1);
uint32_t i;
for (i = 0; i < live_-1; i++ )
fmt2[i] = '>';
if (live_ > 1)
fmt2[i++] = ' ';
strcpy(fmt2 + i, fmt);
#else
const char* fmt2 = fmt;
#endif
va_list args;
va_start(args, fmt);
spewVA(fmt2, args);
va_end(args);
}
void
DisassemblerSpew::spewVA(const char* fmt, va_list va)
{
if (printer_) {
printer_->vprintf(fmt, va);
printer_->put("\n");
}
js::jit::JitSpewVA(js::jit::JitSpew_Codegen, fmt, va);
}
#ifdef JS_DISASM_SUPPORTED
void
DisassemblerSpew::setLabelIndent(const char* s)
{
labelIndent_ = s;
}
void
DisassemblerSpew::setTargetIndent(const char* s)
{
targetIndent_ = s;
}
DisassemblerSpew::LabelDoc
DisassemblerSpew::refLabel(const Label* l)
{
return l ? LabelDoc(internalResolve(l), l->bound()) : LabelDoc();
}
void
DisassemblerSpew::spewRef(const LabelDoc& target)
{
if (isDisabled())
return;
if (!target.valid)
return;
spew("%s-> %d%s", targetIndent_, target.doc, !target.bound ? "f" : "");
}
void
DisassemblerSpew::spewBind(const Label* label)
{
if (isDisabled())
return;
uint32_t v = internalResolve(label);
Node* probe = lookup(label);
if (probe)
probe->bound = true;
spew("%s%d:", labelIndent_, v);
}
void
DisassemblerSpew::spewRetarget(const Label* label, const Label* target)
{
if (isDisabled())
return;
LabelDoc labelDoc = LabelDoc(internalResolve(label), label->bound());
LabelDoc targetDoc = LabelDoc(internalResolve(target), target->bound());
Node* probe = lookup(label);
if (probe)
probe->bound = true;
spew("%s%d: .retarget -> %d%s",
labelIndent_, labelDoc.doc, targetDoc.doc, !targetDoc.bound ? "f" : "");
}
void
DisassemblerSpew::formatLiteral(const LiteralDoc& doc, char* buffer, size_t bufsize)
{
switch (doc.type) {
case LiteralDoc::Type::Patchable:
snprintf(buffer, bufsize, "patchable");
break;
case LiteralDoc::Type::I32:
snprintf(buffer, bufsize, "%d", doc.value.i32);
break;
case LiteralDoc::Type::U32:
snprintf(buffer, bufsize, "%u", doc.value.u32);
break;
case LiteralDoc::Type::I64:
snprintf(buffer, bufsize, "%" PRIi64, doc.value.i64);
break;
case LiteralDoc::Type::U64:
snprintf(buffer, bufsize, "%" PRIu64, doc.value.u64);
break;
case LiteralDoc::Type::F32:
snprintf(buffer, bufsize, "%g", doc.value.f32);
break;
case LiteralDoc::Type::F64:
snprintf(buffer, bufsize, "%g", doc.value.f64);
break;
default:
MOZ_CRASH();
}
}
void
DisassemblerSpew::spewOrphans()
{
for (Node* p = nodes_; p; p = p->next) {
if (!p->bound)
spew("%s%d: ; .orphan", labelIndent_, p->value);
}
}
uint32_t
DisassemblerSpew::internalResolve(const Label* l)
{
// Note, internalResolve will sometimes return 0 when it is triggered by the
// profiler and not by a full disassembly, since in that case a label can be
// used or bound but not previously have been defined. In that case,
// internalResolve(l) will not necessarily create a binding for l!
// Consequently a subsequent lookup(l) may still return null.
return l->used() || l->bound() ? probe(l) : define(l);
}
uint32_t
DisassemblerSpew::probe(const Label* l)
{
Node* n = lookup(l);
return n ? n->value : 0;
}
uint32_t
DisassemblerSpew::define(const Label* l)
{
remove(l);
uint32_t value = spewNext_++;
if (!add(l, value))
return 0;
return value;
}
DisassemblerSpew::Node*
DisassemblerSpew::lookup(const Label* key)
{
Node* p;
for (p = nodes_; p && p->key != key; p = p->next)
;
return p;
}
DisassemblerSpew::Node*
DisassemblerSpew::add(const Label* key, uint32_t value)
{
MOZ_ASSERT(!lookup(key));
Node* node = (Node*)js_malloc(sizeof(Node));
if (node) {
node->key = key;
node->value = value;
node->bound = false;
node->next = nodes_;
nodes_ = node;
}
return node;
}
bool
DisassemblerSpew::remove(const Label* key)
{
// We do not require that there is a node matching the key.
for (Node* p = nodes_, *pp = nullptr; p; pp = p, p = p->next) {
if (p->key == key) {
if (pp)
pp->next = p->next;
else
nodes_ = p->next;
js_free(p);
return true;
}
}
return false;
}
#else
DisassemblerSpew::LabelDoc
DisassemblerSpew::refLabel(const Label* l)
{
return LabelDoc();
}
#endif

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

@ -0,0 +1,183 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef jit_shared_Disassembler_shared_h
#define jit_shared_Disassembler_shared_h
#include "mozilla/Atomics.h"
#include "jit/Label.h"
#ifdef JS_DISASM_SUPPORTED
# include "jit/shared/IonAssemblerBuffer.h"
#endif
using js::jit::Label;
using js::Sprinter;
#if defined(JS_DISASM_ARM) || defined(JS_DISASM_ARM64)
# define JS_DISASM_SUPPORTED
#endif
namespace js {
namespace jit {
// A wrapper around spew/disassembly functionality. The disassembler is built
// on a per-instruction disassembler (as in our ARM, ARM64 back-ends) and
// formats labels with meaningful names and literals with meaningful values, if
// the assembler creates documentation (with provided helpers) at appropriate
// points.
class DisassemblerSpew
{
#ifdef JS_DISASM_SUPPORTED
struct Node
{
const Label* key; // Never dereferenced, only used for its value
uint32_t value; // The printable label value
bool bound; // If the label has been seen by spewBind()
Node* next;
};
Node* lookup(const Label* key);
Node* add(const Label* key, uint32_t value);
bool remove(const Label* key);
uint32_t probe(const Label* l);
uint32_t define(const Label* l);
uint32_t internalResolve(const Label* l);
#endif
void spewVA(const char* fmt, va_list args) MOZ_FORMAT_PRINTF(2, 0);
public:
DisassemblerSpew();
~DisassemblerSpew();
#ifdef JS_DISASM_SUPPORTED
// Set indentation strings. The spewer retains a reference to s.
void setLabelIndent(const char* s);
void setTargetIndent(const char* s);
#endif
// Set the spew printer, which will always be used if it is set, regardless
// of whether the system spew channel is enabled or not. The spewer retains
// a reference to sp.
void setPrinter(Sprinter* sp);
// Return true if disassembly spew is disabled and no additional printer is
// set.
bool isDisabled();
// Format and print text on the spew channel; output is suppressed if spew
// is disabled. The output is not indented, and is terminated by a newline.
void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
// Documentation for a label reference.
struct LabelDoc
{
#ifdef JS_DISASM_SUPPORTED
LabelDoc() : doc(0), bound(false), valid(false) {}
LabelDoc(uint32_t doc, bool bound) : doc(doc), bound(bound), valid(true) {}
const uint32_t doc;
const bool bound;
const bool valid;
#else
LabelDoc() {}
LabelDoc(uint32_t, bool) {}
#endif
};
// Documentation for a literal load.
struct LiteralDoc
{
#ifdef JS_DISASM_SUPPORTED
enum class Type { Patchable, I32, U32, I64, U64, F32, F64 };
const Type type;
union {
int32_t i32;
uint32_t u32;
int64_t i64;
uint64_t u64;
float f32;
double f64;
} value;
LiteralDoc() : type(Type::Patchable) {}
explicit LiteralDoc(int32_t v) : type(Type::I32) { value.i32 = v; }
explicit LiteralDoc(uint32_t v) : type(Type::U32) { value.u32 = v; }
explicit LiteralDoc(int64_t v) : type(Type::I64) { value.i64 = v; }
explicit LiteralDoc(uint64_t v) : type(Type::U64) { value.u64 = v; }
explicit LiteralDoc(float v) : type(Type::F32) { value.f32 = v; }
explicit LiteralDoc(double v) : type(Type::F64) { value.f64 = v; }
#else
LiteralDoc() {}
explicit LiteralDoc(int32_t) {}
explicit LiteralDoc(uint32_t) {}
explicit LiteralDoc(int64_t) {}
explicit LiteralDoc(uint64_t) {}
explicit LiteralDoc(float) {}
explicit LiteralDoc(double) {}
#endif
};
// Reference a label, resolving it to a printable representation.
//
// NOTE: The printable representation depends on the state of the label, so
// if we call resolve() when emitting & disassembling a branch instruction
// then it should be called before the label becomes Used, if emitting the
// branch can change the label's state.
//
// If the disassembler is not defined this returns a structure that is
// marked not valid.
LabelDoc refLabel(const Label* l);
#ifdef JS_DISASM_SUPPORTED
// Spew the label information previously gathered by refLabel(), at a point
// where the label is referenced. The output is indented by targetIndent_
// and terminated by a newline.
void spewRef(const LabelDoc& target);
// Spew the label at the point where the label is bound. The output is
// indented by labelIndent_ and terminated by a newline.
void spewBind(const Label* label);
// Spew a retarget directive at the point where the retarget is recorded.
// The output is indented by labelIndent_ and terminated by a newline.
void spewRetarget(const Label* label, const Label* target);
// Format a literal value into the buffer. The buffer is always
// NUL-terminated even if this chops the formatted value.
void formatLiteral(const LiteralDoc& doc, char* buffer, size_t bufsize);
// Print any unbound labels, one per line, with normal label indent and with
// a comment indicating the label is not defined. Labels can be referenced
// but unbound in some legitimate cases, normally for traps. Printing them
// reduces confusion.
void spewOrphans();
#endif
private:
Sprinter* printer_;
#ifdef JS_DISASM_SUPPORTED
const char* labelIndent_;
const char* targetIndent_;
uint32_t spewNext_;
Node* nodes_;
// This global tracks the nesting level of assemblers, see comments in
// spew() in Disassembler-shared.cpp for why this is desirable.
//
// The variable is atomic to avoid any kind of complaint from thread
// sanitizers etc, (it could also be thread-local). However, trying to look
// at disassembly without using --no-threads is basically insane, so you can
// ignore the multi-threading implications here.
static mozilla::Atomic<uint32_t> live_;
#endif
};
}
}
#endif // jit_shared_Disassembler_shared_h

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

@ -72,172 +72,6 @@ MacroAssemblerX64::loadConstantSimd128Float(const SimdConstant&v, FloatRegister
propagateOOM(val->uses.append(CodeOffset(j.offset())));
}
void
MacroAssemblerX64::convertInt64ToDouble(Register64 input, FloatRegister output)
{
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroDouble(output);
vcvtsq2sd(input.reg, output, output);
}
void
MacroAssemblerX64::convertInt64ToFloat32(Register64 input, FloatRegister output)
{
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroFloat32(output);
vcvtsq2ss(input.reg, output, output);
}
bool
MacroAssemblerX64::convertUInt64ToDoubleNeedsTemp()
{
return true;
}
void
MacroAssemblerX64::convertUInt64ToDouble(Register64 input, FloatRegister output, Register temp)
{
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroDouble(output);
// If the input's sign bit is not set we use vcvtsq2sd directly.
// Else, we divide by 2 and keep the LSB, convert to double, and multiply
// the result by 2.
Label done;
Label isSigned;
testq(input.reg, input.reg);
j(Assembler::Signed, &isSigned);
vcvtsq2sd(input.reg, output, output);
jump(&done);
bind(&isSigned);
ScratchRegisterScope scratch(asMasm());
mov(input.reg, scratch);
mov(input.reg, temp);
shrq(Imm32(1), scratch);
andq(Imm32(1), temp);
orq(temp, scratch);
vcvtsq2sd(scratch, output, output);
vaddsd(output, output, output);
bind(&done);
}
void
MacroAssemblerX64::convertUInt64ToFloat32(Register64 input, FloatRegister output, Register temp)
{
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroFloat32(output);
// See comment in convertUInt64ToDouble.
Label done;
Label isSigned;
testq(input.reg, input.reg);
j(Assembler::Signed, &isSigned);
vcvtsq2ss(input.reg, output, output);
jump(&done);
bind(&isSigned);
ScratchRegisterScope scratch(asMasm());
mov(input.reg, scratch);
mov(input.reg, temp);
shrq(Imm32(1), scratch);
andq(Imm32(1), temp);
orq(temp, scratch);
vcvtsq2ss(scratch, output, output);
vaddss(output, output, output);
bind(&done);
}
void
MacroAssemblerX64::wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
vcvttsd2sq(input, output.reg);
cmpq(Imm32(1), output.reg);
j(Assembler::Overflow, oolEntry);
bind(oolRejoin);
}
void
MacroAssemblerX64::wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
vcvttss2sq(input, output.reg);
cmpq(Imm32(1), output.reg);
j(Assembler::Overflow, oolEntry);
bind(oolRejoin);
}
void
MacroAssemblerX64::wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
// If the input < INT64_MAX, vcvttsd2sq will do the right thing, so
// we use it directly. Else, we subtract INT64_MAX, convert to int64,
// and then add INT64_MAX to the result.
Label isLarge;
ScratchDoubleScope scratch(asMasm());
loadConstantDouble(double(0x8000000000000000), scratch);
asMasm().branchDouble(Assembler::DoubleGreaterThanOrEqual, input, scratch, &isLarge);
vcvttsd2sq(input, output.reg);
testq(output.reg, output.reg);
j(Assembler::Signed, oolEntry);
jump(oolRejoin);
bind(&isLarge);
moveDouble(input, tempReg);
vsubsd(scratch, tempReg, tempReg);
vcvttsd2sq(tempReg, output.reg);
testq(output.reg, output.reg);
j(Assembler::Signed, oolEntry);
asMasm().or64(Imm64(0x8000000000000000), output);
bind(oolRejoin);
}
void
MacroAssemblerX64::wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
// If the input < INT64_MAX, vcvttss2sq will do the right thing, so
// we use it directly. Else, we subtract INT64_MAX, convert to int64,
// and then add INT64_MAX to the result.
Label isLarge;
ScratchFloat32Scope scratch(asMasm());
loadConstantFloat32(float(0x8000000000000000), scratch);
asMasm().branchFloat(Assembler::DoubleGreaterThanOrEqual, input, scratch, &isLarge);
vcvttss2sq(input, output.reg);
testq(output.reg, output.reg);
j(Assembler::Signed, oolEntry);
jump(oolRejoin);
bind(&isLarge);
moveFloat32(input, tempReg);
vsubss(scratch, tempReg, tempReg);
vcvttss2sq(tempReg, output.reg);
testq(output.reg, output.reg);
j(Assembler::Signed, oolEntry);
asMasm().or64(Imm64(0x8000000000000000), output);
bind(oolRejoin);
}
void
MacroAssemblerX64::bindOffsets(const MacroAssemblerX86Shared::UsesVector& uses)
{
@ -923,6 +757,175 @@ MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output
j(Assembler::Above, oolEntry);
}
void
MacroAssembler::wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
vcvttsd2sq(input, output.reg);
cmpq(Imm32(1), output.reg);
j(Assembler::Overflow, oolEntry);
bind(oolRejoin);
}
void
MacroAssembler::wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
vcvttss2sq(input, output.reg);
cmpq(Imm32(1), output.reg);
j(Assembler::Overflow, oolEntry);
bind(oolRejoin);
}
void
MacroAssembler::wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
// If the input < INT64_MAX, vcvttsd2sq will do the right thing, so
// we use it directly. Else, we subtract INT64_MAX, convert to int64,
// and then add INT64_MAX to the result.
Label isLarge;
ScratchDoubleScope scratch(*this);
loadConstantDouble(double(0x8000000000000000), scratch);
branchDouble(Assembler::DoubleGreaterThanOrEqual, input, scratch, &isLarge);
vcvttsd2sq(input, output.reg);
testq(output.reg, output.reg);
j(Assembler::Signed, oolEntry);
jump(oolRejoin);
bind(&isLarge);
moveDouble(input, tempReg);
vsubsd(scratch, tempReg, tempReg);
vcvttsd2sq(tempReg, output.reg);
testq(output.reg, output.reg);
j(Assembler::Signed, oolEntry);
or64(Imm64(0x8000000000000000), output);
bind(oolRejoin);
}
void
MacroAssembler::wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
// If the input < INT64_MAX, vcvttss2sq will do the right thing, so
// we use it directly. Else, we subtract INT64_MAX, convert to int64,
// and then add INT64_MAX to the result.
Label isLarge;
ScratchFloat32Scope scratch(*this);
loadConstantFloat32(float(0x8000000000000000), scratch);
branchFloat(Assembler::DoubleGreaterThanOrEqual, input, scratch, &isLarge);
vcvttss2sq(input, output.reg);
testq(output.reg, output.reg);
j(Assembler::Signed, oolEntry);
jump(oolRejoin);
bind(&isLarge);
moveFloat32(input, tempReg);
vsubss(scratch, tempReg, tempReg);
vcvttss2sq(tempReg, output.reg);
testq(output.reg, output.reg);
j(Assembler::Signed, oolEntry);
or64(Imm64(0x8000000000000000), output);
bind(oolRejoin);
}
// ========================================================================
// Convert floating point.
void
MacroAssembler::convertInt64ToDouble(Register64 input, FloatRegister output)
{
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroDouble(output);
vcvtsq2sd(input.reg, output, output);
}
void
MacroAssembler::convertInt64ToFloat32(Register64 input, FloatRegister output)
{
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroFloat32(output);
vcvtsq2ss(input.reg, output, output);
}
bool
MacroAssembler::convertUInt64ToDoubleNeedsTemp()
{
return true;
}
void
MacroAssembler::convertUInt64ToDouble(Register64 input, FloatRegister output, Register temp)
{
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroDouble(output);
// If the input's sign bit is not set we use vcvtsq2sd directly.
// Else, we divide by 2 and keep the LSB, convert to double, and multiply
// the result by 2.
Label done;
Label isSigned;
testq(input.reg, input.reg);
j(Assembler::Signed, &isSigned);
vcvtsq2sd(input.reg, output, output);
jump(&done);
bind(&isSigned);
ScratchRegisterScope scratch(*this);
mov(input.reg, scratch);
mov(input.reg, temp);
shrq(Imm32(1), scratch);
andq(Imm32(1), temp);
orq(temp, scratch);
vcvtsq2sd(scratch, output, output);
vaddsd(output, output, output);
bind(&done);
}
void
MacroAssembler::convertUInt64ToFloat32(Register64 input, FloatRegister output, Register temp)
{
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroFloat32(output);
// See comment in convertUInt64ToDouble.
Label done;
Label isSigned;
testq(input.reg, input.reg);
j(Assembler::Signed, &isSigned);
vcvtsq2ss(input.reg, output, output);
jump(&done);
bind(&isSigned);
ScratchRegisterScope scratch(*this);
mov(input.reg, scratch);
mov(input.reg, temp);
shrq(Imm32(1), scratch);
andq(Imm32(1), temp);
orq(temp, scratch);
vcvtsq2ss(scratch, output, output);
vaddss(output, output, output);
bind(&done);
}
// ========================================================================
// Primitive atomic operations.

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

@ -913,22 +913,6 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
void loadConstantSimd128Int(const SimdConstant& v, FloatRegister dest);
void loadConstantSimd128Float(const SimdConstant& v, FloatRegister dest);
void convertInt64ToDouble(Register64 input, FloatRegister output);
void convertInt64ToFloat32(Register64 input, FloatRegister output);
static bool convertUInt64ToDoubleNeedsTemp();
void convertUInt64ToDouble(Register64 input, FloatRegister output, Register temp);
void convertUInt64ToFloat32(Register64 input, FloatRegister output, Register temp);
void wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble);
void wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble);
void wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble);
void wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble);
void loadWasmGlobalPtr(uint32_t globalDataOffset, Register dest) {
loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, globalArea) + globalDataOffset), dest);
}

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

@ -22,95 +22,6 @@
using namespace js;
using namespace js::jit;
// vpunpckldq requires 16-byte boundary for memory operand.
// See convertUInt64ToDouble for the details.
MOZ_ALIGNED_DECL(static const uint64_t, 16) TO_DOUBLE[4] = {
0x4530000043300000LL,
0x0LL,
0x4330000000000000LL,
0x4530000000000000LL
};
static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
bool
MacroAssemblerX86::convertUInt64ToDoubleNeedsTemp()
{
return HasSSE3();
}
void
MacroAssemblerX86::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
{
// SUBPD needs SSE2, HADDPD needs SSE3.
if (!HasSSE3()) {
MOZ_ASSERT(temp == Register::Invalid());
// Zero the dest register to break dependencies, see convertInt32ToDouble.
zeroDouble(dest);
asMasm().Push(src.high);
asMasm().Push(src.low);
fild(Operand(esp, 0));
Label notNegative;
asMasm().branch32(Assembler::NotSigned, src.high, Imm32(0), &notNegative);
double add_constant = 18446744073709551616.0; // 2^64
store64(Imm64(mozilla::BitwiseCast<uint64_t>(add_constant)), Address(esp, 0));
fld(Operand(esp, 0));
faddp();
bind(&notNegative);
fstp(Operand(esp, 0));
vmovsd(Address(esp, 0), dest);
asMasm().freeStack(2 * sizeof(intptr_t));
return;
}
// Following operation uses entire 128-bit of dest XMM register.
// Currently higher 64-bit is free when we have access to lower 64-bit.
MOZ_ASSERT(dest.size() == 8);
FloatRegister dest128 = FloatRegister(dest.encoding(), FloatRegisters::Simd128);
// Assume that src is represented as following:
// src = 0x HHHHHHHH LLLLLLLL
// Move src to dest (=dest128) and ScratchInt32x4Reg (=scratch):
// dest = 0x 00000000 00000000 00000000 LLLLLLLL
// scratch = 0x 00000000 00000000 00000000 HHHHHHHH
vmovd(src.low, dest128);
vmovd(src.high, ScratchSimd128Reg);
// Unpack and interleave dest and scratch to dest:
// dest = 0x 00000000 00000000 HHHHHHHH LLLLLLLL
vpunpckldq(ScratchSimd128Reg, dest128, dest128);
// Unpack and interleave dest and a constant C1 to dest:
// C1 = 0x 00000000 00000000 45300000 43300000
// dest = 0x 45300000 HHHHHHHH 43300000 LLLLLLLL
// here, each 64-bit part of dest represents following double:
// HI(dest) = 0x 1.00000HHHHHHHH * 2**84 == 2**84 + 0x HHHHHHHH 00000000
// LO(dest) = 0x 1.00000LLLLLLLL * 2**52 == 2**52 + 0x 00000000 LLLLLLLL
movePtr(ImmWord((uintptr_t)TO_DOUBLE), temp);
vpunpckldq(Operand(temp, 0), dest128, dest128);
// Subtract a constant C2 from dest, for each 64-bit part:
// C2 = 0x 45300000 00000000 43300000 00000000
// here, each 64-bit part of C2 represents following double:
// HI(C2) = 0x 1.0000000000000 * 2**84 == 2**84
// LO(C2) = 0x 1.0000000000000 * 2**52 == 2**52
// after the operation each 64-bit part of dest represents following:
// HI(dest) = double(0x HHHHHHHH 00000000)
// LO(dest) = double(0x 00000000 LLLLLLLL)
vsubpd(Operand(temp, sizeof(uint64_t) * 2), dest128, dest128);
// Add HI(dest) and LO(dest) in double and store it into LO(dest),
// LO(dest) = double(0x HHHHHHHH 00000000) + double(0x 00000000 LLLLLLLL)
// = double(0x HHHHHHHH LLLLLLLL)
// = double(src)
vhaddpd(dest128, dest128);
}
void
MacroAssemblerX86::loadConstantDouble(double d, FloatRegister dest)
{
@ -1078,58 +989,252 @@ MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output
bind(&done);
}
//}}} check_macroassembler_style
void
MacroAssembler::wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
Label fail, convert;
Register temp = output.high;
// Make sure input fits in (u)int64.
reserveStack(2 * sizeof(int32_t));
storeDouble(input, Operand(esp, 0));
branchDoubleNotInInt64Range(Address(esp, 0), temp, &fail);
jump(&convert);
// Handle failure in ool.
bind(&fail);
freeStack(2 * sizeof(int32_t));
jump(oolEntry);
bind(oolRejoin);
reserveStack(2 * sizeof(int32_t));
storeDouble(input, Operand(esp, 0));
// Convert the double/float to int64.
bind(&convert);
truncateDoubleToInt64(Address(esp, 0), Address(esp, 0), temp);
// Load value into int64 register.
load64(Address(esp, 0), output);
freeStack(2 * sizeof(int32_t));
}
void
MacroAssemblerX86::convertInt64ToDouble(Register64 input, FloatRegister output)
MacroAssembler::wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
Label fail, convert;
Register temp = output.high;
// Make sure input fits in (u)int64.
reserveStack(2 * sizeof(int32_t));
storeFloat32(input, Operand(esp, 0));
branchFloat32NotInInt64Range(Address(esp, 0), temp, &fail);
jump(&convert);
// Handle failure in ool.
bind(&fail);
freeStack(2 * sizeof(int32_t));
jump(oolEntry);
bind(oolRejoin);
reserveStack(2 * sizeof(int32_t));
storeFloat32(input, Operand(esp, 0));
// Convert the double/float to int64.
bind(&convert);
truncateFloat32ToInt64(Address(esp, 0), Address(esp, 0), temp);
// Load value into int64 register.
load64(Address(esp, 0), output);
freeStack(2 * sizeof(int32_t));
}
void
MacroAssembler::wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
Label fail, convert;
Register temp = output.high;
// Make sure input fits in (u)int64.
reserveStack(2 * sizeof(int32_t));
storeDouble(input, Operand(esp, 0));
branchDoubleNotInUInt64Range(Address(esp, 0), temp, &fail);
jump(&convert);
// Handle failure in ool.
bind(&fail);
freeStack(2 * sizeof(int32_t));
jump(oolEntry);
bind(oolRejoin);
reserveStack(2 * sizeof(int32_t));
storeDouble(input, Operand(esp, 0));
// Convert the double/float to int64.
bind(&convert);
truncateDoubleToUInt64(Address(esp, 0), Address(esp, 0), temp, tempReg);
// Load value into int64 register.
load64(Address(esp, 0), output);
freeStack(2 * sizeof(int32_t));
}
void
MacroAssembler::wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
Label fail, convert;
Register temp = output.high;
// Make sure input fits in (u)int64.
reserveStack(2 * sizeof(int32_t));
storeFloat32(input, Operand(esp, 0));
branchFloat32NotInUInt64Range(Address(esp, 0), temp, &fail);
jump(&convert);
// Handle failure in ool.
bind(&fail);
freeStack(2 * sizeof(int32_t));
jump(oolEntry);
bind(oolRejoin);
reserveStack(2 * sizeof(int32_t));
storeFloat32(input, Operand(esp, 0));
// Convert the double/float to int64.
bind(&convert);
truncateFloat32ToUInt64(Address(esp, 0), Address(esp, 0), temp, tempReg);
// Load value into int64 register.
load64(Address(esp, 0), output);
freeStack(2 * sizeof(int32_t));
}
// ========================================================================
// Convert floating point.
// vpunpckldq requires 16-byte boundary for memory operand.
// See convertUInt64ToDouble for the details.
MOZ_ALIGNED_DECL(static const uint64_t, 16) TO_DOUBLE[4] = {
0x4530000043300000LL,
0x0LL,
0x4330000000000000LL,
0x4530000000000000LL
};
bool
MacroAssembler::convertUInt64ToDoubleNeedsTemp()
{
return HasSSE3();
}
void
MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
{
// SUBPD needs SSE2, HADDPD needs SSE3.
if (!HasSSE3()) {
MOZ_ASSERT(temp == Register::Invalid());
// Zero the dest register to break dependencies, see convertInt32ToDouble.
zeroDouble(dest);
Push(src.high);
Push(src.low);
fild(Operand(esp, 0));
Label notNegative;
branch32(Assembler::NotSigned, src.high, Imm32(0), &notNegative);
double add_constant = 18446744073709551616.0; // 2^64
store64(Imm64(mozilla::BitwiseCast<uint64_t>(add_constant)), Address(esp, 0));
fld(Operand(esp, 0));
faddp();
bind(&notNegative);
fstp(Operand(esp, 0));
vmovsd(Address(esp, 0), dest);
freeStack(2 * sizeof(intptr_t));
return;
}
// Following operation uses entire 128-bit of dest XMM register.
// Currently higher 64-bit is free when we have access to lower 64-bit.
MOZ_ASSERT(dest.size() == 8);
FloatRegister dest128 = FloatRegister(dest.encoding(), FloatRegisters::Simd128);
// Assume that src is represented as following:
// src = 0x HHHHHHHH LLLLLLLL
// Move src to dest (=dest128) and ScratchInt32x4Reg (=scratch):
// dest = 0x 00000000 00000000 00000000 LLLLLLLL
// scratch = 0x 00000000 00000000 00000000 HHHHHHHH
vmovd(src.low, dest128);
vmovd(src.high, ScratchSimd128Reg);
// Unpack and interleave dest and scratch to dest:
// dest = 0x 00000000 00000000 HHHHHHHH LLLLLLLL
vpunpckldq(ScratchSimd128Reg, dest128, dest128);
// Unpack and interleave dest and a constant C1 to dest:
// C1 = 0x 00000000 00000000 45300000 43300000
// dest = 0x 45300000 HHHHHHHH 43300000 LLLLLLLL
// here, each 64-bit part of dest represents following double:
// HI(dest) = 0x 1.00000HHHHHHHH * 2**84 == 2**84 + 0x HHHHHHHH 00000000
// LO(dest) = 0x 1.00000LLLLLLLL * 2**52 == 2**52 + 0x 00000000 LLLLLLLL
movePtr(ImmWord((uintptr_t)TO_DOUBLE), temp);
vpunpckldq(Operand(temp, 0), dest128, dest128);
// Subtract a constant C2 from dest, for each 64-bit part:
// C2 = 0x 45300000 00000000 43300000 00000000
// here, each 64-bit part of C2 represents following double:
// HI(C2) = 0x 1.0000000000000 * 2**84 == 2**84
// LO(C2) = 0x 1.0000000000000 * 2**52 == 2**52
// after the operation each 64-bit part of dest represents following:
// HI(dest) = double(0x HHHHHHHH 00000000)
// LO(dest) = double(0x 00000000 LLLLLLLL)
vsubpd(Operand(temp, sizeof(uint64_t) * 2), dest128, dest128);
// Add HI(dest) and LO(dest) in double and store it into LO(dest),
// LO(dest) = double(0x HHHHHHHH 00000000) + double(0x 00000000 LLLLLLLL)
// = double(0x HHHHHHHH LLLLLLLL)
// = double(src)
vhaddpd(dest128, dest128);
}
void
MacroAssembler::convertInt64ToDouble(Register64 input, FloatRegister output)
{
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroDouble(output);
asMasm().Push(input.high);
asMasm().Push(input.low);
Push(input.high);
Push(input.low);
fild(Operand(esp, 0));
fstp(Operand(esp, 0));
vmovsd(Address(esp, 0), output);
asMasm().freeStack(2 * sizeof(intptr_t));
freeStack(2 * sizeof(intptr_t));
}
void
MacroAssemblerX86::convertInt64ToFloat32(Register64 input, FloatRegister output)
{
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroDouble(output);
asMasm().Push(input.high);
asMasm().Push(input.low);
fild(Operand(esp, 0));
fstp32(Operand(esp, 0));
vmovss(Address(esp, 0), output);
asMasm().freeStack(2 * sizeof(intptr_t));
}
void
MacroAssemblerX86::convertUInt64ToFloat32(Register64 input, FloatRegister output, Register temp)
MacroAssembler::convertUInt64ToFloat32(Register64 input, FloatRegister output, Register temp)
{
// Zero the dest register to break dependencies, see convertInt32ToDouble.
zeroDouble(output);
// Set the FPU precision to 80 bits.
asMasm().reserveStack(2 * sizeof(intptr_t));
reserveStack(2 * sizeof(intptr_t));
fnstcw(Operand(esp, 0));
load32(Operand(esp, 0), temp);
orl(Imm32(0x300), temp);
store32(temp, Operand(esp, sizeof(intptr_t)));
fldcw(Operand(esp, sizeof(intptr_t)));
asMasm().Push(input.high);
asMasm().Push(input.low);
Push(input.high);
Push(input.low);
fild(Operand(esp, 0));
Label notNegative;
asMasm().branch32(Assembler::NotSigned, input.high, Imm32(0), &notNegative);
branch32(Assembler::NotSigned, input.high, Imm32(0), &notNegative);
double add_constant = 18446744073709551616.0; // 2^64
uint64_t add_constant_u64 = mozilla::BitwiseCast<uint64_t>(add_constant);
store64(Imm64(add_constant_u64), Address(esp, 0));
@ -1140,130 +1245,27 @@ MacroAssemblerX86::convertUInt64ToFloat32(Register64 input, FloatRegister output
fstp32(Operand(esp, 0));
vmovss(Address(esp, 0), output);
asMasm().freeStack(2 * sizeof(intptr_t));
freeStack(2 * sizeof(intptr_t));
// Restore FPU precision to the initial value.
fldcw(Operand(esp, 0));
asMasm().freeStack(2 * sizeof(intptr_t));
freeStack(2 * sizeof(intptr_t));
}
void
MacroAssemblerX86::wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
MacroAssembler::convertInt64ToFloat32(Register64 input, FloatRegister output)
{
Label fail, convert;
Register temp = output.high;
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroDouble(output);
// Make sure input fits in (u)int64.
asMasm().reserveStack(2 * sizeof(int32_t));
asMasm().storeDouble(input, Operand(esp, 0));
asMasm().branchDoubleNotInInt64Range(Address(esp, 0), temp, &fail);
jump(&convert);
Push(input.high);
Push(input.low);
fild(Operand(esp, 0));
// Handle failure in ool.
bind(&fail);
asMasm().freeStack(2 * sizeof(int32_t));
jump(oolEntry);
bind(oolRejoin);
asMasm().reserveStack(2 * sizeof(int32_t));
asMasm().storeDouble(input, Operand(esp, 0));
// Convert the double/float to int64.
bind(&convert);
asMasm().truncateDoubleToInt64(Address(esp, 0), Address(esp, 0), temp);
// Load value into int64 register.
load64(Address(esp, 0), output);
asMasm().freeStack(2 * sizeof(int32_t));
fstp32(Operand(esp, 0));
vmovss(Address(esp, 0), output);
freeStack(2 * sizeof(intptr_t));
}
void
MacroAssemblerX86::wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
Label fail, convert;
Register temp = output.high;
// Make sure input fits in (u)int64.
asMasm().reserveStack(2 * sizeof(int32_t));
asMasm().storeFloat32(input, Operand(esp, 0));
asMasm().branchFloat32NotInInt64Range(Address(esp, 0), temp, &fail);
jump(&convert);
// Handle failure in ool.
bind(&fail);
asMasm().freeStack(2 * sizeof(int32_t));
jump(oolEntry);
bind(oolRejoin);
asMasm().reserveStack(2 * sizeof(int32_t));
asMasm().storeFloat32(input, Operand(esp, 0));
// Convert the double/float to int64.
bind(&convert);
asMasm().truncateFloat32ToInt64(Address(esp, 0), Address(esp, 0), temp);
// Load value into int64 register.
load64(Address(esp, 0), output);
asMasm().freeStack(2 * sizeof(int32_t));
}
void
MacroAssemblerX86::wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
Label fail, convert;
Register temp = output.high;
// Make sure input fits in (u)int64.
asMasm().reserveStack(2 * sizeof(int32_t));
asMasm().storeDouble(input, Operand(esp, 0));
asMasm().branchDoubleNotInUInt64Range(Address(esp, 0), temp, &fail);
jump(&convert);
// Handle failure in ool.
bind(&fail);
asMasm().freeStack(2 * sizeof(int32_t));
jump(oolEntry);
bind(oolRejoin);
asMasm().reserveStack(2 * sizeof(int32_t));
asMasm().storeDouble(input, Operand(esp, 0));
// Convert the double/float to int64.
bind(&convert);
asMasm().truncateDoubleToUInt64(Address(esp, 0), Address(esp, 0), temp, tempReg);
// Load value into int64 register.
load64(Address(esp, 0), output);
asMasm().freeStack(2 * sizeof(int32_t));
}
void
MacroAssemblerX86::wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempReg)
{
Label fail, convert;
Register temp = output.high;
// Make sure input fits in (u)int64.
asMasm().reserveStack(2 * sizeof(int32_t));
asMasm().storeFloat32(input, Operand(esp, 0));
asMasm().branchFloat32NotInUInt64Range(Address(esp, 0), temp, &fail);
jump(&convert);
// Handle failure in ool.
bind(&fail);
asMasm().freeStack(2 * sizeof(int32_t));
jump(oolEntry);
bind(oolRejoin);
asMasm().reserveStack(2 * sizeof(int32_t));
asMasm().storeFloat32(input, Operand(esp, 0));
// Convert the double/float to int64.
bind(&convert);
asMasm().truncateFloat32ToUInt64(Address(esp, 0), Address(esp, 0), temp, tempReg);
// Load value into int64 register.
load64(Address(esp, 0), output);
asMasm().freeStack(2 * sizeof(int32_t));
}
//}}} check_macroassembler_style

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

@ -856,21 +856,6 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
// Note: this function clobbers the source register.
inline void convertUInt32ToFloat32(Register src, FloatRegister dest);
void convertUInt64ToFloat32(Register64 src, FloatRegister dest, Register temp);
void convertInt64ToFloat32(Register64 src, FloatRegister dest);
static bool convertUInt64ToDoubleNeedsTemp();
void convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp);
void convertInt64ToDouble(Register64 src, FloatRegister dest);
void wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble);
void wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble);
void wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble);
void wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble);
void incrementInt32Value(const Address& addr) {
addl(Imm32(1), payloadOf(addr));
}

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

@ -387,10 +387,12 @@ MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG, 0, JSEXN_TYPEERR, "second argument mu
MSG_DEF(JSMSG_WASM_BAD_IMPORT_FIELD, 1, JSEXN_TYPEERR, "import object field '{0}' is not an Object")
MSG_DEF(JSMSG_WASM_BAD_TABLE_VALUE, 0, JSEXN_TYPEERR, "can only assign WebAssembly exported functions to Table")
MSG_DEF(JSMSG_WASM_BAD_I64_TYPE, 0, JSEXN_TYPEERR, "cannot pass i64 to or from JS")
MSG_DEF(JSMSG_WASM_BAD_GLOBAL_TYPE, 0, JSEXN_TYPEERR, "bad type for a WebAssembly.Global")
MSG_DEF(JSMSG_WASM_NO_TRANSFER, 0, JSEXN_TYPEERR, "cannot transfer WebAssembly/asm.js ArrayBuffer")
MSG_DEF(JSMSG_WASM_STREAM_ERROR, 0, JSEXN_TYPEERR, "stream error during WebAssembly compilation")
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM, 0, JSEXN_TYPEERR, "'shared' is true but maximum is not specified")
MSG_DEF(JSMSG_WASM_GLOBAL_IMMUTABLE, 0, JSEXN_TYPEERR, "can't set value of immutable global")
// Proxy
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
@ -438,6 +440,7 @@ MSG_DEF(JSMSG_SC_NOT_CLONABLE, 1, JSEXN_TYPEERR, "{0} cannot be cloned i
MSG_DEF(JSMSG_SC_SAB_DISABLED, 0, JSEXN_TYPEERR, "SharedArrayBuffer not cloned - shared memory disabled in receiver")
MSG_DEF(JSMSG_SC_SAB_REFCNT_OFLO, 0, JSEXN_TYPEERR, "SharedArrayBuffer has too many references")
MSG_DEF(JSMSG_SC_SHMEM_TRANSFERABLE, 0, JSEXN_TYPEERR, "Shared memory objects must not be in the transfer list")
MSG_DEF(JSMSG_SC_SHMEM_POLICY, 0, JSEXN_TYPEERR, "Policy object must forbid cloning shared memory objects cross-process")
// Debugger
MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null")

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

@ -104,7 +104,8 @@ BEGIN_TEST(testErrorInterceptor)
js::StringBuffer buffer(cx);
CHECK(ValueToStringBuffer(cx, exn, buffer));
CHECK(equalStrings(cx, buffer.finishString(), gLatestMessage));
JS::Rooted<JSFlatString*> flat(cx, buffer.finishString());
CHECK(equalStrings(cx, flat, gLatestMessage));
// Cleanup.
gLatestMessage = nullptr;
@ -127,7 +128,8 @@ BEGIN_TEST(testErrorInterceptor)
js::StringBuffer buffer(cx);
CHECK(ValueToStringBuffer(cx, exn, buffer));
CHECK(js::StringEqualsAscii(buffer.finishString(), TO_STRING[i]));
JS::Rooted<JSFlatString*> flat(cx, buffer.finishString());
CHECK(js::StringEqualsAscii(flat, TO_STRING[i]));
// Cleanup.
gLatestMessage = nullptr;

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

@ -127,6 +127,7 @@ IF_SAB(real,imaginary)(Atomics, InitAtomicsClass, OCLASP(Atomics)) \
imaginary(WasmInstance, dummy, dummy) \
imaginary(WasmMemory, dummy, dummy) \
imaginary(WasmTable, dummy, dummy) \
imaginary(WasmGlobal, dummy, dummy) \
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)

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

@ -272,6 +272,7 @@ UNIFIED_SOURCES += [
'jit/shared/Assembler-shared.cpp',
'jit/shared/BaselineCompiler-shared.cpp',
'jit/shared/CodeGenerator-shared.cpp',
'jit/shared/Disassembler-shared.cpp',
'jit/shared/Lowering-shared.cpp',
'jit/SharedIC.cpp',
'jit/Sink.cpp',
@ -652,6 +653,11 @@ if CONFIG['NIGHTLY_BUILD']:
DEFINES['ENABLE_SIMD'] = True
DEFINES['ENABLE_WASM_SIGNEXTEND_OPS'] = True
DEFINES['ENABLE_WASM_THREAD_OPS'] = True
# An experiment we want to run on Nightly: Can we change the JS
# representation of an exported global from the global's value
# to an instance of WebAssembly.Global without breaking existing
# wasm content?
DEFINES['ENABLE_WASM_GLOBAL'] = True
if CONFIG['JS_BUILD_BINAST']:
# Using SOURCES as UNIFIED_SOURCES causes mysterious bugs on 32-bit platforms.

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

@ -1247,10 +1247,14 @@ JSStructuredCloneWriter::writeSharedArrayBuffer(HandleObject obj)
return false;
}
// We must not transfer buffer pointers cross-process. The cloneDataPolicy
// should guard against this; check that it does.
// We must not transmit SAB pointers (including for WebAssembly.Memory)
// cross-process. The cloneDataPolicy should have guarded against this;
// since it did not then throw, with a very explicit message.
MOZ_RELEASE_ASSERT(scope <= JS::StructuredCloneScope::SameProcessDifferentThread);
if (scope > JS::StructuredCloneScope::SameProcessDifferentThread) {
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SHMEM_POLICY);
return false;
}
Rooted<SharedArrayBufferObject*> sharedArrayBuffer(context(), &CheckedUnwrap(obj)->as<SharedArrayBufferObject>());
SharedArrayRawBuffer* rawbuf = sharedArrayBuffer->rawBufferObject();

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

@ -3743,36 +3743,29 @@ class BaseCompiler final : public BaseCompilerInterface
#ifndef RABALDR_I64_TO_FLOAT_CALLOUT
RegI32 needConvertI64ToFloatTemp(ValType to, bool isUnsigned) {
# if defined(JS_CODEGEN_X86)
bool needs = isUnsigned &&
((to == ValType::F64 && AssemblerX86Shared::HasSSE3()) ||
to == ValType::F32);
# else
bool needs = isUnsigned;
bool needs = false;
if (to == ValType::F64) {
needs = isUnsigned && masm.convertUInt64ToDoubleNeedsTemp();
} else {
# if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
needs = true;
# endif
}
return needs ? needI32() : RegI32::Invalid();
}
void convertI64ToF32(RegI64 src, bool isUnsigned, RegF32 dest, RegI32 temp) {
# if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
if (isUnsigned)
masm.convertUInt64ToFloat32(src, dest, temp);
else
masm.convertInt64ToFloat32(src, dest);
# else
MOZ_CRASH("BaseCompiler platform hook: convertI64ToF32");
# endif
}
void convertI64ToF64(RegI64 src, bool isUnsigned, RegF64 dest, RegI32 temp) {
# if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
if (isUnsigned)
masm.convertUInt64ToDouble(src, dest, temp);
else
masm.convertInt64ToDouble(src, dest);
# else
MOZ_CRASH("BaseCompiler platform hook: convertI64ToF64");
# endif
}
#endif // RABALDR_I64_TO_FLOAT_CALLOUT

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

@ -98,6 +98,61 @@ wasm::HasSupport(JSContext* cx)
return cx->options().wasm() && HasCompilerSupport(cx);
}
bool
wasm::ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v, Val* val)
{
switch (targetType) {
case ValType::I32: {
int32_t i32;
if (!ToInt32(cx, v, &i32))
return false;
*val = Val(uint32_t(i32));
return true;
}
case ValType::F32: {
double d;
if (!ToNumber(cx, v, &d))
return false;
*val = Val(float(d));
return true;
}
case ValType::F64: {
double d;
if (!ToNumber(cx, v, &d))
return false;
*val = Val(d);
return true;
}
default: {
MOZ_CRASH("unexpected import value type, caller must guard");
}
}
}
void
wasm::ToJSValue(const Val& val, MutableHandleValue value)
{
switch (val.type()) {
case ValType::I32: {
value.set(Int32Value(val.i32()));
return;
}
case ValType::F32: {
float f = val.f32();
value.set(DoubleValue(JS::CanonicalizeNaN(double(f))));
return;
}
case ValType::F64: {
double d = val.f64();
value.set(DoubleValue(JS::CanonicalizeNaN(d)));
return;
}
default: {
MOZ_CRASH("unexpected type when translating to a JS value");
}
}
}
// ============================================================================
// Imports
@ -187,42 +242,22 @@ GetImports(JSContext* cx,
const GlobalDesc& global = globals[globalIndex++];
MOZ_ASSERT(global.importIndex() == globalIndex - 1);
MOZ_ASSERT(!global.isMutable());
switch (global.type()) {
case ValType::I32: {
if (!v.isNumber())
return ThrowBadImportType(cx, import.field.get(), "Number");
int32_t i32;
if (!ToInt32(cx, v, &i32))
return false;
val = Val(uint32_t(i32));
break;
}
case ValType::I64: {
#ifdef ENABLE_WASM_GLOBAL
if (v.isObject() && v.toObject().is<WasmGlobalObject>())
v.set(v.toObject().as<WasmGlobalObject>().value());
#endif
if (global.type() == ValType::I64) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
return false;
}
case ValType::F32: {
if (!v.isNumber())
return ThrowBadImportType(cx, import.field.get(), "Number");
double d;
if (!ToNumber(cx, v, &d))
if (!ToWebAssemblyValue(cx, global.type(), v, &val))
return false;
val = Val(float(d));
break;
}
case ValType::F64: {
if (!v.isNumber())
return ThrowBadImportType(cx, import.field.get(), "Number");
double d;
if (!ToNumber(cx, v, &d))
return false;
val = Val(d);
break;
}
default: {
MOZ_CRASH("unexpected import value type");
}
}
if (!globalImports->append(val))
return false;
}
@ -1884,6 +1919,204 @@ WasmTableObject::table() const
return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
}
// ============================================================================
// WebAssembly.global class and methods
#ifdef ENABLE_WASM_GLOBAL
const ClassOps WasmGlobalObject::classOps_ =
{
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* enumerate */
nullptr, /* newEnumerate */
nullptr, /* resolve */
nullptr, /* mayResolve */
nullptr, /* finalize */
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr /* trace */
};
const Class WasmGlobalObject::class_ =
{
"WebAssembly.Global",
JSCLASS_HAS_RESERVED_SLOTS(WasmGlobalObject::RESERVED_SLOTS),
&WasmGlobalObject::classOps_
};
/* static */ WasmGlobalObject*
WasmGlobalObject::create(JSContext* cx, wasm::ValType type, bool isMutable, HandleValue val)
{
RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmGlobal).toObject());
AutoSetNewObjectMetadata metadata(cx);
Rooted<WasmGlobalObject*> obj(cx, NewObjectWithGivenProto<WasmGlobalObject>(cx, proto));
if (!obj)
return nullptr;
obj->initReservedSlot(TYPE_SLOT, Int32Value(int32_t(type)));
obj->initReservedSlot(MUTABLE_SLOT, JS::BooleanValue(isMutable));
obj->initReservedSlot(VALUE_SLOT, val);
return obj;
}
/* static */ bool
WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (!ThrowIfNotConstructing(cx, args, "Global"))
return false;
if (!args.requireAtLeast(cx, "WebAssembly.Global", 1))
return false;
if (!args.get(0).isObject()) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_DESC_ARG, "global");
return false;
}
RootedObject obj(cx, &args[0].toObject());
RootedValue typeVal(cx);
if (!JS_GetProperty(cx, obj, "type", &typeVal))
return false;
RootedString typeStr(cx, ToString(cx, typeVal));
if (!typeStr)
return false;
RootedLinearString typeLinearStr(cx, typeStr->ensureLinear(cx));
if (!typeLinearStr)
return false;
ValType globalType;
if (StringEqualsAscii(typeLinearStr, "i32")) {
globalType = ValType::I32;
} else if (StringEqualsAscii(typeLinearStr, "f32")) {
globalType = ValType::F32;
} else if (StringEqualsAscii(typeLinearStr, "f64")) {
globalType = ValType::F64;
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GLOBAL_TYPE);
return false;
}
RootedValue mutableVal(cx);
if (!JS_GetProperty(cx, obj, "mutable", &mutableVal))
return false;
bool isMutable = ToBoolean(mutableVal);
RootedValue valueVal(cx);
if (!JS_GetProperty(cx, obj, "value", &valueVal))
return false;
Val globalVal;
if (!ToWebAssemblyValue(cx, globalType, valueVal, &globalVal))
return false;
RootedValue globalValue(cx);
ToJSValue(globalVal, &globalValue);
Rooted<WasmGlobalObject*> global(cx, WasmGlobalObject::create(cx, globalType, isMutable,
globalValue));
if (!global)
return false;
args.rval().setObject(*global);
return true;
}
static bool
IsGlobal(HandleValue v)
{
return v.isObject() && v.toObject().is<WasmGlobalObject>();
}
/* static */ bool
WasmGlobalObject::valueGetterImpl(JSContext* cx, const CallArgs& args)
{
switch (args.thisv().toObject().as<WasmGlobalObject>().type()) {
case ValType::I32:
case ValType::F32:
case ValType::F64:
args.rval().set(args.thisv().toObject().as<WasmGlobalObject>().value());
return true;
case ValType::I64:
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_TYPE);
return false;
default:
MOZ_CRASH();
}
}
/* static */ bool
WasmGlobalObject::valueGetter(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<IsGlobal, valueGetterImpl>(cx, args);
}
/* static */ bool
WasmGlobalObject::valueSetterImpl(JSContext* cx, const CallArgs& args)
{
if (!args.thisv().toObject().as<WasmGlobalObject>().isMutable()) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_GLOBAL_IMMUTABLE);
return false;
}
// TODO - implement this, we probably need a different representation for
// mutable globals.
JS_ReportErrorASCII(cx, "Value setter not yet implemented");
return false;
}
/* static */ bool
WasmGlobalObject::valueSetter(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<IsGlobal, valueSetterImpl>(cx, args);
}
const JSPropertySpec WasmGlobalObject::properties[] =
{
JS_PSGS("value", WasmGlobalObject::valueGetter, WasmGlobalObject::valueSetter, 0),
JS_PS_END
};
const JSFunctionSpec WasmGlobalObject::methods[] =
{
JS_SYM_FN(toPrimitive, WasmGlobalObject::valueGetter, 1, JSPROP_READONLY),
JS_FS_END
};
const JSFunctionSpec WasmGlobalObject::static_methods[] =
{ JS_FS_END };
ValType
WasmGlobalObject::type() const
{
return static_cast<ValType>(getReservedSlot(TYPE_SLOT).toInt32());
}
bool
WasmGlobalObject::isMutable() const
{
return getReservedSlot(MUTABLE_SLOT).toBoolean();
}
Value
WasmGlobalObject::value() const
{
return getReservedSlot(VALUE_SLOT);
}
#endif // ENABLE_WASM_GLOBAL
// ============================================================================
// WebAssembly class and static methods
@ -2726,6 +2959,9 @@ js::InitWebAssemblyClass(JSContext* cx, HandleObject obj)
return nullptr;
RootedObject moduleProto(cx), instanceProto(cx), memoryProto(cx), tableProto(cx);
#ifdef ENABLE_WASM_GLOBAL
RootedObject globalProto(cx);
#endif
if (!InitConstructor<WasmModuleObject>(cx, wasm, "Module", &moduleProto))
return nullptr;
if (!InitConstructor<WasmInstanceObject>(cx, wasm, "Instance", &instanceProto))
@ -2734,6 +2970,10 @@ js::InitWebAssemblyClass(JSContext* cx, HandleObject obj)
return nullptr;
if (!InitConstructor<WasmTableObject>(cx, wasm, "Table", &tableProto))
return nullptr;
#ifdef ENABLE_WASM_GLOBAL
if (!InitConstructor<WasmGlobalObject>(cx, wasm, "Global", &globalProto))
return nullptr;
#endif
if (!InitErrorClass(cx, wasm, "CompileError", JSEXN_WASMCOMPILEERROR))
return nullptr;
if (!InitErrorClass(cx, wasm, "LinkError", JSEXN_WASMLINKERROR))
@ -2753,6 +2993,9 @@ js::InitWebAssemblyClass(JSContext* cx, HandleObject obj)
global->setPrototype(JSProto_WasmInstance, ObjectValue(*instanceProto));
global->setPrototype(JSProto_WasmMemory, ObjectValue(*memoryProto));
global->setPrototype(JSProto_WasmTable, ObjectValue(*tableProto));
#ifdef ENABLE_WASM_GLOBAL
global->setPrototype(JSProto_WasmGlobal, ObjectValue(*globalProto));
#endif
global->setConstructor(JSProto_WebAssembly, ObjectValue(*wasm));
MOZ_ASSERT(global->isStandardClassResolved(JSProto_WebAssembly));

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

@ -43,6 +43,15 @@ HasCompilerSupport(JSContext* cx);
bool
HasSupport(JSContext* cx);
// ToWebAssemblyValue and ToJSValue are conversion functions defined in
// the Wasm JS API spec.
bool
ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v, Val* val);
void
ToJSValue(const Val& val, MutableHandleValue v);
// Compiles the given binary wasm module given the ArrayBufferObject
// and links the module's imports with the given import object.
@ -275,6 +284,42 @@ class WasmTableObject : public NativeObject
wasm::Table& table() const;
};
#ifdef ENABLE_WASM_GLOBAL
// The class of WebAssembly.Global. A WasmGlobalObject holds either the value
// of an immutable wasm global or the cell of a mutable wasm global.
class WasmGlobalObject : public NativeObject
{
static const unsigned TYPE_SLOT = 0;
static const unsigned MUTABLE_SLOT = 1;
static const unsigned VALUE_SLOT = 2;
static const ClassOps classOps_;
static bool valueGetterImpl(JSContext* cx, const CallArgs& args);
static bool valueGetter(JSContext* cx, unsigned argc, Value* vp);
static bool valueSetterImpl(JSContext* cx, const CallArgs& args);
static bool valueSetter(JSContext* cx, unsigned argc, Value* vp);
public:
static const unsigned RESERVED_SLOTS = 3;
static const Class class_;
static const JSPropertySpec properties[];
static const JSFunctionSpec methods[];
static const JSFunctionSpec static_methods[];
static bool construct(JSContext*, unsigned, Value*);
static WasmGlobalObject* create(JSContext* cx, wasm::ValType type, bool isMutable,
HandleValue value);
wasm::ValType type() const;
bool isMutable() const;
Value value() const;
};
#endif // ENABLE_WASM_GLOBAL
} // namespace js
#endif // wasm_js_h

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

@ -1052,31 +1052,22 @@ GetGlobalExport(JSContext* cx, const GlobalDescVector& globals, uint32_t globalI
}
}
switch (global.type()) {
case ValType::I32: {
jsval.set(Int32Value(val.i32()));
return true;
}
case ValType::I64: {
if (val.type() == ValType::I64) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
return false;
}
case ValType::F32: {
float f = val.f32();
jsval.set(DoubleValue(JS::CanonicalizeNaN(double(f))));
ToJSValue(val, jsval);
#ifdef ENABLE_WASM_GLOBAL
Rooted<WasmGlobalObject*> go(cx, WasmGlobalObject::create(cx, ValType::I32, false, jsval));
if (!go)
return false;
jsval.setObject(*go);
#endif
return true;
}
case ValType::F64: {
double d = val.f64();
jsval.set(DoubleValue(JS::CanonicalizeNaN(d)));
return true;
}
default: {
break;
}
}
MOZ_CRASH("unexpected type when creating global exports");
}
static bool
CreateExportObject(JSContext* cx,

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

@ -1365,6 +1365,8 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import
Label throwLabel;
JitSpew(JitSpew_Codegen, "# Emitting wasm import stubs");
for (uint32_t funcIndex = 0; funcIndex < imports.length(); funcIndex++) {
const FuncImport& fi = imports[funcIndex];
@ -1381,6 +1383,8 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import
return false;
}
JitSpew(JitSpew_Codegen, "# Emitting wasm export stubs");
for (const FuncExport& fe : exports) {
Offsets offsets;
if (!GenerateInterpEntry(masm, fe, &offsets))
@ -1389,6 +1393,8 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import
return false;
}
JitSpew(JitSpew_Codegen, "# Emitting wasm trap stubs");
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
switch (trap) {
case Trap::Unreachable:
@ -1418,6 +1424,8 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import
Offsets offsets;
JitSpew(JitSpew_Codegen, "# Emitting wasm exit stubs");
if (!GenerateOutOfBoundsExit(masm, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(CodeRange::OutOfBoundsExit, offsets))

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

@ -321,6 +321,10 @@ this.XPCOMUtils = {
aObject, aName, aResource, aSymbol,
aPreLambda, aPostLambda, aProxy)
{
if (arguments.length == 3) {
return ChromeUtils.defineModuleGetter(aObject, aName, aResource);
}
let proxy = aProxy || {};
if (typeof(aPreLambda) === "function") {
@ -358,7 +362,7 @@ this.XPCOMUtils = {
aObject, aModules)
{
for (let [name, module] of Object.entries(aModules)) {
this.defineLazyModuleGetter(aObject, name, module);
ChromeUtils.defineModuleGetter(aObject, name, module);
}
},

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

@ -52,6 +52,7 @@
#include "mozilla/ScriptPreloader.h"
#include "mozilla/dom/DOMPrefs.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/Unused.h"
@ -180,25 +181,6 @@ ReportOnCallerUTF8(JSContext* callerContext,
return NS_OK;
}
static nsresult
MOZ_FORMAT_PRINTF(2, 3)
ReportOnCallerUTF8(JSCLContextHelper& helper,
const char* format, ...)
{
va_list ap;
va_start(ap, format);
UniqueChars buf = JS_vsmprintf(format, ap);
if (!buf) {
va_end(ap);
return NS_ERROR_OUT_OF_MEMORY;
}
helper.reportErrorAfterPop(Move(buf));
va_end(ap);
return NS_OK;
}
mozJSComponentLoader::mozJSComponentLoader()
: mModules(16),
mImports(16),
@ -287,6 +269,25 @@ class MOZ_STACK_CLASS ComponentLoaderInfo {
nsCOMPtr<nsIURI> mResolvedURI;
};
template <typename ...Args>
static nsresult
ReportOnCallerUTF8(JSCLContextHelper& helper,
const char* format,
ComponentLoaderInfo& info,
Args... args)
{
nsCString location;
MOZ_TRY(info.GetLocation(location));
UniqueChars buf = JS_smprintf(format, location.get(), args...);
if (!buf) {
return NS_ERROR_OUT_OF_MEMORY;
}
helper.reportErrorAfterPop(Move(buf));
return NS_ERROR_FAILURE;
}
#undef BEGIN_ENSURE
#undef ENSURE_DEPS
#undef ENSURE_DEP
@ -928,7 +929,7 @@ mozJSComponentLoader::UnloadModules()
}
nsresult
mozJSComponentLoader::Import(const nsACString& registryLocation,
mozJSComponentLoader::ImportInto(const nsACString& registryLocation,
HandleValue targetValArg,
JSContext* cx,
uint8_t optionalArgc,
@ -1097,11 +1098,153 @@ ResolveModuleObjectPropertyById(JSContext* aCx, HandleObject aModObj, HandleId i
nsresult
mozJSComponentLoader::ImportInto(const nsACString& aLocation,
HandleObject targetObj,
JSContext* callercx,
JSContext* cx,
MutableHandleObject vp)
{
vp.set(nullptr);
JS::RootedObject exports(cx);
MOZ_TRY(Import(cx, aLocation, vp, &exports, !targetObj));
if (targetObj) {
JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx));
if (!JS_Enumerate(cx, exports, &ids)) {
return NS_ERROR_OUT_OF_MEMORY;
}
JS::RootedValue value(cx);
JS::RootedId id(cx);
for (jsid idVal : ids) {
id = idVal;
if (!JS_GetPropertyById(cx, exports, id, &value) ||
!JS_SetPropertyById(cx, targetObj, id, value)) {
return NS_ERROR_FAILURE;
}
}
}
return NS_OK;
}
nsresult
mozJSComponentLoader::ExtractExports(JSContext* aCx, ComponentLoaderInfo& aInfo,
ModuleEntry* aMod,
JS::MutableHandleObject aExports)
{
// cxhelper must be created before jsapi, so that jsapi is destroyed and
// pops any context it has pushed before we report to the caller context.
JSCLContextHelper cxhelper(aCx);
// Even though we are calling JS_SetPropertyById on targetObj, we want
// to ensure that we never run script here, so we use an AutoJSAPI and
// not an AutoEntryScript.
dom::AutoJSAPI jsapi;
jsapi.Init();
JSContext* cx = jsapi.cx();
JSAutoCompartment ac(cx, aMod->obj);
RootedValue symbols(cx);
{
RootedObject obj(cx, ResolveModuleObjectProperty(cx, aMod->obj,
"EXPORTED_SYMBOLS"));
if (!obj || !JS_GetProperty(cx, obj, "EXPORTED_SYMBOLS", &symbols)) {
return ReportOnCallerUTF8(cxhelper, ERROR_NOT_PRESENT, aInfo);
}
}
bool isArray;
if (!JS_IsArrayObject(cx, symbols, &isArray)) {
return NS_ERROR_FAILURE;
}
if (!isArray) {
return ReportOnCallerUTF8(cxhelper, ERROR_NOT_AN_ARRAY, aInfo);
}
RootedObject symbolsObj(cx, &symbols.toObject());
// Iterate over symbols array, installing symbols on targetObj:
uint32_t symbolCount = 0;
if (!JS_GetArrayLength(cx, symbolsObj, &symbolCount)) {
return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_ARRAY_LENGTH,
aInfo);
}
#ifdef DEBUG
nsAutoCString logBuffer;
#endif
aExports.set(JS_NewPlainObject(cx));
if (!aExports) {
return NS_ERROR_OUT_OF_MEMORY;
}
bool missing = false;
RootedValue value(cx);
RootedId symbolId(cx);
RootedObject symbolHolder(cx);
for (uint32_t i = 0; i < symbolCount; ++i) {
if (!JS_GetElement(cx, symbolsObj, i, &value) ||
!value.isString() ||
!JS_ValueToId(cx, value, &symbolId)) {
return ReportOnCallerUTF8(cxhelper, ERROR_ARRAY_ELEMENT, aInfo, i);
}
symbolHolder = ResolveModuleObjectPropertyById(cx, aMod->obj, symbolId);
if (!symbolHolder ||
!JS_GetPropertyById(cx, symbolHolder, symbolId, &value)) {
JSAutoByteString bytes;
RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
if (!bytes.encodeUtf8(cx, symbolStr))
return NS_ERROR_FAILURE;
return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
aInfo, bytes.ptr());
}
if (value.isUndefined()) {
missing = true;
}
if (!JS_SetPropertyById(cx, aExports, symbolId, value)) {
JSAutoByteString bytes;
RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
if (!bytes.encodeUtf8(cx, symbolStr))
return NS_ERROR_FAILURE;
return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
aInfo, bytes.ptr());
}
#ifdef DEBUG
if (i == 0) {
logBuffer.AssignLiteral("Installing symbols [ ");
}
JSAutoByteString bytes(cx, JSID_TO_STRING(symbolId));
if (!!bytes)
logBuffer.Append(bytes.ptr());
logBuffer.Append(' ');
if (i == symbolCount - 1) {
nsCString location;
MOZ_TRY(aInfo.GetLocation(location));
LOG(("%s] from %s\n", logBuffer.get(), location.get()));
}
#endif
}
// Don't cache the exports object if any of its exported symbols are
// missing. If the module hasn't finished loading yet, they may be
// defined the next time we try to import it.
if (!missing) {
aMod->exports = aExports;
}
return NS_OK;
}
nsresult
mozJSComponentLoader::Import(JSContext* aCx, const nsACString& aLocation,
JS::MutableHandleObject aModuleGlobal,
JS::MutableHandleObject aModuleExports,
bool aIgnoreExports)
{
nsresult rv;
if (!mInitialized) {
rv = ReallyInit();
@ -1116,7 +1259,7 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation,
ModuleEntry* mod;
nsAutoPtr<ModuleEntry> newEntry;
if (!mImports.Get(info.Key(), &mod) && !mInProgressImports.Get(info.Key(), &mod)) {
newEntry = new ModuleEntry(RootingContext::get(callercx));
newEntry = new ModuleEntry(RootingContext::get(aCx));
if (!newEntry)
return NS_ERROR_OUT_OF_MEMORY;
@ -1157,7 +1300,7 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation,
rv = info.EnsureURI();
NS_ENSURE_SUCCESS(rv, rv);
RootedValue exception(callercx);
RootedValue exception(aCx);
rv = ObjectForLocation(info, sourceFile, &newEntry->obj,
&newEntry->thisObjectKey,
&newEntry->location, true, &exception);
@ -1168,10 +1311,10 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation,
if (!exception.isUndefined()) {
// An exception was thrown during compilation. Propagate it
// out to our caller so they can report it.
if (!JS_WrapValue(callercx, &exception))
if (!JS_WrapValue(aCx, &exception))
return NS_ERROR_OUT_OF_MEMORY;
JS_SetPendingException(callercx, exception);
return NS_OK;
JS_SetPendingException(aCx, exception);
return NS_ERROR_FAILURE;
}
// Something failed, but we don't know what it is, guess.
@ -1181,7 +1324,7 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation,
#ifdef STARTUP_RECORDER_ENABLED
if (Preferences::GetBool("browser.startup.record", false)) {
newEntry->importStack =
xpc_PrintJSStack(callercx, false, false, false).get();
xpc_PrintJSStack(aCx, false, false, false).get();
}
#endif
@ -1189,124 +1332,17 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation,
}
MOZ_ASSERT(mod->obj, "Import table contains entry with no object");
vp.set(mod->obj);
aModuleGlobal.set(mod->obj);
if (targetObj) {
// cxhelper must be created before jsapi, so that jsapi is destroyed and
// pops any context it has pushed before we report to the caller context.
JSCLContextHelper cxhelper(callercx);
// Even though we are calling JS_SetPropertyById on targetObj, we want
// to ensure that we never run script here, so we use an AutoJSAPI and
// not an AutoEntryScript.
dom::AutoJSAPI jsapi;
jsapi.Init();
JSContext* cx = jsapi.cx();
JSAutoCompartment ac(cx, mod->obj);
RootedValue symbols(cx);
RootedObject exportedSymbolsHolder(cx, ResolveModuleObjectProperty(cx, mod->obj,
"EXPORTED_SYMBOLS"));
if (!exportedSymbolsHolder ||
!JS_GetProperty(cx, exportedSymbolsHolder,
"EXPORTED_SYMBOLS", &symbols)) {
nsCString location;
rv = info.GetLocation(location);
NS_ENSURE_SUCCESS(rv, rv);
return ReportOnCallerUTF8(cxhelper, ERROR_NOT_PRESENT,
location.get());
JS::RootedObject exports(aCx, mod->exports);
if (!exports && !aIgnoreExports) {
MOZ_TRY(ExtractExports(aCx, info, mod, &exports));
}
bool isArray;
if (!JS_IsArrayObject(cx, symbols, &isArray)) {
if (exports && !JS_WrapObject(aCx, &exports)) {
return NS_ERROR_FAILURE;
}
if (!isArray) {
nsCString location;
rv = info.GetLocation(location);
NS_ENSURE_SUCCESS(rv, rv);
return ReportOnCallerUTF8(cxhelper, ERROR_NOT_AN_ARRAY,
location.get());
}
RootedObject symbolsObj(cx, &symbols.toObject());
// Iterate over symbols array, installing symbols on targetObj:
uint32_t symbolCount = 0;
if (!JS_GetArrayLength(cx, symbolsObj, &symbolCount)) {
nsCString location;
rv = info.GetLocation(location);
NS_ENSURE_SUCCESS(rv, rv);
return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_ARRAY_LENGTH,
location.get());
}
#ifdef DEBUG
nsAutoCString logBuffer;
#endif
RootedValue value(cx);
RootedId symbolId(cx);
RootedObject symbolHolder(cx);
for (uint32_t i = 0; i < symbolCount; ++i) {
if (!JS_GetElement(cx, symbolsObj, i, &value) ||
!value.isString() ||
!JS_ValueToId(cx, value, &symbolId)) {
nsCString location;
rv = info.GetLocation(location);
NS_ENSURE_SUCCESS(rv, rv);
return ReportOnCallerUTF8(cxhelper, ERROR_ARRAY_ELEMENT,
location.get(), i);
}
symbolHolder = ResolveModuleObjectPropertyById(cx, mod->obj, symbolId);
if (!symbolHolder ||
!JS_GetPropertyById(cx, symbolHolder, symbolId, &value)) {
JSAutoByteString bytes;
RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
if (!bytes.encodeUtf8(cx, symbolStr))
return NS_ERROR_FAILURE;
nsCString location;
rv = info.GetLocation(location);
NS_ENSURE_SUCCESS(rv, rv);
return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
location.get(), bytes.ptr());
}
JSAutoCompartment target_ac(cx, targetObj);
JS_MarkCrossZoneId(cx, symbolId);
if (!JS_WrapValue(cx, &value) ||
!JS_SetPropertyById(cx, targetObj, symbolId, value)) {
JSAutoByteString bytes;
RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
if (!bytes.encodeUtf8(cx, symbolStr))
return NS_ERROR_FAILURE;
nsCString location;
rv = info.GetLocation(location);
NS_ENSURE_SUCCESS(rv, rv);
return ReportOnCallerUTF8(cxhelper, ERROR_SETTING_SYMBOL,
location.get(), bytes.ptr());
}
#ifdef DEBUG
if (i == 0) {
logBuffer.AssignLiteral("Installing symbols [ ");
}
JSAutoByteString bytes(cx, JSID_TO_STRING(symbolId));
if (!!bytes)
logBuffer.Append(bytes.ptr());
logBuffer.Append(' ');
if (i == symbolCount - 1) {
nsCString location;
rv = info.GetLocation(location);
NS_ENSURE_SUCCESS(rv, rv);
LOG(("%s] from %s\n", logBuffer.get(), location.get()));
}
#endif
}
}
aModuleExports.set(exports);
// Cache this module for later
if (newEntry) {

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

@ -62,8 +62,14 @@ class mozJSComponentLoader final : public mozilla::ModuleLoader,
static mozJSComponentLoader* Get() { return sSelf; }
nsresult Import(const nsACString& aResourceURI, JS::HandleValue aTargetObj,
nsresult ImportInto(const nsACString& aResourceURI, JS::HandleValue aTargetObj,
JSContext* aCx, uint8_t aArgc, JS::MutableHandleValue aRetval);
nsresult Import(JSContext* aCx, const nsACString& aResourceURI,
JS::MutableHandleObject aModuleGlobal,
JS::MutableHandleObject aModuleExports,
bool aIgnoreExports = false);
nsresult Unload(const nsACString& aResourceURI);
nsresult IsModuleLoaded(const nsACString& aResourceURI, bool* aRetval);
bool IsLoaderGlobal(JSObject* aObj) {
@ -124,7 +130,8 @@ class mozJSComponentLoader final : public mozilla::ModuleLoader,
{
public:
explicit ModuleEntry(JS::RootingContext* aRootingCx)
: mozilla::Module(), obj(aRootingCx), thisObjectKey(aRootingCx)
: mozilla::Module(), obj(aRootingCx), exports(aRootingCx),
thisObjectKey(aRootingCx)
{
mVersion = mozilla::Module::kVersion;
mCIDs = nullptr;
@ -174,6 +181,7 @@ class mozJSComponentLoader final : public mozilla::ModuleLoader,
nsCOMPtr<xpcIJSGetFactory> getfactoryobj;
JS::PersistentRootedObject obj;
JS::PersistentRootedObject exports;
JS::PersistentRootedScript thisObjectKey;
char* location;
nsCString resolvedURL;
@ -182,6 +190,10 @@ class mozJSComponentLoader final : public mozilla::ModuleLoader,
#endif
};
nsresult ExtractExports(JSContext* aCx, ComponentLoaderInfo& aInfo,
ModuleEntry* aMod,
JS::MutableHandleObject aExports);
static size_t DataEntrySizeOfExcludingThis(const nsACString& aKey, ModuleEntry* const& aData,
mozilla::MallocSizeOf aMallocSizeOf, void* arg);
static size_t ClassEntrySizeOfExcludingThis(const nsACString& aKey,

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

@ -2294,7 +2294,7 @@ nsXPCComponents_Utils::Import(const nsACString& registryLocation,
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
"nsXPCComponents_Utils::Import", OTHER, registryLocation);
return moduleloader->Import(registryLocation, targetObj, cx, optionalArgc, retval);
return moduleloader->ImportInto(registryLocation, targetObj, cx, optionalArgc, retval);
}
NS_IMETHODIMP

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

@ -0,0 +1,115 @@
"use strict";
function assertIsGetter(obj, prop) {
let desc = Object.getOwnPropertyDescriptor(obj, prop);
ok(desc, `Property ${prop} exists on object`);
equal(typeof desc.get, "function", `Getter function exists for property ${prop}`);
equal(typeof desc.set, "function", `Setter function exists for property ${prop}`);
equal(desc.enumerable, true, `Property ${prop} is enumerable`);
equal(desc.configurable, true, `Property ${prop} is configurable`);
}
function assertIsValue(obj, prop, value) {
let desc = Object.getOwnPropertyDescriptor(obj, prop);
ok(desc, `Property ${prop} exists on object`);
ok("value" in desc, `${prop} is a data property`);
equal(desc.value, value, `${prop} has the expected value`);
equal(desc.enumerable, true, `Property ${prop} is enumerable`);
equal(desc.configurable, true, `Property ${prop} is configurable`);
equal(desc.writable, true, `Property ${prop} is writable`);
}
add_task(async function() {
let temp = {};
ChromeUtils.import("resource://gre/modules/Services.jsm", temp);
let obj = {};
let child = Object.create(obj);
let sealed = Object.seal(Object.create(obj));
// Test valid import
ChromeUtils.defineModuleGetter(obj, "Services",
"resource://gre/modules/Services.jsm");
assertIsGetter(obj, "Services");
equal(child.Services, temp.Services, "Getter works on descendent object");
assertIsValue(child, "Services", temp.Services);
assertIsGetter(obj, "Services");
Assert.throws(() => sealed.Services, /Object is not extensible/,
"Cannot access lazy getter from sealed object");
Assert.throws(() => sealed.Services = null, /Object is not extensible/,
"Cannot access lazy setter from sealed object");
assertIsGetter(obj, "Services");
equal(obj.Services, temp.Services, "Getter works on object");
assertIsValue(obj, "Services", temp.Services);
// Test overwriting via setter
child = Object.create(obj);
ChromeUtils.defineModuleGetter(obj, "Services",
"resource://gre/modules/Services.jsm");
assertIsGetter(obj, "Services");
child.Services = "foo";
assertIsValue(child, "Services", "foo");
assertIsGetter(obj, "Services");
obj.Services = "foo";
assertIsValue(obj, "Services", "foo");
// Test import missing property
ChromeUtils.defineModuleGetter(obj, "meh",
"resource://gre/modules/Services.jsm");
assertIsGetter(obj, "meh");
equal(obj.meh, undefined, "Missing property returns undefined");
assertIsValue(obj, "meh", undefined);
// Test import broken module
ChromeUtils.defineModuleGetter(obj, "broken",
"resource://test/bogus_exports_type.jsm");
assertIsGetter(obj, "broken");
let errorPattern = /EXPORTED_SYMBOLS is not an array/;
Assert.throws(() => child.broken, errorPattern,
"Broken import throws on child");
Assert.throws(() => child.broken, errorPattern,
"Broken import throws on child again");
Assert.throws(() => sealed.broken, errorPattern,
"Broken import throws on sealed child");
Assert.throws(() => obj.broken, errorPattern,
"Broken import throws on object");
assertIsGetter(obj, "broken");
// Test import missing module
ChromeUtils.defineModuleGetter(obj, "missing",
"resource://test/does_not_exist.jsm");
assertIsGetter(obj, "missing");
Assert.throws(() => obj.missing, /NS_ERROR_FILE_NOT_FOUND/,
"missing import throws on object");
assertIsGetter(obj, "missing");
// Test overwriting broken import via setter
assertIsGetter(obj, "broken");
obj.broken = "foo";
assertIsValue(obj, "broken", "foo");
});

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

@ -67,6 +67,7 @@ support-files =
[test_classesByID_instanceof.js]
[test_compileScript.js]
[test_deepFreezeClone.js]
[test_defineModuleGetter.js]
[test_file.js]
[test_blob.js]
[test_blob2.js]

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

@ -312,7 +312,7 @@ const chromeScript = SpecialPowers.loadChromeScript(_ => {
addMessageListener("testProfileLoad", testProfileLoad);
addMessageListener("set-overrides", function(overrides) { OVERRIDES = overrides});
addMessageListener("notify-on-update", () => { _notifyOnUpdate = true });
}, { wantGlobalProperties: ["TextEncoder", "TextDecoder"]});
}, { wantGlobalProperties: ["ChromeUtils", "TextEncoder", "TextDecoder"]});
chromeScript.addMessageListener("testProfileSaveDone", SimpleTest.finish);
chromeScript.addMessageListener("testProfileLoadDone", function() {

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

@ -15,7 +15,7 @@ transforms = TransformSequence()
@transforms.add
def add_release_eta(config, jobs):
for job in jobs:
if config.params['release_eta'] != '':
if config.params['release_eta']:
job['run']['release-eta'] = config.params['release_eta']
yield job

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

@ -40,7 +40,7 @@ function endOfFirstTest() {
assert.equal(typeof CSS, "undefined", "CSS is not defined");
sendAsyncMessage("valid-assert-done");
});
}, { wantGlobalProperties: ["XMLHttpRequest"] });
}, { wantGlobalProperties: ["ChromeUtils", "XMLHttpRequest"] });
script2.sendAsyncMessage("valid-assert");
script2.addMessageListener("valid-assert-done", endOfTest);

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

@ -485,10 +485,8 @@ SpecialPowersObserverAPI.prototype = {
// and addMessageListener in order to communicate with
// the mochitest.
let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
let sandboxOptions = aMessage.json.sandboxOptions;
if (!sandboxOptions) {
sandboxOptions = {};
}
let sandboxOptions = Object.assign({wantGlobalProperties: ["ChromeUtils"]},
aMessage.json.sandboxOptions);
let sb = Components.utils.Sandbox(systemPrincipal, sandboxOptions);
let mm = aMessage.target.frameLoader
.messageManager;

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

@ -222,6 +222,15 @@ function get(instance, name) {
if (instance.isError())
return instance;
// Experimental API change. We try to export WebAssembly.Global instances,
// not primitive values. In that case the Number() cast is necessary here
// to convert the Global to a value: the harness examines types carefully
// and will not trigger the @@toPrimitive hook on Global, unlike most user
// code.
if (typeof WebAssembly.Global === "function")
return ValueResult(Number(instance.value.exports[name]));
return ValueResult(instance.value.exports[name]);
}

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

@ -136,6 +136,7 @@ var ExtensionAPIs = {
wantXrays: false,
sandboxName: script,
addonId,
wantGlobalProperties: ["ChromeUtils"],
metadata: {addonID: addonId},
});
@ -1326,12 +1327,12 @@ class SchemaAPIManager extends EventEmitter {
_createExtGlobal() {
let global = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), {
wantXrays: false,
wantGlobalProperties: ["ChromeUtils"],
sandboxName: `Namespace of ext-*.js scripts for ${this.processType} (from: resource://gre/modules/ExtensionCommon.jsm)`,
});
Object.assign(global, {
Cc,
ChromeUtils,
ChromeWorker,
Ci,
Cr,

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

@ -300,7 +300,7 @@ function loadLazyObjects() {
let uri = "resource://gre/modules/addons/XPIProviderUtils.js";
let scope = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), {
sandboxName: uri,
wantGlobalProperties: ["TextDecoder"],
wantGlobalProperties: ["ChromeUtils", "TextDecoder"],
});
Object.assign(scope, {
@ -4237,6 +4237,7 @@ this.XPIProvider = {
activeAddon.bootstrapScope =
new Cu.Sandbox(principal, { sandboxName: aFile.path,
addonId: aId,
wantGlobalProperties: ["ChromeUtils"],
metadata: { addonID: aId } });
logger.error("Attempted to load bootstrap scope from missing directory " + aFile.path);
return;
@ -4256,6 +4257,7 @@ this.XPIProvider = {
activeAddon.bootstrapScope =
new Cu.Sandbox(principal, { sandboxName: uri,
addonId: aId,
wantGlobalProperties: ["ChromeUtils"],
metadata: { addonID: aId, URI: uri } });
try {

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

@ -23,9 +23,8 @@ module.exports = function(context) {
if (node.callee.type === "MemberExpression") {
let memexp = node.callee;
if (memexp.object.type === "Identifier" &&
// Only Cu and ChromeUtils, not Components.utils; see bug 1230369.
(memexp.object.name === "Cu" ||
memexp.object.name === "ChromeUtils") &&
// Only Cu, not ChromeUtils or Components.utils; see bug 1230369.
memexp.object.name === "Cu" &&
memexp.property.type === "Identifier" &&
memexp.property.name === "import" &&
node.arguments.length === 1) {