зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
062e1cf551
|
@ -402,8 +402,8 @@ ChromeUtils::Import(const GlobalObject& aGlobal,
|
|||
}
|
||||
|
||||
JS::Rooted<JS::Value> retval(cx);
|
||||
nsresult rv = moduleloader->Import(registryLocation, targetObj, cx,
|
||||
optionalArgc, &retval);
|
||||
nsresult rv = moduleloader->ImportInto(registryLocation, targetObj, cx,
|
||||
optionalArgc, &retval);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return;
|
||||
|
@ -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,7 +119,12 @@ module = wasmEvalText(`(module
|
|||
)`, { globals: {x: 42} }).exports;
|
||||
|
||||
assertEq(module.getter(), 42);
|
||||
assertEq(module.value, 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).
|
||||
module = new WebAssembly.Module(wasmTextToBinary(`(module
|
||||
|
@ -174,8 +179,14 @@ module = wasmEvalText(`(module
|
|||
(export "defined" global 1)
|
||||
)`, { globals: {x: 42} }).exports;
|
||||
|
||||
assertEq(module.imported, 42);
|
||||
assertEq(module.defined, 1337);
|
||||
// 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,12 +223,20 @@ function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = asse
|
|||
|
||||
assertFunc(module.get0(), coercion(initialValue));
|
||||
assertFunc(module.get1(), coercion(initialValue));
|
||||
assertFunc(module.global_imm, 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));
|
||||
assertFunc(module.global_imm, 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,13 +1946,16 @@ 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,
|
||||
/* loadToPC = */ true);
|
||||
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.
|
||||
if (label->bound()) {
|
||||
|
@ -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,22 +1250,27 @@ 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,
|
||||
bool loadToPC = false);
|
||||
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) {
|
||||
return m_buffer.getInst(bo);
|
||||
}
|
||||
|
||||
#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,8 +94,8 @@ 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,
|
||||
literalAddr);
|
||||
return allocLiteralLoadEntry(numInst, numPoolEntries, (uint8_t*)&instructionScratch,
|
||||
literalAddr);
|
||||
}
|
||||
|
||||
BufferOffset
|
||||
|
@ -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,8 +120,8 @@ 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,
|
||||
literalAddr);
|
||||
return allocLiteralLoadEntry(numInst, numPoolEntries, (uint8_t*)&instructionScratch,
|
||||
literalAddr);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -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(¬Taken, Assembler::InvertCondition(cond));
|
||||
branch_bo = b(-1);
|
||||
branch_bo = b(-1, doc);
|
||||
bind(¬Taken);
|
||||
} 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");
|
||||
}
|
||||
|
|
|
@ -1705,8 +1705,8 @@ void Assembler::LoadStoreStruct(const VRegister& vt,
|
|||
|
||||
|
||||
void Assembler::LoadStoreStructSingleAllLanes(const VRegister& vt,
|
||||
const MemOperand& addr,
|
||||
NEONLoadStoreSingleStructOp op) {
|
||||
const MemOperand& addr,
|
||||
NEONLoadStoreSingleStructOp op) {
|
||||
LoadStoreStructVerify(vt, addr, op);
|
||||
Emit(op | LoadStoreStructAddrModeField(addr) | LSVFormat(vt) | Rt(vt));
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
VIXL_ASSERT(buffer_pos_ < buffer_size_);
|
||||
buffer_[buffer_pos_++] = ' ';
|
||||
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,
|
||||
uint8_t* inst, uint8_t* data,
|
||||
ARMBuffer::PoolEntry* pe = nullptr)
|
||||
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), ¬Negative);
|
||||
double add_constant = 18446744073709551616.0; // 2^64
|
||||
store64(Imm64(mozilla::BitwiseCast<uint64_t>(add_constant)), Address(esp, 0));
|
||||
fld(Operand(esp, 0));
|
||||
faddp();
|
||||
bind(¬Negative);
|
||||
|
||||
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), ¬Negative);
|
||||
double add_constant = 18446744073709551616.0; // 2^64
|
||||
store64(Imm64(mozilla::BitwiseCast<uint64_t>(add_constant)), Address(esp, 0));
|
||||
fld(Operand(esp, 0));
|
||||
faddp();
|
||||
bind(¬Negative);
|
||||
|
||||
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), ¬Negative);
|
||||
branch32(Assembler::NotSigned, input.high, Imm32(0), ¬Negative);
|
||||
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))
|
||||
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 (!v.isNumber())
|
||||
return ThrowBadImportType(cx, import.field.get(), "Number");
|
||||
|
||||
if (!ToWebAssemblyValue(cx, global.type(), v, &val))
|
||||
return false;
|
||||
|
||||
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,30 +1052,21 @@ 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))));
|
||||
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");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
|
@ -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,11 +929,11 @@ mozJSComponentLoader::UnloadModules()
|
|||
}
|
||||
|
||||
nsresult
|
||||
mozJSComponentLoader::Import(const nsACString& registryLocation,
|
||||
HandleValue targetValArg,
|
||||
JSContext* cx,
|
||||
uint8_t optionalArgc,
|
||||
MutableHandleValue retval)
|
||||
mozJSComponentLoader::ImportInto(const nsACString& registryLocation,
|
||||
HandleValue targetValArg,
|
||||
JSContext* cx,
|
||||
uint8_t optionalArgc,
|
||||
MutableHandleValue retval)
|
||||
{
|
||||
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
|
||||
|
@ -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,125 +1332,18 @@ 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());
|
||||
}
|
||||
|
||||
bool isArray;
|
||||
if (!JS_IsArrayObject(cx, symbols, &isArray)) {
|
||||
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
|
||||
}
|
||||
JS::RootedObject exports(aCx, mod->exports);
|
||||
if (!exports && !aIgnoreExports) {
|
||||
MOZ_TRY(ExtractExports(aCx, info, mod, &exports));
|
||||
}
|
||||
|
||||
if (exports && !JS_WrapObject(aCx, &exports)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
aModuleExports.set(exports);
|
||||
|
||||
// Cache this module for later
|
||||
if (newEntry) {
|
||||
mImports.Put(info.Key(), newEntry);
|
||||
|
|
|
@ -62,8 +62,14 @@ class mozJSComponentLoader final : public mozilla::ModuleLoader,
|
|||
|
||||
static mozJSComponentLoader* Get() { return sSelf; }
|
||||
|
||||
nsresult Import(const nsACString& aResourceURI, JS::HandleValue aTargetObj,
|
||||
JSContext* aCx, uint8_t aArgc, JS::MutableHandleValue aRetval);
|
||||
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) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче