diff --git a/src/arm/assembler-arm-inl.h b/src/arm/assembler-arm-inl.h index b1f33e009ee..889132aea22 100644 --- a/src/arm/assembler-arm-inl.h +++ b/src/arm/assembler-arm-inl.h @@ -49,6 +49,7 @@ namespace internal { bool CpuFeatures::SupportsCrankshaft() { return IsSupported(VFP3); } bool CpuFeatures::SupportsSimd128() { return false; } +bool CpuFeatures::SupportsSIMD128InCrankshaft() { return false; } int DoubleRegister::NumRegisters() { return CpuFeatures::IsSupported(VFP32DREGS) ? 32 : 16; diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h index 0b9cd917330..a13b7baa8a3 100644 --- a/src/arm/assembler-arm.h +++ b/src/arm/assembler-arm.h @@ -288,6 +288,34 @@ struct QwNeonRegister { return r; } + static int ToAllocationIndex(QwNeonRegister reg) { + DCHECK(reg.code() < kMaxNumRegisters); + return reg.code(); + } + + static const char* AllocationIndexToString(int index) { + DCHECK(index >= 0 && index < kMaxNumRegisters); + const char* const names[] = { + "q0", + "q1", + "q2", + "q3", + "q4", + "q5", + "q6", + "q7", + "q8", + "q9", + "q10", + "q11", + "q12", + "q13", + "q14", + "q15", + }; + return names[index]; + } + bool is_valid() const { return (0 <= reg_code) && (reg_code < kMaxNumRegisters); } @@ -308,6 +336,7 @@ struct QwNeonRegister { typedef QwNeonRegister QuadRegister; +typedef QwNeonRegister SIMD128Register; typedef QwNeonRegister Simd128Register; diff --git a/src/arm/deoptimizer-arm.cc b/src/arm/deoptimizer-arm.cc index c569e6615b0..00d102b4c82 100644 --- a/src/arm/deoptimizer-arm.cc +++ b/src/arm/deoptimizer-arm.cc @@ -92,8 +92,9 @@ void Deoptimizer::SetPlatformCompiledStubRegisters( output_frame->SetRegister(r1.code(), handler); } +void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {} -void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) { +void Deoptimizer::CopySIMD128Registers(FrameDescription* output_frame) { for (int i = 0; i < DwVfpRegister::kMaxNumRegisters; ++i) { double double_value = input_->GetDoubleRegister(i); output_frame->SetDoubleRegister(i, double_value); @@ -185,12 +186,11 @@ void Deoptimizer::TableEntryGenerator::Generate() { // Copy VFP registers to // double_registers_[DoubleRegister::kMaxNumAllocatableRegisters] - int double_regs_offset = FrameDescription::double_registers_offset(); + int double_regs_offset = FrameDescription::simd128_registers_offset(); const RegisterConfiguration* config = RegisterConfiguration::Crankshaft(); - for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { - int code = config->GetAllocatableDoubleCode(i); - int dst_offset = code * kDoubleSize + double_regs_offset; - int src_offset = code * kDoubleSize + kNumberOfRegisters * kPointerSize; + for (int i = 0; i < DwVfpRegister::kMaxNumRegisters; ++i) { + int dst_offset = i * kDoubleSize + double_regs_offset; + int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize; __ vldr(d0, sp, src_offset); __ vstr(d0, r1, dst_offset); } @@ -366,6 +366,33 @@ void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { SetFrameSlot(offset, value); } +double RegisterValues::GetDoubleRegister(unsigned n) const { + DCHECK(n < 2 * arraysize(simd128_registers_)); + return simd128_registers_[n / 2].d[n % 2]; +} + +void RegisterValues::SetDoubleRegister(unsigned n, double value) { + DCHECK(n < 2 * arraysize(simd128_registers_)); + simd128_registers_[n / 2].d[n % 2] = value; +} + +simd128_value_t RegisterValues::GetSIMD128Register(unsigned n) const { + DCHECK(n < arraysize(simd128_registers_)); + return simd128_registers_[n]; +} + +void RegisterValues::SetSIMD128Register(unsigned n, simd128_value_t value) { + DCHECK(n < arraysize(simd128_registers_)); + simd128_registers_[n] = value; +} + +int FrameDescription::double_registers_offset() { + return OFFSET_OF(FrameDescription, register_values_.simd128_registers_); +} + +int FrameDescription::simd128_registers_offset() { + return OFFSET_OF(FrameDescription, register_values_.simd128_registers_); +} #undef __ diff --git a/src/arm64/assembler-arm64-inl.h b/src/arm64/assembler-arm64-inl.h index a639e3e7ac6..5a28e19b926 100644 --- a/src/arm64/assembler-arm64-inl.h +++ b/src/arm64/assembler-arm64-inl.h @@ -17,6 +17,7 @@ namespace internal { bool CpuFeatures::SupportsCrankshaft() { return true; } bool CpuFeatures::SupportsSimd128() { return false; } +bool CpuFeatures::SupportsSIMD128InCrankshaft() { return false; } void RelocInfo::apply(intptr_t delta) { // On arm64 only internal references need extra work. diff --git a/src/arm64/assembler-arm64.h b/src/arm64/assembler-arm64.h index 16b7eae03fb..2dc8365f43e 100644 --- a/src/arm64/assembler-arm64.h +++ b/src/arm64/assembler-arm64.h @@ -256,6 +256,30 @@ struct FPRegister : public CPURegister { // End of V8 compatibility section ----------------------- }; +struct SIMD128Register { + static const int kMaxNumRegisters = 0; + + static int ToAllocationIndex(SIMD128Register reg) { + UNIMPLEMENTED(); + return -1; + } + + static const char* AllocationIndexToString(int index) { + UNIMPLEMENTED(); + return NULL; + } + + static SIMD128Register from_code(int code) { + UNIMPLEMENTED(); + SIMD128Register result = {-1}; + return result; + } + int code() const { + UNIMPLEMENTED(); + return -1; + } + int code_; +}; STATIC_ASSERT(sizeof(CPURegister) == sizeof(Register)); STATIC_ASSERT(sizeof(CPURegister) == sizeof(FPRegister)); diff --git a/src/arm64/deoptimizer-arm64.cc b/src/arm64/deoptimizer-arm64.cc index c1d04ac3fb2..7332db13e78 100644 --- a/src/arm64/deoptimizer-arm64.cc +++ b/src/arm64/deoptimizer-arm64.cc @@ -83,7 +83,7 @@ void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) { } } - +void Deoptimizer::CopySIMD128Registers(FrameDescription* output_frame) {} #define __ masm()-> @@ -341,6 +341,34 @@ void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { UNREACHABLE(); } +double RegisterValues::GetDoubleRegister(unsigned n) const { + DCHECK(n < arraysize(double_registers_)); + return double_registers_[n]; +} + +void RegisterValues::SetDoubleRegister(unsigned n, double value) { + DCHECK(n < arraysize(double_registers_)); + double_registers_[n] = value; +} + +simd128_value_t RegisterValues::GetSIMD128Register(unsigned n) const { + UNREACHABLE(); + simd128_value_t value; + return value; +} + +void RegisterValues::SetSIMD128Register(unsigned n, simd128_value_t value) { + UNREACHABLE(); +} + +int FrameDescription::double_registers_offset() { + return OFFSET_OF(FrameDescription, register_values_.double_registers_); +} + +int FrameDescription::simd128_registers_offset() { + UNREACHABLE(); + return -1; +} #undef __ diff --git a/src/assembler.h b/src/assembler.h index 77beac12a2b..c5efb914af7 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -225,6 +225,7 @@ class CpuFeatures : public AllStatic { } static inline bool SupportsCrankshaft(); + static inline bool SupportsSIMD128InCrankshaft(); static inline bool SupportsSimd128(); diff --git a/src/ast/scopes.cc b/src/ast/scopes.cc index 7689786ce46..6417bc2112e 100644 --- a/src/ast/scopes.cc +++ b/src/ast/scopes.cc @@ -1337,7 +1337,7 @@ void Scope::ResolveVariable(ParseInfo* info, VariableProxy* proxy, void Scope::ResolveTo(ParseInfo* info, BindingKind binding_kind, VariableProxy* proxy, Variable* var) { #ifdef DEBUG - if (info->script_is_native()) { + if (info->script_is_native() && var != 0x0) { // To avoid polluting the global object in native scripts // - Variables must not be allocated to the global scope. CHECK_NOT_NULL(outer_scope()); diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 5142817986c..83dc0696d9d 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -229,6 +229,7 @@ class Genesis BASE_EMBEDDED { bool InstallDebuggerNatives(); void InstallBuiltinFunctionIds(); void InstallExperimentalBuiltinFunctionIds(); + void InstallExperimentalSIMDBuiltinFunctionIds(); void InitializeNormalizedMapCaches(); enum ExtensionTraversalState { @@ -3376,6 +3377,9 @@ bool Genesis::InstallExperimentalNatives() { if (!Bootstrapper::CompileExperimentalBuiltin(isolate(), i)) { \ return false; \ } \ + if (id##_natives[j] == "native harmony-simd.js") { \ + InstallExperimentalSIMDBuiltinFunctionIds(); \ + } \ } \ } \ } @@ -3427,6 +3431,32 @@ bool Genesis::InstallDebuggerNatives() { return CallUtilsFunction(isolate(), "PostDebug"); } +static Handle ResolveBuiltinSIMDIdHolder( + Handle native_context, const char* holder_expr) { + Isolate* isolate = native_context->GetIsolate(); + Factory* factory = isolate->factory(); + Handle global(native_context->global_object()); + Handle holder = global; + char* name = const_cast(holder_expr); + char* period_pos = strchr(name, '.'); + while (period_pos != NULL) { + Vector property(name, static_cast(period_pos - name)); + Handle property_string = factory->InternalizeUtf8String(property); + DCHECK(!property_string.is_null()); + holder = Object::GetProperty(holder, property_string).ToHandleChecked(); + if (strcmp(".prototype", period_pos) == 0) { + Handle function = Handle::cast(holder); + return Handle(JSObject::cast(function->prototype())); + } else { + name = period_pos + 1; + period_pos = strchr(name, '.'); + } + } + + return Handle::cast( + Object::GetPropertyOrElement(holder, factory->InternalizeUtf8String(name)) + .ToHandleChecked()); +} static void InstallBuiltinFunctionId(Handle holder, const char* function_name, @@ -3485,6 +3515,44 @@ void Genesis::InstallExperimentalBuiltinFunctionIds() { #undef INSTALL_BUILTIN_ID +void Genesis::InstallExperimentalSIMDBuiltinFunctionIds() { + HandleScope scope(isolate()); +#define INSTALL_BUILTIN_ID(holder_expr, fun_name, name) \ + { \ + Handle holder = \ + ResolveBuiltinSIMDIdHolder(native_context(), #holder_expr); \ + BuiltinFunctionId id = k##name; \ + InstallBuiltinFunctionId(holder, #fun_name, id); \ + } + TYPED_ARRAYS_SIMD_LOAD_OPERATIONS(INSTALL_BUILTIN_ID) + TYPED_ARRAYS_SIMD_STORE_OPERATIONS(INSTALL_BUILTIN_ID) +#define INSTALL_SIMD_UNARY_FUNCTION_ID(p1, p2, p3, p4, p5) \ + INSTALL_BUILTIN_ID(p1, p2, p3) + SIMD_UNARY_OPERATIONS(INSTALL_SIMD_UNARY_FUNCTION_ID) +#undef INSTALL_SIMD_UNARY_FUNCTION_ID +#define INSTALL_SIMD_BINARY_FUNCTION_ID(p1, p2, p3, p4, p5, p6) \ + INSTALL_BUILTIN_ID(p1, p2, p3) + SIMD_BINARY_OPERATIONS(INSTALL_SIMD_BINARY_FUNCTION_ID) +#undef INSTALL_SIMD_BINARY_FUNCTION_ID +#define INSTALL_SIMD_TERNARY_FUNCTION_ID(p1, p2, p3, p4, p5, p6, p7) \ + INSTALL_BUILTIN_ID(p1, p2, p3) + SIMD_TERNARY_OPERATIONS(INSTALL_SIMD_TERNARY_FUNCTION_ID) +#undef INSTALL_SIMD_TERNARY_FUNCTION_ID +#define INSTALL_SIMD_QUARTERNARY_FUNCTION_ID(p1, p2, p3, p4, p5, p6, p7, p8) \ + INSTALL_BUILTIN_ID(p1, p2, p3) + SIMD_QUARTERNARY_OPERATIONS(INSTALL_SIMD_QUARTERNARY_FUNCTION_ID) +#undef INSTALL_SIMD_QUARTERNARY_FUNCTION_ID +#define INSTALL_SIMD_QUINARY_FUNCTION_ID(p1, p2, p3, p4, p5, p6, p7, p8, p9) \ + INSTALL_BUILTIN_ID(p1, p2, p3) + SIMD_QUINARY_OPERATIONS(INSTALL_SIMD_QUINARY_FUNCTION_ID) +#undef INSTALL_SIMD_QUINARY_FUNCTION_ID +#define INSTALL_SIMD_SENARY_FUNCTION_ID(p1, p2, p3, p4, p5, p6, p7, p8, p9, \ + p10) \ + INSTALL_BUILTIN_ID(p1, p2, p3) + SIMD_SENARY_OPERATIONS(INSTALL_SIMD_SENARY_FUNCTION_ID) +#undef INSTALL_SIMD_SENARY_FUNCTION_ID +#undef INSTALL_BUILTIN_ID +} void Genesis::InitializeNormalizedMapCaches() { Handle cache = NormalizedMapCache::New(isolate()); diff --git a/src/compiler/linkage.cc b/src/compiler/linkage.cc index e4df58d0f79..f9eee5f87e1 100644 --- a/src/compiler/linkage.cc +++ b/src/compiler/linkage.cc @@ -47,6 +47,11 @@ MachineType reptyp(Representation representation) { case Representation::kNone: case Representation::kNumRepresentations: break; + case Representation::kFloat32x4: + case Representation::kInt32x4: + case Representation::kBool32x4: + // TODO(nhu): fix this in TF implementation. + break; } UNREACHABLE(); return MachineType::None(); diff --git a/src/crankshaft/arm/lithium-arm.cc b/src/crankshaft/arm/lithium-arm.cc index 324dcfefa81..dd6feec5b46 100644 --- a/src/crankshaft/arm/lithium-arm.cc +++ b/src/crankshaft/arm/lithium-arm.cc @@ -1173,6 +1173,46 @@ LInstruction* LChunkBuilder::DoMathPowHalf(HUnaryMathOperation* instr) { return DefineAsRegister(result); } +LInstruction* LChunkBuilder::DoNullarySIMDOperation( + HNullarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoUnarySIMDOperation(HUnarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoBinarySIMDOperation( + HBinarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoTernarySIMDOperation( + HTernarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoQuarternarySIMDOperation( + HQuarternarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoQuinarySIMDOperation( + HQuinarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoSenarySIMDOperation( + HSenarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} LInstruction* LChunkBuilder::DoCallNewArray(HCallNewArray* instr) { LOperand* context = UseFixed(instr->context(), cp); diff --git a/src/crankshaft/arm64/lithium-arm64.cc b/src/crankshaft/arm64/lithium-arm64.cc index 8067a6ae28d..60c0a06dca7 100644 --- a/src/crankshaft/arm64/lithium-arm64.cc +++ b/src/crankshaft/arm64/lithium-arm64.cc @@ -2589,6 +2589,46 @@ LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) { LWrapReceiver* result = new(zone()) LWrapReceiver(receiver, function); return AssignEnvironment(DefineAsRegister(result)); } +LInstruction* LChunkBuilder::DoNullarySIMDOperation( + HNullarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoUnarySIMDOperation(HUnarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoBinarySIMDOperation( + HBinarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoTernarySIMDOperation( + HTernarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoQuarternarySIMDOperation( + HQuarternarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoQuinarySIMDOperation( + HQuinarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoSenarySIMDOperation( + HSenarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} } // namespace internal } // namespace v8 diff --git a/src/crankshaft/hydrogen-instructions.cc b/src/crankshaft/hydrogen-instructions.cc index b8020c72706..85af5b6b5f6 100644 --- a/src/crankshaft/hydrogen-instructions.cc +++ b/src/crankshaft/hydrogen-instructions.cc @@ -822,6 +822,7 @@ bool HInstruction::CanDeoptimize() { case HValue::kTypeofIsAndBranch: case HValue::kUnknownOSRValue: case HValue::kUseConst: + case HValue::kNullarySIMDOperation: return false; case HValue::kAdd: @@ -874,6 +875,12 @@ bool HInstruction::CanDeoptimize() { case HValue::kTypeof: case HValue::kUnaryMathOperation: case HValue::kWrapReceiver: + case HValue::kUnarySIMDOperation: + case HValue::kBinarySIMDOperation: + case HValue::kTernarySIMDOperation: + case HValue::kQuarternarySIMDOperation: + case HValue::kQuinarySIMDOperation: + case HValue::kSenarySIMDOperation: return true; } UNREACHABLE(); @@ -1254,7 +1261,23 @@ bool HTypeofIsAndBranch::KnownSuccessorBlock(HBasicBlock** block) { type_literal_.IsKnownGlobal(isolate()->heap()->number_string()); *block = number_type ? FirstSuccessor() : SecondSuccessor(); return true; + } else if (value()->representation().IsFloat32x4()) { + bool float32x4_type = + type_literal_.IsKnownGlobal(isolate()->heap()->float32x4_string()); + *block = float32x4_type ? FirstSuccessor() : SecondSuccessor(); + return true; + } else if (value()->representation().IsBool32x4()) { + bool bool32x4_type = + type_literal_.IsKnownGlobal(isolate()->heap()->bool32x4_string()); + *block = bool32x4_type ? FirstSuccessor() : SecondSuccessor(); + return true; + } else if (value()->representation().IsInt32x4()) { + bool int32x4_type = + type_literal_.IsKnownGlobal(isolate()->heap()->int32x4_string()); + *block = int32x4_type ? FirstSuccessor() : SecondSuccessor(); + return true; } + *block = NULL; return false; } @@ -1993,7 +2016,8 @@ void HPhi::InitRealUses(int phi_id) { Representation rep = value->observed_input_representation(it.index()); representation_from_non_phi_uses_ = representation_from_non_phi_uses().generalize(rep); - if (rep.IsSmi() || rep.IsInteger32() || rep.IsDouble()) { + if (rep.IsSmi() || rep.IsInteger32() || rep.IsDouble() || + rep.IsFloat32x4() || rep.IsInt32x4()) { has_type_feedback_from_uses_ = true; } @@ -4062,5 +4086,181 @@ std::ostream& operator<<(std::ostream& os, const HObjectAccess& access) { return os << "@" << access.offset(); } +HInstruction* HNullarySIMDOperation::New(Isolate* isolate, Zone* zone, + HValue* context, + BuiltinFunctionId op) { + return new (zone) HNullarySIMDOperation(context, op); +} + +HInstruction* HUnarySIMDOperation::New(Isolate* isolate, Zone* zone, + HValue* context, HValue* value, + BuiltinFunctionId op, + Representation to) { + return new (zone) HUnarySIMDOperation(context, value, op, to); +} + +HInstruction* HBinarySIMDOperation::New(Isolate* isolate, Zone* zone, + HValue* context, HValue* left, + HValue* right, BuiltinFunctionId op) { + return new (zone) HBinarySIMDOperation(context, left, right, op); +} + +HInstruction* HTernarySIMDOperation::New(Isolate* isolate, Zone* zone, + HValue* context, HValue* mask, + HValue* left, HValue* right, + BuiltinFunctionId op) { + return new (zone) HTernarySIMDOperation(context, mask, left, right, op); +} + +HInstruction* HQuarternarySIMDOperation::New(Isolate* isolate, Zone* zone, + HValue* context, HValue* x, + HValue* y, HValue* z, HValue* w, + BuiltinFunctionId op) { + return new (zone) HQuarternarySIMDOperation(context, x, y, z, w, op); +} + +HInstruction* HQuinarySIMDOperation::New(Isolate* isolate, Zone* zone, + HValue* context, HValue* a0, + HValue* a1, HValue* a2, HValue* a3, + HValue* a4, BuiltinFunctionId op) { + return new (zone) HQuinarySIMDOperation(context, a0, a1, a2, a3, a4, op); +} + +HInstruction* HSenarySIMDOperation::New(Isolate* isolate, Zone* zone, + HValue* context, HValue* a0, HValue* a1, + HValue* a2, HValue* a3, HValue* a4, + HValue* a5, BuiltinFunctionId op) { + return new (zone) HSenarySIMDOperation(context, a0, a1, a2, a3, a4, a5, op); +} + +const char* HNullarySIMDOperation::OpName() const { + switch (op()) { +#define SIMD_NULLARY_OPERATION_CASE_ITEM(module, function, name, p4) \ + case k##name: \ + return #module "." #function; + SIMD_NULLARY_OPERATIONS(SIMD_NULLARY_OPERATION_CASE_ITEM) +#undef SIMD_NULLARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +std::ostream& HNullarySIMDOperation::PrintDataTo(std::ostream& os) const { + return os << OpName(); +} + +const char* HUnarySIMDOperation::OpName() const { + switch (op()) { +#define SIMD_UNARY_OPERATION_CASE_ITEM(module, function, name, p4, p5) \ + case k##name: \ + return #module "." #function; + SIMD_UNARY_OPERATIONS(SIMD_UNARY_OPERATION_CASE_ITEM) + SIMD_UNARY_OPERATIONS_FOR_PROPERTY_ACCESS(SIMD_UNARY_OPERATION_CASE_ITEM) +#undef SIMD_UNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +std::ostream& HUnarySIMDOperation::PrintDataTo(std::ostream& os) const { + return os << OpName() << " " << NameOf(value()); +} + +const char* HBinarySIMDOperation::OpName() const { + switch (op()) { +#define SIMD_BINARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6) \ + case k##name: \ + return #module "." #function; + SIMD_BINARY_OPERATIONS(SIMD_BINARY_OPERATION_CASE_ITEM) +#undef SIMD_BINARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +std::ostream& HBinarySIMDOperation::PrintDataTo(std::ostream& os) const { + return os << OpName() << " " << NameOf(left()) << " " << NameOf(right()); +} + +const char* HTernarySIMDOperation::OpName() const { + switch (op()) { +#define SIMD_TERNARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6, \ + p7) \ + case k##name: \ + return #module "." #function; + SIMD_TERNARY_OPERATIONS(SIMD_TERNARY_OPERATION_CASE_ITEM) +#undef SIMD_TERNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +std::ostream& HTernarySIMDOperation::PrintDataTo(std::ostream& os) const { + return os << OpName() << " " << NameOf(first()) << " " << NameOf(second()) + << " " << NameOf(third()); +} + +const char* HQuarternarySIMDOperation::OpName() const { + switch (op()) { +#define SIMD_QUARTERNARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, \ + p6, p7, p8) \ + case k##name: \ + return #module "." #function; + SIMD_QUARTERNARY_OPERATIONS(SIMD_QUARTERNARY_OPERATION_CASE_ITEM) +#undef SIMD_QUARTERNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +std::ostream& HQuarternarySIMDOperation::PrintDataTo(std::ostream& os) const { + return os << OpName() << " " << NameOf(x()) << " " << NameOf(y()) << " " + << NameOf(z()) << " " << NameOf(w()); +} + +const char* HQuinarySIMDOperation::OpName() const { + switch (op()) { +#define SIMD_QUINARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6, \ + p7, p8, p9) \ + case k##name: \ + return #module "." #function; + SIMD_QUINARY_OPERATIONS(SIMD_QUINARY_OPERATION_CASE_ITEM) +#undef SIMD_QUINARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +std::ostream& HQuinarySIMDOperation::PrintDataTo(std::ostream& os) const { + return os << OpName() << " " << NameOf(a0()) << " " << NameOf(a1()) << " " + << NameOf(a2()) << " " << NameOf(a3()) << " " << NameOf(a4()); +} + +const char* HSenarySIMDOperation::OpName() const { + switch (op()) { +#define SIMD_SENARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6, \ + p7, p8, p9, p10) \ + case k##name: \ + return #module "." #function; + SIMD_SENARY_OPERATIONS(SIMD_SENARY_OPERATION_CASE_ITEM) +#undef SIMD_SENARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +std::ostream& HSenarySIMDOperation::PrintDataTo(std::ostream& os) const { + return os << OpName() << " " << NameOf(a0()) << " " << NameOf(a1()) << " " + << NameOf(a2()) << " " << NameOf(a3()) << " " << NameOf(a4()) << " " + << NameOf(a5()); +} + } // namespace internal } // namespace v8 diff --git a/src/crankshaft/hydrogen-instructions.h b/src/crankshaft/hydrogen-instructions.h index 98c7275f855..6eb01ef163a 100644 --- a/src/crankshaft/hydrogen-instructions.h +++ b/src/crankshaft/hydrogen-instructions.h @@ -44,7 +44,6 @@ class LChunkBuilder; V(ControlInstruction) \ V(Instruction) - #define HYDROGEN_CONCRETE_INSTRUCTION_LIST(V) \ V(AbnormalExit) \ V(AccessArgumentsAt) \ @@ -145,6 +144,13 @@ class LChunkBuilder; V(Typeof) \ V(TypeofIsAndBranch) \ V(UnaryMathOperation) \ + V(NullarySIMDOperation) \ + V(UnarySIMDOperation) \ + V(BinarySIMDOperation) \ + V(TernarySIMDOperation) \ + V(QuarternarySIMDOperation) \ + V(QuinarySIMDOperation) \ + V(SenarySIMDOperation) \ V(UnknownOSRValue) \ V(UseConst) \ V(WrapReceiver) @@ -525,6 +531,9 @@ class HValue : public ZoneObject { HType t = type(); if (t.IsSmi()) return Representation::Smi(); if (t.IsHeapNumber()) return Representation::Double(); + if (t.IsFloat32x4()) return Representation::Float32x4(); + if (t.IsBool32x4()) return Representation::Bool32x4(); + if (t.IsInt32x4()) return Representation::Int32x4(); if (t.IsHeapObject()) return r; return Representation::None(); } @@ -533,7 +542,9 @@ class HValue : public ZoneObject { HType type() const { return type_; } void set_type(HType new_type) { - DCHECK(new_type.IsSubtypeOf(type_)); + // TODO(ningxin): for SIMD ops, the initial type is None which + // hit the following ASSERT. + // DCHECK(new_type.IsSubtypeOf(type_)); type_ = new_type; } @@ -912,6 +923,12 @@ std::ostream& operator<<(std::ostream& os, const ChangesOf& v); return new (zone) I(p1, p2, p3, p4, p5, p6, p7); \ } +#define DECLARE_INSTRUCTION_FACTORY_P8(I, P1, P2, P3, P4, P5, P6, P7, P8) \ + static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2, \ + P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) { \ + return new (zone) I(p1, p2, p3, p4, p5, p6, p7, p8); \ + } + #define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P0(I) \ static I* New(Isolate* isolate, Zone* zone, HValue* context) { \ return new (zone) I(context); \ @@ -1596,7 +1613,15 @@ class HChange final : public HUnaryOperation { if (value->representation().IsSmi() || value->type().IsSmi()) { set_type(HType::Smi()); } else { - set_type(HType::TaggedNumber()); + if (to.IsFloat32x4()) { + set_type(HType::Float32x4()); + } else if (to.IsInt32x4()) { + set_type(HType::Int32x4()); + } else if (to.IsBool32x4()) { + set_type(HType::Bool32x4()); + } else { + set_type(HType::TaggedNumber()); + } if (to.IsTagged()) SetChangesFlag(kNewSpacePromotion); } } @@ -3651,6 +3676,8 @@ class HAccessArgumentsAt final : public HTemplateInstruction<3> { class HBoundsCheck final : public HTemplateInstruction<2> { public: DECLARE_INSTRUCTION_FACTORY_P2(HBoundsCheck, HValue*, HValue*); + DECLARE_INSTRUCTION_FACTORY_P4(HBoundsCheck, HValue*, HValue*, + BuiltinFunctionId, ElementsKind); bool skip_check() const { return skip_check_; } void set_skip_check() { skip_check_ = true; } @@ -3670,6 +3697,8 @@ class HBoundsCheck final : public HTemplateInstruction<2> { HValue* length() const { return OperandAt(1); } bool allow_equality() const { return allow_equality_; } void set_allow_equality(bool v) { allow_equality_ = v; } + BuiltinFunctionId op() const { return op_; } + ElementsKind element_kind() const { return element_kind_; } int RedefinedOperandIndex() override { return 0; } bool IsPurelyInformativeDefinition() override { return skip_check(); } @@ -3685,16 +3714,24 @@ class HBoundsCheck final : public HTemplateInstruction<2> { int offset_; int scale_; bool allow_equality_; + BuiltinFunctionId op_; + ElementsKind element_kind_; private: // Normally HBoundsCheck should be created using the // HGraphBuilder::AddBoundsCheck() helper. // However when building stubs, where we know that the arguments are Int32, // it makes sense to invoke this constructor directly. - HBoundsCheck(HValue* index, HValue* length) - : skip_check_(false), - base_(NULL), offset_(0), scale_(0), - allow_equality_(false) { + HBoundsCheck(HValue* index, HValue* length, + BuiltinFunctionId op = kNumberOfBuiltinFunction, + ElementsKind element_kind = INT8_ELEMENTS) + : skip_check_(false), + base_(NULL), + offset_(0), + scale_(0), + allow_equality_(false), + op_(op), + element_kind_(element_kind) { SetOperandAt(0, index); SetOperandAt(1, length); SetFlag(kFlexibleRepresentation); @@ -5313,6 +5350,15 @@ class HObjectAccess final { return HObjectAccess(kInobject, Oddball::kTypeOfOffset, Representation::HeapObject()); } + static HObjectAccess ForSIMD128Double0() { + return HObjectAccess(kDouble, Float32x4::kValueOffset, + Representation::Double()); + } + + static HObjectAccess ForSIMD128Double1() { + return HObjectAccess(kDouble, Float32x4::kValueOffset + kDoubleSize, + Representation::Double()); + } static HObjectAccess ForElementsPointer() { return HObjectAccess(kElementsPointer, JSObject::kElementsOffset); @@ -5483,6 +5529,10 @@ class HObjectAccess final { Representation::UInteger16()); } + static HObjectAccess ForMapPrototype() { + return HObjectAccess(kInobject, Map::kPrototypeOffset); + } + static HObjectAccess ForPropertyCellValue() { return HObjectAccess(kInobject, PropertyCell::kValueOffset); } @@ -5950,6 +6000,9 @@ class HLoadKeyed final : public HTemplateInstruction<4>, ElementsKind, LoadKeyedHoleMode); DECLARE_INSTRUCTION_FACTORY_P7(HLoadKeyed, HValue*, HValue*, HValue*, HValue*, ElementsKind, LoadKeyedHoleMode, int); + DECLARE_INSTRUCTION_FACTORY_P8(HLoadKeyed, HValue*, HValue*, HValue*, HValue*, + ElementsKind, LoadKeyedHoleMode, int, + BuiltinFunctionId); bool is_fixed_typed_array() const { return IsFixedTypedArrayElementsKind(elements_kind()); @@ -5968,6 +6021,7 @@ class HLoadKeyed final : public HTemplateInstruction<4>, bool HasBackingStoreOwner() const { return OperandAt(0) != OperandAt(3); } uint32_t base_offset() const { return BaseOffsetField::decode(bit_field_); } bool TryIncreaseBaseOffset(uint32_t increase_by_value) override; + BuiltinFunctionId op() { return op_; } HValue* GetKey() override { return key(); } void SetKey(HValue* key) override { SetOperandAt(1, key); } bool IsDehoisted() const override { @@ -6031,8 +6085,9 @@ class HLoadKeyed final : public HTemplateInstruction<4>, HLoadKeyed(HValue* obj, HValue* key, HValue* dependency, HValue* backing_store_owner, ElementsKind elements_kind, LoadKeyedHoleMode mode = NEVER_RETURN_HOLE, - int offset = kDefaultKeyedHeaderOffsetSentinel) - : bit_field_(0) { + int offset = kDefaultKeyedHeaderOffsetSentinel, + BuiltinFunctionId op = kNumberOfBuiltinFunction) + : bit_field_(0), op_(op) { offset = offset == kDefaultKeyedHeaderOffsetSentinel ? GetDefaultHeaderSizeForElementsKind(elements_kind) : offset; @@ -6072,8 +6127,31 @@ class HLoadKeyed final : public HTemplateInstruction<4>, SetDependsOnFlag(kDoubleArrayElements); } } else { - if (elements_kind == FLOAT32_ELEMENTS || - elements_kind == FLOAT64_ELEMENTS) { + if (op_ == kFloat32ArrayGetFloat32x4XYZW || + op_ == kFloat32ArrayGetFloat32x4X || + op_ == kFloat32ArrayGetFloat32x4XY || + op_ == kFloat32ArrayGetFloat32x4XYZ || + op_ == kInt8ArrayGetFloat32x4XYZW || op_ == kInt8ArrayGetFloat32x4X || + op_ == kInt8ArrayGetFloat32x4XY || op_ == kInt8ArrayGetFloat32x4XYZ || + op_ == kUint8ArrayGetFloat32x4XYZW || + op_ == kUint8ArrayGetFloat32x4X || op_ == kUint8ArrayGetFloat32x4XY || + op_ == kUint8ArrayGetFloat32x4XYZ) { + set_representation(Representation::Float32x4()); + } else if (op_ == kInt32ArrayGetInt32x4XYZW || + op_ == kInt32ArrayGetInt32x4X || + op_ == kInt32ArrayGetInt32x4XY || + op_ == kInt32ArrayGetInt32x4XYZ || + op_ == kInt8ArrayGetInt32x4XYZW || + op_ == kInt8ArrayGetInt32x4X || + op_ == kInt8ArrayGetInt32x4XY || + op_ == kInt8ArrayGetInt32x4XYZ || + op_ == kUint8ArrayGetInt32x4XYZW || + op_ == kUint8ArrayGetInt32x4X || + op_ == kUint8ArrayGetInt32x4XY || + op_ == kUint8ArrayGetInt32x4XYZ) { + set_representation(Representation::Int32x4()); + } else if (elements_kind == FLOAT32_ELEMENTS || + elements_kind == FLOAT64_ELEMENTS) { set_representation(Representation::Double()); } else { set_representation(Representation::Integer32()); @@ -6123,6 +6201,7 @@ class HLoadKeyed final : public HTemplateInstruction<4>, public BitField {}; // NOLINT uint32_t bit_field_; + BuiltinFunctionId op_; }; @@ -6381,6 +6460,9 @@ class HStoreKeyed final : public HTemplateInstruction<4>, DECLARE_INSTRUCTION_FACTORY_P7(HStoreKeyed, HValue*, HValue*, HValue*, HValue*, ElementsKind, StoreFieldOrKeyedMode, int); + DECLARE_INSTRUCTION_FACTORY_P8(HStoreKeyed, HValue*, HValue*, HValue*, + HValue*, ElementsKind, StoreFieldOrKeyedMode, + int, BuiltinFunctionId); Representation RequiredInputRepresentation(int index) override { // kind_fast: tagged[int32] = tagged @@ -6395,6 +6477,30 @@ class HStoreKeyed final : public HTemplateInstruction<4>, return ArrayInstructionInterface::KeyedAccessIndexRequirement( OperandAt(1)->representation()); } else if (index == 2) { + if (op_ == kFloat32ArraySetFloat32x4XYZW || + op_ == kFloat32ArraySetFloat32x4X || + op_ == kFloat32ArraySetFloat32x4XY || + op_ == kFloat32ArraySetFloat32x4XYZ || + op_ == kInt8ArraySetFloat32x4XYZW || op_ == kInt8ArraySetFloat32x4X || + op_ == kInt8ArraySetFloat32x4XY || op_ == kInt8ArraySetFloat32x4XYZ || + op_ == kUint8ArraySetFloat32x4XYZW || + op_ == kUint8ArraySetFloat32x4X || op_ == kUint8ArraySetFloat32x4XY || + op_ == kUint8ArraySetFloat32x4XYZ) { + return Representation::Float32x4(); + } else if (op_ == kInt32ArraySetInt32x4XYZW || + op_ == kInt32ArraySetInt32x4X || + op_ == kInt32ArraySetInt32x4XY || + op_ == kInt32ArraySetInt32x4XYZ || + op_ == kInt8ArraySetInt32x4XYZW || + op_ == kInt8ArraySetInt32x4X || + op_ == kInt8ArraySetInt32x4XY || + op_ == kInt8ArraySetInt32x4XYZ || + op_ == kUint8ArraySetInt32x4XYZW || + op_ == kUint8ArraySetInt32x4X || + op_ == kUint8ArraySetInt32x4XY || + op_ == kUint8ArraySetInt32x4XYZ) { + return Representation::Int32x4(); + } return RequiredValueRepresentation(elements_kind(), store_mode()); } @@ -6433,6 +6539,28 @@ class HStoreKeyed final : public HTemplateInstruction<4>, if (IsUninitialized()) { return Representation::None(); } + if (op_ == kFloat32ArraySetFloat32x4XYZW || + op_ == kFloat32ArraySetFloat32x4X || + op_ == kFloat32ArraySetFloat32x4XY || + op_ == kFloat32ArraySetFloat32x4XYZ || + op_ == kInt8ArraySetFloat32x4XYZW || op_ == kInt8ArraySetFloat32x4X || + op_ == kInt8ArraySetFloat32x4XY || op_ == kInt8ArraySetFloat32x4XYZ || + op_ == kUint8ArraySetFloat32x4XYZW || op_ == kUint8ArraySetFloat32x4X || + op_ == kUint8ArraySetFloat32x4XY || op_ == kUint8ArraySetFloat32x4XYZ) { + return Representation::Float32x4(); + } else if (op_ == kInt32ArraySetInt32x4XYZW || + op_ == kInt32ArraySetInt32x4X || + op_ == kInt32ArraySetInt32x4XY || + op_ == kInt32ArraySetInt32x4XYZ || + op_ == kInt8ArraySetInt32x4XYZW || + op_ == kInt8ArraySetInt32x4X || op_ == kInt8ArraySetInt32x4XY || + op_ == kInt8ArraySetInt32x4XYZ || + op_ == kUint8ArraySetInt32x4XYZW || + op_ == kUint8ArraySetInt32x4X || + op_ == kUint8ArraySetInt32x4XY || + op_ == kUint8ArraySetInt32x4XYZ) { + return Representation::Int32x4(); + } Representation r = RequiredValueRepresentation(elements_kind(), store_mode()); // For fast object elements kinds, don't assume anything. @@ -6440,6 +6568,7 @@ class HStoreKeyed final : public HTemplateInstruction<4>, return r; } + BuiltinFunctionId op() const { return op_; } HValue* elements() const { return OperandAt(0); } HValue* key() const { return OperandAt(1); } HValue* value() const { return OperandAt(2); } @@ -6506,7 +6635,8 @@ class HStoreKeyed final : public HTemplateInstruction<4>, HStoreKeyed(HValue* obj, HValue* key, HValue* val, HValue* backing_store_owner, ElementsKind elements_kind, StoreFieldOrKeyedMode store_mode = INITIALIZING_STORE, - int offset = kDefaultKeyedHeaderOffsetSentinel) + int offset = kDefaultKeyedHeaderOffsetSentinel, + BuiltinFunctionId op = kNumberOfBuiltinFunction) : base_offset_(offset == kDefaultKeyedHeaderOffsetSentinel ? GetDefaultHeaderSizeForElementsKind(elements_kind) : offset), @@ -6514,7 +6644,8 @@ class HStoreKeyed final : public HTemplateInstruction<4>, IsUninitializedField::encode(false) | StoreModeField::encode(store_mode) | ElementsKindField::encode(elements_kind)), - dominator_(NULL) { + dominator_(NULL), + op_(op) { SetOperandAt(0, obj); SetOperandAt(1, key); SetOperandAt(2, val); @@ -6551,6 +6682,7 @@ class HStoreKeyed final : public HTemplateInstruction<4>, uint32_t base_offset_; uint32_t bit_field_; HValue* dominator_; + BuiltinFunctionId op_; }; class HStoreKeyedGeneric final : public HTemplateInstruction<4> { @@ -7126,6 +7258,584 @@ class HLoadFieldByIndex final : public HTemplateInstruction<2> { bool IsDeletable() const override { return true; } }; +class HNullarySIMDOperation final : public HTemplateInstruction<1> { + public: + static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, + BuiltinFunctionId op); + + HValue* context() { return OperandAt(0); } + + std::ostream& PrintDataTo(std::ostream& os) const override; + + Representation observed_input_representation(int index) override { + return RequiredInputRepresentation(index); + } + Representation RequiredInputRepresentation(int index) override { + return Representation::Tagged(); + } + + BuiltinFunctionId op() const { return op_; } + const char* OpName() const; + + DECLARE_CONCRETE_INSTRUCTION(NullarySIMDOperation) + + protected: + bool DataEquals(HValue* other) override { + HNullarySIMDOperation* b = HNullarySIMDOperation::cast(other); + return op_ == b->op(); + } + + private: + HNullarySIMDOperation(HValue* context, BuiltinFunctionId op) + : HTemplateInstruction<1>(HType::None()), op_(op) { + SetOperandAt(0, context); + switch (op) { +#define SIMD_NULLARY_OPERATION_CASE_ITEM(p1, p2, name, representation) \ + case k##name: \ + set_representation(Representation::representation()); \ + set_type(HType::FromRepresentation(representation_)); \ + break; + SIMD_NULLARY_OPERATIONS(SIMD_NULLARY_OPERATION_CASE_ITEM) +#undef SIMD_NULLARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + } + SetFlag(kUseGVN); + } + + bool IsDeletable() const override { return true; } + + BuiltinFunctionId op_; +}; + +class HUnarySIMDOperation final : public HTemplateInstruction<2> { + public: + static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, + HValue* value, BuiltinFunctionId op, + Representation to = Representation::Float32x4()); + + HValue* context() { return OperandAt(0); } + HValue* value() const { return OperandAt(1); } + + std::ostream& PrintDataTo(std::ostream& os) const override; + + Representation observed_input_representation(int index) override { + return RequiredInputRepresentation(index); + } + Representation RequiredInputRepresentation(int index) override { + if (index == 0) { + return Representation::Tagged(); + } else if (op_ == kSIMD128Change) { + return value()->representation(); + } else { + switch (op_) { +#define SIMD_UNARY_OPERATION_CASE_ITEM(p1, p2, name, p4, representation) \ + case k##name: \ + return Representation::representation(); + SIMD_UNARY_OPERATIONS(SIMD_UNARY_OPERATION_CASE_ITEM) + SIMD_UNARY_OPERATIONS_FOR_PROPERTY_ACCESS( + SIMD_UNARY_OPERATION_CASE_ITEM) +#undef SIMD_UNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return Representation::None(); + } + } + } + + BuiltinFunctionId op() const { return op_; } + const char* OpName() const; + + DECLARE_CONCRETE_INSTRUCTION(UnarySIMDOperation) + + protected: + bool DataEquals(HValue* other) override { + HUnarySIMDOperation* b = HUnarySIMDOperation::cast(other); + return op_ == b->op(); + } + + private: + HUnarySIMDOperation(HValue* context, HValue* value, BuiltinFunctionId op, + Representation to = Representation::Float32x4()) + : HTemplateInstruction<2>(HType::None()), op_(op) { + SetOperandAt(0, context); + SetOperandAt(1, value); + switch (op) { + case kSIMD128Change: + set_representation(to); + set_type(HType::FromRepresentation(to)); + break; +#define SIMD_UNARY_OPERATION_CASE_ITEM(p1, p2, name, representation, p5) \ + case k##name: \ + set_representation(Representation::representation()); \ + set_type(HType::FromRepresentation(representation_)); \ + if (Representation::p5().IsInteger32()) { \ + SetFlag(kTruncatingToInt32); \ + } \ + break; + SIMD_UNARY_OPERATIONS(SIMD_UNARY_OPERATION_CASE_ITEM) + SIMD_UNARY_OPERATIONS_FOR_PROPERTY_ACCESS( + SIMD_UNARY_OPERATION_CASE_ITEM) +#undef SIMD_UNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + } + SetFlag(kUseGVN); + } + + bool IsDeletable() const override { return true; } + + BuiltinFunctionId op_; +}; + +class HBinarySIMDOperation final : public HTemplateInstruction<3> { + public: + static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, + HValue* left, HValue* right, BuiltinFunctionId op); + + HValue* context() { return OperandAt(0); } + HValue* left() const { return OperandAt(1); } + HValue* right() const { return OperandAt(2); } + + std::ostream& PrintDataTo(std::ostream& os) const override; + + Representation observed_input_representation(int index) override { + return RequiredInputRepresentation(index); + } + Representation RequiredInputRepresentation(int index) override { + if (index == 0) { + return Representation::Tagged(); + } else { + switch (op_) { +#define SIMD_BINARY_OPERATION_CASE_ITEM(p1, p2, name, p4, left_representation, \ + right_representation) \ + case k##name: \ + return index == 1 ? Representation::left_representation() \ + : Representation::right_representation(); \ + break; + SIMD_BINARY_OPERATIONS(SIMD_BINARY_OPERATION_CASE_ITEM) +#undef SIMD_BINARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return Representation::None(); + } + } + } + + BuiltinFunctionId op() const { return op_; } + const char* OpName() const; + + DECLARE_CONCRETE_INSTRUCTION(BinarySIMDOperation) + + protected: + bool DataEquals(HValue* other) override { + HBinarySIMDOperation* b = HBinarySIMDOperation::cast(other); + return op_ == b->op(); + } + + private: + HBinarySIMDOperation(HValue* context, HValue* left, HValue* right, + BuiltinFunctionId op) + : HTemplateInstruction<3>(HType::None()), op_(op) { + SetOperandAt(0, context); + SetOperandAt(1, left); + SetOperandAt(2, right); + switch (op) { +#define SIMD_BINARY_OPERATION_CASE_ITEM(p1, p2, name, representation, p5, p6) \ + case k##name: \ + set_representation(Representation::representation()); \ + set_type(HType::FromRepresentation(representation_)); \ + if (Representation::p5().IsInteger32() || \ + Representation::p6().IsInteger32()) { \ + SetFlag(kTruncatingToInt32); \ + } \ + break; + SIMD_BINARY_OPERATIONS(SIMD_BINARY_OPERATION_CASE_ITEM) +#undef SIMD_BINARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + } + SetFlag(kUseGVN); + } + + bool IsDeletable() const override { return true; } + + BuiltinFunctionId op_; +}; + +class HTernarySIMDOperation final : public HTemplateInstruction<4> { + public: + static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, + HValue* first, HValue* second, HValue* third, + BuiltinFunctionId op); + + HValue* context() { return OperandAt(0); } + HValue* first() const { return OperandAt(1); } + HValue* second() const { return OperandAt(2); } + HValue* third() const { return OperandAt(3); } + + std::ostream& PrintDataTo(std::ostream& os) const override; + + Representation observed_input_representation(int index) override { + return RequiredInputRepresentation(index); + } + Representation RequiredInputRepresentation(int index) override { + if (index == 0) { + return Representation::Tagged(); + } else { + switch (op_) { +#define SIMD_TERNARY_OPERATION_CASE_ITEM(p1, p2, name, p4, \ + first_representation, \ + second_representation, \ + third_representation) \ + case k##name: \ + switch (index) { \ + case 1: \ + return Representation::first_representation(); \ + case 2: \ + return Representation::second_representation(); \ + case 3: \ + return Representation::third_representation(); \ + default: \ + UNREACHABLE(); \ + return Representation::None(); \ + } + SIMD_TERNARY_OPERATIONS(SIMD_TERNARY_OPERATION_CASE_ITEM) +#undef SIMD_TERNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return Representation::None(); + } + } + } + + BuiltinFunctionId op() const { return op_; } + const char* OpName() const; + + DECLARE_CONCRETE_INSTRUCTION(TernarySIMDOperation) + + protected: + bool DataEquals(HValue* other) override { + HTernarySIMDOperation* b = HTernarySIMDOperation::cast(other); + return op_ == b->op(); + } + + private: + HTernarySIMDOperation(HValue* context, HValue* first, HValue* second, + HValue* third, BuiltinFunctionId op) + : HTemplateInstruction<4>(HType::None()), op_(op) { + SetOperandAt(0, context); + SetOperandAt(1, first); + SetOperandAt(2, second); + SetOperandAt(3, third); + switch (op) { +#define SIMD_TERNARY_OPERATION_CASE_ITEM(p1, p2, name, representation, p5, p6, \ + p7) \ + case k##name: \ + set_representation(Representation::representation()); \ + set_type(HType::FromRepresentation(representation_)); \ + if (Representation::p5().IsInteger32() || \ + Representation::p6().IsInteger32() || \ + Representation::p7().IsInteger32()) { \ + SetFlag(kTruncatingToInt32); \ + } \ + break; + SIMD_TERNARY_OPERATIONS(SIMD_TERNARY_OPERATION_CASE_ITEM) +#undef SIMD_TERNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + } + SetFlag(kUseGVN); + } + + bool IsDeletable() const override { return true; } + + BuiltinFunctionId op_; +}; + +class HQuarternarySIMDOperation final : public HTemplateInstruction<5> { + public: + static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, + HValue* x, HValue* y, HValue* z, HValue* w, + BuiltinFunctionId op); + + HValue* context() { return OperandAt(0); } + HValue* x() const { return OperandAt(1); } + HValue* y() const { return OperandAt(2); } + HValue* z() const { return OperandAt(3); } + HValue* w() const { return OperandAt(4); } + + std::ostream& PrintDataTo(std::ostream& os) const override; + + Representation observed_input_representation(int index) override { + return RequiredInputRepresentation(index); + } + Representation RequiredInputRepresentation(int index) override { + if (index == 0) { + return Representation::Tagged(); + } else { + switch (op_) { +#define SIMD_QUARTERNARY_OPERATION_CASE_ITEM( \ + p1, p2, name, p4, first_representation, second_representation, \ + third_representation, fourth_representation) \ + case k##name: \ + switch (index) { \ + case 1: \ + return Representation::first_representation(); \ + case 2: \ + return Representation::second_representation(); \ + case 3: \ + return Representation::third_representation(); \ + case 4: \ + return Representation::fourth_representation(); \ + default: \ + UNREACHABLE(); \ + return Representation::None(); \ + } + SIMD_QUARTERNARY_OPERATIONS(SIMD_QUARTERNARY_OPERATION_CASE_ITEM) +#undef SIMD_QUARTERNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return Representation::None(); + } + } + } + + BuiltinFunctionId op() const { return op_; } + const char* OpName() const; + + DECLARE_CONCRETE_INSTRUCTION(QuarternarySIMDOperation) + + protected: + bool DataEquals(HValue* other) override { + HQuarternarySIMDOperation* b = HQuarternarySIMDOperation::cast(other); + return op_ == b->op(); + } + + private: + HQuarternarySIMDOperation(HValue* context, HValue* x, HValue* y, HValue* z, + HValue* w, BuiltinFunctionId op) + : HTemplateInstruction<5>(HType::None()), op_(op) { + SetOperandAt(0, context); + SetOperandAt(1, x); + SetOperandAt(2, y); + SetOperandAt(3, z); + SetOperandAt(4, w); + switch (op) { +#define SIMD_QUARTERNARY_OPERATION_CASE_ITEM(p1, p2, name, representation, p5, \ + p6, p7, p8) \ + case k##name: \ + set_representation(Representation::representation()); \ + set_type(HType::FromRepresentation(representation_)); \ + if (Representation::p5().IsInteger32() || \ + Representation::p6().IsInteger32() || \ + Representation::p7().IsInteger32() || \ + Representation::p8().IsInteger32()) { \ + SetFlag(kTruncatingToInt32); \ + } \ + break; + SIMD_QUARTERNARY_OPERATIONS(SIMD_QUARTERNARY_OPERATION_CASE_ITEM) +#undef SIMD_QUARTERNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + } + SetFlag(kUseGVN); + } + + bool IsDeletable() const override { return true; } + + BuiltinFunctionId op_; +}; + +class HQuinarySIMDOperation final : public HTemplateInstruction<6> { + public: + static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, + HValue* a0, HValue* a1, HValue* a2, HValue* a3, + HValue* a4, BuiltinFunctionId op); + + HValue* context() { return OperandAt(0); } + HValue* a0() const { return OperandAt(1); } + HValue* a1() const { return OperandAt(2); } + HValue* a2() const { return OperandAt(3); } + HValue* a3() const { return OperandAt(4); } + HValue* a4() const { return OperandAt(5); } + + std::ostream& PrintDataTo(std::ostream& os) const override; + + Representation observed_input_representation(int index) override { + return RequiredInputRepresentation(index); + } + Representation RequiredInputRepresentation(int index) override { + if (index == 0) { + return Representation::Tagged(); + } else { + switch (op_) { +#define SIMD_QUINARY_OPERATION_CASE_ITEM( \ + p1, p2, name, p4, first_representation, second_representation, \ + third_representation, fourth_representation, fifth_representation) \ + case k##name: \ + switch (index) { \ + case 1: \ + return Representation::first_representation(); \ + case 2: \ + return Representation::second_representation(); \ + case 3: \ + return Representation::third_representation(); \ + case 4: \ + return Representation::fourth_representation(); \ + case 5: \ + return Representation::fifth_representation(); \ + default: \ + UNREACHABLE(); \ + return Representation::None(); \ + } + SIMD_QUINARY_OPERATIONS(SIMD_QUINARY_OPERATION_CASE_ITEM) +#undef SIMD_QUINARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return Representation::None(); + } + } + } + + BuiltinFunctionId op() const { return op_; } + const char* OpName() const; + + DECLARE_CONCRETE_INSTRUCTION(QuinarySIMDOperation) + + protected: + bool DataEquals(HValue* other) override { + HQuinarySIMDOperation* b = HQuinarySIMDOperation::cast(other); + return op_ == b->op(); + } + + private: + HQuinarySIMDOperation(HValue* context, HValue* a0, HValue* a1, HValue* a2, + HValue* a3, HValue* a4, BuiltinFunctionId op) + : HTemplateInstruction<6>(HType::None()), op_(op) { + SetOperandAt(0, context); + SetOperandAt(1, a0); + SetOperandAt(2, a1); + SetOperandAt(3, a2); + SetOperandAt(4, a3); + SetOperandAt(5, a4); + switch (op) { +#define SIMD_QUINARY_OPERATION_CASE_ITEM(p1, p2, name, representation, p5, p6, \ + p7, p8, p9) \ + case k##name: \ + set_representation(Representation::representation()); \ + set_type(HType::FromRepresentation(representation_)); \ + break; + SIMD_QUINARY_OPERATIONS(SIMD_QUINARY_OPERATION_CASE_ITEM) +#undef SIMD_QUINARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + } + SetFlag(kUseGVN); + } + + bool IsDeletable() const override { return true; } + + BuiltinFunctionId op_; +}; + +class HSenarySIMDOperation final : public HTemplateInstruction<7> { + public: + static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, + HValue* a0, HValue* a1, HValue* a2, HValue* a3, + HValue* a4, HValue* a5, BuiltinFunctionId op); + + HValue* context() { return OperandAt(0); } + HValue* a0() const { return OperandAt(1); } + HValue* a1() const { return OperandAt(2); } + HValue* a2() const { return OperandAt(3); } + HValue* a3() const { return OperandAt(4); } + HValue* a4() const { return OperandAt(5); } + HValue* a5() const { return OperandAt(6); } + + std::ostream& PrintDataTo(std::ostream& os) const override; + + Representation observed_input_representation(int index) override { + return RequiredInputRepresentation(index); + } + Representation RequiredInputRepresentation(int index) override { + if (index == 0) { + return Representation::Tagged(); + } else { + switch (op_) { +#define SIMD_SENARY_OPERATION_CASE_ITEM( \ + p1, p2, name, p4, first_representation, second_representation, \ + third_representation, fourth_representation, fifth_representation, \ + sixth_representation) \ + case k##name: \ + switch (index) { \ + case 1: \ + return Representation::first_representation(); \ + case 2: \ + return Representation::second_representation(); \ + case 3: \ + return Representation::third_representation(); \ + case 4: \ + return Representation::fourth_representation(); \ + case 5: \ + return Representation::fifth_representation(); \ + case 6: \ + return Representation::sixth_representation(); \ + default: \ + UNREACHABLE(); \ + return Representation::None(); \ + } + SIMD_SENARY_OPERATIONS(SIMD_SENARY_OPERATION_CASE_ITEM) +#undef SIMD_SENARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return Representation::None(); + } + } + } + + BuiltinFunctionId op() const { return op_; } + const char* OpName() const; + + DECLARE_CONCRETE_INSTRUCTION(SenarySIMDOperation) + + protected: + bool DataEquals(HValue* other) override { + HSenarySIMDOperation* b = HSenarySIMDOperation::cast(other); + return op_ == b->op(); + } + + private: + HSenarySIMDOperation(HValue* context, HValue* a0, HValue* a1, HValue* a2, + HValue* a3, HValue* a4, HValue* a5, BuiltinFunctionId op) + : HTemplateInstruction<7>(HType::None()), op_(op) { + SetOperandAt(0, context); + SetOperandAt(1, a0); + SetOperandAt(2, a1); + SetOperandAt(3, a2); + SetOperandAt(4, a3); + SetOperandAt(5, a4); + SetOperandAt(6, a5); + switch (op) { +#define SIMD_SENARY_OPERATION_CASE_ITEM(p1, p2, name, representation, p5, p6, \ + p7, p8, p9, p10) \ + case k##name: \ + set_representation(Representation::representation()); \ + set_type(HType::FromRepresentation(representation_)); \ + break; + SIMD_SENARY_OPERATIONS(SIMD_SENARY_OPERATION_CASE_ITEM) +#undef SIMD_SENARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + } + SetFlag(kUseGVN); + } + + bool IsDeletable() const override { return true; } + + BuiltinFunctionId op_; +}; + #undef DECLARE_INSTRUCTION #undef DECLARE_CONCRETE_INSTRUCTION diff --git a/src/crankshaft/hydrogen-representation-changes.cc b/src/crankshaft/hydrogen-representation-changes.cc index 32b614c56c3..7fc2e50c639 100644 --- a/src/crankshaft/hydrogen-representation-changes.cc +++ b/src/crankshaft/hydrogen-representation-changes.cc @@ -36,8 +36,20 @@ void HRepresentationChangesPhase::InsertRepresentationChangeForUse( } if (new_value == NULL) { - new_value = new(graph()->zone()) HChange( - value, to, is_truncating_to_smi, is_truncating_to_int); + if (((to.IsFloat32x4() || to.IsBool32x4() || to.IsInt32x4()) && + !value->representation().IsTagged()) || + ((value->representation().IsFloat32x4() || + value->representation().IsBool32x4() || + value->representation().IsInt32x4()) && + !to.IsTagged())) { + new_value = HUnarySIMDOperation::New( + graph()->isolate(), graph()->zone(), + graph()->entry_block()->last_environment()->context(), value, + kSIMD128Change, to); + } else { + new_value = new (graph()->zone()) + HChange(value, to, is_truncating_to_smi, is_truncating_to_int); + } if (!use_value->operand_position(use_index).IsUnknown()) { new_value->set_position(use_value->operand_position(use_index)); } else { diff --git a/src/crankshaft/hydrogen-types.cc b/src/crankshaft/hydrogen-types.cc index 20d50d897c7..f81d24c28ee 100644 --- a/src/crankshaft/hydrogen-types.cc +++ b/src/crankshaft/hydrogen-types.cc @@ -7,6 +7,7 @@ #include "src/field-type.h" #include "src/handles-inl.h" #include "src/ostreams.h" +#include "src/property-details.h" namespace v8 { namespace internal { @@ -43,6 +44,9 @@ HType HType::FromValue(Handle value) { double n = Handle::cast(value)->value(); return IsSmiDouble(n) ? HType::Smi() : HType::HeapNumber(); } + if (raw_value->IsFloat32x4()) return HType::Float32x4(); + if (raw_value->IsBool32x4()) return HType::Bool32x4(); + if (raw_value->IsInt32x4()) return HType::Int32x4(); if (raw_value->IsString()) return HType::String(); if (raw_value->IsBoolean()) return HType::Boolean(); if (raw_value->IsUndefined(isolate)) return HType::Undefined(); @@ -56,6 +60,22 @@ HType HType::FromValue(Handle value) { return HType::HeapObject(); } +// static +HType HType::FromRepresentation(Representation representation) { + HType result = HType::Tagged(); + if (representation.IsSmi()) { + result = HType::Smi(); + } else if (representation.IsDouble()) { + result = HType::HeapNumber(); + } else if (representation.IsFloat32x4()) { + result = HType::Float32x4(); + } else if (representation.IsBool32x4()) { + result = HType::Bool32x4(); + } else if (representation.IsInt32x4()) { + result = HType::Int32x4(); + } + return result; +} std::ostream& operator<<(std::ostream& os, const HType& t) { // Note: The c1visualizer syntax for locals allows only a sequence of the diff --git a/src/crankshaft/hydrogen-types.h b/src/crankshaft/hydrogen-types.h index 0690ece34ff..de2d64b662f 100644 --- a/src/crankshaft/hydrogen-types.h +++ b/src/crankshaft/hydrogen-types.h @@ -18,6 +18,7 @@ namespace internal { template class Handle; class FieldType; class Object; +class Representation; #define HTYPE_LIST(V) \ V(Any, 0x0) /* 0000 0000 0000 0000 */ \ @@ -29,13 +30,16 @@ class Object; V(HeapPrimitive, 0x25) /* 0000 0000 0010 0101 */ \ V(Null, 0x27) /* 0000 0000 0010 0111 */ \ V(HeapNumber, 0x2d) /* 0000 0000 0010 1101 */ \ - V(String, 0x65) /* 0000 0000 0110 0101 */ \ - V(Boolean, 0xa5) /* 0000 0000 1010 0101 */ \ - V(Undefined, 0x125) /* 0000 0001 0010 0101 */ \ - V(JSReceiver, 0x221) /* 0000 0010 0010 0001 */ \ - V(JSObject, 0x621) /* 0000 0110 0010 0001 */ \ - V(JSArray, 0xe21) /* 0000 1110 0010 0001 */ \ - V(None, 0xfff) /* 0000 1111 1111 1111 */ + V(Float32x4, 0x65) /* 0000 0000 0110 0101 */ \ + V(Bool32x4, 0xa5) /* 0000 0000 1010 0101 */ \ + V(Int32x4, 0x125) /* 0000 0001 0010 0101 */ \ + V(String, 0x225) /* 0000 0010 0010 0101 */ \ + V(Boolean, 0x425) /* 0000 0100 0010 0101 */ \ + V(Undefined, 0x825) /* 0000 1000 0010 0101 */ \ + V(JSReceiver, 0x1021) /* 0001 0000 0010 0001 */ \ + V(JSObject, 0x3021) /* 0011 0000 0010 0001 */ \ + V(JSArray, 0x7021) /* 0111 0000 0010 0001 */ \ + V(None, 0x7fff) /* 0111 1111 1111 1111 */ class HType final { public: @@ -68,6 +72,7 @@ class HType final { static HType FromFieldType(Handle type, Zone* temp_zone) WARN_UNUSED_RESULT; static HType FromValue(Handle value) WARN_UNUSED_RESULT; + static HType FromRepresentation(Representation representation); friend std::ostream& operator<<(std::ostream& os, const HType& t); diff --git a/src/crankshaft/hydrogen.cc b/src/crankshaft/hydrogen.cc index 240101eeebd..12833e65a3d 100644 --- a/src/crankshaft/hydrogen.cc +++ b/src/crankshaft/hydrogen.cc @@ -2772,16 +2772,11 @@ HValue* HGraphBuilder::BuildStringAdd( return Pop(); } - HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( - HValue* checked_object, - HValue* key, - HValue* val, - bool is_js_array, - ElementsKind elements_kind, - PropertyAccessType access_type, - LoadKeyedHoleMode load_mode, - KeyedAccessStoreMode store_mode) { + HValue* checked_object, HValue* key, HValue* val, bool is_js_array, + ElementsKind elements_kind, PropertyAccessType access_type, + LoadKeyedHoleMode load_mode, KeyedAccessStoreMode store_mode, + BuiltinFunctionId op) { DCHECK(top_info()->IsStub() || checked_object->IsCompareMap() || checked_object->IsCheckMaps()); DCHECK(!IsFixedTypedArrayElementsKind(elements_kind) || !is_js_array); @@ -2844,10 +2839,10 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( return result; } else { DCHECK(store_mode == STANDARD_STORE); - checked_key = Add(key, length); + checked_key = Add(key, length, op, elements_kind); return AddElementAccess(backing_store, checked_key, val, checked_object, checked_object->ActualValue(), elements_kind, - access_type); + access_type, NEVER_RETURN_HOLE, op); } } DCHECK(fast_smi_only_elements || @@ -2887,7 +2882,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( } } return AddElementAccess(elements, checked_key, val, checked_object, nullptr, - elements_kind, access_type, load_mode); + elements_kind, access_type, load_mode, op); } @@ -2998,25 +2993,27 @@ void HGraphBuilder::BuildJSArrayHeader(HValue* array, } } - HInstruction* HGraphBuilder::AddElementAccess( HValue* elements, HValue* checked_key, HValue* val, HValue* dependency, HValue* backing_store_owner, ElementsKind elements_kind, - PropertyAccessType access_type, LoadKeyedHoleMode load_mode) { + PropertyAccessType access_type, LoadKeyedHoleMode load_mode, + BuiltinFunctionId op) { if (access_type == STORE) { DCHECK(val != NULL); if (elements_kind == UINT8_CLAMPED_ELEMENTS) { val = Add(val); } return Add(elements, checked_key, val, backing_store_owner, - elements_kind, STORE_TO_INITIALIZED_ENTRY); + elements_kind, STORE_TO_INITIALIZED_ENTRY, + kDefaultKeyedHeaderOffsetSentinel, op); } DCHECK(access_type == LOAD); DCHECK(val == NULL); - HLoadKeyed* load = - Add(elements, checked_key, dependency, backing_store_owner, - elements_kind, load_mode); + HLoadKeyed* load = Add( + elements, checked_key, dependency, backing_store_owner, elements_kind, + load_mode, kDefaultKeyedHeaderOffsetSentinel, op); + if (elements_kind == UINT32_ELEMENTS) { graph()->RecordUint32Instruction(load); } @@ -6433,6 +6430,7 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessAsMonomorphic( if (!CanAccessMonomorphic()) return false; STATIC_ASSERT(kMaxLoadPolymorphism == kMaxStorePolymorphism); if (maps->length() > kMaxLoadPolymorphism) return false; + HObjectAccess access = HObjectAccess::ForMap(); // bogus default if (GetJSObjectFieldAccess(&access)) { for (int i = 1; i < maps->length(); ++i) { @@ -6485,7 +6483,6 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::NeedsWrappingFor( return NeedsWrapping(map_, target); } - HValue* HOptimizedGraphBuilder::BuildMonomorphicAccess( PropertyAccessInfo* info, HValue* object, HValue* checked_object, HValue* value, BailoutId ast_id, BailoutId return_id, @@ -8728,7 +8725,7 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr) { // Not supported for inlining yet. break; } - return false; + return TryInlineSIMDBuiltinCall(expr, id, expr->arguments()->length() + 1); } @@ -9212,13 +9209,166 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall( ast_context()->ReturnValue(index); return true; } +#define SIMD_NULLARY_OPERATION_CASE_ITEM(p1, p2, name, p4) case k##name: + SIMD_NULLARY_OPERATIONS(SIMD_NULLARY_OPERATION_CASE_ITEM) +#undef SIMD_NULLARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 1) { + Drop(2); // Receiver and function. + HInstruction* op = NewUncasted(id); + ast_context()->ReturnInstruction(op, ast_id); + return true; + } + break; +#define SIMD_UNARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5) case k##name: + SIMD_UNARY_OPERATIONS(SIMD_UNARY_OPERATION_CASE_ITEM) +#undef SIMD_UNARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 2) { + HValue* argument = Pop(); + Drop(2); // Receiver and function. + HInstruction* op = NewUncasted(argument, id); + ast_context()->ReturnInstruction(op, ast_id); + return true; + } + break; +#define SIMD_BINARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5, p6) case k##name: + SIMD_BINARY_OPERATIONS(SIMD_BINARY_OPERATION_CASE_ITEM) +#undef SIMD_BINARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 3) { + HValue* right = Pop(); + HValue* left = Pop(); + Drop(2); // Receiver and function. + HInstruction* op = NewUncasted(left, right, id); + ast_context()->ReturnInstruction(op, ast_id); + return true; + } + break; +#define SIMD_TERNARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5, p6, p7) \ + case k##name: + SIMD_TERNARY_OPERATIONS(SIMD_TERNARY_OPERATION_CASE_ITEM) +#undef SIMD_TERNARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 4) { + HValue* right = Pop(); + HValue* left = Pop(); + HValue* value = Pop(); + Drop(2); // Receiver and function. + HInstruction* op = + NewUncasted(value, left, right, id); + ast_context()->ReturnInstruction(op, ast_id); + return true; + } + break; +#define SIMD_QUARTERNARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5, p6, p7, p8) \ + case k##name: + SIMD_QUARTERNARY_OPERATIONS(SIMD_QUARTERNARY_OPERATION_CASE_ITEM) +#undef SIMD_QUARTERNARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 5) { + HValue* w = Pop(); + HValue* z = Pop(); + HValue* y = Pop(); + HValue* x = Pop(); + Drop(2); // Receiver and function. + HValue* context = environment()->context(); + HInstruction* op = HQuarternarySIMDOperation::New( + isolate(), zone(), context, x, y, z, w, id); + ast_context()->ReturnInstruction(op, ast_id); + return true; + } + break; +#define SIMD_QUINARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5, p6, p7, p8, p9) \ + case k##name: + SIMD_QUINARY_OPERATIONS(SIMD_QUINARY_OPERATION_CASE_ITEM) +#undef SIMD_QUINARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && + args_count_no_receiver == 5) { + HValue* a4 = Pop(); + HValue* a3 = Pop(); + HValue* a2 = Pop(); + HValue* a1 = Pop(); + HValue* a0 = Pop(); + Drop(2); // Receiver and function. + HInstruction* op = + NewUncasted(a0, a1, a2, a3, a4, id); + ast_context()->ReturnInstruction(op, ast_id); + return true; + } + break; +#define SIMD_SENARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5, p6, p7, p8, p9, \ + p10) \ + case k##name: + SIMD_SENARY_OPERATIONS(SIMD_SENARY_OPERATION_CASE_ITEM) +#undef SIMD_SENARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && + args_count_no_receiver == 6) { + HValue* a5 = Pop(); + HValue* a4 = Pop(); + HValue* a3 = Pop(); + HValue* a2 = Pop(); + HValue* a1 = Pop(); + HValue* a0 = Pop(); + Drop(2); // Receiver and function. + HInstruction* op = + NewUncasted(a0, a1, a2, a3, a4, a5, id); + ast_context()->ReturnInstruction(op, ast_id); + return true; + } + break; +#define TYPED_ARRAY_SIMD_LOAD_OPERATION_CASE_ITEM(p1, p2, name) case k##name: + TYPED_ARRAYS_SIMD_LOAD_OPERATIONS( + TYPED_ARRAY_SIMD_LOAD_OPERATION_CASE_ITEM) +#undef TYPED_ARRAY_SIMD_LOAD_OPERATION_CASE_ITEM + if (receiver_map.is_null()) return false; + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 2) { +#if V8_TARGET_ARCH_X64 + // TODO(nhu): support x64. + return false; +#else + HValue* key = Pop(); + HValue* tarray = Pop(); + Drop(1); // Drop function. + HInstruction* instr = BuildUncheckedMonomorphicElementAccess( + tarray, key, NULL, receiver_map->instance_type() == JS_ARRAY_TYPE, + receiver_map->elements_kind(), + LOAD, // is_store. + NEVER_RETURN_HOLE, // load_mode. + STANDARD_STORE, id); + ast_context()->ReturnValue(instr); + return true; +#endif + } + break; +#define TYPED_ARRAY_SIMD_STORE_OPERATION_CASE_ITEM(p1, p2, name) case k##name: + TYPED_ARRAYS_SIMD_STORE_OPERATIONS( + TYPED_ARRAY_SIMD_STORE_OPERATION_CASE_ITEM) +#undef TYPED_ARRAY_SIMD_STORE_OPERATION_CASE_ITEM + if (receiver_map.is_null()) return false; + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 3) { +#if V8_TARGET_ARCH_X64 + // TODO(nhu): support x64. + return false; +#else + HValue* value = Pop(); + HValue* key = Pop(); + HValue* tarray = Pop(); + Drop(1); // Drop function. + BuildUncheckedMonomorphicElementAccess( + tarray, key, value, receiver_map->instance_type() == JS_ARRAY_TYPE, + receiver_map->elements_kind(), + STORE, // is_store. + NEVER_RETURN_HOLE, // load_mode. + STANDARD_STORE, id); + Push(value); + Add(ast_id, REMOVABLE_SIMULATE); + ast_context()->ReturnValue(Pop()); + return true; +#endif + } + break; default: // Not yet supported for inlining. break; } return false; -} - +} // NOLINT(readability/fn_size) bool HOptimizedGraphBuilder::TryInlineApiFunctionCall(Call* expr, HValue* receiver) { @@ -10205,6 +10355,118 @@ void HGraphBuilder::BuildArrayBufferViewInitialization( buffer); } +bool HOptimizedGraphBuilder::TryInlineSIMDBuiltinCall(Call* expr, + BuiltinFunctionId id, + int argument_count) { + switch (id) { +#define SIMD_NULLARY_OPERATION_CASE_ITEM(p1, p2, name, p4) case k##name: + SIMD_NULLARY_OPERATIONS(SIMD_NULLARY_OPERATION_CASE_ITEM) +#undef SIMD_NULLARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 1) { + Drop(2); // Receiver and function. + HInstruction* op = NewUncasted(id); + ast_context()->ReturnInstruction(op, expr->id()); + return true; + } + break; +#define SIMD_UNARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5) case k##name: + SIMD_UNARY_OPERATIONS(SIMD_UNARY_OPERATION_CASE_ITEM) +#undef SIMD_UNARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 2) { + HValue* argument = Pop(); + Drop(2); // Receiver and function. + HInstruction* op = NewUncasted(argument, id); + ast_context()->ReturnInstruction(op, expr->id()); + return true; + } + break; +#define SIMD_BINARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5, p6) case k##name: + SIMD_BINARY_OPERATIONS(SIMD_BINARY_OPERATION_CASE_ITEM) +#undef SIMD_BINARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 3) { + HValue* right = Pop(); + HValue* left = Pop(); + Drop(2); // Receiver and function. + HInstruction* op = NewUncasted(left, right, id); + ast_context()->ReturnInstruction(op, expr->id()); + return true; + } + break; +#define SIMD_TERNARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5, p6, p7) \ + case k##name: + SIMD_TERNARY_OPERATIONS(SIMD_TERNARY_OPERATION_CASE_ITEM) +#undef SIMD_TERNARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 4) { + HValue* right = Pop(); + HValue* left = Pop(); + HValue* value = Pop(); + Drop(2); // Receiver and function. + HInstruction* op = + NewUncasted(value, left, right, id); + ast_context()->ReturnInstruction(op, expr->id()); + return true; + } + break; +#define SIMD_QUARTERNARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5, p6, p7, p8) \ + case k##name: + SIMD_QUARTERNARY_OPERATIONS(SIMD_QUARTERNARY_OPERATION_CASE_ITEM) +#undef SIMD_QUARTERNARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && argument_count == 5) { + HValue* w = Pop(); + HValue* z = Pop(); + HValue* y = Pop(); + HValue* x = Pop(); + Drop(2); // Receiver and function. + HValue* context = environment()->context(); + HInstruction* op = HQuarternarySIMDOperation::New( + isolate(), zone(), context, x, y, z, w, id); + ast_context()->ReturnInstruction(op, expr->id()); + return true; + } + break; +#define SIMD_QUINARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5, p6, p7, p8, p9) \ + case k##name: + SIMD_QUINARY_OPERATIONS(SIMD_QUINARY_OPERATION_CASE_ITEM) +#undef SIMD_QUINARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && + expr->arguments()->length() == 5) { + HValue* a4 = Pop(); + HValue* a3 = Pop(); + HValue* a2 = Pop(); + HValue* a1 = Pop(); + HValue* a0 = Pop(); + Drop(2); // Receiver and function. + HInstruction* op = + NewUncasted(a0, a1, a2, a3, a4, id); + ast_context()->ReturnInstruction(op, expr->id()); + return true; + } + break; +#define SIMD_SENARY_OPERATION_CASE_ITEM(p1, p2, name, p4, p5, p6, p7, p8, p9, \ + p10) \ + case k##name: + SIMD_SENARY_OPERATIONS(SIMD_SENARY_OPERATION_CASE_ITEM) +#undef SIMD_SENARY_OPERATION_CASE_ITEM + if (CpuFeatures::SupportsSIMD128InCrankshaft() && + expr->arguments()->length() == 6) { + HValue* a5 = Pop(); + HValue* a4 = Pop(); + HValue* a3 = Pop(); + HValue* a2 = Pop(); + HValue* a1 = Pop(); + HValue* a0 = Pop(); + Drop(2); // Receiver and function. + HInstruction* op = + NewUncasted(a0, a1, a2, a3, a4, a5, id); + ast_context()->ReturnInstruction(op, expr->id()); + return true; + } + break; + default: + break; + } + return false; +} HValue* HOptimizedGraphBuilder::BuildAllocateExternalElements( ExternalArrayType array_type, @@ -13297,6 +13559,30 @@ void HTracer::TraceLiveRange(LiveRange* range, const char* type, if (op->IsDoubleRegister()) { trace_.Add(" \"%s\"", GetRegConfig()->GetDoubleRegisterName(assigned_reg)); + } else if (op->IsFloat32x4Register()) { +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + trace_.Add(" \"%s\"", + RegisterConfiguration::Crankshaft()->GetSimd128RegisterName( + assigned_reg)); +#else + trace_.Add(" \"%s\"", "target hasn't no method toString()"); +#endif + } else if (op->IsBool32x4Register()) { +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + trace_.Add(" \"%s\"", + RegisterConfiguration::Crankshaft()->GetSimd128RegisterName( + assigned_reg)); +#else + trace_.Add(" \"%s\"", "target hasn't no method toString()"); +#endif + } else if (op->IsInt32x4Register()) { +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + trace_.Add(" \"%s\"", + RegisterConfiguration::Crankshaft()->GetSimd128RegisterName( + assigned_reg)); +#else + trace_.Add(" \"%s\"", "target hasn't no method toString()"); +#endif } else { DCHECK(op->IsRegister()); trace_.Add(" \"%s\"", @@ -13306,6 +13592,12 @@ void HTracer::TraceLiveRange(LiveRange* range, const char* type, LOperand* op = range->TopLevel()->GetSpillOperand(); if (op->IsDoubleStackSlot()) { trace_.Add(" \"double_stack:%d\"", op->index()); + } else if (op->IsFloat32x4StackSlot()) { + trace_.Add(" \"float32x4_stack:%d\"", op->index()); + } else if (op->IsBool32x4StackSlot()) { + trace_.Add(" \"bool32x4_stack:%d\"", op->index()); + } else if (op->IsInt32x4StackSlot()) { + trace_.Add(" \"int32x4_stack:%d\"", op->index()); } else { DCHECK(op->IsStackSlot()); trace_.Add(" \"stack:%d\"", op->index()); diff --git a/src/crankshaft/hydrogen.h b/src/crankshaft/hydrogen.h index 931dd01dcbe..8f87bd37ba6 100644 --- a/src/crankshaft/hydrogen.h +++ b/src/crankshaft/hydrogen.h @@ -1466,20 +1466,17 @@ class HGraphBuilder { HAllocationMode allocation_mode); HInstruction* BuildUncheckedMonomorphicElementAccess( - HValue* checked_object, - HValue* key, - HValue* val, - bool is_js_array, - ElementsKind elements_kind, - PropertyAccessType access_type, - LoadKeyedHoleMode load_mode, - KeyedAccessStoreMode store_mode); + HValue* checked_object, HValue* key, HValue* val, bool is_js_array, + ElementsKind elements_kind, PropertyAccessType access_type, + LoadKeyedHoleMode load_mode, KeyedAccessStoreMode store_mode, + BuiltinFunctionId id = kNumberOfBuiltinFunction); HInstruction* AddElementAccess( HValue* elements, HValue* checked_key, HValue* val, HValue* dependency, HValue* backing_store_owner, ElementsKind elements_kind, PropertyAccessType access_type, - LoadKeyedHoleMode load_mode = NEVER_RETURN_HOLE); + LoadKeyedHoleMode load_mode = NEVER_RETURN_HOLE, + BuiltinFunctionId id = kNumberOfBuiltinFunction); HInstruction* AddLoadStringInstanceType(HValue* string); HInstruction* AddLoadStringLength(HValue* string); @@ -2832,6 +2829,9 @@ class HOptimizedGraphBuilder : public HGraphBuilder, bool CanBeFunctionApplyArguments(Call* expr); + bool TryInlineSIMDBuiltinCall(Call* expr, BuiltinFunctionId id, + int argument_count); + // The translation state of the currently-being-translated function. FunctionState* function_state_; diff --git a/src/crankshaft/ia32/lithium-codegen-ia32.cc b/src/crankshaft/ia32/lithium-codegen-ia32.cc index 8233659ddbe..cfd8d8bf494 100644 --- a/src/crankshaft/ia32/lithium-codegen-ia32.cc +++ b/src/crankshaft/ia32/lithium-codegen-ia32.cc @@ -19,6 +19,75 @@ namespace v8 { namespace internal { +inline bool IsSIMD128LoadStoreOp(BuiltinFunctionId op) { + return ( + op == kFloat32ArrayGetFloat32x4XYZW || op == kFloat32ArrayGetFloat32x4X || + op == kFloat32ArrayGetFloat32x4XY || op == kFloat32ArrayGetFloat32x4XYZ || + op == kInt32ArrayGetInt32x4XYZW || op == kInt32ArrayGetInt32x4X || + op == kInt32ArrayGetInt32x4XY || op == kInt32ArrayGetInt32x4XYZ || + op == kInt8ArrayGetFloat32x4XYZW || op == kInt8ArrayGetFloat32x4X || + op == kInt8ArrayGetFloat32x4XY || op == kInt8ArrayGetFloat32x4XYZ || + op == kInt8ArrayGetInt32x4XYZW || op == kInt8ArrayGetInt32x4X || + op == kInt8ArrayGetInt32x4XY || op == kInt8ArrayGetInt32x4XYZ || + op == kUint8ArrayGetFloat32x4XYZW || op == kUint8ArrayGetFloat32x4X || + op == kUint8ArrayGetFloat32x4XY || op == kUint8ArrayGetFloat32x4XYZ || + op == kUint8ArrayGetInt32x4XYZW || op == kUint8ArrayGetInt32x4X || + op == kUint8ArrayGetInt32x4XY || op == kUint8ArrayGetInt32x4XYZ || + op == kFloat32ArraySetFloat32x4XYZW || op == kFloat32ArraySetFloat32x4X || + op == kFloat32ArraySetFloat32x4XY || op == kFloat32ArraySetFloat32x4XYZ || + op == kInt32ArraySetInt32x4XYZW || op == kInt32ArraySetInt32x4X || + op == kInt32ArraySetInt32x4XY || op == kInt32ArraySetInt32x4XYZ || + op == kInt8ArraySetFloat32x4XYZW || op == kInt8ArraySetFloat32x4X || + op == kInt8ArraySetFloat32x4XY || op == kInt8ArraySetFloat32x4XYZ || + op == kInt8ArraySetInt32x4XYZW || op == kInt8ArraySetInt32x4X || + op == kInt8ArraySetInt32x4XY || op == kInt8ArraySetInt32x4XYZ || + op == kUint8ArraySetFloat32x4XYZW || op == kUint8ArraySetFloat32x4X || + op == kUint8ArraySetFloat32x4XY || op == kUint8ArraySetFloat32x4XYZ || + op == kUint8ArraySetInt32x4XYZW || op == kUint8ArraySetInt32x4X || + op == kUint8ArraySetInt32x4XY || op == kUint8ArraySetInt32x4XYZ); +} + +int GetSIMD128LoadStoreBytes(BuiltinFunctionId op) { + if (op == kFloat32ArrayGetFloat32x4XYZW || op == kInt32ArrayGetInt32x4XYZW || + op == kInt8ArrayGetFloat32x4XYZW || op == kInt8ArrayGetInt32x4XYZW || + op == kUint8ArrayGetFloat32x4XYZW || op == kUint8ArrayGetInt32x4XYZW || + op == kFloat32ArraySetFloat32x4XYZW || op == kInt32ArraySetInt32x4XYZW || + op == kInt8ArraySetFloat32x4XYZW || op == kInt8ArraySetInt32x4XYZW || + op == kUint8ArraySetFloat32x4XYZW || op == kUint8ArraySetInt32x4XYZW) { + return 16; + } else if (op == kFloat32ArrayGetFloat32x4X || op == kInt32ArrayGetInt32x4X || + op == kInt8ArrayGetFloat32x4X || op == kInt8ArrayGetInt32x4X || + op == kUint8ArrayGetFloat32x4X || op == kUint8ArrayGetInt32x4X || + op == kFloat32ArraySetFloat32x4X || op == kInt32ArraySetInt32x4X || + op == kInt8ArraySetFloat32x4X || op == kInt8ArraySetInt32x4X || + op == kUint8ArraySetFloat32x4X || op == kUint8ArraySetInt32x4X) { + return 4; + } else if (op == kFloat32ArrayGetFloat32x4XY || + op == kInt32ArrayGetInt32x4XY || op == kInt8ArrayGetFloat32x4XY || + op == kInt8ArrayGetInt32x4XY || op == kUint8ArrayGetFloat32x4XY || + op == kUint8ArrayGetInt32x4XY || + op == kFloat32ArraySetFloat32x4XY || + op == kInt32ArraySetInt32x4XY || op == kInt8ArraySetFloat32x4XY || + op == kInt8ArraySetInt32x4XY || op == kUint8ArraySetFloat32x4XY || + op == kUint8ArraySetInt32x4XY) { + return 8; + } else if (op == kFloat32ArrayGetFloat32x4XYZ || + op == kInt32ArrayGetInt32x4XYZ || + op == kInt8ArrayGetFloat32x4XYZ || op == kInt8ArrayGetInt32x4XYZ || + op == kUint8ArrayGetFloat32x4XYZ || + op == kUint8ArrayGetInt32x4XYZ || + op == kFloat32ArraySetFloat32x4XYZ || + op == kInt32ArraySetInt32x4XYZ || + op == kInt8ArraySetFloat32x4XYZ || op == kInt8ArraySetInt32x4XYZ || + op == kUint8ArraySetFloat32x4XYZ || + op == kUint8ArraySetInt32x4XYZ) { + return 12; + } else { + UNREACHABLE(); + return -1; + } +} + // When invoking builtins, we need to record the safepoint in the middle of // the invoke instruction sequence generated by the macro assembler. class SafepointGenerator final : public CallWrapper { @@ -387,6 +456,9 @@ XMMRegister LCodeGen::ToDoubleRegister(int code) const { return XMMRegister::from_code(code); } +XMMRegister LCodeGen::ToSIMD128Register(int code) const { + return XMMRegister::from_code(code); +} Register LCodeGen::ToRegister(LOperand* op) const { DCHECK(op->IsRegister()); @@ -399,6 +471,26 @@ XMMRegister LCodeGen::ToDoubleRegister(LOperand* op) const { return ToDoubleRegister(op->index()); } +XMMRegister LCodeGen::ToFloat32x4Register(LOperand* op) const { + DCHECK(op->IsFloat32x4Register()); + return ToSIMD128Register(op->index()); +} + +XMMRegister LCodeGen::ToBool32x4Register(LOperand* op) const { + DCHECK(op->IsBool32x4Register()); + return ToSIMD128Register(op->index()); +} + +XMMRegister LCodeGen::ToInt32x4Register(LOperand* op) const { + DCHECK(op->IsInt32x4Register()); + return ToSIMD128Register(op->index()); +} + +XMMRegister LCodeGen::ToSIMD128Register(LOperand* op) const { + DCHECK(op->IsFloat32x4Register() || op->IsBool32x4Register() || + op->IsInt32x4Register()); + return ToSIMD128Register(op->index()); +} int32_t LCodeGen::ToInteger32(LConstantOperand* op) const { return ToRepresentation(op, Representation::Integer32()); @@ -459,7 +551,12 @@ static int ArgumentsOffsetWithoutFrame(int index) { Operand LCodeGen::ToOperand(LOperand* op) const { if (op->IsRegister()) return Operand(ToRegister(op)); if (op->IsDoubleRegister()) return Operand(ToDoubleRegister(op)); - DCHECK(op->IsStackSlot() || op->IsDoubleStackSlot()); + if (op->IsFloat32x4Register()) return Operand(ToFloat32x4Register(op)); + if (op->IsBool32x4Register()) return Operand(ToBool32x4Register(op)); + if (op->IsInt32x4Register()) return Operand(ToInt32x4Register(op)); + DCHECK(op->IsStackSlot() || op->IsDoubleStackSlot() || + op->IsFloat32x4StackSlot() || op->IsBool32x4StackSlot() || + op->IsInt32x4StackSlot()); if (NeedsEagerFrame()) { return Operand(ebp, FrameSlotToFPOffset(op->index())); } else { @@ -552,6 +649,25 @@ void LCodeGen::AddToTranslation(LEnvironment* environment, } else if (op->IsDoubleStackSlot()) { int index = op->index(); translation->StoreDoubleStackSlot(index); + } else if (op->IsFloat32x4StackSlot()) { + int index = op->index(); + if (index >= 0) { + index += StandardFrameConstants::kFixedFrameSize / kPointerSize; + } + translation->StoreSIMD128StackSlot(index, + Translation::FLOAT32x4_STACK_SLOT); + } else if (op->IsBool32x4StackSlot()) { + int index = op->index(); + if (index >= 0) { + index += StandardFrameConstants::kFixedFrameSize / kPointerSize; + } + translation->StoreSIMD128StackSlot(index, Translation::BOOL32x4_STACK_SLOT); + } else if (op->IsInt32x4StackSlot()) { + int index = op->index(); + if (index >= 0) { + index += StandardFrameConstants::kFixedFrameSize / kPointerSize; + } + translation->StoreSIMD128StackSlot(index, Translation::INT32x4_STACK_SLOT); } else if (op->IsRegister()) { Register reg = ToRegister(op); if (is_tagged) { @@ -564,6 +680,15 @@ void LCodeGen::AddToTranslation(LEnvironment* environment, } else if (op->IsDoubleRegister()) { XMMRegister reg = ToDoubleRegister(op); translation->StoreDoubleRegister(reg); + } else if (op->IsFloat32x4Register()) { + XMMRegister reg = ToFloat32x4Register(op); + translation->StoreSIMD128Register(reg, Translation::FLOAT32x4_REGISTER); + } else if (op->IsBool32x4Register()) { + XMMRegister reg = ToBool32x4Register(op); + translation->StoreSIMD128Register(reg, Translation::BOOL32x4_REGISTER); + } else if (op->IsInt32x4Register()) { + XMMRegister reg = ToInt32x4Register(op); + translation->StoreSIMD128Register(reg, Translation::INT32x4_REGISTER); } else if (op->IsConstantOperand()) { HConstant* constant = chunk()->LookupConstant(LConstantOperand::cast(op)); int src_index = DefineDeoptimizationLiteral(constant->handle(isolate())); @@ -686,6 +811,7 @@ void LCodeGen::DeoptimizeIf(Condition cc, LInstruction* instr, RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); DCHECK(environment->HasBeenRegistered()); int id = environment->deoptimization_index(); + if (never == cc) return; Address entry = Deoptimizer::GetDeoptimizationEntry(isolate(), id, bailout_type); if (entry == NULL) { @@ -1823,6 +1949,9 @@ void LCodeGen::DoBranch(LBranch* instr) { __ xorps(xmm_scratch, xmm_scratch); __ ucomisd(reg, xmm_scratch); EmitBranch(instr, not_equal); + } else if (r.IsSIMD128()) { + DCHECK(!info()->IsStub()); + EmitBranch(instr, no_condition); } else { DCHECK(r.IsTagged()); Register reg = ToRegister(instr->value()); @@ -2584,6 +2713,37 @@ void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) { } } +void LCodeGen::DoDeferredSIMD128ToTagged(LInstruction* instr, + Runtime::FunctionId id) { + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + Register reg = ToRegister(instr->result()); + __ Move(reg, Immediate(0)); + + PushSafepointRegistersScope scope(this); + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + __ CallRuntimeSaveDoubles(id); + RecordSafepointWithRegisters(instr->pointer_map(), 0, + Safepoint::kNoLazyDeopt); + __ StoreToSafepointRegisterSlot(reg, eax); +} + +void LCodeGen::HandleExternalArrayOpRequiresTemp( + LOperand* key, Representation key_representation, + ElementsKind elements_kind) { + if (ExternalArrayOpRequiresPreScale(key_representation, elements_kind)) { + int pre_shift_size = ElementsKindToShiftSize(elements_kind) - + static_cast(maximal_scale_factor); + if (key_representation.IsSmi()) { + pre_shift_size -= kSmiTagSize; + } + DCHECK(pre_shift_size > 0); + __ shl(ToRegister(key), pre_shift_size); + } else { + __ SmiUntag(ToRegister(key)); + } +} void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) { ElementsKind elements_kind = instr->elements_kind(); @@ -2591,20 +2751,42 @@ void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) { if (!key->IsConstantOperand() && ExternalArrayOpRequiresTemp(instr->hydrogen()->key()->representation(), elements_kind)) { - __ SmiUntag(ToRegister(key)); + HandleExternalArrayOpRequiresTemp( + key, instr->hydrogen()->key()->representation(), elements_kind); } + Operand operand(BuildFastArrayOperand( instr->elements(), key, instr->hydrogen()->key()->representation(), elements_kind, instr->base_offset())); - if (elements_kind == FLOAT32_ELEMENTS) { + BuiltinFunctionId op = instr->hydrogen()->op(); + if (IsSIMD128LoadStoreOp(op)) { + if (GetSIMD128LoadStoreBytes(op) == 16) { + __ movups(ToSIMD128Register(instr->result()), operand); + } else if (GetSIMD128LoadStoreBytes(op) == 4) { + __ movss(ToSIMD128Register(instr->result()), operand); + } else if (GetSIMD128LoadStoreBytes(op) == 8) { + __ movq(ToSIMD128Register(instr->result()), operand); + } else if (GetSIMD128LoadStoreBytes(op) == 12) { + XMMRegister result(ToSIMD128Register(instr->result())); + XMMRegister xmm_scratch = double_scratch0(); + __ movq(result, operand); + Operand operand2(BuildFastArrayOperand( + instr->elements(), key, instr->hydrogen()->key()->representation(), + elements_kind, instr->base_offset() + 8)); + __ movss(xmm_scratch, operand2); + __ movlhps(result, xmm_scratch); + } + } else if (elements_kind == FLOAT32_ELEMENTS) { XMMRegister result(ToDoubleRegister(instr->result())); __ movss(result, operand); __ cvtss2sd(result, result); } else if (elements_kind == FLOAT64_ELEMENTS) { __ movsd(ToDoubleRegister(instr->result()), operand); + } else if (IsSIMD128ElementsKind(elements_kind)) { + __ movups(ToSIMD128Register(instr->result()), operand); } else { Register result(ToRegister(instr->result())); switch (elements_kind) { @@ -2741,8 +2923,11 @@ Operand LCodeGen::BuildFastArrayOperand( ((constant_value) << shift_size) + base_offset); } else { - // Take the tag bit into account while computing the shift size. - if (key_representation.IsSmi() && (shift_size >= 1)) { + if (ExternalArrayOpRequiresPreScale(key_representation, elements_kind)) { + // Make sure the key is pre-scaled against maximal_scale_factor. + shift_size = static_cast(maximal_scale_factor); + } else if (key_representation.IsSmi() && (shift_size >= 1)) { + // Take the tag bit into account while computing the shift size. shift_size -= kSmiTagSize; } ScaleFactor scale_factor = static_cast(shift_size); @@ -3715,17 +3900,51 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) { Condition cc = instr->hydrogen()->allow_equality() ? above : above_equal; - if (instr->index()->IsConstantOperand()) { - __ cmp(ToOperand(instr->length()), - ToImmediate(LConstantOperand::cast(instr->index()), - instr->hydrogen()->length()->representation())); - cc = CommuteCondition(cc); - } else if (instr->length()->IsConstantOperand()) { - __ cmp(ToOperand(instr->index()), - ToImmediate(LConstantOperand::cast(instr->length()), - instr->hydrogen()->index()->representation())); + BuiltinFunctionId op = instr->hydrogen()->op(); + if (IsSIMD128LoadStoreOp(op)) { + cc = above; + Register index_in_bytes = ToRegister(instr->temp0()); + Register length_in_bytes = ToRegister(instr->temp1()); + if (instr->index()->IsConstantOperand()) + __ mov(index_in_bytes, + ToImmediate(LConstantOperand::cast(instr->index()), + instr->hydrogen()->index()->representation())); + else + __ mov(index_in_bytes, ToOperand(instr->index())); + int index_shift_size = + ElementsKindToShiftSize(instr->hydrogen()->element_kind()); + if (instr->hydrogen()->index()->representation().IsSmi()) + index_shift_size -= kSmiTagSize; + DCHECK(index_shift_size >= 0); + if (index_shift_size > 0) __ shl(index_in_bytes, index_shift_size); + int bytes = GetSIMD128LoadStoreBytes(op); + __ add(index_in_bytes, Immediate(bytes)); + if (instr->length()->IsConstantOperand()) + __ mov(length_in_bytes, + ToImmediate(LConstantOperand::cast(instr->length()), + instr->hydrogen()->length()->representation())); + else + __ mov(length_in_bytes, ToOperand(instr->length())); + int length_shift_size = + ElementsKindToShiftSize(instr->hydrogen()->element_kind()); + if (instr->hydrogen()->length()->representation().IsSmi()) + length_shift_size -= kSmiTagSize; + DCHECK(length_shift_size >= 0); + if (length_shift_size > 0) __ shl(length_in_bytes, length_shift_size); + __ cmp(index_in_bytes, length_in_bytes); } else { - __ cmp(ToRegister(instr->index()), ToOperand(instr->length())); + if (instr->index()->IsConstantOperand()) { + __ cmp(ToOperand(instr->length()), + ToImmediate(LConstantOperand::cast(instr->index()), + instr->hydrogen()->length()->representation())); + cc = CommuteCondition(cc); + } else if (instr->length()->IsConstantOperand()) { + __ cmp(ToOperand(instr->index()), + ToImmediate(LConstantOperand::cast(instr->length()), + instr->hydrogen()->index()->representation())); + } else { + __ cmp(ToRegister(instr->index()), ToOperand(instr->length())); + } } if (FLAG_debug_code && instr->hydrogen()->skip_check()) { Label done; @@ -3744,20 +3963,42 @@ void LCodeGen::DoStoreKeyedExternalArray(LStoreKeyed* instr) { if (!key->IsConstantOperand() && ExternalArrayOpRequiresTemp(instr->hydrogen()->key()->representation(), elements_kind)) { - __ SmiUntag(ToRegister(key)); + HandleExternalArrayOpRequiresTemp( + key, instr->hydrogen()->key()->representation(), elements_kind); } + Operand operand(BuildFastArrayOperand( instr->elements(), key, instr->hydrogen()->key()->representation(), elements_kind, instr->base_offset())); - if (elements_kind == FLOAT32_ELEMENTS) { + BuiltinFunctionId op = instr->hydrogen()->op(); + if (IsSIMD128LoadStoreOp(op)) { + if (GetSIMD128LoadStoreBytes(op) == 16) { + __ movups(operand, ToSIMD128Register(instr->value())); + } else if (GetSIMD128LoadStoreBytes(op) == 4) { + __ movss(operand, ToSIMD128Register(instr->value())); + } else if (GetSIMD128LoadStoreBytes(op) == 8) { + __ movq(operand, ToSIMD128Register(instr->value())); + } else if (GetSIMD128LoadStoreBytes(op) == 12) { + XMMRegister value(ToSIMD128Register(instr->value())); + XMMRegister xmm_scratch = double_scratch0(); + __ movq(operand, value); + Operand operand2(BuildFastArrayOperand( + instr->elements(), key, instr->hydrogen()->key()->representation(), + elements_kind, instr->base_offset() + 8)); + __ movhlps(xmm_scratch, value); + __ movss(operand2, xmm_scratch); + } + } else if (elements_kind == FLOAT32_ELEMENTS) { XMMRegister xmm_scratch = double_scratch0(); __ cvtsd2ss(xmm_scratch, ToDoubleRegister(instr->value())); __ movss(operand, xmm_scratch); } else if (elements_kind == FLOAT64_ELEMENTS) { __ movsd(operand, ToDoubleRegister(instr->value())); + } else if (IsSIMD128ElementsKind(elements_kind)) { + __ movups(operand, ToSIMD128Register(instr->value())); } else { Register value = ToRegister(instr->value()); switch (elements_kind) { @@ -5282,6 +5523,1240 @@ void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) { __ bind(&done); } +template +void LCodeGen::HandleSIMD128ToTagged(LSIMD128ToTagged* instr) { + class DeferredSIMD128ToTagged final : public LDeferredCode { + public: + DeferredSIMD128ToTagged(LCodeGen* codegen, LInstruction* instr, + Runtime::FunctionId id) + : LDeferredCode(codegen), instr_(instr), id_(id) {} + void Generate() override { + codegen()->DoDeferredSIMD128ToTagged(instr_, id_); + } + LInstruction* instr() override { return instr_; } + + private: + LInstruction* instr_; + Runtime::FunctionId id_; + }; + + XMMRegister input_reg = ToSIMD128Register(instr->value()); + Register reg = ToRegister(instr->result()); + Register tmp = ToRegister(instr->temp()); + Register tmp2 = ToRegister(instr->temp2()); + + DeferredSIMD128ToTagged* deferred = new (zone()) + DeferredSIMD128ToTagged(this, instr, static_cast(D)); + + if (FLAG_inline_new) { + if (I == FLOAT32x4_TYPE) { + __ AllocateFloat32x4(reg, tmp, tmp2, deferred->entry()); + } else if (I == BOOL32x4_TYPE) { + __ AllocateBool32x4(reg, tmp, tmp2, deferred->entry()); + } else if (I == INT32x4_TYPE) { + __ AllocateInt32x4(reg, tmp, tmp2, deferred->entry()); + } + } else { + __ jmp(deferred->entry()); + } + __ bind(deferred->exit()); + + // load the value to SIMD object. + __ movups(FieldOperand(reg, T::kValueOffset), input_reg); +} + +void LCodeGen::DoSIMD128ToTagged(LSIMD128ToTagged* instr) { + if (instr->value()->IsFloat32x4Register()) { + HandleSIMD128ToTagged(instr); + } else if (instr->value()->IsBool32x4Register()) { + DCHECK(instr->value()->IsBool32x4Register()); + HandleSIMD128ToTagged( + instr); + } else { + DCHECK(instr->value()->IsInt32x4Register()); + HandleSIMD128ToTagged( + instr); + } +} + +template +void LCodeGen::HandleTaggedToSIMD128(LTaggedToSIMD128* instr) { + LOperand* input = instr->value(); + DCHECK(input->IsRegister()); + LOperand* result = instr->result(); + DCHECK(result->IsSIMD128Register()); + + Register input_reg = ToRegister(input); + Register temp_reg = ToRegister(instr->temp()); + XMMRegister result_reg = ToSIMD128Register(result); + + __ test(input_reg, Immediate(kSmiTagMask)); + DeoptimizeIf(zero, instr, DeoptimizeReason::kSmi); + __ CmpObjectType(input_reg, SIMD128_VALUE_TYPE, temp_reg); + DeoptimizeIf(not_equal, instr, DeoptimizeReason::kNotASIMD128); + + // Load the object to SIMD128 register. + __ movups(result_reg, FieldOperand(input_reg, T::kValueOffset)); +} + +void LCodeGen::DoTaggedToSIMD128(LTaggedToSIMD128* instr) { + if (instr->representation().IsFloat32x4()) { + HandleTaggedToSIMD128(instr); + } else if (instr->representation().IsBool32x4()) { + HandleTaggedToSIMD128(instr); + } else { + DCHECK(instr->representation().IsInt32x4()); + HandleTaggedToSIMD128(instr); + } +} + +void LCodeGen::DoNullarySIMDOperation(LNullarySIMDOperation* instr) { + switch (instr->op()) { + case kFloat32x4Zero: { + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + __ xorps(result_reg, result_reg); + return; + } + case kInt32x4Zero: { + XMMRegister result_reg = ToInt32x4Register(instr->result()); + __ xorps(result_reg, result_reg); + return; + } + default: + UNREACHABLE(); + return; + } +} + +void LCodeGen::DoUnarySIMDOperation(LUnarySIMDOperation* instr) { + uint8_t select = 0; + switch (instr->op()) { + case kFloat32x4Check: { + XMMRegister input_reg = ToFloat32x4Register(instr->value()); + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + if (!result_reg.is(input_reg)) { + __ movaps(result_reg, input_reg); + } + return; + } + case kInt32x4Check: { + XMMRegister input_reg = ToInt32x4Register(instr->value()); + XMMRegister result_reg = ToInt32x4Register(instr->result()); + if (!result_reg.is(input_reg)) { + __ movaps(result_reg, input_reg); + } + return; + } + case kSIMD128Change: { + Comment( + ";;; deoptimize: can not perform representation change" + "for float32x4 or int32x4"); + DeoptimizeIf(no_condition, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kFloat32x4Abs: + case kFloat32x4Neg: + case kFloat32x4RecipApprox: + case kFloat32x4RecipSqrtApprox: + case kFloat32x4Sqrt: { + DCHECK(instr->value()->Equals(instr->result())); + DCHECK(instr->hydrogen()->value()->representation().IsFloat32x4()); + XMMRegister input_reg = ToFloat32x4Register(instr->value()); + switch (instr->op()) { + case kFloat32x4Abs: + __ absps(input_reg); + break; + case kFloat32x4Neg: + __ negateps(input_reg); + break; + case kFloat32x4RecipApprox: + __ rcpps(input_reg, input_reg); + break; + case kFloat32x4RecipSqrtApprox: + __ rsqrtps(input_reg, input_reg); + break; + case kFloat32x4Sqrt: + __ sqrtps(input_reg, input_reg); + break; + default: + UNREACHABLE(); + break; + } + return; + } + case kInt32x4Not: + case kInt32x4Neg: { + DCHECK(instr->hydrogen()->value()->representation().IsInt32x4()); + XMMRegister input_reg = ToInt32x4Register(instr->value()); + switch (instr->op()) { + case kInt32x4Not: + __ notps(input_reg); + break; + case kInt32x4Neg: + __ pnegd(input_reg); + break; + default: + UNREACHABLE(); + break; + } + return; + } + case kFloat32x4BitsToInt32x4: + case kFloat32x4ToInt32x4: { + DCHECK(instr->hydrogen()->value()->representation().IsFloat32x4()); + XMMRegister input_reg = ToFloat32x4Register(instr->value()); + XMMRegister result_reg = ToInt32x4Register(instr->result()); + if (instr->op() == kFloat32x4BitsToInt32x4) { + if (!result_reg.is(input_reg)) { + __ movaps(result_reg, input_reg); + } + } else { + DCHECK(instr->op() == kFloat32x4ToInt32x4); + __ cvtps2dq(result_reg, input_reg); + } + return; + } + case kInt32x4BitsToFloat32x4: + case kInt32x4ToFloat32x4: { + DCHECK(instr->hydrogen()->value()->representation().IsInt32x4()); + XMMRegister input_reg = ToInt32x4Register(instr->value()); + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + if (instr->op() == kInt32x4BitsToFloat32x4) { + if (!result_reg.is(input_reg)) { + __ movaps(result_reg, input_reg); + } + } else { + DCHECK(instr->op() == kInt32x4ToFloat32x4); + __ cvtdq2ps(result_reg, input_reg); + } + return; + } + case kFloat32x4Splat: { + DCHECK(instr->hydrogen()->value()->representation().IsDouble()); + XMMRegister input_reg = ToDoubleRegister(instr->value()); + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + XMMRegister xmm_scratch = xmm0; + __ xorps(xmm_scratch, xmm_scratch); + __ cvtsd2ss(xmm_scratch, input_reg); + __ shufps(xmm_scratch, xmm_scratch, 0x0); + __ movaps(result_reg, xmm_scratch); + return; + } + case kInt32x4Splat: { + DCHECK(instr->hydrogen()->value()->representation().IsInteger32()); + Register input_reg = ToRegister(instr->value()); + XMMRegister result_reg = ToInt32x4Register(instr->result()); + __ movd(result_reg, input_reg); + __ shufps(result_reg, result_reg, 0x0); + return; + } + case kInt32x4GetSignMask: { + DCHECK(instr->hydrogen()->value()->representation().IsInt32x4()); + XMMRegister input_reg = ToInt32x4Register(instr->value()); + Register result = ToRegister(instr->result()); + __ movmskps(result, input_reg); + return; + } + case kFloat32x4GetSignMask: { + DCHECK(instr->hydrogen()->value()->representation().IsFloat32x4()); + XMMRegister input_reg = ToFloat32x4Register(instr->value()); + Register result = ToRegister(instr->result()); + __ movmskps(result, input_reg); + return; + } + case kFloat32x4GetW: + select++; + case kFloat32x4GetZ: + select++; + case kFloat32x4GetY: + select++; + case kFloat32x4GetX: { + DCHECK(instr->hydrogen()->value()->representation().IsFloat32x4()); + XMMRegister input_reg = ToFloat32x4Register(instr->value()); + XMMRegister result = ToDoubleRegister(instr->result()); + XMMRegister xmm_scratch = result.is(input_reg) ? xmm0 : result; + + if (select == 0x0) { + __ xorps(xmm_scratch, xmm_scratch); + __ cvtss2sd(xmm_scratch, input_reg); + if (!xmm_scratch.is(result)) { + __ movaps(result, xmm_scratch); + } + } else { + __ pshufd(xmm_scratch, input_reg, select); + if (!xmm_scratch.is(result)) { + __ xorps(result, result); + } + __ cvtss2sd(result, xmm_scratch); + } + return; + } + case kBool32x4AnyTrue: { + DCHECK(instr->hydrogen()->value()->representation().IsBool32x4()); + XMMRegister input_reg = ToBool32x4Register(instr->value()); + Register result = ToRegister(instr->result()); + __ movmskps(result, input_reg); + Label false_value, done; + __ test(result, result); + __ j(zero, &false_value, Label::kNear); + __ LoadRoot(result, Heap::kTrueValueRootIndex); + __ jmp(&done, Label::kNear); + __ bind(&false_value); + __ LoadRoot(result, Heap::kFalseValueRootIndex); + __ bind(&done); + return; + } + case kBool32x4AllTrue: { + DCHECK(instr->hydrogen()->value()->representation().IsBool32x4()); + XMMRegister input_reg = ToBool32x4Register(instr->value()); + Register result = ToRegister(instr->result()); + __ movmskps(result, input_reg); + Label all_value, done; + __ xor_(result, 0xF); + __ j(zero, &all_value, Label::kNear); + __ LoadRoot(result, Heap::kFalseValueRootIndex); + __ jmp(&done, Label::kNear); + __ bind(&all_value); + __ LoadRoot(result, Heap::kTrueValueRootIndex); + __ bind(&done); + return; + } + case kInt32x4GetX: + case kInt32x4GetY: + case kInt32x4GetZ: + case kInt32x4GetW: + case kInt32x4GetFlagX: + case kInt32x4GetFlagY: + case kInt32x4GetFlagZ: + case kInt32x4GetFlagW: { + DCHECK(instr->hydrogen()->value()->representation().IsInt32x4()); + bool flag = false; + switch (instr->op()) { + case kInt32x4GetFlagX: + flag = true; + case kInt32x4GetX: + break; + case kInt32x4GetFlagY: + flag = true; + case kInt32x4GetY: + select = 0x1; + break; + case kInt32x4GetFlagZ: + flag = true; + case kInt32x4GetZ: + select = 0x2; + break; + case kInt32x4GetFlagW: + flag = true; + case kInt32x4GetW: + select = 0x3; + break; + default: + UNREACHABLE(); + } + + XMMRegister input_reg = ToInt32x4Register(instr->value()); + Register result = ToRegister(instr->result()); + if (select == 0x0) { + __ movd(result, input_reg); + } else { + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope scope(masm(), SSE4_1); + __ extractps(result, input_reg, select); + } else { + XMMRegister xmm_scratch = xmm0; + __ pshufd(xmm_scratch, input_reg, select); + __ movd(result, xmm_scratch); + } + } + + if (flag) { + Label false_value, done; + __ test(result, result); + __ j(zero, &false_value, Label::kNear); + __ LoadRoot(result, Heap::kTrueValueRootIndex); + __ jmp(&done, Label::kNear); + __ bind(&false_value); + __ LoadRoot(result, Heap::kFalseValueRootIndex); + __ bind(&done); + } + return; + } + default: + UNREACHABLE(); + return; + } +} + +void LCodeGen::DoBinarySIMDOperation(LBinarySIMDOperation* instr) { + uint8_t imm8 = 0; // for with operation + switch (instr->op()) { + case kFloat32x4ExtractLane: { + Condition cc = never; + DCHECK(instr->hydrogen()->left()->representation().IsFloat32x4()); + DCHECK(instr->hydrogen()->right()->representation().IsInteger32()); + if (instr->hydrogen()->right()->IsConstant() && + HConstant::cast(instr->hydrogen()->right())->HasInteger32Value()) { + uint32_t right = ToInteger32(LConstantOperand::cast(instr->right())); + DCHECK((right >= 0) && (right <= 3)); + XMMRegister left_reg = ToFloat32x4Register(instr->left()); + XMMRegister result = ToDoubleRegister(instr->result()); + XMMRegister xmm_scratch = result.is(left_reg) ? xmm0 : result; + switch (right) { + case 3: + imm8++; + case 2: + imm8++; + case 1: + imm8++; + case 0: + break; + } + if (imm8 == 0x0) { + __ xorps(xmm_scratch, xmm_scratch); + __ cvtss2sd(xmm_scratch, left_reg); + if (!xmm_scratch.is(result)) { + __ movaps(result, xmm_scratch); + } + } else { + __ pshufd(xmm_scratch, left_reg, imm8); + if (!xmm_scratch.is(result)) { + __ xorps(result, result); + } + __ cvtss2sd(result, xmm_scratch); + } + } else { + Comment(";;; deoptimize: non-constant selector for extractLane"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kBool32x4ExtractLane: { + Condition cc = never; + DCHECK(instr->hydrogen()->left()->representation().IsBool32x4()); + DCHECK(instr->hydrogen()->right()->representation().IsInteger32()); + if (instr->hydrogen()->right()->IsConstant() && + HConstant::cast(instr->hydrogen()->right())->HasInteger32Value()) { + uint32_t right = ToInteger32(LConstantOperand::cast(instr->right())); + DCHECK((right >= 0) && (right <= 3)); + XMMRegister left_reg = ToBool32x4Register(instr->left()); + Register result = ToRegister(instr->result()); + switch (right) { + case 3: + imm8++; + case 2: + imm8++; + case 1: + imm8++; + case 0: + break; + } + if (imm8 == 0x0) { + __ movd(result, left_reg); + } else { + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope scope(masm(), SSE4_1); + __ extractps(result, left_reg, imm8); + } else { + XMMRegister xmm_scratch = xmm0; + __ pshufd(xmm_scratch, left_reg, imm8); + __ movd(result, xmm_scratch); + } + } + { + Label false_value, done; + __ test(result, result); + __ j(zero, &false_value, Label::kNear); + __ LoadRoot(result, Heap::kTrueValueRootIndex); + __ jmp(&done, Label::kNear); + __ bind(&false_value); + __ LoadRoot(result, Heap::kFalseValueRootIndex); + __ bind(&done); + } + } else { + Comment(";;; deoptimize: non-constant selector for extractLane"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + + case kInt32x4ExtractLane: { + Condition cc = never; + DCHECK(instr->hydrogen()->left()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->right()->representation().IsInteger32()); + if (instr->hydrogen()->right()->IsConstant() && + HConstant::cast(instr->hydrogen()->right())->HasInteger32Value()) { + XMMRegister left_reg = ToInt32x4Register(instr->left()); + uint32_t right = ToInteger32(LConstantOperand::cast(instr->right())); + DCHECK((right >= 0) && (right <= 3)); + Register result = ToRegister(instr->result()); + switch (right) { + case 3: + imm8 = 0x3; + break; + case 2: + imm8 = 0x2; + break; + case 1: + imm8 = 0x1; + break; + case 0: + imm8 = 0x0; + break; + default: + UNREACHABLE(); + } + if (imm8 == 0x0) { + __ movd(result, left_reg); + } else { + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope scope(masm(), SSE4_1); + __ extractps(result, left_reg, imm8); + } else { + XMMRegister xmm_scratch = xmm0; + __ pshufd(xmm_scratch, left_reg, imm8); + __ movd(result, xmm_scratch); + } + } + } else { + Comment(";;; deoptimize: non-constant selector for extractLane"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kFloat32x4Add: + case kFloat32x4Sub: + case kFloat32x4Mul: + case kFloat32x4Div: + case kFloat32x4Min: + case kFloat32x4Max: { + DCHECK(instr->left()->Equals(instr->result())); + DCHECK(instr->hydrogen()->left()->representation().IsFloat32x4()); + DCHECK(instr->hydrogen()->right()->representation().IsFloat32x4()); + XMMRegister left_reg = ToFloat32x4Register(instr->left()); + XMMRegister right_reg = ToFloat32x4Register(instr->right()); + switch (instr->op()) { + case kFloat32x4Add: + __ addps(left_reg, right_reg); + break; + case kFloat32x4Sub: + __ subps(left_reg, right_reg); + break; + case kFloat32x4Mul: + __ mulps(left_reg, right_reg); + break; + case kFloat32x4Div: + __ divps(left_reg, right_reg); + break; + case kFloat32x4Min: + __ minps(left_reg, right_reg); + break; + case kFloat32x4Max: + __ maxps(left_reg, right_reg); + break; + default: + UNREACHABLE(); + break; + } + return; + } + case kInt32x4ShiftLeft: + case kInt32x4ShiftRightArithmetic: { + DCHECK(instr->left()->Equals(instr->result())); + DCHECK(instr->hydrogen()->left()->representation().IsInt32x4()); + if (instr->hydrogen()->right()->IsConstant() && + HConstant::cast(instr->hydrogen()->right())->HasInteger32Value()) { + int32_t value = ToInteger32(LConstantOperand::cast(instr->right())); + uint8_t shift = static_cast(value & 0xFF); + XMMRegister left_reg = ToInt32x4Register(instr->left()); + switch (instr->op()) { + case kInt32x4ShiftLeft: + __ pslld(left_reg, shift); + break; + case kInt32x4ShiftRightArithmetic: + __ psrad(left_reg, shift); + break; + default: + UNREACHABLE(); + } + return; + } else { + XMMRegister left_reg = ToInt32x4Register(instr->left()); + Register shift = ToRegister(instr->right()); + XMMRegister xmm_scratch = double_scratch0(); + __ movd(xmm_scratch, shift); + switch (instr->op()) { + case kInt32x4ShiftLeft: + __ pslld(left_reg, xmm_scratch); + break; + case kInt32x4ShiftRightArithmetic: + __ psrad(left_reg, xmm_scratch); + break; + default: + UNREACHABLE(); + } + return; + } + } + case kFloat32x4LessThan: + case kFloat32x4LessThanOrEqual: + case kFloat32x4Equal: + case kFloat32x4NotEqual: + case kFloat32x4GreaterThanOrEqual: + case kFloat32x4GreaterThan: { + DCHECK(instr->hydrogen()->left()->representation().IsFloat32x4()); + DCHECK(instr->hydrogen()->right()->representation().IsFloat32x4()); + XMMRegister left_reg = ToFloat32x4Register(instr->left()); + XMMRegister right_reg = ToFloat32x4Register(instr->right()); + XMMRegister result_reg = ToBool32x4Register(instr->result()); + switch (instr->op()) { + case kFloat32x4LessThan: + if (result_reg.is(left_reg)) { + __ cmpltps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpnltps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpltps(result_reg, right_reg); + } + break; + case kFloat32x4LessThanOrEqual: { + if (result_reg.is(left_reg)) { + __ cmpleps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpnltps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpleps(result_reg, right_reg); + } + break; + } + case kFloat32x4Equal: + if (result_reg.is(left_reg)) { + __ cmpeqps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpeqps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpeqps(result_reg, right_reg); + } + break; + case kFloat32x4NotEqual: + if (result_reg.is(left_reg)) { + __ cmpneqps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpneqps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpneqps(result_reg, right_reg); + } + break; + case kFloat32x4GreaterThanOrEqual: + if (result_reg.is(left_reg)) { + __ cmpnltps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpltps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpnltps(result_reg, right_reg); + } + break; + case kFloat32x4GreaterThan: + if (result_reg.is(left_reg)) { + __ cmpnleps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpleps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpnleps(result_reg, right_reg); + } + break; + default: + UNREACHABLE(); + break; + } + return; + } + case kInt32x4And: + case kInt32x4Or: + case kInt32x4Xor: + case kInt32x4Add: + case kInt32x4Sub: + case kInt32x4Mul: { + DCHECK(instr->left()->Equals(instr->result())); + DCHECK(instr->hydrogen()->left()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->right()->representation().IsInt32x4()); + XMMRegister left_reg = ToInt32x4Register(instr->left()); + XMMRegister right_reg = ToInt32x4Register(instr->right()); + switch (instr->op()) { + case kInt32x4And: + __ andps(left_reg, right_reg); + break; + case kInt32x4Or: + __ orps(left_reg, right_reg); + break; + case kInt32x4Xor: + __ xorps(left_reg, right_reg); + break; + case kInt32x4Add: + __ paddd(left_reg, right_reg); + break; + case kInt32x4Sub: + __ psubd(left_reg, right_reg); + break; + case kInt32x4Mul: { + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope scope(masm(), SSE4_1); + __ pmulld(left_reg, right_reg); + } else { + // The algorithm is from + // http://stackoverflow.com/questions/10500766/sse-multiplication-of-4-32-bit-integers + XMMRegister xmm_scratch = xmm0; + __ movaps(xmm_scratch, left_reg); + __ pmuludq(left_reg, right_reg); + __ psrldq(xmm_scratch, 4); + __ psrldq(right_reg, 4); + __ pmuludq(xmm_scratch, right_reg); + __ pshufd(left_reg, left_reg, 8); + __ pshufd(xmm_scratch, xmm_scratch, 8); + __ punpackldq(left_reg, xmm_scratch); + } + } break; + default: + UNREACHABLE(); + break; + } + return; + } + case kInt32x4GreaterThan: + case kInt32x4Equal: + case kInt32x4LessThan: { + DCHECK(instr->left()->Equals(instr->result())); + DCHECK(instr->hydrogen()->left()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->right()->representation().IsInt32x4()); + XMMRegister left_reg = ToInt32x4Register(instr->left()); + XMMRegister right_reg = ToBool32x4Register(instr->right()); + switch (instr->op()) { + case kInt32x4GreaterThan: + __ pcmpgtd(left_reg, right_reg); + break; + case kInt32x4Equal: + __ pcmpeqd(left_reg, right_reg); + break; + case kInt32x4LessThan: { + XMMRegister xmm_scratch = xmm0; + __ movaps(xmm_scratch, right_reg); + __ pcmpgtd(xmm_scratch, left_reg); + __ movaps(left_reg, xmm_scratch); + break; + } + default: + UNREACHABLE(); + break; + } + return; + } + default: + UNREACHABLE(); + return; + } +} + +void LCodeGen::DoTernarySIMDOperation(LTernarySIMDOperation* instr) { + uint8_t imm8 = 0; + switch (instr->op()) { + case kFloat32x4Select: { + DCHECK(instr->hydrogen()->first()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->second()->representation().IsFloat32x4()); + DCHECK(instr->hydrogen()->third()->representation().IsFloat32x4()); + + XMMRegister mask_reg = ToInt32x4Register(instr->first()); + XMMRegister left_reg = ToFloat32x4Register(instr->second()); + XMMRegister right_reg = ToFloat32x4Register(instr->third()); + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + XMMRegister temp_reg = xmm0; + + // Copy mask. + __ movaps(temp_reg, mask_reg); + // Invert it. + __ notps(temp_reg); + // temp_reg = temp_reg & falseValue. + __ andps(temp_reg, right_reg); + + if (!result_reg.is(mask_reg)) { + if (result_reg.is(left_reg)) { + // result_reg = result_reg & trueValue. + __ andps(result_reg, mask_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } else { + __ movaps(result_reg, mask_reg); + // result_reg = result_reg & trueValue. + __ andps(result_reg, left_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } + } else { + // result_reg = result_reg & trueValue. + __ andps(result_reg, left_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } + return; + } + case kInt32x4Select: { + DCHECK(instr->hydrogen()->first()->representation().IsBool32x4()); + DCHECK(instr->hydrogen()->second()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->third()->representation().IsInt32x4()); + + XMMRegister mask_reg = ToBool32x4Register(instr->first()); + XMMRegister left_reg = ToInt32x4Register(instr->second()); + XMMRegister right_reg = ToInt32x4Register(instr->third()); + XMMRegister result_reg = ToInt32x4Register(instr->result()); + XMMRegister temp_reg = xmm0; + + // Copy mask. + __ movaps(temp_reg, mask_reg); + // Invert it. + __ notps(temp_reg); + // temp_reg = temp_reg & falseValue. + __ andps(temp_reg, right_reg); + + if (!result_reg.is(mask_reg)) { + if (result_reg.is(left_reg)) { + // result_reg = result_reg & trueValue. + __ andps(result_reg, mask_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } else { + __ movaps(result_reg, mask_reg); + // result_reg = result_reg & trueValue. + __ andps(result_reg, left_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } + } else { + // result_reg = result_reg & trueValue. + __ andps(result_reg, left_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } + return; + } + case kFloat32x4ReplaceLane: { + Condition cc = never; + DCHECK(instr->first()->Equals(instr->result())); + DCHECK(instr->hydrogen()->first()->representation().IsFloat32x4()); + DCHECK(instr->hydrogen()->second()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->third()->representation().IsDouble()); + if (instr->hydrogen()->second()->IsConstant() && + HConstant::cast(instr->hydrogen()->second())->HasInteger32Value()) { + int32_t x = ToInteger32(LConstantOperand::cast(instr->second())); + DCHECK((x >= 0) && (x <= 3)); + switch (x) { + case 3: + imm8++; + case 2: + imm8++; + case 1: + imm8++; + case 0: + break; + } + XMMRegister result_reg = ToFloat32x4Register(instr->first()); + XMMRegister value_reg = ToDoubleRegister(instr->third()); + XMMRegister xmm_scratch = xmm0; + __ xorps(xmm_scratch, xmm_scratch); + __ cvtsd2ss(xmm_scratch, value_reg); + if (CpuFeatures::IsSupported(SSE4_1)) { + imm8 = imm8 << 4; + CpuFeatureScope scope(masm(), SSE4_1); + __ insertps(result_reg, xmm_scratch, imm8); + } else { + __ sub(esp, Immediate(kFloat32x4Size)); + __ movups(Operand(esp, 0), result_reg); + __ movss(Operand(esp, imm8 * kFloatSize), xmm_scratch); + __ movups(result_reg, Operand(esp, 0)); + __ add(esp, Immediate(kFloat32x4Size)); + } + } else { + Comment(";;; deoptimize: non-constant selector for replaceLane."); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kInt32x4ReplaceLane: { + Condition cc = never; + DCHECK(instr->first()->Equals(instr->result())); + DCHECK(instr->hydrogen()->first()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->second()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->third()->representation().IsInteger32()); + if (instr->hydrogen()->second()->IsConstant() && + HConstant::cast(instr->hydrogen()->second())->HasInteger32Value()) { + int32_t x = ToInteger32(LConstantOperand::cast(instr->second())); + DCHECK((x >= 0) && (x <= 4)); + switch (x) { + case 3: + imm8++; + case 2: + imm8++; + case 1: + imm8++; + case 0: + break; + } + XMMRegister result_reg = ToInt32x4Register(instr->first()); + Register value_reg = ToRegister(instr->third()); + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope scope(masm(), SSE4_1); + __ pinsrd(result_reg, value_reg, imm8); + } else { + __ sub(esp, Immediate(kInt32x4Size)); + __ movdqu(Operand(esp, 0), result_reg); + __ mov(Operand(esp, imm8 * kFloatSize), value_reg); + __ movdqu(result_reg, Operand(esp, 0)); + __ add(esp, Immediate(kInt32x4Size)); + } + } else { + Comment(";;; deoptimize: non-constant selector for replaceLane."); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + default: + UNREACHABLE(); + return; + } +} + +void LCodeGen::DoQuarternarySIMDOperation(LQuarternarySIMDOperation* instr) { + switch (instr->op()) { + case kFloat32x4Constructor: { + DCHECK(instr->hydrogen()->x()->representation().IsDouble()); + DCHECK(instr->hydrogen()->y()->representation().IsDouble()); + DCHECK(instr->hydrogen()->z()->representation().IsDouble()); + DCHECK(instr->hydrogen()->w()->representation().IsDouble()); + XMMRegister x_reg = ToDoubleRegister(instr->x()); + XMMRegister y_reg = ToDoubleRegister(instr->y()); + XMMRegister z_reg = ToDoubleRegister(instr->z()); + XMMRegister w_reg = ToDoubleRegister(instr->w()); + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + __ sub(esp, Immediate(kFloat32x4Size)); + __ xorps(xmm0, xmm0); + __ cvtsd2ss(xmm0, x_reg); + __ movss(Operand(esp, 0 * kFloatSize), xmm0); + __ xorps(xmm0, xmm0); + __ cvtsd2ss(xmm0, y_reg); + __ movss(Operand(esp, 1 * kFloatSize), xmm0); + __ xorps(xmm0, xmm0); + __ cvtsd2ss(xmm0, z_reg); + __ movss(Operand(esp, 2 * kFloatSize), xmm0); + __ xorps(xmm0, xmm0); + __ cvtsd2ss(xmm0, w_reg); + __ movss(Operand(esp, 3 * kFloatSize), xmm0); + __ movups(result_reg, Operand(esp, 0 * kFloatSize)); + __ add(esp, Immediate(kFloat32x4Size)); + return; + } + case kInt32x4Constructor: { + DCHECK(instr->hydrogen()->x()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->y()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->z()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->w()->representation().IsInteger32()); + Register x_reg = ToRegister(instr->x()); + Register y_reg = ToRegister(instr->y()); + Register z_reg = ToRegister(instr->z()); + Register w_reg = ToRegister(instr->w()); + XMMRegister result_reg = ToInt32x4Register(instr->result()); + __ sub(esp, Immediate(kInt32x4Size)); + __ mov(Operand(esp, 0 * kInt32Size), x_reg); + __ mov(Operand(esp, 1 * kInt32Size), y_reg); + __ mov(Operand(esp, 2 * kInt32Size), z_reg); + __ mov(Operand(esp, 3 * kInt32Size), w_reg); + __ movups(result_reg, Operand(esp, 0 * kInt32Size)); + __ add(esp, Immediate(kInt32x4Size)); + return; + } + case kBool32x4Constructor: { + DCHECK(instr->hydrogen()->x()->representation().IsTagged()); + DCHECK(instr->hydrogen()->y()->representation().IsTagged()); + DCHECK(instr->hydrogen()->z()->representation().IsTagged()); + DCHECK(instr->hydrogen()->w()->representation().IsTagged()); + DCHECK(instr->hydrogen()->x()->type().IsBoolean()); + DCHECK(instr->hydrogen()->y()->type().IsBoolean()); + DCHECK(instr->hydrogen()->z()->type().IsBoolean()); + DCHECK(instr->hydrogen()->w()->type().IsBoolean()); + + Register x_reg = ToRegister(instr->x()); + Register y_reg = ToRegister(instr->y()); + Register z_reg = ToRegister(instr->z()); + Register w_reg = ToRegister(instr->w()); + XMMRegister result_reg = ToBool32x4Register(instr->result()); + + Immediate neg(-1); + Label done_x, done_y, done_z, done_w; + + __ xorps(result_reg, result_reg); + __ sub(esp, Immediate(kBool32x4Size)); + __ movups(Operand(esp, 0 * kBool32Size), result_reg); + + __ CompareRoot(x_reg, Heap::kTrueValueRootIndex); + __ j(not_equal, &done_x, Label::kNear); + __ mov(Operand(esp, 0 * kBool32Size), neg); + __ jmp(&done_x, Label::kNear); + __ bind(&done_x); + + __ CompareRoot(y_reg, Heap::kTrueValueRootIndex); + __ j(not_equal, &done_y, Label::kNear); + __ mov(Operand(esp, 1 * kBool32Size), neg); + __ jmp(&done_y, Label::kNear); + __ bind(&done_y); + + __ CompareRoot(z_reg, Heap::kTrueValueRootIndex); + __ j(not_equal, &done_z, Label::kNear); + __ mov(Operand(esp, 2 * kBool32Size), neg); + __ jmp(&done_z, Label::kNear); + __ bind(&done_z); + + __ CompareRoot(w_reg, Heap::kTrueValueRootIndex); + __ j(not_equal, &done_w, Label::kNear); + __ mov(Operand(esp, 3 * kBool32Size), neg); + __ jmp(&done_w, Label::kNear); + __ bind(&done_w); + + __ movups(result_reg, Operand(esp, 0 * kInt32Size)); + __ add(esp, Immediate(kBool32x4Size)); + return; + } + default: + UNREACHABLE(); + return; + } +} + +static uint8_t ComputeShuffleSelect(uint32_t x, uint32_t y, uint32_t z, + uint32_t w) { + DCHECK(x < 4 && y < 4 && z < 4 && w < 4); + uint32_t r = + static_cast(((w << 6) | (z << 4) | (y << 2) | (x << 0)) & 0xFF); + return r; +} + +void LCodeGen::DoQuinarySIMDOperation(LQuinarySIMDOperation* instr) { + switch (instr->op()) { + case kFloat32x4Swizzle: { + Condition cc = never; + DCHECK(instr->a0()->Equals(instr->result())); + DCHECK(instr->hydrogen()->a0()->representation().IsFloat32x4()); + if ((instr->hydrogen()->a1()->IsConstant() && + HConstant::cast(instr->hydrogen()->a1())->HasInteger32Value()) && + (instr->hydrogen()->a2()->IsConstant() && + HConstant::cast(instr->hydrogen()->a2())->HasInteger32Value()) && + (instr->hydrogen()->a3()->IsConstant() && + HConstant::cast(instr->hydrogen()->a3())->HasInteger32Value()) && + (instr->hydrogen()->a4()->IsConstant() && + HConstant::cast(instr->hydrogen()->a4())->HasInteger32Value())) { + int32_t x = ToInteger32(LConstantOperand::cast(instr->a1())); + int32_t y = ToInteger32(LConstantOperand::cast(instr->a2())); + int32_t z = ToInteger32(LConstantOperand::cast(instr->a3())); + int32_t w = ToInteger32(LConstantOperand::cast(instr->a4())); + uint8_t select = ComputeShuffleSelect(x, y, z, w); + XMMRegister left_reg = ToFloat32x4Register(instr->a0()); + __ shufps(left_reg, left_reg, select); + } else { + Comment(";;; deoptimize: non-constant selector for swizzle"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kInt32x4Swizzle: { + Condition cc = never; + DCHECK(instr->a0()->Equals(instr->result())); + DCHECK(instr->hydrogen()->a0()->representation().IsInt32x4()); + if ((instr->hydrogen()->a1()->IsConstant() && + HConstant::cast(instr->hydrogen()->a1())->HasInteger32Value()) && + (instr->hydrogen()->a2()->IsConstant() && + HConstant::cast(instr->hydrogen()->a2())->HasInteger32Value()) && + (instr->hydrogen()->a3()->IsConstant() && + HConstant::cast(instr->hydrogen()->a3())->HasInteger32Value()) && + (instr->hydrogen()->a4()->IsConstant() && + HConstant::cast(instr->hydrogen()->a4())->HasInteger32Value())) { + int32_t x = ToInteger32(LConstantOperand::cast(instr->a1())); + int32_t y = ToInteger32(LConstantOperand::cast(instr->a2())); + int32_t z = ToInteger32(LConstantOperand::cast(instr->a3())); + int32_t w = ToInteger32(LConstantOperand::cast(instr->a4())); + uint8_t select = ComputeShuffleSelect(x, y, z, w); + XMMRegister left_reg = ToInt32x4Register(instr->a0()); + __ pshufd(left_reg, left_reg, select); + } else { + Comment(";;; deoptimize: non-constant selector for shuffle"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + default: + UNREACHABLE(); + return; + } +} + +void LCodeGen::DoSenarySIMDOperation(LSenarySIMDOperation* instr) { + switch (instr->op()) { + case kFloat32x4Shuffle: + case kInt32x4Shuffle: { + Condition cc = never; + DCHECK(instr->a0()->Equals(instr->result())); + if (instr->op() == kFloat32x4Shuffle) { + DCHECK(instr->hydrogen()->a0()->representation().IsFloat32x4()); + DCHECK(instr->hydrogen()->a1()->representation().IsFloat32x4()); + } else { + DCHECK(instr->hydrogen()->a0()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->a1()->representation().IsInt32x4()); + } + + if ((instr->hydrogen()->a2()->IsConstant() && + HConstant::cast(instr->hydrogen()->a2())->HasInteger32Value()) && + (instr->hydrogen()->a3()->IsConstant() && + HConstant::cast(instr->hydrogen()->a3())->HasInteger32Value()) && + (instr->hydrogen()->a4()->IsConstant() && + HConstant::cast(instr->hydrogen()->a4())->HasInteger32Value()) && + (instr->hydrogen()->a5()->IsConstant() && + HConstant::cast(instr->hydrogen()->a5())->HasInteger32Value())) { + int32_t x = ToInteger32(LConstantOperand::cast(instr->a2())); + int32_t y = ToInteger32(LConstantOperand::cast(instr->a3())); + int32_t z = ToInteger32(LConstantOperand::cast(instr->a4())); + int32_t w = ToInteger32(LConstantOperand::cast(instr->a5())); + XMMRegister lhs, rhs; + if (instr->op() == kFloat32x4Shuffle) { + lhs = ToFloat32x4Register(instr->a0()); + rhs = ToFloat32x4Register(instr->a1()); + } else { + lhs = ToInt32x4Register(instr->a0()); + rhs = ToInt32x4Register(instr->a1()); + } + XMMRegister temp = xmm0; + + uint32_t num_lanes_from_lhs = (x < 4) + (y < 4) + (z < 4) + (w < 4); + if (num_lanes_from_lhs == 4) { + uint8_t select = ComputeShuffleSelect(x, y, z, w); + __ shufps(lhs, lhs, select); + } else if (num_lanes_from_lhs == 0) { + x -= 4; + y -= 4; + z -= 4; + w -= 4; + uint8_t select = ComputeShuffleSelect(x, y, z, w); + __ movaps(lhs, rhs); + __ shufps(lhs, lhs, select); + } else if (num_lanes_from_lhs == 3) { + uint8_t first_select = 0xFF; + uint8_t second_select = 0xFF; + if (x < 4 && y < 4) { + if (w >= 4) { + w -= 4; + // T = (Rw Rw Lz Lz) = shufps(firstMask, lhs, rhs) + first_select = ComputeShuffleSelect(w, w, z, z); + // (Lx Ly Lz Rw) = (Lx Ly Tz Tx) = shufps(secondMask, T, lhs) + second_select = ComputeShuffleSelect(x, y, 2, 0); + } else { + DCHECK(z >= 4); + z -= 4; + // T = (Rz Rz Lw Lw) = shufps(firstMask, lhs, rhs) + first_select = ComputeShuffleSelect(z, z, w, w); + // (Lx Ly Rz Lw) = (Lx Ly Tx Tz) = shufps(secondMask, T, lhs) + second_select = ComputeShuffleSelect(x, y, 0, 2); + } + + __ movaps(temp, rhs); + __ shufps(temp, lhs, first_select); + __ shufps(lhs, temp, second_select); + } + + DCHECK(z < 4 && w < 4); + if (z < 4 && w < 4) { + if (y >= 4) { + y -= 4; + // T = (Ry Ry Lx Lx) = shufps(firstMask, lhs, rhs) + first_select = ComputeShuffleSelect(y, y, x, x); + // (Lx Ry Lz Lw) = (Tz Tx Lz Lw) = shufps(secondMask, lhs, T) + second_select = ComputeShuffleSelect(2, 0, z, w); + } else { + DCHECK(x >= 4); + x -= 4; + // T = (Rx Rx Ly Ly) = shufps(firstMask, lhs, rhs) + first_select = ComputeShuffleSelect(x, x, y, y); + // (Rx Ly Lz Lw) = (Tx Tz Lz Lw) = shufps(secondMask, lhs, T) + second_select = ComputeShuffleSelect(0, 2, z, w); + } + + __ movaps(temp, rhs); + __ shufps(temp, lhs, first_select); + __ shufps(temp, lhs, second_select); + __ movaps(lhs, temp); + } + } else if (num_lanes_from_lhs == 2) { + if (x < 4 && y < 4) { + uint8_t select = ComputeShuffleSelect(x, y, z % 4, w % 4); + __ shufps(lhs, rhs, select); + } else if (z < 4 && w < 4) { + uint8_t select = ComputeShuffleSelect(x % 4, y % 4, z, w); + __ movaps(temp, rhs); + __ shufps(temp, lhs, select); + __ movaps(lhs, temp); + } else { + // In two shufps, for the most generic case: + uint8_t first_select[4], second_select[4]; + uint32_t i = 0, j = 2, k = 0; + +#define COMPUTE_SELECT(lane) \ + if (lane >= 4) { \ + first_select[j] = lane % 4; \ + second_select[k++] = j++; \ + } else { \ + first_select[i] = lane; \ + second_select[k++] = i++; \ + } + + COMPUTE_SELECT(x) + COMPUTE_SELECT(y) + COMPUTE_SELECT(z) + COMPUTE_SELECT(w) +#undef COMPUTE_SELECT + + DCHECK(i == 2 && j == 4 && k == 4); + + int8_t select; + select = ComputeShuffleSelect(first_select[0], first_select[1], + first_select[2], first_select[3]); + __ shufps(lhs, rhs, select); + + select = ComputeShuffleSelect(second_select[0], second_select[1], + second_select[2], second_select[3]); + __ shufps(lhs, lhs, select); + } + } + } else { + Comment(";;; deoptimize: non-constant selector for shuffle"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + + default: + UNREACHABLE(); + return; + } +} + #undef __ } // namespace internal diff --git a/src/crankshaft/ia32/lithium-codegen-ia32.h b/src/crankshaft/ia32/lithium-codegen-ia32.h index 38a493dbb4d..c61bfae1c4f 100644 --- a/src/crankshaft/ia32/lithium-codegen-ia32.h +++ b/src/crankshaft/ia32/lithium-codegen-ia32.h @@ -56,6 +56,10 @@ class LCodeGen: public LCodeGenBase { Operand ToOperand(LOperand* op) const; Register ToRegister(LOperand* op) const; XMMRegister ToDoubleRegister(LOperand* op) const; + XMMRegister ToFloat32x4Register(LOperand* op) const; + XMMRegister ToBool32x4Register(LOperand* op) const; + XMMRegister ToInt32x4Register(LOperand* op) const; + XMMRegister ToSIMD128Register(LOperand* op) const; bool IsInteger32(LConstantOperand* op) const; bool IsSmi(LConstantOperand* op) const; @@ -89,6 +93,8 @@ class LCodeGen: public LCodeGenBase { IntegerSignedness signedness); void DoDeferredTaggedToI(LTaggedToI* instr, Label* done); + void DoDeferredFloat32x4ToTagged(LInstruction* instr); + void DoDeferredInt32x4ToTagged(LInstruction* instr); void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr); void DoDeferredStackCheck(LStackCheck* instr); void DoDeferredMaybeGrowElements(LMaybeGrowElements* instr); @@ -99,6 +105,12 @@ class LCodeGen: public LCodeGenBase { void DoDeferredLoadMutableDouble(LLoadFieldByIndex* instr, Register object, Register index); + void DoDeferredSIMD128ToTagged(LInstruction* instr, Runtime::FunctionId id); + + template + void HandleTaggedToSIMD128(LTaggedToSIMD128* instr); + template + void HandleSIMD128ToTagged(LSIMD128ToTagged* instr); // Parallel move support. void DoParallelMove(LParallelMove* move); @@ -223,6 +235,10 @@ class LCodeGen: public LCodeGenBase { Register ToRegister(int index) const; XMMRegister ToDoubleRegister(int index) const; + XMMRegister ToFloat32x4Register(int index) const; + XMMRegister ToBool32x4Register(int index) const; + XMMRegister ToInt32x4Register(int index) const; + XMMRegister ToSIMD128Register(int index) const; int32_t ToRepresentation(LConstantOperand* op, const Representation& r) const; int32_t ToInteger32(LConstantOperand* op) const; ExternalReference ToExternalReference(LConstantOperand* op) const; @@ -286,6 +302,9 @@ class LCodeGen: public LCodeGenBase { void EnsureSpaceForLazyDeopt(int space_needed) override; void DoLoadKeyedExternalArray(LLoadKeyed* instr); + void HandleExternalArrayOpRequiresTemp(LOperand* key, + Representation key_representation, + ElementsKind elements_kind); void DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr); void DoLoadKeyedFixedArray(LLoadKeyed* instr); void DoStoreKeyedExternalArray(LStoreKeyed* instr); diff --git a/src/crankshaft/ia32/lithium-gap-resolver-ia32.cc b/src/crankshaft/ia32/lithium-gap-resolver-ia32.cc index be8251cffba..b97365cae76 100644 --- a/src/crankshaft/ia32/lithium-gap-resolver-ia32.cc +++ b/src/crankshaft/ia32/lithium-gap-resolver-ia32.cc @@ -346,6 +346,23 @@ void LGapResolver::EmitMove(int index) { __ movsd(xmm0, src); __ movsd(dst, xmm0); } + } else if (source->IsSIMD128Register()) { + XMMRegister src = cgen_->ToSIMD128Register(source); + if (destination->IsSIMD128Register()) { + __ movaps(cgen_->ToSIMD128Register(destination), src); + } else { + DCHECK(destination->IsSIMD128StackSlot()); + __ movups(cgen_->ToOperand(destination), src); + } + } else if (source->IsSIMD128StackSlot()) { + Operand src = cgen_->ToOperand(source); + if (destination->IsSIMD128Register()) { + __ movups(cgen_->ToSIMD128Register(destination), src); + } else { + DCHECK(destination->IsSIMD128StackSlot()); + __ movups(xmm0, src); + __ movups(cgen_->ToOperand(destination), xmm0); + } } else { UNREACHABLE(); } @@ -446,6 +463,40 @@ void LGapResolver::EmitSwap(int index) { __ mov(dst1, tmp); __ movsd(src0, xmm0); + } else if ((source->IsSIMD128StackSlot() && + destination->IsSIMD128StackSlot())) { + // Swap two XMM stack slots. + Operand src = cgen_->ToOperand(source); + Operand dst = cgen_->ToOperand(destination); + Register tmp = EnsureTempRegister(); + __ movups(xmm0, src); + for (int offset = 0; offset < kSIMD128Size; offset += kPointerSize) { + __ mov(tmp, Operand(dst, offset)); + __ mov(Operand(src, offset), tmp); + } + __ movups(dst, xmm0); + + } else if (source->IsSIMD128Register() && destination->IsSIMD128Register()) { + // Swap two XMM registers. + XMMRegister source_reg = cgen_->ToSIMD128Register(source); + XMMRegister destination_reg = cgen_->ToSIMD128Register(destination); + __ movaps(xmm0, source_reg); + __ movaps(source_reg, destination_reg); + __ movaps(destination_reg, xmm0); + + } else if (source->IsSIMD128Register() || destination->IsSIMD128Register()) { + // Swap a xmm register and a xmm stack slot. + DCHECK((source->IsSIMD128Register() && destination->IsSIMD128StackSlot()) || + (source->IsSIMD128StackSlot() && destination->IsSIMD128Register())); + XMMRegister reg = cgen_->ToSIMD128Register( + source->IsSIMD128Register() ? source : destination); + LOperand* other = source->IsSIMD128Register() ? destination : source; + DCHECK(other->IsSIMD128StackSlot()); + Operand other_operand = cgen_->ToOperand(other); + __ movups(xmm0, other_operand); + __ movups(other_operand, reg); + __ movaps(reg, xmm0); + } else { // No other combinations are possible. UNREACHABLE(); diff --git a/src/crankshaft/ia32/lithium-ia32.cc b/src/crankshaft/ia32/lithium-ia32.cc index 67942241e6f..1981c693f83 100644 --- a/src/crankshaft/ia32/lithium-ia32.cc +++ b/src/crankshaft/ia32/lithium-ia32.cc @@ -321,23 +321,47 @@ void LAccessArgumentsAt::PrintDataTo(StringStream* stream) { int LPlatformChunk::GetNextSpillIndex(RegisterKind kind) { - // Skip a slot if for a double-width slot. - if (kind == DOUBLE_REGISTERS) { - current_frame_slots_++; - current_frame_slots_ |= 1; - num_double_slots_++; + switch (kind) { + case GENERAL_REGISTERS: + return current_frame_slots_++; + case DOUBLE_REGISTERS: { + // Skip a slot if for a double-width slot. + current_frame_slots_++; + current_frame_slots_ |= 1; + num_double_slots_++; + return current_frame_slots_++; + } + case FLOAT32x4_REGISTERS: + case BOOL32x4_REGISTERS: + case INT32x4_REGISTERS: { + // Skip three slots if for a quad-width slot. + current_frame_slots_ += 3; + num_double_slots_ += 2; // for dynamic frame alignment + return current_frame_slots_++; + } + default: + UNREACHABLE(); + return -1; } - return current_frame_slots_++; } LOperand* LPlatformChunk::GetNextSpillSlot(RegisterKind kind) { int index = GetNextSpillIndex(kind); - if (kind == DOUBLE_REGISTERS) { - return LDoubleStackSlot::Create(index, zone()); - } else { - DCHECK(kind == GENERAL_REGISTERS); - return LStackSlot::Create(index, zone()); + switch (kind) { + case GENERAL_REGISTERS: + return LStackSlot::Create(index, zone()); + case DOUBLE_REGISTERS: + return LDoubleStackSlot::Create(index, zone()); + case FLOAT32x4_REGISTERS: + return LFloat32x4StackSlot::Create(index, zone()); + case BOOL32x4_REGISTERS: + return LBool32x4StackSlot::Create(index, zone()); + case INT32x4_REGISTERS: + return LInt32x4StackSlot::Create(index, zone()); + default: + UNREACHABLE(); + return NULL; } } @@ -931,6 +955,7 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { bool easy_case = !r.IsTagged() || type.IsBoolean() || type.IsSmi() || type.IsJSArray() || type.IsHeapNumber() || type.IsString(); + LOperand* temp = !easy_case && expected.NeedsMap() ? TempRegister() : NULL; LInstruction* branch = new(zone()) LBranch(UseRegister(value), temp); if (!easy_case && @@ -1782,7 +1807,9 @@ LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) { LOperand* length = !index->IsConstantOperand() ? UseOrConstantAtStart(instr->length()) : UseAtStart(instr->length()); - LInstruction* result = new(zone()) LBoundsCheck(index, length); + LOperand* temp0 = TempRegister(); + LOperand* temp1 = TempRegister(); + LInstruction* result = new (zone()) LBoundsCheck(index, length, temp0, temp1); if (!FLAG_debug_code || !instr->skip_check()) { result = AssignEnvironment(result); } @@ -1829,6 +1856,11 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { DefineAsRegister(new(zone()) LNumberUntagD(value, temp)); if (!val->representation().IsSmi()) result = AssignEnvironment(result); return result; + } else if (to.IsSIMD128()) { + LOperand* value = UseRegister(instr->value()); + LOperand* temp = TempRegister(); + LTaggedToSIMD128* res = new (zone()) LTaggedToSIMD128(value, temp, to); + return AssignEnvironment(DefineAsRegister(res)); } else if (to.IsSmi()) { LOperand* value = UseRegister(val); if (val->type().IsSmi()) { @@ -1903,6 +1935,18 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { return DefineAsRegister(new(zone()) LInteger32ToDouble(Use(val))); } } + } else if (from.IsSIMD128()) { + DCHECK(to.IsTagged()); + info()->MarkAsDeferredCalling(); + LOperand* value = UseRegister(instr->value()); + LOperand* temp = TempRegister(); + LOperand* temp2 = TempRegister(); + + // Make sure that temp and result_temp are different registers. + LUnallocated* result_temp = TempRegister(); + LSIMD128ToTagged* result = + new (zone()) LSIMD128ToTagged(value, temp, temp2); + return AssignPointerMap(Define(result, result_temp)); } UNREACHABLE(); return NULL; @@ -2147,12 +2191,20 @@ LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) { LOperand* LChunkBuilder::GetStoreKeyedValueOperand(HStoreKeyed* instr) { ElementsKind elements_kind = instr->elements_kind(); + BuiltinFunctionId op = instr->op(); // Determine if we need a byte register in this case for the value. bool val_is_fixed_register = - elements_kind == UINT8_ELEMENTS || - elements_kind == INT8_ELEMENTS || - elements_kind == UINT8_CLAMPED_ELEMENTS; + (elements_kind == UINT8_ELEMENTS || elements_kind == INT8_ELEMENTS || + elements_kind == UINT8_CLAMPED_ELEMENTS) && + (op != kInt8ArraySetFloat32x4XYZW && op != kInt8ArraySetFloat32x4X && + op != kInt8ArraySetFloat32x4XY && op != kInt8ArraySetFloat32x4XYZ && + op != kInt8ArraySetInt32x4XYZW && op != kInt8ArraySetInt32x4X && + op != kInt8ArraySetInt32x4XY && op != kInt8ArraySetInt32x4XYZ && + op != kUint8ArraySetFloat32x4XYZW && op != kUint8ArraySetFloat32x4X && + op != kUint8ArraySetFloat32x4XY && op != kUint8ArraySetFloat32x4XYZ && + op != kUint8ArraySetInt32x4XYZW && op != kUint8ArraySetInt32x4X && + op != kUint8ArraySetInt32x4XY && op != kUint8ArraySetInt32x4XYZ); if (val_is_fixed_register) { return UseFixed(instr->value(), eax); } @@ -2566,6 +2618,286 @@ LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) { return AssignPointerMap(result); } +const char* LNullarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_NULLARY_OPERATION_CASE_ITEM(module, function, name, p4) \ + case k##name: \ + return #module "-" #function; + SIMD_NULLARY_OPERATIONS(SIMD_NULLARY_OPERATION_CASE_ITEM) +#undef SIMD_NULLARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoNullarySIMDOperation( + HNullarySIMDOperation* instr) { + LNullarySIMDOperation* result = + new (zone()) LNullarySIMDOperation(instr->op()); + switch (instr->op()) { +#define SIMD_NULLARY_OPERATION_CASE_ITEM(module, function, name, p4) \ + case k##name: + SIMD_NULLARY_OPERATIONS(SIMD_NULLARY_OPERATION_CASE_ITEM) +#undef SIMD_NULLARY_OPERATION_CASE_ITEM + return DefineAsRegister(result); + default: + UNREACHABLE(); + return NULL; + } +} + +const char* LUnarySIMDOperation::Mnemonic() const { + switch (op()) { + case kSIMD128Change: + return "SIMD128-change"; +#define SIMD_UNARY_OPERATION_CASE_ITEM(module, function, name, p4, p5) \ + case k##name: \ + return #module "-" #function; + SIMD_UNARY_OPERATIONS(SIMD_UNARY_OPERATION_CASE_ITEM) + SIMD_UNARY_OPERATIONS_FOR_PROPERTY_ACCESS(SIMD_UNARY_OPERATION_CASE_ITEM) +#undef SIMD_UNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoUnarySIMDOperation(HUnarySIMDOperation* instr) { + LOperand* input = UseRegisterAtStart(instr->value()); + LUnarySIMDOperation* result = + new (zone()) LUnarySIMDOperation(input, instr->op()); + switch (instr->op()) { + case kSIMD128Change: + return AssignEnvironment(DefineAsRegister(result)); + case kFloat32x4Abs: + case kFloat32x4Neg: + case kFloat32x4RecipApprox: + case kFloat32x4RecipSqrtApprox: + case kFloat32x4Sqrt: + case kInt32x4Neg: + case kInt32x4Not: + return DefineSameAsFirst(result); + case kFloat32x4Check: + case kInt32x4Check: + case kFloat32x4BitsToInt32x4: + case kFloat32x4ToInt32x4: + case kInt32x4BitsToFloat32x4: + case kInt32x4ToFloat32x4: + case kFloat32x4Splat: + case kInt32x4Splat: + case kFloat32x4GetSignMask: + case kFloat32x4GetX: + case kFloat32x4GetY: + case kFloat32x4GetZ: + case kFloat32x4GetW: + case kInt32x4GetSignMask: + case kInt32x4GetX: + case kInt32x4GetY: + case kInt32x4GetZ: + case kInt32x4GetW: + case kBool32x4AnyTrue: + case kBool32x4AllTrue: + case kInt32x4GetFlagX: + case kInt32x4GetFlagY: + case kInt32x4GetFlagZ: + case kInt32x4GetFlagW: + return DefineAsRegister(result); + default: + UNREACHABLE(); + return NULL; + } +} + +const char* LBinarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_BINARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6) \ + case k##name: \ + return #module "-" #function; + SIMD_BINARY_OPERATIONS(SIMD_BINARY_OPERATION_CASE_ITEM) +#undef SIMD_BINARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoBinarySIMDOperation( + HBinarySIMDOperation* instr) { + switch (instr->op()) { + case kFloat32x4Add: + case kFloat32x4Div: + case kFloat32x4Max: + case kFloat32x4Min: + case kFloat32x4Mul: + case kFloat32x4Sub: + case kInt32x4Add: + case kInt32x4And: + case kInt32x4Mul: + case kInt32x4Or: + case kInt32x4Sub: + case kInt32x4Xor: + case kInt32x4GreaterThan: + case kInt32x4Equal: + case kInt32x4LessThan: { + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseRegisterAtStart(instr->right()); + LBinarySIMDOperation* result = + new (zone()) LBinarySIMDOperation(left, right, instr->op()); + return DefineSameAsFirst(result); + } + case kFloat32x4ExtractLane: + case kBool32x4ExtractLane: + case kInt32x4ExtractLane: + case kInt32x4ShiftLeft: + case kInt32x4ShiftRightArithmetic: { + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseOrConstant(instr->right()); + LBinarySIMDOperation* result = + new (zone()) LBinarySIMDOperation(left, right, instr->op()); + if (instr->op() == kFloat32x4ExtractLane || + instr->op() == kBool32x4ExtractLane || + instr->op() == kInt32x4ExtractLane) + return AssignEnvironment(DefineAsRegister(result)); + else + return AssignEnvironment(DefineSameAsFirst(result)); + } + case kFloat32x4LessThan: + case kFloat32x4LessThanOrEqual: + case kFloat32x4Equal: + case kFloat32x4NotEqual: + case kFloat32x4GreaterThanOrEqual: + case kFloat32x4GreaterThan: { + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseRegisterAtStart(instr->right()); + LBinarySIMDOperation* result = + new (zone()) LBinarySIMDOperation(left, right, instr->op()); + return DefineAsRegister(result); + } + default: + UNREACHABLE(); + return NULL; + } +} + +const char* LTernarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_TERNARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6, \ + p7) \ + case k##name: \ + return #module "-" #function; + SIMD_TERNARY_OPERATIONS(SIMD_TERNARY_OPERATION_CASE_ITEM) +#undef SIMD_TERNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoTernarySIMDOperation( + HTernarySIMDOperation* instr) { + LOperand* first = UseRegisterAtStart(instr->first()); + LOperand* second = UseRegisterAtStart(instr->second()); + LOperand* third = UseRegisterAtStart(instr->third()); + LTernarySIMDOperation* result = + new (zone()) LTernarySIMDOperation(first, second, third, instr->op()); + switch (instr->op()) { + case kInt32x4Select: + case kFloat32x4Select: { + return DefineAsRegister(result); + } + case kFloat32x4ReplaceLane: + case kInt32x4ReplaceLane: { + LOperand* second = UseOrConstant(instr->second()); + LOperand* third = UseRegisterAtStart(instr->third()); + LTernarySIMDOperation* result = + new (zone()) LTernarySIMDOperation(first, second, third, instr->op()); + return AssignEnvironment(DefineSameAsFirst(result)); + } + default: + UNREACHABLE(); + return NULL; + } +} + +const char* LQuarternarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_QUARTERNARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, \ + p6, p7, p8) \ + case k##name: \ + return #module "-" #function; + SIMD_QUARTERNARY_OPERATIONS(SIMD_QUARTERNARY_OPERATION_CASE_ITEM) +#undef SIMD_QUARTERNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoQuarternarySIMDOperation( + HQuarternarySIMDOperation* instr) { + LOperand* x = UseRegisterAtStart(instr->x()); + LOperand* y = UseRegisterAtStart(instr->y()); + LOperand* z = UseRegisterAtStart(instr->z()); + LOperand* w = UseRegisterAtStart(instr->w()); + LQuarternarySIMDOperation* result = + new (zone()) LQuarternarySIMDOperation(x, y, z, w, instr->op()); + return DefineAsRegister(result); +} + +const char* LQuinarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_QUINARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6, \ + p7, p8, p9) \ + case k##name: \ + return #module "-" #function; + SIMD_QUINARY_OPERATIONS(SIMD_QUINARY_OPERATION_CASE_ITEM) +#undef SIMD_QUINARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoQuinarySIMDOperation( + HQuinarySIMDOperation* instr) { + LOperand* a0 = UseRegisterAtStart(instr->a0()); + LOperand* a1 = UseOrConstant(instr->a1()); + LOperand* a2 = UseOrConstant(instr->a2()); + LOperand* a3 = UseOrConstant(instr->a3()); + LOperand* a4 = UseOrConstant(instr->a4()); + LQuinarySIMDOperation* result = + new (zone()) LQuinarySIMDOperation(a0, a1, a2, a3, a4, instr->op()); + return AssignEnvironment(DefineSameAsFirst(result)); +} + +const char* LSenarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_SENARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6, \ + p7, p8, p9, p10) \ + case k##name: \ + return #module "-" #function; + SIMD_SENARY_OPERATIONS(SIMD_SENARY_OPERATION_CASE_ITEM) +#undef SIMD_SENARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoSenarySIMDOperation( + HSenarySIMDOperation* instr) { + LOperand* a0 = UseRegisterAtStart(instr->a0()); + LOperand* a1 = UseRegisterAtStart(instr->a1()); + LOperand* a2 = UseOrConstant(instr->a2()); + LOperand* a3 = UseOrConstant(instr->a3()); + LOperand* a4 = UseOrConstant(instr->a4()); + LOperand* a5 = UseOrConstant(instr->a5()); + LSenarySIMDOperation* result = + new (zone()) LSenarySIMDOperation(a0, a1, a2, a3, a4, a5, instr->op()); + return AssignEnvironment(DefineSameAsFirst(result)); +} + } // namespace internal } // namespace v8 diff --git a/src/crankshaft/ia32/lithium-ia32.h b/src/crankshaft/ia32/lithium-ia32.h index e525341ca0f..526b51e070a 100644 --- a/src/crankshaft/ia32/lithium-ia32.h +++ b/src/crankshaft/ia32/lithium-ia32.h @@ -115,12 +115,21 @@ class LCodeGen; V(MaybeGrowElements) \ V(ModByConstI) \ V(ModByPowerOf2I) \ + V(NullarySIMDOperation) \ + V(UnarySIMDOperation) \ + V(BinarySIMDOperation) \ + V(TernarySIMDOperation) \ + V(QuarternarySIMDOperation) \ + V(QuinarySIMDOperation) \ + V(SenarySIMDOperation) \ V(ModI) \ V(MulI) \ V(NumberTagD) \ V(NumberTagI) \ V(NumberTagU) \ V(NumberUntagD) \ + V(SIMD128ToTagged) \ + V(TaggedToSIMD128) \ V(OsrEntry) \ V(Parameter) \ V(Power) \ @@ -965,6 +974,204 @@ class LMathPowHalf final : public LTemplateInstruction<1, 1, 1> { DECLARE_CONCRETE_INSTRUCTION(MathPowHalf, "math-pow-half") }; +class LNullarySIMDOperation final : public LTemplateInstruction<1, 0, 0> { + public: + explicit LNullarySIMDOperation(BuiltinFunctionId op) : op_(op) {} + + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kNullarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LNullarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsNullarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(NullarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LUnarySIMDOperation final : public LTemplateInstruction<1, 1, 0> { + public: + LUnarySIMDOperation(LOperand* value, BuiltinFunctionId op) : op_(op) { + inputs_[0] = value; + } + + LOperand* value() { return inputs_[0]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kUnarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LUnarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsUnarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(UnarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LBinarySIMDOperation final : public LTemplateInstruction<1, 2, 0> { + public: + LBinarySIMDOperation(LOperand* left, LOperand* right, BuiltinFunctionId op) + : op_(op) { + inputs_[0] = left; + inputs_[1] = right; + } + + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kBinarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LBinarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsBinarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(BinarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LTernarySIMDOperation final : public LTemplateInstruction<1, 3, 0> { + public: + LTernarySIMDOperation(LOperand* first, LOperand* second, LOperand* third, + BuiltinFunctionId op) + : op_(op) { + inputs_[0] = first; + inputs_[1] = second; + inputs_[2] = third; + } + + LOperand* first() { return inputs_[0]; } + LOperand* second() { return inputs_[1]; } + LOperand* third() { return inputs_[2]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kTernarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LTernarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsTernarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(TernarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LQuarternarySIMDOperation final : public LTemplateInstruction<1, 4, 0> { + public: + LQuarternarySIMDOperation(LOperand* x, LOperand* y, LOperand* z, LOperand* w, + BuiltinFunctionId op) + : op_(op) { + inputs_[0] = x; + inputs_[1] = y; + inputs_[2] = z; + inputs_[3] = w; + } + + LOperand* x() { return inputs_[0]; } + LOperand* y() { return inputs_[1]; } + LOperand* z() { return inputs_[2]; } + LOperand* w() { return inputs_[3]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { + return LInstruction::kQuarternarySIMDOperation; + } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LQuarternarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsQuarternarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(QuarternarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LQuinarySIMDOperation final : public LTemplateInstruction<1, 5, 0> { + public: + LQuinarySIMDOperation(LOperand* a0, LOperand* a1, LOperand* a2, LOperand* a3, + LOperand* a4, BuiltinFunctionId op) + : op_(op) { + inputs_[0] = a0; + inputs_[1] = a1; + inputs_[2] = a2; + inputs_[3] = a3; + inputs_[4] = a4; + } + + LOperand* a0() { return inputs_[0]; } + LOperand* a1() { return inputs_[1]; } + LOperand* a2() { return inputs_[2]; } + LOperand* a3() { return inputs_[3]; } + LOperand* a4() { return inputs_[4]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kQuinarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LQuinarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsQuinarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(QuinarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LSenarySIMDOperation final : public LTemplateInstruction<1, 6, 0> { + public: + LSenarySIMDOperation(LOperand* a0, LOperand* a1, LOperand* a2, LOperand* a3, + LOperand* a4, LOperand* a5, BuiltinFunctionId op) + : op_(op) { + inputs_[0] = a0; + inputs_[1] = a1; + inputs_[2] = a2; + inputs_[3] = a3; + inputs_[4] = a4; + inputs_[5] = a5; + } + + LOperand* a0() { return inputs_[0]; } + LOperand* a1() { return inputs_[1]; } + LOperand* a2() { return inputs_[2]; } + LOperand* a3() { return inputs_[3]; } + LOperand* a4() { return inputs_[4]; } + LOperand* a5() { return inputs_[5]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kSenarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LSenarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsSenarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(SenarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; class LCmpObjectEqAndBranch final : public LControlInstruction<2, 0> { public: @@ -1165,16 +1372,20 @@ class LHasInPrototypeChainAndBranch final : public LControlInstruction<2, 1> { DECLARE_HYDROGEN_ACCESSOR(HasInPrototypeChainAndBranch) }; - -class LBoundsCheck final : public LTemplateInstruction<0, 2, 0> { +class LBoundsCheck final : public LTemplateInstruction<0, 2, 2> { public: - LBoundsCheck(LOperand* index, LOperand* length) { + LBoundsCheck(LOperand* index, LOperand* length, LOperand* temp0, + LOperand* temp1) { inputs_[0] = index; inputs_[1] = length; + temps_[0] = temp0; + temps_[1] = temp1; } LOperand* index() { return inputs_[0]; } LOperand* length() { return inputs_[1]; } + LOperand* temp0() { return temps_[0]; } + LOperand* temp1() { return temps_[1]; } DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds-check") DECLARE_HYDROGEN_ACCESSOR(BoundsCheck) @@ -1565,6 +1776,13 @@ class LLoadKeyed final : public LTemplateInstruction<1, 3, 0> { } }; +inline static bool ExternalArrayOpRequiresPreScale( + Representation key_representation, ElementsKind kind) { + int shift_size = ElementsKindToShiftSize(kind); + return key_representation.IsSmi() + ? shift_size > static_cast(maximal_scale_factor) + kSmiTagSize + : shift_size > static_cast(maximal_scale_factor); +} inline static bool ExternalArrayOpRequiresTemp( Representation key_representation, @@ -1572,9 +1790,10 @@ inline static bool ExternalArrayOpRequiresTemp( // Operations that require the key to be divided by two to be converted into // an index cannot fold the scale operation into a load and need an extra // temp register to do the work. - return key_representation.IsSmi() && - (elements_kind == UINT8_ELEMENTS || elements_kind == INT8_ELEMENTS || - elements_kind == UINT8_CLAMPED_ELEMENTS); + return ExternalArrayOpRequiresPreScale(key_representation, elements_kind) || + (key_representation.IsSmi() && + (elements_kind == UINT8_ELEMENTS || elements_kind == INT8_ELEMENTS || + elements_kind == UINT8_CLAMPED_ELEMENTS)); } @@ -1902,6 +2121,21 @@ class LNumberTagD final : public LTemplateInstruction<1, 1, 1> { DECLARE_HYDROGEN_ACCESSOR(Change) }; +class LSIMD128ToTagged final : public LTemplateInstruction<1, 1, 2> { + public: + explicit LSIMD128ToTagged(LOperand* value, LOperand* temp, LOperand* temp2) { + inputs_[0] = value; + temps_[0] = temp; + temps_[1] = temp2; + } + + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + LOperand* temp2() { return temps_[1]; } + + DECLARE_CONCRETE_INSTRUCTION(SIMD128ToTagged, "simd128-tag") + DECLARE_HYDROGEN_ACCESSOR(Change) +}; // Sometimes truncating conversion from a tagged value to an int32. class LDoubleToI final : public LTemplateInstruction<1, 1, 1> { @@ -1979,6 +2213,25 @@ class LNumberUntagD final : public LTemplateInstruction<1, 1, 1> { DECLARE_HYDROGEN_ACCESSOR(Change); }; +class LTaggedToSIMD128 final : public LTemplateInstruction<1, 1, 1> { + public: + explicit LTaggedToSIMD128(LOperand* value, LOperand* temp, + Representation representation) + : representation_(representation) { + inputs_[0] = value; + temps_[0] = temp; + } + + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + Representation representation() const { return representation_; } + + DECLARE_CONCRETE_INSTRUCTION(TaggedToSIMD128, "simd128-untag") + DECLARE_HYDROGEN_ACCESSOR(Change); + + private: + Representation representation_; +}; class LSmiUntag final : public LTemplateInstruction<1, 1, 0> { public: diff --git a/src/crankshaft/lithium-allocator-inl.h b/src/crankshaft/lithium-allocator-inl.h index 631af6024bb..31c89add082 100644 --- a/src/crankshaft/lithium-allocator-inl.h +++ b/src/crankshaft/lithium-allocator-inl.h @@ -46,7 +46,8 @@ LGap* LAllocator::GapAt(int index) { void LAllocator::SetLiveRangeAssignedRegister(LiveRange* range, int reg) { - if (range->Kind() == DOUBLE_REGISTERS) { + if (range->Kind() == DOUBLE_REGISTERS || + IsSIMD128RegisterKind(range->Kind())) { assigned_double_registers_->Add(reg); } else { DCHECK(range->Kind() == GENERAL_REGISTERS); diff --git a/src/crankshaft/lithium-allocator.cc b/src/crankshaft/lithium-allocator.cc index d17cd27c107..63bbe7faa7e 100644 --- a/src/crankshaft/lithium-allocator.cc +++ b/src/crankshaft/lithium-allocator.cc @@ -208,6 +208,15 @@ LOperand* LiveRange::CreateAssignedOperand(Zone* zone) { case DOUBLE_REGISTERS: op = LDoubleRegister::Create(assigned_register(), zone); break; + case FLOAT32x4_REGISTERS: + op = LFloat32x4Register::Create(assigned_register(), zone); + break; + case BOOL32x4_REGISTERS: + op = LBool32x4Register::Create(assigned_register(), zone); + break; + case INT32x4_REGISTERS: + op = LInt32x4Register::Create(assigned_register(), zone); + break; default: UNREACHABLE(); } @@ -458,7 +467,7 @@ void LiveRange::ConvertOperands(Zone* zone) { if (use_pos->HasOperand()) { DCHECK(op->IsRegister() || op->IsDoubleRegister() || - !use_pos->RequiresRegister()); + op->IsSIMD128Register() || !use_pos->RequiresRegister()); use_pos->operand()->ConvertTo(op->kind(), op->index()); } use_pos = use_pos->next(); @@ -523,6 +532,7 @@ LAllocator::LAllocator(int num_values, HGraph* graph) active_live_ranges_(8, zone()), inactive_live_ranges_(8, zone()), reusable_slots_(8, zone()), + reusable_simd128_slots_(8, zone()), next_virtual_register_(num_values), first_artificial_register_(num_values), mode_(UNALLOCATED_REGISTERS), @@ -841,6 +851,21 @@ void LAllocator::MeetConstraintsBetween(LInstruction* first, double_artificial_registers_.Add( cur_input->virtual_register() - first_artificial_register_, zone()); + } else if (RequiredRegisterKind(input_copy->virtual_register()) == + FLOAT32x4_REGISTERS) { + float32x4_artificial_registers_.Add( + cur_input->virtual_register() - first_artificial_register_, + zone()); + } else if (RequiredRegisterKind(input_copy->virtual_register()) == + BOOL32x4_REGISTERS) { + bool32x4_artificial_registers_.Add( + cur_input->virtual_register() - first_artificial_register_, + zone()); + } else if (RequiredRegisterKind(input_copy->virtual_register()) == + INT32x4_REGISTERS) { + int32x4_artificial_registers_.Add( + cur_input->virtual_register() - first_artificial_register_, + zone()); } AddConstraintsGapMove(gap_index, input_copy, cur_input); @@ -1162,7 +1187,9 @@ void LAllocator::ResolveControlFlow(LiveRange* range, if (HasTaggedValue(range->id())) { branch->pointer_map()->RecordPointer(cur_op, chunk()->zone()); } else if (!cur_op->IsDoubleStackSlot() && - !cur_op->IsDoubleRegister()) { + !cur_op->IsDoubleRegister() && + !cur_op->IsSIMD128StackSlot() && + !cur_op->IsSIMD128Register()) { branch->pointer_map()->RemovePointer(cur_op); } } @@ -1485,6 +1512,9 @@ void LAllocator::AllocateRegisters() { if (live_ranges_[i] != NULL) { if (live_ranges_[i]->Kind() == mode_) { AddToUnhandledUnsorted(live_ranges_[i]); + } else if (mode_ == DOUBLE_REGISTERS && + IsSIMD128RegisterKind(live_ranges_[i]->Kind())) { + AddToUnhandledUnsorted(live_ranges_[i]); } } } @@ -1492,6 +1522,7 @@ void LAllocator::AllocateRegisters() { DCHECK(UnhandledIsSorted()); DCHECK(reusable_slots_.is_empty()); + DCHECK(reusable_simd128_slots_.is_empty()); DCHECK(active_live_ranges_.is_empty()); DCHECK(inactive_live_ranges_.is_empty()); @@ -1583,6 +1614,7 @@ void LAllocator::AllocateRegisters() { } reusable_slots_.Rewind(0); + reusable_simd128_slots_.Rewind(0); active_live_ranges_.Rewind(0); inactive_live_ranges_.Rewind(0); } @@ -1619,10 +1651,25 @@ RegisterKind LAllocator::RequiredRegisterKind(int virtual_register) const { HValue* value = graph_->LookupValue(virtual_register); if (value != NULL && value->representation().IsDouble()) { return DOUBLE_REGISTERS; + } else if (value != NULL && (value->representation().IsFloat32x4())) { + return FLOAT32x4_REGISTERS; + } else if (value != NULL && (value->representation().IsBool32x4())) { + return BOOL32x4_REGISTERS; + } else if (value != NULL && (value->representation().IsInt32x4())) { + return INT32x4_REGISTERS; } } else if (double_artificial_registers_.Contains( virtual_register - first_artificial_register_)) { return DOUBLE_REGISTERS; + } else if (float32x4_artificial_registers_.Contains( + virtual_register - first_artificial_register_)) { + return FLOAT32x4_REGISTERS; + } else if (bool32x4_artificial_registers_.Contains( + virtual_register - first_artificial_register_)) { + return BOOL32x4_REGISTERS; + } else if (int32x4_artificial_registers_.Contains( + virtual_register - first_artificial_register_)) { + return INT32x4_REGISTERS; } return GENERAL_REGISTERS; @@ -1705,19 +1752,26 @@ void LAllocator::FreeSpillSlot(LiveRange* range) { int index = range->TopLevel()->GetSpillOperand()->index(); if (index >= 0) { - reusable_slots_.Add(range, zone()); + if (IsSIMD128RegisterKind(range->Kind())) { + reusable_simd128_slots_.Add(range, zone()); + } else { + reusable_slots_.Add(range, zone()); + } } } LOperand* LAllocator::TryReuseSpillSlot(LiveRange* range) { - if (reusable_slots_.is_empty()) return NULL; - if (reusable_slots_.first()->End().Value() > + ZoneList* reusable_slots = IsSIMD128RegisterKind(range->Kind()) + ? &reusable_simd128_slots_ + : &reusable_slots_; + if (reusable_slots->is_empty()) return NULL; + if (reusable_slots->first()->End().Value() > range->TopLevel()->Start().Value()) { return NULL; } - LOperand* result = reusable_slots_.first()->TopLevel()->GetSpillOperand(); - reusable_slots_.Remove(0); + LOperand* result = reusable_slots->first()->TopLevel()->GetSpillOperand(); + reusable_slots->Remove(0); return result; } @@ -1780,7 +1834,8 @@ bool LAllocator::TryAllocateFreeReg(LiveRange* current) { } LOperand* hint = current->FirstHint(); - if (hint != NULL && (hint->IsRegister() || hint->IsDoubleRegister())) { + if (hint != NULL && (hint->IsRegister() || hint->IsDoubleRegister() || + hint->IsSIMD128Register())) { int register_index = hint->index(); TraceAlloc( "Found reg hint %s (free until [%d) for live range %d (end %d[).\n", @@ -2133,7 +2188,21 @@ void LAllocator::Spill(LiveRange* range) { if (!first->HasAllocatedSpillOperand()) { LOperand* op = TryReuseSpillSlot(range); - if (op == NULL) op = chunk_->GetNextSpillSlot(range->Kind()); + if (op == NULL) { + op = chunk_->GetNextSpillSlot(range->Kind()); + } else if (range->Kind() == FLOAT32x4_REGISTERS && + op->kind() != LOperand::FLOAT32x4_STACK_SLOT) { + // Convert to Float32x4StackSlot. + op = LFloat32x4StackSlot::Create(op->index(), zone()); + } else if (range->Kind() == BOOL32x4_REGISTERS && + op->kind() != LOperand::BOOL32x4_STACK_SLOT) { + // Convert to Bool32x4StackSlot. + op = LBool32x4StackSlot::Create(op->index(), zone()); + } else if (range->Kind() == INT32x4_REGISTERS && + op->kind() != LOperand::INT32x4_STACK_SLOT) { + // Convert to Int32x4StackSlot. + op = LInt32x4StackSlot::Create(op->index(), zone()); + } first->SetSpillOperand(op); } range->MakeSpilled(chunk()->zone()); diff --git a/src/crankshaft/lithium-allocator.h b/src/crankshaft/lithium-allocator.h index ce0e56560b4..262ba683b56 100644 --- a/src/crankshaft/lithium-allocator.h +++ b/src/crankshaft/lithium-allocator.h @@ -116,6 +116,10 @@ class LifetimePosition { int value_; }; +inline bool IsSIMD128RegisterKind(RegisterKind kind) { + return kind == FLOAT32x4_REGISTERS || kind == BOOL32x4_REGISTERS || + kind == INT32x4_REGISTERS; +} // Representation of the non-empty interval [start,end[. class UseInterval: public ZoneObject { @@ -529,11 +533,16 @@ class LAllocator BASE_EMBEDDED { ZoneList active_live_ranges_; ZoneList inactive_live_ranges_; ZoneList reusable_slots_; + // Slots reusable for float32x4, bool32x4 and int32x4 register spilling. + ZoneList reusable_simd128_slots_; // Next virtual register number to be assigned to temporaries. int next_virtual_register_; int first_artificial_register_; GrowableBitVector double_artificial_registers_; + GrowableBitVector float32x4_artificial_registers_; + GrowableBitVector bool32x4_artificial_registers_; + GrowableBitVector int32x4_artificial_registers_; RegisterKind mode_; int num_registers_; diff --git a/src/crankshaft/lithium.cc b/src/crankshaft/lithium.cc index 8cf3a3f0e6a..8eb16867468 100644 --- a/src/crankshaft/lithium.cc +++ b/src/crankshaft/lithium.cc @@ -116,6 +116,15 @@ void LOperand::PrintTo(StringStream* stream) { } break; } + case FLOAT32x4_STACK_SLOT: + stream->Add("[float32x4_stack:%d]", index()); + break; + case BOOL32x4_STACK_SLOT: + stream->Add("[bool32x4_stack:%d]", index()); + break; + case INT32x4_STACK_SLOT: + stream->Add("[int32x4_stack:%d]", index()); + break; case DOUBLE_REGISTER: { int reg_index = index(); if (reg_index < 0 || reg_index >= DoubleRegister::kMaxNumRegisters) { @@ -125,6 +134,35 @@ void LOperand::PrintTo(StringStream* stream) { } break; } + case FLOAT32x4_REGISTER: +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + stream->Add( + "[%s|R]", + RegisterConfiguration::Crankshaft()->GetSimd128RegisterName(index())); +#else + stream->Add("[%s|R]", "Target hasn't no method toString()"); +#endif + break; + case BOOL32x4_REGISTER: + +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + stream->Add("[%s|R]", "QwNeonRegister hasn't no toString"); + stream->Add( + "[%s|R]", + RegisterConfiguration::Crankshaft()->GetSimd128RegisterName(index())); +#else + stream->Add("[%s|R]", "Target hasn't no method toString()"); +#endif + break; + case INT32x4_REGISTER: +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + stream->Add( + "[%s|R]", + RegisterConfiguration::Crankshaft()->GetSimd128RegisterName(index())); +#else + stream->Add("[%s|R]", "Target hasn't no method toString()"); +#endif + break; } } @@ -216,7 +254,10 @@ void LEnvironment::PrintTo(StringStream* stream) { void LPointerMap::RecordPointer(LOperand* op, Zone* zone) { // Do not record arguments as pointers. if (op->IsStackSlot() && op->index() < 0) return; - DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot()); + DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot() && + !op->IsFloat32x4Register() && !op->IsFloat32x4StackSlot() && + !op->IsBool32x4Register() && !op->IsBool32x4StackSlot() && + !op->IsInt32x4Register() && !op->IsInt32x4StackSlot()); pointer_operands_.Add(op, zone); } @@ -224,7 +265,10 @@ void LPointerMap::RecordPointer(LOperand* op, Zone* zone) { void LPointerMap::RemovePointer(LOperand* op) { // Do not record arguments as pointers. if (op->IsStackSlot() && op->index() < 0) return; - DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot()); + DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot() && + !op->IsFloat32x4Register() && !op->IsFloat32x4StackSlot() && + !op->IsBool32x4Register() && !op->IsBool32x4StackSlot() && + !op->IsInt32x4Register() && !op->IsInt32x4StackSlot()); for (int i = 0; i < pointer_operands_.length(); ++i) { if (pointer_operands_[i]->Equals(op)) { pointer_operands_.Remove(i); @@ -237,7 +281,10 @@ void LPointerMap::RemovePointer(LOperand* op) { void LPointerMap::RecordUntagged(LOperand* op, Zone* zone) { // Do not record arguments as pointers. if (op->IsStackSlot() && op->index() < 0) return; - DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot()); + DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot() && + !op->IsFloat32x4Register() && !op->IsFloat32x4StackSlot() && + !op->IsBool32x4Register() && !op->IsBool32x4StackSlot() && + !op->IsInt32x4Register() && !op->IsInt32x4StackSlot()); untagged_operands_.Add(op, zone); } diff --git a/src/crankshaft/lithium.h b/src/crankshaft/lithium.h index a2c028330b9..5c7ed075cda 100644 --- a/src/crankshaft/lithium.h +++ b/src/crankshaft/lithium.h @@ -17,12 +17,18 @@ namespace v8 { namespace internal { -#define LITHIUM_OPERAND_LIST(V) \ - V(ConstantOperand, CONSTANT_OPERAND, 128) \ - V(StackSlot, STACK_SLOT, 128) \ - V(DoubleStackSlot, DOUBLE_STACK_SLOT, 128) \ - V(Register, REGISTER, 16) \ - V(DoubleRegister, DOUBLE_REGISTER, 16) +#define LITHIUM_OPERAND_LIST(V) \ + V(ConstantOperand, CONSTANT_OPERAND, 128) \ + V(StackSlot, STACK_SLOT, 128) \ + V(DoubleStackSlot, DOUBLE_STACK_SLOT, 128) \ + V(Float32x4StackSlot, FLOAT32x4_STACK_SLOT, 128) \ + V(Bool32x4StackSlot, BOOL32x4_STACK_SLOT, 128) \ + V(Int32x4StackSlot, INT32x4_STACK_SLOT, 128) \ + V(Register, REGISTER, 16) \ + V(DoubleRegister, DOUBLE_REGISTER, 16) \ + V(Float32x4Register, FLOAT32x4_REGISTER, 16) \ + V(Bool32x4Register, BOOL32x4_REGISTER, 16) \ + V(Int32x4Register, INT32x4_REGISTER, 16) class LOperand : public ZoneObject { public: @@ -32,8 +38,14 @@ class LOperand : public ZoneObject { CONSTANT_OPERAND, STACK_SLOT, DOUBLE_STACK_SLOT, + FLOAT32x4_STACK_SLOT, + BOOL32x4_STACK_SLOT, + INT32x4_STACK_SLOT, REGISTER, - DOUBLE_REGISTER + DOUBLE_REGISTER, + FLOAT32x4_REGISTER, + BOOL32x4_REGISTER, + INT32x4_REGISTER }; LOperand() : value_(KindField::encode(INVALID)) { } @@ -46,7 +58,20 @@ class LOperand : public ZoneObject { LITHIUM_OPERAND_PREDICATE(Unallocated, UNALLOCATED, 0) LITHIUM_OPERAND_PREDICATE(Ignored, INVALID, 0) #undef LITHIUM_OPERAND_PREDICATE - bool Equals(LOperand* other) const { return value_ == other->value_; } + bool IsSIMD128Register() const { + return kind() == FLOAT32x4_REGISTER || kind() == BOOL32x4_REGISTER || + kind() == INT32x4_REGISTER; + } + bool IsSIMD128StackSlot() const { + return kind() == FLOAT32x4_STACK_SLOT || kind() == BOOL32x4_STACK_SLOT || + kind() == INT32x4_STACK_SLOT; + } + bool Equals(LOperand* other) const { + return value_ == other->value_ || + (index() == other->index() && + ((IsSIMD128Register() && other->IsSIMD128Register()) || + (IsSIMD128StackSlot() && other->IsSIMD128StackSlot()))); + } void PrintTo(StringStream* stream); void ConvertTo(Kind kind, int index) { @@ -61,7 +86,7 @@ class LOperand : public ZoneObject { static void TearDownCaches(); protected: - static const int kKindFieldWidth = 3; + static const int kKindFieldWidth = 4; class KindField : public BitField { }; LOperand(Kind kind, int index) { ConvertTo(kind, index); } @@ -146,32 +171,32 @@ class LUnallocated : public LOperand { // because it accommodates a larger pay-load. // // For FIXED_SLOT policy: - // +------------------------------------------+ - // | slot_index | vreg | 0 | 001 | - // +------------------------------------------+ + // +-------------------------------------------+ + // | slot_index | vreg | 0 | 0001 | + // +-------------------------------------------+ // // For all other (extended) policies: - // +------------------------------------------+ - // | reg_index | L | PPP | vreg | 1 | 001 | L ... Lifetime - // +------------------------------------------+ P ... Policy + // +-------------------------------------------+ + // | reg_index | L | PPP | vreg | 1 | 0001 | L ... Lifetime + // +-------------------------------------------+ P ... Policy // // The slot index is a signed value which requires us to decode it manually // instead of using the BitField utility class. // The superclass has a KindField. - STATIC_ASSERT(kKindFieldWidth == 3); + STATIC_ASSERT(kKindFieldWidth == 4); // BitFields for all unallocated operands. - class BasicPolicyField : public BitField {}; - class VirtualRegisterField : public BitField {}; + class BasicPolicyField : public BitField {}; + class VirtualRegisterField : public BitField {}; // BitFields specific to BasicPolicy::FIXED_SLOT. - class FixedSlotIndexField : public BitField {}; + class FixedSlotIndexField : public BitField {}; // BitFields specific to BasicPolicy::EXTENDED_POLICY. - class ExtendedPolicyField : public BitField {}; - class LifetimeField : public BitField {}; - class FixedRegisterField : public BitField {}; + class ExtendedPolicyField : public BitField {}; + class LifetimeField : public BitField {}; + class FixedRegisterField : public BitField {}; static const int kMaxVirtualRegisters = VirtualRegisterField::kMax + 1; static const int kFixedSlotIndexWidth = FixedSlotIndexField::kSize; @@ -797,11 +822,13 @@ class LPhase : public CompilationPhase { // A register-allocator view of a Lithium instruction. It contains the id of // the output operand and a list of input operand uses. - enum RegisterKind { UNALLOCATED_REGISTERS, GENERAL_REGISTERS, - DOUBLE_REGISTERS + DOUBLE_REGISTERS, + FLOAT32x4_REGISTERS, + BOOL32x4_REGISTERS, + INT32x4_REGISTERS }; // Iterator for non-null temp operands. diff --git a/src/crankshaft/mips/lithium-mips.cc b/src/crankshaft/mips/lithium-mips.cc index a7880eee87d..5130a35bf4c 100644 --- a/src/crankshaft/mips/lithium-mips.cc +++ b/src/crankshaft/mips/lithium-mips.cc @@ -1181,6 +1181,46 @@ LInstruction* LChunkBuilder::DoMathRound(HUnaryMathOperation* instr) { return AssignEnvironment(DefineAsRegister(result)); } +LInstruction* LChunkBuilder::DoNullarySIMDOperation( + HNullarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoUnarySIMDOperation(HUnarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoBinarySIMDOperation( + HBinarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoTernarySIMDOperation( + HTernarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoQuarternarySIMDOperation( + HQuarternarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoQuinarySIMDOperation( + HQuinarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} + +LInstruction* LChunkBuilder::DoSenarySIMDOperation( + HSenarySIMDOperation* instr) { + UNIMPLEMENTED(); + return NULL; +} LInstruction* LChunkBuilder::DoCallNewArray(HCallNewArray* instr) { LOperand* context = UseFixed(instr->context(), cp); diff --git a/src/crankshaft/x64/lithium-codegen-x64.cc b/src/crankshaft/x64/lithium-codegen-x64.cc index e417eaaeb19..dbc1c322498 100644 --- a/src/crankshaft/x64/lithium-codegen-x64.cc +++ b/src/crankshaft/x64/lithium-codegen-x64.cc @@ -407,6 +407,9 @@ XMMRegister LCodeGen::ToDoubleRegister(int index) const { return XMMRegister::from_code(index); } +XMMRegister LCodeGen::ToSIMD128Register(int index) const { + return XMMRegister::from_code(index); +} Register LCodeGen::ToRegister(LOperand* op) const { DCHECK(op->IsRegister()); @@ -419,6 +422,26 @@ XMMRegister LCodeGen::ToDoubleRegister(LOperand* op) const { return ToDoubleRegister(op->index()); } +XMMRegister LCodeGen::ToFloat32x4Register(LOperand* op) const { + DCHECK(op->IsFloat32x4Register()); + return ToSIMD128Register(op->index()); +} + +XMMRegister LCodeGen::ToBool32x4Register(LOperand* op) const { + DCHECK(op->IsBool32x4Register()); + return ToSIMD128Register(op->index()); +} + +XMMRegister LCodeGen::ToInt32x4Register(LOperand* op) const { + DCHECK(op->IsInt32x4Register()); + return ToSIMD128Register(op->index()); +} + +XMMRegister LCodeGen::ToSIMD128Register(LOperand* op) const { + DCHECK(op->IsFloat32x4Register() || op->IsBool32x4Register() || + op->IsInt32x4Register()); + return ToSIMD128Register(op->index()); +} bool LCodeGen::IsInteger32Constant(LConstantOperand* op) const { return chunk_->LookupLiteralRepresentation(op).IsSmiOrInteger32(); @@ -492,7 +515,9 @@ static int ArgumentsOffsetWithoutFrame(int index) { Operand LCodeGen::ToOperand(LOperand* op) const { // Does not handle registers. In X64 assembler, plain registers are not // representable as an Operand. - DCHECK(op->IsStackSlot() || op->IsDoubleStackSlot()); + DCHECK(op->IsStackSlot() || op->IsDoubleStackSlot() || + op->IsFloat32x4StackSlot() || op->IsBool32x4StackSlot() || + op->IsInt32x4StackSlot()); if (NeedsEagerFrame()) { return Operand(rbp, FrameSlotToFPOffset(op->index())); } else { @@ -572,6 +597,25 @@ void LCodeGen::AddToTranslation(LEnvironment* environment, } else if (op->IsDoubleStackSlot()) { int index = op->index(); translation->StoreDoubleStackSlot(index); + } else if (op->IsFloat32x4StackSlot()) { + int index = op->index(); + if (index >= 0) { + index += StandardFrameConstants::kFixedFrameSize / kPointerSize; + } + translation->StoreSIMD128StackSlot(index, + Translation::FLOAT32x4_STACK_SLOT); + } else if (op->IsBool32x4StackSlot()) { + int index = op->index(); + if (index >= 0) { + index += StandardFrameConstants::kFixedFrameSize / kPointerSize; + } + translation->StoreSIMD128StackSlot(index, Translation::BOOL32x4_STACK_SLOT); + } else if (op->IsInt32x4StackSlot()) { + int index = op->index(); + if (index >= 0) { + index += StandardFrameConstants::kFixedFrameSize / kPointerSize; + } + translation->StoreSIMD128StackSlot(index, Translation::INT32x4_STACK_SLOT); } else if (op->IsRegister()) { Register reg = ToRegister(op); if (is_tagged) { @@ -584,6 +628,15 @@ void LCodeGen::AddToTranslation(LEnvironment* environment, } else if (op->IsDoubleRegister()) { XMMRegister reg = ToDoubleRegister(op); translation->StoreDoubleRegister(reg); + } else if (op->IsFloat32x4Register()) { + XMMRegister reg = ToFloat32x4Register(op); + translation->StoreSIMD128Register(reg, Translation::FLOAT32x4_REGISTER); + } else if (op->IsBool32x4Register()) { + XMMRegister reg = ToBool32x4Register(op); + translation->StoreSIMD128Register(reg, Translation::BOOL32x4_REGISTER); + } else if (op->IsInt32x4Register()) { + XMMRegister reg = ToInt32x4Register(op); + translation->StoreSIMD128Register(reg, Translation::INT32x4_REGISTER); } else if (op->IsConstantOperand()) { HConstant* constant = chunk()->LookupConstant(LConstantOperand::cast(op)); int src_index = DefineDeoptimizationLiteral(constant->handle(isolate())); @@ -1962,6 +2015,9 @@ void LCodeGen::DoBranch(LBranch* instr) { __ Xorpd(xmm_scratch, xmm_scratch); __ Ucomisd(reg, xmm_scratch); EmitBranch(instr, not_equal); + } else if (r.IsSIMD128()) { + DCHECK(!info()->IsStub()); + EmitBranch(instr, no_condition); } else { DCHECK(r.IsTagged()); Register reg = ToRegister(instr->value()); @@ -2736,6 +2792,19 @@ void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) { } } +bool LCodeGen::HandleExternalArrayOpRequiresPreScale( + LOperand* key, Representation key_representation, + ElementsKind elements_kind) { + Register key_reg = ToRegister(key); + if (ExternalArrayOpRequiresPreScale(key_representation, elements_kind)) { + int pre_shift_size = ElementsKindToShiftSize(elements_kind) - + static_cast(maximal_scale_factor); + DCHECK(pre_shift_size > 0); + __ shll(key_reg, Immediate(pre_shift_size)); + return true; + } + return false; +} void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) { ElementsKind elements_kind = instr->elements_kind(); @@ -2745,13 +2814,22 @@ void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) { Representation key_representation = instr->hydrogen()->key()->representation(); if (ExternalArrayOpRequiresTemp(key_representation, elements_kind)) { - __ SmiToInteger64(key_reg, key_reg); + if (!HandleExternalArrayOpRequiresPreScale(key, key_representation, + elements_kind)) + __ SmiToInteger64(key_reg, key_reg); } else if (instr->hydrogen()->IsDehoisted()) { // Sign extend key because it could be a 32 bit negative value // and the dehoisted address computation happens in 64 bits __ movsxlq(key_reg, key_reg); } + } else if (kPointerSize == kInt64Size && !key->IsConstantOperand()) { + Representation key_representation = + instr->hydrogen()->key()->representation(); + if (ExternalArrayOpRequiresTemp(key_representation, elements_kind)) + HandleExternalArrayOpRequiresPreScale(key, key_representation, + elements_kind); } + Operand operand(BuildFastArrayOperand( instr->elements(), key, @@ -2764,6 +2842,8 @@ void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) { __ Cvtss2sd(result, operand); } else if (elements_kind == FLOAT64_ELEMENTS) { __ Movsd(ToDoubleRegister(instr->result()), operand); + } else if (IsSIMD128ElementsKind(elements_kind)) { + __ movups(ToSIMD128Register(instr->result()), operand); } else { Register result(ToRegister(instr->result())); switch (elements_kind) { @@ -2934,6 +3014,7 @@ Operand LCodeGen::BuildFastArrayOperand( if (constant_value & 0xF0000000) { Abort(kArrayIndexConstantValueTooBig); } + return Operand(elements_pointer_reg, (constant_value << shift_size) + offset); } else { @@ -3551,6 +3632,1135 @@ void LCodeGen::DoMathPowHalf(LMathPowHalf* instr) { __ bind(&done); } +void LCodeGen::DoNullarySIMDOperation(LNullarySIMDOperation* instr) { + switch (instr->op()) { + case kFloat32x4Zero: { + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + __ xorps(result_reg, result_reg); + return; + } + case kInt32x4Zero: { + XMMRegister result_reg = ToInt32x4Register(instr->result()); + __ xorps(result_reg, result_reg); + return; + } + default: + UNREACHABLE(); + return; + } +} + +void LCodeGen::DoUnarySIMDOperation(LUnarySIMDOperation* instr) { + uint8_t select = 0; + switch (instr->op()) { + case kFloat32x4Check: { + XMMRegister input_reg = ToFloat32x4Register(instr->value()); + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + if (!result_reg.is(input_reg)) { + __ movaps(result_reg, input_reg); + } + return; + } + case kInt32x4Check: { + XMMRegister input_reg = ToInt32x4Register(instr->value()); + XMMRegister result_reg = ToInt32x4Register(instr->result()); + if (!result_reg.is(input_reg)) { + __ movaps(result_reg, input_reg); + } + return; + } + case kSIMD128Change: { + Comment( + ";;; deoptimize: can not perform representation change" + "for float32x4 or int32x4"); + DeoptimizeIf(no_condition, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kFloat32x4Abs: + case kFloat32x4Neg: + case kFloat32x4RecipApprox: + case kFloat32x4RecipSqrtApprox: + case kFloat32x4Sqrt: { + DCHECK(instr->value()->Equals(instr->result())); + DCHECK(instr->hydrogen()->value()->representation().IsFloat32x4()); + XMMRegister input_reg = ToFloat32x4Register(instr->value()); + switch (instr->op()) { + case kFloat32x4Abs: + __ absps(input_reg); + break; + case kFloat32x4Neg: + __ negateps(input_reg); + break; + case kFloat32x4RecipApprox: + __ rcpps(input_reg, input_reg); + break; + case kFloat32x4RecipSqrtApprox: + __ rsqrtps(input_reg, input_reg); + break; + case kFloat32x4Sqrt: + __ sqrtps(input_reg, input_reg); + break; + default: + UNREACHABLE(); + break; + } + return; + } + case kInt32x4Not: + case kInt32x4Neg: { + DCHECK(instr->hydrogen()->value()->representation().IsInt32x4()); + XMMRegister input_reg = ToInt32x4Register(instr->value()); + switch (instr->op()) { + case kInt32x4Not: + __ notps(input_reg); + break; + case kInt32x4Neg: + __ pnegd(input_reg); + break; + default: + UNREACHABLE(); + break; + } + return; + } + case kFloat32x4BitsToInt32x4: + case kFloat32x4ToInt32x4: { + DCHECK(instr->hydrogen()->value()->representation().IsFloat32x4()); + XMMRegister input_reg = ToFloat32x4Register(instr->value()); + XMMRegister result_reg = ToInt32x4Register(instr->result()); + if (instr->op() == kFloat32x4BitsToInt32x4) { + if (!result_reg.is(input_reg)) { + __ movaps(result_reg, input_reg); + } + } else { + DCHECK(instr->op() == kFloat32x4ToInt32x4); + __ cvtps2dq(result_reg, input_reg); + } + return; + } + case kInt32x4BitsToFloat32x4: + case kInt32x4ToFloat32x4: { + DCHECK(instr->hydrogen()->value()->representation().IsInt32x4()); + XMMRegister input_reg = ToInt32x4Register(instr->value()); + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + if (instr->op() == kInt32x4BitsToFloat32x4) { + if (!result_reg.is(input_reg)) { + __ movaps(result_reg, input_reg); + } + } else { + DCHECK(instr->op() == kInt32x4ToFloat32x4); + __ cvtdq2ps(result_reg, input_reg); + } + return; + } + case kFloat32x4Splat: { + DCHECK(instr->hydrogen()->value()->representation().IsDouble()); + XMMRegister input_reg = ToDoubleRegister(instr->value()); + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + XMMRegister xmm_scratch = double_scratch0(); + __ xorps(xmm_scratch, xmm_scratch); + __ cvtsd2ss(xmm_scratch, input_reg); + __ shufps(xmm_scratch, xmm_scratch, 0x0); + __ movaps(result_reg, xmm_scratch); + return; + } + case kInt32x4Splat: { + DCHECK(instr->hydrogen()->value()->representation().IsInteger32()); + Register input_reg = ToRegister(instr->value()); + XMMRegister result_reg = ToInt32x4Register(instr->result()); + __ movd(result_reg, input_reg); + __ shufps(result_reg, result_reg, 0x0); + return; + } + case kInt32x4GetSignMask: { + DCHECK(instr->hydrogen()->value()->representation().IsInt32x4()); + XMMRegister input_reg = ToInt32x4Register(instr->value()); + Register result = ToRegister(instr->result()); + __ movmskps(result, input_reg); + return; + } + case kFloat32x4GetSignMask: { + DCHECK(instr->hydrogen()->value()->representation().IsFloat32x4()); + XMMRegister input_reg = ToFloat32x4Register(instr->value()); + Register result = ToRegister(instr->result()); + __ movmskps(result, input_reg); + return; + } + case kFloat32x4GetW: + select++; + case kFloat32x4GetZ: + select++; + case kFloat32x4GetY: + select++; + case kFloat32x4GetX: { + DCHECK(instr->hydrogen()->value()->representation().IsFloat32x4()); + XMMRegister input_reg = ToFloat32x4Register(instr->value()); + XMMRegister result = ToDoubleRegister(instr->result()); + XMMRegister xmm_scratch = result.is(input_reg) ? xmm0 : result; + + if (select == 0x0) { + __ xorps(xmm_scratch, xmm_scratch); + __ cvtss2sd(xmm_scratch, input_reg); + if (!xmm_scratch.is(result)) { + __ movaps(result, xmm_scratch); + } + } else { + __ pshufd(xmm_scratch, input_reg, select); + if (!xmm_scratch.is(result)) { + __ xorps(result, result); + } + __ cvtss2sd(result, xmm_scratch); + } + return; + } + case kBool32x4AnyTrue: { + DCHECK(instr->hydrogen()->value()->representation().IsBool32x4()); + XMMRegister input_reg = ToBool32x4Register(instr->value()); + Register result = ToRegister(instr->result()); + __ movmskps(result, input_reg); + Label false_value, done; + __ testl(result, result); + __ j(zero, &false_value, Label::kNear); + __ LoadRoot(result, Heap::kTrueValueRootIndex); + __ jmp(&done, Label::kNear); + __ bind(&false_value); + __ LoadRoot(result, Heap::kFalseValueRootIndex); + __ bind(&done); + return; + } + case kBool32x4AllTrue: { + DCHECK(instr->hydrogen()->value()->representation().IsBool32x4()); + XMMRegister input_reg = ToBool32x4Register(instr->value()); + Register result = ToRegister(instr->result()); + __ movmskps(result, input_reg); + Label true_value, done; + __ xorl(result, Immediate(0xF)); + __ j(zero, &true_value, Label::kNear); + __ LoadRoot(result, Heap::kFalseValueRootIndex); + __ jmp(&done, Label::kNear); + __ bind(&true_value); + __ LoadRoot(result, Heap::kTrueValueRootIndex); + __ bind(&done); + return; + } + case kInt32x4GetX: + case kInt32x4GetY: + case kInt32x4GetZ: + case kInt32x4GetW: + case kInt32x4GetFlagX: + case kInt32x4GetFlagY: + case kInt32x4GetFlagZ: + case kInt32x4GetFlagW: { + DCHECK(instr->hydrogen()->value()->representation().IsInt32x4()); + bool flag = false; + switch (instr->op()) { + case kInt32x4GetFlagX: + flag = true; + case kInt32x4GetX: + break; + case kInt32x4GetFlagY: + flag = true; + case kInt32x4GetY: + select = 0x1; + break; + case kInt32x4GetFlagZ: + flag = true; + case kInt32x4GetZ: + select = 0x2; + break; + case kInt32x4GetFlagW: + flag = true; + case kInt32x4GetW: + select = 0x3; + break; + default: + UNREACHABLE(); + } + + XMMRegister input_reg = ToInt32x4Register(instr->value()); + Register result = ToRegister(instr->result()); + if (select == 0x0) { + __ movd(result, input_reg); + } else { + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope scope(masm(), SSE4_1); + __ extractps(result, input_reg, select); + } else { + XMMRegister xmm_scratch = xmm0; + __ pshufd(xmm_scratch, input_reg, select); + __ movd(result, xmm_scratch); + } + } + + if (flag) { + Label false_value, done; + __ testl(result, result); + __ j(zero, &false_value, Label::kNear); + __ LoadRoot(result, Heap::kTrueValueRootIndex); + __ jmp(&done, Label::kNear); + __ bind(&false_value); + __ LoadRoot(result, Heap::kFalseValueRootIndex); + __ bind(&done); + } + return; + } + default: + UNREACHABLE(); + return; + } +} + +#define DCHECK_EXTRACTLANE(TYPE) \ + DCHECK(instr->hydrogen()->left()->representation().Is##TYPE()); \ + DCHECK(instr->hydrogen()->right()->representation().IsInteger32()); + +void LCodeGen::DoBinarySIMDOperation(LBinarySIMDOperation* instr) { + uint8_t imm8 = 0; // for with operation + switch (instr->op()) { + case kFloat32x4ExtractLane: { + DCHECK_EXTRACTLANE(Float32x4); + Condition cc = never; + if (instr->hydrogen()->right()->IsConstant() && + HConstant::cast(instr->hydrogen()->right())->HasInteger32Value()) { + uint32_t right = ToInteger32(LConstantOperand::cast(instr->right())); + DCHECK((right >= 0) && (right <= 3)); + XMMRegister left_reg = ToFloat32x4Register(instr->left()); + XMMRegister result = ToDoubleRegister(instr->result()); + XMMRegister xmm_scratch = result.is(left_reg) ? xmm0 : result; + imm8 = right; + if (imm8 == 0x0) { + __ xorps(xmm_scratch, xmm_scratch); + __ cvtss2sd(xmm_scratch, left_reg); + __ movaps(result, xmm_scratch); + } else { + __ pshufd(xmm_scratch, left_reg, imm8); + __ cvtss2sd(result, xmm_scratch); + } + } else { + cc = no_condition; + Comment(";;; deoptimize: non-constant selector for extractLane"); + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kInt32x4ExtractLane: { + DCHECK_EXTRACTLANE(Int32x4); + Condition cc = never; + if (instr->hydrogen()->right()->IsConstant() && + HConstant::cast(instr->hydrogen()->right())->HasInteger32Value()) { + uint32_t right = ToInteger32(LConstantOperand::cast(instr->right())); + DCHECK((right >= 0) && (right <= 3)); + XMMRegister left_reg = ToInt32x4Register(instr->left()); + Register result = ToRegister(instr->result()); + imm8 = right; + if (imm8 == 0x0) { + __ movd(result, left_reg); + } else { + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope scope(masm(), SSE4_1); + __ extractps(result, left_reg, imm8); + } else { + XMMRegister xmm_scratch = xmm0; + __ pshufd(xmm_scratch, left_reg, imm8); + __ movd(result, xmm_scratch); + } + } + } else { + Comment(";;; deoptimize: non-constant selector for extractLane"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kBool32x4ExtractLane: { + DCHECK_EXTRACTLANE(Bool32x4); + Condition cc = never; + if (instr->hydrogen()->right()->IsConstant() && + HConstant::cast(instr->hydrogen()->right())->HasInteger32Value()) { + uint32_t right = ToInteger32(LConstantOperand::cast(instr->right())); + DCHECK((right >= 0) && (right <= 3)); + XMMRegister left_reg = ToBool32x4Register(instr->left()); + Register result = ToRegister(instr->result()); + imm8 = right; + if (imm8 == 0x0) { + __ movd(result, left_reg); + } else { + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope scope(masm(), SSE4_1); + __ extractps(result, left_reg, imm8); + } else { + XMMRegister xmm_scratch = xmm0; + __ pshufd(xmm_scratch, left_reg, imm8); + __ movd(result, xmm_scratch); + } + } + { + Label false_value, done; + __ testl(result, result); + __ j(zero, &false_value, Label::kNear); + __ LoadRoot(result, Heap::kTrueValueRootIndex); + __ jmp(&done, Label::kNear); + __ bind(&false_value); + __ LoadRoot(result, Heap::kFalseValueRootIndex); + __ bind(&done); + } + } else { + Comment(";;; deoptimize: non-constant selector for extractLane"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kFloat32x4Add: + case kFloat32x4Sub: + case kFloat32x4Mul: + case kFloat32x4Div: + case kFloat32x4Min: + case kFloat32x4Max: { + DCHECK(instr->left()->Equals(instr->result())); + DCHECK(instr->hydrogen()->left()->representation().IsFloat32x4()); + DCHECK(instr->hydrogen()->right()->representation().IsFloat32x4()); + XMMRegister left_reg = ToFloat32x4Register(instr->left()); + XMMRegister right_reg = ToFloat32x4Register(instr->right()); + switch (instr->op()) { + case kFloat32x4Add: + __ addps(left_reg, right_reg); + break; + case kFloat32x4Sub: + __ subps(left_reg, right_reg); + break; + case kFloat32x4Mul: + __ mulps(left_reg, right_reg); + break; + case kFloat32x4Div: + __ divps(left_reg, right_reg); + break; + case kFloat32x4Min: + __ minps(left_reg, right_reg); + break; + case kFloat32x4Max: + __ maxps(left_reg, right_reg); + break; + default: + UNREACHABLE(); + break; + } + return; + } + case kFloat32x4Shuffle: { + DCHECK(instr->left()->Equals(instr->result())); + DCHECK(instr->hydrogen()->left()->representation().IsFloat32x4()); + Condition cc = never; + if (instr->hydrogen()->right()->IsConstant() && + HConstant::cast(instr->hydrogen()->right())->HasInteger32Value()) { + int32_t value = ToInteger32(LConstantOperand::cast(instr->right())); + uint8_t select = static_cast(value & 0xFF); + XMMRegister left_reg = ToFloat32x4Register(instr->left()); + __ shufps(left_reg, left_reg, select); + } else { + Comment(";;; deoptimize: non-constant selector for shuffle"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kInt32x4Shuffle: { + DCHECK(instr->left()->Equals(instr->result())); + DCHECK(instr->hydrogen()->left()->representation().IsInt32x4()); + Condition cc = never; + if (instr->hydrogen()->right()->IsConstant() && + HConstant::cast(instr->hydrogen()->right())->HasInteger32Value()) { + int32_t value = ToInteger32(LConstantOperand::cast(instr->right())); + uint8_t select = static_cast(value & 0xFF); + XMMRegister left_reg = ToInt32x4Register(instr->left()); + __ pshufd(left_reg, left_reg, select); + } else { + Comment(";;; deoptimize: non-constant selector for shuffle"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kInt32x4ShiftLeft: + case kInt32x4ShiftRightArithmetic: { + DCHECK(instr->left()->Equals(instr->result())); + DCHECK(instr->hydrogen()->left()->representation().IsInt32x4()); + if (instr->hydrogen()->right()->IsConstant() && + HConstant::cast(instr->hydrogen()->right())->HasInteger32Value()) { + int32_t value = ToInteger32(LConstantOperand::cast(instr->right())); + uint8_t shift = static_cast(value & 0xFF); + XMMRegister left_reg = ToInt32x4Register(instr->left()); + switch (instr->op()) { + case kInt32x4ShiftLeft: + __ pslld(left_reg, shift); + break; + case kInt32x4ShiftRightArithmetic: + __ psrad(left_reg, shift); + break; + default: + UNREACHABLE(); + } + return; + } else { + XMMRegister left_reg = ToInt32x4Register(instr->left()); + Register shift = ToRegister(instr->right()); + XMMRegister xmm_scratch = double_scratch0(); + __ movd(xmm_scratch, shift); + switch (instr->op()) { + case kInt32x4ShiftLeft: + __ pslld(left_reg, xmm_scratch); + break; + case kInt32x4ShiftRightArithmetic: + __ psrad(left_reg, xmm_scratch); + break; + default: + UNREACHABLE(); + } + return; + } + } + case kFloat32x4LessThan: + case kFloat32x4LessThanOrEqual: + case kFloat32x4Equal: + case kFloat32x4NotEqual: + case kFloat32x4GreaterThanOrEqual: + case kFloat32x4GreaterThan: { + DCHECK(instr->hydrogen()->left()->representation().IsFloat32x4()); + DCHECK(instr->hydrogen()->right()->representation().IsFloat32x4()); + XMMRegister left_reg = ToFloat32x4Register(instr->left()); + XMMRegister right_reg = ToFloat32x4Register(instr->right()); + XMMRegister result_reg = ToBool32x4Register(instr->result()); + switch (instr->op()) { + case kFloat32x4LessThan: + if (result_reg.is(left_reg)) { + __ cmpltps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpnltps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpltps(result_reg, right_reg); + } + break; + case kFloat32x4LessThanOrEqual: + if (result_reg.is(left_reg)) { + __ cmpleps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpnleps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpleps(result_reg, right_reg); + } + break; + case kFloat32x4Equal: + if (result_reg.is(left_reg)) { + __ cmpeqps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpeqps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpeqps(result_reg, right_reg); + } + break; + case kFloat32x4NotEqual: + if (result_reg.is(left_reg)) { + __ cmpneqps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpneqps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpneqps(result_reg, right_reg); + } + break; + case kFloat32x4GreaterThanOrEqual: + if (result_reg.is(left_reg)) { + __ cmpnltps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpltps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpnltps(result_reg, right_reg); + } + break; + case kFloat32x4GreaterThan: + if (result_reg.is(left_reg)) { + __ cmpnleps(result_reg, right_reg); + } else if (result_reg.is(right_reg)) { + __ cmpleps(result_reg, left_reg); + } else { + __ movaps(result_reg, left_reg); + __ cmpnleps(result_reg, right_reg); + } + break; + default: + UNREACHABLE(); + break; + } + return; + } + case kInt32x4And: + case kInt32x4Or: + case kInt32x4Xor: + case kInt32x4Add: + case kInt32x4Sub: + case kInt32x4Mul: + case kInt32x4GreaterThan: + case kInt32x4Equal: + case kInt32x4LessThan: { + DCHECK(instr->left()->Equals(instr->result())); + DCHECK(instr->hydrogen()->left()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->right()->representation().IsInt32x4()); + XMMRegister left_reg = ToInt32x4Register(instr->left()); + XMMRegister right_reg = ToInt32x4Register(instr->right()); + switch (instr->op()) { + case kInt32x4And: + __ andps(left_reg, right_reg); + break; + case kInt32x4Or: + __ orps(left_reg, right_reg); + break; + case kInt32x4Xor: + __ xorps(left_reg, right_reg); + break; + case kInt32x4Add: + __ paddd(left_reg, right_reg); + break; + case kInt32x4Sub: + __ psubd(left_reg, right_reg); + break; + case kInt32x4Mul: + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope scope(masm(), SSE4_1); + __ pmulld(left_reg, right_reg); + } else { + // The algorithm is from + // http://stackoverflow.com/questions/10500766/sse-multiplication-of-4-32-bit-integers + XMMRegister xmm_scratch = xmm0; + __ movaps(xmm_scratch, left_reg); + __ pmuludq(left_reg, right_reg); + __ psrldq(xmm_scratch, 4); + __ psrldq(right_reg, 4); + __ pmuludq(xmm_scratch, right_reg); + __ pshufd(left_reg, left_reg, 8); + __ pshufd(xmm_scratch, xmm_scratch, 8); + __ punpackldq(left_reg, xmm_scratch); + } + break; + case kInt32x4GreaterThan: + __ pcmpgtd(left_reg, right_reg); + break; + case kInt32x4Equal: + __ pcmpeqd(left_reg, right_reg); + break; + case kInt32x4LessThan: { + XMMRegister xmm_scratch = xmm0; + __ movaps(xmm_scratch, right_reg); + __ pcmpgtd(xmm_scratch, left_reg); + __ movaps(left_reg, xmm_scratch); + break; + } + default: + UNREACHABLE(); + break; + } + return; + } + default: + UNREACHABLE(); + return; + } +} + +void LCodeGen::DoTernarySIMDOperation(LTernarySIMDOperation* instr) { + uint8_t imm8 = 0; + switch (instr->op()) { + case kFloat32x4Select: { + DCHECK(instr->hydrogen()->first()->representation().IsBool32x4()); + DCHECK(instr->hydrogen()->second()->representation().IsFloat32x4()); + DCHECK(instr->hydrogen()->third()->representation().IsFloat32x4()); + + XMMRegister mask_reg = ToBool32x4Register(instr->first()); + XMMRegister left_reg = ToFloat32x4Register(instr->second()); + XMMRegister right_reg = ToFloat32x4Register(instr->third()); + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + XMMRegister temp_reg = xmm0; + + // Copy mask. + __ movaps(temp_reg, mask_reg); + // Invert it. + __ notps(temp_reg); + // temp_reg = temp_reg & falseValue. + __ andps(temp_reg, right_reg); + + if (!result_reg.is(mask_reg)) { + if (result_reg.is(left_reg)) { + // result_reg = result_reg & trueValue. + __ andps(result_reg, mask_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } else { + __ movaps(result_reg, mask_reg); + // result_reg = result_reg & trueValue. + __ andps(result_reg, left_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } + } else { + // result_reg = result_reg & trueValue. + __ andps(result_reg, left_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } + return; + } + case kInt32x4Select: { + DCHECK(instr->hydrogen()->first()->representation().IsBool32x4()); + DCHECK(instr->hydrogen()->second()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->third()->representation().IsInt32x4()); + + XMMRegister mask_reg = ToBool32x4Register(instr->first()); + XMMRegister left_reg = ToInt32x4Register(instr->second()); + XMMRegister right_reg = ToInt32x4Register(instr->third()); + XMMRegister result_reg = ToInt32x4Register(instr->result()); + XMMRegister temp_reg = double_scratch0(); + + // Copy mask. + __ movaps(temp_reg, mask_reg); + // Invert it. + __ notps(temp_reg); + // temp_reg = temp_reg & falseValue. + __ andps(temp_reg, right_reg); + + if (!result_reg.is(mask_reg)) { + if (result_reg.is(left_reg)) { + // result_reg = result_reg & trueValue. + __ andps(result_reg, mask_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } else { + __ movaps(result_reg, mask_reg); + // result_reg = result_reg & trueValue. + __ andps(result_reg, left_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } + } else { + // result_reg = result_reg & trueValue. + __ andps(result_reg, left_reg); + // out = result_reg | temp_reg. + __ orps(result_reg, temp_reg); + } + return; + } + case kFloat32x4ReplaceLane: { + DCHECK(instr->first()->Equals(instr->result())); + DCHECK(instr->hydrogen()->first()->representation().IsFloat32x4()); + DCHECK(instr->hydrogen()->second()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->third()->representation().IsDouble()); + Condition cc = never; + if (instr->hydrogen()->second()->IsConstant() && + HConstant::cast(instr->hydrogen()->second())->HasInteger32Value()) { + int32_t x = ToInteger32(LConstantOperand::cast(instr->second())); + DCHECK((x >= 0) && (x <= 3)); + switch (x) { + case 3: + imm8++; + case 2: + imm8++; + case 1: + imm8++; + case 0: + break; + } + XMMRegister result_reg = ToFloat32x4Register(instr->first()); + XMMRegister value_reg = ToDoubleRegister(instr->third()); + XMMRegister xmm_scratch = xmm0; + __ xorps(xmm_scratch, xmm_scratch); + __ cvtsd2ss(xmm_scratch, value_reg); + if (CpuFeatures::IsSupported(SSE4_1)) { + imm8 = imm8 << 4; + CpuFeatureScope scope(masm(), SSE4_1); + __ insertps(result_reg, xmm_scratch, imm8); + } else { + __ subq(rsp, Immediate(kFloat32x4Size)); + __ movups(Operand(rsp, 0), result_reg); + __ movss(Operand(rsp, imm8 * kFloatSize), xmm_scratch); + __ movups(result_reg, Operand(rsp, 0)); + __ addq(rsp, Immediate(kFloat32x4Size)); + } + } else { + Comment(";;; deoptimize: non-constant selector for replacetLane"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kInt32x4ReplaceLane: { + DCHECK(instr->first()->Equals(instr->result())); + DCHECK(instr->hydrogen()->first()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->second()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->third()->representation().IsInteger32()); + Condition cc = never; + if (instr->hydrogen()->second()->IsConstant() && + HConstant::cast(instr->hydrogen()->second())->HasInteger32Value()) { + int32_t x = ToInteger32(LConstantOperand::cast(instr->second())); + DCHECK((x >= 0) && (x <= 4)); + switch (x) { + case 3: + imm8++; + case 2: + imm8++; + case 1: + imm8++; + case 0: + break; + } + XMMRegister result_reg = ToInt32x4Register(instr->first()); + Register value_reg = ToRegister(instr->third()); + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope scope(masm(), SSE4_1); + __ pinsrd(result_reg, value_reg, imm8); + } else { + __ subq(rsp, Immediate(kInt32x4Size)); + __ movdqu(Operand(rsp, 0), result_reg); + __ movl(Operand(rsp, imm8 * kFloatSize), value_reg); + __ movdqu(result_reg, Operand(rsp, 0)); + __ addq(rsp, Immediate(kInt32x4Size)); + } + } else { + Comment(";;; deoptimize: non-constant selector for replacetLane"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + default: + UNREACHABLE(); + return; + } +} + +void LCodeGen::DoQuarternarySIMDOperation(LQuarternarySIMDOperation* instr) { + switch (instr->op()) { + case kFloat32x4Constructor: { + DCHECK(instr->hydrogen()->x()->representation().IsDouble()); + DCHECK(instr->hydrogen()->y()->representation().IsDouble()); + DCHECK(instr->hydrogen()->z()->representation().IsDouble()); + DCHECK(instr->hydrogen()->w()->representation().IsDouble()); + XMMRegister x_reg = ToDoubleRegister(instr->x()); + XMMRegister y_reg = ToDoubleRegister(instr->y()); + XMMRegister z_reg = ToDoubleRegister(instr->z()); + XMMRegister w_reg = ToDoubleRegister(instr->w()); + XMMRegister result_reg = ToFloat32x4Register(instr->result()); + XMMRegister xmm_scratch = double_scratch0(); + __ subq(rsp, Immediate(kFloat32x4Size)); + __ xorps(xmm_scratch, xmm_scratch); + __ cvtsd2ss(xmm_scratch, x_reg); + __ movss(Operand(rsp, 0 * kFloatSize), xmm_scratch); + __ xorps(xmm_scratch, xmm_scratch); + __ cvtsd2ss(xmm_scratch, y_reg); + __ movss(Operand(rsp, 1 * kFloatSize), xmm_scratch); + __ xorps(xmm_scratch, xmm_scratch); + __ cvtsd2ss(xmm_scratch, z_reg); + __ movss(Operand(rsp, 2 * kFloatSize), xmm_scratch); + __ xorps(xmm_scratch, xmm_scratch); + __ cvtsd2ss(xmm_scratch, w_reg); + __ movss(Operand(rsp, 3 * kFloatSize), xmm_scratch); + __ movups(result_reg, Operand(rsp, 0 * kFloatSize)); + __ addq(rsp, Immediate(kFloat32x4Size)); + return; + } + case kInt32x4Constructor: { + DCHECK(instr->hydrogen()->x()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->y()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->z()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->w()->representation().IsInteger32()); + Register x_reg = ToRegister(instr->x()); + Register y_reg = ToRegister(instr->y()); + Register z_reg = ToRegister(instr->z()); + Register w_reg = ToRegister(instr->w()); + XMMRegister result_reg = ToInt32x4Register(instr->result()); + __ subq(rsp, Immediate(kInt32x4Size)); + __ movl(Operand(rsp, 0 * kInt32Size), x_reg); + __ movl(Operand(rsp, 1 * kInt32Size), y_reg); + __ movl(Operand(rsp, 2 * kInt32Size), z_reg); + __ movl(Operand(rsp, 3 * kInt32Size), w_reg); + __ movups(result_reg, Operand(rsp, 0 * kInt32Size)); + __ addq(rsp, Immediate(kInt32x4Size)); + return; + } + case kBool32x4Constructor: { + DCHECK(instr->hydrogen()->x()->representation().IsTagged()); + DCHECK(instr->hydrogen()->y()->representation().IsTagged()); + DCHECK(instr->hydrogen()->z()->representation().IsTagged()); + DCHECK(instr->hydrogen()->w()->representation().IsTagged()); + DCHECK(instr->hydrogen()->x()->type().IsBoolean()); + DCHECK(instr->hydrogen()->y()->type().IsBoolean()); + DCHECK(instr->hydrogen()->z()->type().IsBoolean()); + DCHECK(instr->hydrogen()->w()->type().IsBoolean()); + + Register x_reg = ToRegister(instr->x()); + Register y_reg = ToRegister(instr->y()); + Register z_reg = ToRegister(instr->z()); + Register w_reg = ToRegister(instr->w()); + XMMRegister result_reg = ToBool32x4Register(instr->result()); + + Immediate neg(-1); + Label done_x, done_y, done_z, done_w; + + __ xorps(result_reg, result_reg); + __ subq(rsp, Immediate(kBool32x4Size)); + __ movups(Operand(rsp, 0 * kBool32Size), result_reg); + + __ CompareRoot(x_reg, Heap::kTrueValueRootIndex); + __ j(not_equal, &done_x, Label::kNear); + __ movl(Operand(rsp, 0 * kBool32Size), neg); + __ jmp(&done_x, Label::kNear); + __ bind(&done_x); + + __ CompareRoot(y_reg, Heap::kTrueValueRootIndex); + __ j(not_equal, &done_y, Label::kNear); + __ movl(Operand(rsp, 1 * kBool32Size), neg); + __ jmp(&done_y, Label::kNear); + __ bind(&done_y); + + __ CompareRoot(z_reg, Heap::kTrueValueRootIndex); + __ j(not_equal, &done_z, Label::kNear); + __ movl(Operand(rsp, 2 * kBool32Size), neg); + __ jmp(&done_z, Label::kNear); + __ bind(&done_z); + + __ CompareRoot(w_reg, Heap::kTrueValueRootIndex); + __ j(not_equal, &done_w, Label::kNear); + __ movl(Operand(rsp, 3 * kBool32Size), neg); + __ jmp(&done_w, Label::kNear); + __ bind(&done_w); + + __ movups(result_reg, Operand(rsp, 0 * kBool32Size)); + __ addq(rsp, Immediate(kBool32x4Size)); + return; + } + default: + UNREACHABLE(); + return; + } +} + +static uint8_t ComputeShuffleSelect(uint32_t x, uint32_t y, uint32_t z, + uint32_t w) { + DCHECK(x < 4 && y < 4 && z < 4 && w < 4); + uint32_t r = + static_cast(((w << 6) | (z << 4) | (y << 2) | (x << 0)) & 0xFF); + return r; +} + +void LCodeGen::DoQuinarySIMDOperation(LQuinarySIMDOperation* instr) { + switch (instr->op()) { + case kFloat32x4Swizzle: { + DCHECK(instr->a0()->Equals(instr->result())); + DCHECK(instr->hydrogen()->a0()->representation().IsFloat32x4()); + Condition cc = never; + if ((instr->hydrogen()->a1()->IsConstant() && + HConstant::cast(instr->hydrogen()->a1())->HasInteger32Value()) && + (instr->hydrogen()->a2()->IsConstant() && + HConstant::cast(instr->hydrogen()->a2())->HasInteger32Value()) && + (instr->hydrogen()->a3()->IsConstant() && + HConstant::cast(instr->hydrogen()->a3())->HasInteger32Value()) && + (instr->hydrogen()->a4()->IsConstant() && + HConstant::cast(instr->hydrogen()->a4())->HasInteger32Value())) { + int32_t x = ToInteger32(LConstantOperand::cast(instr->a1())); + int32_t y = ToInteger32(LConstantOperand::cast(instr->a2())); + int32_t z = ToInteger32(LConstantOperand::cast(instr->a3())); + int32_t w = ToInteger32(LConstantOperand::cast(instr->a4())); + uint8_t select = ComputeShuffleSelect(x, y, z, w); + XMMRegister left_reg = ToFloat32x4Register(instr->a0()); + __ shufps(left_reg, left_reg, select); + } else { + Comment(";;; deoptimize: non-constant selector for swizzle"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + case kInt32x4Swizzle: { + DCHECK(instr->a0()->Equals(instr->result())); + DCHECK(instr->hydrogen()->a0()->representation().IsInt32x4()); + Condition cc = never; + if ((instr->hydrogen()->a1()->IsConstant() && + HConstant::cast(instr->hydrogen()->a1())->HasInteger32Value()) && + (instr->hydrogen()->a2()->IsConstant() && + HConstant::cast(instr->hydrogen()->a2())->HasInteger32Value()) && + (instr->hydrogen()->a3()->IsConstant() && + HConstant::cast(instr->hydrogen()->a3())->HasInteger32Value()) && + (instr->hydrogen()->a4()->IsConstant() && + HConstant::cast(instr->hydrogen()->a4())->HasInteger32Value())) { + int32_t x = ToInteger32(LConstantOperand::cast(instr->a1())); + int32_t y = ToInteger32(LConstantOperand::cast(instr->a2())); + int32_t z = ToInteger32(LConstantOperand::cast(instr->a3())); + int32_t w = ToInteger32(LConstantOperand::cast(instr->a4())); + uint8_t select = ComputeShuffleSelect(x, y, z, w); + XMMRegister left_reg = ToInt32x4Register(instr->a0()); + __ pshufd(left_reg, left_reg, select); + } else { + Comment(";;; deoptimize: non-constant selector for shuffle"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + default: + UNREACHABLE(); + return; + } +} + +void LCodeGen::DoSenarySIMDOperation(LSenarySIMDOperation* instr) { + switch (instr->op()) { + case kFloat32x4Shuffle: + case kInt32x4Shuffle: { + Condition cc = never; + DCHECK(instr->a0()->Equals(instr->result())); + if (instr->op() == kFloat32x4Shuffle) { + DCHECK(instr->hydrogen()->a0()->representation().IsFloat32x4()); + DCHECK(instr->hydrogen()->a1()->representation().IsFloat32x4()); + } else { + DCHECK(instr->hydrogen()->a0()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->a1()->representation().IsInt32x4()); + } + + if ((instr->hydrogen()->a2()->IsConstant() && + HConstant::cast(instr->hydrogen()->a2())->HasInteger32Value()) && + (instr->hydrogen()->a3()->IsConstant() && + HConstant::cast(instr->hydrogen()->a3())->HasInteger32Value()) && + (instr->hydrogen()->a4()->IsConstant() && + HConstant::cast(instr->hydrogen()->a4())->HasInteger32Value()) && + (instr->hydrogen()->a5()->IsConstant() && + HConstant::cast(instr->hydrogen()->a5())->HasInteger32Value())) { + int32_t x = ToInteger32(LConstantOperand::cast(instr->a2())); + int32_t y = ToInteger32(LConstantOperand::cast(instr->a3())); + int32_t z = ToInteger32(LConstantOperand::cast(instr->a4())); + int32_t w = ToInteger32(LConstantOperand::cast(instr->a5())); + XMMRegister lhs, rhs; + if (instr->op() == kFloat32x4Shuffle) { + lhs = ToFloat32x4Register(instr->a0()); + rhs = ToFloat32x4Register(instr->a1()); + } else { + lhs = ToInt32x4Register(instr->a0()); + rhs = ToInt32x4Register(instr->a1()); + } + XMMRegister temp = xmm0; + + uint32_t num_lanes_from_lhs = (x < 4) + (y < 4) + (z < 4) + (w < 4); + if (num_lanes_from_lhs == 4) { + uint8_t select = ComputeShuffleSelect(x, y, z, w); + __ shufps(lhs, lhs, select); + } else if (num_lanes_from_lhs == 0) { + x -= 4; + y -= 4; + z -= 4; + w -= 4; + uint8_t select = ComputeShuffleSelect(x, y, z, w); + __ movaps(lhs, rhs); + __ shufps(lhs, lhs, select); + } else if (num_lanes_from_lhs == 3) { + uint8_t first_select = 0xFF; + uint8_t second_select = 0xFF; + if (x < 4 && y < 4) { + if (w >= 4) { + w -= 4; + // T = (Rw Rw Lz Lz) = shufps(firstMask, lhs, rhs) + first_select = ComputeShuffleSelect(w, w, z, z); + // (Lx Ly Lz Rw) = (Lx Ly Tz Tx) = shufps(secondMask, T, lhs) + second_select = ComputeShuffleSelect(x, y, 2, 0); + } else { + DCHECK(z >= 4); + z -= 4; + // T = (Rz Rz Lw Lw) = shufps(firstMask, lhs, rhs) + first_select = ComputeShuffleSelect(z, z, w, w); + // (Lx Ly Rz Lw) = (Lx Ly Tx Tz) = shufps(secondMask, T, lhs) + second_select = ComputeShuffleSelect(x, y, 0, 2); + } + + __ movaps(temp, rhs); + __ shufps(temp, lhs, first_select); + __ shufps(lhs, temp, second_select); + } + + DCHECK(z < 4 && w < 4); + if (z < 4 && w < 4) { + if (y >= 4) { + y -= 4; + // T = (Ry Ry Lx Lx) = shufps(firstMask, lhs, rhs) + first_select = ComputeShuffleSelect(y, y, x, x); + // (Lx Ry Lz Lw) = (Tz Tx Lz Lw) = shufps(secondMask, lhs, T) + second_select = ComputeShuffleSelect(2, 0, z, w); + } else { + DCHECK(x >= 4); + x -= 4; + // T = (Rx Rx Ly Ly) = shufps(firstMask, lhs, rhs) + first_select = ComputeShuffleSelect(x, x, y, y); + // (Rx Ly Lz Lw) = (Tx Tz Lz Lw) = shufps(secondMask, lhs, T) + second_select = ComputeShuffleSelect(0, 2, z, w); + } + + __ movaps(temp, rhs); + __ shufps(temp, lhs, first_select); + __ shufps(temp, lhs, second_select); + __ movaps(lhs, temp); + } + } else if (num_lanes_from_lhs == 2) { + if (x < 4 && y < 4) { + uint8_t select = ComputeShuffleSelect(x, y, z % 4, w % 4); + __ shufps(lhs, rhs, select); + } else if (z < 4 && w < 4) { + uint8_t select = ComputeShuffleSelect(x % 4, y % 4, z, w); + __ movaps(temp, rhs); + __ shufps(temp, lhs, select); + __ movaps(lhs, temp); + } else { + // In two shufps, for the most generic case: + uint8_t first_select[4], second_select[4]; + uint32_t i = 0, j = 2, k = 0; + +#define COMPUTE_SELECT(lane) \ + if (lane >= 4) { \ + first_select[j] = lane % 4; \ + second_select[k++] = j++; \ + } else { \ + first_select[i] = lane; \ + second_select[k++] = i++; \ + } + + COMPUTE_SELECT(x) + COMPUTE_SELECT(y) + COMPUTE_SELECT(z) + COMPUTE_SELECT(w) +#undef COMPUTE_SELECT + + DCHECK(i == 2 && j == 4 && k == 4); + + int8_t select; + select = ComputeShuffleSelect(first_select[0], first_select[1], + first_select[2], first_select[3]); + __ shufps(lhs, rhs, select); + + select = ComputeShuffleSelect(second_select[0], second_select[1], + second_select[2], second_select[3]); + __ shufps(lhs, lhs, select); + } + } + } else { + Comment(";;; deoptimize: non-constant selector for shuffle"); + cc = no_condition; + } + DeoptimizeIf(cc, instr, DeoptimizeReason::kForcedDeoptToRuntime); + return; + } + + default: + UNREACHABLE(); + return; + } +} void LCodeGen::DoPower(LPower* instr) { Representation exponent_type = instr->hydrogen()->right()->representation(); @@ -3981,12 +5191,20 @@ void LCodeGen::DoStoreKeyedExternalArray(LStoreKeyed* instr) { Representation key_representation = instr->hydrogen()->key()->representation(); if (ExternalArrayOpRequiresTemp(key_representation, elements_kind)) { - __ SmiToInteger64(key_reg, key_reg); + if (!HandleExternalArrayOpRequiresPreScale(key, key_representation, + elements_kind)) + __ SmiToInteger64(key_reg, key_reg); } else if (instr->hydrogen()->IsDehoisted()) { // Sign extend key because it could be a 32 bit negative value // and the dehoisted address computation happens in 64 bits __ movsxlq(key_reg, key_reg); } + } else if (kPointerSize == kInt64Size && !key->IsConstantOperand()) { + Representation key_representation = + instr->hydrogen()->key()->representation(); + if (ExternalArrayOpRequiresTemp(key_representation, elements_kind)) + HandleExternalArrayOpRequiresPreScale(key, key_representation, + elements_kind); } Operand operand(BuildFastArrayOperand( instr->elements(), @@ -4001,6 +5219,8 @@ void LCodeGen::DoStoreKeyedExternalArray(LStoreKeyed* instr) { __ Movss(operand, value); } else if (elements_kind == FLOAT64_ELEMENTS) { __ Movsd(operand, ToDoubleRegister(instr->value())); + } else if (IsSIMD128ElementsKind(elements_kind)) { + __ movups(operand, ToSIMD128Register(instr->value())); } else { Register value(ToRegister(instr->value())); switch (elements_kind) { @@ -4614,6 +5834,81 @@ void LCodeGen::DoDeferredNumberTagD(LNumberTagD* instr) { __ movp(reg, kScratchRegister); } +void LCodeGen::DoDeferredSIMD128ToTagged(LSIMD128ToTagged* instr, + Runtime::FunctionId id) { + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + Register reg = ToRegister(instr->result()); + __ Move(reg, Smi::FromInt(0)); + + { + PushSafepointRegistersScope scope(this); + __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + __ CallRuntimeSaveDoubles(id); + RecordSafepointWithRegisters(instr->pointer_map(), 0, + Safepoint::kNoLazyDeopt); + __ movp(kScratchRegister, rax); + } + __ movp(reg, kScratchRegister); +} + +template +void LCodeGen::HandleSIMD128ToTagged(LSIMD128ToTagged* instr) { + class DeferredSIMD128ToTagged final : public LDeferredCode { + public: + DeferredSIMD128ToTagged(LCodeGen* codegen, LSIMD128ToTagged* instr, + Runtime::FunctionId id) + : LDeferredCode(codegen), instr_(instr), id_(id) {} + void Generate() override { + codegen()->DoDeferredSIMD128ToTagged(instr_, id_); + } + LInstruction* instr() override { return instr_; } + + private: + LSIMD128ToTagged* instr_; + Runtime::FunctionId id_; + }; + + XMMRegister input_reg = ToSIMD128Register(instr->value()); + Register reg = ToRegister(instr->result()); + Register tmp = ToRegister(instr->temp()); + Register tmp2 = ToRegister(instr->temp2()); + Register tmp3 = ToRegister(instr->temp3()); + + DeferredSIMD128ToTagged* deferred = new (zone()) + DeferredSIMD128ToTagged(this, instr, static_cast(D)); + if (FLAG_inline_new) { + if (I == FLOAT32x4_TYPE) { + __ AllocateFloat32x4(reg, tmp, tmp2, tmp3, deferred->entry()); + } else if (I == BOOL32x4_TYPE) { + __ AllocateBool32x4(reg, tmp, tmp2, tmp3, deferred->entry()); + } else if (I == INT32x4_TYPE) { + __ AllocateInt32x4(reg, tmp, tmp2, tmp3, deferred->entry()); + } + } else { + __ jmp(deferred->entry()); + } + __ bind(deferred->exit()); + + // load the value to SIMD object. + __ movups(FieldOperand(reg, T::kValueOffset), input_reg); +} + +void LCodeGen::DoSIMD128ToTagged(LSIMD128ToTagged* instr) { + if (instr->value()->IsFloat32x4Register()) { + HandleSIMD128ToTagged(instr); + } else if (instr->value()->IsBool32x4Register()) { + DCHECK(instr->value()->IsBool32x4Register()); + HandleSIMD128ToTagged( + instr); + } else { + DCHECK(instr->value()->IsInt32x4Register()); + HandleSIMD128ToTagged( + instr); + } +} void LCodeGen::DoSmiTag(LSmiTag* instr) { HChange* hchange = instr->hydrogen(); @@ -4806,6 +6101,36 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { EmitNumberUntagD(instr, input_reg, result_reg, mode); } +template +void LCodeGen::HandleTaggedToSIMD128(LTaggedToSIMD128* instr) { + LOperand* input = instr->value(); + DCHECK(input->IsRegister()); + LOperand* result = instr->result(); + DCHECK(result->IsSIMD128Register()); + + Register input_reg = ToRegister(input); + XMMRegister result_reg = ToSIMD128Register(result); + + __ testp(input_reg, Immediate(kSmiTagMask)); + DeoptimizeIf(zero, instr, DeoptimizeReason::kSmi); + __ CmpObjectType(input_reg, SIMD128_VALUE_TYPE, kScratchRegister); + DeoptimizeIf(not_equal, instr, DeoptimizeReason::kNotASIMD128); + + // Load value to SIMD register. + __ movups(result_reg, FieldOperand(input_reg, T::kValueOffset)); +} + +void LCodeGen::DoTaggedToSIMD128(LTaggedToSIMD128* instr) { + if (instr->representation().IsFloat32x4()) { + HandleTaggedToSIMD128(instr); + } else if (instr->representation().IsBool32x4()) { + DCHECK(instr->representation().IsBool32x4()); + HandleTaggedToSIMD128(instr); + } else { + DCHECK(instr->representation().IsInt32x4()); + HandleTaggedToSIMD128(instr); + } +} void LCodeGen::DoDoubleToI(LDoubleToI* instr) { LOperand* input = instr->value(); diff --git a/src/crankshaft/x64/lithium-codegen-x64.h b/src/crankshaft/x64/lithium-codegen-x64.h index 22c39ad088d..07c2d067c3c 100644 --- a/src/crankshaft/x64/lithium-codegen-x64.h +++ b/src/crankshaft/x64/lithium-codegen-x64.h @@ -55,6 +55,10 @@ class LCodeGen: public LCodeGenBase { // Support for converting LOperands to assembler types. Register ToRegister(LOperand* op) const; XMMRegister ToDoubleRegister(LOperand* op) const; + XMMRegister ToFloat32x4Register(LOperand* op) const; + XMMRegister ToBool32x4Register(LOperand* op) const; + XMMRegister ToInt32x4Register(LOperand* op) const; + XMMRegister ToSIMD128Register(LOperand* op) const; bool IsInteger32Constant(LConstantOperand* op) const; bool IsExternalConstant(LConstantOperand* op) const; bool IsDehoistedKeyConstant(LConstantOperand* op) const; @@ -97,8 +101,15 @@ class LCodeGen: public LCodeGenBase { void DoDeferredLoadMutableDouble(LLoadFieldByIndex* instr, Register object, Register index); + void DoDeferredSIMD128ToTagged(LSIMD128ToTagged* instr, + Runtime::FunctionId id); -// Parallel move support. + template + void HandleTaggedToSIMD128(LTaggedToSIMD128* instr); + template + void HandleSIMD128ToTagged(LSIMD128ToTagged* instr); + + // Parallel move support. void DoParallelMove(LParallelMove* move); void DoGap(LGap* instr); @@ -224,6 +235,7 @@ class LCodeGen: public LCodeGenBase { Register ToRegister(int index) const; XMMRegister ToDoubleRegister(int index) const; + XMMRegister ToSIMD128Register(int index) const; Operand BuildFastArrayOperand( LOperand* elements_pointer, LOperand* key, @@ -289,6 +301,9 @@ class LCodeGen: public LCodeGenBase { void EnsureSpaceForLazyDeopt(int space_needed) override; void DoLoadKeyedExternalArray(LLoadKeyed* instr); + bool HandleExternalArrayOpRequiresPreScale(LOperand* key, + Representation key_representation, + ElementsKind elements_kind); void DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr); void DoLoadKeyedFixedArray(LLoadKeyed* instr); void DoStoreKeyedExternalArray(LStoreKeyed* instr); diff --git a/src/crankshaft/x64/lithium-gap-resolver-x64.cc b/src/crankshaft/x64/lithium-gap-resolver-x64.cc index 94dffb333ac..04e504d5389 100644 --- a/src/crankshaft/x64/lithium-gap-resolver-x64.cc +++ b/src/crankshaft/x64/lithium-gap-resolver-x64.cc @@ -226,6 +226,23 @@ void LGapResolver::EmitMove(int index) { __ Movsd(kScratchDoubleReg, src); __ Movsd(cgen_->ToOperand(destination), kScratchDoubleReg); } + } else if (source->IsSIMD128Register()) { + XMMRegister src = cgen_->ToSIMD128Register(source); + if (destination->IsSIMD128Register()) { + __ movaps(cgen_->ToSIMD128Register(destination), src); + } else { + DCHECK(destination->IsSIMD128StackSlot()); + __ movups(cgen_->ToOperand(destination), src); + } + } else if (source->IsSIMD128StackSlot()) { + Operand src = cgen_->ToOperand(source); + if (destination->IsSIMD128Register()) { + __ movups(cgen_->ToSIMD128Register(destination), src); + } else { + DCHECK(destination->IsSIMD128StackSlot()); + __ movups(xmm0, src); + __ movups(cgen_->ToOperand(destination), xmm0); + } } else { UNREACHABLE(); } @@ -269,6 +286,19 @@ void LGapResolver::EmitSwap(int index) { __ Movsd(dst, kScratchDoubleReg); __ movp(src, kScratchRegister); + } else if ((source->IsSIMD128StackSlot() && + destination->IsSIMD128StackSlot())) { + // Swap two XMM stack slots. + STATIC_ASSERT(kSIMD128Size == 2 * kDoubleSize); + Operand src = cgen_->ToOperand(source); + Operand dst = cgen_->ToOperand(destination); + __ movups(xmm0, src); + __ movq(kScratchRegister, dst); + __ movq(src, kScratchRegister); + __ movq(kScratchRegister, Operand(dst, kDoubleSize)); + __ movq(Operand(src, kDoubleSize), kScratchRegister); + __ movups(dst, xmm0); + } else if (source->IsDoubleRegister() && destination->IsDoubleRegister()) { // Swap two double registers. XMMRegister source_reg = cgen_->ToDoubleRegister(source); @@ -277,6 +307,14 @@ void LGapResolver::EmitSwap(int index) { __ Movapd(source_reg, destination_reg); __ Movapd(destination_reg, kScratchDoubleReg); + } else if (source->IsSIMD128Register() && destination->IsSIMD128Register()) { + // Swap two XMM registers. + XMMRegister source_reg = cgen_->ToSIMD128Register(source); + XMMRegister destination_reg = cgen_->ToSIMD128Register(destination); + __ movaps(xmm0, source_reg); + __ movaps(source_reg, destination_reg); + __ movaps(destination_reg, xmm0); + } else if (source->IsDoubleRegister() || destination->IsDoubleRegister()) { // Swap a double register and a double stack slot. DCHECK((source->IsDoubleRegister() && destination->IsDoubleStackSlot()) || @@ -291,6 +329,18 @@ void LGapResolver::EmitSwap(int index) { __ Movsd(reg, other_operand); __ Movsd(other_operand, kScratchDoubleReg); + } else if (source->IsSIMD128Register() || destination->IsSIMD128Register()) { + // Swap a xmm register and a xmm stack slot. + DCHECK((source->IsSIMD128Register() && destination->IsSIMD128StackSlot()) || + (source->IsSIMD128StackSlot() && destination->IsSIMD128Register())); + XMMRegister reg = cgen_->ToSIMD128Register( + source->IsSIMD128Register() ? source : destination); + LOperand* other = source->IsSIMD128Register() ? destination : source; + DCHECK(other->IsSIMD128StackSlot()); + Operand other_operand = cgen_->ToOperand(other); + __ movups(xmm0, other_operand); + __ movups(other_operand, reg); + __ movaps(reg, xmm0); } else { // No other combinations are possible. UNREACHABLE(); diff --git a/src/crankshaft/x64/lithium-x64.cc b/src/crankshaft/x64/lithium-x64.cc index 42451690af1..d7a3a8624b5 100644 --- a/src/crankshaft/x64/lithium-x64.cc +++ b/src/crankshaft/x64/lithium-x64.cc @@ -321,6 +321,23 @@ int LPlatformChunk::GetNextSpillIndex(RegisterKind kind) { // TODO(haitao): make sure rbp is aligned at 8-byte boundary for x32 port. current_frame_slots_ |= 1; } + + switch (kind) { + case GENERAL_REGISTERS: + return current_frame_slots_++; + case DOUBLE_REGISTERS: + return current_frame_slots_++; + case FLOAT32x4_REGISTERS: + case BOOL32x4_REGISTERS: + case INT32x4_REGISTERS: { + current_frame_slots_++; + return current_frame_slots_++; + } + default: + UNREACHABLE(); + return -1; + } + return current_frame_slots_++; } @@ -330,11 +347,20 @@ LOperand* LPlatformChunk::GetNextSpillSlot(RegisterKind kind) { // Alternatively, at some point, start using half-size // stack slots for int32 values. int index = GetNextSpillIndex(kind); - if (kind == DOUBLE_REGISTERS) { - return LDoubleStackSlot::Create(index, zone()); - } else { - DCHECK(kind == GENERAL_REGISTERS); - return LStackSlot::Create(index, zone()); + switch (kind) { + case GENERAL_REGISTERS: + return LStackSlot::Create(index, zone()); + case DOUBLE_REGISTERS: + return LDoubleStackSlot::Create(index, zone()); + case FLOAT32x4_REGISTERS: + return LFloat32x4StackSlot::Create(index, zone()); + case BOOL32x4_REGISTERS: + return LBool32x4StackSlot::Create(index, zone()); + case INT32x4_REGISTERS: + return LInt32x4StackSlot::Create(index, zone()); + default: + UNREACHABLE(); + return NULL; } } @@ -1207,6 +1233,234 @@ LInstruction* LChunkBuilder::DoMathPowHalf(HUnaryMathOperation* instr) { return DefineSameAsFirst(result); } +const char* LNullarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_NULLARY_OPERATION_CASE_ITEM(module, function, name, p4) \ + case k##name: \ + return #module "-" #function; + SIMD_NULLARY_OPERATIONS(SIMD_NULLARY_OPERATION_CASE_ITEM) +#undef SIMD_NULLARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoNullarySIMDOperation( + HNullarySIMDOperation* instr) { + LNullarySIMDOperation* result = + new (zone()) LNullarySIMDOperation(instr->op()); + switch (instr->op()) { +#define SIMD_NULLARY_OPERATION_CASE_ITEM(module, function, name, p4) \ + case k##name: + SIMD_NULLARY_OPERATIONS(SIMD_NULLARY_OPERATION_CASE_ITEM) +#undef SIMD_NULLARY_OPERATION_CASE_ITEM + return DefineAsRegister(result); + default: + UNREACHABLE(); + return NULL; + } +} + +const char* LUnarySIMDOperation::Mnemonic() const { + switch (op()) { + case kSIMD128Change: + return "SIMD128-change"; +#define SIMD_UNARY_OPERATION_CASE_ITEM(module, function, name, p4, p5) \ + case k##name: \ + return #module "-" #function; + SIMD_UNARY_OPERATIONS(SIMD_UNARY_OPERATION_CASE_ITEM) + SIMD_UNARY_OPERATIONS_FOR_PROPERTY_ACCESS(SIMD_UNARY_OPERATION_CASE_ITEM) +#undef SIMD_UNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoUnarySIMDOperation(HUnarySIMDOperation* instr) { + LOperand* input = UseRegisterAtStart(instr->value()); + LUnarySIMDOperation* result = + new (zone()) LUnarySIMDOperation(input, instr->op()); + switch (instr->op()) { + case kSIMD128Change: + return AssignEnvironment(DefineAsRegister(result)); + case kFloat32x4Abs: + case kFloat32x4Neg: + case kFloat32x4RecipApprox: + case kFloat32x4RecipSqrtApprox: + case kFloat32x4Sqrt: + case kInt32x4Neg: + case kInt32x4Not: + return DefineSameAsFirst(result); + case kFloat32x4Check: + case kInt32x4Check: + case kFloat32x4BitsToInt32x4: + case kFloat32x4ToInt32x4: + case kInt32x4BitsToFloat32x4: + case kInt32x4ToFloat32x4: + case kFloat32x4Splat: + case kInt32x4Splat: + case kFloat32x4GetSignMask: + case kFloat32x4GetX: + case kFloat32x4GetY: + case kFloat32x4GetZ: + case kFloat32x4GetW: + case kInt32x4GetSignMask: + case kInt32x4GetX: + case kInt32x4GetY: + case kInt32x4GetZ: + case kInt32x4GetW: + case kBool32x4AnyTrue: + case kBool32x4AllTrue: + case kInt32x4GetFlagX: + case kInt32x4GetFlagY: + case kInt32x4GetFlagZ: + case kInt32x4GetFlagW: + return DefineAsRegister(result); + default: + UNREACHABLE(); + return NULL; + } +} + +const char* LBinarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_BINARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6) \ + case k##name: \ + return #module "-" #function; + SIMD_BINARY_OPERATIONS(SIMD_BINARY_OPERATION_CASE_ITEM) +#undef SIMD_BINARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoBinarySIMDOperation( + HBinarySIMDOperation* instr) { + switch (instr->op()) { + case kFloat32x4Add: + case kFloat32x4Div: + case kFloat32x4Max: + case kFloat32x4Min: + case kFloat32x4Mul: + case kFloat32x4Sub: + case kInt32x4Add: + case kInt32x4And: + case kInt32x4Mul: + case kInt32x4Or: + case kInt32x4Sub: + case kInt32x4Xor: + case kInt32x4GreaterThan: + case kInt32x4Equal: + case kInt32x4LessThan: { + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseRegisterAtStart(instr->right()); + LBinarySIMDOperation* result = + new (zone()) LBinarySIMDOperation(left, right, instr->op()); + return DefineSameAsFirst(result); + } + case kFloat32x4ExtractLane: + case kFloat32x4Shuffle: + case kBool32x4ExtractLane: + case kInt32x4Shuffle: + case kInt32x4ShiftLeft: + case kInt32x4ExtractLane: + case kInt32x4ShiftRightArithmetic: { + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseOrConstant(instr->right()); + LBinarySIMDOperation* result = + new (zone()) LBinarySIMDOperation(left, right, instr->op()); + if (instr->op() == kFloat32x4ExtractLane || + instr->op() == kBool32x4ExtractLane || + instr->op() == kInt32x4ExtractLane) + return AssignEnvironment(DefineAsRegister(result)); + else + return AssignEnvironment(DefineSameAsFirst(result)); + } + case kFloat32x4LessThan: + case kFloat32x4LessThanOrEqual: + case kFloat32x4Equal: + case kFloat32x4NotEqual: + case kFloat32x4GreaterThanOrEqual: + case kFloat32x4GreaterThan: { + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseRegisterAtStart(instr->right()); + LBinarySIMDOperation* result = + new (zone()) LBinarySIMDOperation(left, right, instr->op()); + return DefineAsRegister(result); + } + default: + UNREACHABLE(); + return NULL; + } +} + +const char* LTernarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_TERNARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6, \ + p7) \ + case k##name: \ + return #module "-" #function; + SIMD_TERNARY_OPERATIONS(SIMD_TERNARY_OPERATION_CASE_ITEM) +#undef SIMD_TERNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoTernarySIMDOperation( + HTernarySIMDOperation* instr) { + LOperand* first = UseRegisterAtStart(instr->first()); + LOperand* second = UseRegisterAtStart(instr->second()); + LOperand* third = UseRegisterAtStart(instr->third()); + LTernarySIMDOperation* result = + new (zone()) LTernarySIMDOperation(first, second, third, instr->op()); + switch (instr->op()) { + case kFloat32x4Select: + case kInt32x4Select: { + return DefineAsRegister(result); + } + case kFloat32x4ReplaceLane: + case kInt32x4ReplaceLane: { + LOperand* second = UseOrConstant(instr->second()); + LOperand* third = UseRegisterAtStart(instr->third()); + LTernarySIMDOperation* result = + new (zone()) LTernarySIMDOperation(first, second, third, instr->op()); + return AssignEnvironment(DefineSameAsFirst(result)); + } + default: + UNREACHABLE(); + return NULL; + } +} + +const char* LQuarternarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_QUARTERNARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, \ + p6, p7, p8) \ + case k##name: \ + return #module "-" #function; + SIMD_QUARTERNARY_OPERATIONS(SIMD_QUARTERNARY_OPERATION_CASE_ITEM) +#undef SIMD_QUARTERNARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoQuarternarySIMDOperation( + HQuarternarySIMDOperation* instr) { + LOperand* x = UseRegisterAtStart(instr->x()); + LOperand* y = UseRegisterAtStart(instr->y()); + LOperand* z = UseRegisterAtStart(instr->z()); + LOperand* w = UseRegisterAtStart(instr->w()); + LQuarternarySIMDOperation* result = + new (zone()) LQuarternarySIMDOperation(x, y, z, w, instr->op()); + return DefineAsRegister(result); +} LInstruction* LChunkBuilder::DoCallNewArray(HCallNewArray* instr) { LOperand* context = UseFixed(instr->context(), rsi); @@ -1817,6 +2071,11 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { LInstruction* result = DefineAsRegister(new(zone()) LNumberUntagD(value)); if (!val->representation().IsSmi()) result = AssignEnvironment(result); return result; + } else if (to.IsSIMD128()) { + LOperand* value = UseRegister(instr->value()); + LOperand* temp = TempRegister(); + LTaggedToSIMD128* res = new (zone()) LTaggedToSIMD128(value, temp, to); + return AssignEnvironment(DefineAsRegister(res)); } else if (to.IsSmi()) { LOperand* value = UseRegister(val); if (val->type().IsSmi()) { @@ -1892,6 +2151,19 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { return DefineAsRegister(new(zone()) LInteger32ToDouble(value)); } } + } else if (from.IsSIMD128()) { + DCHECK(to.IsTagged()); + info()->MarkAsDeferredCalling(); + LOperand* value = UseRegister(instr->value()); + LOperand* temp = TempRegister(); + LOperand* temp2 = TempRegister(); + LOperand* temp3 = TempRegister(); + + // Make sure that temp and result_temp are different registers. + LUnallocated* result_temp = TempRegister(); + LSIMD128ToTagged* result = + new (zone()) LSIMD128ToTagged(value, temp, temp2, temp3); + return AssignPointerMap(Define(result, result_temp)); } UNREACHABLE(); return NULL; @@ -2572,6 +2844,59 @@ LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) { return AssignPointerMap(result); } +const char* LQuinarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_QUINARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6, \ + p7, p8, p9) \ + case k##name: \ + return #module "-" #function; + SIMD_QUINARY_OPERATIONS(SIMD_QUINARY_OPERATION_CASE_ITEM) +#undef SIMD_QUINARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoQuinarySIMDOperation( + HQuinarySIMDOperation* instr) { + LOperand* a0 = UseRegisterAtStart(instr->a0()); + LOperand* a1 = UseOrConstant(instr->a1()); + LOperand* a2 = UseOrConstant(instr->a2()); + LOperand* a3 = UseOrConstant(instr->a3()); + LOperand* a4 = UseOrConstant(instr->a4()); + LQuinarySIMDOperation* result = + new (zone()) LQuinarySIMDOperation(a0, a1, a2, a3, a4, instr->op()); + return AssignEnvironment(DefineSameAsFirst(result)); +} + +const char* LSenarySIMDOperation::Mnemonic() const { + switch (op()) { +#define SIMD_SENARY_OPERATION_CASE_ITEM(module, function, name, p4, p5, p6, \ + p7, p8, p9, p10) \ + case k##name: \ + return #module "-" #function; + SIMD_SENARY_OPERATIONS(SIMD_SENARY_OPERATION_CASE_ITEM) +#undef SIMD_SENARY_OPERATION_CASE_ITEM + default: + UNREACHABLE(); + return NULL; + } +} + +LInstruction* LChunkBuilder::DoSenarySIMDOperation( + HSenarySIMDOperation* instr) { + LOperand* a0 = UseRegisterAtStart(instr->a0()); + LOperand* a1 = UseRegisterAtStart(instr->a1()); + LOperand* a2 = UseOrConstant(instr->a2()); + LOperand* a3 = UseOrConstant(instr->a3()); + LOperand* a4 = UseOrConstant(instr->a4()); + LOperand* a5 = UseOrConstant(instr->a5()); + LSenarySIMDOperation* result = + new (zone()) LSenarySIMDOperation(a0, a1, a2, a3, a4, a5, instr->op()); + return AssignEnvironment(DefineSameAsFirst(result)); +} + } // namespace internal } // namespace v8 diff --git a/src/crankshaft/x64/lithium-x64.h b/src/crankshaft/x64/lithium-x64.h index 5c0ce04a8ad..3f38b9ba6bd 100644 --- a/src/crankshaft/x64/lithium-x64.h +++ b/src/crankshaft/x64/lithium-x64.h @@ -111,12 +111,21 @@ class LCodeGen; V(MaybeGrowElements) \ V(ModByConstI) \ V(ModByPowerOf2I) \ + V(NullarySIMDOperation) \ + V(UnarySIMDOperation) \ + V(BinarySIMDOperation) \ + V(TernarySIMDOperation) \ + V(QuarternarySIMDOperation) \ + V(QuinarySIMDOperation) \ + V(SenarySIMDOperation) \ V(ModI) \ V(MulI) \ V(NumberTagD) \ + V(SIMD128ToTagged) \ V(NumberTagI) \ V(NumberTagU) \ V(NumberUntagD) \ + V(TaggedToSIMD128) \ V(OsrEntry) \ V(Parameter) \ V(Power) \ @@ -959,6 +968,204 @@ class LMathPowHalf final : public LTemplateInstruction<1, 1, 0> { DECLARE_CONCRETE_INSTRUCTION(MathPowHalf, "math-pow-half") }; +class LNullarySIMDOperation final : public LTemplateInstruction<1, 0, 0> { + public: + explicit LNullarySIMDOperation(BuiltinFunctionId op) : op_(op) {} + + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kNullarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LNullarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsNullarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(NullarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LUnarySIMDOperation final : public LTemplateInstruction<1, 1, 0> { + public: + LUnarySIMDOperation(LOperand* value, BuiltinFunctionId op) : op_(op) { + inputs_[0] = value; + } + + LOperand* value() { return inputs_[0]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kUnarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LUnarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsUnarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(UnarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LBinarySIMDOperation final : public LTemplateInstruction<1, 2, 0> { + public: + LBinarySIMDOperation(LOperand* left, LOperand* right, BuiltinFunctionId op) + : op_(op) { + inputs_[0] = left; + inputs_[1] = right; + } + + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kBinarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LBinarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsBinarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(BinarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LTernarySIMDOperation final : public LTemplateInstruction<1, 3, 0> { + public: + LTernarySIMDOperation(LOperand* first, LOperand* second, LOperand* third, + BuiltinFunctionId op) + : op_(op) { + inputs_[0] = first; + inputs_[1] = second; + inputs_[2] = third; + } + + LOperand* first() { return inputs_[0]; } + LOperand* second() { return inputs_[1]; } + LOperand* third() { return inputs_[2]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kTernarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LTernarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsTernarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(TernarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LQuarternarySIMDOperation final : public LTemplateInstruction<1, 4, 0> { + public: + LQuarternarySIMDOperation(LOperand* x, LOperand* y, LOperand* z, LOperand* w, + BuiltinFunctionId op) + : op_(op) { + inputs_[0] = x; + inputs_[1] = y; + inputs_[2] = z; + inputs_[3] = w; + } + + LOperand* x() { return inputs_[0]; } + LOperand* y() { return inputs_[1]; } + LOperand* z() { return inputs_[2]; } + LOperand* w() { return inputs_[3]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { + return LInstruction::kQuarternarySIMDOperation; + } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LQuarternarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsQuarternarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(QuarternarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LQuinarySIMDOperation final : public LTemplateInstruction<1, 5, 0> { + public: + LQuinarySIMDOperation(LOperand* a0, LOperand* a1, LOperand* a2, LOperand* a3, + LOperand* a4, BuiltinFunctionId op) + : op_(op) { + inputs_[0] = a0; + inputs_[1] = a1; + inputs_[2] = a2; + inputs_[3] = a3; + inputs_[4] = a4; + } + + LOperand* a0() { return inputs_[0]; } + LOperand* a1() { return inputs_[1]; } + LOperand* a2() { return inputs_[2]; } + LOperand* a3() { return inputs_[3]; } + LOperand* a4() { return inputs_[4]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kQuinarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LQuinarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsQuinarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(QuinarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; + +class LSenarySIMDOperation final : public LTemplateInstruction<1, 6, 0> { + public: + LSenarySIMDOperation(LOperand* a0, LOperand* a1, LOperand* a2, LOperand* a3, + LOperand* a4, LOperand* a5, BuiltinFunctionId op) + : op_(op) { + inputs_[0] = a0; + inputs_[1] = a1; + inputs_[2] = a2; + inputs_[3] = a3; + inputs_[4] = a4; + inputs_[5] = a5; + } + + LOperand* a0() { return inputs_[0]; } + LOperand* a1() { return inputs_[1]; } + LOperand* a2() { return inputs_[2]; } + LOperand* a3() { return inputs_[3]; } + LOperand* a4() { return inputs_[4]; } + LOperand* a5() { return inputs_[5]; } + BuiltinFunctionId op() const { return op_; } + + Opcode opcode() const override { return LInstruction::kSenarySIMDOperation; } + void CompileToNative(LCodeGen* generator) override; + const char* Mnemonic() const override; + static LSenarySIMDOperation* cast(LInstruction* instr) { + DCHECK(instr->IsSenarySIMDOperation()); + return reinterpret_cast(instr); + } + + DECLARE_HYDROGEN_ACCESSOR(SenarySIMDOperation) + + private: + BuiltinFunctionId op_; +}; class LCmpObjectEqAndBranch final : public LControlInstruction<2, 0> { public: @@ -1533,6 +1740,13 @@ class LLoadRoot final : public LTemplateInstruction<1, 0, 0> { Heap::RootListIndex index() const { return hydrogen()->index(); } }; +inline static bool ExternalArrayOpRequiresPreScale( + Representation key_representation, ElementsKind kind) { + int shift_size = ElementsKindToShiftSize(kind); + return SmiValuesAre31Bits() && key_representation.IsSmi() + ? shift_size > static_cast(maximal_scale_factor) + kSmiTagSize + : shift_size > static_cast(maximal_scale_factor); +} inline static bool ExternalArrayOpRequiresTemp( Representation key_representation, @@ -1540,9 +1754,10 @@ inline static bool ExternalArrayOpRequiresTemp( // Operations that require the key to be divided by two to be converted into // an index cannot fold the scale operation into a load and need an extra // temp register to do the work. - return SmiValuesAre31Bits() && key_representation.IsSmi() && - (elements_kind == UINT8_ELEMENTS || elements_kind == INT8_ELEMENTS || - elements_kind == UINT8_CLAMPED_ELEMENTS); + return ExternalArrayOpRequiresPreScale(key_representation, elements_kind) || + (SmiValuesAre31Bits() && key_representation.IsSmi() && + (elements_kind == UINT8_ELEMENTS || elements_kind == INT8_ELEMENTS || + elements_kind == UINT8_CLAMPED_ELEMENTS)); } @@ -1899,6 +2114,24 @@ class LNumberTagD final : public LTemplateInstruction<1, 1, 1> { DECLARE_HYDROGEN_ACCESSOR(Change) }; +class LSIMD128ToTagged final : public LTemplateInstruction<1, 1, 3> { + public: + explicit LSIMD128ToTagged(LOperand* value, LOperand* temp, LOperand* temp2, + LOperand* temp3) { + inputs_[0] = value; + temps_[0] = temp; + temps_[1] = temp2; + temps_[2] = temp3; + } + + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + LOperand* temp2() { return temps_[1]; } + LOperand* temp3() { return temps_[2]; } + + DECLARE_CONCRETE_INSTRUCTION(SIMD128ToTagged, "simd128-tag") + DECLARE_HYDROGEN_ACCESSOR(Change) +}; // Sometimes truncating conversion from a tagged value to an int32. class LDoubleToI final : public LTemplateInstruction<1, 1, 0> { @@ -1972,6 +2205,25 @@ class LNumberUntagD final : public LTemplateInstruction<1, 1, 0> { DECLARE_HYDROGEN_ACCESSOR(Change); }; +class LTaggedToSIMD128 final : public LTemplateInstruction<1, 1, 1> { + public: + explicit LTaggedToSIMD128(LOperand* value, LOperand* temp, + Representation representation) + : representation_(representation) { + inputs_[0] = value; + temps_[0] = temp; + } + + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + Representation representation() const { return representation_; } + + DECLARE_CONCRETE_INSTRUCTION(TaggedToSIMD128, "simd128-untag") + DECLARE_HYDROGEN_ACCESSOR(Change); + + private: + Representation representation_; +}; class LSmiUntag final : public LTemplateInstruction<1, 1, 0> { public: diff --git a/src/deoptimize-reason.h b/src/deoptimize-reason.h index 60e0a59c5a1..76c4e57d6c0 100644 --- a/src/deoptimize-reason.h +++ b/src/deoptimize-reason.h @@ -48,6 +48,7 @@ namespace internal { V(NonStrictElementsInKeyedLoadGenericStub, \ "non-strict elements in KeyedLoadGenericStub") \ V(NotAHeapNumber, "not a heap number") \ + V(NotASIMD128, "not a simd128 value") \ V(NotAHeapNumberUndefinedBoolean, "not a heap number/undefined/true/false") \ V(NotAHeapNumberUndefined, "not a heap number/undefined") \ V(NotAJavaScriptObject, "not a JavaScript object") \ diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc index d4756ff1838..dfda60d8366 100644 --- a/src/deoptimizer.cc +++ b/src/deoptimizer.cc @@ -2047,6 +2047,9 @@ void Deoptimizer::DoComputeCompiledStubFrame(TranslatedFrame* translated_frame, // Copy the double registers from the input into the output frame. CopyDoubleRegisters(output_frame); + // Copy the simd128 registers from the input into the output frame. + CopySIMD128Registers(output_frame); + // Fill registers containing handler and number of parameters. SetPlatformCompiledStubRegisters(output_frame, &descriptor); @@ -2456,6 +2459,10 @@ void Translation::StoreDoubleRegister(DoubleRegister reg) { buffer_->Add(reg.code(), zone()); } +void Translation::StoreSIMD128Register(SIMD128Register reg, Opcode opcode) { + buffer_->Add(opcode, zone()); + buffer_->Add(reg.code(), zone()); +} void Translation::StoreStackSlot(int index) { buffer_->Add(STACK_SLOT, zone()); @@ -2490,6 +2497,10 @@ void Translation::StoreDoubleStackSlot(int index) { buffer_->Add(index, zone()); } +void Translation::StoreSIMD128StackSlot(int index, Opcode opcode) { + buffer_->Add(opcode, zone()); + buffer_->Add(index, zone()); +} void Translation::StoreLiteral(int literal_id) { buffer_->Add(LITERAL, zone()); @@ -2526,12 +2537,18 @@ int Translation::NumberOfOperandsFor(Opcode opcode) { case BOOL_REGISTER: case FLOAT_REGISTER: case DOUBLE_REGISTER: + case FLOAT32x4_REGISTER: + case INT32x4_REGISTER: + case BOOL32x4_REGISTER: case STACK_SLOT: case INT32_STACK_SLOT: case UINT32_STACK_SLOT: case BOOL_STACK_SLOT: case FLOAT_STACK_SLOT: case DOUBLE_STACK_SLOT: + case FLOAT32x4_STACK_SLOT: + case INT32x4_STACK_SLOT: + case BOOL32x4_STACK_SLOT: case LITERAL: case COMPILED_STUB_FRAME: case TAIL_CALLER_FRAME: @@ -2839,6 +2856,30 @@ TranslatedValue TranslatedValue::NewDouble(TranslatedState* container, } +// static +TranslatedValue TranslatedValue::NewFloat32x4(TranslatedState* container, + float32x4_value_t value) { + TranslatedValue slot(container, kFloat32x4); + slot.float32x4_value_ = value; + return slot; +} + +// static +TranslatedValue TranslatedValue::NewInt32x4(TranslatedState* container, + int32x4_value_t value) { + TranslatedValue slot(container, kInt32x4); + slot.int32x4_value_ = value; + return slot; +} + +// static +TranslatedValue TranslatedValue::NewBool32x4(TranslatedState* container, + bool32x4_value_t value) { + TranslatedValue slot(container, kBool32x4); + slot.bool32x4_value_ = value; + return slot; +} + // static TranslatedValue TranslatedValue::NewInt32(TranslatedState* container, int32_t value) { @@ -2911,6 +2952,20 @@ double TranslatedValue::double_value() const { return double_value_; } +float32x4_value_t TranslatedValue::float32x4_value() const { + DCHECK_EQ(kFloat32x4, kind()); + return float32x4_value_; +} + +int32x4_value_t TranslatedValue::int32x4_value() const { + DCHECK_EQ(kInt32x4, kind()); + return int32x4_value_; +} + +bool32x4_value_t TranslatedValue::bool32x4_value() const { + DCHECK_EQ(kBool32x4, kind()); + return bool32x4_value_; +} int TranslatedValue::object_length() const { DCHECK(kind() == kArgumentsObject || kind() == kCapturedObject); @@ -2984,7 +3039,10 @@ Handle TranslatedValue::GetValue() { case TranslatedValue::kUInt32: case TranslatedValue::kBoolBit: case TranslatedValue::kFloat: - case TranslatedValue::kDouble: { + case TranslatedValue::kDouble: + case TranslatedValue::kFloat32x4: + case TranslatedValue::kInt32x4: + case TranslatedValue::kBool32x4: { MaterializeSimple(); return value_.ToHandleChecked(); } @@ -3033,6 +3091,28 @@ void TranslatedValue::MaterializeSimple() { value_ = Handle(isolate()->factory()->NewNumber(double_value())); return; + case kFloat32x4: + value_ = Handle( + isolate()->factory()->NewFloat32x4(float32x4_value().storage)); + return; + case kInt32x4: + value_ = Handle( + isolate()->factory()->NewInt32x4(int32x4_value().storage)); + return; + case kBool32x4: + bool input[4]; + for (int i = 0; i < 4; i++) { + switch (bool32x4_value().storage[i]) { + case 0: + input[i] = false; + break; + case -1: + input[i] = true; + break; + } + } + value_ = Handle(isolate()->factory()->NewBool32x4(input)); + return; case kCapturedObject: case kDuplicatedObject: case kArgumentsObject: @@ -3300,12 +3380,18 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame( case Translation::BOOL_REGISTER: case Translation::FLOAT_REGISTER: case Translation::DOUBLE_REGISTER: + case Translation::FLOAT32x4_REGISTER: + case Translation::INT32x4_REGISTER: + case Translation::BOOL32x4_REGISTER: case Translation::STACK_SLOT: case Translation::INT32_STACK_SLOT: case Translation::UINT32_STACK_SLOT: case Translation::BOOL_STACK_SLOT: case Translation::FLOAT_STACK_SLOT: case Translation::DOUBLE_STACK_SLOT: + case Translation::FLOAT32x4_STACK_SLOT: + case Translation::INT32x4_STACK_SLOT: + case Translation::BOOL32x4_STACK_SLOT: case Translation::LITERAL: break; } @@ -3455,6 +3541,58 @@ TranslatedValue TranslatedState::CreateNextTranslatedValue( return TranslatedValue::NewDouble(this, value); } + case Translation::FLOAT32x4_REGISTER: + case Translation::BOOL32x4_REGISTER: + case Translation::INT32x4_REGISTER: { + int input_reg = iterator->Next(); + if (registers == nullptr) return TranslatedValue::NewInvalid(this); + simd128_value_t value = registers->GetSIMD128Register(input_reg); + if (trace_file != nullptr) { + if (opcode == Translation::FLOAT32x4_REGISTER) { + float32x4_value_t x4 = value.f4; + PrintF(trace_file, "float32x4(%e, %e, %e, %e) ; %s\n", x4.storage[0], + x4.storage[1], x4.storage[2], x4.storage[3], +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + RegisterConfiguration::Crankshaft()->GetSimd128RegisterName( + input_reg)); +#else + "Target hasn't no method toString()"); +#endif + } else if (opcode == Translation::BOOL32x4_REGISTER) { + bool32x4_value_t x4 = value.b4; + PrintF(trace_file, "bool32x4(%u, %u, %u, %u) ; %s\n", x4.storage[0], + x4.storage[1], x4.storage[2], x4.storage[3], +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + RegisterConfiguration::Crankshaft()->GetSimd128RegisterName( + input_reg)); +#else + "Target hasn't no method toString()"); +#endif + } else { + DCHECK(opcode == Translation::INT32x4_REGISTER); + int32x4_value_t x4 = value.i4; + PrintF(trace_file, "int32x4(%u, %u, %u, %u) ; %s\n", x4.storage[0], + x4.storage[1], x4.storage[2], x4.storage[3], +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + RegisterConfiguration::Crankshaft()->GetSimd128RegisterName( + input_reg)); +#else + "Target hasn't no method toString()"); +#endif + } + } + if (opcode == Translation::FLOAT32x4_REGISTER) { + float32x4_value_t x4 = value.f4; + return TranslatedValue::NewFloat32x4(this, x4); + } else if (opcode == Translation::BOOL32x4_REGISTER) { + bool32x4_value_t x4 = value.b4; + return TranslatedValue::NewBool32x4(this, x4); + } else { + int32x4_value_t x4 = value.i4; + return TranslatedValue::NewInt32x4(this, x4); + } + } + case Translation::STACK_SLOT: { int slot_offset = OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next()); @@ -3523,6 +3661,43 @@ TranslatedValue TranslatedState::CreateNextTranslatedValue( return TranslatedValue::NewDouble(this, value); } + case Translation::FLOAT32x4_STACK_SLOT: + case Translation::BOOL32x4_STACK_SLOT: + case Translation::INT32x4_STACK_SLOT: { + int slot_offset = + OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next()); + simd128_value_t value = read_simd128_value(fp, slot_offset); + if (trace_file != nullptr) { + if (opcode == Translation::FLOAT32x4_STACK_SLOT) { + float32x4_value_t x4 = value.f4; + PrintF(trace_file, "float32x4(%e, %e, %e, %e) ; [sp + %d]\n", + x4.storage[0], x4.storage[1], x4.storage[2], x4.storage[3], + slot_offset); + } else if (opcode == Translation::BOOL32x4_STACK_SLOT) { + bool32x4_value_t x4 = value.b4; + PrintF(trace_file, "bool32x4(%u, %u, %u, %u) ; [sp + %d]\n", + x4.storage[0], x4.storage[1], x4.storage[2], x4.storage[3], + slot_offset); + } else { + DCHECK(opcode == Translation::INT32x4_STACK_SLOT); + int32x4_value_t x4 = value.i4; + PrintF(trace_file, "int32x4(%u, %u, %u, %u) ; [sp + %d]\n", + x4.storage[0], x4.storage[1], x4.storage[2], x4.storage[3], + slot_offset); + } + } + if (opcode == Translation::FLOAT32x4_STACK_SLOT) { + float32x4_value_t x4 = value.f4; + return TranslatedValue::NewFloat32x4(this, x4); + } else if (opcode == Translation::BOOL32x4_STACK_SLOT) { + bool32x4_value_t x4 = value.b4; + return TranslatedValue::NewBool32x4(this, x4); + } else { + int32x4_value_t x4 = value.i4; + return TranslatedValue::NewInt32x4(this, x4); + } + } + case Translation::LITERAL: { int literal_index = iterator->Next(); Object* value = literal_array->get(literal_index); @@ -3660,7 +3835,10 @@ Handle TranslatedState::MaterializeAt(int frame_index, case TranslatedValue::kUInt32: case TranslatedValue::kBoolBit: case TranslatedValue::kFloat: - case TranslatedValue::kDouble: { + case TranslatedValue::kDouble: + case TranslatedValue::kFloat32x4: + case TranslatedValue::kBool32x4: + case TranslatedValue::kInt32x4: { slot->MaterializeSimple(); Handle value = slot->GetValue(); if (value->IsMutableHeapNumber()) { diff --git a/src/deoptimizer.h b/src/deoptimizer.h index 7822d1cf508..338e115de3d 100644 --- a/src/deoptimizer.h +++ b/src/deoptimizer.h @@ -19,6 +19,11 @@ class DeoptimizedFrameInfo; class TranslatedState; class RegisterValues; +static inline simd128_value_t read_simd128_value(Address p, int slot_offset) { + Address address = p + slot_offset; + return *reinterpret_cast(address); +} + class TranslatedValue { public: // Allocation-less getter of the value. @@ -42,6 +47,9 @@ class TranslatedValue { kBoolBit, kFloat, kDouble, + kFloat32x4, + kInt32x4, + kBool32x4, kCapturedObject, // Object captured by the escape analysis. // The number of nested objects can be obtained // with the DeferredObjectLength() method @@ -68,6 +76,12 @@ class TranslatedValue { static TranslatedValue NewInt32(TranslatedState* container, int32_t value); static TranslatedValue NewUInt32(TranslatedState* container, uint32_t value); static TranslatedValue NewBool(TranslatedState* container, uint32_t value); + static TranslatedValue NewFloat32x4(TranslatedState* container, + float32x4_value_t value); + static TranslatedValue NewInt32x4(TranslatedState* container, + int32x4_value_t value); + static TranslatedValue NewBool32x4(TranslatedState* container, + bool32x4_value_t value); static TranslatedValue NewTagged(TranslatedState* container, Object* literal); static TranslatedValue NewInvalid(TranslatedState* container); @@ -100,6 +114,12 @@ class TranslatedValue { float float_value_; // kind is kDouble double double_value_; + // Kind is kFloat32x4 + float32x4_value_t float32x4_value_; + // Kind is kBool32x4 + bool32x4_value_t bool32x4_value_; + // Kind is kInt32x4 + int32x4_value_t int32x4_value_; // kind is kDuplicatedObject or kArgumentsObject or kCapturedObject. MaterializedObjectInfo materialization_info_; }; @@ -110,6 +130,9 @@ class TranslatedValue { uint32_t uint32_value() const; float float_value() const; double double_value() const; + float32x4_value_t float32x4_value() const; + int32x4_value_t int32x4_value() const; + bool32x4_value_t bool32x4_value() const; int object_length() const; int object_index() const; }; @@ -592,6 +615,10 @@ class Deoptimizer : public Malloced { // from the input frame's double registers. void CopyDoubleRegisters(FrameDescription* output_frame); + // Fill the given output frame's simd128 registers with the original values + // from the input frame's simd128 registers. + void CopySIMD128Registers(FrameDescription* output_frame); + Isolate* isolate_; JSFunction* function_; Code* compiled_code_; @@ -662,10 +689,9 @@ class RegisterValues { return float_registers_[n]; } - double GetDoubleRegister(unsigned n) const { - DCHECK(n < arraysize(double_registers_)); - return double_registers_[n]; - } + double GetDoubleRegister(unsigned n) const; + + simd128_value_t GetSIMD128Register(unsigned n) const; void SetRegister(unsigned n, intptr_t value) { DCHECK(n < arraysize(registers_)); @@ -677,14 +703,16 @@ class RegisterValues { float_registers_[n] = value; } - void SetDoubleRegister(unsigned n, double value) { - DCHECK(n < arraysize(double_registers_)); - double_registers_[n] = value; - } + void SetDoubleRegister(unsigned n, double value); + + void SetSIMD128Register(unsigned n, simd128_value_t value); intptr_t registers_[Register::kNumRegisters]; float float_registers_[FloatRegister::kMaxNumRegisters]; double double_registers_[DoubleRegister::kMaxNumRegisters]; +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM + simd128_value_t simd128_registers_[SIMD128Register::kMaxNumRegisters]; +#endif }; @@ -743,6 +771,10 @@ class FrameDescription { return register_values_.GetDoubleRegister(n); } + simd128_value_t GetSIMD128Register(unsigned n) const { + return register_values_.GetSIMD128Register(n); + } + void SetRegister(unsigned n, intptr_t value) { register_values_.SetRegister(n, value); } @@ -751,6 +783,10 @@ class FrameDescription { register_values_.SetDoubleRegister(n, value); } + void SetSIMD128Register(unsigned n, simd128_value_t value) { + register_values_.SetSIMD128Register(n, value); + } + intptr_t GetTop() const { return top_; } void SetTop(intptr_t top) { top_ = top; } @@ -783,9 +819,9 @@ class FrameDescription { return OFFSET_OF(FrameDescription, register_values_.registers_); } - static int double_registers_offset() { - return OFFSET_OF(FrameDescription, register_values_.double_registers_); - } + static int double_registers_offset(); + + static int simd128_registers_offset(); static int frame_size_offset() { return offsetof(FrameDescription, frame_size_); @@ -909,12 +945,18 @@ class TranslationIterator BASE_EMBEDDED { V(BOOL_REGISTER) \ V(FLOAT_REGISTER) \ V(DOUBLE_REGISTER) \ + V(FLOAT32x4_REGISTER) \ + V(BOOL32x4_REGISTER) \ + V(INT32x4_REGISTER) \ V(STACK_SLOT) \ V(INT32_STACK_SLOT) \ V(UINT32_STACK_SLOT) \ V(BOOL_STACK_SLOT) \ V(FLOAT_STACK_SLOT) \ V(DOUBLE_STACK_SLOT) \ + V(FLOAT32x4_STACK_SLOT) \ + V(BOOL32x4_STACK_SLOT) \ + V(INT32x4_STACK_SLOT) \ V(LITERAL) class Translation BASE_EMBEDDED { @@ -957,12 +999,14 @@ class Translation BASE_EMBEDDED { void StoreBoolRegister(Register reg); void StoreFloatRegister(FloatRegister reg); void StoreDoubleRegister(DoubleRegister reg); + void StoreSIMD128Register(SIMD128Register reg, Opcode opcode); void StoreStackSlot(int index); void StoreInt32StackSlot(int index); void StoreUint32StackSlot(int index); void StoreBoolStackSlot(int index); void StoreFloatStackSlot(int index); void StoreDoubleStackSlot(int index); + void StoreSIMD128StackSlot(int index, Opcode opcode); void StoreLiteral(int literal_id); void StoreArgumentsObject(bool args_known, int args_index, int args_length); void StoreJSFrameFunction(); diff --git a/src/elements-kind.h b/src/elements-kind.h index 3ebc9ad287c..321ec376a3c 100644 --- a/src/elements-kind.h +++ b/src/elements-kind.h @@ -137,6 +137,7 @@ inline bool IsDoubleOrFloatElementsKind(ElementsKind kind) { return IsFastDoubleElementsKind(kind) || IsFixedFloatElementsKind(kind); } +inline bool IsSIMD128ElementsKind(ElementsKind kind) { return false; } inline bool IsFastSmiOrObjectElementsKind(ElementsKind kind) { return kind == FAST_SMI_ELEMENTS || diff --git a/src/globals.h b/src/globals.h index 0d02f77fd63..53d8ccf4e84 100644 --- a/src/globals.h +++ b/src/globals.h @@ -98,6 +98,22 @@ typedef byte* Address; // ----------------------------------------------------------------------------- // Constants +struct float32x4_value_t { + float storage[4]; +}; +struct int32x4_value_t { + int32_t storage[4]; +}; +struct bool32x4_value_t { + int32_t storage[4]; +}; +union simd128_value_t { + double d[2]; + float32x4_value_t f4; + int32x4_value_t i4; + bool32x4_value_t b4; +}; + const int KB = 1024; const int MB = KB * KB; const int GB = KB * KB * KB; @@ -115,15 +131,20 @@ const int kMinUInt16 = 0; const uint32_t kMaxUInt32 = 0xFFFFFFFFu; const int kMinUInt32 = 0; -const int kCharSize = sizeof(char); // NOLINT -const int kShortSize = sizeof(short); // NOLINT -const int kIntSize = sizeof(int); // NOLINT -const int kInt32Size = sizeof(int32_t); // NOLINT -const int kInt64Size = sizeof(int64_t); // NOLINT -const int kFloatSize = sizeof(float); // NOLINT -const int kDoubleSize = sizeof(double); // NOLINT -const int kIntptrSize = sizeof(intptr_t); // NOLINT -const int kPointerSize = sizeof(void*); // NOLINT +const int kCharSize = sizeof(char); // NOLINT +const int kShortSize = sizeof(short); // NOLINT +const int kIntSize = sizeof(int); // NOLINT +const int kInt32Size = sizeof(int32_t); // NOLINT +const int kBool32Size = sizeof(int32_t); // NOLINT +const int kInt64Size = sizeof(int64_t); // NOLINT +const int kDoubleSize = sizeof(double); // NOLINT +const int kFloatSize = sizeof(float); // NOLINT +const int kFloat32x4Size = sizeof(float32x4_value_t); // NOLINT +const int kBool32x4Size = sizeof(bool32x4_value_t); // NOLINT +const int kInt32x4Size = sizeof(int32x4_value_t); // NOLINT +const int kSIMD128Size = sizeof(simd128_value_t); // NOLINT +const int kIntptrSize = sizeof(intptr_t); // NOLINT +const int kPointerSize = sizeof(void*); // NOLINT #if V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT const int kRegisterSize = kPointerSize + kPointerSize; #else diff --git a/src/ia32/assembler-ia32-inl.h b/src/ia32/assembler-ia32-inl.h index 281c3ef9321..38335be9ea4 100644 --- a/src/ia32/assembler-ia32-inl.h +++ b/src/ia32/assembler-ia32-inl.h @@ -48,6 +48,7 @@ namespace internal { bool CpuFeatures::SupportsCrankshaft() { return true; } bool CpuFeatures::SupportsSimd128() { return false; } +bool CpuFeatures::SupportsSIMD128InCrankshaft() { return true; } static const byte kCallOpcode = 0xE8; static const int kNoCodeAgeSequenceLength = 5; diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index 4d3195957e6..f0d67575479 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -270,6 +270,50 @@ Operand::Operand(Register index, set_dispr(disp, rmode); } +Operand::Operand(const Operand& operand, int32_t offset) { + DCHECK(operand.len_ >= 1); + // Operand encodes REX ModR/M [SIB] [Disp]. + byte modrm = operand.buf_[0]; + DCHECK(modrm < 0xC0); // Disallow mode 3 (register target). + bool has_sib = ((modrm & 0x07) == 0x04); + byte mode = modrm & 0xC0; + int disp_offset = has_sib ? 2 : 1; + int base_reg = (has_sib ? operand.buf_[1] : modrm) & 0x07; + // Mode 0 with rbp/r13 as ModR/M or SIB base register always has a 32-bit + // displacement. + bool is_baseless = (mode == 0) && (base_reg == 0x05); // No base or RIP base. + int32_t disp_value = 0; + if (mode == 0x80 || is_baseless) { + // Mode 2 or mode 0 with rbp/r13 as base: Word displacement. + disp_value = *bit_cast(&operand.buf_[disp_offset]); + } else if (mode == 0x40) { + // Mode 1: Byte displacement. + disp_value = static_cast(operand.buf_[disp_offset]); + } + + // Write new operand with same registers, but with modified displacement. + DCHECK(offset >= 0 ? disp_value + offset >= disp_value + : disp_value + offset < disp_value); // No overflow. + disp_value += offset; + if (!is_int8(disp_value) || is_baseless) { + // Need 32 bits of displacement, mode 2 or mode 1 with register rbp/r13. + buf_[0] = (modrm & 0x3f) | (is_baseless ? 0x00 : 0x80); + len_ = disp_offset + 4; + Memory::int32_at(&buf_[disp_offset]) = disp_value; + } else if (disp_value != 0 || (base_reg == 0x05)) { + // Need 8 bits of displacement. + buf_[0] = (modrm & 0x3f) | 0x40; // Mode 1. + len_ = disp_offset + 1; + buf_[disp_offset] = static_cast(disp_value); + } else { + // Need no displacement. + buf_[0] = (modrm & 0x3f); // Mode 0. + len_ = disp_offset; + } + if (has_sib) { + buf_[1] = operand.buf_[1]; + } +} bool Operand::is_reg(Register reg) const { return ((buf_[0] & 0xF8) == 0xC0) // addressing mode is register only. @@ -2194,6 +2238,13 @@ void Assembler::xorpd(XMMRegister dst, XMMRegister src) { emit_sse_operand(dst, src); } +void Assembler::xorpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x57); + emit_sse_operand(dst, src); +} void Assembler::andps(XMMRegister dst, const Operand& src) { EnsureSpace ensure_space(this); @@ -2250,6 +2301,37 @@ void Assembler::divps(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::addpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x58); + emit_sse_operand(dst, src); +} + +void Assembler::subpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x5C); + emit_sse_operand(dst, src); +} + +void Assembler::mulpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x59); + emit_sse_operand(dst, src); +} + +void Assembler::divpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x5E); + emit_sse_operand(dst, src); +} void Assembler::sqrtsd(XMMRegister dst, const Operand& src) { EnsureSpace ensure_space(this); @@ -2268,6 +2350,13 @@ void Assembler::andpd(XMMRegister dst, XMMRegister src) { emit_sse_operand(dst, src); } +void Assembler::andpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x54); + emit_sse_operand(dst, src); +} void Assembler::orpd(XMMRegister dst, XMMRegister src) { EnsureSpace ensure_space(this); @@ -2374,6 +2463,13 @@ void Assembler::minsd(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::pcmpgtd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x66); + emit_sse_operand(dst, src); +} void Assembler::cmpltsd(XMMRegister dst, XMMRegister src) { EnsureSpace ensure_space(this); @@ -2399,6 +2495,20 @@ void Assembler::movups(XMMRegister dst, XMMRegister src) { emit_sse_operand(dst, src); } +void Assembler::movlhps(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0x16); + emit_sse_operand(dst, src); +} + +void Assembler::movhlps(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0x12); + emit_sse_operand(dst, src); +} + void Assembler::movups(XMMRegister dst, const Operand& src) { EnsureSpace ensure_space(this); EMIT(0x0F); @@ -2422,6 +2532,15 @@ void Assembler::shufps(XMMRegister dst, XMMRegister src, byte imm8) { EMIT(imm8); } +void Assembler::shufpd(XMMRegister dst, XMMRegister src, byte imm8) { + DCHECK(is_uint8(imm8)); + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0xC6); + emit_sse_operand(dst, src); + EMIT(imm8); +} void Assembler::movdqa(const Operand& dst, XMMRegister src) { EnsureSpace ensure_space(this); @@ -2505,6 +2624,21 @@ void Assembler::movss(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::movq(const Operand& dst, XMMRegister src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0xD6); // store + emit_sse_operand(src, dst); +} + +void Assembler::movq(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0xF3); + EMIT(0x0F); + EMIT(0x7E); // load + emit_sse_operand(dst, src); +} void Assembler::movd(XMMRegister dst, const Operand& src) { EnsureSpace ensure_space(this); @@ -2613,6 +2747,38 @@ void Assembler::psllq(XMMRegister dst, XMMRegister src) { emit_sse_operand(dst, src); } +void Assembler::pslld(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0xF2); + emit_sse_operand(dst, src); +} + +void Assembler::psrld(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0xD2); + emit_sse_operand(dst, src); +} + +void Assembler::psrad(XMMRegister reg, int8_t shift) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x72); + emit_sse_operand(esp, reg); // esp == 4 + EMIT(shift); +} + +void Assembler::psrad(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0xE2); + emit_sse_operand(dst, src); +} void Assembler::psrlq(XMMRegister reg, int8_t shift) { EnsureSpace ensure_space(this); @@ -2632,6 +2798,14 @@ void Assembler::psrlq(XMMRegister dst, XMMRegister src) { emit_sse_operand(dst, src); } +void Assembler::psrldq(XMMRegister dst, int8_t shift) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x73); + emit_sse_operand(ebx, dst); // ebx == 3 + EMIT(shift); +} void Assembler::pshufd(XMMRegister dst, XMMRegister src, uint8_t shuffle) { EnsureSpace ensure_space(this); @@ -2675,6 +2849,12 @@ void Assembler::addss(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::minps(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0x5D); + emit_sse_operand(dst, src); +} void Assembler::subss(XMMRegister dst, const Operand& src) { EnsureSpace ensure_space(this); @@ -2684,6 +2864,12 @@ void Assembler::subss(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::maxps(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0x5F); + emit_sse_operand(dst, src); +} void Assembler::mulss(XMMRegister dst, const Operand& src) { EnsureSpace ensure_space(this); @@ -2693,6 +2879,13 @@ void Assembler::mulss(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::minpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x5D); + emit_sse_operand(dst, src); +} void Assembler::divss(XMMRegister dst, const Operand& src) { EnsureSpace ensure_space(this); @@ -2711,6 +2904,13 @@ void Assembler::sqrtss(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::maxpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x5F); + emit_sse_operand(dst, src); +} void Assembler::ucomiss(XMMRegister dst, const Operand& src) { EnsureSpace ensure_space(this); @@ -2728,6 +2928,12 @@ void Assembler::maxss(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::rcpps(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0x53); + emit_sse_operand(dst, src); +} void Assembler::minss(XMMRegister dst, const Operand& src) { EnsureSpace ensure_space(this); @@ -2737,6 +2943,42 @@ void Assembler::minss(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::rsqrtps(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0x52); + emit_sse_operand(dst, src); +} + +void Assembler::sqrtps(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0x51); + emit_sse_operand(dst, src); +} + +void Assembler::sqrtpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x51); + emit_sse_operand(dst, src); +} + +void Assembler::cvtdq2ps(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0x5B); + emit_sse_operand(dst, src); +} + +void Assembler::paddd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0xFE); + emit_sse_operand(dst, src); +} // AVX instructions void Assembler::vfmasd(byte op, XMMRegister dst, XMMRegister src1, @@ -2859,6 +3101,91 @@ void Assembler::rorx(Register dst, const Operand& src, byte imm8) { EMIT(imm8); } +void Assembler::psubd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0xFA); + emit_sse_operand(dst, src); +} + +void Assembler::pmulld(XMMRegister dst, const Operand& src) { + DCHECK(IsEnabled(SSE4_1)); + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x38); + EMIT(0x40); + emit_sse_operand(dst, src); +} + +void Assembler::pmuludq(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0xF4); + emit_sse_operand(dst, src); +} + +void Assembler::punpackldq(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x62); + emit_sse_operand(dst, src); +} + +void Assembler::cvtps2dq(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x5B); + emit_sse_operand(dst, src); +} + +void Assembler::cmpps(XMMRegister dst, XMMRegister src, int8_t cmp) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xC2); + emit_sse_operand(dst, src); + EMIT(cmp); +} + +void Assembler::cmpeqps(XMMRegister dst, XMMRegister src) { + cmpps(dst, src, 0x0); +} + +void Assembler::cmpltps(XMMRegister dst, XMMRegister src) { + cmpps(dst, src, 0x1); +} + +void Assembler::cmpleps(XMMRegister dst, XMMRegister src) { + cmpps(dst, src, 0x2); +} + +void Assembler::cmpneqps(XMMRegister dst, XMMRegister src) { + cmpps(dst, src, 0x4); +} + +void Assembler::cmpnltps(XMMRegister dst, XMMRegister src) { + cmpps(dst, src, 0x5); +} + +void Assembler::cmpnleps(XMMRegister dst, XMMRegister src) { + cmpps(dst, src, 0x6); +} + +void Assembler::insertps(XMMRegister dst, XMMRegister src, byte imm8) { + DCHECK(CpuFeatures::IsSupported(SSE4_1)); + DCHECK(is_uint8(imm8)); + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0x3A); + EMIT(0x21); + emit_sse_operand(dst, src); + EMIT(imm8); +} void Assembler::emit_sse_operand(XMMRegister reg, const Operand& adr) { Register ireg = { reg.code() }; diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index a1dc4b62be3..16421760d8e 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -189,37 +189,38 @@ DOUBLE_REGISTERS(DECLARE_REGISTER) #undef DECLARE_REGISTER const DoubleRegister no_double_reg = {DoubleRegister::kCode_no_reg}; +typedef DoubleRegister SIMD128Register; + enum Condition { // any value < 0 is considered no_condition - no_condition = -1, - - overflow = 0, - no_overflow = 1, - below = 2, - above_equal = 3, - equal = 4, - not_equal = 5, - below_equal = 6, - above = 7, - negative = 8, - positive = 9, - parity_even = 10, - parity_odd = 11, - less = 12, + no_condition = -1, + + overflow = 0, + no_overflow = 1, + below = 2, + above_equal = 3, + equal = 4, + not_equal = 5, + below_equal = 6, + above = 7, + negative = 8, + positive = 9, + parity_even = 10, + parity_odd = 11, + less = 12, greater_equal = 13, - less_equal = 14, - greater = 15, - + less_equal = 14, + greater = 15, + never = 17, // aliases - carry = below, - not_carry = above_equal, - zero = equal, - not_zero = not_equal, - sign = negative, - not_sign = positive + carry = below, + not_carry = above_equal, + zero = equal, + not_zero = not_equal, + sign = negative, + not_sign = positive }; - // Returns the equivalent of !cc. // Negation of the default no_condition (-1) results in a non-default // no_condition value (-2). As long as tests for no_condition check @@ -312,13 +313,13 @@ enum ScaleFactor { times_2 = 1, times_4 = 2, times_8 = 3, + maximal_scale_factor = times_8, times_int_size = times_4, times_half_pointer_size = times_2, times_pointer_size = times_4, times_twice_pointer_size = times_8 }; - class Operand BASE_EMBEDDED { public: // reg @@ -355,6 +356,11 @@ class Operand BASE_EMBEDDED { RelocInfo::INTERNAL_REFERENCE); } + // Offset from existing memory operand. + // Offset is added to existing displacement as 32-bit signed values and + // this must not overflow. + Operand(const Operand& base, int32_t offset); + static Operand StaticVariable(const ExternalReference& ext) { return Operand(reinterpret_cast(ext.address()), RelocInfo::EXTERNAL_REFERENCE); @@ -960,9 +966,12 @@ class Assembler : public AssemblerBase { void ucomiss(XMMRegister dst, const Operand& src); void movaps(XMMRegister dst, XMMRegister src); void movups(XMMRegister dst, XMMRegister src); + void movlhps(XMMRegister dst, XMMRegister src); + void movhlps(XMMRegister dst, XMMRegister src); void movups(XMMRegister dst, const Operand& src); void movups(const Operand& dst, XMMRegister src); void shufps(XMMRegister dst, XMMRegister src, byte imm8); + void shufpd(XMMRegister dst, XMMRegister src, byte imm8); void maxss(XMMRegister dst, XMMRegister src) { maxss(dst, Operand(src)); } void maxss(XMMRegister dst, const Operand& src); @@ -984,6 +993,63 @@ class Assembler : public AssemblerBase { void mulps(XMMRegister dst, XMMRegister src) { mulps(dst, Operand(src)); } void divps(XMMRegister dst, const Operand& src); void divps(XMMRegister dst, XMMRegister src) { divps(dst, Operand(src)); } + void minps(XMMRegister dst, XMMRegister src) { minps(dst, Operand(src)); } + void minps(XMMRegister dst, const Operand& src); + void maxps(XMMRegister dst, XMMRegister src) { maxps(dst, Operand(src)); } + void maxps(XMMRegister dst, const Operand& src); + void rcpps(XMMRegister dst, XMMRegister src) { rcpps(dst, Operand(src)); } + void rcpps(XMMRegister dst, const Operand& src); + void rsqrtps(XMMRegister dst, XMMRegister src) { rsqrtps(dst, Operand(src)); } + void rsqrtps(XMMRegister dst, const Operand& src); + void sqrtps(XMMRegister dst, XMMRegister src) { sqrtps(dst, Operand(src)); } + void sqrtps(XMMRegister dst, const Operand& src); + void sqrtpd(XMMRegister dst, XMMRegister src) { sqrtpd(dst, Operand(src)); } + void sqrtpd(XMMRegister dst, const Operand& src); + + void addpd(XMMRegister dst, const Operand& src); + void addpd(XMMRegister dst, XMMRegister src) { addpd(dst, Operand(src)); } + void subpd(XMMRegister dst, const Operand& src); + void subpd(XMMRegister dst, XMMRegister src) { subpd(dst, Operand(src)); } + void mulpd(XMMRegister dst, const Operand& src); + void mulpd(XMMRegister dst, XMMRegister src) { mulpd(dst, Operand(src)); } + void divpd(XMMRegister dst, const Operand& src); + void divpd(XMMRegister dst, XMMRegister src) { divpd(dst, Operand(src)); } + void minpd(XMMRegister dst, XMMRegister src) { minpd(dst, Operand(src)); } + void minpd(XMMRegister dst, const Operand& src); + void maxpd(XMMRegister dst, XMMRegister src) { maxpd(dst, Operand(src)); } + void maxpd(XMMRegister dst, const Operand& src); + + void cvtdq2ps(XMMRegister dst, const Operand& src); + void cmpps(XMMRegister dst, XMMRegister src, int8_t cmp); + void cmpeqps(XMMRegister dst, XMMRegister src); + void cmpltps(XMMRegister dst, XMMRegister src); + void cmpleps(XMMRegister dst, XMMRegister src); + void cmpneqps(XMMRegister dst, XMMRegister src); + void cmpnltps(XMMRegister dst, XMMRegister src); + void cmpnleps(XMMRegister dst, XMMRegister src); + + // SSE 2, introduced by SIMD + void paddd(XMMRegister dst, XMMRegister src) { paddd(dst, Operand(src)); } + void paddd(XMMRegister dst, const Operand& src); + void psubd(XMMRegister dst, XMMRegister src) { psubd(dst, Operand(src)); } + void psubd(XMMRegister dst, const Operand& src); + void pmuludq(XMMRegister dst, XMMRegister src) { pmuludq(dst, Operand(src)); } + void pmuludq(XMMRegister dst, const Operand& src); + void punpackldq(XMMRegister dst, XMMRegister src) { + punpackldq(dst, Operand(src)); + } + void punpackldq(XMMRegister dst, const Operand& src); + void cvtps2dq(XMMRegister dst, XMMRegister src) { + cvtps2dq(dst, Operand(src)); + } + void cvtps2dq(XMMRegister dst, const Operand& src); + void cvtdq2ps(XMMRegister dst, XMMRegister src) { + cvtdq2ps(dst, Operand(src)); + } + // SSE 4.1, introduced by SIMD + void insertps(XMMRegister dst, XMMRegister src, byte imm8); + void pmulld(XMMRegister dst, XMMRegister src) { pmulld(dst, Operand(src)); } + void pmulld(XMMRegister dst, const Operand& src); // SSE2 instructions void cvttss2si(Register dst, const Operand& src); @@ -1017,10 +1083,12 @@ class Assembler : public AssemblerBase { void divsd(XMMRegister dst, XMMRegister src) { divsd(dst, Operand(src)); } void divsd(XMMRegister dst, const Operand& src); void xorpd(XMMRegister dst, XMMRegister src); + void xorpd(XMMRegister dst, const Operand& src); void sqrtsd(XMMRegister dst, XMMRegister src) { sqrtsd(dst, Operand(src)); } void sqrtsd(XMMRegister dst, const Operand& src); void andpd(XMMRegister dst, XMMRegister src); + void andpd(XMMRegister dst, const Operand& src); void orpd(XMMRegister dst, XMMRegister src); void ucomisd(XMMRegister dst, XMMRegister src) { ucomisd(dst, Operand(src)); } @@ -1034,6 +1102,7 @@ class Assembler : public AssemblerBase { void cmpltsd(XMMRegister dst, XMMRegister src); void pcmpeqd(XMMRegister dst, XMMRegister src); + void pcmpgtd(XMMRegister dst, XMMRegister src); void punpckldq(XMMRegister dst, XMMRegister src); void punpckhdq(XMMRegister dst, XMMRegister src); @@ -1067,6 +1136,8 @@ class Assembler : public AssemblerBase { void movss(XMMRegister dst, const Operand& src); void movss(const Operand& dst, XMMRegister src); void movss(XMMRegister dst, XMMRegister src) { movss(dst, Operand(src)); } + void movq(XMMRegister dst, const Operand& src); + void movq(const Operand& dst, XMMRegister src); void extractps(Register dst, XMMRegister src, byte imm8); void pand(XMMRegister dst, XMMRegister src); @@ -1078,8 +1149,13 @@ class Assembler : public AssemblerBase { void psrld(XMMRegister reg, int8_t shift); void psllq(XMMRegister reg, int8_t shift); void psllq(XMMRegister dst, XMMRegister src); + void pslld(XMMRegister dst, XMMRegister src); + void psrld(XMMRegister dst, XMMRegister src); + void psrad(XMMRegister reg, int8_t shift); + void psrad(XMMRegister dst, XMMRegister src); void psrlq(XMMRegister reg, int8_t shift); void psrlq(XMMRegister dst, XMMRegister src); + void psrldq(XMMRegister dst, int8_t shift); void pshufd(XMMRegister dst, XMMRegister src, uint8_t shuffle); void pextrd(Register dst, XMMRegister src, int8_t offset) { pextrd(Operand(dst), src, offset); diff --git a/src/ia32/deoptimizer-ia32.cc b/src/ia32/deoptimizer-ia32.cc index 390f3a76a8f..49910e6dc20 100644 --- a/src/ia32/deoptimizer-ia32.cc +++ b/src/ia32/deoptimizer-ia32.cc @@ -179,11 +179,12 @@ void Deoptimizer::SetPlatformCompiledStubRegisters( output_frame->SetRegister(ebx.code(), handler); } +void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {} -void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) { +void Deoptimizer::CopySIMD128Registers(FrameDescription* output_frame) { for (int i = 0; i < XMMRegister::kMaxNumRegisters; ++i) { - double double_value = input_->GetDoubleRegister(i); - output_frame->SetDoubleRegister(i, double_value); + simd128_value_t xmm_value = input_->GetSIMD128Register(i); + output_frame->SetSIMD128Register(i, xmm_value); } } @@ -195,14 +196,14 @@ void Deoptimizer::TableEntryGenerator::Generate() { // Save all general purpose registers before messing with them. const int kNumberOfRegisters = Register::kNumRegisters; - const int kDoubleRegsSize = kDoubleSize * XMMRegister::kMaxNumRegisters; - __ sub(esp, Immediate(kDoubleRegsSize)); + const int kXMMRegsSize = kSIMD128Size * XMMRegister::kMaxNumRegisters; + __ sub(esp, Immediate(kXMMRegsSize)); const RegisterConfiguration* config = RegisterConfiguration::Crankshaft(); for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { int code = config->GetAllocatableDoubleCode(i); XMMRegister xmm_reg = XMMRegister::from_code(code); - int offset = code * kDoubleSize; - __ movsd(Operand(esp, offset), xmm_reg); + int offset = code * kSIMD128Size; + __ movups(Operand(esp, offset), xmm_reg); } __ pushad(); @@ -210,8 +211,8 @@ void Deoptimizer::TableEntryGenerator::Generate() { ExternalReference c_entry_fp_address(Isolate::kCEntryFPAddress, isolate()); __ mov(Operand::StaticVariable(c_entry_fp_address), ebp); - const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize + - kDoubleRegsSize; + const int kSavedRegistersAreaSize = + kNumberOfRegisters * kPointerSize + kXMMRegsSize; // Get the bailout id from the stack. __ mov(ebx, Operand(esp, kSavedRegistersAreaSize)); @@ -254,14 +255,14 @@ void Deoptimizer::TableEntryGenerator::Generate() { __ pop(Operand(ebx, offset)); } - int double_regs_offset = FrameDescription::double_registers_offset(); + int xmm_regs_offset = FrameDescription::simd128_registers_offset(); // Fill in the double input registers. for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { int code = config->GetAllocatableDoubleCode(i); - int dst_offset = code * kDoubleSize + double_regs_offset; - int src_offset = code * kDoubleSize; - __ movsd(xmm0, Operand(esp, src_offset)); - __ movsd(Operand(ebx, dst_offset), xmm0); + int dst_offset = code * kSIMD128Size + xmm_regs_offset; + int src_offset = code * kSIMD128Size; + __ movups(xmm0, Operand(esp, src_offset)); + __ movups(Operand(ebx, dst_offset), xmm0); } // Clear FPU all exceptions. @@ -270,7 +271,7 @@ void Deoptimizer::TableEntryGenerator::Generate() { __ fnclex(); // Remove the bailout id, return address and the double registers. - __ add(esp, Immediate(kDoubleRegsSize + 2 * kPointerSize)); + __ add(esp, Immediate(kXMMRegsSize + 2 * kPointerSize)); // Compute a pointer to the unwinding limit in register ecx; that is // the first stack slot not part of the input frame. @@ -333,8 +334,8 @@ void Deoptimizer::TableEntryGenerator::Generate() { for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { int code = config->GetAllocatableDoubleCode(i); XMMRegister xmm_reg = XMMRegister::from_code(code); - int src_offset = code * kDoubleSize + double_regs_offset; - __ movsd(xmm_reg, Operand(ebx, src_offset)); + int src_offset = code * kSIMD128Size + xmm_regs_offset; + __ movups(xmm_reg, Operand(ebx, src_offset)); } // Push state, pc, and continuation from the last output frame. @@ -386,6 +387,33 @@ void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { UNREACHABLE(); } +double RegisterValues::GetDoubleRegister(unsigned n) const { + DCHECK(n < arraysize(simd128_registers_)); + return simd128_registers_[n].d[0]; +} + +void RegisterValues::SetDoubleRegister(unsigned n, double value) { + DCHECK(n < arraysize(simd128_registers_)); + simd128_registers_[n].d[0] = value; +} + +simd128_value_t RegisterValues::GetSIMD128Register(unsigned n) const { + DCHECK(n < arraysize(simd128_registers_)); + return simd128_registers_[n]; +} + +void RegisterValues::SetSIMD128Register(unsigned n, simd128_value_t value) { + DCHECK(n < arraysize(simd128_registers_)); + simd128_registers_[n] = value; +} + +int FrameDescription::double_registers_offset() { + return OFFSET_OF(FrameDescription, register_values_.simd128_registers_); +} + +int FrameDescription::simd128_registers_offset() { + return OFFSET_OF(FrameDescription, register_values_.simd128_registers_); +} #undef __ diff --git a/src/ia32/disasm-ia32.cc b/src/ia32/disasm-ia32.cc index a0a4e1ceeb2..ecbc4335b8e 100644 --- a/src/ia32/disasm-ia32.cc +++ b/src/ia32/disasm-ia32.cc @@ -1442,28 +1442,43 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, get_modrm(*data, &mod, ®op, &rm); AppendToBuffer("ucomiss %s,", NameOfXMMRegister(regop)); data += PrintRightXMMOperand(data); + } else if (f0byte == 0x12) { + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movhlps %s,%s", NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (f0byte == 0x16) { + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movlhps %s,%s", NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (f0byte == 0x10) { + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movups %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (f0byte == 0x11) { + AppendToBuffer("movups "); + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + data += PrintRightXMMOperand(data); + AppendToBuffer(",%s", NameOfXMMRegister(regop)); } else if (f0byte >= 0x53 && f0byte <= 0x5F) { const char* const pseudo_op[] = { - "rcpps", - "andps", - "andnps", - "orps", - "xorps", - "addps", - "mulps", - "cvtps2pd", - "cvtdq2ps", - "subps", - "minps", - "divps", - "maxps", - }; + "sqrtps", "rsqrtps", "rcpps", "andps", "andnps", + "orps", "xorps", "addps", "mulps", "cvtps2pd", + "cvtdq2ps", "subps", "minps", "divps", "maxps"}; data += 2; int mod, regop, rm; get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("%s %s,", - pseudo_op[f0byte - 0x53], + AppendToBuffer("%s %s,", pseudo_op[f0byte - 0x51], NameOfXMMRegister(regop)); data += PrintRightXMMOperand(data); } else if (f0byte == 0x50) { @@ -1474,6 +1489,17 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, NameOfCPURegister(regop), NameOfXMMRegister(rm)); data++; + } else if (f0byte == 0xC2) { + // Intel manual 2A, Table 3-11. + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + const char* const pseudo_op[] = { + "cmpeqps", "cmpltps", "cmpleps", "cmpunordps", + "cmpneqps", "cmpnltps", "cmpnleps", "cmpordps"}; + AppendToBuffer("%s %s,%s", pseudo_op[data[1]], + NameOfXMMRegister(regop), NameOfXMMRegister(rm)); + data += 2; } else if (f0byte== 0xC6) { // shufps xmm, xmm/m128, imm8 data += 2; @@ -1485,6 +1511,12 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, NameOfXMMRegister(regop), static_cast(imm8)); data += 2; + } else if (f0byte == 0x5B) { + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("cvtdq2ps %s,", NameOfXMMRegister(rm)); + data += PrintRightXMMOperand(data); } else if ((f0byte & 0xF0) == 0x80) { data += JumpConditional(data, branch_hint); } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 || @@ -1693,6 +1725,12 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, NameOfXMMRegister(regop), NameOfXMMRegister(rm)); data++; + } else if (*data == 0x40) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("pmulld %s", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); } else if (*data == 0x2A) { // movntdqa UnimplementedInstruction(); @@ -1729,6 +1767,14 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, NameOfXMMRegister(rm), static_cast(imm8)); data += 2; + } else if (*data == 0x21) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("insertps %s,%s,%d", NameOfXMMRegister(regop), + NameOfXMMRegister(rm), static_cast(imm8)); + data += 2; } else if (*data == 0x17) { data++; int mod, regop, rm; @@ -1774,6 +1820,13 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, NameOfCPURegister(regop), NameOfXMMRegister(rm)); data++; + } else if (*data == 0x51) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("sqrtpd %s,%s", NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; } else if (*data == 0x54) { data++; int mod, regop, rm; @@ -1794,10 +1847,81 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, data++; int mod, regop, rm; get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("xorpd %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); + AppendToBuffer("xorpd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0x58) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("addpd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0x59) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("mulpd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0x5B) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("cvtps2dq %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0x5C) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("subpd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0x5D) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("minpd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0x5E) { data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("divpd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0x5F) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("maxpd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0x62) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("punpackldq %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0xD6) { + AppendToBuffer("movq "); + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + data += PrintRightXMMOperand(data); + AppendToBuffer(",%s", NameOfXMMRegister(regop)); + } else if (*data == 0xF4) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("pmuludq %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0xFA) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("psubd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0xFE) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("paddd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); } else if (*data == 0x6E) { data++; int mod, regop, rm; @@ -1834,6 +1958,13 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, AppendToBuffer("punpckhdq %s,%s", NameOfXMMRegister(regop), NameOfXMMRegister(rm)); data++; + } else if (*data == 0x66) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("pcmpgtd %s,%s", NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; } else if (*data == 0x76) { data++; int mod, regop, rm; @@ -1873,6 +2004,39 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, NameOfXMMRegister(rm), static_cast(imm8)); data += 2; + } else if (*data == 0xF2) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("pslld %s,%s", NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0x72) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + DCHECK(regop == esi || regop == edx); + AppendToBuffer( + "%s %s,%d", + (regop == esi) ? "pslld" : ((regop == edx) ? "psrld" : "psrad"), + NameOfXMMRegister(rm), static_cast(imm8)); + data += 2; + } else if (*data == 0xC6) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("shufpd %s,%s,%d", NameOfXMMRegister(regop), + NameOfXMMRegister(rm), static_cast(imm8)); + data += 2; + } else if (*data == 0xD2) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("psrld %s,%s", NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; } else if (*data == 0xD3) { data++; int mod, regop, rm; @@ -1881,6 +2045,13 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, NameOfXMMRegister(regop), NameOfXMMRegister(rm)); data++; + } else if (*data == 0xE2) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("psrad %s,%s", NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; } else if (*data == 0x7F) { AppendToBuffer("movdqa "); data++; @@ -2139,6 +2310,12 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, get_modrm(*data, &mod, ®op, &rm); AppendToBuffer("lzcnt %s,", NameOfCPURegister(regop)); data += PrintRightOperand(data); + } else if (b2 == 0x7E) { + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movq %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); } else { const char* mnem = "?"; switch (b2) { diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index 83c7ce89175..977ddbb70bb 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -1178,13 +1178,13 @@ void MacroAssembler::EnterExitFramePrologue(StackFrame::Type frame_type) { void MacroAssembler::EnterExitFrameEpilogue(int argc, bool save_doubles) { // Optionally save all XMM registers. if (save_doubles) { - int space = XMMRegister::kMaxNumRegisters * kDoubleSize + - argc * kPointerSize; + int space = + XMMRegister::kMaxNumRegisters * kSIMD128Size + argc * kPointerSize; sub(esp, Immediate(space)); const int offset = -ExitFrameConstants::kFixedFrameSizeFromFp; for (int i = 0; i < XMMRegister::kMaxNumRegisters; i++) { XMMRegister reg = XMMRegister::from_code(i); - movsd(Operand(ebp, offset - ((i + 1) * kDoubleSize)), reg); + movups(Operand(ebp, offset - ((i + 1) * kSIMD128Size)), reg); } } else { sub(esp, Immediate(argc * kPointerSize)); @@ -1227,7 +1227,7 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles, bool pop_arguments) { const int offset = -ExitFrameConstants::kFixedFrameSizeFromFp; for (int i = 0; i < XMMRegister::kMaxNumRegisters; i++) { XMMRegister reg = XMMRegister::from_code(i); - movsd(reg, Operand(ebp, offset - ((i + 1) * kDoubleSize))); + movups(reg, Operand(ebp, offset - ((i + 1) * kSIMD128Size))); } } @@ -1818,6 +1818,24 @@ void MacroAssembler::AllocateHeapNumber(Register result, mov(FieldOperand(result, HeapObject::kMapOffset), Immediate(map)); } +#define SIMD128_HEAP_ALLOCATE_FUNCTIONS(V) \ + V(Float32x4, float32x4) \ + V(Int32x4, int32x4) \ + V(Bool32x4, bool32x4) + +#define DECLARE_SIMD_HEAP_ALLOCATE_FUNCTION(Type, type) \ + void MacroAssembler::Allocate##Type(Register result, Register scratch1, \ + Register scratch2, Label* gc_required) { \ + /* Allocate SIMD128 object */ \ + Allocate(Type::kSize, result, scratch1, no_reg, gc_required, \ + NO_ALLOCATION_FLAGS); \ + Handle map = isolate()->factory()->type##_map(); \ + \ + /*set the map*/ \ + mov(FieldOperand(result, HeapObject::kMapOffset), Immediate(map)); \ + } + +SIMD128_HEAP_ALLOCATE_FUNCTIONS(DECLARE_SIMD_HEAP_ALLOCATE_FUNCTION) void MacroAssembler::AllocateTwoByteString(Register result, Register length, @@ -3460,6 +3478,72 @@ void MacroAssembler::TruncatingDiv(Register dividend, int32_t divisor) { add(edx, eax); } +void MacroAssembler::absps(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } float_absolute_constant = {0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF}; + andps(dst, Operand(reinterpret_cast(&float_absolute_constant), + RelocInfo::NONE32)); +} + +void MacroAssembler::abspd(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } double_absolute_constant = {0xFFFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, 0x7FFFFFFF}; + andps(dst, Operand(reinterpret_cast(&double_absolute_constant), + RelocInfo::NONE32)); +} + +void MacroAssembler::notps(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } float_not_constant = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; + xorps(dst, Operand(reinterpret_cast(&float_not_constant), + RelocInfo::NONE32)); +} + +void MacroAssembler::negateps(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } float_negate_constant = {0x80000000, 0x80000000, 0x80000000, 0x80000000}; + xorps(dst, Operand(reinterpret_cast(&float_negate_constant), + RelocInfo::NONE32)); +} + +void MacroAssembler::negatepd(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } double_negate_constant = {0x00000000, 0x80000000, 0x00000000, 0x80000000}; + xorpd(dst, Operand(reinterpret_cast(&double_negate_constant), + RelocInfo::NONE32)); +} + +void MacroAssembler::pnegd(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } int32_one_constant = {0x1, 0x1, 0x1, 0x1}; + notps(dst); + paddd(dst, Operand(reinterpret_cast(&int32_one_constant), + RelocInfo::NONE32)); +} } // namespace internal } // namespace v8 diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index 08cc7ceb64d..c1344c3a355 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -655,6 +655,19 @@ class MacroAssembler: public Assembler { void AllocateHeapNumber(Register result, Register scratch1, Register scratch2, Label* gc_required, MutableMode mode = IMMUTABLE); + // Allocate a float32x4, bool32x4 and int32x4 object in new space with + // undefined value. + // Returns tagged pointer in result register, or jumps to gc_required if new + // space is full. + void AllocateFloat32x4(Register result, Register scratch1, Register scratch2, + Label* gc_required); + + void AllocateBool32x4(Register result, Register scratch1, Register scratch2, + Label* gc_required); + + void AllocateInt32x4(Register result, Register scratch1, Register scratch2, + Label* gc_required); + // Allocate a sequential string. All the header fields of the string object // are initialized. void AllocateTwoByteString(Register result, Register length, @@ -892,6 +905,15 @@ class MacroAssembler: public Assembler { bool has_frame() { return has_frame_; } inline bool AllowThisStubCall(CodeStub* stub); + // --------------------------------------------------------------------------- + // SIMD macros. + void absps(XMMRegister dst); + void abspd(XMMRegister dst); + void negateps(XMMRegister dst); + void negatepd(XMMRegister dst); + void notps(XMMRegister dst); + void pnegd(XMMRegister dst); + // --------------------------------------------------------------------------- // String utilities. diff --git a/src/js/harmony-simd.js b/src/js/harmony-simd.js index 0880b5bdf1e..f9f5358942a 100644 --- a/src/js/harmony-simd.js +++ b/src/js/harmony-simd.js @@ -217,11 +217,19 @@ function NAMEGreaterThanOrEqualJS(a, b) { } function NAMELoadJS(tarray, index) { - return %NAMELoad(tarray, index); + if (NAME == "Float32x4" || NAME == "Int32x4") { + return tarray._getTYPEXYZW(index); + } else { + return %NAMELoad(tarray, index); + } } function NAMEStoreJS(tarray, index, a) { - return %NAMEStore(tarray, index, a); + if (NAME == "Float32x4" || NAME == "Int32x4") { + return tarray._setTYPEXYZW(index, a); + } else { + return %NAMEStore(tarray, index, a); + } } endmacro @@ -329,24 +337,32 @@ SIMD_FROM_BITS_TYPES(DECLARE_FROM_BITS_FUNCTIONS) macro SIMD_LOADN_STOREN_TYPES(FUNCTION) -FUNCTION(Float32x4, 1) -FUNCTION(Float32x4, 2) -FUNCTION(Float32x4, 3) -FUNCTION(Int32x4, 1) -FUNCTION(Int32x4, 2) -FUNCTION(Int32x4, 3) -FUNCTION(Uint32x4, 1) -FUNCTION(Uint32x4, 2) -FUNCTION(Uint32x4, 3) -endmacro - -macro DECLARE_LOADN_STOREN_FUNCTIONS(NAME, COUNT) +FUNCTION(Float32x4, 1, X) +FUNCTION(Float32x4, 2, XY) +FUNCTION(Float32x4, 3, XYZ) +FUNCTION(Int32x4, 1, X) +FUNCTION(Int32x4, 2, XY) +FUNCTION(Int32x4, 3, XYZ) +FUNCTION(Uint32x4, 1, X) +FUNCTION(Uint32x4, 2, XY) +FUNCTION(Uint32x4, 3, XYZ) +endmacro + +macro DECLARE_LOADN_STOREN_FUNCTIONS(NAME, COUNT, LANES) function NAMELoadCOUNTJS(tarray, index) { - return %NAMELoadCOUNT(tarray, index); + if (NAME == "Float32x4" || NAME == "Int32x4") { + return tarray._getTYPELANES(index); + } else { + return %NAMELoadCOUNT(tarray, index); + } } function NAMEStoreCOUNTJS(tarray, index, a) { - return %NAMEStoreCOUNT(tarray, index, a); + if (NAME == "Float32x4" || NAME == "Int32x4") { + return tarray._setTYPELANES(index, a); + } else { + return %NAMEStoreCOUNT(tarray, index, a); + } } endmacro @@ -920,4 +936,112 @@ utils.InstallFunctions(GlobalBool8x16, DONT_ENUM, [ 'shuffle', Bool8x16ShuffleJS, ]); +// --------------------SIMD128 Access in Typed Array ----------------- +var $Uint8Array = global.Uint8Array; +var $Int8Array = global.Int8Array; +var $Uint16Array = global.Uint16Array; +var $Int16Array = global.Int16Array; +var $Uint32Array = global.Uint32Array; +var $Int32Array = global.Int32Array; +var $Float32Array = global.Float32Array; +var $Float64Array = global.Float64Array; + +macro DECLARE_TYPED_ARRAY_SIMD_LOAD_AND_STORE_FUNCTION(VIEW, TYPE, TYPENAME, LANES, NBYTES) +function VIEWGetTYPELANESJS(index) { + if (!(%_ClassOf(this) === 'VIEW')) { + throw MakeTypeError('incompatible_method_receiver', + ["VIEW._getTYPELANES", this]); + } + var tarray = this; + if (arguments.length < 1) { + throw MakeTypeError('invalid_argument'); + } + if (!IS_NUMBER(index)) { + throw MakeTypeError('The 2nd argument must be a Number.'); + } + var offset = TO_INTEGER(index) * tarray.BYTES_PER_ELEMENT + tarray.byteOffset; + if (offset < tarray.byteOffset || (offset + NBYTES) > (tarray.byteLength + tarray.byteOffset)) + throw MakeRangeError('The value of index is invalid.'); + var arraybuffer = tarray.buffer; + return %TYPELoadLANES(arraybuffer, offset); +} + +function VIEWSetTYPELANESJS(index, value) { + if (!(%_ClassOf(this) === 'VIEW')) { + throw MakeTypeError('incompatible_method_receiver', + ["VIEW._setTYPELANES", this]); + } + var tarray = this; + if (arguments.length < 2) { + throw MakeTypeError('invalid_argument'); + } + if (!IS_NUMBER(index)) { + throw MakeTypeError('The 2nd argument must be a Number.'); + } + + if (typeof(value) !== 'TYPENAME') { + throw MakeTypeError(kInvalidArgument); + } + var offset = TO_INTEGER(index) * tarray.BYTES_PER_ELEMENT + tarray.byteOffset; + if (offset < tarray.byteOffset || (offset + NBYTES) > (tarray.byteLength + tarray.byteOffset)) + throw MakeRangeError('The value of index is invalid.'); + var arraybuffer = tarray.buffer; + %TYPEStoreLANES(arraybuffer, offset, value); +} +endmacro + +macro DECLARE_VIEW_SIMD_LOAD_AND_STORE_FUNCTION(VIEW) +DECLARE_TYPED_ARRAY_SIMD_LOAD_AND_STORE_FUNCTION(VIEW, Float32x4, float32x4, XYZW, 16) +DECLARE_TYPED_ARRAY_SIMD_LOAD_AND_STORE_FUNCTION(VIEW, Float32x4, float32x4, XYZ, 12) +DECLARE_TYPED_ARRAY_SIMD_LOAD_AND_STORE_FUNCTION(VIEW, Float32x4, float32x4, XY, 8) +DECLARE_TYPED_ARRAY_SIMD_LOAD_AND_STORE_FUNCTION(VIEW, Float32x4, float32x4, X, 4) +DECLARE_TYPED_ARRAY_SIMD_LOAD_AND_STORE_FUNCTION(VIEW, Int32x4, int32x4, XYZW, 16) +DECLARE_TYPED_ARRAY_SIMD_LOAD_AND_STORE_FUNCTION(VIEW, Int32x4, int32x4, XYZ, 12) +DECLARE_TYPED_ARRAY_SIMD_LOAD_AND_STORE_FUNCTION(VIEW, Int32x4, int32x4, XY, 8) +DECLARE_TYPED_ARRAY_SIMD_LOAD_AND_STORE_FUNCTION(VIEW, Int32x4, int32x4, X, 4) +endmacro + +DECLARE_VIEW_SIMD_LOAD_AND_STORE_FUNCTION(Uint8Array) +DECLARE_VIEW_SIMD_LOAD_AND_STORE_FUNCTION(Int8Array) +DECLARE_VIEW_SIMD_LOAD_AND_STORE_FUNCTION(Uint16Array) +DECLARE_VIEW_SIMD_LOAD_AND_STORE_FUNCTION(Int16Array) +DECLARE_VIEW_SIMD_LOAD_AND_STORE_FUNCTION(Uint32Array) +DECLARE_VIEW_SIMD_LOAD_AND_STORE_FUNCTION(Int32Array) +DECLARE_VIEW_SIMD_LOAD_AND_STORE_FUNCTION(Float32Array) +DECLARE_VIEW_SIMD_LOAD_AND_STORE_FUNCTION(Float64Array) + +function SetupTypedArraysSimdLoadStore() { +macro DECLARE_INSTALL_SIMD_LOAD_AND_STORE_FUNCTION(VIEW) + utils.InstallFunctions($VIEW.prototype, DONT_ENUM, [ + "_getFloat32x4X", VIEWGetFloat32x4XJS, + "_setFloat32x4X", VIEWSetFloat32x4XJS, + "_getFloat32x4XY", VIEWGetFloat32x4XYJS, + "_setFloat32x4XY", VIEWSetFloat32x4XYJS, + "_getFloat32x4XYZ", VIEWGetFloat32x4XYZJS, + "_setFloat32x4XYZ", VIEWSetFloat32x4XYZJS, + "_getFloat32x4XYZW", VIEWGetFloat32x4XYZWJS, + "_setFloat32x4XYZW", VIEWSetFloat32x4XYZWJS, + "_getInt32x4X", VIEWGetInt32x4XJS, + "_setInt32x4X", VIEWSetInt32x4XJS, + "_getInt32x4XY", VIEWGetInt32x4XYJS, + "_setInt32x4XY", VIEWSetInt32x4XYJS, + "_getInt32x4XYZ", VIEWGetInt32x4XYZJS, + "_setInt32x4XYZ", VIEWSetInt32x4XYZJS, + "_getInt32x4XYZW", VIEWGetInt32x4XYZWJS, + "_setInt32x4XYZW", VIEWSetInt32x4XYZWJS + ]); +endmacro + +DECLARE_INSTALL_SIMD_LOAD_AND_STORE_FUNCTION(Uint8Array) +DECLARE_INSTALL_SIMD_LOAD_AND_STORE_FUNCTION(Int8Array) +DECLARE_INSTALL_SIMD_LOAD_AND_STORE_FUNCTION(Uint16Array) +DECLARE_INSTALL_SIMD_LOAD_AND_STORE_FUNCTION(Int16Array) +DECLARE_INSTALL_SIMD_LOAD_AND_STORE_FUNCTION(Uint32Array) +DECLARE_INSTALL_SIMD_LOAD_AND_STORE_FUNCTION(Int32Array) +DECLARE_INSTALL_SIMD_LOAD_AND_STORE_FUNCTION(Float32Array) +DECLARE_INSTALL_SIMD_LOAD_AND_STORE_FUNCTION(Float64Array) +} + +SetupTypedArraysSimdLoadStore(); + }) diff --git a/src/messages.h b/src/messages.h index cf49ac9c5cb..8e258462cb5 100644 --- a/src/messages.h +++ b/src/messages.h @@ -401,6 +401,7 @@ class CallSiteUtils : public AllStatic { T(TypedArraySetSourceTooLarge, "Source is too large") \ T(UnsupportedTimeZone, "Unsupported time zone specified %") \ T(ValueOutOfRange, "Value % out of range for % options property %") \ + T(InvalidOffset, "invalid_offset") \ /* SyntaxError */ \ T(BadGetterArity, "Getter must not have any formal parameters.") \ T(BadSetterArity, "Setter must have exactly one formal parameter.") \ diff --git a/src/mips/assembler-mips-inl.h b/src/mips/assembler-mips-inl.h index 963ed4acc90..0bca7fb606e 100644 --- a/src/mips/assembler-mips-inl.h +++ b/src/mips/assembler-mips-inl.h @@ -50,6 +50,7 @@ namespace internal { bool CpuFeatures::SupportsCrankshaft() { return IsSupported(FPU); } bool CpuFeatures::SupportsSimd128() { return false; } +bool CpuFeatures::SupportsSIMD128InCrankshaft() { return false; } // ----------------------------------------------------------------------------- // Operand and MemOperand. diff --git a/src/mips/assembler-mips.h b/src/mips/assembler-mips.h index 0e41671a67d..819af7e7a12 100644 --- a/src/mips/assembler-mips.h +++ b/src/mips/assembler-mips.h @@ -309,6 +309,20 @@ struct FPUControlRegister { const FPUControlRegister no_fpucreg = { kInvalidFPUControlRegister }; const FPUControlRegister FCSR = { kFCSRRegister }; +struct SIMD128Register { + static const int kMaxNumRegisters = 0; + + static int ToAllocationIndex(SIMD128Register reg) { + UNIMPLEMENTED(); + return -1; + } + + static const char* AllocationIndexToString(int index) { + UNIMPLEMENTED(); + return NULL; + } +}; + // ----------------------------------------------------------------------------- // Machine instruction Operands. diff --git a/src/mips/deoptimizer-mips.cc b/src/mips/deoptimizer-mips.cc index 478b9dfe30e..29e87be278a 100644 --- a/src/mips/deoptimizer-mips.cc +++ b/src/mips/deoptimizer-mips.cc @@ -98,6 +98,8 @@ void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) { } } +void Deoptimizer::CopySIMD128Registers(FrameDescription* output_frame) {} + #define __ masm()-> @@ -383,6 +385,34 @@ void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { UNREACHABLE(); } +double RegisterValues::GetDoubleRegister(unsigned n) const { + DCHECK(n < arraysize(double_registers_)); + return double_registers_[n]; +} + +void RegisterValues::SetDoubleRegister(unsigned n, double value) { + DCHECK(n < arraysize(double_registers_)); + double_registers_[n] = value; +} + +simd128_value_t RegisterValues::GetSIMD128Register(unsigned n) const { + UNREACHABLE(); + simd128_value_t value; + return value; +} + +void RegisterValues::SetSIMD128Register(unsigned n, simd128_value_t value) { + UNREACHABLE(); +} + +int FrameDescription::double_registers_offset() { + return OFFSET_OF(FrameDescription, register_values_.double_registers_); +} + +int FrameDescription::simd128_registers_offset() { + UNREACHABLE(); + return -1; +} #undef __ diff --git a/src/objects.cc b/src/objects.cc index 00721c2d1bb..8c8fd03d52e 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -3049,6 +3049,12 @@ const char* Representation::Mnemonic() const { case kTagged: return "t"; case kSmi: return "s"; case kDouble: return "d"; + case kFloat32x4: + return "Float32x4"; + case kInt32x4: + return "Int32x4"; + case kBool32x4: + return "Bool32x4"; case kInteger32: return "i"; case kHeapObject: return "h"; case kExternal: return "x"; @@ -4823,7 +4829,8 @@ Maybe Object::SetDataProperty(LookupIterator* it, Handle value) { Handle to_assign = value; // Convert the incoming value to a number for storing into typed arrays. if (it->IsElement() && receiver->HasFixedTypedArrayElements()) { - if (!value->IsNumber() && !value->IsUndefined(it->isolate())) { + if (!value->IsNumber() && !value->IsFloat32x4() && !value->IsInt32x4() && + !value->IsBool32x4() && !value->IsUndefined(it->isolate())) { ASSIGN_RETURN_ON_EXCEPTION_VALUE( it->isolate(), to_assign, Object::ToNumber(value), Nothing()); // We have to recheck the length. However, it can only change if the @@ -14307,6 +14314,48 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint( break; } + case Translation::FLOAT32x4_REGISTER: { + int reg_code = iterator.Next(); +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + os << "{input=" + << RegisterConfiguration::Crankshaft()->GetSimd128RegisterName( + reg_code) + << "}"; +#else + os << "{input=" << reg_code << "on other target" + << "}"; +#endif + break; + } + + case Translation::BOOL32x4_REGISTER: { + int reg_code = iterator.Next(); +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + os << "{input=" + << RegisterConfiguration::Crankshaft()->GetSimd128RegisterName( + reg_code) + << "}"; +#else + os << "{input=" << reg_code << "on ther target" + << "}"; +#endif + break; + } + + case Translation::INT32x4_REGISTER: { + int reg_code = iterator.Next(); +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 + os << "{input=" + << RegisterConfiguration::Crankshaft()->GetSimd128RegisterName( + reg_code) + << "}"; +#else + os << "{input=" << reg_code << "on other target" + << "}"; +#endif + break; + } + case Translation::STACK_SLOT: { int input_slot_index = iterator.Next(); os << "{input=" << input_slot_index << "}"; @@ -14338,6 +14387,24 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint( break; } + case Translation::FLOAT32x4_STACK_SLOT: { + int input_slot_index = iterator.Next(); + os << "{input=" << input_slot_index << "}"; + break; + } + + case Translation::BOOL32x4_STACK_SLOT: { + int input_slot_index = iterator.Next(); + os << "{input=" << input_slot_index << "}"; + break; + } + + case Translation::INT32x4_STACK_SLOT: { + int input_slot_index = iterator.Next(); + os << "{input=" << input_slot_index << "}"; + break; + } + case Translation::LITERAL: { int literal_index = iterator.Next(); Object* literal_value = LiteralArray()->get(literal_index); diff --git a/src/objects.h b/src/objects.h index b7c67030c50..4589d89164e 100644 --- a/src/objects.h +++ b/src/objects.h @@ -418,6 +418,9 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1; V(JS_ARRAY_BUFFER_TYPE) \ V(JS_TYPED_ARRAY_TYPE) \ V(JS_DATA_VIEW_TYPE) \ + V(FLOAT32x4_TYPE) \ + V(INT32x4_TYPE) \ + V(BOOL32x4_TYPE) \ V(JS_PROXY_TYPE) \ V(JS_SET_TYPE) \ V(JS_MAP_TYPE) \ @@ -713,6 +716,9 @@ enum InstanceType { JS_ARRAY_BUFFER_TYPE, JS_TYPED_ARRAY_TYPE, JS_DATA_VIEW_TYPE, + FLOAT32x4_TYPE, + INT32x4_TYPE, + BOOL32x4_TYPE, JS_SET_TYPE, JS_MAP_TYPE, JS_SET_ITERATOR_TYPE, @@ -6854,13 +6860,201 @@ class Script: public Struct { V(Atomics, load, AtomicsLoad) \ V(Atomics, store, AtomicsStore) +#define SIMD_NULLARY_OPERATIONS(V) \ + V(SIMD.Float32x4, zero, Float32x4Zero, Float32x4) \ + V(SIMD.Int32x4, zero, Int32x4Zero, Int32x4) + +#define SIMD_UNARY_OPERATIONS(V) \ + V(SIMD.Float32x4, check, Float32x4Check, Float32x4, Float32x4) \ + V(SIMD.Int32x4, check, Int32x4Check, Int32x4, Int32x4) \ + V(SIMD.Float32x4, abs, Float32x4Abs, Float32x4, Float32x4) \ + V(SIMD.Float32x4, fromInt32x4, Int32x4ToFloat32x4, Float32x4, Int32x4) \ + V(SIMD.Float32x4, fromInt32x4Bits, Int32x4BitsToFloat32x4, Float32x4, \ + Int32x4) \ + V(SIMD.Float32x4, neg, Float32x4Neg, Float32x4, Float32x4) \ + V(SIMD.Float32x4, reciprocalApproximation, Float32x4RecipApprox, Float32x4, \ + Float32x4) \ + V(SIMD.Float32x4, reciprocalSqrtApproximation, Float32x4RecipSqrtApprox, \ + Float32x4, Float32x4) \ + V(SIMD.Float32x4, splat, Float32x4Splat, Float32x4, Double) \ + V(SIMD.Float32x4, sqrt, Float32x4Sqrt, Float32x4, Float32x4) \ + V(SIMD.Int32x4, fromFloat32x4, Float32x4ToInt32x4, Int32x4, Float32x4) \ + V(SIMD.Int32x4, fromFloat32x4Bits, Float32x4BitsToInt32x4, Int32x4, \ + Float32x4) \ + V(SIMD.Int32x4, neg, Int32x4Neg, Int32x4, Int32x4) \ + V(SIMD.Int32x4, not, Int32x4Not, Int32x4, Int32x4) \ + V(SIMD.Int32x4, splat, Int32x4Splat, Int32x4, Integer32) \ + V(SIMD.Bool32x4, anyTrue, Bool32x4AnyTrue, Tagged, Bool32x4) \ + V(SIMD.Bool32x4, allTrue, Bool32x4AllTrue, Tagged, Bool32x4) + +// Do not need to install them in InstallExperimentalSIMDBuiltinFunctionIds. +#define SIMD_UNARY_OPERATIONS_FOR_PROPERTY_ACCESS(V) \ + V(SIMD.Float32x4.prototype, signMask, Float32x4GetSignMask, Integer32, \ + Float32x4) \ + V(SIMD.Float32x4.prototype, x, Float32x4GetX, Double, Float32x4) \ + V(SIMD.Float32x4.prototype, y, Float32x4GetY, Double, Float32x4) \ + V(SIMD.Float32x4.prototype, z, Float32x4GetZ, Double, Float32x4) \ + V(SIMD.Float32x4.prototype, w, Float32x4GetW, Double, Float32x4) \ + V(SIMD.Int32x4.prototype, signMask, Int32x4GetSignMask, Integer32, Int32x4) \ + V(SIMD.Int32x4.prototype, x, Int32x4GetX, Integer32, Int32x4) \ + V(SIMD.Int32x4.prototype, y, Int32x4GetY, Integer32, Int32x4) \ + V(SIMD.Int32x4.prototype, z, Int32x4GetZ, Integer32, Int32x4) \ + V(SIMD.Int32x4.prototype, w, Int32x4GetW, Integer32, Int32x4) \ + V(SIMD.Int32x4.prototype, flagX, Int32x4GetFlagX, Tagged, Int32x4) \ + V(SIMD.Int32x4.prototype, flagY, Int32x4GetFlagY, Tagged, Int32x4) \ + V(SIMD.Int32x4.prototype, flagZ, Int32x4GetFlagZ, Tagged, Int32x4) \ + V(SIMD.Int32x4.prototype, flagW, Int32x4GetFlagW, Tagged, Int32x4) + +#define SIMD_BINARY_OPERATIONS(V) \ + V(SIMD.Float32x4, add, Float32x4Add, Float32x4, Float32x4, Float32x4) \ + V(SIMD.Float32x4, div, Float32x4Div, Float32x4, Float32x4, Float32x4) \ + V(SIMD.Float32x4, max, Float32x4Max, Float32x4, Float32x4, Float32x4) \ + V(SIMD.Float32x4, min, Float32x4Min, Float32x4, Float32x4, Float32x4) \ + V(SIMD.Float32x4, mul, Float32x4Mul, Float32x4, Float32x4, Float32x4) \ + V(SIMD.Float32x4, sub, Float32x4Sub, Float32x4, Float32x4, Float32x4) \ + V(SIMD.Float32x4, equal, Float32x4Equal, Bool32x4, Float32x4, Float32x4) \ + V(SIMD.Float32x4, notEqual, Float32x4NotEqual, Bool32x4, Float32x4, \ + Float32x4) \ + V(SIMD.Float32x4, greaterThan, Float32x4GreaterThan, Bool32x4, Float32x4, \ + Float32x4) \ + V(SIMD.Float32x4, greaterThanOrEqual, Float32x4GreaterThanOrEqual, Bool32x4, \ + Float32x4, Float32x4) \ + V(SIMD.Float32x4, lessThan, Float32x4LessThan, Bool32x4, Float32x4, \ + Float32x4) \ + V(SIMD.Float32x4, lessThanOrEqual, Float32x4LessThanOrEqual, Bool32x4, \ + Float32x4, Float32x4) \ + V(SIMD.Float32x4, extractLane, Float32x4ExtractLane, Double, Float32x4, \ + Integer32) \ + V(SIMD.Int32x4, add, Int32x4Add, Int32x4, Int32x4, Int32x4) \ + V(SIMD.Int32x4, and, Int32x4And, Int32x4, Int32x4, Int32x4) \ + V(SIMD.Int32x4, mul, Int32x4Mul, Int32x4, Int32x4, Int32x4) \ + V(SIMD.Int32x4, or, Int32x4Or, Int32x4, Int32x4, Int32x4) \ + V(SIMD.Int32x4, sub, Int32x4Sub, Int32x4, Int32x4, Int32x4) \ + V(SIMD.Int32x4, xor, Int32x4Xor, Int32x4, Int32x4, Int32x4) \ + V(SIMD.Int32x4, extractLane, Int32x4ExtractLane, Integer32, Int32x4, \ + Integer32) \ + V(SIMD.Int32x4, greaterThan, Int32x4GreaterThan, Bool32x4, Int32x4, Int32x4) \ + V(SIMD.Int32x4, equal, Int32x4Equal, Bool32x4, Int32x4, Int32x4) \ + V(SIMD.Int32x4, lessThan, Int32x4LessThan, Bool32x4, Int32x4, Int32x4) \ + V(SIMD.Int32x4, shiftLeftByScalar, Int32x4ShiftLeft, Int32x4, Int32x4, \ + Integer32) \ + V(SIMD.Int32x4, shiftRightByScalar, Int32x4ShiftRightArithmetic, Int32x4, \ + Int32x4, Integer32) \ + V(SIMD.Bool32x4, extractLane, Bool32x4ExtractLane, Tagged, Bool32x4, \ + Integer32) + +#define SIMD_TERNARY_OPERATIONS(V) \ + V(SIMD.Float32x4, select, Float32x4Select, Float32x4, Int32x4, Float32x4, \ + Float32x4) \ + V(SIMD.Int32x4, select, Int32x4Select, Int32x4, Bool32x4, Int32x4, Int32x4) \ + V(SIMD.Float32x4, replaceLane, Float32x4ReplaceLane, Float32x4, Float32x4, \ + Integer32, Double) \ + V(SIMD.Int32x4, replaceLane, Int32x4ReplaceLane, Int32x4, Int32x4, \ + Integer32, Integer32) + +#define SIMD_QUARTERNARY_OPERATIONS(V) \ + V(SIMD, Float32x4, Float32x4Constructor, Float32x4, Double, Double, Double, \ + Double) \ + V(SIMD, Int32x4, Int32x4Constructor, Int32x4, Integer32, Integer32, \ + Integer32, Integer32) \ + V(SIMD, Bool32x4, Bool32x4Constructor, Bool32x4, Tagged, Tagged, Tagged, \ + Tagged) + +#define SIMD_QUINARY_OPERATIONS(V) \ + V(SIMD.Float32x4, swizzle, Float32x4Swizzle, Float32x4, Float32x4, \ + Integer32, Integer32, Integer32, Integer32) \ + V(SIMD.Int32x4, swizzle, Int32x4Swizzle, Int32x4, Int32x4, Integer32, \ + Integer32, Integer32, Integer32) + +#define SIMD_SENARY_OPERATIONS(V) \ + V(SIMD.Float32x4, shuffle, Float32x4Shuffle, Float32x4, Float32x4, \ + Float32x4, Integer32, Integer32, Integer32, Integer32) \ + V(SIMD.Int32x4, shuffle, Int32x4Shuffle, Int32x4, Int32x4, Int32x4, \ + Integer32, Integer32, Integer32, Integer32) + +#define SIMD_LOAD_OPERATIONS(V) \ + V(SIMD.Float32x4, load, GetFloat32x4XYZW) \ + V(SIMD.Float32x4, loadX, GetFloat32x4X) \ + V(SIMD.Float32x4, loadXY, GetFloat32x4XY) \ + V(SIMD.Float32x4, loadXYZ, GetFloat32x4XYZ) \ + V(SIMD.Int32x4, load, GetInt32x4XYZW) \ + V(SIMD.Int32x4, loadX, GetInt32x4X) \ + V(SIMD.Int32x4, loadXY, GetInt32x4XY) \ + V(SIMD.Int32x4, loadXYZ, GetInt32x4XYZ) + +#define SIMD_STORE_OPERATIONS(V) \ + V(SIMD.Float32x4, store, SetFloat32x4XYZW) \ + V(SIMD.Float32x4, storeX, SetFloat32x4X) \ + V(SIMD.Float32x4, storeXY, SetFloat32x4XY) \ + V(SIMD.Float32x4, storeXYZ, SetFloat32x4XYZ) \ + V(SIMD.Int32x4, store, SetInt32x4XYZW) \ + V(SIMD.Int32x4, storeX, SetInt32x4X) \ + V(SIMD.Int32x4, storeXY, SetInt32x4XY) \ + V(SIMD.Int32x4, storeXYZ, SetInt32x4XYZ) + +#define TYPED_ARRAYS_SIMD_LOAD_OPERATIONS(V) \ + V(Float32Array.prototype, _getFloat32x4XYZW, Float32ArrayGetFloat32x4XYZW) \ + V(Float32Array.prototype, _getFloat32x4XYZ, Float32ArrayGetFloat32x4XYZ) \ + V(Float32Array.prototype, _getFloat32x4XY, Float32ArrayGetFloat32x4XY) \ + V(Float32Array.prototype, _getFloat32x4X, Float32ArrayGetFloat32x4X) \ + V(Int32Array.prototype, _getInt32x4XYZW, Int32ArrayGetInt32x4XYZW) \ + V(Int32Array.prototype, _getInt32x4XYZ, Int32ArrayGetInt32x4XYZ) \ + V(Int32Array.prototype, _getInt32x4XY, Int32ArrayGetInt32x4XY) \ + V(Int32Array.prototype, _getInt32x4X, Int32ArrayGetInt32x4X) \ + V(Int8Array.prototype, _getFloat32x4XYZW, Int8ArrayGetFloat32x4XYZW) \ + V(Int8Array.prototype, _getFloat32x4XYZ, Int8ArrayGetFloat32x4XYZ) \ + V(Int8Array.prototype, _getFloat32x4XY, Int8ArrayGetFloat32x4XY) \ + V(Int8Array.prototype, _getFloat32x4X, Int8ArrayGetFloat32x4X) \ + V(Int8Array.prototype, _getInt32x4XYZW, Int8ArrayGetInt32x4XYZW) \ + V(Int8Array.prototype, _getInt32x4XYZ, Int8ArrayGetInt32x4XYZ) \ + V(Int8Array.prototype, _getInt32x4XY, Int8ArrayGetInt32x4XY) \ + V(Int8Array.prototype, _getInt32x4X, Int8ArrayGetInt32x4X) \ + V(Uint8Array.prototype, _getFloat32x4XYZW, Uint8ArrayGetFloat32x4XYZW) \ + V(Uint8Array.prototype, _getFloat32x4XYZ, Uint8ArrayGetFloat32x4XYZ) \ + V(Uint8Array.prototype, _getFloat32x4XY, Uint8ArrayGetFloat32x4XY) \ + V(Uint8Array.prototype, _getFloat32x4X, Uint8ArrayGetFloat32x4X) \ + V(Uint8Array.prototype, _getInt32x4XYZW, Uint8ArrayGetInt32x4XYZW) \ + V(Uint8Array.prototype, _getInt32x4XYZ, Uint8ArrayGetInt32x4XYZ) \ + V(Uint8Array.prototype, _getInt32x4XY, Uint8ArrayGetInt32x4XY) \ + V(Uint8Array.prototype, _getInt32x4X, Uint8ArrayGetInt32x4X) + +#define TYPED_ARRAYS_SIMD_STORE_OPERATIONS(V) \ + V(Float32Array.prototype, _setFloat32x4XYZW, Float32ArraySetFloat32x4XYZW) \ + V(Float32Array.prototype, _setFloat32x4XYZ, Float32ArraySetFloat32x4XYZ) \ + V(Float32Array.prototype, _setFloat32x4XY, Float32ArraySetFloat32x4XY) \ + V(Float32Array.prototype, _setFloat32x4X, Float32ArraySetFloat32x4X) \ + V(Int32Array.prototype, _setInt32x4XYZW, Int32ArraySetInt32x4XYZW) \ + V(Int32Array.prototype, _setInt32x4XYZ, Int32ArraySetInt32x4XYZ) \ + V(Int32Array.prototype, _setInt32x4XY, Int32ArraySetInt32x4XY) \ + V(Int32Array.prototype, _setInt32x4X, Int32ArraySetInt32x4X) \ + V(Int8Array.prototype, _setFloat32x4XYZW, Int8ArraySetFloat32x4XYZW) \ + V(Int8Array.prototype, _setFloat32x4XYZ, Int8ArraySetFloat32x4XYZ) \ + V(Int8Array.prototype, _setFloat32x4XY, Int8ArraySetFloat32x4XY) \ + V(Int8Array.prototype, _setFloat32x4X, Int8ArraySetFloat32x4X) \ + V(Int8Array.prototype, _setInt32x4XYZW, Int8ArraySetInt32x4XYZW) \ + V(Int8Array.prototype, _setInt32x4XYZ, Int8ArraySetInt32x4XYZ) \ + V(Int8Array.prototype, _setInt32x4XY, Int8ArraySetInt32x4XY) \ + V(Int8Array.prototype, _setInt32x4X, Int8ArraySetInt32x4X) \ + V(Uint8Array.prototype, _setFloat32x4XYZW, Uint8ArraySetFloat32x4XYZW) \ + V(Uint8Array.prototype, _setFloat32x4XYZ, Uint8ArraySetFloat32x4XYZ) \ + V(Uint8Array.prototype, _setFloat32x4XY, Uint8ArraySetFloat32x4XY) \ + V(Uint8Array.prototype, _setFloat32x4X, Uint8ArraySetFloat32x4X) \ + V(Uint8Array.prototype, _setInt32x4XYZW, Uint8ArraySetInt32x4XYZW) \ + V(Uint8Array.prototype, _setInt32x4XYZ, Uint8ArraySetInt32x4XYZ) \ + V(Uint8Array.prototype, _setInt32x4XY, Uint8ArraySetInt32x4XY) \ + V(Uint8Array.prototype, _setInt32x4X, Uint8ArraySetInt32x4X) + +// Do not need to install them in InstallExperimentalSIMDBuiltinFunctionIds. +#define SIMD_FAKE_ID_LISTS(V) \ + V(SIMD, unreachable, SIMD128Unreachable) \ + V(SIMD, change, SIMD128Change) + enum BuiltinFunctionId { kArrayCode, #define DECLARE_FUNCTION_ID(ignored1, ignore2, name) \ k##name, FUNCTIONS_WITH_ID_LIST(DECLARE_FUNCTION_ID) ATOMIC_FUNCTIONS_WITH_ID_LIST(DECLARE_FUNCTION_ID) -#undef DECLARE_FUNCTION_ID // Fake id for a special case of Math.pow. Note, it continues the // list of math functions. kMathPowHalf, @@ -6879,9 +7073,47 @@ enum BuiltinFunctionId { kTypedArrayByteOffset, kTypedArrayLength, kSharedArrayBufferByteLength, + SIMD_FAKE_ID_LISTS(DECLARE_FUNCTION_ID) TYPED_ARRAYS_SIMD_LOAD_OPERATIONS( + DECLARE_FUNCTION_ID) + TYPED_ARRAYS_SIMD_STORE_OPERATIONS(DECLARE_FUNCTION_ID) + SIMD_LOAD_OPERATIONS(DECLARE_FUNCTION_ID) SIMD_STORE_OPERATIONS( + DECLARE_FUNCTION_ID) +#undef DECLARE_FUNCTION_ID +#define DECLARE_SIMD_NULLARY_FUNCTION_ID(i1, i2, name, i3) k##name, + SIMD_NULLARY_OPERATIONS(DECLARE_SIMD_NULLARY_FUNCTION_ID) +#undef DECLARE_SIMD_NULLARY_FUNCTION_ID +#define DECLARE_SIMD_UNARY_FUNCTION_ID(i1, i2, name, i3, i4) k##name, + SIMD_UNARY_OPERATIONS(DECLARE_SIMD_UNARY_FUNCTION_ID) + SIMD_UNARY_OPERATIONS_FOR_PROPERTY_ACCESS( + DECLARE_SIMD_UNARY_FUNCTION_ID) +#undef DECLARE_SIMD_UNARY_FUNCTION_ID +#define DECLARE_SIMD_BINARY_FUNCTION_ID(i1, i2, name, i3, i4, i5) k##name, + SIMD_BINARY_OPERATIONS( + DECLARE_SIMD_BINARY_FUNCTION_ID) +#undef DECLARE_SIMD_BINARY_FUNCTION_ID +#define DECLARE_SIMD_TERNARY_FUNCTION_ID(i1, i2, name, i3, i4, i5, i6) k##name, + SIMD_TERNARY_OPERATIONS( + DECLARE_SIMD_TERNARY_FUNCTION_ID) +#undef DECLARE_SIMD_TERNARY_FUNCTION_ID +#define DECLARE_SIMD_QUARTERNARY_FUNCTION_ID(i1, i2, name, i3, i4, i5, i6, i7) \ + k##name, + SIMD_QUARTERNARY_OPERATIONS( + DECLARE_SIMD_QUARTERNARY_FUNCTION_ID) +#undef DECLARE_SIMD_QUARTERNARY_FUNCTION_ID +#define DECLARE_SIMD_QUINARY_FUNCTION_ID(i1, i2, name, i3, i4, i5, i6, i7, i8) \ + k##name, + SIMD_QUINARY_OPERATIONS( + DECLARE_SIMD_QUINARY_FUNCTION_ID) +#undef DECLARE_SIMD_QUINARY_FUNCTION_ID +#define DECLARE_SIMD_SENARY_FUNCTION_ID(i1, i2, name, i3, i4, i5, i6, i7, i8, \ + i9) \ + k##name, + SIMD_SENARY_OPERATIONS( + DECLARE_SIMD_SENARY_FUNCTION_ID) +#undef DECLARE_SIMD_SENARY_FUNCTION_ID + kNumberOfBuiltinFunction }; - // Result of searching in an optimized code map of a SharedFunctionInfo. Note // that both {code} and {literals} can be NULL to pass search result status. struct CodeAndLiterals { diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h index b8703d06917..2d6ff2e7ad5 100644 --- a/src/parsing/parser-base.h +++ b/src/parsing/parser-base.h @@ -2926,6 +2926,10 @@ ParserBase::ParseLeftHandSideExpression(ExpressionClassifier* classifier, ArrowFormalParametersUnexpectedToken(classifier); + if (this->BuildSIMD128LoadStoreExpression(&result, args, pos, + factory())) + break; + // Keep track of eval() calls since they disable all local variable // optimizations. // The calls that need special treatment are the diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc index 25571470dce..49bd400103d 100644 --- a/src/parsing/parser.cc +++ b/src/parsing/parser.cc @@ -434,6 +434,157 @@ bool ParserBaseTraits::ShortcutNumericLiteralBinaryExpression( return false; } +bool ParserBaseTraits::BuildSIMD128LoadStoreExpression( + Expression** expression, ZoneList* arguments, int pos, + AstNodeFactory* factory) { + Property* prop = (*expression)->AsProperty(); + Expression* tarray_op_literal = NULL; + + if (prop) { + Property* simd_type_prop = prop->obj()->AsProperty(); + if (simd_type_prop) { + VariableProxy* simd_var = simd_type_prop->obj()->AsVariableProxy(); + if (simd_var && simd_var->raw_name() && + simd_var->raw_name()->IsOneByteEqualTo("SIMD")) { + Literal* type_literal = simd_type_prop->key()->AsLiteral(); + if (type_literal && type_literal->raw_value() && + type_literal->raw_value()->AsString()) { + const AstRawString* type_literal_raw_string = + type_literal->raw_value()->AsString(); + if (type_literal_raw_string->IsOneByteEqualTo("Float32x4")) { + Literal* op_literal = prop->key()->AsLiteral(); + if (op_literal && op_literal->raw_value() && + op_literal->raw_value()->AsString()) { + const AstRawString* op_raw_string = + op_literal->raw_value()->AsString(); + AstValueFactory* ast_factory = delegate()->ast_value_factory(); + if (op_raw_string->IsOneByteEqualTo("load")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_getFloat32x4XYZW"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("load1")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_getFloat32x4X"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("load2")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_getFloat32x4XY"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("load3")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_getFloat32x4XYZ"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("store")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_setFloat32x4XYZW"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("store1")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_setFloat32x4X"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("store2")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_setFloat32x4XY"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("store3")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_setFloat32x4XYZ"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } + } + } else if (type_literal_raw_string->IsOneByteEqualTo("Int32x4")) { + Literal* op_literal = prop->key()->AsLiteral(); + if (op_literal && op_literal->raw_value() && + op_literal->raw_value()->AsString()) { + const AstRawString* op_raw_string = + op_literal->raw_value()->AsString(); + AstValueFactory* ast_factory = delegate()->ast_value_factory(); + if (op_raw_string->IsOneByteEqualTo("load")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_getInt32x4XYZW"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("load1")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_getInt32x4X"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("load2")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_getInt32x4XY"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("load3")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_getInt32x4XYZ"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("store")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_setInt32x4XYZW"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("store1")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_setInt32x4X"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("store2")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_setInt32x4XY"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } else if (op_raw_string->IsOneByteEqualTo("store3")) { + const AstRawString* op_str = + ast_factory->GetOneByteString("_setInt32x4XYZ"); + tarray_op_literal = + factory->NewStringLiteral(op_str, kNoSourcePosition); + } + } + } + } + } + } + } + + if (tarray_op_literal) { + if (arguments && arguments->length() == 2) { + Expression* tarray = arguments->at(0); + Expression* index = arguments->at(1); + Expression* tarray_op = + factory->NewProperty(tarray, tarray_op_literal, pos); + Zone* zone = delegate()->zone(); + ZoneList* tarray_op_args = + new (zone) ZoneList(1, zone); + tarray_op_args->Add(index, zone); + *expression = factory->NewCall(tarray_op, tarray_op_args, pos); + return true; + } else if (arguments && arguments->length() == 3) { + Expression* tarray = arguments->at(0); + Expression* index = arguments->at(1); + Expression* value = arguments->at(2); + Expression* tarray_op = + factory->NewProperty(tarray, tarray_op_literal, pos); + Zone* zone = delegate()->zone(); + ZoneList* tarray_op_args = + new (zone) ZoneList(1, zone); + tarray_op_args->Add(index, zone); + tarray_op_args->Add(value, zone); + *expression = factory->NewCall(tarray_op, tarray_op_args, pos); + return true; + } + } + return false; +} + Expression* ParserBaseTraits::BuildUnaryExpression( Expression* expression, Token::Value op, int pos, AstNodeFactory* factory) { DCHECK(expression != NULL); diff --git a/src/parsing/parser.h b/src/parsing/parser.h index b069f9af980..ff5304bafbc 100644 --- a/src/parsing/parser.h +++ b/src/parsing/parser.h @@ -243,6 +243,14 @@ class ParserBaseTraits { Token::Value op, int pos, AstNodeFactory* factory); + // If we find a SIMD load or store call with array types + // and offset as arguments, we will return an expression + // calling array types load or store with offset as argument. + // Otherwise, returns NULL. + bool BuildSIMD128LoadStoreExpression(Expression** expression, + ZoneList* arguments, + int pos, AstNodeFactory* factory); + // Rewrites the following types of unary expressions: // not -> true / false // + -> diff --git a/src/parsing/preparser.h b/src/parsing/preparser.h index 3f268ee14ad..97ba3eca102 100644 --- a/src/parsing/preparser.h +++ b/src/parsing/preparser.h @@ -722,6 +722,12 @@ class ParserBaseTraits { return false; } + bool BuildSIMD128LoadStoreExpression(PreParserExpression* expression, + PreParserExpressionList arguments, + int pos, PreParserFactory* factory) { + return false; + } + PreParserExpression BuildUnaryExpression(PreParserExpression expression, Token::Value op, int pos, PreParserFactory* factory) { diff --git a/src/property-details.h b/src/property-details.h index 87df02d08e3..07c352e98ef 100644 --- a/src/property-details.h +++ b/src/property-details.h @@ -98,6 +98,9 @@ class Representation { kSmi, kInteger32, kDouble, + kFloat32x4, + kInt32x4, + kBool32x4, kHeapObject, kTagged, kExternal, @@ -115,6 +118,9 @@ class Representation { static Representation Smi() { return Representation(kSmi); } static Representation Integer32() { return Representation(kInteger32); } static Representation Double() { return Representation(kDouble); } + static Representation Float32x4() { return Representation(kFloat32x4); } + static Representation Int32x4() { return Representation(kInt32x4); } + static Representation Bool32x4() { return Representation(kBool32x4); } static Representation HeapObject() { return Representation(kHeapObject); } static Representation External() { return Representation(kExternal); } @@ -143,6 +149,7 @@ class Representation { if (IsHeapObject()) return other.IsNone(); if (kind_ == kUInteger8 && other.kind_ == kInteger8) return false; if (kind_ == kUInteger16 && other.kind_ == kInteger16) return false; + if (IsSIMD128() && other.IsSIMD128()) return false; return kind_ > other.kind_; } @@ -182,6 +189,12 @@ class Representation { bool IsInteger32() const { return kind_ == kInteger32; } bool IsSmiOrInteger32() const { return IsSmi() || IsInteger32(); } bool IsDouble() const { return kind_ == kDouble; } + bool IsFloat32x4() const { return kind_ == kFloat32x4; } + bool IsInt32x4() const { return kind_ == kInt32x4; } + bool IsBool32x4() const { return kind_ == kBool32x4; } + bool IsSIMD128() const { + return IsFloat32x4() || IsInt32x4() || IsBool32x4(); + } bool IsHeapObject() const { return kind_ == kHeapObject; } bool IsExternal() const { return kind_ == kExternal; } bool IsSpecialization() const { diff --git a/src/runtime/runtime-simd.cc b/src/runtime/runtime-simd.cc index 9542a4420a2..be238022257 100644 --- a/src/runtime/runtime-simd.cc +++ b/src/runtime/runtime-simd.cc @@ -17,6 +17,30 @@ namespace v8 { namespace internal { +RUNTIME_FUNCTION(Runtime_AllocateFloat32x4) { + HandleScope scope(isolate); + DCHECK(args.length() == 0); + + float32x4_value_t zero = {{0, 0, 0, 0}}; + return *isolate->factory()->NewFloat32x4(zero.storage); +} + +RUNTIME_FUNCTION(Runtime_AllocateInt32x4) { + HandleScope scope(isolate); + DCHECK(args.length() == 0); + + int32x4_value_t zero = {{0, 0, 0, 0}}; + return *isolate->factory()->NewInt32x4(zero.storage); +} + +RUNTIME_FUNCTION(Runtime_AllocateBool32x4) { + HandleScope scope(isolate); + DCHECK(args.length() == 0); + + bool zero[4] = {false, false, false, false}; + return *isolate->factory()->NewBool32x4(zero); +} + namespace { // Functions to convert Numbers to SIMD component types. @@ -1010,6 +1034,125 @@ SIMD_LOADN_STOREN_TYPES(SIMD_STORE1_FUNCTION) SIMD_LOADN_STOREN_TYPES(SIMD_STORE2_FUNCTION) SIMD_LOADN_STOREN_TYPES(SIMD_STORE3_FUNCTION) +template +inline void CopyBytes(uint8_t* target, uint8_t* source) { + for (int i = 0; i < n; i++) { + *(target++) = *(source++); + } +} + +template +inline static bool SimdTypeLoadValue(Isolate* isolate, + Handle buffer, + Handle byte_offset_obj, + T* result) { + size_t byte_offset = 0; + if (!TryNumberToSize(*byte_offset_obj, &byte_offset)) { + return false; + } + + size_t buffer_byte_length = NumberToSize(buffer->byte_length()); + if (byte_offset + Bytes > buffer_byte_length) { // overflow + return false; + } + + union Value { + T data; + uint8_t bytes[sizeof(T)]; + }; + + Value value; + memset(value.bytes, 0, sizeof(T)); + uint8_t* source = + static_cast(buffer->backing_store()) + byte_offset; + DCHECK(Bytes <= sizeof(T)); + CopyBytes(value.bytes, source); + *result = value.data; + return true; +} + +template +static bool SimdTypeStoreValue(Isolate* isolate, Handle buffer, + Handle byte_offset_obj, T data) { + size_t byte_offset = 0; + if (!TryNumberToSize(*byte_offset_obj, &byte_offset)) { + return false; + } + + size_t buffer_byte_length = NumberToSize(buffer->byte_length()); + if (byte_offset + Bytes > buffer_byte_length) { // overflow + return false; + } + + union Value { + T data; + uint8_t bytes[sizeof(T)]; + }; + + Value value; + value.data = data; + + uint8_t* target = + static_cast(buffer->backing_store()) + byte_offset; + DCHECK(Bytes <= sizeof(T)); + CopyBytes(target, value.bytes); + return true; +} + +#define SIMD128_LOAD_RUNTIME_FUNCTION(Type, ValueType, Lanes, Bytes) \ + RUNTIME_FUNCTION(Runtime_##Type##Load##Lanes) { \ + HandleScope scope(isolate); \ + DCHECK(args.length() == 2); \ + CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 0); \ + CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \ + ValueType result; \ + if (SimdTypeLoadValue(isolate, buffer, offset, \ + &result)) { \ + return *isolate->factory()->New##Type(result.storage); \ + } else { \ + THROW_NEW_ERROR_RETURN_FAILURE( \ + isolate, NewRangeError(MessageTemplate::kInvalidOffset)); \ + } \ + } + +SIMD128_LOAD_RUNTIME_FUNCTION(Float32x4, float32x4_value_t, XYZW, 16) +SIMD128_LOAD_RUNTIME_FUNCTION(Float32x4, float32x4_value_t, XYZ, 12) +SIMD128_LOAD_RUNTIME_FUNCTION(Float32x4, float32x4_value_t, XY, 8) +SIMD128_LOAD_RUNTIME_FUNCTION(Float32x4, float32x4_value_t, X, 4) +SIMD128_LOAD_RUNTIME_FUNCTION(Int32x4, int32x4_value_t, XYZW, 16) +SIMD128_LOAD_RUNTIME_FUNCTION(Int32x4, int32x4_value_t, XYZ, 12) +SIMD128_LOAD_RUNTIME_FUNCTION(Int32x4, int32x4_value_t, XY, 8) +SIMD128_LOAD_RUNTIME_FUNCTION(Int32x4, int32x4_value_t, X, 4) + +#define SIMD128_STORE_RUNTIME_FUNCTION(Type, ValueType, Lanes, Lanes_count, \ + Bytes) \ + RUNTIME_FUNCTION(Runtime_##Type##Store##Lanes) { \ + HandleScope scope(isolate); \ + DCHECK(args.length() == 3); \ + CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 0); \ + CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset, 1); \ + CONVERT_ARG_CHECKED(Type, value, 2); \ + ValueType v; \ + for (uint32_t count = 0; count < Lanes_count; count++) { \ + v.storage[count] = value->get_lane(count); \ + } \ + if (SimdTypeStoreValue(isolate, buffer, offset, v)) { \ + return isolate->heap()->undefined_value(); \ + } else { \ + THROW_NEW_ERROR_RETURN_FAILURE( \ + isolate, NewRangeError(MessageTemplate::kInvalidOffset)); \ + } \ + } + +SIMD128_STORE_RUNTIME_FUNCTION(Float32x4, float32x4_value_t, XYZW, 4, 16) +SIMD128_STORE_RUNTIME_FUNCTION(Float32x4, float32x4_value_t, XYZ, 3, 12) +SIMD128_STORE_RUNTIME_FUNCTION(Float32x4, float32x4_value_t, XY, 2, 8) +SIMD128_STORE_RUNTIME_FUNCTION(Float32x4, float32x4_value_t, X, 1, 4) +SIMD128_STORE_RUNTIME_FUNCTION(Int32x4, int32x4_value_t, XYZW, 4, 16) +SIMD128_STORE_RUNTIME_FUNCTION(Int32x4, int32x4_value_t, XYZ, 3, 12) +SIMD128_STORE_RUNTIME_FUNCTION(Int32x4, int32x4_value_t, XY, 2, 8) +SIMD128_STORE_RUNTIME_FUNCTION(Int32x4, int32x4_value_t, X, 1, 4) + //------------------------------------------------------------------- } // namespace internal diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 38eb51d5a39..12dc3478c49 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -485,6 +485,9 @@ namespace internal { F(StoreLookupSlot_Strict, 2, 1) #define FOR_EACH_INTRINSIC_SIMD(F) \ + F(AllocateFloat32x4, 0, 1) \ + F(AllocateInt32x4, 0, 1) \ + F(AllocateBool32x4, 0, 1) \ F(IsSimdValue, 1, 1) \ F(CreateFloat32x4, 4, 1) \ F(CreateInt32x4, 4, 1) \ @@ -537,6 +540,14 @@ namespace internal { F(Float32x4Store1, 3, 1) \ F(Float32x4Store2, 3, 1) \ F(Float32x4Store3, 3, 1) \ + F(Float32x4LoadX, 2, 1) \ + F(Float32x4LoadXY, 2, 1) \ + F(Float32x4LoadXYZ, 2, 1) \ + F(Float32x4LoadXYZW, 2, 1) \ + F(Float32x4StoreX, 3, 1) \ + F(Float32x4StoreXY, 3, 1) \ + F(Float32x4StoreXYZ, 3, 1) \ + F(Float32x4StoreXYZW, 3, 1) \ F(Int32x4Check, 1, 1) \ F(Int32x4ExtractLane, 2, 1) \ F(Int32x4ReplaceLane, 3, 1) \ @@ -577,6 +588,14 @@ namespace internal { F(Int32x4Store1, 3, 1) \ F(Int32x4Store2, 3, 1) \ F(Int32x4Store3, 3, 1) \ + F(Int32x4LoadX, 2, 1) \ + F(Int32x4LoadXY, 2, 1) \ + F(Int32x4LoadXYZ, 2, 1) \ + F(Int32x4LoadXYZW, 2, 1) \ + F(Int32x4StoreX, 3, 1) \ + F(Int32x4StoreXY, 3, 1) \ + F(Int32x4StoreXYZ, 3, 1) \ + F(Int32x4StoreXYZW, 3, 1) \ F(Uint32x4Check, 1, 1) \ F(Uint32x4ExtractLane, 2, 1) \ F(Uint32x4ReplaceLane, 3, 1) \ diff --git a/src/types.cc b/src/types.cc index c978dac5c2c..73f04281108 100644 --- a/src/types.cc +++ b/src/types.cc @@ -230,6 +230,11 @@ Type::bitset BitsetType::Lub(i::Map* map) { case JS_BOUND_FUNCTION_TYPE: DCHECK(!map->is_undetectable()); return kOtherObject; + case FLOAT32x4_TYPE: + case INT32x4_TYPE: + case BOOL32x4_TYPE: + if (map->is_undetectable()) return kUndetectable; + return kOtherObject; case JS_FUNCTION_TYPE: DCHECK(!map->is_undetectable()); return kFunction; diff --git a/src/x64/assembler-x64-inl.h b/src/x64/assembler-x64-inl.h index 518df5a47cc..943650f8301 100644 --- a/src/x64/assembler-x64-inl.h +++ b/src/x64/assembler-x64-inl.h @@ -17,6 +17,7 @@ namespace internal { bool CpuFeatures::SupportsCrankshaft() { return true; } bool CpuFeatures::SupportsSimd128() { return false; } +bool CpuFeatures::SupportsSIMD128InCrankshaft() { return true; } // ----------------------------------------------------------------------------- // Implementation of Assembler diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index 9a0d18e9c2f..56d9f303b35 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -2703,6 +2703,77 @@ void Assembler::divps(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::addpd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x58); + emit_sse_operand(dst, src); +} + +void Assembler::addpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x58); + emit_sse_operand(dst, src); +} + +void Assembler::subpd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x5C); + emit_sse_operand(dst, src); +} + +void Assembler::subpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x5C); + emit_sse_operand(dst, src); +} + +void Assembler::mulpd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x59); + emit_sse_operand(dst, src); +} + +void Assembler::mulpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x59); + emit_sse_operand(dst, src); +} + +void Assembler::divpd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x5E); + emit_sse_operand(dst, src); +} + +void Assembler::divpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x5E); + emit_sse_operand(dst, src); +} // SSE 2 operations. @@ -2847,7 +2918,6 @@ void Assembler::pextrd(Register dst, XMMRegister src, int8_t imm8) { emit(imm8); } - void Assembler::pinsrd(XMMRegister dst, Register src, int8_t imm8) { DCHECK(IsEnabled(SSE4_1)); EnsureSpace ensure_space(this); @@ -2936,11 +3006,21 @@ void Assembler::movaps(XMMRegister dst, XMMRegister src) { } } - void Assembler::shufps(XMMRegister dst, XMMRegister src, byte imm8) { DCHECK(is_uint8(imm8)); EnsureSpace ensure_space(this); - emit_optional_rex_32(src, dst); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0xC6); + emit_sse_operand(dst, src); + emit(imm8); +} + +void Assembler::shufpd(XMMRegister dst, XMMRegister src, byte imm8) { + DCHECK(is_uint8(imm8)); + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); emit(0x0F); emit(0xC6); emit_sse_operand(dst, src); @@ -3632,7 +3712,6 @@ void Assembler::andpd(XMMRegister dst, XMMRegister src) { emit_sse_operand(dst, src); } - void Assembler::andpd(XMMRegister dst, const Operand& src) { EnsureSpace ensure_space(this); emit(0x66); @@ -3642,7 +3721,6 @@ void Assembler::andpd(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } - void Assembler::orpd(XMMRegister dst, XMMRegister src) { EnsureSpace ensure_space(this); emit(0x66); @@ -3652,7 +3730,6 @@ void Assembler::orpd(XMMRegister dst, XMMRegister src) { emit_sse_operand(dst, src); } - void Assembler::orpd(XMMRegister dst, const Operand& src) { EnsureSpace ensure_space(this); emit(0x66); @@ -3673,7 +3750,6 @@ void Assembler::xorpd(XMMRegister dst, XMMRegister src) { emit_sse_operand(dst, src); } - void Assembler::xorpd(XMMRegister dst, const Operand& src) { DCHECK(!IsEnabled(AVX)); EnsureSpace ensure_space(this); @@ -3754,6 +3830,51 @@ void Assembler::roundss(XMMRegister dst, XMMRegister src, RoundingMode mode) { emit(static_cast(mode) | 0x8); } +void Assembler::pslld(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0xF2); + emit_sse_operand(dst, src); +} + +void Assembler::psrld(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0xD2); + emit_sse_operand(dst, src); +} + +void Assembler::psrad(XMMRegister reg, int8_t shift) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(reg); + emit(0x0F); + emit(0x72); + emit_sse_operand(rsp, reg); // rsp == 4 + emit(shift); +} + +void Assembler::psrad(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0xE2); + emit_sse_operand(dst, src); +} + +void Assembler::pcmpgtd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x66); + emit_sse_operand(dst, src); +} void Assembler::roundsd(XMMRegister dst, XMMRegister src, RoundingMode mode) { DCHECK(!IsEnabled(AVX)); @@ -3996,6 +4117,14 @@ void Assembler::vucomiss(XMMRegister dst, XMMRegister src) { emit_sse_operand(dst, src); } +void Assembler::minpd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x5D); + emit_sse_operand(dst, src); +} void Assembler::vucomiss(XMMRegister dst, const Operand& src) { DCHECK(IsEnabled(AVX)); @@ -4005,6 +4134,14 @@ void Assembler::vucomiss(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::minpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x5D); + emit_sse_operand(dst, src); +} void Assembler::vss(byte op, XMMRegister dst, XMMRegister src1, XMMRegister src2) { @@ -4313,6 +4450,24 @@ void Assembler::maxps(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::maxpd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x5F); + emit_sse_operand(dst, src); +} + +void Assembler::maxpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x5F); + emit_sse_operand(dst, src); +} + void Assembler::rcpps(XMMRegister dst, XMMRegister src) { EnsureSpace ensure_space(this); emit_optional_rex_32(dst, src); @@ -4409,6 +4564,24 @@ void Assembler::movups(const Operand& dst, XMMRegister src) { emit_sse_operand(src, dst); } +void Assembler::sqrtpd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x51); + emit_sse_operand(dst, src); +} + +void Assembler::sqrtpd(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x51); + emit_sse_operand(dst, src); +} + void Assembler::paddd(XMMRegister dst, XMMRegister src) { EnsureSpace ensure_space(this); emit(0x66); @@ -4484,6 +4657,24 @@ void Assembler::pmuludq(XMMRegister dst, const Operand& src) { emit_sse_operand(dst, src); } +void Assembler::punpackldq(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x62); + emit_sse_operand(dst, src); +} + +void Assembler::punpackldq(XMMRegister dst, const Operand& src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0F); + emit(0x62); + emit_sse_operand(dst, src); +} + void Assembler::psrldq(XMMRegister dst, uint8_t shift) { EnsureSpace ensure_space(this); emit(0x66); diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index b2154fbaf4a..e1a216f5aa9 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -250,6 +250,8 @@ DOUBLE_REGISTERS(DECLARE_REGISTER) #undef DECLARE_REGISTER const DoubleRegister no_double_reg = {DoubleRegister::kCode_no_reg}; +typedef DoubleRegister SIMD128Register; + enum Condition { // any value < 0 is considered no_condition no_condition = -1, @@ -357,11 +359,11 @@ enum ScaleFactor { times_2 = 1, times_4 = 2, times_8 = 3, + maximal_scale_factor = times_8, times_int_size = times_4, times_pointer_size = (kPointerSize == 8) ? times_8 : times_4 }; - class Operand BASE_EMBEDDED { public: // [base + disp/r] @@ -1044,10 +1046,10 @@ class Assembler : public AssemblerBase { // Use movaps when moving float values and movd for integer // values in xmm registers. void movss(XMMRegister dst, XMMRegister src); - void movss(XMMRegister dst, const Operand& src); void movss(const Operand& dst, XMMRegister src); void shufps(XMMRegister dst, XMMRegister src, byte imm8); + void shufpd(XMMRegister dst, XMMRegister src, byte imm8); void cvttss2si(Register dst, const Operand& src); void cvttss2si(Register dst, XMMRegister src); @@ -1070,6 +1072,15 @@ class Assembler : public AssemblerBase { void divps(XMMRegister dst, XMMRegister src); void divps(XMMRegister dst, const Operand& src); + void addpd(XMMRegister dst, XMMRegister src); + void addpd(XMMRegister dst, const Operand& src); + void subpd(XMMRegister dst, XMMRegister src); + void subpd(XMMRegister dst, const Operand& src); + void mulpd(XMMRegister dst, XMMRegister src); + void mulpd(XMMRegister dst, const Operand& src); + void divpd(XMMRegister dst, XMMRegister src); + void divpd(XMMRegister dst, const Operand& src); + void movmskps(Register dst, XMMRegister src); // SSE2 instructions @@ -1166,6 +1177,16 @@ class Assembler : public AssemblerBase { // SSE 4.1 instruction void insertps(XMMRegister dst, XMMRegister src, byte imm8); void extractps(Register dst, XMMRegister src, byte imm8); + + void minpd(XMMRegister dst, XMMRegister src); + void minpd(XMMRegister dst, const Operand& src); + void maxpd(XMMRegister dst, XMMRegister src); + void maxpd(XMMRegister dst, const Operand& src); + void sqrtpd(XMMRegister dst, XMMRegister src); + void sqrtpd(XMMRegister dst, const Operand& src); + void punpackldq(XMMRegister dst, XMMRegister src); + void punpackldq(XMMRegister dst, const Operand& src); + void pextrd(Register dst, XMMRegister src, int8_t imm8); void pinsrd(XMMRegister dst, Register src, int8_t imm8); void pinsrd(XMMRegister dst, const Operand& src, int8_t imm8); @@ -1391,6 +1412,14 @@ class Assembler : public AssemblerBase { vsd(0x11, src, xmm0, dst); } + void pslld(XMMRegister dst, XMMRegister src); + void psrld(XMMRegister dst, XMMRegister src); + void psrad(XMMRegister reg, int8_t shift); + void psrad(XMMRegister dst, XMMRegister src); + + void pcmpgtd(XMMRegister dst, XMMRegister src); + void pcmpltd(XMMRegister dst, XMMRegister src); + #define AVX_SP_3(instr, opcode) \ AVX_S_3(instr, opcode) \ AVX_P_3(instr, opcode) diff --git a/src/x64/deoptimizer-x64.cc b/src/x64/deoptimizer-x64.cc index 35da7a2c00c..9e32f055b3d 100644 --- a/src/x64/deoptimizer-x64.cc +++ b/src/x64/deoptimizer-x64.cc @@ -97,11 +97,12 @@ void Deoptimizer::SetPlatformCompiledStubRegisters( output_frame->SetRegister(rbx.code(), handler); } +void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {} -void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) { +void Deoptimizer::CopySIMD128Registers(FrameDescription* output_frame) { for (int i = 0; i < XMMRegister::kMaxNumRegisters; ++i) { - double double_value = input_->GetDoubleRegister(i); - output_frame->SetDoubleRegister(i, double_value); + simd128_value_t xmm_value = input_->GetSIMD128Register(i); + output_frame->SetSIMD128Register(i, xmm_value); } } @@ -113,15 +114,15 @@ void Deoptimizer::TableEntryGenerator::Generate() { // Save all general purpose registers before messing with them. const int kNumberOfRegisters = Register::kNumRegisters; - const int kDoubleRegsSize = kDoubleSize * XMMRegister::kMaxNumRegisters; - __ subp(rsp, Immediate(kDoubleRegsSize)); + const int kXMMRegsSize = kSIMD128Size * XMMRegister::kMaxNumRegisters; + __ subp(rsp, Immediate(kXMMRegsSize)); const RegisterConfiguration* config = RegisterConfiguration::Crankshaft(); for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { int code = config->GetAllocatableDoubleCode(i); XMMRegister xmm_reg = XMMRegister::from_code(code); - int offset = code * kDoubleSize; - __ Movsd(Operand(rsp, offset), xmm_reg); + int offset = code * kSIMD128Size; + __ movups(Operand(rsp, offset), xmm_reg); } // We push all registers onto the stack, even though we do not need @@ -131,8 +132,8 @@ void Deoptimizer::TableEntryGenerator::Generate() { __ pushq(r); } - const int kSavedRegistersAreaSize = kNumberOfRegisters * kRegisterSize + - kDoubleRegsSize; + const int kSavedRegistersAreaSize = + kNumberOfRegisters * kRegisterSize + kXMMRegsSize; __ Store(ExternalReference(Isolate::kCEntryFPAddress, isolate()), rbp); @@ -189,11 +190,13 @@ void Deoptimizer::TableEntryGenerator::Generate() { __ PopQuad(Operand(rbx, offset)); } - // Fill in the double input registers. - int double_regs_offset = FrameDescription::double_registers_offset(); + // Fill in the xmm input registers. + STATIC_ASSERT(kSIMD128Size == 2 * kDoubleSize); + int xmm_regs_offset = FrameDescription::simd128_registers_offset(); for (int i = 0; i < XMMRegister::kMaxNumRegisters; i++) { - int dst_offset = i * kDoubleSize + double_regs_offset; + int dst_offset = i * kSIMD128Size + xmm_regs_offset; __ popq(Operand(rbx, dst_offset)); + __ popq(Operand(rbx, dst_offset + kDoubleSize)); } // Remove the bailout id and return address from the stack. @@ -260,8 +263,8 @@ void Deoptimizer::TableEntryGenerator::Generate() { for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { int code = config->GetAllocatableDoubleCode(i); XMMRegister xmm_reg = XMMRegister::from_code(code); - int src_offset = code * kDoubleSize + double_regs_offset; - __ Movsd(xmm_reg, Operand(rbx, src_offset)); + int src_offset = code * kSIMD128Size + xmm_regs_offset; + __ movups(xmm_reg, Operand(rbx, src_offset)); } // Push state, pc, and continuation from the last output frame. @@ -332,6 +335,33 @@ void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { UNREACHABLE(); } +double RegisterValues::GetDoubleRegister(unsigned n) const { + DCHECK(n < arraysize(simd128_registers_)); + return simd128_registers_[n].d[0]; +} + +void RegisterValues::SetDoubleRegister(unsigned n, double value) { + DCHECK(n < arraysize(simd128_registers_)); + simd128_registers_[n].d[0] = value; +} + +simd128_value_t RegisterValues::GetSIMD128Register(unsigned n) const { + DCHECK(n < arraysize(simd128_registers_)); + return simd128_registers_[n]; +} + +void RegisterValues::SetSIMD128Register(unsigned n, simd128_value_t value) { + DCHECK(n < arraysize(simd128_registers_)); + simd128_registers_[n] = value; +} + +int FrameDescription::double_registers_offset() { + return OFFSET_OF(FrameDescription, register_values_.simd128_registers_); +} + +int FrameDescription::simd128_registers_offset() { + return OFFSET_OF(FrameDescription, register_values_.simd128_registers_); +} #undef __ diff --git a/src/x64/disasm-x64.cc b/src/x64/disasm-x64.cc index 83f34d07a0b..cb28a283440 100644 --- a/src/x64/disasm-x64.cc +++ b/src/x64/disasm-x64.cc @@ -318,6 +318,17 @@ class DisassemblerX64 { OPERAND_QUADWORD_SIZE = 3 }; + enum { + rax = 0, + rcx = 1, + rdx = 2, + rbx = 3, + rsp = 4, + rbp = 5, + rsi = 6, + rdi = 7 + }; + const NameConverter& converter_; v8::internal::EmbeddedVector tmp_buffer_; unsigned int tmp_buffer_pos_; @@ -1579,6 +1590,18 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { current += PrintRightXMMOperand(current); AppendToBuffer(",0x%x", (*current) & 3); current += 1; + } else if (third_byte == 0x21) { + get_modrm(*current, &mod, ®op, &rm); + // insertps xmm, xmm, imm8 + AppendToBuffer("insertps %s,%s,%d", NameOfXMMRegister(regop), + NameOfXMMRegister(rm), (*(current + 1)) & 3); + current += 2; + } else if (third_byte == 0x22) { + get_modrm(*current, &mod, ®op, &rm); + // pinsrd xmm, reg32, imm8 + AppendToBuffer("pinsrd %s,%s,%d", NameOfXMMRegister(regop), + NameOfCPURegister(rm), (*(current + 1)) & 3); + current += 2; } else if (third_byte == 0x0b) { get_modrm(*current, &mod, ®op, &rm); // roundsd xmm, xmm/m64, imm8 @@ -1609,6 +1632,16 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { } else { UnimplementedInstruction(); } + } else if (opcode == 0x38) { + byte third_byte = *current; + current = data + 3; + if (third_byte == 0x40) { + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("pmulld %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + } else { + UnimplementedInstruction(); + } } else { get_modrm(*current, &mod, ®op, &rm); if (opcode == 0x1f) { @@ -1645,6 +1678,20 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { AppendToBuffer("movdqa %s,", NameOfXMMRegister(regop)); current += PrintRightXMMOperand(current); + } else if (opcode == 0x70) { + AppendToBuffer("pshufd %s,", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + AppendToBuffer(",0x%x", (*current) & 0xff); + current += 1; + } else if (opcode == 0x5B) { + AppendToBuffer("cvtps2dq %s,", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + } else if (opcode == 0xFE) { + AppendToBuffer("paddd %s,", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + } else if (opcode == 0xFA) { + AppendToBuffer("psubd %s,", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); } else if (opcode == 0x7E) { AppendToBuffer("mov%c ", rex_w() ? 'q' : 'd'); @@ -1678,9 +1725,28 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { current += 1; } else if (opcode == 0xB1) { current += PrintOperands("cmpxchg", OPER_REG_OP_ORDER, current); + } else if (opcode == 0x62) { + AppendToBuffer("punpackldq %s,", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + } else if (opcode == 0x72) { + AppendToBuffer(regop == rsi ? "pslld " : regop == rdx ? "psrld" + : "psrad"); + current += PrintRightXMMOperand(current); + AppendToBuffer(",0x%x", (*current) & 0xff); + current += 1; + } else if (opcode == 0xC6) { + AppendToBuffer("shufpd %s,", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + AppendToBuffer(",0x%x", (*current) & 0xff); + current += 1; + } else if (opcode == 0xF4) { + AppendToBuffer("pmuludq %s,", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); } else { const char* mnemonic = "?"; - if (opcode == 0x54) { + if (opcode == 0x51) { + mnemonic = "sqrtpd"; + } else if (opcode == 0x54) { mnemonic = "andpd"; } else if (opcode == 0x56) { mnemonic = "orpd"; @@ -1688,6 +1754,18 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { mnemonic = "xorpd"; } else if (opcode == 0x5B) { mnemonic = "cvtps2dq"; + } else if (opcode == 0x58) { + mnemonic = "addpd"; + } else if (opcode == 0x59) { + mnemonic = "mulpd"; + } else if (opcode == 0x5C) { + mnemonic = "subpd"; + } else if (opcode == 0x5D) { + mnemonic = "minpd"; + } else if (opcode == 0x5E) { + mnemonic = "divpd"; + } else if (opcode == 0x5F) { + mnemonic = "maxpd"; } else if (opcode == 0x2E) { mnemonic = "ucomisd"; } else if (opcode == 0x2F) { @@ -1706,6 +1784,14 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { mnemonic = "paddd"; } else if (opcode == 0xC2) { mnemonic = "cmppd"; + } else if (opcode == 0x66) { + mnemonic = "pcmpgtd"; + } else if (opcode == 0xD2) { + mnemonic = "psrld"; + } else if (opcode == 0xE2) { + mnemonic = "psrad"; + } else if (opcode == 0xF2) { + mnemonic = "pslld"; } else { UnimplementedInstruction(); } @@ -1901,6 +1987,22 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { get_modrm(*current, &mod, ®op, &rm); AppendToBuffer("ucomiss %s,", NameOfXMMRegister(regop)); current += PrintRightXMMOperand(current); + + } else if (opcode == 0x10) { + // movups xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("movups %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x11) { + // movups xmm/m128, xmm + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("movups "); + current += PrintRightXMMOperand(current); + AppendToBuffer(", %s", NameOfXMMRegister(regop)); + } else if (opcode == 0xA2) { // CPUID AppendToBuffer("%s", mnemonic); @@ -1942,6 +2044,91 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { current += PrintRightXMMOperand(current); AppendToBuffer(", %d", (*current) & 3); current += 1; + + } else if (opcode == 0x54) { + // andps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("andps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x56) { + // orps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("orps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x58) { + // addps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("addps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x59) { + // mulps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("mulps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x5C) { + // subps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("subps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x5E) { + // divps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("divps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x5D) { + // minps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("minps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x5F) { + // maxps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("maxps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x5B) { + // cvtdq2ps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("cvtdq2ps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x53) { + // rcpps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("rcpps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x52) { + // rsqrtps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("rsqrtps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + + } else if (opcode == 0x51) { + // sqrtps xmm, xmm/m128 + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("sqrtps %s, ", NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + } else if (opcode == 0x50) { // movmskps reg, xmm int mod, regop, rm; @@ -1949,6 +2136,17 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { AppendToBuffer("movmskps %s,", NameOfCPURegister(regop)); current += PrintRightXMMOperand(current); + } else if (opcode == 0xC2) { + // Intel manual 2A, Table 3-11. + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + const char* const pseudo_op[] = {"cmpeqps", "cmpltps", "cmpleps", + "cmpunordps", "cmpneqps", "cmpnltps", + "cmpnleps", "cmpordps"}; + AppendToBuffer("%s %s,%s", pseudo_op[current[1]], NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + current += 2; + } else if ((opcode & 0xF0) == 0x80) { // Jcc: Conditional jump (branch). current = data + JumpConditional(data); @@ -1992,8 +2190,7 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { UnimplementedInstruction(); } return static_cast(current - data); -} - +} // NOLINT(readability/fn_size) // Mnemonics for two-byte opcode instructions starting with 0x0F. // The argument is the second byte of the two-byte opcode. diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index 6dacc011df3..71d46a6be47 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -776,10 +776,10 @@ void MacroAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, } // R12 to r15 are callee save on all platforms. if (fp_mode == kSaveFPRegs) { - subp(rsp, Immediate(kDoubleSize * XMMRegister::kMaxNumRegisters)); + subp(rsp, Immediate(kSIMD128Size * XMMRegister::kMaxNumRegisters)); for (int i = 0; i < XMMRegister::kMaxNumRegisters; i++) { XMMRegister reg = XMMRegister::from_code(i); - Movsd(Operand(rsp, i * kDoubleSize), reg); + movups(Operand(rsp, i * kSIMD128Size), reg); } } } @@ -792,9 +792,9 @@ void MacroAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, if (fp_mode == kSaveFPRegs) { for (int i = 0; i < XMMRegister::kMaxNumRegisters; i++) { XMMRegister reg = XMMRegister::from_code(i); - Movsd(reg, Operand(rsp, i * kDoubleSize)); + movups(reg, Operand(rsp, i * kSIMD128Size)); } - addp(rsp, Immediate(kDoubleSize * XMMRegister::kMaxNumRegisters)); + addp(rsp, Immediate(kSIMD128Size * XMMRegister::kMaxNumRegisters)); } for (int i = kNumberOfSavedRegs - 1; i >= 0; i--) { Register reg = saved_regs[i]; @@ -2520,6 +2520,70 @@ void MacroAssembler::Test(const Operand& src, Smi* source) { // ---------------------------------------------------------------------------- +void MacroAssembler::absps(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } float_absolute_constant = {0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF}; + Set(kScratchRegister, reinterpret_cast(&float_absolute_constant)); + andps(dst, Operand(kScratchRegister, 0)); +} + +void MacroAssembler::abspd(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint64_t a; + uint64_t b; + } double_absolute_constant = {V8_UINT64_C(0x7FFFFFFFFFFFFFFF), + V8_UINT64_C(0x7FFFFFFFFFFFFFFF)}; + Set(kScratchRegister, reinterpret_cast(&double_absolute_constant)); + andpd(dst, Operand(kScratchRegister, 0)); +} + +void MacroAssembler::negateps(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } float_negate_constant = {0x80000000, 0x80000000, 0x80000000, 0x80000000}; + Set(kScratchRegister, reinterpret_cast(&float_negate_constant)); + xorps(dst, Operand(kScratchRegister, 0)); +} + +void MacroAssembler::negatepd(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint64_t a; + uint64_t b; + } double_absolute_constant = {V8_UINT64_C(0x8000000000000000), + V8_UINT64_C(0x8000000000000000)}; + Set(kScratchRegister, reinterpret_cast(&double_absolute_constant)); + xorpd(dst, Operand(kScratchRegister, 0)); +} + +void MacroAssembler::notps(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } float_not_constant = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; + Set(kScratchRegister, reinterpret_cast(&float_not_constant)); + xorps(dst, Operand(kScratchRegister, 0)); +} + +void MacroAssembler::pnegd(XMMRegister dst) { + static const struct V8_ALIGNED(16) { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } int32_one_constant = {0x1, 0x1, 0x1, 0x1}; + notps(dst); + Set(kScratchRegister, reinterpret_cast(&int32_one_constant)); + paddd(dst, Operand(kScratchRegister, 0)); +} void MacroAssembler::JumpIfNotString(Register object, Register object_map, @@ -4583,7 +4647,7 @@ void MacroAssembler::EnterExitFrameEpilogue(int arg_stack_space, #endif // Optionally save all XMM registers. if (save_doubles) { - int space = XMMRegister::kMaxNumRegisters * kDoubleSize + + int space = XMMRegister::kMaxNumRegisters * kSIMD128Size + arg_stack_space * kRegisterSize; subp(rsp, Immediate(space)); int offset = -ExitFrameConstants::kFixedFrameSizeFromFp; @@ -4591,7 +4655,7 @@ void MacroAssembler::EnterExitFrameEpilogue(int arg_stack_space, for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { DoubleRegister reg = DoubleRegister::from_code(config->GetAllocatableDoubleCode(i)); - Movsd(Operand(rbp, offset - ((i + 1) * kDoubleSize)), reg); + movups(Operand(rbp, offset - ((i + 1) * kSIMD128Size)), reg); } } else if (arg_stack_space > 0) { subp(rsp, Immediate(arg_stack_space * kRegisterSize)); @@ -4637,7 +4701,7 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles, bool pop_arguments) { for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { DoubleRegister reg = DoubleRegister::from_code(config->GetAllocatableDoubleCode(i)); - Movsd(reg, Operand(rbp, offset - ((i + 1) * kDoubleSize))); + movups(reg, Operand(rbp, offset - ((i + 1) * kSIMD128Size))); } } @@ -5144,6 +5208,27 @@ void MacroAssembler::AllocateHeapNumber(Register result, movp(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister); } +#define SIMD128_HEAP_ALLOCATE_FUNCTIONS(V) \ + V(Float32x4, float32x4) \ + V(Bool32x4, bool32x4) \ + V(Int32x4, int32x4) + +#define DECLARE_SIMD_HEAP_ALLOCATE_FUNCTION(Type, type) \ + void MacroAssembler::Allocate##Type(Register result, Register scratch1, \ + Register scratch2, Register scratch3, \ + Label* gc_required) { \ + /* Allocate SIMD128 object.*/ \ + Allocate(Type::kSize, result, scratch1, no_reg, gc_required, \ + NO_ALLOCATION_FLAGS); \ + \ + Heap::RootListIndex map_index = Heap::k##Type##MapRootIndex; \ + \ + /* Set the map. */ \ + LoadRoot(kScratchRegister, map_index); \ + movp(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister); \ + } + +SIMD128_HEAP_ALLOCATE_FUNCTIONS(DECLARE_SIMD_HEAP_ALLOCATE_FUNCTION) void MacroAssembler::AllocateTwoByteString(Register result, Register length, diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index d5e411f36f4..b644426a67c 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -772,6 +772,15 @@ class MacroAssembler: public Assembler { void Test(const Operand& dst, Smi* source); + // --------------------------------------------------------------------------- + // SIMD macros. + void absps(XMMRegister dst); + void abspd(XMMRegister dst); + void negateps(XMMRegister dst); + void negatepd(XMMRegister dst); + void notps(XMMRegister dst); + void pnegd(XMMRegister dst); + // --------------------------------------------------------------------------- // String macros. @@ -1355,6 +1364,19 @@ class MacroAssembler: public Assembler { Label* gc_required, MutableMode mode = IMMUTABLE); + // Allocate a float32x4, bool32x4 and int32x4 object in new space with + // undefined value. + // Returns tagged pointer in result register, or jumps to gc_required if new + // space is full. + void AllocateFloat32x4(Register result, Register scratch1, Register scratch2, + Register scratch3, Label* gc_required); + + void AllocateBool32x4(Register result, Register scratch1, Register scratch2, + Register scratch3, Label* gc_required); + + void AllocateInt32x4(Register result, Register scratch1, Register scratch2, + Register scratch3, Label* gc_required); + // Allocate a sequential string. All the header fields of the string object // are initialized. void AllocateTwoByteString(Register result, diff --git a/test/cctest/test-disasm-ia32.cc b/test/cctest/test-disasm-ia32.cc index 88471a26c85..2fa1157ba16 100644 --- a/test/cctest/test-disasm-ia32.cc +++ b/test/cctest/test-disasm-ia32.cc @@ -472,6 +472,83 @@ TEST(DisasmIa320) { __ punpckldq(xmm1, xmm6); __ punpckhdq(xmm7, xmm5); } + { + __ cvttss2si(edx, Operand(ebx, ecx, times_4, 10000)); + __ cvtsi2sd(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ movsd(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ movsd(Operand(ebx, ecx, times_4, 10000), xmm1); + // 128 bit move instructions. + __ movdqa(xmm0, Operand(ebx, ecx, times_4, 10000)); + __ movdqa(Operand(ebx, ecx, times_4, 10000), xmm0); + __ movdqu(xmm0, Operand(ebx, ecx, times_4, 10000)); + __ movdqu(Operand(ebx, ecx, times_4, 10000), xmm0); + + __ addsd(xmm1, xmm0); + __ mulsd(xmm1, xmm0); + __ subsd(xmm1, xmm0); + __ divsd(xmm1, xmm0); + __ ucomisd(xmm0, xmm1); + __ cmpltsd(xmm0, xmm1); + + __ andpd(xmm0, xmm1); + __ psllq(xmm0, 17); + __ psllq(xmm0, xmm1); + __ psrlq(xmm0, 17); + __ psrlq(xmm0, xmm1); + __ por(xmm0, xmm1); + + // new instruction introduced by SIMD + __ cvtdq2ps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ cvtdq2ps(xmm1, xmm0); + __ cvtps2dq(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ cvtps2dq(xmm1, xmm0); + __ paddd(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ paddd(xmm1, xmm0); + __ psubd(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ psubd(xmm1, xmm0); + __ pmuludq(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ pmuludq(xmm1, xmm0); + __ punpackldq(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ punpackldq(xmm1, xmm0); + { + __ shufps(xmm1, xmm1, 0x0); + __ movups(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ movups(Operand(ebx, ecx, times_4, 10000), xmm1); + + __ andps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ andps(xmm1, xmm0); + __ xorps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ xorps(xmm1, xmm0); + __ orps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ orps(xmm1, xmm0); + + __ addps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ addps(xmm1, xmm0); + __ subps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ subps(xmm1, xmm0); + __ mulps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ mulps(xmm1, xmm0); + __ divps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ divps(xmm1, xmm0); + __ minps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ minps(xmm1, xmm0); + __ maxps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ maxps(xmm1, xmm0); + __ rcpps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ rcpps(xmm1, xmm0); + __ rsqrtps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ rsqrtps(xmm1, xmm0); + __ sqrtps(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ sqrtps(xmm1, xmm0); + + __ cmpeqps(xmm1, xmm0); + __ cmpltps(xmm1, xmm0); + __ cmpleps(xmm1, xmm0); + __ cmpneqps(xmm1, xmm0); + __ cmpnltps(xmm1, xmm0); + __ cmpnleps(xmm1, xmm0); + } + } // cmov. { @@ -499,6 +576,9 @@ TEST(DisasmIa320) { __ pextrd(eax, xmm0, 1); __ pinsrd(xmm1, eax, 0); __ extractps(eax, xmm1, 0); + __ insertps(xmm1, xmm0, 0); + __ pmulld(xmm1, Operand(ebx, ecx, times_4, 10000)); + __ pmulld(xmm1, xmm0); } } diff --git a/test/mjsunit/harmony/int32x4.js b/test/mjsunit/harmony/int32x4.js new file mode 100644 index 00000000000..9d26d7ee416 --- /dev/null +++ b/test/mjsunit/harmony/int32x4.js @@ -0,0 +1,425 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd --harmony-tostring --harmony-reflect +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt + +function testConstructor() { + var u4 = SIMD.Int32x4(1, 2, 3, 4); + assertEquals(1, SIMD.Int32x4.extractLane(u4, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(u4, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(u4, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(u4, 3)); +} + +testConstructor(); + +function testCheck() { + var u4 = SIMD.Int32x4(1, 2, 3, 4); + var u4_new = SIMD.Int32x4.check(u4); + assertEquals(SIMD.Int32x4.extractLane(u4_new, 0), SIMD.Int32x4.extractLane(u4, 0)); + assertEquals(SIMD.Int32x4.extractLane(u4_new, 1), SIMD.Int32x4.extractLane(u4, 1)); + assertEquals(SIMD.Int32x4.extractLane(u4_new, 2), SIMD.Int32x4.extractLane(u4, 2)); + assertEquals(SIMD.Int32x4.extractLane(u4_new, 3), SIMD.Int32x4.extractLane(u4, 3)); +} + +testCheck(); +testCheck(); +%OptimizeFunctionOnNextCall(testCheck); +testCheck(); + +function testSplatConstructor() { + var u4 = SIMD.Int32x4.splat(4); + assertEquals(4, SIMD.Int32x4.extractLane(u4, 0)); + assertEquals(4, SIMD.Int32x4.extractLane(u4, 1)); + assertEquals(4, SIMD.Int32x4.extractLane(u4, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(u4, 3)); +} + +testSplatConstructor(); +testSplatConstructor(); +%OptimizeFunctionOnNextCall(testSplatConstructor); +testSplatConstructor(); +/* +function testTypeof() { + var u4 = SIMD.Int32x4(1, 2, 3, 4); + assertEquals(typeof(u4), "object"); + + var new_u4 = new SIMD.Int32x4(1, 2, 3, 4); + assertEquals(typeof(new_u4), "object"); + assertEquals(typeof(new_u4.valueOf()), "object"); + assertEquals(Object.prototype.toString.call(new_u4), "[object Object]"); +} + +testTypeof(); +*/ + +function testSIMDAnd() { + var m = SIMD.Int32x4(0xAAAAAAAA - 0xFFFFFFFF - 1, 0xAAAAAAAA - 0xFFFFFFFF - 1, + 0xAAAAAAAA - 0xFFFFFFFF - 1, 0xAAAAAAAA - 0xFFFFFFFF - 1); + var n = SIMD.Int32x4(0x55555555, 0x55555555, 0x55555555, 0x55555555); + assertEquals(0xAAAAAAAA - 0xFFFFFFFF - 1, SIMD.Int32x4.extractLane(m, 0)); + assertEquals(0xAAAAAAAA - 0xFFFFFFFF - 1, SIMD.Int32x4.extractLane(m, 1)); + assertEquals(0xAAAAAAAA - 0xFFFFFFFF - 1, SIMD.Int32x4.extractLane(m, 2)); + assertEquals(0xAAAAAAAA - 0xFFFFFFFF - 1, SIMD.Int32x4.extractLane(m, 3)); + assertEquals(0x55555555, SIMD.Int32x4.extractLane(n, 0)); + assertEquals(0x55555555, SIMD.Int32x4.extractLane(n, 1)); + assertEquals(0x55555555, SIMD.Int32x4.extractLane(n, 2)); + assertEquals(0x55555555, SIMD.Int32x4.extractLane(n, 3)); +} + +testSIMDAnd(); +testSIMDAnd(); +%OptimizeFunctionOnNextCall(testSIMDAnd); +testSIMDAnd(); + +function testSIMDOr() { + var m = SIMD.Int32x4(0xAAAAAAAA - 0xFFFFFFFF - 1, 0xAAAAAAAA - 0xFFFFFFFF - 1, + 0xAAAAAAAA - 0xFFFFFFFF - 1, 0xAAAAAAAA - 0xFFFFFFFF - 1); + var n = SIMD.Int32x4(0x55555555, 0x55555555, 0x55555555, 0x55555555); + var o = SIMD.Int32x4.or(m,n); // or + assertEquals(-1, SIMD.Int32x4.extractLane(o, 0)); + assertEquals(-1, SIMD.Int32x4.extractLane(o, 1)); + assertEquals(-1, SIMD.Int32x4.extractLane(o, 2)); + assertEquals(-1, SIMD.Int32x4.extractLane(o, 3)); +} + +testSIMDOr(); +testSIMDOr(); +%OptimizeFunctionOnNextCall(testSIMDOr); +testSIMDOr(); + +function testSIMDInt32x4Or() { + var m = SIMD.Int32x4(0xAAAAAAAA - 0xFFFFFFFF - 1, 0xAAAAAAAA - 0xFFFFFFFF - 1, + 0xAAAAAAAA - 0xFFFFFFFF - 1, 0xAAAAAAAA - 0xFFFFFFFF - 1); + var n = SIMD.Int32x4(0xAAAAAAAA - 0xFFFFFFFF - 1, 0xAAAAAAAA - 0xFFFFFFFF - 1, + 0xAAAAAAAA - 0xFFFFFFFF - 1, 0xAAAAAAAA - 0xFFFFFFFF - 1); + var o = SIMD.Int32x4.xor(m,n); // xor + assertEquals(0x0, SIMD.Int32x4.extractLane(o, 0)); + assertEquals(0x0, SIMD.Int32x4.extractLane(o, 1)); + assertEquals(0x0, SIMD.Int32x4.extractLane(o, 2)); + assertEquals(0x0, SIMD.Int32x4.extractLane(o, 3)); +} + +testSIMDInt32x4Or(); +testSIMDInt32x4Or(); +%OptimizeFunctionOnNextCall(testSIMDInt32x4Or); +testSIMDInt32x4Or(); + +function testSIMDNot() { + var m = SIMD.Int32x4(0xAAAAAAAA - 0xFFFFFFFF - 1, 0xAAAAAAAA - 0xFFFFFFFF - 1, + 0xAAAAAAAA - 0xFFFFFFFF - 1, 0xAAAAAAAA - 0xFFFFFFFF - 1); + var n = SIMD.Int32x4(0x55555555, 0x55555555, 0x55555555, 0x55555555); + m = SIMD.Int32x4.not(m); + n = SIMD.Int32x4.not(n); + assertEquals(0xAAAAAAAA - 0xFFFFFFFF - 1, SIMD.Int32x4.extractLane(n, 0)); + assertEquals(0xAAAAAAAA - 0xFFFFFFFF - 1, SIMD.Int32x4.extractLane(n, 1)); + assertEquals(0xAAAAAAAA - 0xFFFFFFFF - 1, SIMD.Int32x4.extractLane(n, 2)); + assertEquals(0xAAAAAAAA - 0xFFFFFFFF - 1, SIMD.Int32x4.extractLane(n, 3)); + assertEquals(0x55555555, SIMD.Int32x4.extractLane(m, 0)); + assertEquals(0x55555555, SIMD.Int32x4.extractLane(m, 1)); + assertEquals(0x55555555, SIMD.Int32x4.extractLane(m, 2)); + assertEquals(0x55555555, SIMD.Int32x4.extractLane(m, 3)); +} + +testSIMDNot(); +testSIMDNot(); +%OptimizeFunctionOnNextCall(testSIMDNot); +testSIMDNot(); + +function testSIMDNegu32() { + var m = SIMD.Int32x4(-1, 1, -1, 1); + m = SIMD.Int32x4.neg(m); + assertEquals(1, SIMD.Int32x4.extractLane(m, 0)); + assertEquals(-1, SIMD.Int32x4.extractLane(m, 1)); + assertEquals(1, SIMD.Int32x4.extractLane(m, 2)); + assertEquals(-1, SIMD.Int32x4.extractLane(m, 3)); +} + +testSIMDNegu32(); +testSIMDNegu32(); +%OptimizeFunctionOnNextCall(testSIMDNegu32); +testSIMDNegu32(); + +function testSIMDReplaceLaneXu32() { + var a = SIMD.Int32x4(1, 2, 3, 4); + var c = SIMD.Int32x4.replaceLane(a, 0, 20); + assertEquals(20, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDReplaceLaneXu32(); +testSIMDReplaceLaneXu32(); +%OptimizeFunctionOnNextCall(testSIMDReplaceLaneXu32); +testSIMDReplaceLaneXu32(); + +function testSIMDReplaceLaneYu32() { + var a = SIMD.Int32x4(1, 2, 3, 4); + var c = SIMD.Int32x4.replaceLane(a, 1, 20); + assertEquals(1, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(20, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDReplaceLaneYu32(); +testSIMDReplaceLaneYu32(); +%OptimizeFunctionOnNextCall(testSIMDReplaceLaneYu32); +testSIMDReplaceLaneYu32(); + +function testSIMDReplaceLaneZu32() { + var a = SIMD.Int32x4(1, 2, 3, 4); + var c = SIMD.Int32x4.replaceLane(a, 2, 20); + assertEquals(1, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(20, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDReplaceLaneZu32(); +testSIMDReplaceLaneZu32(); +%OptimizeFunctionOnNextCall(testSIMDReplaceLaneZu32); +testSIMDReplaceLaneZu32(); + +function testSIMDReplaceLaneWu32() { + var a = SIMD.Int32x4(1, 2, 3, 4); + var c = SIMD.Int32x4.replaceLane(a, 3, 20); + assertEquals(1, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(20, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDReplaceLaneWu32(); +testSIMDReplaceLaneWu32(); +%OptimizeFunctionOnNextCall(testSIMDReplaceLaneWu32); +testSIMDReplaceLaneWu32(); + +function testSIMDAddu32() { + var a = SIMD.Int32x4(-1, -1, 0x7fffffff, 0x0); + var b = SIMD.Int32x4(0x1, -1, 0x1, -1); + var c = SIMD.Int32x4.add(a, b); + assertEquals(0x0, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(-2, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(0x80000000 - 0xFFFFFFFF - 1, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(-1, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDAddu32(); +testSIMDAddu32(); +%OptimizeFunctionOnNextCall(testSIMDAddu32); +testSIMDAddu32(); + +function testSIMDSubu32() { + var a = SIMD.Int32x4(-1, -1, 0x80000000 - 0xFFFFFFFF - 1, 0x0); + var b = SIMD.Int32x4(0x1, -1, 0x1, -1); + var c = SIMD.Int32x4.sub(a, b); + assertEquals(-2, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(0x0, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(0x7FFFFFFF, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(0x1, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDSubu32(); +testSIMDSubu32(); +%OptimizeFunctionOnNextCall(testSIMDSubu32); +testSIMDSubu32(); + +function testSIMDMulu32() { + var a = SIMD.Int32x4(-1, -1, 0x80000000 - 0xFFFFFFFF - 1, 0x0); + var b = SIMD.Int32x4(0x1, -1, 0x80000000 - 0xFFFFFFFF - 1, -1); + var c = SIMD.Int32x4.mul(a, b); + assertEquals(-1, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(0x1, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(0x0, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(0x0, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDMulu32(); +testSIMDMulu32(); +%OptimizeFunctionOnNextCall(testSIMDMulu32); +testSIMDMulu32(); + +function testSIMDSwizzleu32() { + var m = SIMD.Int32x4(1, 2, 3, 4); + var xxxx = SIMD.Int32x4.swizzle(m, 0, 0, 0, 0); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 0)); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 1)); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 2)); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 3)); + var yyyy = SIMD.Int32x4.swizzle(m, 1, 1, 1, 1); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 1)); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 2)); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 3)); + var zzzz = SIMD.Int32x4.swizzle(m, 2, 2, 2, 2); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 0)); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 2)); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 3)); + var wwww = SIMD.Int32x4.swizzle(m, 3, 3, 3, 3); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 0)); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 1)); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 3)); + var wzyx = SIMD.Int32x4.swizzle(m, 3, 2, 1, 0); + assertEquals(4, SIMD.Int32x4.extractLane(wzyx, 0)); + assertEquals(3, SIMD.Int32x4.extractLane(wzyx, 1)); + assertEquals(2, SIMD.Int32x4.extractLane(wzyx, 2)); + assertEquals(1, SIMD.Int32x4.extractLane(wzyx, 3)); + var wwzz = SIMD.Int32x4.swizzle(m, 3, 3, 2, 2); + assertEquals(4, SIMD.Int32x4.extractLane(wwzz, 0)); + assertEquals(4, SIMD.Int32x4.extractLane(wwzz, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(wwzz, 2)); + assertEquals(3, SIMD.Int32x4.extractLane(wwzz, 3)); + var xxyy = SIMD.Int32x4.swizzle(m, 0, 0, 1, 1); + assertEquals(1, SIMD.Int32x4.extractLane(xxyy, 0)); + assertEquals(1, SIMD.Int32x4.extractLane(xxyy, 1)); + assertEquals(2, SIMD.Int32x4.extractLane(xxyy, 2)); + assertEquals(2, SIMD.Int32x4.extractLane(xxyy, 3)); + var yyww = SIMD.Int32x4.swizzle(m, 1, 1, 3, 3); + assertEquals(2, SIMD.Int32x4.extractLane(yyww, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(yyww, 1)); + assertEquals(4, SIMD.Int32x4.extractLane(yyww, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(yyww, 3)); +} + +testSIMDSwizzleu32(); +testSIMDSwizzleu32(); +%OptimizeFunctionOnNextCall(testSIMDSwizzleu32); +testSIMDSwizzleu32(); + +function testSIMDShuffle() { + var a = SIMD.Int32x4(1, 2, 3, 4); + var b = SIMD.Int32x4(5, 6, 7, 8); + var xxxx = SIMD.Int32x4.shuffle(a, b, 0, 0, 4, 4); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 0)); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 1)); + assertEquals(5, SIMD.Int32x4.extractLane(xxxx, 2)); + assertEquals(5, SIMD.Int32x4.extractLane(xxxx, 3)); + var yyyy = SIMD.Int32x4.shuffle(a, b, 1, 1, 5, 5); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 1)); + assertEquals(6, SIMD.Int32x4.extractLane(yyyy, 2)); + assertEquals(6, SIMD.Int32x4.extractLane(yyyy, 3)); + var zzzz = SIMD.Int32x4.shuffle(a, b, 2, 2, 6, 6); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 0)); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 1)); + assertEquals(7, SIMD.Int32x4.extractLane(zzzz, 2)); + assertEquals(7, SIMD.Int32x4.extractLane(zzzz, 3)); + var wwww = SIMD.Int32x4.shuffle(a, b, 3, 3, 7, 7); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 0)); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 1)); + assertEquals(8, SIMD.Int32x4.extractLane(wwww, 2)); + assertEquals(8, SIMD.Int32x4.extractLane(wwww, 3)); + var wzyx = SIMD.Int32x4.shuffle(a, b, 3, 2, 5, 4); + assertEquals(4, SIMD.Int32x4.extractLane(wzyx, 0)); + assertEquals(3, SIMD.Int32x4.extractLane(wzyx, 1)); + assertEquals(6, SIMD.Int32x4.extractLane(wzyx, 2)); + assertEquals(5, SIMD.Int32x4.extractLane(wzyx, 3)); + var wwzz = SIMD.Int32x4.shuffle(a, b, 3, 3, 6, 6); + assertEquals(4, SIMD.Int32x4.extractLane(wwzz, 0)); + assertEquals(4, SIMD.Int32x4.extractLane(wwzz, 1)); + assertEquals(7, SIMD.Int32x4.extractLane(wwzz, 2)); + assertEquals(7, SIMD.Int32x4.extractLane(wwzz, 3)); + var xxyy = SIMD.Int32x4.shuffle(a, b, 0, 0, 5, 5); + assertEquals(1, SIMD.Int32x4.extractLane(xxyy, 0)); + assertEquals(1, SIMD.Int32x4.extractLane(xxyy, 1)); + assertEquals(6, SIMD.Int32x4.extractLane(xxyy, 2)); + assertEquals(6, SIMD.Int32x4.extractLane(xxyy, 3)); + var yyww = SIMD.Int32x4.shuffle(a, b, 1, 1, 7, 7); + assertEquals(2, SIMD.Int32x4.extractLane(yyww, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(yyww, 1)); + assertEquals(8, SIMD.Int32x4.extractLane(yyww, 2)); + assertEquals(8, SIMD.Int32x4.extractLane(yyww, 3)); +} + +testSIMDShuffle(); +testSIMDShuffle(); +%OptimizeFunctionOnNextCall(testSIMDShuffle); +testSIMDShuffle(); + +function testSIMDShift() { + var m = SIMD.Int32x4(1, 2, 100, 0); + + var a = SIMD.Int32x4.shiftLeftByScalar(m, 2); + assertEquals(4, SIMD.Int32x4.extractLane(a, 0)); + assertEquals(8, SIMD.Int32x4.extractLane(a, 1)); + assertEquals(400, SIMD.Int32x4.extractLane(a, 2)); + assertEquals(0, SIMD.Int32x4.extractLane(a, 3)); + + var n = SIMD.Int32x4(-8, 2, 1, 100); + + var c = SIMD.Int32x4.shiftRightByScalar(n, 2); + assertEquals(-2, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(0, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(0, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(25, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDShift(); +testSIMDShift(); +%OptimizeFunctionOnNextCall(testSIMDShift); +testSIMDShift(); + +function testSIMDExtractLane() { + var m = SIMD.Int32x4(1, 2, 3, 4); + var x = SIMD.Int32x4.extractLane(m, 0); + var y = SIMD.Int32x4.extractLane(m ,1); + var z = SIMD.Int32x4.extractLane(m ,2); + var w = SIMD.Int32x4.extractLane(m ,3); + + assertEquals(1, x); + assertEquals(2, y); + assertEquals(3, z); + assertEquals(4, w); +} + +testSIMDExtractLane(); +testSIMDExtractLane(); +%OptimizeFunctionOnNextCall(testSIMDExtractLane); +testSIMDExtractLane(); + +function testSIMDReplaceLane() { + var m = SIMD.Int32x4(1, 2, 3, 4); + var a = SIMD.Int32x4.replaceLane(m, 0, 5); + var b = SIMD.Int32x4.replaceLane(m, 1, 6); + var c = SIMD.Int32x4.replaceLane(m, 2, 7); + var d = SIMD.Int32x4.replaceLane(m, 3, 8); + + assertEquals(5, SIMD.Int32x4.extractLane(a, 0)); + assertEquals(6, SIMD.Int32x4.extractLane(b, 1)); + assertEquals(7, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(8, SIMD.Int32x4.extractLane(d, 3)); +} + +testSIMDReplaceLane(); +testSIMDReplaceLane(); +%OptimizeFunctionOnNextCall(testSIMDReplaceLane); +testSIMDReplaceLane(); diff --git a/test/mjsunit/harmony/simd/argument_object.js b/test/mjsunit/harmony/simd/argument_object.js new file mode 100644 index 00000000000..fb8362a0e85 --- /dev/null +++ b/test/mjsunit/harmony/simd/argument_object.js @@ -0,0 +1,126 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd --harmony-tostring --harmony-reflect +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt +/* +function testArgumentsObjectwithFloat32x4Field() { + "use strict"; + var forceDeopt = { deopt:false }; + function inner(a,b,c,d,e,f,g,h,i,j,k) { + var args = arguments; + forceDeopt.deopt; + assertSame(11, args.length); + assertSame(a, args[0]); + assertSame(b, args[1]); + assertSame(c, args[2]); + assertSame(d, args[3]); + assertSame(e, args[4]); + assertSame(f, args[5]); + assertSame(g, args[6]); + assertSame(h, args[7]); + assertSame(i, args[8]); + assertSame(j, args[9]); + assertEquals(1, SIMD.Float32x4.extractLane(args[10], 0)); + assertEquals(2, SIMD.Float32x4.extractLane(args[10], 1)); + assertEquals(3, SIMD.Float32x4.extractLane(args[10], 2)); + assertEquals(4, SIMD.Float32x4.extractLane(args[10], 3)); + } + + var a = 0.5; + var b = 1.7; + var c = 123; + function outer() { + inner( + a - 0.3, // double in double register + b + 2.3, // integer in double register + c + 321, // integer in general register + c - 456, // integer in stack slot + a + 0.1, a + 0.2, a + 0.3, a + 0.4, a + 0.5, + a + 0.6, // double in stack slot + SIMD.Float32x4(1, 2, 3, 4) + ); + } + + outer(); + outer(); + %OptimizeFunctionOnNextCall(outer); + outer(); + delete forceDeopt.deopt; + outer(); +} + +testArgumentsObjectwithFloat32x4Field(); + +function testArgumentsObjectwithInt32x4Field() { + "use strict"; + var forceDeopt = { deopt:false }; + function inner(a,b,c,d,e,f,g,h,i,j,k) { + var args = arguments; + forceDeopt.deopt; + assertSame(11, args.length); + assertSame(a, args[0]); + assertSame(b, args[1]); + assertSame(c, args[2]); + assertSame(d, args[3]); + assertSame(e, args[4]); + assertSame(f, args[5]); + assertSame(g, args[6]); + assertSame(h, args[7]); + assertSame(i, args[8]); + assertSame(j, args[9]); + assertEquals(1, SIMD.Int32x4.extractLane(args[10], 0); + assertEquals(2, SIMD.Int32x4.extractLane(args[10], 1); + assertEquals(3, SIMD.Int32x4.extractLane(args[10], 2); + assertEquals(4, SIMD.Int32x4.extractLane(args[10], 3); + } + + var a = 0.5; + var b = 1.7; + var c = 123; + function outer() { + inner( + a - 0.3, // double in double register + b + 2.3, // integer in double register + c + 321, // integer in general register + c - 456, // integer in stack slot + a + 0.1, a + 0.2, a + 0.3, a + 0.4, a + 0.5, + a + 0.6, // double in stack slot + SIMD.Int32x4(1, 2, 3, 4) + ); + } + + outer(); + outer(); + %OptimizeFunctionOnNextCall(outer); + outer(); + delete forceDeopt.deopt; + outer(); +} + +testArgumentsObjectwithInt32x4Field(); +*/ diff --git a/test/mjsunit/harmony/simd/bool32x4.js b/test/mjsunit/harmony/simd/bool32x4.js new file mode 100644 index 00000000000..ac2bb60d161 --- /dev/null +++ b/test/mjsunit/harmony/simd/bool32x4.js @@ -0,0 +1,55 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt + +function testAnyTrue() { + var a = SIMD.Bool32x4.anyTrue(SIMD.Bool32x4(true, true, true, false)); + assertEquals(true, a); + + var b = SIMD.Bool32x4.anyTrue(SIMD.Bool32x4(false, false, false, false)); + assertEquals(false, b); +} + +testAnyTrue(); +testAnyTrue(); +%OptimizeFunctionOnNextCall(testAnyTrue); +testAnyTrue(); + +function testAllTrue() { + var a = SIMD.Bool32x4.allTrue(SIMD.Bool32x4(true, true, true, true)); + assertEquals(true, a); + + var b = SIMD.Bool32x4.allTrue(SIMD.Bool32x4(true, false, true, true)); + assertEquals(false, b); +} + +testAllTrue(); +testAllTrue(); +%OptimizeFunctionOnNextCall(testAllTrue); +testAllTrue(); diff --git a/test/mjsunit/harmony/simd/builtin_operator.js b/test/mjsunit/harmony/simd/builtin_operator.js new file mode 100644 index 00000000000..c4ba35d1a9f --- /dev/null +++ b/test/mjsunit/harmony/simd/builtin_operator.js @@ -0,0 +1,185 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd --harmony-tostring --harmony-reflect +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt +/* +function testArithmeticOperators() { + var a = SIMD.Float32x4.splat(0); + var b = SIMD.Float32x4.splat(0); + var c; + + c = a + b; + assertEquals('Float32x4(0,0,0,0)Float32x4(0,0,0,0)', c); + c = a++; + assertEquals(NaN, c); + c = a - b; + assertEquals(NaN, c); + c = a--; + assertEquals(NaN, c); + c = a * b; + assertEquals(NaN, c); + c = a / b; + assertEquals(NaN, c); + c = a % b; + assertEquals(NaN, c); +} + +testArithmeticOperators(); +testArithmeticOperators(); +%OptimizeFunctionOnNextCall(testArithmeticOperators); +testArithmeticOperators(); + + +function testBitwiseOperators() { + var a = SIMD.Float32x4.splat(0); + var b = SIMD.Float32x4.splat(0); + var c; + c = a | b; + assertEquals(0, c); + c = a & b; + assertEquals(0, c); + c = a ^ b; + assertEquals(0, c); + c = ~a; + assertEquals(-1, c); + c = a << 0; + assertEquals(0, c); + c = a >> 0; + assertEquals(0, c); + c = a >>> 0; + assertEquals(0, c); +} + +testBitwiseOperators(); +testBitwiseOperators(); +%OptimizeFunctionOnNextCall(testBitwiseOperators); +testBitwiseOperators(); + + +function testAssignmentOperators() { + var a = SIMD.Float32x4.splat(0); + var b = SIMD.Float32x4.splat(0); + var c = a; + c += b; + assertEquals('Float32x4(0,0,0,0)Float32x4(0,0,0,0)', c); + c -= b; + assertEquals(NaN, c); + c *= b; + assertEquals(NaN, c); + c /= b; + assertEquals(NaN, c); + c %= b; + assertEquals(NaN, c); + + c &= b; + assertEquals(0, c); + c |= b; + assertEquals(0, c); + c ^= b; + assertEquals(0, c); + c <<= b; + assertEquals(0, c); + c >>= b; + assertEquals(0, c); + c >>>= b; + assertEquals(0, c); +} + +testAssignmentOperators(); +testAssignmentOperators(); +%OptimizeFunctionOnNextCall(testAssignmentOperators); +testAssignmentOperators(); + + +function testStringOperators() { + var a = SIMD.Float32x4.splat(0); + var b = "0"; + var c = a; + c += b; + assertEquals("Float32x4(0,0,0,0)0", c); + c = b + a; + assertEquals("0Float32x4(0,0,0,0)", c); +} + +testStringOperators(); +testStringOperators(); +%OptimizeFunctionOnNextCall(testStringOperators); +testStringOperators(); + + +function testComparisionOperators() { + var a = SIMD.Float32x4.splat(0); + var b = SIMD.Float32x4.splat(0); + assertEquals(false, a == b); + assertEquals(true, a != b); + assertEquals(false, a === b); + assertEquals(true, a !== b); + assertEquals(false, a > b); + assertEquals(true, a >= b); + assertEquals(false, a < b); + assertEquals(true, a <= b); +} + +testComparisionOperators(); +testComparisionOperators(); +// TODO(ningxin): optimized code will get opposite result. +//%OptimizeFunctionOnNextCall(testComparisionOperators); +testComparisionOperators(); + + +function testLogicalOperators() { + var a = SIMD.Float32x4.splat(0); + var b = SIMD.Float32x4.splat(1); + assertEquals(1, SIMD.Float32x4.extractLane((a && b), 0)); + assertEquals(1, SIMD.Float32x4.extractLane((a && b), 1)); + assertEquals(1, SIMD.Float32x4.extractLane((a && b), 2)); + assertEquals(1, SIMD.Float32x4.extractLane((a && b), 3)); + assertEquals(0, SIMD.Float32x4.extractLane((a || b), 0)); + assertEquals(0, SIMD.Float32x4.extractLane((a || b), 1)); + assertEquals(0, SIMD.Float32x4.extractLane((a || b), 2)); + assertEquals(0, SIMD.Float32x4.extractLane((a || b), 3)); + assertEquals(false, !a); +} + +testLogicalOperators(); +testLogicalOperators(); +%OptimizeFunctionOnNextCall(testLogicalOperators); +testLogicalOperators(); + + +function testConditionalOperators() { + var a = SIMD.Int32x4.zero(); + var c = a ? 1 : 0; + assertEquals(1, c); +} + +testConditionalOperators(); +testConditionalOperators(); +%OptimizeFunctionOnNextCall(testConditionalOperators); +testConditionalOperators(); +*/ diff --git a/test/mjsunit/harmony/simd/captured_object.js b/test/mjsunit/harmony/simd/captured_object.js new file mode 100644 index 00000000000..040587e9cf0 --- /dev/null +++ b/test/mjsunit/harmony/simd/captured_object.js @@ -0,0 +1,81 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd --harmony-tostring --harmony-reflect +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt + +function testCapturedObjectwithFloat32x4Field() { + var deopt = { deopt:false }; + function constructor() { + this.x = 1.1; + this.y = SIMD.Float32x4(1,2,3,4); + } + function field(x) { + var o = new constructor(); + o.x = x; + deopt.deopt; + assertEquals(x, o.x); + assertEquals(SIMD.Float32x4.extractLane(o.y, 0), 1); + assertEquals(SIMD.Float32x4.extractLane(o.y, 1), 2); + assertEquals(SIMD.Float32x4.extractLane(o.y, 2), 3); + assertEquals(SIMD.Float32x4.extractLane(o.y, 3), 4); + } + field(1); field(2); + // TODO(ningxin): fails in x64 test. + //%OptimizeFunctionOnNextCall(field); + field(3); field(4); + delete deopt.deopt; + field(5); field(6); +} + +testCapturedObjectwithFloat32x4Field(); + +function testCapturedObjectwithInt32x4Field() { + var deopt = { deopt:false }; + function constructor() { + this.x = 1.1; + this.y = SIMD.Int32x4(1,2,3,4); + } + function field(x) { + var o = new constructor(); + o.x = x; + deopt.deopt; + assertEquals(x, o.x); + assertEquals(SIMD.Int32x4.extractLane(o.y, 0), 1); + assertEquals(SIMD.Int32x4.extractLane(o.y, 1), 2); + assertEquals(SIMD.Int32x4.extractLane(o.y, 2), 3); + assertEquals(SIMD.Int32x4.extractLane(o.y, 3), 4); + } + field(1); field(2); + // TODO(ningxin): fix the failures. + //%OptimizeFunctionOnNextCall(field); + field(3); field(4); + delete deopt.deopt; + field(5); field(6); +} + +testCapturedObjectwithInt32x4Field(); diff --git a/test/mjsunit/harmony/simd/conversions.js b/test/mjsunit/harmony/simd/conversions.js new file mode 100644 index 00000000000..0f55984d6d8 --- /dev/null +++ b/test/mjsunit/harmony/simd/conversions.js @@ -0,0 +1,82 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd --harmony-tostring --harmony-reflect +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt +/* +function testObject() { + var a = SIMD.Float32x4.splat(0); + var b = Object(a); + assertEquals(0, SIMD.Float32x4.extractLane(b, 0)); + assertEquals(0, SIMD.Float32x4.extractLane(b, 1)); + assertEquals(0, SIMD.Float32x4.extractLane(b, 2)); + assertEquals(0, SIMD.Float32x4.extractLane(b, 3)); + assertEquals(typeof(b), "object"); + assertEquals(typeof(b.valueOf()), "object"); + assertEquals(Object.prototype.toString.call(b), "[object Object]"); +} + +testObject(); +testObject(); +%OptimizeFunctionOnNextCall(testObject); +testObject(); + + +function testNumber() { + var a = SIMD.Float32x4.splat(0); + var b = Number(a); + assertEquals(NaN, b); +} + +testNumber(); +testNumber(); +%OptimizeFunctionOnNextCall(testNumber); +testNumber(); + +*/ +function testString() { + var a = SIMD.Float32x4.splat(0); + var b = String(a); + assertEquals("SIMD.Float32x4(0, 0, 0, 0)", b); +} + +testString(); +testString(); +%OptimizeFunctionOnNextCall(testString); +testString(); + + +function testBoolean() { + var a = SIMD.Float32x4.splat(0); + var b = Boolean(a); + assertEquals(true, b); +} + +testBoolean(); +testBoolean(); +%OptimizeFunctionOnNextCall(testBoolean); +testBoolean(); diff --git a/test/mjsunit/harmony/simd/deopt.js b/test/mjsunit/harmony/simd/deopt.js new file mode 100644 index 00000000000..bdf4b73dba0 --- /dev/null +++ b/test/mjsunit/harmony/simd/deopt.js @@ -0,0 +1,80 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd --harmony-tostring --harmony-reflect +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt + +function testdeopt(a, b) { + var a4 = SIMD.Float32x4(1.0, -2.0, 3.0, -4.0); + var b4 = SIMD.Float32x4.abs(a4); + // print(b4); + + if (a > 0) { + a = 0; + } else { + a += b; //deopt + } + + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(b4, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(b4, 2)); + assertEquals(4.0, SIMD.Float32x4.extractLane(b4, 3)); +} + +testdeopt(1, 1); +testdeopt(1, 1); +%OptimizeFunctionOnNextCall(testdeopt); +testdeopt(0, 1); + +function testdeopt2() { + var a4 = SIMD.Float32x4(1.0, -1.0, 1.0, -1.0); + var b4 = SIMD.Float32x4.abs(a4); + + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 0)); + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 1)); + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 2)); + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 3)); + + var new_a4 = SIMD.Float32x4(1.0, -1.0, 1.0, -1.0); + var new_b4 = SIMD.Float32x4.abs(new_a4); + + assertEquals(1.0, SIMD.Float32x4.extractLane(new_b4, 0)); + assertEquals(1.0, SIMD.Float32x4.extractLane(new_b4, 1)); + assertEquals(1.0, SIMD.Float32x4.extractLane(new_b4, 2)); + assertEquals(1.0, SIMD.Float32x4.extractLane(new_b4, 3)); + + // Verifying deoptimization + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 0)); + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 1)); + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 1)); + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 3)); +} + +testdeopt2(); +testdeopt2(); +%OptimizeFunctionOnNextCall(testdeopt2); +testdeopt2(); diff --git a/test/mjsunit/harmony/simd/float32x4.js b/test/mjsunit/harmony/simd/float32x4.js new file mode 100644 index 00000000000..d69ae473179 --- /dev/null +++ b/test/mjsunit/harmony/simd/float32x4.js @@ -0,0 +1,595 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd --harmony-tostring --harmony-reflect +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt + +function testConstructor() { + var f4 = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); + assertEquals(1.0, SIMD.Float32x4.extractLane(f4, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(f4, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(f4, 2)); + assertEquals(4.0, SIMD.Float32x4.extractLane(f4, 3)); + + f4 = SIMD.Float32x4(1.1, 2.2, 3.3, 4.4); + assertEquals(1.100000023841858, SIMD.Float32x4.extractLane(f4, 0)); + assertEquals(2.200000047683716, SIMD.Float32x4.extractLane(f4, 1)); + assertEquals(3.299999952316284, SIMD.Float32x4.extractLane(f4, 2)); + assertEquals(4.400000095367432, SIMD.Float32x4.extractLane(f4, 3)); +} + +testConstructor(); +testConstructor(); +%OptimizeFunctionOnNextCall(testConstructor); +testConstructor(); + +function testCheck() { + var f4 = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); + var f4_new = SIMD.Float32x4.check(f4); + assertEquals(SIMD.Float32x4.extractLane(f4_new, 0), SIMD.Float32x4.extractLane(f4, 0)); + assertEquals(SIMD.Float32x4.extractLane(f4_new, 1), SIMD.Float32x4.extractLane(f4, 1)); + assertEquals(SIMD.Float32x4.extractLane(f4_new, 2), SIMD.Float32x4.extractLane(f4, 2)); + assertEquals(SIMD.Float32x4.extractLane(f4_new, 3), SIMD.Float32x4.extractLane(f4, 3)); + + f4 = SIMD.Float32x4(1.1, 2.2, 3.3, 4.4); + f4_new = SIMD.Float32x4.check(f4); + assertEquals(SIMD.Float32x4.extractLane(f4_new, 0), SIMD.Float32x4.extractLane(f4, 0)); + assertEquals(SIMD.Float32x4.extractLane(f4_new, 1), SIMD.Float32x4.extractLane(f4, 1)); + assertEquals(SIMD.Float32x4.extractLane(f4_new, 2), SIMD.Float32x4.extractLane(f4, 2)); + assertEquals(SIMD.Float32x4.extractLane(f4_new, 3), SIMD.Float32x4.extractLane(f4, 3)); +} + +testCheck(); +testCheck(); +%OptimizeFunctionOnNextCall(testCheck); +testCheck(); + +function testSplatConstructor() { + var z4 = SIMD.Float32x4.splat(5.0); + assertEquals(5.0, SIMD.Float32x4.extractLane(z4, 0)); + assertEquals(5.0, SIMD.Float32x4.extractLane(z4, 1)); + assertEquals(5.0, SIMD.Float32x4.extractLane(z4, 2)); + assertEquals(5.0, SIMD.Float32x4.extractLane(z4, 3)); +} + +testSplatConstructor(); +testSplatConstructor(); +%OptimizeFunctionOnNextCall(testSplatConstructor); +testSplatConstructor(); + +function testTypeof() { + var z4 = SIMD.Float32x4.splat(0); + assertEquals(typeof(z4), "float32x4"); + + var new_z4 = SIMD.Float32x4(0, 0, 0, 0); + assertEquals(typeof(new_z4), "float32x4"); + assertEquals(typeof(new_z4.valueOf()), "float32x4"); + assertEquals(Object.prototype.toString.call(new_z4), "[object Float32x4]"); +} + +testTypeof(); + + +function testSIMDAbs() { + var a4 = SIMD.Float32x4(1.0, -1.0, 1.0, -1.0); + var b4 = SIMD.Float32x4.abs(a4); + + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 0)); + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 1)); + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 2)); + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 3)); +} + +testSIMDAbs(); +testSIMDAbs(); +%OptimizeFunctionOnNextCall(testSIMDAbs); +testSIMDAbs(); + +function testSIMDNeg() { + var a4 = SIMD.Float32x4(1.0, -1.0, 1.0, -1.0); + var b4 = SIMD.Float32x4.neg(a4); + + assertEquals(-1.0, SIMD.Float32x4.extractLane(b4, 0)); + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 1)); + assertEquals(-1.0, SIMD.Float32x4.extractLane(b4, 2)); + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 3)); +} + +testSIMDNeg(); +testSIMDNeg(); +%OptimizeFunctionOnNextCall(testSIMDNeg); +testSIMDNeg(); + +function testSIMDAdd() { + var a4 = SIMD.Float32x4(1.0, 1.0, 1.0, 1.0); + var b4 = SIMD.Float32x4(2.0, 2.0, 2.0, 2.0); + var c4 = SIMD.Float32x4.add(a4, b4); + + assertEquals(3.0, SIMD.Float32x4.extractLane(c4, 0)); + assertEquals(3.0, SIMD.Float32x4.extractLane(c4, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(c4, 2)); + assertEquals(3.0, SIMD.Float32x4.extractLane(c4, 3)); +} + +testSIMDAdd(); +testSIMDAdd(); +%OptimizeFunctionOnNextCall(testSIMDAdd); +testSIMDAdd(); + +function testSIMDSub() { + var a4 = SIMD.Float32x4(1.0, 1.0, 1.0, 1.0); + var b4 = SIMD.Float32x4(2.0, 2.0, 2.0, 2.0); + var c4 = SIMD.Float32x4.sub(a4, b4); + + assertEquals(-1.0, SIMD.Float32x4.extractLane(c4, 0)); + assertEquals(-1.0, SIMD.Float32x4.extractLane(c4, 1)); + assertEquals(-1.0, SIMD.Float32x4.extractLane(c4, 2)); + assertEquals(-1.0, SIMD.Float32x4.extractLane(c4, 3)); +} + +testSIMDSub(); +testSIMDSub(); +%OptimizeFunctionOnNextCall(testSIMDSub); +testSIMDSub(); + +function testSIMDMul() { + var a4 = SIMD.Float32x4(1.0, 1.0, 1.0, 1.0); + var b4 = SIMD.Float32x4(2.0, 2.0, 2.0, 2.0); + var c4 = SIMD.Float32x4.mul(a4, b4); + + assertEquals(2.0, SIMD.Float32x4.extractLane(c4, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(c4, 1)); + assertEquals(2.0, SIMD.Float32x4.extractLane(c4, 2)); + assertEquals(2.0, SIMD.Float32x4.extractLane(c4, 3)); +} + +testSIMDMul(); +testSIMDMul(); +%OptimizeFunctionOnNextCall(testSIMDMul); +testSIMDMul(); + +function testSIMDDiv() { + var a4 = SIMD.Float32x4(1.0, 1.0, 1.0, 1.0); + var b4 = SIMD.Float32x4(2.0, 2.0, 2.0, 2.0); + var c4 = SIMD.Float32x4.div(a4, b4); + + assertEquals(0.5, SIMD.Float32x4.extractLane(c4, 0)); + assertEquals(0.5, SIMD.Float32x4.extractLane(c4, 1)); + assertEquals(0.5, SIMD.Float32x4.extractLane(c4, 2)); + assertEquals(0.5, SIMD.Float32x4.extractLane(c4, 3)); +} + +testSIMDDiv(); +testSIMDDiv(); +%OptimizeFunctionOnNextCall(testSIMDDiv); +testSIMDDiv(); + +function testSIMDMin() { + var m = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); + var n = SIMD.Float32x4(1.0, 0.0, 2.5, 5.0); + m = SIMD.Float32x4.min(m, n); + assertEquals(1.0, SIMD.Float32x4.extractLane(m, 0)); + assertEquals(0.0, SIMD.Float32x4.extractLane(m, 1)); + assertEquals(2.5, SIMD.Float32x4.extractLane(m, 2)); + assertEquals(4.0, SIMD.Float32x4.extractLane(m, 3)); +} + +testSIMDMin(); +testSIMDMin(); +%OptimizeFunctionOnNextCall(testSIMDMin); +testSIMDMin(); + +function testSIMDMax() { + var m = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); + var n = SIMD.Float32x4(1.0, 0.0, 2.5, 5.0); + m = SIMD.Float32x4.max(m, n); + assertEquals(1.0, SIMD.Float32x4.extractLane(m, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(m, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(m, 2)); + assertEquals(5.0, SIMD.Float32x4.extractLane(m, 3)); +} + +testSIMDMax(); +testSIMDMax(); +%OptimizeFunctionOnNextCall(testSIMDMax); +testSIMDMax(); + +function testSIMDReciprocal() { + var m = SIMD.Float32x4(1.0, 4.0, 9.0, 16.0); + m = SIMD.Float32x4.reciprocalApproximation(m); + assertTrue(Math.abs(1.0 - SIMD.Float32x4.extractLane(m, 0)) <= 0.001); + assertTrue(Math.abs(0.25 - SIMD.Float32x4.extractLane(m, 1)) <= 0.001); + assertTrue(Math.abs(0.1111111 - SIMD.Float32x4.extractLane(m, 2)) <= 0.001); + assertTrue(Math.abs(0.0625 - SIMD.Float32x4.extractLane(m, 3)) <= 0.001); +} + +testSIMDReciprocal(); +testSIMDReciprocal(); +%OptimizeFunctionOnNextCall(testSIMDReciprocal); +testSIMDReciprocal(); + +function testSIMDReciprocalSqrt() { + var m = SIMD.Float32x4(1.0, 0.25, 0.111111, 0.0625); + m = SIMD.Float32x4.reciprocalSqrtApproximation(m); + assertTrue(Math.abs(1.0 - SIMD.Float32x4.extractLane(m, 0)) <= 0.001); + assertTrue(Math.abs(2.0 - SIMD.Float32x4.extractLane(m, 1)) <= 0.001); + assertTrue(Math.abs(3.0 - SIMD.Float32x4.extractLane(m, 2)) <= 0.001); + assertTrue(Math.abs(4.0 - SIMD.Float32x4.extractLane(m, 3)) <= 0.001); +} + +testSIMDReciprocalSqrt(); +testSIMDReciprocalSqrt(); +%OptimizeFunctionOnNextCall(testSIMDReciprocalSqrt); +testSIMDReciprocalSqrt(); + +function testSIMDSqrt() { + var m = SIMD.Float32x4(1.0, 4.0, 9.0, 16.0); + m = SIMD.Float32x4.sqrt(m); + assertEquals(1.0, SIMD.Float32x4.extractLane(m, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(m, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(m, 2)); + assertEquals(4.0, SIMD.Float32x4.extractLane(m, 3)); +} + +testSIMDSqrt(); +testSIMDSqrt(); +%OptimizeFunctionOnNextCall(testSIMDSqrt); +testSIMDSqrt(); + +function testSIMDSwizzle() { + var m = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); + var xxxx = SIMD.Float32x4.swizzle(m, 0, 0, 0, 0); + print('------'); + print(SIMD.Float32x4.extractLane(xxxx, 0)); + print(SIMD.Float32x4.extractLane(xxxx, 1)); + print(SIMD.Float32x4.extractLane(xxxx, 2)); + print(SIMD.Float32x4.extractLane(xxxx, 3)); + print('------'); + assertEquals(1.0, SIMD.Float32x4.extractLane(xxxx, 0)); + assertEquals(1.0, SIMD.Float32x4.extractLane(xxxx, 1)); + assertEquals(1.0, SIMD.Float32x4.extractLane(xxxx, 2)); + assertEquals(1.0, SIMD.Float32x4.extractLane(xxxx, 3)); + var yyyy = SIMD.Float32x4.swizzle(m, 1, 1, 1, 1); + assertEquals(2.0, SIMD.Float32x4.extractLane(yyyy, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(yyyy, 1)); + assertEquals(2.0, SIMD.Float32x4.extractLane(yyyy, 2)); + assertEquals(2.0, SIMD.Float32x4.extractLane(yyyy, 3)); + var zzzz = SIMD.Float32x4.swizzle(m, 2, 2, 2, 2); + assertEquals(3.0, SIMD.Float32x4.extractLane(zzzz, 0)); + assertEquals(3.0, SIMD.Float32x4.extractLane(zzzz, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(zzzz, 2)); + assertEquals(3.0, SIMD.Float32x4.extractLane(zzzz, 3)); + var wwww = SIMD.Float32x4.swizzle(m, 3, 3, 3, 3); + assertEquals(4.0, SIMD.Float32x4.extractLane(wwww, 0)); + assertEquals(4.0, SIMD.Float32x4.extractLane(wwww, 1)); + assertEquals(4.0, SIMD.Float32x4.extractLane(wwww, 2)); + assertEquals(4.0, SIMD.Float32x4.extractLane(wwww, 3)); + var wzyx = SIMD.Float32x4.swizzle(m, 3, 2, 1, 0); + assertEquals(4.0, SIMD.Float32x4.extractLane(wzyx, 0)); + assertEquals(3.0, SIMD.Float32x4.extractLane(wzyx, 1)); + assertEquals(2.0, SIMD.Float32x4.extractLane(wzyx, 2)); + assertEquals(1.0, SIMD.Float32x4.extractLane(wzyx, 3)); + var wwzz = SIMD.Float32x4.swizzle(m, 3, 3, 2, 2); + assertEquals(4.0, SIMD.Float32x4.extractLane(wwzz, 0)); + assertEquals(4.0, SIMD.Float32x4.extractLane(wwzz, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(wwzz, 2)); + assertEquals(3.0, SIMD.Float32x4.extractLane(wwzz, 3)); + var xxyy = SIMD.Float32x4.swizzle(m, 0, 0, 1, 1); + assertEquals(1.0, SIMD.Float32x4.extractLane(xxyy, 0)); + assertEquals(1.0, SIMD.Float32x4.extractLane(xxyy, 1)); + assertEquals(2.0, SIMD.Float32x4.extractLane(xxyy, 2)); + assertEquals(2.0, SIMD.Float32x4.extractLane(xxyy, 3)); + var yyww = SIMD.Float32x4.swizzle(m, 1, 1, 3, 3); + assertEquals(2.0, SIMD.Float32x4.extractLane(yyww, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(yyww, 1)); + assertEquals(4.0, SIMD.Float32x4.extractLane(yyww, 2)); + assertEquals(4.0, SIMD.Float32x4.extractLane(yyww, 3)); +} + +testSIMDSwizzle(); +testSIMDSwizzle(); +%OptimizeFunctionOnNextCall(testSIMDSwizzle); +testSIMDSwizzle(); + +function testSIMDShuffle() { + var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); + var b = SIMD.Float32x4(5.0, 6.0, 7.0, 8.0); + var xxxx = SIMD.Float32x4.shuffle(a, b, 0, 0, 4, 4); + assertEquals(1.0, SIMD.Float32x4.extractLane(xxxx, 0)); + assertEquals(1.0, SIMD.Float32x4.extractLane(xxxx, 1)); + assertEquals(5.0, SIMD.Float32x4.extractLane(xxxx, 2)); + assertEquals(5.0, SIMD.Float32x4.extractLane(xxxx, 3)); + var yyyy = SIMD.Float32x4.shuffle(a, b, 1, 1, 5, 5); + assertEquals(2.0, SIMD.Float32x4.extractLane(yyyy, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(yyyy, 1)); + assertEquals(6.0, SIMD.Float32x4.extractLane(yyyy, 2)); + assertEquals(6.0, SIMD.Float32x4.extractLane(yyyy, 3)); + var zzzz = SIMD.Float32x4.shuffle(a, b, 2, 2, 6, 6); + assertEquals(3.0, SIMD.Float32x4.extractLane(zzzz, 0)); + assertEquals(3.0, SIMD.Float32x4.extractLane(zzzz, 1)); + assertEquals(7.0, SIMD.Float32x4.extractLane(zzzz, 2)); + assertEquals(7.0, SIMD.Float32x4.extractLane(zzzz, 3)); + var wwww = SIMD.Float32x4.shuffle(a, b, 3, 3, 7, 7); + assertEquals(4.0, SIMD.Float32x4.extractLane(wwww, 0)); + assertEquals(4.0, SIMD.Float32x4.extractLane(wwww, 1)); + assertEquals(8.0, SIMD.Float32x4.extractLane(wwww, 2)); + assertEquals(8.0, SIMD.Float32x4.extractLane(wwww, 3)); + var wzyx = SIMD.Float32x4.shuffle(a, b, 3, 2, 5, 4); + assertEquals(4.0, SIMD.Float32x4.extractLane(wzyx, 0)); + assertEquals(3.0, SIMD.Float32x4.extractLane(wzyx, 1)); + assertEquals(6.0, SIMD.Float32x4.extractLane(wzyx, 2)); + assertEquals(5.0, SIMD.Float32x4.extractLane(wzyx, 3)); + var wwzz = SIMD.Float32x4.shuffle(a, b, 3, 3, 6, 6); + assertEquals(4.0, SIMD.Float32x4.extractLane(wwzz, 0)); + assertEquals(4.0, SIMD.Float32x4.extractLane(wwzz, 1)); + assertEquals(7.0, SIMD.Float32x4.extractLane(wwzz, 2)); + assertEquals(7.0, SIMD.Float32x4.extractLane(wwzz, 3)); + var xxyy = SIMD.Float32x4.shuffle(a, b, 0, 0, 5, 5); + assertEquals(1.0, SIMD.Float32x4.extractLane(xxyy, 0)); + assertEquals(1.0, SIMD.Float32x4.extractLane(xxyy, 1)); + assertEquals(6.0, SIMD.Float32x4.extractLane(xxyy, 2)); + assertEquals(6.0, SIMD.Float32x4.extractLane(xxyy, 3)); + var yyww = SIMD.Float32x4.shuffle(a, b, 1, 1, 7, 7); + assertEquals(2.0, SIMD.Float32x4.extractLane(yyww, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(yyww, 1)); + assertEquals(8.0, SIMD.Float32x4.extractLane(yyww, 2)); + assertEquals(8.0, SIMD.Float32x4.extractLane(yyww, 3)); +} + +testSIMDShuffle(); +testSIMDShuffle(); +%OptimizeFunctionOnNextCall(testSIMDShuffle); +testSIMDShuffle(); + +function testSIMDConversion() { + var m = SIMD.Int32x4(0x3F800000, 0x40000000, 0x40400000, 0x40800000); + var n = SIMD.Float32x4.fromInt32x4Bits(m); + assertEquals(1.0, SIMD.Float32x4.extractLane(n, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(n, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(n, 2)); + assertEquals(4.0, SIMD.Float32x4.extractLane(n, 3)); + n = SIMD.Float32x4(5.0, 6.0, 7.0, 8.0); + m = SIMD.Int32x4.fromFloat32x4Bits(n); + assertEquals(0x40A00000, SIMD.Int32x4.extractLane(m, 0)); + assertEquals(0x40C00000, SIMD.Int32x4.extractLane(m, 1)); + assertEquals(0x40E00000, SIMD.Int32x4.extractLane(m, 2)); + assertEquals(0x41000000, SIMD.Int32x4.extractLane(m, 3)); + // Flip sign using bit-wise operators. + n = SIMD.Float32x4(9.0, 10.0, 11.0, 12.0); + m = SIMD.Int32x4(0x80000000, 0x80000000, 0x80000000, 0x80000000); + var nMask = SIMD.Int32x4.fromFloat32x4Bits(n); + nMask = SIMD.Int32x4.xor(nMask, m); // flip sign. + n = SIMD.Float32x4.fromInt32x4Bits(nMask); + assertEquals(-9.0, SIMD.Float32x4.extractLane(n, 0)); + assertEquals(-10.0, SIMD.Float32x4.extractLane(n, 1)); + assertEquals(-11.0, SIMD.Float32x4.extractLane(n, 2)); + assertEquals(-12.0, SIMD.Float32x4.extractLane(n, 3)); + nMask = SIMD.Int32x4.fromFloat32x4Bits(n); + nMask = SIMD.Int32x4.xor(nMask, m); // flip sign. + n = SIMD.Float32x4.fromInt32x4Bits(nMask); + assertEquals(9.0, SIMD.Float32x4.extractLane(n, 0)); + assertEquals(10.0, SIMD.Float32x4.extractLane(n, 1)); + assertEquals(11.0, SIMD.Float32x4.extractLane(n, 2)); + assertEquals(12.0, SIMD.Float32x4.extractLane(n, 3)); +} + +testSIMDConversion(); +testSIMDConversion(); +%OptimizeFunctionOnNextCall(testSIMDConversion); +testSIMDConversion(); + +function testSIMDConversion2() { + var m = SIMD.Int32x4(1, 2, 3, 4); + var n = SIMD.Float32x4.fromInt32x4(m); + assertEquals(1.0, SIMD.Float32x4.extractLane(n, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(n, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(n, 2)); + assertEquals(4.0, SIMD.Float32x4.extractLane(n, 3)); + n = SIMD.Float32x4(5.0, 6.0, 7.0, 8.0); + m = SIMD.Int32x4.fromFloat32x4(n); + assertEquals(5, SIMD.Int32x4.extractLane(m, 0)); + assertEquals(6, SIMD.Int32x4.extractLane(m, 1)); + assertEquals(7, SIMD.Int32x4.extractLane(m, 2)); + assertEquals(8, SIMD.Int32x4.extractLane(m, 3)); +} + +testSIMDConversion2(); +testSIMDConversion2(); +%OptimizeFunctionOnNextCall(testSIMDConversion2); +testSIMDConversion2(); + +/* +function testSIMDComparisons() { + var m = SIMD.Float32x4(1.0, 2.0, 0.1, 0.001); + var n = SIMD.Float32x4(2.0, 2.0, 0.001, 0.1); + var cmp; + cmp = SIMD.Float32x4.lessThan(m, n); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 0)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 1)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 2)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 3)); + + cmp = SIMD.Float32x4.lessThanOrEqual(m, n); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 0)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 1)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 2)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 3)); + + cmp = SIMD.Float32x4.equal(m, n); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 0)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 1)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 2)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 3)); + + cmp = SIMD.Float32x4.notEqual(m, n); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 0)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 1)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 2)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 3)); + + cmp = SIMD.Float32x4.greaterThanOrEqual(m, n); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 0)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 1)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 2)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 3)); + + cmp = SIMD.Float32x4.greaterThan(m, n); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 0)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 1)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 2)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 3)); +} + +testSIMDComparisons(); +testSIMDComparisons(); +%OptimizeFunctionOnNextCall(testSIMDComparisons); +testSIMDComparisons(); +*/ + + +function testSIMDSelect() { + var m = SIMD.Bool32x4(true, true, false, false); + var t = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); + var f = SIMD.Float32x4(5.0, 6.0, 7.0, 8.0); + var s = SIMD.Float32x4.select(m, t, f); + assertEquals(1.0, SIMD.Float32x4.extractLane(s, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(s, 1)); + assertEquals(7.0, SIMD.Float32x4.extractLane(s, 2)); + assertEquals(8.0, SIMD.Float32x4.extractLane(s, 3)); +} + +testSIMDSelect(); +testSIMDSelect(); +%OptimizeFunctionOnNextCall(testSIMDSelect); +testSIMDSelect(); + + +function testFloat32ArrayByteOffset() { + var b = new ArrayBuffer(40); + var a = new Float32Array(b, 8); + for (var i = 0; i < a.length; i++) { + a[i] = i; + } + + for (var i = 0; i < a.length - 3; i++) { + var v = SIMD.Float32x4.load(a, i); + assertEquals(i, SIMD.Float32x4.extractLane(v, 0)); + assertEquals(i+1, SIMD.Float32x4.extractLane(v, 1)); + assertEquals(i+2, SIMD.Float32x4.extractLane(v, 2)); + assertEquals(i+3, SIMD.Float32x4.extractLane(v, 3)); + } +} + +testFloat32ArrayByteOffset(); +testFloat32ArrayByteOffset(); +%OptimizeFunctionOnNextCall(testFloat32ArrayByteOffset); +testFloat32ArrayByteOffset(); + +function testFloat32x4Store() { + var b = new ArrayBuffer(56); + var a = new Float32Array(b, 8); + SIMD.Float32x4.store(a, 0, SIMD.Float32x4(0, 1, 2, 3)); + SIMD.Float32x4.store(a, 4, SIMD.Float32x4(4, 5, 6, 7)); + SIMD.Float32x4.store(a, 8, SIMD.Float32x4(8, 9, 10, 11)); + + for (var i = 0; i < a.length; i++) { + assertEquals(i, a[i]); + } +} + +testFloat32x4Store(); +testFloat32x4Store(); +%OptimizeFunctionOnNextCall(testFloat32x4Store); +testFloat32x4Store(); + +function testSIMDFromInt32x4() { + var m = SIMD.Float32x4(9, 10, 11, 12); + var nMask = SIMD.Int32x4.fromFloat32x4(m); + var n = SIMD.Float32x4.fromInt32x4(nMask); + + assertEquals(9.0, SIMD.Float32x4.extractLane(n, 0)); + assertEquals(10.0, SIMD.Float32x4.extractLane(n, 1)); + assertEquals(11.0, SIMD.Float32x4.extractLane(n, 2)); + assertEquals(12.0, SIMD.Float32x4.extractLane(n, 3)); +}; + +testSIMDFromInt32x4(); +testSIMDFromInt32x4(); +%OptimizeFunctionOnNextCall(testSIMDFromInt32x4); +testSIMDFromInt32x4(); + +function testSIMDFromInt32x4Bits() { + var m = SIMD.Float32x4(9, 10, 11, 12); + var nMask = SIMD.Int32x4.fromFloat32x4Bits(m); + var n = SIMD.Float32x4.fromInt32x4Bits(nMask); + + assertEquals(9.0, SIMD.Float32x4.extractLane(n, 0)); + assertEquals(10.0, SIMD.Float32x4.extractLane(n, 1)); + assertEquals(11.0, SIMD.Float32x4.extractLane(n, 2)); + assertEquals(12.0, SIMD.Float32x4.extractLane(n, 3)); +}; + +testSIMDFromInt32x4Bits(); +testSIMDFromInt32x4Bits(); +%OptimizeFunctionOnNextCall(testSIMDFromInt32x4Bits); +testSIMDFromInt32x4Bits(); + +function testSIMDExtractLane() { + var m = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); + var x = SIMD.Float32x4.extractLane(m, 0); + var y = SIMD.Float32x4.extractLane(m ,1); + var z = SIMD.Float32x4.extractLane(m ,2); + var w = SIMD.Float32x4.extractLane(m ,3); + + assertEquals(1.0, x); + assertEquals(2.0, y); + assertEquals(3.0, z); + assertEquals(4.0, w); +} + +testSIMDExtractLane(); +testSIMDExtractLane(); +%OptimizeFunctionOnNextCall(testSIMDExtractLane); +testSIMDExtractLane(); + +function testSIMDReplaceLane() { + var m = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); + var a = SIMD.Float32x4.replaceLane(m, 0, 5.0); + var b = SIMD.Float32x4.replaceLane(m, 1, 6.0); + var c = SIMD.Float32x4.replaceLane(m, 2, 7.0); + var d = SIMD.Float32x4.replaceLane(m, 3, 8.0); + + assertEquals(5.0, SIMD.Float32x4.extractLane(a, 0)); + assertEquals(6.0, SIMD.Float32x4.extractLane(b, 1)); + assertEquals(7.0, SIMD.Float32x4.extractLane(c, 2)); + assertEquals(8.0, SIMD.Float32x4.extractLane(d, 3)); +} + +testSIMDReplaceLane(); +testSIMDReplaceLane(); +%OptimizeFunctionOnNextCall(testSIMDReplaceLane); +testSIMDReplaceLane(); diff --git a/test/mjsunit/harmony/simd/int32x4.js b/test/mjsunit/harmony/simd/int32x4.js new file mode 100644 index 00000000000..08963df65b6 --- /dev/null +++ b/test/mjsunit/harmony/simd/int32x4.js @@ -0,0 +1,410 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd --harmony-tostring --harmony-reflect +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt + +function testConstructor() { + var u4 = SIMD.Int32x4(1, 2, 3, 4); + assertEquals(1, SIMD.Int32x4.extractLane(u4, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(u4, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(u4, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(u4, 3)); +} + +testConstructor(); + +function testCheck() { + var u4 = SIMD.Int32x4(1, 2, 3, 4); + var u4_new = SIMD.Int32x4.check(u4); + assertEquals(SIMD.Int32x4.extractLane(u4_new, 0), SIMD.Int32x4.extractLane(u4, 0)); + assertEquals(SIMD.Int32x4.extractLane(u4_new, 1), SIMD.Int32x4.extractLane(u4, 1)); + assertEquals(SIMD.Int32x4.extractLane(u4_new, 2), SIMD.Int32x4.extractLane(u4, 2)); + assertEquals(SIMD.Int32x4.extractLane(u4_new, 3), SIMD.Int32x4.extractLane(u4, 3)); +} + +testCheck(); +testCheck(); +%OptimizeFunctionOnNextCall(testCheck); +testCheck(); + +function testZeroConstructor() { + var u4 = SIMD.Int32x4.splat(0); + assertEquals(0, SIMD.Int32x4.extractLane(u4, 0)); + assertEquals(0, SIMD.Int32x4.extractLane(u4, 1)); + assertEquals(0, SIMD.Int32x4.extractLane(u4, 2)); + assertEquals(0, SIMD.Int32x4.extractLane(u4, 3)); +} + +testZeroConstructor(); +testZeroConstructor(); +%OptimizeFunctionOnNextCall(testZeroConstructor); +testZeroConstructor(); + +function testSplatConstructor() { + var u4 = SIMD.Int32x4.splat(4); + assertEquals(4, SIMD.Int32x4.extractLane(u4, 0)); + assertEquals(4, SIMD.Int32x4.extractLane(u4, 1)); + assertEquals(4, SIMD.Int32x4.extractLane(u4, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(u4, 3)); +} + +testSplatConstructor(); +testSplatConstructor(); +%OptimizeFunctionOnNextCall(testSplatConstructor); +testSplatConstructor(); + +function testTypeof() { + var u4 = SIMD.Int32x4(1, 2, 3, 4); + assertEquals(typeof(u4), "int32x4"); + + var new_u4 = SIMD.Int32x4(1, 2, 3, 4); + assertEquals(typeof(new_u4), "int32x4"); + assertEquals(typeof(new_u4.valueOf()), "int32x4"); + assertEquals(Object.prototype.toString.call(new_u4), "[object Int32x4]"); +} + +testTypeof(); + + +function testSIMDNegu32() { + var m = SIMD.Int32x4(-1, 1, -1, 1); + m = SIMD.Int32x4.neg(m); + assertEquals(1, SIMD.Int32x4.extractLane(m, 0)); + assertEquals(-1, SIMD.Int32x4.extractLane(m, 1)); + assertEquals(1, SIMD.Int32x4.extractLane(m, 2)); + assertEquals(-1, SIMD.Int32x4.extractLane(m, 3)); +} + +testSIMDNegu32(); +testSIMDNegu32(); +%OptimizeFunctionOnNextCall(testSIMDNegu32); +testSIMDNegu32(); + +function testSIMDSelect() { + var m = SIMD.Bool32x4(true, true, false, false); + var t = SIMD.Int32x4(1, 2, 3, 4); + var f = SIMD.Int32x4(5, 6, 7, 8); + var s = SIMD.Int32x4.select(m, t, f); + assertEquals(1, SIMD.Int32x4.extractLane(s, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(s, 1)); + assertEquals(7, SIMD.Int32x4.extractLane(s, 2)); + assertEquals(8, SIMD.Int32x4.extractLane(s, 3)); +} + +testSIMDSelect(); +testSIMDSelect(); +%OptimizeFunctionOnNextCall(testSIMDSelect); +testSIMDSelect(); + + +function testSIMDReplaceLaneXu32() { + var a = SIMD.Int32x4(1, 2, 3, 4); + var c = SIMD.Int32x4.replaceLane(a, 0, 20); + assertEquals(20, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDReplaceLaneXu32(); +testSIMDReplaceLaneXu32(); +%OptimizeFunctionOnNextCall(testSIMDReplaceLaneXu32); +testSIMDReplaceLaneXu32(); + +function testSIMDReplaceLaneYu32() { + var a = SIMD.Int32x4(1, 2, 3, 4); + var c = SIMD.Int32x4.replaceLane(a, 1, 20); + assertEquals(1, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(20, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDReplaceLaneYu32(); +testSIMDReplaceLaneYu32(); +%OptimizeFunctionOnNextCall(testSIMDReplaceLaneYu32); +testSIMDReplaceLaneYu32(); + +function testSIMDReplaceLaneZu32() { + var a = SIMD.Int32x4(1, 2, 3, 4); + var c = SIMD.Int32x4.replaceLane(a, 2, 20); + assertEquals(1, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(20, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDReplaceLaneZu32(); +testSIMDReplaceLaneZu32(); +%OptimizeFunctionOnNextCall(testSIMDReplaceLaneZu32); +testSIMDReplaceLaneZu32(); + +function testSIMDReplaceLaneWu32() { + var a = SIMD.Int32x4(1, 2, 3, 4); + var c = SIMD.Int32x4.replaceLane(a, 3, 20); + assertEquals(1, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(20, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDReplaceLaneWu32(); +testSIMDReplaceLaneWu32(); +%OptimizeFunctionOnNextCall(testSIMDReplaceLaneWu32); +testSIMDReplaceLaneWu32(); + +function testSIMDAddu32() { + var a = SIMD.Int32x4(-1, -1, 0x7fffffff, 0x0); + var b = SIMD.Int32x4(0x1, -1, 0x1, -1); + var c = SIMD.Int32x4.add(a, b); + assertEquals(0x0, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(-2, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(0x80000000 - 0xFFFFFFFF - 1, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(-1, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDAddu32(); +testSIMDAddu32(); +%OptimizeFunctionOnNextCall(testSIMDAddu32); +testSIMDAddu32(); + +function testSIMDSubu32() { + var a = SIMD.Int32x4(-1, -1, 0x80000000 - 0xFFFFFFFF - 1, 0x0); + var b = SIMD.Int32x4(0x1, -1, 0x1, -1); + var c = SIMD.Int32x4.sub(a, b); + assertEquals(-2, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(0x0, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(0x7FFFFFFF, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(0x1, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDSubu32(); +testSIMDSubu32(); +%OptimizeFunctionOnNextCall(testSIMDSubu32); +testSIMDSubu32(); + +function testSIMDMulu32() { + var a = SIMD.Int32x4(-1, -1, 0x80000000 - 0xFFFFFFFF - 1, 0x0); + var b = SIMD.Int32x4(0x1, -1, 0x80000000 - 0xFFFFFFFF - 1, -1); + var c = SIMD.Int32x4.mul(a, b); + assertEquals(-1, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(0x1, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(0x0, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(0x0, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDMulu32(); +testSIMDMulu32(); +%OptimizeFunctionOnNextCall(testSIMDMulu32); +testSIMDMulu32(); + +function testSIMDSwizzleu32() { + var m = SIMD.Int32x4(1, 2, 3, 4); + var xxxx = SIMD.Int32x4.swizzle(m, 0, 0, 0, 0); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 0)); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 1)); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 2)); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 3)); + var yyyy = SIMD.Int32x4.swizzle(m, 1, 1, 1, 1); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 1)); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 2)); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 3)); + var zzzz = SIMD.Int32x4.swizzle(m, 2, 2, 2, 2); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 0)); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 2)); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 3)); + var wwww = SIMD.Int32x4.swizzle(m, 3, 3, 3, 3); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 0)); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 1)); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 3)); + var wzyx = SIMD.Int32x4.swizzle(m, 3, 2, 1, 0); + assertEquals(4, SIMD.Int32x4.extractLane(wzyx, 0)); + assertEquals(3, SIMD.Int32x4.extractLane(wzyx, 1)); + assertEquals(2, SIMD.Int32x4.extractLane(wzyx, 2)); + assertEquals(1, SIMD.Int32x4.extractLane(wzyx, 3)); + var wwzz = SIMD.Int32x4.swizzle(m, 3, 3, 2, 2); + assertEquals(4, SIMD.Int32x4.extractLane(wwzz, 0)); + assertEquals(4, SIMD.Int32x4.extractLane(wwzz, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(wwzz, 2)); + assertEquals(3, SIMD.Int32x4.extractLane(wwzz, 3)); + var xxyy = SIMD.Int32x4.swizzle(m, 0, 0, 1, 1); + assertEquals(1, SIMD.Int32x4.extractLane(xxyy, 0)); + assertEquals(1, SIMD.Int32x4.extractLane(xxyy, 1)); + assertEquals(2, SIMD.Int32x4.extractLane(xxyy, 2)); + assertEquals(2, SIMD.Int32x4.extractLane(xxyy, 3)); + var yyww = SIMD.Int32x4.swizzle(m, 1, 1, 3, 3); + assertEquals(2, SIMD.Int32x4.extractLane(yyww, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(yyww, 1)); + assertEquals(4, SIMD.Int32x4.extractLane(yyww, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(yyww, 3)); +} + +testSIMDSwizzleu32(); +testSIMDSwizzleu32(); +%OptimizeFunctionOnNextCall(testSIMDSwizzleu32); +testSIMDSwizzleu32(); + +function testSIMDShuffle() { + var a = SIMD.Int32x4(1, 2, 3, 4); + var b = SIMD.Int32x4(5, 6, 7, 8); + var xxxx = SIMD.Int32x4.shuffle(a, b, 0, 0, 4, 4); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 0)); + assertEquals(1, SIMD.Int32x4.extractLane(xxxx, 1)); + assertEquals(5, SIMD.Int32x4.extractLane(xxxx, 2)); + assertEquals(5, SIMD.Int32x4.extractLane(xxxx, 3)); + var yyyy = SIMD.Int32x4.shuffle(a, b, 1, 1, 5, 5); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(yyyy, 1)); + assertEquals(6, SIMD.Int32x4.extractLane(yyyy, 2)); + assertEquals(6, SIMD.Int32x4.extractLane(yyyy, 3)); + var zzzz = SIMD.Int32x4.shuffle(a, b, 2, 2, 6, 6); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 0)); + assertEquals(3, SIMD.Int32x4.extractLane(zzzz, 1)); + assertEquals(7, SIMD.Int32x4.extractLane(zzzz, 2)); + assertEquals(7, SIMD.Int32x4.extractLane(zzzz, 3)); + var wwww = SIMD.Int32x4.shuffle(a, b, 3, 3, 7, 7); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 0)); + assertEquals(4, SIMD.Int32x4.extractLane(wwww, 1)); + assertEquals(8, SIMD.Int32x4.extractLane(wwww, 2)); + assertEquals(8, SIMD.Int32x4.extractLane(wwww, 3)); + var wzyx = SIMD.Int32x4.shuffle(a, b, 3, 2, 5, 4); + assertEquals(4, SIMD.Int32x4.extractLane(wzyx, 0)); + assertEquals(3, SIMD.Int32x4.extractLane(wzyx, 1)); + assertEquals(6, SIMD.Int32x4.extractLane(wzyx, 2)); + assertEquals(5, SIMD.Int32x4.extractLane(wzyx, 3)); + var wwzz = SIMD.Int32x4.shuffle(a, b, 3, 3, 6, 6); + assertEquals(4, SIMD.Int32x4.extractLane(wwzz, 0)); + assertEquals(4, SIMD.Int32x4.extractLane(wwzz, 1)); + assertEquals(7, SIMD.Int32x4.extractLane(wwzz, 2)); + assertEquals(7, SIMD.Int32x4.extractLane(wwzz, 3)); + var xxyy = SIMD.Int32x4.shuffle(a, b, 0, 0, 5, 5); + assertEquals(1, SIMD.Int32x4.extractLane(xxyy, 0)); + assertEquals(1, SIMD.Int32x4.extractLane(xxyy, 1)); + assertEquals(6, SIMD.Int32x4.extractLane(xxyy, 2)); + assertEquals(6, SIMD.Int32x4.extractLane(xxyy, 3)); + var yyww = SIMD.Int32x4.shuffle(a, b, 1, 1, 7, 7); + assertEquals(2, SIMD.Int32x4.extractLane(yyww, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(yyww, 1)); + assertEquals(8, SIMD.Int32x4.extractLane(yyww, 2)); + assertEquals(8, SIMD.Int32x4.extractLane(yyww, 3)); +} + +testSIMDShuffle(); +testSIMDShuffle(); +%OptimizeFunctionOnNextCall(testSIMDShuffle); +testSIMDShuffle(); +/* +function testSIMDComparisons() { + var m = SIMD.Int32x4(1, 2, 100, 1); + var n = SIMD.Int32x4(2, 2, 1, 100); + var cmp; + cmp = SIMD.Int32x4.lessThan(m, n); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 0)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 1)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 2)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 3)); + + cmp = SIMD.Int32x4.equal(m, n); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 0)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 1)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 2)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 3)); + + cmp = SIMD.Int32x4.greaterThan(m, n); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 0)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 1)); + assertEquals(-1, SIMD.Int32x4.extractLane(cmp, 2)); + assertEquals(0x0, SIMD.Int32x4.extractLane(cmp, 3)); +} + +testSIMDComparisons(); +testSIMDComparisons(); +%OptimizeFunctionOnNextCall(testSIMDComparisons); +testSIMDComparisons(); +*/ +function testSIMDShift() { + var m = SIMD.Int32x4(1, 2, 100, 0); + + var a = SIMD.Int32x4.shiftLeftByScalar(m, 2); + assertEquals(4, SIMD.Int32x4.extractLane(a, 0)); + assertEquals(8, SIMD.Int32x4.extractLane(a, 1)); + assertEquals(400, SIMD.Int32x4.extractLane(a, 2)); + assertEquals(0, SIMD.Int32x4.extractLane(a, 3)); + + var n = SIMD.Int32x4(-8, 2, 1, 100); + + var c = SIMD.Int32x4.shiftRightByScalar(n, 2); + assertEquals(-2, SIMD.Int32x4.extractLane(c, 0)); + assertEquals(0, SIMD.Int32x4.extractLane(c, 1)); + assertEquals(0, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(25, SIMD.Int32x4.extractLane(c, 3)); +} + +testSIMDShift(); +testSIMDShift(); +%OptimizeFunctionOnNextCall(testSIMDShift); +testSIMDShift(); + +function testSIMDExtractLane() { + var m = SIMD.Int32x4(1, 2, 3, 4); + var x = SIMD.Int32x4.extractLane(m, 0); + var y = SIMD.Int32x4.extractLane(m ,1); + var z = SIMD.Int32x4.extractLane(m ,2); + var w = SIMD.Int32x4.extractLane(m ,3); + + assertEquals(1, x); + assertEquals(2, y); + assertEquals(3, z); + assertEquals(4, w); +} + +testSIMDExtractLane(); +testSIMDExtractLane(); +%OptimizeFunctionOnNextCall(testSIMDExtractLane); +testSIMDExtractLane(); + +function testSIMDReplaceLane() { + var m = SIMD.Int32x4(1, 2, 3, 4); + var a = SIMD.Int32x4.replaceLane(m, 0, 5); + var b = SIMD.Int32x4.replaceLane(m, 1, 6); + var c = SIMD.Int32x4.replaceLane(m, 2, 7); + var d = SIMD.Int32x4.replaceLane(m, 3, 8); + + assertEquals(5, SIMD.Int32x4.extractLane(a, 0)); + assertEquals(6, SIMD.Int32x4.extractLane(b, 1)); + assertEquals(7, SIMD.Int32x4.extractLane(c, 2)); + assertEquals(8, SIMD.Int32x4.extractLane(d, 3)); +} + +testSIMDReplaceLane(); +testSIMDReplaceLane(); +%OptimizeFunctionOnNextCall(testSIMDReplaceLane); +testSIMDReplaceLane(); diff --git a/test/mjsunit/harmony/simd/loadstore.js b/test/mjsunit/harmony/simd/loadstore.js new file mode 100644 index 00000000000..5275fb608e9 --- /dev/null +++ b/test/mjsunit/harmony/simd/loadstore.js @@ -0,0 +1,223 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd --harmony-tostring --harmony-reflect +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt + +function testFloat32x4LoadAndStore() { + var f32_array = new Float32Array(12); + for (var i = 0; i < 12; ++i) + f32_array[i] = 1.0 + i; + + var v1 = SIMD.Float32x4.load(f32_array, 0); + var v2 = SIMD.Float32x4.load(f32_array, 4); + var v3 = SIMD.Float32x4.load(f32_array, 8); + + assertEquals(1.0, SIMD.Float32x4.extractLane(v1, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(v1, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(v1, 2)); + assertEquals(4.0, SIMD.Float32x4.extractLane(v1, 3)); + + assertEquals(5.0, SIMD.Float32x4.extractLane(v2, 0)); + assertEquals(6.0, SIMD.Float32x4.extractLane(v2, 1)); + assertEquals(7.0, SIMD.Float32x4.extractLane(v2, 2)); + assertEquals(8.0, SIMD.Float32x4.extractLane(v2, 3)); + + assertEquals(9.0, SIMD.Float32x4.extractLane(v3, 0)); + assertEquals(10.0, SIMD.Float32x4.extractLane(v3, 1)); + assertEquals(11.0, SIMD.Float32x4.extractLane(v3, 2)); + assertEquals(12.0, SIMD.Float32x4.extractLane(v3, 3)); + + SIMD.Float32x4.store(f32_array, 0, SIMD.Float32x4(12.0, 11.0, 10.0, 9.0)); + SIMD.Float32x4.store(f32_array, 4, SIMD.Float32x4(8.0, 7.0, 6.0, 5.0)); + SIMD.Float32x4.store(f32_array, 8, SIMD.Float32x4(4.0, 3.0, 2.0, 1.0)); + + for (var i = 0; i < 12; ++i) + assertEquals(12.0 - i, f32_array[i]); +} + +testFloat32x4LoadAndStore(); +testFloat32x4LoadAndStore(); +%OptimizeFunctionOnNextCall(testFloat32x4LoadAndStore); +testFloat32x4LoadAndStore(); + +function testFloat32x4LoadXAndStoreX() { + var f32_array = new Float32Array(12); + for (var i = 0; i < 12; ++i) + f32_array[i] = 1.0 + i; + + for (var i = 0; i < 12; ++i) { + var v = SIMD.Float32x4.load1(f32_array, i); + + assertEquals(1.0 + i, SIMD.Float32x4.extractLane(v, 0)); + assertEquals(0.0, SIMD.Float32x4.extractLane(v, 1)); + assertEquals(0.0, SIMD.Float32x4.extractLane(v, 2)); + assertEquals(0.0, SIMD.Float32x4.extractLane(v, 3)); + } + + for (var i = 0; i < 12; ++i) { + SIMD.Float32x4.store1(f32_array, i, SIMD.Float32x4(12.0 - i, 0.0, 0.0, 0.0)); + } + + for (var i = 0; i < 12; ++i) + assertEquals(12.0 - i, f32_array[i]); +} + +testFloat32x4LoadXAndStoreX(); +testFloat32x4LoadXAndStoreX(); +%OptimizeFunctionOnNextCall(testFloat32x4LoadXAndStoreX); +testFloat32x4LoadXAndStoreX(); + +function testFloat32x4LoadXYAndStoreXY() { + var f32_array = new Float32Array(12); + for (var i = 0; i < 12; ++i) + f32_array[i] = 1.0 + i; + + for (var i = 0; i < 12; i += 2) { + var v = SIMD.Float32x4.load2(f32_array, i); + + assertEquals(1.0 + i, SIMD.Float32x4.extractLane(v, 0)); + assertEquals(2.0 + i, SIMD.Float32x4.extractLane(v, 1)); + assertEquals(0.0, SIMD.Float32x4.extractLane(v, 2)); + assertEquals(0.0, SIMD.Float32x4.extractLane(v, 3)); + } + + for (var i = 0; i < 12; i += 2) { + SIMD.Float32x4.store2(f32_array, i, SIMD.Float32x4(12.0 - i, 11.0 - i, 0.0, 0.0)); + } + + for (var i = 0; i < 12; ++i) + assertEquals(12.0 - i, f32_array[i]); +} + +testFloat32x4LoadXYAndStoreXY(); +testFloat32x4LoadXYAndStoreXY(); +%OptimizeFunctionOnNextCall(testFloat32x4LoadXYAndStoreXY); +testFloat32x4LoadXYAndStoreXY(); + +function testFloat32x4LoadXYZAndStoreXYZ() { + var f32_array = new Float32Array(12); + for (var i = 0; i < 12; ++i) + f32_array[i] = 1.0 + i; + + for (var i = 0; i < 12; i += 3) { + var v = SIMD.Float32x4.load3(f32_array, i); + + assertEquals(1.0 + i, SIMD.Float32x4.extractLane(v, 0)); + assertEquals(2.0 + i, SIMD.Float32x4.extractLane(v, 1)); + assertEquals(3.0 + i, SIMD.Float32x4.extractLane(v, 2)); + assertEquals(0.0, SIMD.Float32x4.extractLane(v, 3)); + } + + for (var i = 0; i < 12; i += 3) { + SIMD.Float32x4.store3(f32_array, i, SIMD.Float32x4(12.0 - i, 11.0 - i, 10.0 - i, 0.0)); + } + + for (var i = 0; i < 12; ++i) + assertEquals(12.0 - i, f32_array[i]); +} + +testFloat32x4LoadXYZAndStoreXYZ(); +testFloat32x4LoadXYZAndStoreXYZ(); +%OptimizeFunctionOnNextCall(testFloat32x4LoadXYZAndStoreXYZ); +testFloat32x4LoadXYZAndStoreXYZ(); + +function testFloat32x4LoadAndStoreFromInt8Array() { + var f32_array = new Float32Array(12); + for (var i = 0; i < 12; ++i) + f32_array[i] = 1.0 + i; + + var i8_array = new Int8Array(f32_array.buffer); + + var v1 = SIMD.Float32x4.load(i8_array, 0); + var v2 = SIMD.Float32x4.load(i8_array, 16); + var v3 = SIMD.Float32x4.load(i8_array, 32); + + assertEquals(1.0, SIMD.Float32x4.extractLane(v1, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(v1, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(v1, 2)); + assertEquals(4.0, SIMD.Float32x4.extractLane(v1, 3)); + + assertEquals(5.0, SIMD.Float32x4.extractLane(v2, 0)); + assertEquals(6.0, SIMD.Float32x4.extractLane(v2, 1)); + assertEquals(7.0, SIMD.Float32x4.extractLane(v2, 2)); + assertEquals(8.0, SIMD.Float32x4.extractLane(v2, 3)); + + assertEquals(9.0, SIMD.Float32x4.extractLane(v3, 0)); + assertEquals(10.0, SIMD.Float32x4.extractLane(v3, 1)); + assertEquals(11.0, SIMD.Float32x4.extractLane(v3, 2)); + assertEquals(12.0, SIMD.Float32x4.extractLane(v3, 3)); + + SIMD.Float32x4.store(i8_array, 0, SIMD.Float32x4(12.0, 11.0, 10.0, 9.0)); + SIMD.Float32x4.store(i8_array, 16, SIMD.Float32x4(8.0, 7.0, 6.0, 5.0)); + SIMD.Float32x4.store(i8_array, 32, SIMD.Float32x4(4.0, 3.0, 2.0, 1.0)); + + for (var i = 0; i < 12; ++i) + assertEquals(12.0 - i, f32_array[i]); +} + +testFloat32x4LoadAndStoreFromInt8Array(); +testFloat32x4LoadAndStoreFromInt8Array(); +%OptimizeFunctionOnNextCall(testFloat32x4LoadAndStoreFromInt8Array); +testFloat32x4LoadAndStoreFromInt8Array(); + +function testInt32x4LoadAndStore() { + var i32_array = new Int32Array(12); + for (var i = 0; i < 12; ++i) + i32_array[i] = 1 + i; + + var v1 = SIMD.Int32x4.load(i32_array, 0); + var v2 = SIMD.Int32x4.load(i32_array, 4); + var v3 = SIMD.Int32x4.load(i32_array, 8); + + assertEquals(1, SIMD.Int32x4.extractLane(v1, 0)); + assertEquals(2, SIMD.Int32x4.extractLane(v1, 1)); + assertEquals(3, SIMD.Int32x4.extractLane(v1, 2)); + assertEquals(4, SIMD.Int32x4.extractLane(v1, 3)); + + assertEquals(5, SIMD.Int32x4.extractLane(v2, 0)); + assertEquals(6, SIMD.Int32x4.extractLane(v2, 1)); + assertEquals(7, SIMD.Int32x4.extractLane(v2, 2)); + assertEquals(8, SIMD.Int32x4.extractLane(v2, 3)); + + assertEquals(9, SIMD.Int32x4.extractLane(v3, 0)); + assertEquals(10, SIMD.Int32x4.extractLane(v3, 1)); + assertEquals(11, SIMD.Int32x4.extractLane(v3, 2)); + assertEquals(12, SIMD.Int32x4.extractLane(v3, 3)); + + SIMD.Int32x4.store(i32_array, 0, SIMD.Int32x4(12, 11, 10, 9)); + SIMD.Int32x4.store(i32_array, 4, SIMD.Int32x4(8, 7, 6, 5)); + SIMD.Int32x4.store(i32_array, 8, SIMD.Int32x4(4, 3, 2, 1)); + + for (var i = 0; i < 12; ++i) + assertEquals(12.0 - i, i32_array[i]); +} + +testInt32x4LoadAndStore(); +testInt32x4LoadAndStore(); +%OptimizeFunctionOnNextCall(testInt32x4LoadAndStore); +testInt32x4LoadAndStore(); diff --git a/test/mjsunit/harmony/simd/osr.js b/test/mjsunit/harmony/simd/osr.js new file mode 100644 index 00000000000..8c9ee0ba67c --- /dev/null +++ b/test/mjsunit/harmony/simd/osr.js @@ -0,0 +1,44 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd --harmony-tostring --harmony-reflect +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt + +function testSIMDAbs() { + var a4 = SIMD.Float32x4(1.0, -2.0, 3.0, -4.0); + var b4; + for (var i = 0; i < 100000; i++) { + b4 = SIMD.Float32x4.abs(a4); + } + + assertEquals(1.0, SIMD.Float32x4.extractLane(b4, 0)); + assertEquals(2.0, SIMD.Float32x4.extractLane(b4, 1)); + assertEquals(3.0, SIMD.Float32x4.extractLane(b4, 2)); + assertEquals(4.0, SIMD.Float32x4.extractLane(b4, 3)); +} + +testSIMDAbs(); diff --git a/test/mjsunit/harmony/simd/prototype.js b/test/mjsunit/harmony/simd/prototype.js new file mode 100644 index 00000000000..2a16db87321 --- /dev/null +++ b/test/mjsunit/harmony/simd/prototype.js @@ -0,0 +1,61 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-simd --harmony-tostring --harmony-reflect +// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt + +function testFloat32x4Prototype() { + var a4 = SIMD.Float32x4(1.0, -2.0, 3.0, -4.0); + SIMD.Float32x4.prototype = {}; + try { + var x = SIMD.Float32x4.extractLane(a4, 0); + } catch (o) { + assertEquals(o instanceof TypeError, true); + assertEquals(o.message, ""); + } +} + +testFloat32x4Prototype(); +testFloat32x4Prototype(); +%OptimizeFunctionOnNextCall(testFloat32x4Prototype); +testFloat32x4Prototype(); + +function testInt32x4Prototype() { + var a4 = SIMD.Int32x4(1.0, -2.0, 3.0, -4.0); + SIMD.Int32x4.prototype = {}; + try { + var x = SIMD.Int32x4.extractLane(a4, 0); + } catch (o) { + assertEquals(o instanceof TypeError, true); + assertEquals(o.message, ""); + } +} + +testInt32x4Prototype(); +testInt32x4Prototype(); +%OptimizeFunctionOnNextCall(testInt32x4Prototype); +testInt32x4Prototype();