Commit cbc1c0fc authored by kbr@chromium.org's avatar kbr@chromium.org

Truncate rather than round to nearest when performing float-to-integer

conversions for external array types, which implement the Typed Array
spec. (Revision of http://codereview.chromium.org/6315004 .)

Prefer SSE2 code path on x86 processors. Non-SSE2 processors now make
a slow runtime call for float-to-int conversions. Use SSE3 for 32-bit
signed and unsigned int array types where possible.

The movement of code from ic-arm.cc to stub-cache-arm.cc caused the
VFP3 code path to be tested for the first time. Fixed bugs in the
register usage and in the constant value stored into integer arrays
for NaN and +/-Infinity.

Added new truncation test to test-api.cc. Storage of NaN and +/-Inf
was already covered. Ran unit tests on x86, x64 and ARM simulator.
Tested ia32 and x64 code in Chromium on Mac and Linux respectively
with Typed Array unit tests and WebGL content.

BUG=http://code.google.com/p/chromium/issues/detail?id=50972
TEST=test-api/ExternalArrays

Review URL: http://codereview.chromium.org/6303012

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6431 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent bb9c9fe9
This diff is collapsed.
This diff is collapsed.
......@@ -1282,44 +1282,6 @@ static void Generate_KeyedLoadIC_String(MacroAssembler* masm) {
}
static void Generate_KeyedLoadIC_ExternalByteArray(MacroAssembler* masm) {
KeyedLoadIC::GenerateExternalArray(masm, kExternalByteArray);
}
static void Generate_KeyedLoadIC_ExternalUnsignedByteArray(
MacroAssembler* masm) {
KeyedLoadIC::GenerateExternalArray(masm, kExternalUnsignedByteArray);
}
static void Generate_KeyedLoadIC_ExternalShortArray(MacroAssembler* masm) {
KeyedLoadIC::GenerateExternalArray(masm, kExternalShortArray);
}
static void Generate_KeyedLoadIC_ExternalUnsignedShortArray(
MacroAssembler* masm) {
KeyedLoadIC::GenerateExternalArray(masm, kExternalUnsignedShortArray);
}
static void Generate_KeyedLoadIC_ExternalIntArray(MacroAssembler* masm) {
KeyedLoadIC::GenerateExternalArray(masm, kExternalIntArray);
}
static void Generate_KeyedLoadIC_ExternalUnsignedIntArray(
MacroAssembler* masm) {
KeyedLoadIC::GenerateExternalArray(masm, kExternalUnsignedIntArray);
}
static void Generate_KeyedLoadIC_ExternalFloatArray(MacroAssembler* masm) {
KeyedLoadIC::GenerateExternalArray(masm, kExternalFloatArray);
}
static void Generate_KeyedLoadIC_PreMonomorphic(MacroAssembler* masm) {
KeyedLoadIC::GeneratePreMonomorphic(masm);
}
......@@ -1364,44 +1326,6 @@ static void Generate_KeyedStoreIC_Generic(MacroAssembler* masm) {
}
static void Generate_KeyedStoreIC_ExternalByteArray(MacroAssembler* masm) {
KeyedStoreIC::GenerateExternalArray(masm, kExternalByteArray);
}
static void Generate_KeyedStoreIC_ExternalUnsignedByteArray(
MacroAssembler* masm) {
KeyedStoreIC::GenerateExternalArray(masm, kExternalUnsignedByteArray);
}
static void Generate_KeyedStoreIC_ExternalShortArray(MacroAssembler* masm) {
KeyedStoreIC::GenerateExternalArray(masm, kExternalShortArray);
}
static void Generate_KeyedStoreIC_ExternalUnsignedShortArray(
MacroAssembler* masm) {
KeyedStoreIC::GenerateExternalArray(masm, kExternalUnsignedShortArray);
}
static void Generate_KeyedStoreIC_ExternalIntArray(MacroAssembler* masm) {
KeyedStoreIC::GenerateExternalArray(masm, kExternalIntArray);
}
static void Generate_KeyedStoreIC_ExternalUnsignedIntArray(
MacroAssembler* masm) {
KeyedStoreIC::GenerateExternalArray(masm, kExternalUnsignedIntArray);
}
static void Generate_KeyedStoreIC_ExternalFloatArray(MacroAssembler* masm) {
KeyedStoreIC::GenerateExternalArray(masm, kExternalFloatArray);
}
static void Generate_KeyedStoreIC_Miss(MacroAssembler* masm) {
KeyedStoreIC::GenerateMiss(masm);
}
......
......@@ -93,13 +93,6 @@ enum BuiltinExtraArguments {
V(KeyedLoadIC_PreMonomorphic, KEYED_LOAD_IC, PREMONOMORPHIC) \
V(KeyedLoadIC_Generic, KEYED_LOAD_IC, MEGAMORPHIC) \
V(KeyedLoadIC_String, KEYED_LOAD_IC, MEGAMORPHIC) \
V(KeyedLoadIC_ExternalByteArray, KEYED_LOAD_IC, MEGAMORPHIC) \
V(KeyedLoadIC_ExternalUnsignedByteArray, KEYED_LOAD_IC, MEGAMORPHIC) \
V(KeyedLoadIC_ExternalShortArray, KEYED_LOAD_IC, MEGAMORPHIC) \
V(KeyedLoadIC_ExternalUnsignedShortArray, KEYED_LOAD_IC, MEGAMORPHIC) \
V(KeyedLoadIC_ExternalIntArray, KEYED_LOAD_IC, MEGAMORPHIC) \
V(KeyedLoadIC_ExternalUnsignedIntArray, KEYED_LOAD_IC, MEGAMORPHIC) \
V(KeyedLoadIC_ExternalFloatArray, KEYED_LOAD_IC, MEGAMORPHIC) \
V(KeyedLoadIC_IndexedInterceptor, KEYED_LOAD_IC, MEGAMORPHIC) \
\
V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \
......@@ -110,13 +103,6 @@ enum BuiltinExtraArguments {
\
V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED) \
V(KeyedStoreIC_Generic, KEYED_STORE_IC, MEGAMORPHIC) \
V(KeyedStoreIC_ExternalByteArray, KEYED_STORE_IC, MEGAMORPHIC) \
V(KeyedStoreIC_ExternalUnsignedByteArray, KEYED_STORE_IC, MEGAMORPHIC) \
V(KeyedStoreIC_ExternalShortArray, KEYED_STORE_IC, MEGAMORPHIC) \
V(KeyedStoreIC_ExternalUnsignedShortArray, KEYED_STORE_IC, MEGAMORPHIC) \
V(KeyedStoreIC_ExternalIntArray, KEYED_STORE_IC, MEGAMORPHIC) \
V(KeyedStoreIC_ExternalUnsignedIntArray, KEYED_STORE_IC, MEGAMORPHIC) \
V(KeyedStoreIC_ExternalFloatArray, KEYED_STORE_IC, MEGAMORPHIC) \
\
/* Uses KeyedLoadIC_Initialize; must be after in list. */ \
V(FunctionCall, BUILTIN, UNINITIALIZED) \
......
......@@ -204,7 +204,9 @@ namespace internal {
V(global_eval_symbol, "GlobalEval") \
V(identity_hash_symbol, "v8::IdentityHash") \
V(closure_symbol, "(closure)") \
V(use_strict, "use strict")
V(use_strict, "use strict") \
V(KeyedLoadExternalArray_symbol, "KeyedLoadExternalArray") \
V(KeyedStoreExternalArray_symbol, "KeyedStoreExternalArray")
// Forward declarations.
......
This diff is collapsed.
This diff is collapsed.
......@@ -367,55 +367,6 @@ void KeyedStoreIC::Clear(Address address, Code* target) {
}
Code* KeyedLoadIC::external_array_stub(JSObject::ElementsKind elements_kind) {
switch (elements_kind) {
case JSObject::EXTERNAL_BYTE_ELEMENTS:
return Builtins::builtin(Builtins::KeyedLoadIC_ExternalByteArray);
case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedByteArray);
case JSObject::EXTERNAL_SHORT_ELEMENTS:
return Builtins::builtin(Builtins::KeyedLoadIC_ExternalShortArray);
case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
return Builtins::builtin(
Builtins::KeyedLoadIC_ExternalUnsignedShortArray);
case JSObject::EXTERNAL_INT_ELEMENTS:
return Builtins::builtin(Builtins::KeyedLoadIC_ExternalIntArray);
case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedIntArray);
case JSObject::EXTERNAL_FLOAT_ELEMENTS:
return Builtins::builtin(Builtins::KeyedLoadIC_ExternalFloatArray);
default:
UNREACHABLE();
return NULL;
}
}
Code* KeyedStoreIC::external_array_stub(JSObject::ElementsKind elements_kind) {
switch (elements_kind) {
case JSObject::EXTERNAL_BYTE_ELEMENTS:
return Builtins::builtin(Builtins::KeyedStoreIC_ExternalByteArray);
case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
return Builtins::builtin(
Builtins::KeyedStoreIC_ExternalUnsignedByteArray);
case JSObject::EXTERNAL_SHORT_ELEMENTS:
return Builtins::builtin(Builtins::KeyedStoreIC_ExternalShortArray);
case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
return Builtins::builtin(
Builtins::KeyedStoreIC_ExternalUnsignedShortArray);
case JSObject::EXTERNAL_INT_ELEMENTS:
return Builtins::builtin(Builtins::KeyedStoreIC_ExternalIntArray);
case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
return Builtins::builtin(Builtins::KeyedStoreIC_ExternalUnsignedIntArray);
case JSObject::EXTERNAL_FLOAT_ELEMENTS:
return Builtins::builtin(Builtins::KeyedStoreIC_ExternalFloatArray);
default:
UNREACHABLE();
return NULL;
}
}
static bool HasInterceptorGetter(JSObject* object) {
return !object->GetNamedInterceptor()->getter()->IsUndefined();
}
......@@ -1243,7 +1194,10 @@ MaybeObject* KeyedLoadIC::Load(State state,
} else if (object->IsJSObject()) {
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
if (receiver->HasExternalArrayElements()) {
stub = external_array_stub(receiver->GetElementsKind());
MaybeObject* probe =
StubCache::ComputeKeyedLoadOrStoreExternalArray(*receiver, false);
stub =
probe->IsFailure() ? NULL : Code::cast(probe->ToObjectUnchecked());
} else if (receiver->HasIndexedInterceptor()) {
stub = indexed_interceptor_stub();
} else if (state == UNINITIALIZED &&
......@@ -1636,7 +1590,10 @@ MaybeObject* KeyedStoreIC::Store(State state,
if (object->IsJSObject()) {
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
if (receiver->HasExternalArrayElements()) {
stub = external_array_stub(receiver->GetElementsKind());
MaybeObject* probe =
StubCache::ComputeKeyedLoadOrStoreExternalArray(*receiver, true);
stub =
probe->IsFailure() ? NULL : Code::cast(probe->ToObjectUnchecked());
} else if (state == UNINITIALIZED &&
key->IsSmi() &&
receiver->map()->has_fast_elements()) {
......
......@@ -345,12 +345,6 @@ class KeyedLoadIC: public IC {
static void GenerateGeneric(MacroAssembler* masm);
static void GenerateString(MacroAssembler* masm);
// Generators for external array types. See objects.h.
// These are similar to the generic IC; they optimize the case of
// operating upon external array types but fall back to the runtime
// for all other types.
static void GenerateExternalArray(MacroAssembler* masm,
ExternalArrayType array_type);
static void GenerateIndexedInterceptor(MacroAssembler* masm);
// Clear the use of the inlined version.
......@@ -386,7 +380,6 @@ class KeyedLoadIC: public IC {
static Code* string_stub() {
return Builtins::builtin(Builtins::KeyedLoadIC_String);
}
static Code* external_array_stub(JSObject::ElementsKind elements_kind);
static Code* indexed_interceptor_stub() {
return Builtins::builtin(Builtins::KeyedLoadIC_IndexedInterceptor);
......@@ -470,13 +463,6 @@ class KeyedStoreIC: public IC {
static void GenerateRuntimeSetProperty(MacroAssembler* masm);
static void GenerateGeneric(MacroAssembler* masm);
// Generators for external array types. See objects.h.
// These are similar to the generic IC; they optimize the case of
// operating upon external array types but fall back to the runtime
// for all other types.
static void GenerateExternalArray(MacroAssembler* masm,
ExternalArrayType array_type);
// Clear the inlined version so the IC is always hit.
static void ClearInlinedVersion(Address address);
......@@ -501,7 +487,6 @@ class KeyedStoreIC: public IC {
static Code* generic_stub() {
return Builtins::builtin(Builtins::KeyedStoreIC_Generic);
}
static Code* external_array_stub(JSObject::ElementsKind elements_kind);
static void Clear(Address address, Code* target);
......
......@@ -172,23 +172,11 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
}
void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
ExternalArrayType array_type) {
UNIMPLEMENTED_MIPS();
}
void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
UNIMPLEMENTED_MIPS();
}
void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm,
ExternalArrayType array_type) {
UNIMPLEMENTED_MIPS();
}
void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
UNIMPLEMENTED_MIPS();
}
......
......@@ -397,6 +397,20 @@ Object* ConstructStubCompiler::CompileConstructStub(
}
Object* ExternalArrayStubCompiler::CompileKeyedLoadStub(
ExternalArrayType array_type, Code::Flags flags) {
UNIMPLEMENTED_MIPS();
return reinterpret_cast<Object*>(NULL); // UNIMPLEMENTED RETURN
}
Object* ExternalArrayStubCompiler::CompileKeyedStoreStub(
ExternalArrayType array_type, Code::Flags flags) {
UNIMPLEMENTED_MIPS();
return reinterpret_cast<Object*>(NULL); // UNIMPLEMENTED RETURN
}
#undef __
} } // namespace v8::internal
......
......@@ -507,6 +507,74 @@ MaybeObject* StubCache::ComputeKeyedStoreSpecialized(JSObject* receiver) {
}
namespace {
ExternalArrayType ElementsKindToExternalArrayType(JSObject::ElementsKind kind) {
switch (kind) {
case JSObject::EXTERNAL_BYTE_ELEMENTS:
return kExternalByteArray;
case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
return kExternalUnsignedByteArray;
case JSObject::EXTERNAL_SHORT_ELEMENTS:
return kExternalShortArray;
case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
return kExternalUnsignedShortArray;
case JSObject::EXTERNAL_INT_ELEMENTS:
return kExternalIntArray;
case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
return kExternalUnsignedIntArray;
case JSObject::EXTERNAL_FLOAT_ELEMENTS:
return kExternalFloatArray;
default:
UNREACHABLE();
return static_cast<ExternalArrayType>(0);
}
}
} // anonymous namespace
MaybeObject* StubCache::ComputeKeyedLoadOrStoreExternalArray(
JSObject* receiver,
bool is_store) {
Code::Flags flags =
Code::ComputeMonomorphicFlags(
is_store ? Code::KEYED_STORE_IC : Code::KEYED_LOAD_IC,
NORMAL);
ExternalArrayType array_type =
ElementsKindToExternalArrayType(receiver->GetElementsKind());
String* name =
is_store ? Heap::KeyedStoreExternalArray_symbol()
: Heap::KeyedLoadExternalArray_symbol();
// Use the global maps for the particular external array types,
// rather than the receiver's map, when looking up the cached code,
// so that we actually canonicalize these stubs.
Map* map = Heap::MapForExternalArrayType(array_type);
Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
ExternalArrayStubCompiler compiler;
{ MaybeObject* maybe_code =
is_store ? compiler.CompileKeyedStoreStub(array_type, flags) :
compiler.CompileKeyedLoadStub(array_type, flags);
if (!maybe_code->ToObject(&code)) return maybe_code;
}
if (is_store) {
PROFILE(
CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, Code::cast(code), 0));
} else {
PROFILE(
CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), 0));
}
Object* result;
{ MaybeObject* maybe_result =
map->UpdateCodeCache(name, Code::cast(code));
if (!maybe_result->ToObject(&result)) return maybe_result;
}
}
return code;
}
MaybeObject* StubCache::ComputeStoreNormal() {
return Builtins::builtin(Builtins::StoreIC_Normal);
}
......@@ -1709,4 +1777,16 @@ void CallOptimization::AnalyzePossibleApiFunction(JSFunction* function) {
}
MaybeObject* ExternalArrayStubCompiler::GetCode(Code::Flags flags) {
Object* result;
{ MaybeObject* maybe_result = GetCodeWithFlags(flags, "ExternalArrayStub");
if (!maybe_result->ToObject(&result)) return maybe_result;
}
Code* code = Code::cast(result);
USE(code);
PROFILE(CodeCreateEvent(Logger::STUB_TAG, code, "ExternalArrayStub"));
return result;
}
} } // namespace v8::internal
......@@ -167,6 +167,10 @@ class StubCache : public AllStatic {
MUST_USE_RESULT static MaybeObject* ComputeKeyedStoreSpecialized(
JSObject* receiver);
MUST_USE_RESULT static MaybeObject* ComputeKeyedLoadOrStoreExternalArray(
JSObject* receiver,
bool is_store);
// ---
MUST_USE_RESULT static MaybeObject* ComputeCallField(int argc,
......@@ -797,6 +801,20 @@ class CallOptimization BASE_EMBEDDED {
CallHandlerInfo* api_call_info_;
};
class ExternalArrayStubCompiler: public StubCompiler {
public:
explicit ExternalArrayStubCompiler() {}
MUST_USE_RESULT MaybeObject* CompileKeyedLoadStub(
ExternalArrayType array_type, Code::Flags flags);
MUST_USE_RESULT MaybeObject* CompileKeyedStoreStub(
ExternalArrayType array_type, Code::Flags flags);
private:
MaybeObject* GetCode(Code::Flags flags);
};
} } // namespace v8::internal
#endif // V8_STUB_CACHE_H_
......@@ -2721,6 +2721,17 @@ void Assembler::cvttss2si(Register dst, const Operand& src) {
}
void Assembler::cvttss2si(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit(0xF3);
emit_optional_rex_32(dst, src);
emit(0x0F);
emit(0x2C);
emit_sse_operand(dst, src);
}
void Assembler::cvttsd2si(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
......@@ -2732,6 +2743,17 @@ void Assembler::cvttsd2si(Register dst, const Operand& src) {
}
void Assembler::cvttsd2si(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
emit(0x2C);
emit_sse_operand(dst, src);
}
void Assembler::cvttsd2siq(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
......
......@@ -1209,7 +1209,9 @@ class Assembler : public Malloced {
void movss(const Operand& dst, XMMRegister src);
void cvttss2si(Register dst, const Operand& src);
void cvttss2si(Register dst, XMMRegister src);
void cvttsd2si(Register dst, const Operand& src);
void cvttsd2si(Register dst, XMMRegister src);
void cvttsd2siq(Register dst, XMMRegister src);
void cvtlsi2sd(XMMRegister dst, const Operand& src);
......
......@@ -1113,9 +1113,11 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
} else if (opcode == 0x2C) {
// CVTTSS2SI:
// Convert with truncation scalar single-precision FP to dword integer.
// Assert that mod is not 3, so source is memory, not an XMM register.
ASSERT_NE(0xC0, *current & 0xC0);
current += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, current);
int mod, regop, rm;
get_modrm(*current, &mod, &regop, &rm);
AppendToBuffer("cvttss2si%c %s,",
operand_size_code(), NameOfCPURegister(regop));
current += PrintRightXMMOperand(current);
} else if (opcode == 0x5A) {
// CVTSS2SD:
// Convert scalar single-precision FP to scalar double-precision FP.
......
......@@ -727,131 +727,6 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
}
void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
ExternalArrayType array_type) {
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label slow;
// Check that the object isn't a smi.
__ JumpIfSmi(rdx, &slow);
// Check that the key is a smi.
__ JumpIfNotSmi(rax, &slow);
// Check that the object is a JS object.
__ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx);
__ j(not_equal, &slow);
// Check that the receiver does not require access checks. We need
// to check this explicitly since this generic stub does not perform
// map checks. The map is already in rdx.
__ testb(FieldOperand(rcx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &slow);
// Check that the elements array is the appropriate type of
// ExternalArray.
// rax: index (as a smi)
// rdx: JSObject
__ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
__ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
Heap::RootIndexForExternalArrayType(array_type));
__ j(not_equal, &slow);
// Check that the index is in range.
__ SmiToInteger32(rcx, rax);
__ cmpl(rcx, FieldOperand(rbx, ExternalArray::kLengthOffset));
// Unsigned comparison catches both negative and too-large values.
__ j(above_equal, &slow);
// rax: index (as a smi)
// rdx: receiver (JSObject)
// rcx: untagged index
// rbx: elements array
__ movq(rbx, FieldOperand(rbx, ExternalArray::kExternalPointerOffset));
// rbx: base pointer of external storage
switch (array_type) {
case kExternalByteArray:
__ movsxbq(rcx, Operand(rbx, rcx, times_1, 0));
break;
case kExternalUnsignedByteArray:
__ movzxbq(rcx, Operand(rbx, rcx, times_1, 0));
break;
case kExternalShortArray:
__ movsxwq(rcx, Operand(rbx, rcx, times_2, 0));
break;
case kExternalUnsignedShortArray:
__ movzxwq(rcx, Operand(rbx, rcx, times_2, 0));
break;
case kExternalIntArray:
__ movsxlq(rcx, Operand(rbx, rcx, times_4, 0));
break;
case kExternalUnsignedIntArray:
__ movl(rcx, Operand(rbx, rcx, times_4, 0));
break;
case kExternalFloatArray:
__ cvtss2sd(xmm0, Operand(rbx, rcx, times_4, 0));
break;
default:
UNREACHABLE();
break;
}
// rax: index
// rdx: receiver
// For integer array types:
// rcx: value
// For floating-point array type:
// xmm0: value as double.
ASSERT(kSmiValueSize == 32);
if (array_type == kExternalUnsignedIntArray) {
// For the UnsignedInt array type, we need to see whether
// the value can be represented in a Smi. If not, we need to convert
// it to a HeapNumber.
NearLabel box_int;
__ JumpIfUIntNotValidSmiValue(rcx, &box_int);
__ Integer32ToSmi(rax, rcx);
__ ret(0);
__ bind(&box_int);
// Allocate a HeapNumber for the int and perform int-to-double
// conversion.
// The value is zero-extended since we loaded the value from memory
// with movl.
__ cvtqsi2sd(xmm0, rcx);
__ AllocateHeapNumber(rcx, rbx, &slow);
// Set the value.
__ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0);
__ movq(rax, rcx);
__ ret(0);
} else if (array_type == kExternalFloatArray) {
// For the floating-point array type, we need to always allocate a
// HeapNumber.
__ AllocateHeapNumber(rcx, rbx, &slow);
// Set the value.
__ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0);
__ movq(rax, rcx);
__ ret(0);
} else {
__ Integer32ToSmi(rax, rcx);
__ ret(0);
}
// Slow case: Jump to runtime.
__ bind(&slow);
__ IncrementCounter(&Counters::keyed_load_external_array_slow, 1);
GenerateRuntimeGetProperty(masm);
}
void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : key
......@@ -1023,149 +898,6 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
}
void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm,
ExternalArrayType array_type) {
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : key
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label slow;
// Check that the object isn't a smi.
__ JumpIfSmi(rdx, &slow);
// Get the map from the receiver.
__ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset));
// Check that the receiver does not require access checks. We need
// to do this because this generic stub does not perform map checks.
__ testb(FieldOperand(rbx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &slow);
// Check that the key is a smi.
__ JumpIfNotSmi(rcx, &slow);
// Check that the object is a JS object.
__ CmpInstanceType(rbx, JS_OBJECT_TYPE);
__ j(not_equal, &slow);
// Check that the elements array is the appropriate type of
// ExternalArray.
// rax: value
// rcx: key (a smi)
// rdx: receiver (a JSObject)
__ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
__ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
Heap::RootIndexForExternalArrayType(array_type));
__ j(not_equal, &slow);
// Check that the index is in range.
__ SmiToInteger32(rdi, rcx); // Untag the index.
__ cmpl(rdi, FieldOperand(rbx, ExternalArray::kLengthOffset));
// Unsigned comparison catches both negative and too-large values.
__ j(above_equal, &slow);
// Handle both smis and HeapNumbers in the fast path. Go to the
// runtime for all other kinds of values.
// rax: value
// rcx: key (a smi)
// rdx: receiver (a JSObject)
// rbx: elements array
// rdi: untagged key
NearLabel check_heap_number;
__ JumpIfNotSmi(rax, &check_heap_number);
// No more branches to slow case on this path. Key and receiver not needed.
__ SmiToInteger32(rdx, rax);
__ movq(rbx, FieldOperand(rbx, ExternalArray::kExternalPointerOffset));
// rbx: base pointer of external storage
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
__ movb(Operand(rbx, rdi, times_1, 0), rdx);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
__ movw(Operand(rbx, rdi, times_2, 0), rdx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
__ movl(Operand(rbx, rdi, times_4, 0), rdx);
break;
case kExternalFloatArray:
// Need to perform int-to-float conversion.
__ cvtlsi2ss(xmm0, rdx);
__ movss(Operand(rbx, rdi, times_4, 0), xmm0);
break;
default:
UNREACHABLE();
break;
}
__ ret(0);
__ bind(&check_heap_number);
// rax: value
// rcx: key (a smi)
// rdx: receiver (a JSObject)
// rbx: elements array
// rdi: untagged key
__ CmpObjectType(rax, HEAP_NUMBER_TYPE, kScratchRegister);
__ j(not_equal, &slow);
// No more branches to slow case on this path.
// The WebGL specification leaves the behavior of storing NaN and
// +/-Infinity into integer arrays basically undefined. For more
// reproducible behavior, convert these to zero.
__ movsd(xmm0, FieldOperand(rax, HeapNumber::kValueOffset));
__ movq(rbx, FieldOperand(rbx, ExternalArray::kExternalPointerOffset));
// rdi: untagged index
// rbx: base pointer of external storage
// top of FPU stack: value
if (array_type == kExternalFloatArray) {
__ cvtsd2ss(xmm0, xmm0);
__ movss(Operand(rbx, rdi, times_4, 0), xmm0);
__ ret(0);
} else {
// Need to perform float-to-int conversion.
// Test the value for NaN.
// Convert to int32 and store the low byte/word.
// If the value is NaN or +/-infinity, the result is 0x80000000,
// which is automatically zero when taken mod 2^n, n < 32.
// rdx: value (converted to an untagged integer)
// rdi: untagged index
// rbx: base pointer of external storage
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
__ cvtsd2si(rdx, xmm0);
__ movb(Operand(rbx, rdi, times_1, 0), rdx);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
__ cvtsd2si(rdx, xmm0);
__ movw(Operand(rbx, rdi, times_2, 0), rdx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray: {
// Convert to int64, so that NaN and infinities become
// 0x8000000000000000, which is zero mod 2^32.
__ cvtsd2siq(rdx, xmm0);
__ movl(Operand(rbx, rdi, times_4, 0), rdx);
break;
}
default:
UNREACHABLE();
break;
}
__ ret(0);
}
// Slow case: call runtime.
__ bind(&slow);
GenerateRuntimeSetProperty(masm);
}
// The generated code does not accept smi keys.
// The generated code falls through if both probes miss.
static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
......
......@@ -3144,6 +3144,306 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) {
}
MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
ExternalArrayType array_type, Code::Flags flags) {
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label slow;
// Check that the object isn't a smi.
__ JumpIfSmi(rdx, &slow);
// Check that the key is a smi.
__ JumpIfNotSmi(rax, &slow);
// Check that the object is a JS object.
__ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx);
__ j(not_equal, &slow);
// Check that the receiver does not require access checks. We need
// to check this explicitly since this generic stub does not perform
// map checks. The map is already in rdx.
__ testb(FieldOperand(rcx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &slow);
// Check that the elements array is the appropriate type of
// ExternalArray.
// rax: index (as a smi)
// rdx: JSObject
__ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
__ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
Heap::RootIndexForExternalArrayType(array_type));
__ j(not_equal, &slow);
// Check that the index is in range.
__ SmiToInteger32(rcx, rax);
__ cmpl(rcx, FieldOperand(rbx, ExternalArray::kLengthOffset));
// Unsigned comparison catches both negative and too-large values.
__ j(above_equal, &slow);
// rax: index (as a smi)
// rdx: receiver (JSObject)
// rcx: untagged index
// rbx: elements array
__ movq(rbx, FieldOperand(rbx, ExternalArray::kExternalPointerOffset));
// rbx: base pointer of external storage
switch (array_type) {
case kExternalByteArray:
__ movsxbq(rcx, Operand(rbx, rcx, times_1, 0));
break;
case kExternalUnsignedByteArray:
__ movzxbq(rcx, Operand(rbx, rcx, times_1, 0));
break;
case kExternalShortArray:
__ movsxwq(rcx, Operand(rbx, rcx, times_2, 0));
break;
case kExternalUnsignedShortArray:
__ movzxwq(rcx, Operand(rbx, rcx, times_2, 0));
break;
case kExternalIntArray:
__ movsxlq(rcx, Operand(rbx, rcx, times_4, 0));
break;
case kExternalUnsignedIntArray:
__ movl(rcx, Operand(rbx, rcx, times_4, 0));
break;
case kExternalFloatArray:
__ cvtss2sd(xmm0, Operand(rbx, rcx, times_4, 0));
break;
default:
UNREACHABLE();
break;
}
// rax: index
// rdx: receiver
// For integer array types:
// rcx: value
// For floating-point array type:
// xmm0: value as double.
ASSERT(kSmiValueSize == 32);
if (array_type == kExternalUnsignedIntArray) {
// For the UnsignedInt array type, we need to see whether
// the value can be represented in a Smi. If not, we need to convert
// it to a HeapNumber.
NearLabel box_int;
__ JumpIfUIntNotValidSmiValue(rcx, &box_int);
__ Integer32ToSmi(rax, rcx);
__ ret(0);
__ bind(&box_int);
// Allocate a HeapNumber for the int and perform int-to-double
// conversion.
// The value is zero-extended since we loaded the value from memory
// with movl.
__ cvtqsi2sd(xmm0, rcx);
__ AllocateHeapNumber(rcx, rbx, &slow);
// Set the value.
__ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0);
__ movq(rax, rcx);
__ ret(0);
} else if (array_type == kExternalFloatArray) {
// For the floating-point array type, we need to always allocate a
// HeapNumber.
__ AllocateHeapNumber(rcx, rbx, &slow);
// Set the value.
__ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0);
__ movq(rax, rcx);
__ ret(0);
} else {
__ Integer32ToSmi(rax, rcx);
__ ret(0);
}
// Slow case: Jump to runtime.
__ bind(&slow);
__ IncrementCounter(&Counters::keyed_load_external_array_slow, 1);
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
__ pop(rbx);
__ push(rdx); // receiver
__ push(rax); // name
__ push(rbx); // return address
// Perform tail call to the entry.
__ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
// Return the generated code.
return GetCode(flags);
}
MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
ExternalArrayType array_type, Code::Flags flags) {
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : key
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label slow;
// Check that the object isn't a smi.
__ JumpIfSmi(rdx, &slow);
// Get the map from the receiver.
__ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset));
// Check that the receiver does not require access checks. We need
// to do this because this generic stub does not perform map checks.
__ testb(FieldOperand(rbx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &slow);
// Check that the key is a smi.
__ JumpIfNotSmi(rcx, &slow);
// Check that the object is a JS object.
__ CmpInstanceType(rbx, JS_OBJECT_TYPE);
__ j(not_equal, &slow);
// Check that the elements array is the appropriate type of
// ExternalArray.
// rax: value
// rcx: key (a smi)
// rdx: receiver (a JSObject)
__ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
__ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
Heap::RootIndexForExternalArrayType(array_type));
__ j(not_equal, &slow);
// Check that the index is in range.
__ SmiToInteger32(rdi, rcx); // Untag the index.
__ cmpl(rdi, FieldOperand(rbx, ExternalArray::kLengthOffset));
// Unsigned comparison catches both negative and too-large values.
__ j(above_equal, &slow);
// Handle both smis and HeapNumbers in the fast path. Go to the
// runtime for all other kinds of values.
// rax: value
// rcx: key (a smi)
// rdx: receiver (a JSObject)
// rbx: elements array
// rdi: untagged key
NearLabel check_heap_number;
__ JumpIfNotSmi(rax, &check_heap_number);
// No more branches to slow case on this path. Key and receiver not needed.
__ SmiToInteger32(rdx, rax);
__ movq(rbx, FieldOperand(rbx, ExternalArray::kExternalPointerOffset));
// rbx: base pointer of external storage
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
__ movb(Operand(rbx, rdi, times_1, 0), rdx);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
__ movw(Operand(rbx, rdi, times_2, 0), rdx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
__ movl(Operand(rbx, rdi, times_4, 0), rdx);
break;
case kExternalFloatArray:
// Need to perform int-to-float conversion.
__ cvtlsi2ss(xmm0, rdx);
__ movss(Operand(rbx, rdi, times_4, 0), xmm0);
break;
default:
UNREACHABLE();
break;
}
__ ret(0);
__ bind(&check_heap_number);
// rax: value
// rcx: key (a smi)
// rdx: receiver (a JSObject)
// rbx: elements array
// rdi: untagged key
__ CmpObjectType(rax, HEAP_NUMBER_TYPE, kScratchRegister);
__ j(not_equal, &slow);
// No more branches to slow case on this path.
// The WebGL specification leaves the behavior of storing NaN and
// +/-Infinity into integer arrays basically undefined. For more
// reproducible behavior, convert these to zero.
__ movsd(xmm0, FieldOperand(rax, HeapNumber::kValueOffset));
__ movq(rbx, FieldOperand(rbx, ExternalArray::kExternalPointerOffset));
// rdi: untagged index
// rbx: base pointer of external storage
// top of FPU stack: value
if (array_type == kExternalFloatArray) {
__ cvtsd2ss(xmm0, xmm0);
__ movss(Operand(rbx, rdi, times_4, 0), xmm0);
__ ret(0);
} else {
// Perform float-to-int conversion with truncation (round-to-zero)
// behavior.
// Convert to int32 and store the low byte/word.
// If the value is NaN or +/-infinity, the result is 0x80000000,
// which is automatically zero when taken mod 2^n, n < 32.
// rdx: value (converted to an untagged integer)
// rdi: untagged index
// rbx: base pointer of external storage
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
__ cvttsd2si(rdx, xmm0);
__ movb(Operand(rbx, rdi, times_1, 0), rdx);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
__ cvttsd2si(rdx, xmm0);
__ movw(Operand(rbx, rdi, times_2, 0), rdx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray: {
// Convert to int64, so that NaN and infinities become
// 0x8000000000000000, which is zero mod 2^32.
__ cvttsd2siq(rdx, xmm0);
__ movl(Operand(rbx, rdi, times_4, 0), rdx);
break;
}
default:
UNREACHABLE();
break;
}
__ ret(0);
}
// Slow case: call runtime.
__ bind(&slow);
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : key
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
__ pop(rbx);
__ push(rdx); // receiver
__ push(rcx); // key
__ push(rax); // value
__ push(rbx); // return address
// Do tail-call to runtime routine.
__ TailCallRuntime(Runtime::kSetProperty, 3, 1);
return GetCode(flags);
}
#undef __
} } // namespace v8::internal
......
......@@ -10587,6 +10587,33 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
CHECK_EQ(0, result->Int32Value());
CHECK_EQ(0,
i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
// Check truncation behavior of integral arrays.
const char* unsigned_data =
"var source_data = [0.6, 10.6];"
"var expected_results = [0, 10];";
const char* signed_data =
"var source_data = [0.6, 10.6, -0.6, -10.6];"
"var expected_results = [0, 10, 0, -10];";
bool is_unsigned =
(array_type == v8::kExternalUnsignedByteArray ||
array_type == v8::kExternalUnsignedShortArray ||
array_type == v8::kExternalUnsignedIntArray);
i::OS::SNPrintF(test_buf,
"%s"
"var all_passed = true;"
"for (var i = 0; i < source_data.length; i++) {"
" for (var j = 0; j < 8; j++) {"
" ext_array[j] = source_data[i];"
" }"
" all_passed = all_passed &&"
" (ext_array[5] == expected_results[i]);"
"}"
"all_passed;",
(is_unsigned ? unsigned_data : signed_data));
result = CompileRun(test_buf.start());
CHECK_EQ(true, result->BooleanValue());
}
result = CompileRun("ext_array[3] = 33;"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment