From d97fc1017df5ddb37f14064bff1dd056bae5b259 Mon Sep 17 00:00:00 2001 From: Haitao Feng Date: Wed, 5 Mar 2014 15:33:11 +0800 Subject: [PATCH 1/3] Optimize SIMD operations by CrankShaft The implementation is only for ia32 and x64 ports. Inline all SIMD operations and emit SIMD instructions for ia32 and x64 port Conflicts: src/crankshaft/arm64/lithium-arm64.cc src/crankshaft/hydrogen-instructions.h src/crankshaft/hydrogen-types.cc src/crankshaft/hydrogen.cc src/crankshaft/ia32/lithium-codegen-ia32.cc src/crankshaft/ia32/lithium-ia32.cc src/crankshaft/x64/lithium-x64.cc src/deoptimizer.h src/ia32/deoptimizer-ia32.cc src/mips/assembler-mips.h src/mips/deoptimizer-mips.cc src/runtime/runtime.h src/x64/deoptimizer-x64.cc src/x64/disasm-x64.cc Conflicts log: 1. Change the deoptimize msg list to [DeoptimizeReason](https://cs.chromium.org/chromium/src/v8/src/deoptimize-reason.h?q=DeoptimizeReason&sq=package:chromium&l=79&dr=C). 2. Remove instructions which are added by upstream. --- src/arm/assembler-arm-inl.h | 1 + src/arm/assembler-arm.h | 29 + src/arm/deoptimizer-arm.cc | 39 +- src/arm64/assembler-arm64-inl.h | 1 + src/arm64/assembler-arm64.h | 24 + src/arm64/deoptimizer-arm64.cc | 30 +- src/assembler.h | 1 + src/ast/scopes.cc | 2 +- src/bootstrapper.cc | 68 + src/compiler/linkage.cc | 5 + src/crankshaft/arm/lithium-arm.cc | 40 + src/crankshaft/arm64/lithium-arm64.cc | 40 + src/crankshaft/hydrogen-instructions.cc | 202 ++- src/crankshaft/hydrogen-instructions.h | 736 ++++++++- .../hydrogen-representation-changes.cc | 16 +- src/crankshaft/hydrogen-types.cc | 20 + src/crankshaft/hydrogen-types.h | 19 +- src/crankshaft/hydrogen.cc | 336 +++- src/crankshaft/hydrogen.h | 18 +- src/crankshaft/ia32/lithium-codegen-ia32.cc | 1462 ++++++++++++++++- src/crankshaft/ia32/lithium-codegen-ia32.h | 19 + .../ia32/lithium-gap-resolver-ia32.cc | 51 + src/crankshaft/ia32/lithium-ia32.cc | 361 +++- src/crankshaft/ia32/lithium-ia32.h | 265 ++- src/crankshaft/lithium-allocator-inl.h | 3 +- src/crankshaft/lithium-allocator.cc | 87 +- src/crankshaft/lithium-allocator.h | 9 + src/crankshaft/lithium.cc | 53 +- src/crankshaft/lithium.h | 75 +- src/crankshaft/mips/lithium-mips.cc | 40 + src/crankshaft/x64/lithium-codegen-x64.cc | 1284 ++++++++++++++- src/crankshaft/x64/lithium-codegen-x64.h | 17 +- .../x64/lithium-gap-resolver-x64.cc | 50 + src/crankshaft/x64/lithium-x64.cc | 334 +++- src/crankshaft/x64/lithium-x64.h | 258 ++- src/deoptimize-reason.h | 1 + src/deoptimizer.cc | 182 +- src/deoptimizer.h | 66 +- src/elements-kind.h | 1 + src/globals.h | 39 +- src/ia32/assembler-ia32-inl.h | 1 + src/ia32/assembler-ia32.cc | 327 ++++ src/ia32/assembler-ia32.h | 128 +- src/ia32/deoptimizer-ia32.cc | 62 +- src/ia32/disasm-ia32.cc | 215 ++- src/ia32/macro-assembler-ia32.cc | 92 +- src/ia32/macro-assembler-ia32.h | 22 + src/js/harmony-simd.js | 156 +- src/messages.h | 1 + src/mips/assembler-mips-inl.h | 1 + src/mips/assembler-mips.h | 14 + src/mips/deoptimizer-mips.cc | 30 + src/objects.cc | 69 +- src/objects.h | 235 ++- src/parsing/parser-base.h | 4 + src/parsing/parser.cc | 151 ++ src/parsing/parser.h | 8 + src/parsing/preparser.h | 6 + src/property-details.h | 13 + src/runtime/runtime-simd.cc | 143 ++ src/runtime/runtime.h | 19 + src/types.cc | 5 + src/x64/assembler-x64-inl.h | 1 + src/x64/assembler-x64.cc | 205 ++- src/x64/assembler-x64.h | 33 +- src/x64/deoptimizer-x64.cc | 58 +- src/x64/disasm-x64.cc | 203 ++- src/x64/macro-assembler-x64.cc | 99 +- src/x64/macro-assembler-x64.h | 22 + test/cctest/test-disasm-ia32.cc | 80 + test/mjsunit/harmony/int32x4.js | 425 +++++ test/mjsunit/harmony/simd/argument_object.js | 126 ++ test/mjsunit/harmony/simd/builtin_operator.js | 185 +++ test/mjsunit/harmony/simd/captured_object.js | 81 + test/mjsunit/harmony/simd/conversions.js | 82 + test/mjsunit/harmony/simd/deopt.js | 80 + test/mjsunit/harmony/simd/float32x4.js | 595 +++++++ test/mjsunit/harmony/simd/int32x4.js | 410 +++++ test/mjsunit/harmony/simd/loadstore.js | 223 +++ test/mjsunit/harmony/simd/osr.js | 44 + test/mjsunit/harmony/simd/prototype.js | 61 + 81 files changed, 10710 insertions(+), 289 deletions(-) create mode 100644 test/mjsunit/harmony/int32x4.js create mode 100644 test/mjsunit/harmony/simd/argument_object.js create mode 100644 test/mjsunit/harmony/simd/builtin_operator.js create mode 100644 test/mjsunit/harmony/simd/captured_object.js create mode 100644 test/mjsunit/harmony/simd/conversions.js create mode 100644 test/mjsunit/harmony/simd/deopt.js create mode 100644 test/mjsunit/harmony/simd/float32x4.js create mode 100644 test/mjsunit/harmony/simd/int32x4.js create mode 100644 test/mjsunit/harmony/simd/loadstore.js create mode 100644 test/mjsunit/harmony/simd/osr.js create mode 100644 test/mjsunit/harmony/simd/prototype.js 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..4de0752d32e 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,1193 @@ 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 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().IsInt32x4()); + DCHECK(instr->hydrogen()->second()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->third()->representation().IsInt32x4()); + + XMMRegister mask_reg = ToInt32x4Register(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().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 = ToBool32x4Register(instr->result()); + __ sub(esp, Immediate(kInt32x4Size)); + __ mov(Operand(esp, 0 * kBool32Size), x_reg); + __ mov(Operand(esp, 1 * kBool32Size), y_reg); + __ mov(Operand(esp, 2 * kBool32Size), z_reg); + __ mov(Operand(esp, 3 * kBool32Size), w_reg); + __ 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..9f9fe6eb910 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,285 @@ 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 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..e67f0b33bd3 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,1088 @@ 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 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 = ToInt32x4Register(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().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().IsInt32x4()); + DCHECK(instr->hydrogen()->second()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->third()->representation().IsInt32x4()); + + XMMRegister mask_reg = ToInt32x4Register(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: { + 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().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 = ToBool32x4Register(instr->result()); + __ subq(rsp, Immediate(kBool32x4Size)); + __ movl(Operand(rsp, 0 * kBool32Size), x_reg); + __ movl(Operand(rsp, 1 * kBool32Size), y_reg); + __ movl(Operand(rsp, 2 * kBool32Size), z_reg); + __ movl(Operand(rsp, 3 * kBool32Size), w_reg); + __ 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 +5144,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 +5172,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 +5787,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 +6054,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..1ebd9b2567e 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,233 @@ 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 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 +2070,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 +2150,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 +2843,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..ebfea53dd21 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,200 @@ 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) + +// 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, Int32x4, 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, Integer32, Integer32, \ + Integer32, Integer32) + +#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 +7072,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/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(); From 9de96c32c99787fdde9048843484fc44be4b1494 Mon Sep 17 00:00:00 2001 From: chuan9 Date: Wed, 3 Feb 2016 09:21:36 +0800 Subject: [PATCH 2/3] [SIMD.Bool32x4] Modify parameter of SIMD.Int32x4.select in crankshaft, and change the method of constructor of SIMD.Bool32x4 for ia32 and x64 Correct type to SIMD128_VALUE_TYPE in HanleTaggedToSIMD128 --- src/crankshaft/ia32/lithium-codegen-ia32.cc | 54 ++++++++++++++++----- src/crankshaft/x64/lithium-codegen-x64.cc | 52 ++++++++++++++++---- src/objects.h | 18 +++---- 3 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/crankshaft/ia32/lithium-codegen-ia32.cc b/src/crankshaft/ia32/lithium-codegen-ia32.cc index 4de0752d32e..40d82d16991 100644 --- a/src/crankshaft/ia32/lithium-codegen-ia32.cc +++ b/src/crankshaft/ia32/lithium-codegen-ia32.cc @@ -6294,11 +6294,11 @@ void LCodeGen::DoTernarySIMDOperation(LTernarySIMDOperation* instr) { return; } case kInt32x4Select: { - DCHECK(instr->hydrogen()->first()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->first()->representation().IsBool32x4()); DCHECK(instr->hydrogen()->second()->representation().IsInt32x4()); DCHECK(instr->hydrogen()->third()->representation().IsInt32x4()); - XMMRegister mask_reg = ToInt32x4Register(instr->first()); + XMMRegister mask_reg = ToBool32x4Register(instr->first()); XMMRegister left_reg = ToInt32x4Register(instr->second()); XMMRegister right_reg = ToInt32x4Register(instr->third()); XMMRegister result_reg = ToInt32x4Register(instr->result()); @@ -6469,20 +6469,52 @@ void LCodeGen::DoQuarternarySIMDOperation(LQuarternarySIMDOperation* instr) { return; } case kBool32x4Constructor: { - DCHECK(instr->hydrogen()->x()->representation().IsInteger32()); - DCHECK(instr->hydrogen()->y()->representation().IsInteger32()); - DCHECK(instr->hydrogen()->z()->representation().IsInteger32()); - DCHECK(instr->hydrogen()->w()->representation().IsInteger32()); + 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()); - __ sub(esp, Immediate(kInt32x4Size)); - __ mov(Operand(esp, 0 * kBool32Size), x_reg); - __ mov(Operand(esp, 1 * kBool32Size), y_reg); - __ mov(Operand(esp, 2 * kBool32Size), z_reg); - __ mov(Operand(esp, 3 * kBool32Size), w_reg); + + 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; diff --git a/src/crankshaft/x64/lithium-codegen-x64.cc b/src/crankshaft/x64/lithium-codegen-x64.cc index e67f0b33bd3..59e1a2b605f 100644 --- a/src/crankshaft/x64/lithium-codegen-x64.cc +++ b/src/crankshaft/x64/lithium-codegen-x64.cc @@ -4298,11 +4298,11 @@ void LCodeGen::DoTernarySIMDOperation(LTernarySIMDOperation* instr) { return; } case kInt32x4Select: { - DCHECK(instr->hydrogen()->first()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->first()->representation().IsBool32x4()); DCHECK(instr->hydrogen()->second()->representation().IsInt32x4()); DCHECK(instr->hydrogen()->third()->representation().IsInt32x4()); - XMMRegister mask_reg = ToInt32x4Register(instr->first()); + XMMRegister mask_reg = ToBool32x4Register(instr->first()); XMMRegister left_reg = ToInt32x4Register(instr->second()); XMMRegister right_reg = ToInt32x4Register(instr->third()); XMMRegister result_reg = ToInt32x4Register(instr->result()); @@ -4474,20 +4474,52 @@ void LCodeGen::DoQuarternarySIMDOperation(LQuarternarySIMDOperation* instr) { return; } case kBool32x4Constructor: { - DCHECK(instr->hydrogen()->x()->representation().IsInteger32()); - DCHECK(instr->hydrogen()->y()->representation().IsInteger32()); - DCHECK(instr->hydrogen()->z()->representation().IsInteger32()); - DCHECK(instr->hydrogen()->w()->representation().IsInteger32()); + 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)); - __ movl(Operand(rsp, 0 * kBool32Size), x_reg); - __ movl(Operand(rsp, 1 * kBool32Size), y_reg); - __ movl(Operand(rsp, 2 * kBool32Size), z_reg); - __ movl(Operand(rsp, 3 * kBool32Size), w_reg); + __ 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; diff --git a/src/objects.h b/src/objects.h index ebfea53dd21..37daa3fd5e0 100644 --- a/src/objects.h +++ b/src/objects.h @@ -6942,13 +6942,13 @@ class Script: public Struct { 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, Int32x4, Int32x4, Int32x4) \ - V(SIMD.Float32x4, replaceLane, Float32x4ReplaceLane, Float32x4, Float32x4, \ - Integer32, Double) \ - V(SIMD.Int32x4, replaceLane, Int32x4ReplaceLane, Int32x4, Int32x4, \ +#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) \ @@ -6956,8 +6956,8 @@ class Script: public Struct { Double) \ V(SIMD, Int32x4, Int32x4Constructor, Int32x4, Integer32, Integer32, \ Integer32, Integer32) \ - V(SIMD, Bool32x4, Bool32x4Constructor, Bool32x4, 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, \ From 67fc8c293045b597c623b448854e73b47f936bc7 Mon Sep 17 00:00:00 2001 From: chuan9 Date: Wed, 29 Jun 2016 11:27:02 +0800 Subject: [PATCH 3/3] Add SIMD.Bool32x4.allTrue in crankshaft, use SIMD.Int32x4 to represent result of SIMD.Float32x4.lessThanOrEqual to accelerate mandlebrot, wirte testcase for SIMID.Bool32x4.anyTrue and SIMD.Bool32x4.allTrue. Change xmmscratch from xmm0 to xmm15 in kInt32x4Select on x64 It's a workaround and need to be refined. BUG=XWALK-6040 --- src/crankshaft/ia32/lithium-codegen-ia32.cc | 15 ++++++ src/crankshaft/ia32/lithium-ia32.cc | 1 + src/crankshaft/x64/lithium-codegen-x64.cc | 23 +++++++-- src/crankshaft/x64/lithium-x64.cc | 1 + src/objects.h | 3 +- test/mjsunit/harmony/simd/bool32x4.js | 55 +++++++++++++++++++++ 6 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 test/mjsunit/harmony/simd/bool32x4.js diff --git a/src/crankshaft/ia32/lithium-codegen-ia32.cc b/src/crankshaft/ia32/lithium-codegen-ia32.cc index 40d82d16991..cfd8d8bf494 100644 --- a/src/crankshaft/ia32/lithium-codegen-ia32.cc +++ b/src/crankshaft/ia32/lithium-codegen-ia32.cc @@ -5807,6 +5807,21 @@ void LCodeGen::DoUnarySIMDOperation(LUnarySIMDOperation* instr) { __ 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: diff --git a/src/crankshaft/ia32/lithium-ia32.cc b/src/crankshaft/ia32/lithium-ia32.cc index 9f9fe6eb910..1981c693f83 100644 --- a/src/crankshaft/ia32/lithium-ia32.cc +++ b/src/crankshaft/ia32/lithium-ia32.cc @@ -2697,6 +2697,7 @@ LInstruction* LChunkBuilder::DoUnarySIMDOperation(HUnarySIMDOperation* instr) { case kInt32x4GetZ: case kInt32x4GetW: case kBool32x4AnyTrue: + case kBool32x4AllTrue: case kInt32x4GetFlagX: case kInt32x4GetFlagY: case kInt32x4GetFlagZ: diff --git a/src/crankshaft/x64/lithium-codegen-x64.cc b/src/crankshaft/x64/lithium-codegen-x64.cc index 59e1a2b605f..dbc1c322498 100644 --- a/src/crankshaft/x64/lithium-codegen-x64.cc +++ b/src/crankshaft/x64/lithium-codegen-x64.cc @@ -3828,6 +3828,21 @@ void LCodeGen::DoUnarySIMDOperation(LUnarySIMDOperation* instr) { __ 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: @@ -4114,7 +4129,7 @@ void LCodeGen::DoBinarySIMDOperation(LBinarySIMDOperation* instr) { DCHECK(instr->hydrogen()->right()->representation().IsFloat32x4()); XMMRegister left_reg = ToFloat32x4Register(instr->left()); XMMRegister right_reg = ToFloat32x4Register(instr->right()); - XMMRegister result_reg = ToInt32x4Register(instr->result()); + XMMRegister result_reg = ToBool32x4Register(instr->result()); switch (instr->op()) { case kFloat32x4LessThan: if (result_reg.is(left_reg)) { @@ -4259,11 +4274,11 @@ void LCodeGen::DoTernarySIMDOperation(LTernarySIMDOperation* instr) { uint8_t imm8 = 0; switch (instr->op()) { case kFloat32x4Select: { - DCHECK(instr->hydrogen()->first()->representation().IsInt32x4()); + DCHECK(instr->hydrogen()->first()->representation().IsBool32x4()); DCHECK(instr->hydrogen()->second()->representation().IsFloat32x4()); DCHECK(instr->hydrogen()->third()->representation().IsFloat32x4()); - XMMRegister mask_reg = ToInt32x4Register(instr->first()); + XMMRegister mask_reg = ToBool32x4Register(instr->first()); XMMRegister left_reg = ToFloat32x4Register(instr->second()); XMMRegister right_reg = ToFloat32x4Register(instr->third()); XMMRegister result_reg = ToFloat32x4Register(instr->result()); @@ -4306,7 +4321,7 @@ void LCodeGen::DoTernarySIMDOperation(LTernarySIMDOperation* instr) { XMMRegister left_reg = ToInt32x4Register(instr->second()); XMMRegister right_reg = ToInt32x4Register(instr->third()); XMMRegister result_reg = ToInt32x4Register(instr->result()); - XMMRegister temp_reg = xmm0; + XMMRegister temp_reg = double_scratch0(); // Copy mask. __ movaps(temp_reg, mask_reg); diff --git a/src/crankshaft/x64/lithium-x64.cc b/src/crankshaft/x64/lithium-x64.cc index 1ebd9b2567e..d7a3a8624b5 100644 --- a/src/crankshaft/x64/lithium-x64.cc +++ b/src/crankshaft/x64/lithium-x64.cc @@ -1312,6 +1312,7 @@ LInstruction* LChunkBuilder::DoUnarySIMDOperation(HUnarySIMDOperation* instr) { case kInt32x4GetZ: case kInt32x4GetW: case kBool32x4AnyTrue: + case kBool32x4AllTrue: case kInt32x4GetFlagX: case kInt32x4GetFlagY: case kInt32x4GetFlagZ: diff --git a/src/objects.h b/src/objects.h index 37daa3fd5e0..4589d89164e 100644 --- a/src/objects.h +++ b/src/objects.h @@ -6884,7 +6884,8 @@ class Script: public Struct { 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, 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) \ 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();