mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 16:46:26 +00:00
Bug 1112156 - Add MSimdUnbox to extract SIMD values from the TypedObjects. r=bbouvier
This commit is contained in:
parent
27d6f52ec9
commit
0cb9c4ed18
108
js/src/jit-test/tests/ion/simd-unbox.js
Normal file
108
js/src/jit-test/tests/ion/simd-unbox.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
if (typeof SIMD === "undefined")
|
||||||
|
quit();
|
||||||
|
|
||||||
|
setJitCompilerOption("baseline.warmup.trigger", 10);
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 30);
|
||||||
|
var max = 40, pivot = 35;
|
||||||
|
|
||||||
|
var i32x4 = SIMD.int32x4;
|
||||||
|
var f32x4 = SIMD.float32x4;
|
||||||
|
var i32x4Add = SIMD.int32x4.add;
|
||||||
|
|
||||||
|
var FakeSIMDType = function (o) { this.x = o.x; this.y = o.y; this.z = o.z; this.w = o.w; };
|
||||||
|
if (this.hasOwnProperty("TypedObject")) {
|
||||||
|
var TO = TypedObject;
|
||||||
|
FakeSIMDType = new TO.StructType({ x: TO.int32, y: TO.int32, z: TO.int32, w: TO.int32 });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function simdunbox_bail_undef(i, lhs, rhs) {
|
||||||
|
return i32x4Add(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function simdunbox_bail_object(i, lhs, rhs) {
|
||||||
|
return i32x4Add(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function simdunbox_bail_typeobj(i, lhs, rhs) {
|
||||||
|
return i32x4Add(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function simdunbox_bail_badsimd(i, lhs, rhs) {
|
||||||
|
return i32x4Add(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
var arr_undef = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||||
|
var fail_undef = 0;
|
||||||
|
var arr_object = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||||
|
var fail_object = 0;
|
||||||
|
var arr_typeobj = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||||
|
var fail_typeobj = 0;
|
||||||
|
var arr_badsimd = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||||
|
var fail_badsimd = 0;
|
||||||
|
for (var i = 0; i < max; i++) {
|
||||||
|
try {
|
||||||
|
arr_undef[i + 2] = simdunbox_bail_undef(i, arr_undef[i], arr_undef[i + 1]);
|
||||||
|
} catch (x) {
|
||||||
|
arr_undef[i + 2] = arr_undef[i - 1];
|
||||||
|
fail_undef++;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
arr_object[i + 2] = simdunbox_bail_object(i, arr_object[i], arr_object[i + 1]);
|
||||||
|
} catch (x) {
|
||||||
|
arr_object[i + 2] = arr_object[i - 1];
|
||||||
|
fail_object++;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
arr_typeobj[i + 2] = simdunbox_bail_typeobj(i, arr_typeobj[i], arr_typeobj[i + 1]);
|
||||||
|
} catch (x) {
|
||||||
|
arr_typeobj[i + 2] = arr_typeobj[i - 1];
|
||||||
|
fail_typeobj++;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
arr_badsimd[i + 2] = simdunbox_bail_badsimd(i, arr_badsimd[i], arr_badsimd[i + 1]);
|
||||||
|
} catch (x) {
|
||||||
|
arr_badsimd[i + 2] = arr_badsimd[i - 1];
|
||||||
|
fail_badsimd++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 2 == pivot) {
|
||||||
|
arr_undef[pivot] = undefined;
|
||||||
|
arr_object[pivot] = { x: 0, y: 1, z: 2, w: 3 };
|
||||||
|
arr_typeobj[pivot] = new FakeSIMDType({ x: 0, y: 1, z: 2, w: 3 });
|
||||||
|
arr_badsimd[pivot] = f32x4(0, 1, 2, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEq(fail_undef, 2);
|
||||||
|
assertEq(fail_object, 2);
|
||||||
|
assertEq(fail_typeobj, 2);
|
||||||
|
assertEq(fail_badsimd, 2);
|
||||||
|
|
||||||
|
// Assert that all SIMD values are correct.
|
||||||
|
function assertEqX4(real, expected, assertFunc) {
|
||||||
|
if (typeof assertFunc === 'undefined')
|
||||||
|
assertFunc = assertEq;
|
||||||
|
|
||||||
|
assertFunc(real.x, expected[0]);
|
||||||
|
assertFunc(real.y, expected[1]);
|
||||||
|
assertFunc(real.z, expected[2]);
|
||||||
|
assertFunc(real.w, expected[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fib = [0, 1];
|
||||||
|
for (i = 0; i < max + 5; i++)
|
||||||
|
fib[i+2] = (fib[i] + fib[i+1]) | 0;
|
||||||
|
|
||||||
|
for (i = 0; i < max; i++) {
|
||||||
|
if (i == pivot)
|
||||||
|
continue;
|
||||||
|
var ref = fib.slice(i < pivot ? i : i - 3);
|
||||||
|
assertEqX4(arr_undef[i], ref);
|
||||||
|
assertEqX4(arr_object[i], ref);
|
||||||
|
assertEqX4(arr_typeobj[i], ref);
|
||||||
|
assertEqX4(arr_badsimd[i], ref);
|
||||||
|
}
|
@ -4233,6 +4233,74 @@ CodeGenerator::visitSimdBox(LSimdBox *lir)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdUnbox(LSimdUnbox *lir)
|
||||||
|
{
|
||||||
|
Register object = ToRegister(lir->input());
|
||||||
|
FloatRegister simd = ToFloatRegister(lir->output());
|
||||||
|
Register temp = ToRegister(lir->temp());
|
||||||
|
Label bail;
|
||||||
|
|
||||||
|
// obj->type()
|
||||||
|
masm.loadPtr(Address(object, JSObject::offsetOfType()), temp);
|
||||||
|
|
||||||
|
// Guard that the object has the same representation as the one produced for
|
||||||
|
// SIMD value-type.
|
||||||
|
Address clasp(temp, types::TypeObject::offsetOfClasp());
|
||||||
|
static_assert(!SimdTypeDescr::Opaque, "SIMD objects are transparent");
|
||||||
|
masm.branchPtr(Assembler::NotEqual, clasp, ImmPtr(&InlineTransparentTypedObject::class_),
|
||||||
|
&bail);
|
||||||
|
|
||||||
|
// obj->type()->typeDescr()
|
||||||
|
// The previous class pointer comparison implies that the addendumKind is
|
||||||
|
// Addendum_TypeDescr.
|
||||||
|
masm.loadPtr(Address(temp, types::TypeObject::offsetOfAddendum()), temp);
|
||||||
|
|
||||||
|
// Check for the /Kind/ reserved slot of the TypeDescr. This is an Int32
|
||||||
|
// Value which is equivalent to the object class check.
|
||||||
|
static_assert(JS_DESCR_SLOT_KIND < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots");
|
||||||
|
Address typeDescrKind(temp, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_KIND));
|
||||||
|
masm.assertTestInt32(Assembler::Equal, typeDescrKind,
|
||||||
|
"MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_KIND).isInt32())");
|
||||||
|
masm.branch32(Assembler::NotEqual, masm.ToPayload(typeDescrKind), Imm32(js::type::Simd), &bail);
|
||||||
|
|
||||||
|
// Convert the SIMD MIRType to a SimdTypeDescr::Type.
|
||||||
|
js::SimdTypeDescr::Type type;
|
||||||
|
switch (lir->mir()->type()) {
|
||||||
|
case MIRType_Int32x4:
|
||||||
|
type = js::SimdTypeDescr::TYPE_INT32;
|
||||||
|
break;
|
||||||
|
case MIRType_Float32x4:
|
||||||
|
type = js::SimdTypeDescr::TYPE_FLOAT32;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unexpected SIMD Type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the SimdTypeDescr /Type/ match the specialization of this
|
||||||
|
// MSimdUnbox instruction.
|
||||||
|
static_assert(JS_DESCR_SLOT_TYPE < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots");
|
||||||
|
Address typeDescrType(temp, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_TYPE));
|
||||||
|
masm.assertTestInt32(Assembler::Equal, typeDescrType,
|
||||||
|
"MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_TYPE).isInt32())");
|
||||||
|
masm.branch32(Assembler::NotEqual, masm.ToPayload(typeDescrType), Imm32(type), &bail);
|
||||||
|
|
||||||
|
// Load the value from the data of the InlineTypedObject.
|
||||||
|
Address objectData(object, InlineTypedObject::offsetOfDataStart());
|
||||||
|
switch (lir->mir()->type()) {
|
||||||
|
case MIRType_Int32x4:
|
||||||
|
masm.loadUnalignedInt32x4(objectData, simd);
|
||||||
|
break;
|
||||||
|
case MIRType_Float32x4:
|
||||||
|
masm.loadUnalignedFloat32x4(objectData, simd);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("The impossible happened!");
|
||||||
|
}
|
||||||
|
|
||||||
|
bailoutFrom(&bail, lir->snapshot());
|
||||||
|
}
|
||||||
|
|
||||||
typedef js::DeclEnvObject *(*NewDeclEnvObjectFn)(JSContext *, HandleFunction, gc::InitialHeap);
|
typedef js::DeclEnvObject *(*NewDeclEnvObjectFn)(JSContext *, HandleFunction, gc::InitialHeap);
|
||||||
static const VMFunction NewDeclEnvObjectInfo =
|
static const VMFunction NewDeclEnvObjectInfo =
|
||||||
FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject);
|
FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject);
|
||||||
|
@ -156,6 +156,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||||||
void visitOutOfLineNewObject(OutOfLineNewObject *ool);
|
void visitOutOfLineNewObject(OutOfLineNewObject *ool);
|
||||||
void visitNewTypedObject(LNewTypedObject *lir);
|
void visitNewTypedObject(LNewTypedObject *lir);
|
||||||
void visitSimdBox(LSimdBox *lir);
|
void visitSimdBox(LSimdBox *lir);
|
||||||
|
void visitSimdUnbox(LSimdUnbox *lir);
|
||||||
void visitNewDeclEnvObject(LNewDeclEnvObject *lir);
|
void visitNewDeclEnvObject(LNewDeclEnvObject *lir);
|
||||||
void visitNewCallObject(LNewCallObject *lir);
|
void visitNewCallObject(LNewCallObject *lir);
|
||||||
void visitNewSingletonCallObject(LNewSingletonCallObject *lir);
|
void visitNewSingletonCallObject(LNewSingletonCallObject *lir);
|
||||||
|
@ -157,6 +157,26 @@ class LSimdBox : public LInstructionHelper<1, 1, 1>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LSimdUnbox : public LInstructionHelper<1, 1, 1>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LIR_HEADER(SimdUnbox)
|
||||||
|
|
||||||
|
LSimdUnbox(const LAllocation &obj, const LDefinition &temp)
|
||||||
|
{
|
||||||
|
setOperand(0, obj);
|
||||||
|
setTemp(0, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
const LDefinition *temp() {
|
||||||
|
return getTemp(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
MSimdUnbox *mir() const {
|
||||||
|
return mir_->toSimdUnbox();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Constructs a SIMD value with 4 equal components (e.g. int32x4, float32x4).
|
// Constructs a SIMD value with 4 equal components (e.g. int32x4, float32x4).
|
||||||
class LSimdSplatX4 : public LInstructionHelper<1, 1, 0>
|
class LSimdSplatX4 : public LInstructionHelper<1, 1, 0>
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
_(Double) \
|
_(Double) \
|
||||||
_(Float32) \
|
_(Float32) \
|
||||||
_(SimdBox) \
|
_(SimdBox) \
|
||||||
|
_(SimdUnbox) \
|
||||||
_(SimdSplatX4) \
|
_(SimdSplatX4) \
|
||||||
_(Int32x4) \
|
_(Int32x4) \
|
||||||
_(Float32x4) \
|
_(Float32x4) \
|
||||||
|
@ -3734,6 +3734,30 @@ LIRGenerator::visitSimdBox(MSimdBox *ins)
|
|||||||
define(lir, ins);
|
define(lir, ins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdUnbox(MSimdUnbox *ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(ins->input()->type() == MIRType_Object);
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->type()));
|
||||||
|
LUse in = useRegister(ins->input());
|
||||||
|
|
||||||
|
BailoutKind kind;
|
||||||
|
switch (ins->type()) {
|
||||||
|
case MIRType_Int32x4:
|
||||||
|
kind = Bailout_NonSimdInt32x4Input;
|
||||||
|
break;
|
||||||
|
case MIRType_Float32x4:
|
||||||
|
kind = Bailout_NonSimdFloat32x4Input;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unexpected SIMD Type.");
|
||||||
|
};
|
||||||
|
|
||||||
|
LSimdUnbox *lir = new(alloc()) LSimdUnbox(in, temp());
|
||||||
|
assignSnapshot(lir, kind);
|
||||||
|
define(lir, ins);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LIRGenerator::visitSimdConstant(MSimdConstant *ins)
|
LIRGenerator::visitSimdConstant(MSimdConstant *ins)
|
||||||
{
|
{
|
||||||
|
@ -266,6 +266,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||||||
void visitRecompileCheck(MRecompileCheck *ins);
|
void visitRecompileCheck(MRecompileCheck *ins);
|
||||||
void visitMemoryBarrier(MMemoryBarrier *ins);
|
void visitMemoryBarrier(MMemoryBarrier *ins);
|
||||||
void visitSimdBox(MSimdBox *ins);
|
void visitSimdBox(MSimdBox *ins);
|
||||||
|
void visitSimdUnbox(MSimdUnbox *ins);
|
||||||
void visitSimdExtractElement(MSimdExtractElement *ins);
|
void visitSimdExtractElement(MSimdExtractElement *ins);
|
||||||
void visitSimdInsertElement(MSimdInsertElement *ins);
|
void visitSimdInsertElement(MSimdInsertElement *ins);
|
||||||
void visitSimdSignMask(MSimdSignMask *ins);
|
void visitSimdSignMask(MSimdSignMask *ins);
|
||||||
|
@ -2984,6 +2984,33 @@ class MSimdBox
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MSimdUnbox
|
||||||
|
: public MUnaryInstruction,
|
||||||
|
public SingleObjectPolicy::Data
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
MSimdUnbox(MDefinition *op, MIRType type)
|
||||||
|
: MUnaryInstruction(op)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsSimdType(type));
|
||||||
|
setMovable();
|
||||||
|
setResultType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
INSTRUCTION_HEADER(SimdUnbox)
|
||||||
|
ALLOW_CLONE(MSimdUnbox)
|
||||||
|
|
||||||
|
static MSimdUnbox *New(TempAllocator &alloc, MDefinition *op, MIRType type)
|
||||||
|
{
|
||||||
|
return new(alloc) MSimdUnbox(op, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
AliasSet getAliasSet() const MOZ_OVERRIDE {
|
||||||
|
return AliasSet::None();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Creates a new derived type object. At runtime, this is just a call
|
// Creates a new derived type object. At runtime, this is just a call
|
||||||
// to `BinaryBlock::createDerived()`. That is, the MIR itself does not
|
// to `BinaryBlock::createDerived()`. That is, the MIR itself does not
|
||||||
// compile to particularly optimized code. However, using a distinct
|
// compile to particularly optimized code. However, using a distinct
|
||||||
|
@ -13,6 +13,7 @@ namespace jit {
|
|||||||
#define MIR_OPCODE_LIST(_) \
|
#define MIR_OPCODE_LIST(_) \
|
||||||
_(Constant) \
|
_(Constant) \
|
||||||
_(SimdBox) \
|
_(SimdBox) \
|
||||||
|
_(SimdUnbox) \
|
||||||
_(SimdValueX4) \
|
_(SimdValueX4) \
|
||||||
_(SimdSplatX4) \
|
_(SimdSplatX4) \
|
||||||
_(SimdConstant) \
|
_(SimdConstant) \
|
||||||
|
@ -1356,6 +1356,20 @@ MacroAssembler::assumeUnreachable(const char *output)
|
|||||||
breakpoint();
|
breakpoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void
|
||||||
|
MacroAssembler::assertTestInt32(Condition cond, const T &value, const char *output)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
Label ok;
|
||||||
|
branchTestInt32(cond, value, &ok);
|
||||||
|
assumeUnreachable(output);
|
||||||
|
bind(&ok);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template void MacroAssembler::assertTestInt32(Condition, const Address &, const char *);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
Printf0_(const char *output) {
|
Printf0_(const char *output) {
|
||||||
// Use stderr instead of stdout because this is only used for debug
|
// Use stderr instead of stdout because this is only used for debug
|
||||||
|
@ -976,6 +976,10 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||||||
void link(JitCode *code);
|
void link(JitCode *code);
|
||||||
|
|
||||||
void assumeUnreachable(const char *output);
|
void assumeUnreachable(const char *output);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void assertTestInt32(Condition cond, const T &value, const char *output);
|
||||||
|
|
||||||
void printf(const char *output);
|
void printf(const char *output);
|
||||||
void printf(const char *output, Register value);
|
void printf(const char *output, Register value);
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ class MacroAssemblerARM : public Assembler
|
|||||||
// address.
|
// address.
|
||||||
Register secondScratchReg_;
|
Register secondScratchReg_;
|
||||||
|
|
||||||
|
public:
|
||||||
// Higher level tag testing code.
|
// Higher level tag testing code.
|
||||||
Operand ToPayload(Operand base) {
|
Operand ToPayload(Operand base) {
|
||||||
return Operand(Register::FromCode(base.base()), base.disp());
|
return Operand(Register::FromCode(base.base()), base.disp());
|
||||||
@ -45,6 +46,8 @@ class MacroAssemblerARM : public Assembler
|
|||||||
Address ToPayload(Address base) {
|
Address ToPayload(Address base) {
|
||||||
return ToPayload(Operand(base)).toAddress();
|
return ToPayload(Operand(base)).toAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
Operand ToType(Operand base) {
|
Operand ToType(Operand base) {
|
||||||
return Operand(Register::FromCode(base.base()), base.disp() + sizeof(void *));
|
return Operand(Register::FromCode(base.base()), base.disp() + sizeof(void *));
|
||||||
}
|
}
|
||||||
|
@ -67,12 +67,13 @@ static_assert(1 << defaultShift == sizeof(jsval), "The defaultShift is wrong");
|
|||||||
|
|
||||||
class MacroAssemblerMIPS : public Assembler
|
class MacroAssemblerMIPS : public Assembler
|
||||||
{
|
{
|
||||||
protected:
|
public:
|
||||||
// higher level tag testing code
|
// higher level tag testing code
|
||||||
Operand ToPayload(Operand base);
|
Operand ToPayload(Operand base);
|
||||||
Address ToPayload(Address base) {
|
Address ToPayload(Address base) {
|
||||||
return ToPayload(Operand(base)).toAddress();
|
return ToPayload(Operand(base)).toAddress();
|
||||||
}
|
}
|
||||||
|
protected:
|
||||||
Operand ToType(Operand base);
|
Operand ToType(Operand base);
|
||||||
Address ToType(Address base) {
|
Address ToType(Address base) {
|
||||||
return ToType(Operand(base)).toAddress();
|
return ToType(Operand(base)).toAddress();
|
||||||
|
@ -161,6 +161,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
|||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
// X86/X64-common interface.
|
// X86/X64-common interface.
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
Address ToPayload(Address value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
void storeValue(ValueOperand val, Operand dest) {
|
void storeValue(ValueOperand val, Operand dest) {
|
||||||
movq(val.valueReg(), dest);
|
movq(val.valueReg(), dest);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user