mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Bug 1364908 - IonMonkey: Add LoadElementFromSate to support argument[x] in inlined functions. r=jandem
This commit is contained in:
parent
d6ec64d163
commit
a1f0784e66
@ -42,7 +42,8 @@ namespace JS {
|
||||
_(GetElem_TypedArray) \
|
||||
_(GetElem_String) \
|
||||
_(GetElem_Arguments) \
|
||||
_(GetElem_ArgumentsInlined) \
|
||||
_(GetElem_ArgumentsInlinedConstant) \
|
||||
_(GetElem_ArgumentsInlinedSwitch) \
|
||||
_(GetElem_InlineCache) \
|
||||
\
|
||||
_(SetElem_TypedObject) \
|
||||
|
59
js/src/jit-test/tests/ion/inlining/inline-getelem-args.js
Normal file
59
js/src/jit-test/tests/ion/inlining/inline-getelem-args.js
Normal file
@ -0,0 +1,59 @@
|
||||
function cat() {
|
||||
var res = "";
|
||||
for (var i = 0; i < arguments.length; i++)
|
||||
res += arguments[i];
|
||||
return res;
|
||||
}
|
||||
|
||||
function cat_m1(ion) {
|
||||
var res = "";
|
||||
for (var i = (ion ? -1 : 0); i < arguments.length; i++)
|
||||
res += arguments[i];
|
||||
return res;
|
||||
}
|
||||
|
||||
function cat_p1(ion) {
|
||||
var res = "";
|
||||
for (var i = 0; i < arguments.length + (ion ? 1 : 0); i++)
|
||||
res += arguments[i];
|
||||
return res;
|
||||
}
|
||||
|
||||
function sum() {
|
||||
var res = 0;
|
||||
for (var i = 0; i < arguments.length; i++)
|
||||
res += arguments[i];
|
||||
return res;
|
||||
}
|
||||
|
||||
function wrapper() {
|
||||
var res;
|
||||
var i6 = { valueOf: () => 6 },
|
||||
i8 = 8.5,
|
||||
s2 = { toString: () => "2" },
|
||||
s4 = {},
|
||||
s6 = "6",
|
||||
s7 = undefined,
|
||||
s8 = null;
|
||||
for (var b = true; b; b = !inIon()) {
|
||||
res = sum(1,2,3,4,5,i6,7,i8,9,10);
|
||||
assertEq(res, 55.5);
|
||||
|
||||
res = cat(true,s2,3,s4,5,s6,s7,s8);
|
||||
assertEq(res, "true23[object Object]56undefinednull");
|
||||
|
||||
ion = inIon();
|
||||
if (typeof ion !== "boolean") break;
|
||||
res = cat_m1(ion,1,s2,3,s4,5,s6,s7,s8);
|
||||
if (ion) assertEq(res, "undefinedtrue123[object Object]56undefinednull");
|
||||
else assertEq(res, "false123[object Object]56undefinednull");
|
||||
|
||||
ion = inIon();
|
||||
if (typeof ion !== "boolean") break;
|
||||
res = cat_p1(ion,1,s2,3,s4,5,s6,s7,s8);
|
||||
if (ion) assertEq(res, "true123[object Object]56undefinednullundefined");
|
||||
else assertEq(res, "false123[object Object]56undefinednull");
|
||||
}
|
||||
}
|
||||
|
||||
wrapper();
|
@ -42,6 +42,7 @@
|
||||
#include "jit/MoveEmitter.h"
|
||||
#include "jit/RangeAnalysis.h"
|
||||
#include "jit/SharedICHelpers.h"
|
||||
#include "jit/StackSlotAllocator.h"
|
||||
#include "vm/AsyncFunction.h"
|
||||
#include "vm/AsyncIteration.h"
|
||||
#include "vm/MatchPairs.h"
|
||||
@ -11023,6 +11024,238 @@ CodeGenerator::visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole* lir)
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
template <SwitchTableType tableType>
|
||||
class OutOfLineSwitch : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
using LabelsVector = Vector<Label, 0, JitAllocPolicy>;
|
||||
using CodeLabelsVector = Vector<CodeLabel, 0, JitAllocPolicy>;
|
||||
LabelsVector labels_;
|
||||
CodeLabelsVector codeLabels_;
|
||||
CodeLabel start_;
|
||||
bool isOutOfLine_;
|
||||
|
||||
void accept(CodeGenerator* codegen) {
|
||||
codegen->visitOutOfLineSwitch(this);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit OutOfLineSwitch(TempAllocator& alloc)
|
||||
: labels_(alloc),
|
||||
codeLabels_(alloc),
|
||||
isOutOfLine_(false)
|
||||
{}
|
||||
|
||||
CodeLabel* start() {
|
||||
return &start_;
|
||||
}
|
||||
|
||||
CodeLabelsVector& codeLabels() {
|
||||
return codeLabels_;
|
||||
}
|
||||
LabelsVector& labels() {
|
||||
return labels_;
|
||||
}
|
||||
|
||||
void jumpToCodeEntries(MacroAssembler& masm, Register index, Register temp) {
|
||||
Register base;
|
||||
if (tableType == SwitchTableType::Inline) {
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
base = ::js::jit::pc;
|
||||
#else
|
||||
MOZ_CRASH("NYI: SwitchTableType::Inline");
|
||||
#endif
|
||||
} else {
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
|
||||
MOZ_CRASH("NYI: SwitchTableType::OutOfLine");
|
||||
#else
|
||||
masm.mov(start_.patchAt(), temp);
|
||||
base = temp;
|
||||
#endif
|
||||
}
|
||||
BaseIndex jumpTarget(base, index, ScalePointer);
|
||||
masm.branchToComputedAddress(jumpTarget);
|
||||
}
|
||||
|
||||
// Register an entry in the switch table.
|
||||
void addTableEntry(MacroAssembler& masm) {
|
||||
if ((!isOutOfLine_ && tableType == SwitchTableType::Inline) ||
|
||||
(isOutOfLine_ && tableType == SwitchTableType::OutOfLine))
|
||||
{
|
||||
CodeLabel cl;
|
||||
masm.writeCodePointer(cl.patchAt());
|
||||
masm.propagateOOM(codeLabels_.append(mozilla::Move(cl)));
|
||||
}
|
||||
}
|
||||
// Register the code, to which the table will jump to.
|
||||
void addCodeEntry(MacroAssembler& masm) {
|
||||
Label entry;
|
||||
masm.bind(&entry);
|
||||
masm.propagateOOM(labels_.append(mozilla::Move(entry)));
|
||||
}
|
||||
|
||||
void setOutOfLine() {
|
||||
isOutOfLine_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
template <SwitchTableType tableType>
|
||||
void
|
||||
CodeGenerator::visitOutOfLineSwitch(OutOfLineSwitch<tableType>* jumpTable)
|
||||
{
|
||||
jumpTable->setOutOfLine();
|
||||
if (tableType == SwitchTableType::OutOfLine) {
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
|
||||
MOZ_CRASH("NYI: SwitchTableType::OutOfLine");
|
||||
#else
|
||||
masm.haltingAlign(sizeof(void*));
|
||||
masm.use(jumpTable->start()->target());
|
||||
masm.addCodeLabel(*jumpTable->start());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Add table entries if the table is inlined.
|
||||
auto& labels = jumpTable->labels();
|
||||
for (size_t i = 0, e = labels.length(); i < e; i++)
|
||||
jumpTable->addTableEntry(masm);
|
||||
|
||||
auto& codeLabels = jumpTable->codeLabels();
|
||||
for (size_t i = 0, e = codeLabels.length(); i < e; i++) {
|
||||
// The entries of the jump table need to be absolute addresses and thus
|
||||
// must be patched after codegen is finished.
|
||||
auto& cl = codeLabels[i];
|
||||
cl.target()->bind(labels[i].offset());
|
||||
masm.addCodeLabel(cl);
|
||||
}
|
||||
}
|
||||
|
||||
template void CodeGenerator::visitOutOfLineSwitch(OutOfLineSwitch<SwitchTableType::Inline>* jumpTable);
|
||||
template void CodeGenerator::visitOutOfLineSwitch(OutOfLineSwitch<SwitchTableType::OutOfLine>* jumpTable);
|
||||
|
||||
void
|
||||
CodeGenerator::visitLoadElementFromStateV(LLoadElementFromStateV* lir)
|
||||
{
|
||||
Register index = ToRegister(lir->index());
|
||||
Register temp0 = ToRegister(lir->temp0());
|
||||
#ifdef JS_NUNBOX32
|
||||
Register temp1 = ToRegister(lir->temp1());
|
||||
#endif
|
||||
FloatRegister tempD = ToFloatRegister(lir->tempD());
|
||||
ValueOperand out = ToOutValue(lir);
|
||||
|
||||
// For each element, load it and box it.
|
||||
MArgumentState* array = lir->array()->toArgumentState();
|
||||
Label join;
|
||||
|
||||
// Jump to the code which is loading the element, based on its index.
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
|
||||
auto* jumpTable = new (alloc()) OutOfLineSwitch<SwitchTableType::Inline>(alloc());
|
||||
#else
|
||||
auto* jumpTable = new (alloc()) OutOfLineSwitch<SwitchTableType::OutOfLine>(alloc());
|
||||
#endif
|
||||
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
|
||||
// Inhibit pools within the following sequence because we are indexing into
|
||||
// a pc relative table. The region will have one instruction for ma_ldr, one
|
||||
// for breakpoint, and each table case takes one word.
|
||||
AutoForbidPools afp(&masm, 1 + 1 + array->numElements());
|
||||
#endif
|
||||
jumpTable->jumpToCodeEntries(masm, index, temp0);
|
||||
|
||||
// Add table entries if the table is inlined.
|
||||
for (size_t i = 0, e = array->numElements(); i < e; i++)
|
||||
jumpTable->addTableEntry(masm);
|
||||
}
|
||||
|
||||
// Add inlined code for loading arguments from where they are allocated.
|
||||
for (size_t i = 0, e = array->numElements(); i < e; i++) {
|
||||
MDefinition* elem = array->getElement(i);
|
||||
ConstantOrRegister input;
|
||||
|
||||
jumpTable->addCodeEntry(masm);
|
||||
Register typeReg = Register::Invalid();
|
||||
const LAllocation* a = lir->getOperand(1 + BOX_PIECES * i);
|
||||
if (a->isBogus()) {
|
||||
if (elem->type() == MIRType::Null) {
|
||||
input = NullValue();
|
||||
} else if (elem->type() == MIRType::Undefined) {
|
||||
input = UndefinedValue();
|
||||
} else if (elem->isConstant() && elem->isEmittedAtUses()) {
|
||||
input = elem->toConstant()->toJSValue();
|
||||
} else {
|
||||
MOZ_CRASH("Unsupported element constant allocation.");
|
||||
}
|
||||
} else if (a->isMemory()) {
|
||||
if (elem->type() == MIRType::Double) {
|
||||
masm.loadDouble(ToAddress(a), tempD);
|
||||
input = TypedOrValueRegister(elem->type(), AnyRegister(tempD));
|
||||
} else if (elem->type() == MIRType::Value) {
|
||||
typeReg = temp0;
|
||||
masm.loadPtr(ToAddress(a), temp0);
|
||||
#ifdef JS_PUNBOX64
|
||||
input = TypedOrValueRegister(ValueOperand(temp0));
|
||||
#endif
|
||||
} else {
|
||||
typeReg = temp0;
|
||||
size_t width = StackSlotAllocator::width(LDefinition::TypeFrom(elem->type()));
|
||||
if (width == 4)
|
||||
masm.load32(ToAddress(a), temp0);
|
||||
else if (width == 8)
|
||||
masm.loadPtr(ToAddress(a), temp0);
|
||||
else
|
||||
MOZ_CRASH("Unsupported load size");
|
||||
input = TypedOrValueRegister(elem->type(), AnyRegister(typeReg));
|
||||
}
|
||||
} else if (a->isGeneralReg()) {
|
||||
typeReg = ToRegister(a);
|
||||
input = TypedOrValueRegister(elem->type(), AnyRegister(typeReg));
|
||||
#ifdef JS_PUNBOX64
|
||||
if (elem->type() != MIRType::Value)
|
||||
input = TypedOrValueRegister(elem->type(), AnyRegister(typeReg));
|
||||
else
|
||||
input = TypedOrValueRegister(ValueOperand(typeReg));
|
||||
#else
|
||||
if (elem->type() != MIRType::Value)
|
||||
input = TypedOrValueRegister(elem->type(), AnyRegister(typeReg));
|
||||
#endif
|
||||
} else if (a->isFloatReg()) {
|
||||
input = TypedOrValueRegister(elem->type(), AnyRegister(ToFloatRegister(a)));
|
||||
} else if (a->isConstantValue()) {
|
||||
input = a->toConstant()->toJSValue();
|
||||
} else {
|
||||
MOZ_CRASH("Unsupported element allocation.");
|
||||
}
|
||||
|
||||
#ifdef JS_NUNBOX32
|
||||
if (elem->type() == MIRType::Value) {
|
||||
static_assert(TYPE_INDEX == 0, "Unexpected type allocation index");
|
||||
static_assert(PAYLOAD_INDEX == 1, "Unexpected payload allocation index");
|
||||
const LAllocation* a1 = lir->getOperand(1 + BOX_PIECES * i + 1);
|
||||
MOZ_ASSERT(!a1->isBogus());
|
||||
MOZ_ASSERT(typeReg != Register::Invalid());
|
||||
if (a1->isMemory()) {
|
||||
masm.loadPtr(ToAddress(a1), temp1);
|
||||
input = TypedOrValueRegister(ValueOperand(typeReg, temp1));
|
||||
} else if (a1->isGeneralReg()) {
|
||||
input = TypedOrValueRegister(ValueOperand(typeReg, ToRegister(a1)));
|
||||
} else {
|
||||
MOZ_CRASH("Unsupported Value allocation.");
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(lir->getOperand(1 + BOX_PIECES * i + 1)->isBogus());
|
||||
}
|
||||
#endif
|
||||
masm.moveValue(input, out);
|
||||
|
||||
// For the last entry, fall-through.
|
||||
if (i + 1 < e)
|
||||
masm.jump(&join);
|
||||
}
|
||||
|
||||
addOutOfLineCode(jumpTable, lir->mir());
|
||||
masm.bind(&join);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void
|
||||
StoreToTypedArray(MacroAssembler& masm, Scalar::Type writeType, const LAllocation* value,
|
||||
|
@ -33,6 +33,12 @@
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
enum class SwitchTableType {
|
||||
Inline,
|
||||
OutOfLine
|
||||
};
|
||||
|
||||
template <SwitchTableType tableType> class OutOfLineSwitch;
|
||||
class OutOfLineTestObject;
|
||||
class OutOfLineNewArray;
|
||||
class OutOfLineNewObject;
|
||||
@ -307,6 +313,9 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
||||
void visitLoadUnboxedPointerV(LLoadUnboxedPointerV* lir);
|
||||
void visitLoadUnboxedPointerT(LLoadUnboxedPointerT* lir);
|
||||
void visitUnboxObjectOrNull(LUnboxObjectOrNull* lir);
|
||||
template <SwitchTableType tableType>
|
||||
void visitOutOfLineSwitch(OutOfLineSwitch<tableType>* ool);
|
||||
void visitLoadElementFromStateV(LLoadElementFromStateV* lir);
|
||||
void visitStoreElementT(LStoreElementT* lir);
|
||||
void visitStoreElementV(LStoreElementV* lir);
|
||||
template <typename T> void emitStoreElementHoleT(T* lir);
|
||||
|
@ -7735,13 +7735,20 @@ IonBuilder::jsop_getelem()
|
||||
if (emitted)
|
||||
return Ok();
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlined);
|
||||
MOZ_TRY(getElemTryArgumentsInlined(&emitted, obj, index));
|
||||
trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlinedConstant);
|
||||
MOZ_TRY(getElemTryArgumentsInlinedConstant(&emitted, obj, index));
|
||||
if (emitted)
|
||||
return Ok();
|
||||
|
||||
if (script()->argumentsHasVarBinding())
|
||||
trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlinedSwitch);
|
||||
MOZ_TRY(getElemTryArgumentsInlinedIndex(&emitted, obj, index));
|
||||
if (emitted)
|
||||
return Ok();
|
||||
|
||||
if (script()->argumentsHasVarBinding()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
|
||||
return abort(AbortReason::Disable, "Type is not definitely lazy arguments.");
|
||||
}
|
||||
}
|
||||
|
||||
obj = maybeUnboxForPropertyAccess(obj);
|
||||
@ -8393,7 +8400,7 @@ IonBuilder::getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* in
|
||||
}
|
||||
|
||||
AbortReasonOr<Ok>
|
||||
IonBuilder::getElemTryArgumentsInlined(bool* emitted, MDefinition* obj, MDefinition* index)
|
||||
IonBuilder::getElemTryArgumentsInlinedConstant(bool* emitted, MDefinition* obj, MDefinition* index)
|
||||
{
|
||||
MOZ_ASSERT(*emitted == false);
|
||||
|
||||
@ -8403,31 +8410,83 @@ IonBuilder::getElemTryArgumentsInlined(bool* emitted, MDefinition* obj, MDefinit
|
||||
if (obj->type() != MIRType::MagicOptimizedArguments)
|
||||
return Ok();
|
||||
|
||||
MConstant* indexConst = index->maybeConstantValue();
|
||||
if (!indexConst || indexConst->type() != MIRType::Int32)
|
||||
return Ok();
|
||||
|
||||
// Emit inlined arguments.
|
||||
obj->setImplicitlyUsedUnchecked();
|
||||
|
||||
MOZ_ASSERT(!info().argsObjAliasesFormals());
|
||||
|
||||
// When the id is constant, we can just return the corresponding inlined argument
|
||||
MConstant* indexConst = index->maybeConstantValue();
|
||||
if (indexConst && indexConst->type() == MIRType::Int32) {
|
||||
MOZ_ASSERT(inliningDepth_ > 0);
|
||||
MOZ_ASSERT(inliningDepth_ > 0);
|
||||
|
||||
int32_t id = indexConst->toInt32();
|
||||
index->setImplicitlyUsedUnchecked();
|
||||
int32_t id = indexConst->toInt32();
|
||||
index->setImplicitlyUsedUnchecked();
|
||||
|
||||
if (id < (int32_t)inlineCallInfo_->argc() && id >= 0)
|
||||
current->push(inlineCallInfo_->getArg(id));
|
||||
else
|
||||
pushConstant(UndefinedValue());
|
||||
if (id < (int32_t)inlineCallInfo_->argc() && id >= 0)
|
||||
current->push(inlineCallInfo_->getArg(id));
|
||||
else
|
||||
pushConstant(UndefinedValue());
|
||||
|
||||
trackOptimizationSuccess();
|
||||
*emitted = true;
|
||||
trackOptimizationSuccess();
|
||||
*emitted = true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
AbortReasonOr<Ok>
|
||||
IonBuilder::getElemTryArgumentsInlinedIndex(bool* emitted, MDefinition* obj, MDefinition* index)
|
||||
{
|
||||
MOZ_ASSERT(*emitted == false);
|
||||
|
||||
if (inliningDepth_ == 0)
|
||||
return Ok();
|
||||
|
||||
if (obj->type() != MIRType::MagicOptimizedArguments)
|
||||
return Ok();
|
||||
|
||||
if (!IsNumberType(index->type()))
|
||||
return Ok();
|
||||
|
||||
// Currently, we do not support any arguments vector larger than 10, as this
|
||||
// is being translated into code at the call site, and it would be better to
|
||||
// store the arguments contiguously on the stack.
|
||||
if (inlineCallInfo_->argc() > 10) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineBound);
|
||||
return abort(AbortReason::Disable, "NYI get argument element with too many arguments");
|
||||
}
|
||||
|
||||
// inlined not constant not supported, yet.
|
||||
return abort(AbortReason::Disable, "NYI inlined not constant get argument element");
|
||||
// Emit inlined arguments.
|
||||
obj->setImplicitlyUsedUnchecked();
|
||||
|
||||
MOZ_ASSERT(!info().argsObjAliasesFormals());
|
||||
|
||||
// Ensure index is an integer.
|
||||
MInstruction* idInt32 = MToInt32::New(alloc(), index);
|
||||
current->add(idInt32);
|
||||
index = idInt32;
|
||||
|
||||
// Bailout if we read more than the number of actual arguments. This bailout
|
||||
// cannot re-enter because reading out of bounds arguments will disable the
|
||||
// lazy arguments optimization for this script, when this code would be
|
||||
// executed in Baseline. (see GetElemOptimizedArguments)
|
||||
index = addBoundsCheck(index, constantInt(inlineCallInfo_->argc()));
|
||||
|
||||
// Get an instruction to represent the state of the argument vector.
|
||||
MInstruction* args = MArgumentState::New(alloc().fallible(), inlineCallInfo_->argv());
|
||||
if (!args)
|
||||
return abort(AbortReason::Alloc);
|
||||
current->add(args);
|
||||
|
||||
// Select a value to pick from a vector.
|
||||
MInstruction* load = MLoadElementFromState::New(alloc(), args, index);
|
||||
current->add(load);
|
||||
current->push(load);
|
||||
|
||||
trackOptimizationSuccess();
|
||||
*emitted = true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
AbortReasonOr<Ok>
|
||||
|
@ -427,8 +427,10 @@ class IonBuilder
|
||||
AbortReasonOr<Ok> getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index);
|
||||
AbortReasonOr<Ok> getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index);
|
||||
AbortReasonOr<Ok> getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* index);
|
||||
AbortReasonOr<Ok> getElemTryArgumentsInlined(bool* emitted, MDefinition* obj,
|
||||
MDefinition* index);
|
||||
AbortReasonOr<Ok> getElemTryArgumentsInlinedConstant(bool* emitted, MDefinition* obj,
|
||||
MDefinition* index);
|
||||
AbortReasonOr<Ok> getElemTryArgumentsInlinedIndex(bool* emitted, MDefinition* obj,
|
||||
MDefinition* index);
|
||||
AbortReasonOr<Ok> getElemAddCache(MDefinition* obj, MDefinition* index);
|
||||
AbortReasonOr<Ok> getElemTryScalarElemOfTypedObject(bool* emitted,
|
||||
MDefinition* obj,
|
||||
|
@ -1177,6 +1177,14 @@ class LVariadicInstruction : public details::LInstructionFixedDefsTempsHelper<De
|
||||
void setOperand(size_t index, const LAllocation& a) final override {
|
||||
operands_[index] = a;
|
||||
}
|
||||
void setBoxOperand(size_t index, const LBoxAllocation& a) {
|
||||
#ifdef JS_NUNBOX32
|
||||
operands_[index + TYPE_INDEX] = a.type();
|
||||
operands_[index + PAYLOAD_INDEX] = a.payload();
|
||||
#else
|
||||
operands_[index] = a.value();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t Defs, size_t Operands, size_t Temps>
|
||||
|
@ -3235,6 +3235,73 @@ LIRGenerator::visitLoadUnboxedString(MLoadUnboxedString* ins)
|
||||
define(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitLoadElementFromState(MLoadElementFromState* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->index()->type() == MIRType::Int32);
|
||||
|
||||
LDefinition temp1 = LDefinition::BogusTemp();
|
||||
#ifdef JS_NUNBOX32
|
||||
temp1 = temp();
|
||||
#endif
|
||||
LLoadElementFromStateV* lir = new(alloc()) LLoadElementFromStateV(temp(), temp1, tempDouble());
|
||||
MOZ_ASSERT(ins->array()->isArgumentState(),
|
||||
"LIRGenerator::visitLoadElementFromState: Unsupported state object");
|
||||
MArgumentState* array = ins->array()->toArgumentState();
|
||||
|
||||
// 1 -- for the index as a register
|
||||
// BOX_PIECES * array->numElements() -- for using as operand all the
|
||||
// elements of the inlined array.
|
||||
size_t numOperands = 1 + BOX_PIECES * array->numElements();
|
||||
if (!lir->init(alloc(), numOperands)) {
|
||||
abort(AbortReason::Alloc, "OOM: LIRGenerator::visitLoadElementFromState");
|
||||
return;
|
||||
}
|
||||
lir->setOperand(0, useRegister(ins->index())); // index
|
||||
for (size_t i = 0, e = array->numElements(); i < e; i++) {
|
||||
MDefinition* elem = array->getElement(i);
|
||||
if (elem->isConstant() && elem->isEmittedAtUses()) {
|
||||
lir->setOperand(1 + BOX_PIECES * i, LAllocation());
|
||||
#ifdef JS_NUNBOX32
|
||||
lir->setOperand(1 + BOX_PIECES * i + 1, LAllocation());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (array->getElement(i)->type()) {
|
||||
case MIRType::Value:
|
||||
lir->setBoxOperand(1 + BOX_PIECES * i, useBox(elem, LUse::ANY));
|
||||
break;
|
||||
// Anything which can be boxed:
|
||||
case MIRType::Boolean:
|
||||
case MIRType::Int32:
|
||||
case MIRType::Double:
|
||||
case MIRType::Object:
|
||||
case MIRType::String:
|
||||
case MIRType::Symbol:
|
||||
lir->setOperand(1 + BOX_PIECES * i, use(elem));
|
||||
#ifdef JS_NUNBOX32
|
||||
// Bogus second operand.
|
||||
lir->setOperand(1 + BOX_PIECES * i + 1, LAllocation());
|
||||
#endif
|
||||
break;
|
||||
case MIRType::Null:
|
||||
case MIRType::Undefined:
|
||||
// Bogus operand, as these can be inlined.
|
||||
lir->setOperand(1 + BOX_PIECES * i, LAllocation());
|
||||
#ifdef JS_NUNBOX32
|
||||
lir->setOperand(1 + BOX_PIECES * i + 1, LAllocation());
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("LIRGenerator::visitLoadElementFromState: Unsupported element type.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
defineBox(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitStoreElement(MStoreElement* ins)
|
||||
{
|
||||
@ -5130,6 +5197,12 @@ LIRGenerator::visitArrayState(MArrayState* objState)
|
||||
MOZ_CRASH("Unexpected ArrayState node during Lowering.");
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitArgumentState(MArgumentState* objState)
|
||||
{
|
||||
// ArgumentState nodes are always inlined at their uses.
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitUnknownValue(MUnknownValue* ins)
|
||||
{
|
||||
|
@ -232,6 +232,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitLoadElementHole(MLoadElementHole* ins);
|
||||
void visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins);
|
||||
void visitLoadUnboxedString(MLoadUnboxedString* ins);
|
||||
void visitLoadElementFromState(MLoadElementFromState* ins);
|
||||
void visitStoreElement(MStoreElement* ins);
|
||||
void visitStoreElementHole(MStoreElementHole* ins);
|
||||
void visitFallibleStoreElement(MFallibleStoreElement* ins);
|
||||
@ -325,6 +326,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitBeta(MBeta* ins);
|
||||
void visitObjectState(MObjectState* ins);
|
||||
void visitArrayState(MArrayState* ins);
|
||||
void visitArgumentState(MArgumentState* ins);
|
||||
void visitUnknownValue(MUnknownValue* ins);
|
||||
void visitLexicalCheck(MLexicalCheck* ins);
|
||||
void visitThrowRuntimeLexicalError(MThrowRuntimeLexicalError* ins);
|
||||
|
@ -5098,6 +5098,28 @@ MArrayState::Copy(TempAllocator& alloc, MArrayState* state)
|
||||
return res;
|
||||
}
|
||||
|
||||
MArgumentState*
|
||||
MArgumentState::New(TempAllocator::Fallible view, const MDefinitionVector& args)
|
||||
{
|
||||
MArgumentState* res = new(view.alloc) MArgumentState();
|
||||
if (!res || !res->init(view.alloc, args.length()))
|
||||
return nullptr;
|
||||
for (size_t i = 0, e = args.length(); i < e; i++)
|
||||
res->initOperand(i, args[i]);
|
||||
return res;
|
||||
}
|
||||
|
||||
MArgumentState*
|
||||
MArgumentState::Copy(TempAllocator& alloc, MArgumentState* state)
|
||||
{
|
||||
MArgumentState* res = new(alloc) MArgumentState();
|
||||
if (!res || !res->init(alloc, state->numElements()))
|
||||
return nullptr;
|
||||
for (size_t i = 0, e = res->numOperands(); i < e; i++)
|
||||
res->initOperand(i, state->getOperand(i));
|
||||
return res;
|
||||
}
|
||||
|
||||
MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t length, MConstant* templateConst,
|
||||
gc::InitialHeap initialHeap, jsbytecode* pc, bool vmCall)
|
||||
: MUnaryInstruction(templateConst),
|
||||
|
@ -3957,6 +3957,41 @@ class MArrayState
|
||||
}
|
||||
};
|
||||
|
||||
// Hold the arguments of an inlined frame. At the moment this class is not
|
||||
// recovered on bailout as it does not have an implementation and it should
|
||||
// be inlined at all its uses.
|
||||
class MArgumentState
|
||||
: public MVariadicInstruction,
|
||||
public NoFloatPolicyAfter<0>::Data
|
||||
{
|
||||
private:
|
||||
explicit MArgumentState() {
|
||||
setResultType(MIRType::Object);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(ArgumentState)
|
||||
|
||||
static MArgumentState* New(TempAllocator::Fallible view, const MDefinitionVector& args);
|
||||
static MArgumentState* Copy(TempAllocator& alloc, MArgumentState* state);
|
||||
|
||||
size_t numElements() const {
|
||||
return numOperands();
|
||||
}
|
||||
|
||||
MDefinition* getElement(uint32_t index) const {
|
||||
return getOperand(index);
|
||||
}
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
// Setting __proto__ in an object literal.
|
||||
class MMutateProto
|
||||
: public MAryInstruction<2>,
|
||||
@ -9656,6 +9691,30 @@ class MStoreElementCommon
|
||||
}
|
||||
};
|
||||
|
||||
// This instruction is used to load an element of a non-escaped inlined array.
|
||||
class MLoadElementFromState
|
||||
: public MBinaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
MLoadElementFromState(MDefinition* array, MDefinition* index)
|
||||
: MBinaryInstruction(array, index)
|
||||
{
|
||||
MOZ_ASSERT(array->isArgumentState());
|
||||
MOZ_ASSERT(index->type() == MIRType::Int32);
|
||||
setResultType(MIRType::Value);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(LoadElementFromState)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, array), (1, index));
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
// Store a value to a dense array slots vector.
|
||||
class MStoreElement
|
||||
: public MAryInstruction<3>,
|
||||
@ -12691,7 +12750,7 @@ class MArgumentsLength : public MNullaryInstruction
|
||||
AliasSet getAliasSet() const override {
|
||||
// Arguments |length| cannot be mutated by Ion Code.
|
||||
return AliasSet::None();
|
||||
}
|
||||
}
|
||||
|
||||
void computeRange(TempAllocator& alloc) override;
|
||||
|
||||
|
@ -144,6 +144,7 @@ namespace jit {
|
||||
_(NewStringObject) \
|
||||
_(ObjectState) \
|
||||
_(ArrayState) \
|
||||
_(ArgumentState) \
|
||||
_(InitElem) \
|
||||
_(InitElemGetterSetter) \
|
||||
_(MutateProto) \
|
||||
@ -217,6 +218,7 @@ namespace jit {
|
||||
_(LoadUnboxedScalar) \
|
||||
_(LoadUnboxedObjectOrNull) \
|
||||
_(LoadUnboxedString) \
|
||||
_(LoadElementFromState) \
|
||||
_(StoreElement) \
|
||||
_(StoreElementHole) \
|
||||
_(FallibleStoreElement) \
|
||||
|
@ -587,6 +587,7 @@ NoFloatPolicyAfter<FirstOp>::adjustInputs(TempAllocator& alloc, MInstruction* de
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool NoFloatPolicyAfter<0>::adjustInputs(TempAllocator& alloc, MInstruction* def);
|
||||
template bool NoFloatPolicyAfter<1>::adjustInputs(TempAllocator& alloc, MInstruction* def);
|
||||
template bool NoFloatPolicyAfter<2>::adjustInputs(TempAllocator& alloc, MInstruction* def);
|
||||
|
||||
@ -1268,6 +1269,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
|
||||
_(MixPolicy<StringPolicy<0>, StringPolicy<1> >) \
|
||||
_(MixPolicy<BoxPolicy<0>, BoxPolicy<1> >) \
|
||||
_(NoFloatPolicy<0>) \
|
||||
_(NoFloatPolicyAfter<0>) \
|
||||
_(NoFloatPolicyAfter<1>) \
|
||||
_(NoFloatPolicyAfter<2>) \
|
||||
_(ObjectPolicy<0>) \
|
||||
|
@ -387,6 +387,12 @@ class Assembler : public vixl::Assembler
|
||||
LabelBase* label = absoluteLabel;
|
||||
label->bind(off.getOffset());
|
||||
}
|
||||
void writeCodePointer(CodeOffset* label) {
|
||||
uintptr_t x = LabelBase::INVALID_OFFSET;
|
||||
BufferOffset off = EmitData(&x, sizeof(uintptr_t));
|
||||
label->bind(off.getOffset());
|
||||
}
|
||||
|
||||
|
||||
void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
|
||||
const Disassembler::HeapAccess& heapAccess)
|
||||
|
@ -5744,6 +5744,47 @@ class LUnboxObjectOrNull : public LInstructionHelper<1, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Load an element from a non-allocated entity represented by its state object
|
||||
// such as ArgumentState. The elements of the state object are set as operands
|
||||
// of this variadic LIR instruction and inlined in the code generated for this
|
||||
// instruction.
|
||||
//
|
||||
// Each element is represented with BOX_PIECES allocations, even if 1 (typed
|
||||
// register) or 0 (constants) is enough. In such case, the unused allocations
|
||||
// would be bogus.
|
||||
class LLoadElementFromStateV : public LVariadicInstruction<BOX_PIECES, 3>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(LoadElementFromStateV)
|
||||
|
||||
LLoadElementFromStateV(const LDefinition& temp0, const LDefinition& temp1,
|
||||
const LDefinition& tempD)
|
||||
{
|
||||
setTemp(0, temp0);
|
||||
setTemp(1, temp1);
|
||||
setTemp(2, tempD);
|
||||
}
|
||||
|
||||
const MLoadElementFromState* mir() const {
|
||||
return mir_->toLoadElementFromState();
|
||||
}
|
||||
const LAllocation* index() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LDefinition* temp0() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp1() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const LDefinition* tempD() {
|
||||
return getTemp(2);
|
||||
}
|
||||
MDefinition* array() {
|
||||
return mir()->array();
|
||||
}
|
||||
};
|
||||
|
||||
// Store a boxed value to a dense array's element vector.
|
||||
class LStoreElementV : public LInstructionHelper<0, 2 + BOX_PIECES, 0>
|
||||
{
|
||||
|
@ -283,6 +283,7 @@
|
||||
_(LoadUnboxedScalar) \
|
||||
_(LoadUnboxedPointerV) \
|
||||
_(LoadUnboxedPointerT) \
|
||||
_(LoadElementFromStateV) \
|
||||
_(UnboxObjectOrNull) \
|
||||
_(StoreElementV) \
|
||||
_(StoreElementT) \
|
||||
|
@ -159,9 +159,9 @@ LIRGeneratorShared::defineInt64ReuseInput(LInstructionHelper<INT64_PIECES, Ops,
|
||||
|
||||
}
|
||||
|
||||
template <size_t Ops, size_t Temps> void
|
||||
LIRGeneratorShared::defineBox(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir,
|
||||
LDefinition::Policy policy)
|
||||
template <size_t Temps> void
|
||||
LIRGeneratorShared::defineBox(details::LInstructionFixedDefsTempsHelper<BOX_PIECES, Temps>* lir,
|
||||
MDefinition* mir, LDefinition::Policy policy)
|
||||
{
|
||||
// Call instructions should use defineReturn.
|
||||
MOZ_ASSERT(!lir->isCall());
|
||||
|
@ -157,9 +157,9 @@ class LIRGeneratorShared : public MDefinitionVisitor
|
||||
inline void defineFixed(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir,
|
||||
const LAllocation& output);
|
||||
|
||||
template <size_t Ops, size_t Temps>
|
||||
inline void defineBox(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir,
|
||||
LDefinition::Policy policy = LDefinition::REGISTER);
|
||||
template <size_t Temps>
|
||||
inline void defineBox(details::LInstructionFixedDefsTempsHelper<BOX_PIECES, Temps>* lir,
|
||||
MDefinition* mir, LDefinition::Policy policy = LDefinition::REGISTER);
|
||||
|
||||
template <size_t Ops, size_t Temps>
|
||||
inline void defineInt64(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
|
||||
|
Loading…
Reference in New Issue
Block a user