Bug 1530937 part 2 - Add new VMFunction mechanism and use it for some Baseline callVMs. r=nbp,tcampbell

We are doing this to:

1) Eliminate (hundreds of) static constructors. These account for a significant
   fraction of all remaining static constructors in Gecko.

2) Use constexpr for VMFunction data. This was not possible with the linked list
   but the new design stores all data in a constexpr array. This will save a few
   KB per process.

3) Make it easier to define a new VMFunction.

4) Coalesce duplicate VMFunction copies in Baseline/Ion/ICs.

5) Get rid of the (read-only) HashMap for the VMFunction => code lookup. We can
   use a Vector instead.

6) Make it easier in the future to generate the wrappers at compile time.

This patch will let us incrementally convert the remaining VM functions. The
only thing not handled by this patch is support for the TailCall and
extraValuesToPop fields. We can do this when we convert the Baseline IC code
that uses these fields.

Once all VM functions have been converted we can remove and simplify more code.

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jan de Mooij 2019-02-28 12:31:01 +00:00
Родитель 7e8bf994ed
Коммит b4faab5772
17 изменённых файлов: 335 добавлений и 114 удалений

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

@ -31,6 +31,7 @@
#include "jit/BaselineFrameInfo-inl.h"
#include "jit/MacroAssembler-inl.h"
#include "jit/VMFunctionList-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/NativeObject-inl.h"
@ -584,10 +585,8 @@ void BaselineInterpreterCodeGen::storeFrameSizeAndPushDescriptor(
}
template <typename Handler>
bool BaselineCodeGen<Handler>::callVM(const VMFunction& fun,
CallVMPhase phase) {
TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(fun);
bool BaselineCodeGen<Handler>::callVM(const VMFunctionData& fun,
TrampolinePtr code, CallVMPhase phase) {
#ifdef DEBUG
// Assert prepareVMCall() has been called.
MOZ_ASSERT(inCall_);
@ -665,6 +664,21 @@ bool BaselineCodeGen<Handler>::callVM(const VMFunction& fun,
return handler.appendRetAddrEntry(cx, RetAddrEntry::Kind::CallVM, callOffset);
}
template <typename Handler>
bool BaselineCodeGen<Handler>::callVM(const VMFunction& fun,
CallVMPhase phase) {
TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(fun);
return callVM(fun, code, phase);
}
template <typename Handler>
template <typename Fn, Fn fn>
bool BaselineCodeGen<Handler>::callVM(CallVMPhase phase) {
VMFunctionId fnId = VMFunctionToId<Fn, fn>::id;
TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(fnId);
return callVM(GetVMFunction(fnId), code, phase);
}
typedef bool (*CheckOverRecursedBaselineFn)(JSContext*, BaselineFrame*);
static const VMFunction CheckOverRecursedBaselineInfo =
FunctionInfo<CheckOverRecursedBaselineFn>(CheckOverRecursedBaseline,
@ -887,10 +901,6 @@ void BaselineInterpreterCodeGen::loadResumeIndexBytecodeOperand(Register dest) {
MOZ_CRASH("NYI: interpreter loadResumeIndexBytecodeOperand");
}
typedef bool (*DebugPrologueFn)(JSContext*, BaselineFrame*, jsbytecode*, bool*);
static const VMFunction DebugPrologueInfo =
FunctionInfo<DebugPrologueFn>(jit::DebugPrologue, "DebugPrologue");
template <typename Handler>
bool BaselineCodeGen<Handler>::emitDebugPrologue() {
auto ifDebuggee = [this]() {
@ -900,7 +910,9 @@ bool BaselineCodeGen<Handler>::emitDebugPrologue() {
prepareVMCall();
pushBytecodePCArg();
pushArg(R0.scratchReg());
if (!callVM(DebugPrologueInfo)) {
using Fn = bool (*)(JSContext*, BaselineFrame*, jsbytecode*, bool*);
if (!callVM<Fn, jit::DebugPrologue>()) {
return false;
}
@ -1009,10 +1021,6 @@ bool BaselineInterpreterCodeGen::initEnvironmentChain() {
MOZ_CRASH("NYI: interpreter initEnvironmentChain");
}
typedef bool (*InterruptCheckFn)(JSContext*);
static const VMFunction InterruptCheckInfo =
FunctionInfo<InterruptCheckFn>(InterruptCheck, "InterruptCheck");
template <typename Handler>
bool BaselineCodeGen<Handler>::emitInterruptCheck() {
frame.syncStack(0);
@ -1022,7 +1030,9 @@ bool BaselineCodeGen<Handler>::emitInterruptCheck() {
Imm32(0), &done);
prepareVMCall();
if (!callVM(InterruptCheckInfo)) {
using Fn = bool (*)(JSContext*);
if (!callVM<Fn, InterruptCheck>()) {
return false;
}
@ -1030,12 +1040,6 @@ bool BaselineCodeGen<Handler>::emitInterruptCheck() {
return true;
}
typedef bool (*IonCompileScriptForBaselineFn)(JSContext*, BaselineFrame*,
jsbytecode*);
static const VMFunction IonCompileScriptForBaselineInfo =
FunctionInfo<IonCompileScriptForBaselineFn>(IonCompileScriptForBaseline,
"IonCompileScriptForBaseline");
template <>
bool BaselineCompilerCodeGen::emitWarmUpCounterIncrement() {
// Emit no warm-up counter increments or bailouts if Ion is not
@ -1099,7 +1103,8 @@ bool BaselineCompilerCodeGen::emitWarmUpCounterIncrement() {
pushBytecodePCArg();
masm.PushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
if (!callVM(IonCompileScriptForBaselineInfo)) {
using Fn = bool (*)(JSContext*, BaselineFrame*, jsbytecode*);
if (!callVM<Fn, IonCompileScriptForBaseline>()) {
return false;
}
@ -1782,11 +1787,6 @@ bool BaselineCodeGen<Handler>::emit_JSOP_NULL() {
return true;
}
typedef bool (*ThrowCheckIsObjectFn)(JSContext*, CheckIsObjectKind);
static const VMFunction ThrowCheckIsObjectInfo =
FunctionInfo<ThrowCheckIsObjectFn>(ThrowCheckIsObject,
"ThrowCheckIsObject");
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_CHECKISOBJ() {
frame.syncStack(0);
@ -1798,7 +1798,9 @@ bool BaselineCodeGen<Handler>::emit_JSOP_CHECKISOBJ() {
prepareVMCall();
pushUint8BytecodeOperandArg();
if (!callVM(ThrowCheckIsObjectInfo)) {
using Fn = bool (*)(JSContext*, CheckIsObjectKind);
if (!callVM<Fn, ThrowCheckIsObject>()) {
return false;
}
@ -1806,10 +1808,6 @@ bool BaselineCodeGen<Handler>::emit_JSOP_CHECKISOBJ() {
return true;
}
typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind);
static const VMFunction CheckIsCallableInfo =
FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable");
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_CHECKISCALLABLE() {
frame.syncStack(0);
@ -1819,23 +1817,15 @@ bool BaselineCodeGen<Handler>::emit_JSOP_CHECKISCALLABLE() {
pushUint8BytecodeOperandArg();
pushArg(R0);
if (!callVM(CheckIsCallableInfo)) {
using Fn = bool (*)(JSContext*, HandleValue, CheckIsCallableKind);
if (!callVM<Fn, CheckIsCallable>()) {
return false;
}
return true;
}
typedef bool (*ThrowUninitializedThisFn)(JSContext*, BaselineFrame* frame);
static const VMFunction ThrowUninitializedThisInfo =
FunctionInfo<ThrowUninitializedThisFn>(BaselineThrowUninitializedThis,
"BaselineThrowUninitializedThis");
typedef bool (*ThrowInitializedThisFn)(JSContext*);
static const VMFunction ThrowInitializedThisInfo =
FunctionInfo<ThrowInitializedThisFn>(BaselineThrowInitializedThis,
"BaselineThrowInitializedThis");
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_CHECKTHIS() {
frame.syncStack(0);
@ -1864,14 +1854,16 @@ bool BaselineCodeGen<Handler>::emitCheckThis(ValueOperand val, bool reinit) {
prepareVMCall();
if (reinit) {
if (!callVM(ThrowInitializedThisInfo)) {
using Fn = bool (*)(JSContext*);
if (!callVM<Fn, BaselineThrowInitializedThis>()) {
return false;
}
} else {
masm.loadBaselineFramePtr(BaselineFrameReg, val.scratchReg());
pushArg(val.scratchReg());
if (!callVM(ThrowUninitializedThisInfo)) {
using Fn = bool (*)(JSContext*, BaselineFrame*);
if (!callVM<Fn, BaselineThrowUninitializedThis>()) {
return false;
}
}
@ -1880,11 +1872,6 @@ bool BaselineCodeGen<Handler>::emitCheckThis(ValueOperand val, bool reinit) {
return true;
}
typedef bool (*ThrowBadDerivedReturnFn)(JSContext*, HandleValue);
static const VMFunction ThrowBadDerivedReturnInfo =
FunctionInfo<ThrowBadDerivedReturnFn>(jit::ThrowBadDerivedReturn,
"ThrowBadDerivedReturn");
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_CHECKRETURN() {
MOZ_ASSERT_IF(handler.maybeScript(),
@ -1900,7 +1887,9 @@ bool BaselineCodeGen<Handler>::emit_JSOP_CHECKRETURN() {
prepareVMCall();
pushArg(R1);
if (!callVM(ThrowBadDerivedReturnInfo)) {
using Fn = bool (*)(JSContext*, HandleValue);
if (!callVM<Fn, ThrowBadDerivedReturn>()) {
return false;
}
masm.assumeUnreachable("Should throw on bad derived constructor return");
@ -1919,11 +1908,6 @@ bool BaselineCodeGen<Handler>::emit_JSOP_CHECKRETURN() {
return true;
}
typedef bool (*GetFunctionThisFn)(JSContext*, BaselineFrame*,
MutableHandleValue);
static const VMFunction GetFunctionThisInfo = FunctionInfo<GetFunctionThisFn>(
jit::BaselineGetFunctionThis, "BaselineGetFunctionThis");
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_FUNCTIONTHIS() {
MOZ_ASSERT_IF(handler.maybeFunction(), !handler.maybeFunction()->isArrow());
@ -1941,7 +1925,8 @@ bool BaselineCodeGen<Handler>::emit_JSOP_FUNCTIONTHIS() {
pushArg(R1.scratchReg());
if (!callVM(GetFunctionThisInfo)) {
using Fn = bool (*)(JSContext*, BaselineFrame*, MutableHandleValue);
if (!callVM<Fn, BaselineGetFunctionThis>()) {
return false;
}
@ -1955,12 +1940,6 @@ bool BaselineCodeGen<Handler>::emit_JSOP_FUNCTIONTHIS() {
R2.scratchReg());
}
typedef void (*GetNonSyntacticGlobalThisFn)(JSContext*, HandleObject,
MutableHandleValue);
static const VMFunction GetNonSyntacticGlobalThisInfo =
FunctionInfo<GetNonSyntacticGlobalThisFn>(js::GetNonSyntacticGlobalThis,
"GetNonSyntacticGlobalThis");
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_GLOBALTHIS() {
frame.syncStack(0);
@ -1971,7 +1950,8 @@ bool BaselineCodeGen<Handler>::emit_JSOP_GLOBALTHIS() {
masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
pushArg(R0.scratchReg());
if (!callVM(GetNonSyntacticGlobalThisInfo)) {
using Fn = void (*)(JSContext*, HandleObject, MutableHandleValue);
if (!callVM<Fn, GetNonSyntacticGlobalThis>()) {
return false;
}

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

@ -346,8 +346,13 @@ class BaselineCodeGen {
Register scratch1, Register scratch2);
enum CallVMPhase { POST_INITIALIZE, CHECK_OVER_RECURSED };
bool callVM(const VMFunctionData& fun, TrampolinePtr code,
CallVMPhase phase = POST_INITIALIZE);
bool callVM(const VMFunction& fun, CallVMPhase phase = POST_INITIALIZE);
template <typename Fn, Fn fn>
bool callVM(CallVMPhase phase = POST_INITIALIZE);
bool callVMNonOp(const VMFunction& fun, CallVMPhase phase = POST_INITIALIZE) {
if (!callVM(fun, phase)) {
return false;

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

@ -8912,8 +8912,8 @@ void JitRuntime::generateDoubleToInt32ValueStub(MacroAssembler& masm) {
masm.abiret();
}
bool JitRuntime::generateTLEventVM(MacroAssembler& masm, const VMFunction& f,
bool enter) {
bool JitRuntime::generateTLEventVM(MacroAssembler& masm,
const VMFunctionData& f, bool enter) {
#ifdef JS_TRACE_LOGGING
bool vmEventEnabled = TraceLogTextIdEnabled(TraceLogger_VM);
bool vmSpecificEventEnabled = TraceLogTextIdEnabled(TraceLogger_VMSpecific);

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

@ -286,13 +286,22 @@ bool JitRuntime::initialize(JSContext* cx) {
generateDoubleToInt32ValueStub(masm);
JitSpew(JitSpew_Codegen, "# Emitting VM function wrappers");
if (!generateVMWrappers(cx, masm)) {
return false;
}
// TODO(bug 1530937): remove this after converting all VM functions.
for (VMFunction* fun = VMFunction::functions; fun; fun = fun->next) {
if (functionWrappers_->has(fun)) {
// Duplicate VMFunction definition. See VMFunction::hash.
continue;
}
JitSpew(JitSpew_Codegen, "# VM function wrapper (%s)", fun->name());
if (!generateVMWrapper(cx, masm, *fun)) {
uint32_t offset;
if (!generateVMWrapper(cx, masm, *fun, &offset)) {
return false;
}
if (!functionWrappers_->putNew(fun, offset)) {
return false;
}
}

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

@ -1022,7 +1022,8 @@ uint8_t* alignDoubleSpillWithOffset(uint8_t* pointer, int32_t offset) {
return reinterpret_cast<uint8_t*>(address);
}
static void TraceJitExitFrameCopiedArguments(JSTracer* trc, const VMFunction* f,
static void TraceJitExitFrameCopiedArguments(JSTracer* trc,
const VMFunctionData* f,
ExitFooterFrame* footer) {
uint8_t* doubleArgs = reinterpret_cast<uint8_t*>(footer);
doubleArgs = alignDoubleSpillWithOffset(doubleArgs, sizeof(intptr_t));
@ -1044,7 +1045,8 @@ static void TraceJitExitFrameCopiedArguments(JSTracer* trc, const VMFunction* f,
}
}
#else
static void TraceJitExitFrameCopiedArguments(JSTracer* trc, const VMFunction* f,
static void TraceJitExitFrameCopiedArguments(JSTracer* trc,
const VMFunctionData* f,
ExitFooterFrame* footer) {
// This is NO-OP on other platforms.
}
@ -1127,7 +1129,7 @@ static void TraceJitExitFrame(JSTracer* trc, const JSJitFrameIter& frame) {
MOZ_ASSERT(frame.exitFrame()->isWrapperExit());
const VMFunction* f = footer->function();
const VMFunctionData* f = footer->function();
MOZ_ASSERT(f);
// Trace arguments of the VM wrapper.

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

@ -17,6 +17,7 @@ namespace js {
namespace jit {
struct SafepointSlotEntry;
struct VMFunctionData;
enum CalleeTokenTag {
CalleeToken_Function = 0x0, // untagged
@ -418,7 +419,7 @@ enum class ExitFrameType : uint8_t {
// GC related data used to keep alive data surrounding the Exit frame.
class ExitFooterFrame {
// Stores the ExitFrameType or, for ExitFrameType::VMFunction, the
// VMFunction*.
// VMFunctionData*.
uintptr_t data_;
public:
@ -433,9 +434,9 @@ class ExitFooterFrame {
MOZ_ASSERT(ExitFrameType(data_) != ExitFrameType::VMFunction);
return ExitFrameType(data_);
}
inline const VMFunction* function() const {
inline const VMFunctionData* function() const {
MOZ_ASSERT(type() == ExitFrameType::VMFunction);
return reinterpret_cast<const VMFunction*>(data_);
return reinterpret_cast<const VMFunctionData*>(data_);
}
// This should only be called for function()->outParam == Type_Handle

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

@ -28,6 +28,8 @@ namespace js {
namespace jit {
class FrameSizeClass;
struct VMFunctionData;
enum class VMFunctionId;
struct EnterJitData {
explicit EnterJitData(JSContext* cx)
@ -143,6 +145,10 @@ class JitRuntime {
using VMWrapperMap = HashMap<const VMFunction*, uint32_t, VMFunction>;
WriteOnceData<VMWrapperMap*> functionWrappers_;
// Maps VMFunctionId to the offset of the wrapper code in trampolineCode_.
using VMWrapperOffsets = Vector<uint32_t, 0, SystemAllocPolicy>;
VMWrapperOffsets functionWrapperOffsets_;
// Global table of jitcode native address => bytecode address mappings.
UnprotectedData<JitcodeGlobalTable*> jitcodeGlobalTable_;
@ -190,15 +196,18 @@ class JitRuntime {
JitCode* generateDebugTrapHandler(JSContext* cx);
JitCode* generateBaselineDebugModeOSRHandler(
JSContext* cx, uint32_t* noFrameRegPopOffsetOut);
bool generateVMWrapper(JSContext* cx, MacroAssembler& masm,
const VMFunction& f);
const VMFunctionData& f, uint32_t* wrapperOffset);
bool generateVMWrappers(JSContext* cx, MacroAssembler& masm);
bool generateTLEventVM(MacroAssembler& masm, const VMFunction& f, bool enter);
bool generateTLEventVM(MacroAssembler& masm, const VMFunctionData& f,
bool enter);
inline bool generateTLEnterVM(MacroAssembler& masm, const VMFunction& f) {
inline bool generateTLEnterVM(MacroAssembler& masm, const VMFunctionData& f) {
return generateTLEventVM(masm, f, /* enter = */ true);
}
inline bool generateTLExitVM(MacroAssembler& masm, const VMFunction& f) {
inline bool generateTLExitVM(MacroAssembler& masm, const VMFunctionData& f) {
return generateTLEventVM(masm, f, /* enter = */ false);
}
@ -227,6 +236,12 @@ class JitRuntime {
}
TrampolinePtr getVMWrapper(const VMFunction& f) const;
TrampolinePtr getVMWrapper(const VMFunctionId funId) const {
MOZ_ASSERT(trampolineCode_);
return trampolineCode(functionWrapperOffsets_[size_t(funId)]);
}
JitCode* debugTrapHandler(JSContext* cx);
JitCode* getBaselineDebugModeOSRHandler(JSContext* cx);
void* getBaselineDebugModeOSRHandlerAddress(JSContext* cx, bool popFrameReg);

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

@ -260,7 +260,7 @@ uint32_t MacroAssembler::buildFakeExitFrame(Register scratch) {
// Exit frame footer.
void MacroAssembler::enterExitFrame(Register cxreg, Register scratch,
const VMFunction* f) {
const VMFunctionData* f) {
MOZ_ASSERT(f);
linkExitFrame(cxreg, scratch);
// Push VMFunction pointer, to mark arguments.

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

@ -714,9 +714,9 @@ class MacroAssembler : public MacroAssemblerSpecific {
//
// See JitFrames.h, and MarkJitExitFrame in JitFrames.cpp.
// Push stub code and the VMFunction pointer.
// Push stub code and the VMFunctionData pointer.
inline void enterExitFrame(Register cxreg, Register scratch,
const VMFunction* f);
const VMFunctionData* f);
// Push an exit frame token to identify which fake exit frame this footer
// corresponds to.

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

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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/BaselineIC.h"
#include "jit/JitRealm.h"
#include "jit/VMFunctions.h"
#include "vm/Interpreter.h"
#include "jit/BaselineFrame-inl.h"
#include "vm/Interpreter-inl.h"
namespace js {
namespace jit {
// List of all VM functions to be used with callVM. Each entry stores the name
// (must be unique, used for the VMFunctionId enum and profiling) and the C++
// function to be called. This list must be sorted on the name field.
#define VMFUNCTION_LIST(_) \
_(BaselineDebugPrologue, js::jit::DebugPrologue) \
_(BaselineGetFunctionThis, js::jit::BaselineGetFunctionThis) \
_(BaselineThrowInitializedThis, js::jit::BaselineThrowInitializedThis) \
_(BaselineThrowUninitializedThis, js::jit::BaselineThrowUninitializedThis) \
_(CheckIsCallable, js::jit::CheckIsCallable) \
_(CheckOverRecursedBaseline, js::jit::CheckOverRecursedBaseline) \
_(GetNonSyntacticGlobalThis, js::GetNonSyntacticGlobalThis) \
_(InterruptCheck, js::jit::InterruptCheck) \
_(IonCompileScriptForBaseline, js::jit::IonCompileScriptForBaseline) \
_(ThrowBadDerivedReturn, js::jit::ThrowBadDerivedReturn) \
_(ThrowCheckIsObject, js::ThrowCheckIsObject)
enum class VMFunctionId {
#define DEF_ID(name, fp) name,
VMFUNCTION_LIST(DEF_ID)
#undef DEF_ID
Count
};
// Define the VMFunctionToId template to map from signature + function to
// the VMFunctionId. This lets us verify the consumer/codegen code matches
// the C++ signature.
template <typename Function, Function fun>
struct VMFunctionToId; // Error on this line? Forgot to update VMFUNCTION_LIST?
// GCC warns when the signature does not have matching attributes (for example
// MOZ_MUST_USE). Squelch this warning to avoid a GCC-only footgun.
#if MOZ_IS_GCC
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wignored-attributes"
#endif
// Note: the use of ::fp instead of fp is intentional to enforce use of
// fully-qualified names in the list above.
#define DEF_TEMPLATE(name, fp) \
template <> \
struct VMFunctionToId<decltype(&(::fp)), ::fp> { \
static constexpr VMFunctionId id = VMFunctionId::name; \
};
VMFUNCTION_LIST(DEF_TEMPLATE)
#undef DEF_TEMPLATE
#if MOZ_IS_GCC
# pragma GCC diagnostic pop
#endif
} // namespace jit
} // namespace js

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

@ -24,6 +24,7 @@
#include "jit/BaselineFrame-inl.h"
#include "jit/JitFrames-inl.h"
#include "jit/VMFunctionList-inl.h"
#include "vm/Debugger-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/NativeObject-inl.h"
@ -37,6 +38,113 @@ using namespace js::jit;
namespace js {
namespace jit {
// Helper template to build the VMFunctionData for a function.
template <typename... Args>
struct VMFunctionDataHelper;
template <class R, typename... Args>
struct VMFunctionDataHelper<R (*)(JSContext*, Args...)>
: public VMFunctionData {
using Fun = R (*)(JSContext*, Args...);
static constexpr DataType returnType() { return TypeToDataType<R>::result; }
static constexpr DataType outParam() {
return OutParamToDataType<typename LastArg<Args...>::Type>::result;
}
static constexpr RootType outParamRootType() {
return OutParamToRootType<typename LastArg<Args...>::Type>::result;
}
static constexpr size_t NbArgs() { return LastArg<Args...>::nbArgs; }
static constexpr size_t explicitArgs() {
return NbArgs() - (outParam() != Type_Void ? 1 : 0);
}
static constexpr uint32_t argumentProperties() {
return BitMask<TypeToArgProperties, uint32_t, 2, Args...>::result;
}
static constexpr uint32_t argumentPassedInFloatRegs() {
return BitMask<TypeToPassInFloatReg, uint32_t, 2, Args...>::result;
}
static constexpr uint64_t argumentRootTypes() {
return BitMask<TypeToRootType, uint64_t, 3, Args...>::result;
}
constexpr VMFunctionDataHelper(Fun fun, const char* name,
PopValues extraValuesToPop = PopValues(0))
: VMFunctionData((void*)fun, name, explicitArgs(), argumentProperties(),
argumentPassedInFloatRegs(), argumentRootTypes(),
outParam(), outParamRootType(), returnType(),
extraValuesToPop.numValues, NonTailCall) {}
constexpr VMFunctionDataHelper(Fun fun, const char* name,
MaybeTailCall expectTailCall,
PopValues extraValuesToPop = PopValues(0))
: VMFunctionData((void*)fun, name, explicitArgs(), argumentProperties(),
argumentPassedInFloatRegs(), argumentRootTypes(),
outParam(), outParamRootType(), returnType(),
extraValuesToPop.numValues, expectTailCall) {}
};
// GCC warns when the signature does not have matching attributes (for example
// MOZ_MUST_USE). Squelch this warning to avoid a GCC-only footgun.
#if MOZ_IS_GCC
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wignored-attributes"
#endif
// Generate VMFunctionData array.
static constexpr VMFunctionData vmFunctions[] = {
#define DEF_VMFUNCTION(name, fp) \
VMFunctionDataHelper<decltype(&(::fp))>(::fp, #name),
VMFUNCTION_LIST(DEF_VMFUNCTION)
#undef DEF_VMFUNCTION
};
#if MOZ_IS_GCC
# pragma GCC diagnostic pop
#endif
const VMFunctionData& GetVMFunction(VMFunctionId id) {
return vmFunctions[size_t(id)];
}
bool JitRuntime::generateVMWrappers(JSContext* cx, MacroAssembler& masm) {
// Generate all VM function wrappers.
static constexpr size_t NumVMFunctions = size_t(VMFunctionId::Count);
if (!functionWrapperOffsets_.reserve(NumVMFunctions)) {
return false;
}
#ifdef DEBUG
const char* lastName = nullptr;
#endif
for (size_t i = 0; i < NumVMFunctions; i++) {
VMFunctionId id = VMFunctionId(i);
const VMFunctionData& fun = GetVMFunction(id);
#ifdef DEBUG
// Assert the list is sorted by name.
if (lastName) {
MOZ_ASSERT(strcmp(lastName, fun.name()) < 0,
"VM function list must be sorted by name");
}
lastName = fun.name();
#endif
JitSpew(JitSpew_Codegen, "# VM function wrapper (%s)", fun.name());
uint32_t offset;
if (!generateVMWrapper(cx, masm, fun, &offset)) {
return false;
}
MOZ_ASSERT(functionWrapperOffsets_.length() == size_t(id));
functionWrapperOffsets_.infallibleAppend(offset);
}
return true;
}
// Statics are initialized to null.
/* static */ VMFunction* VMFunction::functions;

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

@ -55,6 +55,8 @@ enum MaybeTailCall : bool { TailCall, NonTailCall };
// [SMDOC] JIT-to-C++ Function Calls. (callVM)
//
// TODO(bug 1530937): update this comment after converting all VM functions.
//
// Sometimes it is easier to reuse C++ code by calling VM's functions. Calling a
// function from the VM can be achieved with the use of callWithABI but this is
// discouraged when the called functions might trigger exceptions and/or
@ -123,11 +125,9 @@ enum MaybeTailCall : bool { TailCall, NonTailCall };
// }
//
// After this, the result value is in the return value register.
struct VMFunction {
// Global linked list of all VMFunctions.
static VMFunction* functions;
VMFunction* next;
// Data for a VM function. All VMFunctionDatas are stored in a constexpr array.
struct VMFunctionData {
// Address of the C function.
void* wrapped;
@ -293,15 +293,14 @@ struct VMFunction {
return count;
}
constexpr VMFunction(void* wrapped, const char* name, uint32_t explicitArgs,
uint32_t argumentProperties,
uint32_t argumentPassedInFloatRegs,
uint64_t argRootTypes, DataType outParam,
RootType outParamRootType, DataType returnType,
uint8_t extraValuesToPop = 0,
MaybeTailCall expectTailCall = NonTailCall)
: next(nullptr),
wrapped(wrapped),
constexpr VMFunctionData(void* wrapped, const char* name,
uint32_t explicitArgs, uint32_t argumentProperties,
uint32_t argumentPassedInFloatRegs,
uint64_t argRootTypes, DataType outParam,
RootType outParamRootType, DataType returnType,
uint8_t extraValuesToPop = 0,
MaybeTailCall expectTailCall = NonTailCall)
: wrapped(wrapped),
#if defined(JS_JITSPEW) || defined(JS_TRACE_LOGGING)
name_(name),
#endif
@ -314,11 +313,18 @@ struct VMFunction {
returnType(returnType),
extraValuesToPop(extraValuesToPop),
expectTailCall(expectTailCall) {
// Check for valid failure/return type.
MOZ_ASSERT_IF(outParam != Type_Void,
returnType == Type_Void || returnType == Type_Bool);
MOZ_ASSERT(returnType == Type_Void || returnType == Type_Bool ||
returnType == Type_Object);
}
VMFunction(const VMFunction& o)
: next(functions),
wrapped(o.wrapped),
// Note: clang-tidy suggests using |= auto| here but that generates extra
// static initializers for old-style VMFunction definitions with Clang. We can
// do this after bug 1530937 converts all of them.
constexpr VMFunctionData(const VMFunctionData& o)
: wrapped(o.wrapped),
#if defined(JS_JITSPEW) || defined(JS_TRACE_LOGGING)
name_(o.name_),
#endif
@ -331,14 +337,32 @@ struct VMFunction {
returnType(o.returnType),
extraValuesToPop(o.extraValuesToPop),
expectTailCall(o.expectTailCall) {
}
};
// TODO(bug 1530937): remove VMFunction and FunctionInfo after converting all VM
// functions to the new design.
struct VMFunction : public VMFunctionData {
// Global linked list of all VMFunctions.
static VMFunction* functions;
VMFunction* next;
constexpr VMFunction(void* wrapped, const char* name, uint32_t explicitArgs,
uint32_t argumentProperties,
uint32_t argumentPassedInFloatRegs,
uint64_t argRootTypes, DataType outParam,
RootType outParamRootType, DataType returnType,
uint8_t extraValuesToPop = 0,
MaybeTailCall expectTailCall = NonTailCall)
: VMFunctionData(wrapped, name, explicitArgs, argumentProperties,
argumentPassedInFloatRegs, argRootTypes, outParam,
outParamRootType, returnType, extraValuesToPop,
expectTailCall),
next(nullptr) {}
VMFunction(const VMFunction& o) : VMFunctionData(o), next(functions) {
// Add this to the global list of VMFunctions.
functions = this;
// Check for valid failure/return type.
MOZ_ASSERT_IF(outParam != Type_Void,
returnType == Type_Void || returnType == Type_Bool);
MOZ_ASSERT(returnType == Type_Void || returnType == Type_Bool ||
returnType == Type_Object);
}
typedef const VMFunction* Lookup;
@ -1215,6 +1239,10 @@ extern const VMFunction ToNumericInfo;
// TailCall VMFunctions
extern const VMFunction DoConcatStringObjectInfo;
enum class VMFunctionId;
extern const VMFunctionData& GetVMFunction(VMFunctionId id);
} // namespace jit
} // namespace js

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

@ -704,10 +704,11 @@ void JitRuntime::generateBailoutHandler(MacroAssembler& masm,
}
bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
const VMFunction& f) {
const VMFunctionData& f,
uint32_t* wrapperOffset) {
MOZ_ASSERT(functionWrappers_);
uint32_t wrapperOffset = startTrampolineCode(masm);
*wrapperOffset = startTrampolineCode(masm);
AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
@ -893,7 +894,7 @@ bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
f.explicitStackSlots() * sizeof(void*) +
f.extraValuesToPop * sizeof(Value)));
return functionWrappers_->putNew(&f, wrapperOffset);
return true;
}
uint32_t JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm,

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

@ -533,10 +533,11 @@ void JitRuntime::generateBailoutHandler(MacroAssembler& masm,
}
bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
const VMFunction& f) {
const VMFunctionData& f,
uint32_t* wrapperOffset) {
MOZ_ASSERT(functionWrappers_);
uint32_t wrapperOffset = startTrampolineCode(masm);
*wrapperOffset = startTrampolineCode(masm);
// Avoid conflicts with argument registers while discarding the result after
// the function call.
@ -733,7 +734,7 @@ bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
f.explicitStackSlots() * sizeof(void*) +
f.extraValuesToPop * sizeof(Value)));
return functionWrappers_->putNew(&f, wrapperOffset);
return true;
}
uint32_t JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm,

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

@ -40,7 +40,7 @@ void JitRuntime::generateProfilerExitFrameTailStub(MacroAssembler&, Label*) {
}
bool JitRuntime::generateVMWrapper(JSContext*, MacroAssembler&,
const VMFunction&) {
const VMFunctionData&, uint32_t*) {
MOZ_CRASH();
}

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

@ -591,10 +591,11 @@ void JitRuntime::generateBailoutHandler(MacroAssembler& masm,
}
bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
const VMFunction& f) {
const VMFunctionData& f,
uint32_t* wrapperOffset) {
MOZ_ASSERT(functionWrappers_);
uint32_t wrapperOffset = startTrampolineCode(masm);
*wrapperOffset = startTrampolineCode(masm);
// Avoid conflicts with argument registers while discarding the result after
// the function call.
@ -772,7 +773,7 @@ bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
f.explicitStackSlots() * sizeof(void*) +
f.extraValuesToPop * sizeof(Value)));
return functionWrappers_->putNew(&f, wrapperOffset);
return true;
}
uint32_t JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm,

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

@ -608,10 +608,11 @@ void JitRuntime::generateBailoutHandler(MacroAssembler& masm,
}
bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
const VMFunction& f) {
const VMFunctionData& f,
uint32_t* wrapperOffset) {
MOZ_ASSERT(functionWrappers_);
uint32_t wrapperOffset = startTrampolineCode(masm);
*wrapperOffset = startTrampolineCode(masm);
// Avoid conflicts with argument registers while discarding the result after
// the function call.
@ -786,7 +787,7 @@ bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
f.explicitStackSlots() * sizeof(void*) +
f.extraValuesToPop * sizeof(Value)));
return functionWrappers_->putNew(&f, wrapperOffset);
return true;
}
uint32_t JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm,