S390: Initial impl of debug and ic
Initial implementation of S390 specific debug and IC functions. R=danno@chromium.org,jkummerow@chromium.org,jochen@chromium.org,jyan@ca.ibm.com,michael_dawson@ca.ibm.com,mbrandy@us.ibm.com BUG= Review URL: https://codereview.chromium.org/1743263003 Cr-Commit-Position: refs/heads/master@{#34400}
This commit is contained in:
parent
008981cf12
commit
503d589340
6
BUILD.gn
6
BUILD.gn
@ -1637,6 +1637,12 @@ source_set("v8_base") {
|
||||
]
|
||||
} else if (v8_target_arch == "s390" || v8_target_arch == "s390x") {
|
||||
sources += [
|
||||
"src/debug/s390/debug-s390.cc",
|
||||
"src/ic/s390/access-compiler-s390.cc",
|
||||
"src/ic/s390/handler-compiler-s390.cc",
|
||||
"src/ic/s390/ic-compiler-s390.cc",
|
||||
"src/ic/s390/ic-s390.cc",
|
||||
"src/ic/s390/stub-cache-s390.cc",
|
||||
"src/s390/assembler-s390-inl.h",
|
||||
"src/s390/assembler-s390.cc",
|
||||
"src/s390/assembler-s390.h",
|
||||
|
158
src/debug/s390/debug-s390.cc
Normal file
158
src/debug/s390/debug-s390.cc
Normal file
@ -0,0 +1,158 @@
|
||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/v8.h"
|
||||
|
||||
#if V8_TARGET_ARCH_S390
|
||||
|
||||
#include "src/codegen.h"
|
||||
#include "src/debug/debug.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
void EmitDebugBreakSlot(MacroAssembler* masm) {
|
||||
Label check_size;
|
||||
__ bind(&check_size);
|
||||
// oill r3, 0
|
||||
// oill r3, 0
|
||||
__ nop(Assembler::DEBUG_BREAK_NOP);
|
||||
__ nop(Assembler::DEBUG_BREAK_NOP);
|
||||
|
||||
// lr r0, r0 64-bit only
|
||||
// lr r0, r0 64-bit only
|
||||
// lr r0, r0 64-bit only
|
||||
for (int i = 8; i < Assembler::kDebugBreakSlotLength; i += 2) {
|
||||
__ nop();
|
||||
}
|
||||
DCHECK_EQ(Assembler::kDebugBreakSlotLength,
|
||||
masm->SizeOfCodeGeneratedSince(&check_size));
|
||||
}
|
||||
|
||||
void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) {
|
||||
// Generate enough nop's to make space for a call instruction.
|
||||
masm->RecordDebugBreakSlot(mode);
|
||||
EmitDebugBreakSlot(masm);
|
||||
}
|
||||
|
||||
void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) {
|
||||
CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotLength);
|
||||
EmitDebugBreakSlot(patcher.masm());
|
||||
}
|
||||
|
||||
void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc,
|
||||
Handle<Code> code) {
|
||||
DCHECK_EQ(Code::BUILTIN, code->kind());
|
||||
CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotLength);
|
||||
// Patch the code changing the debug break slot code from
|
||||
//
|
||||
// oill r3, 0
|
||||
// oill r3, 0
|
||||
// oill r3, 0 64-bit only
|
||||
// lr r0, r0 64-bit only
|
||||
//
|
||||
// to a call to the debug break code, using a FIXED_SEQUENCE.
|
||||
//
|
||||
// iilf r14, <address> 6-bytes
|
||||
// basr r14, r14A 2-bytes
|
||||
//
|
||||
// The 64bit sequence has an extra iihf.
|
||||
//
|
||||
// iihf r14, <high 32-bits address> 6-bytes
|
||||
// iilf r14, <lower 32-bits address> 6-bytes
|
||||
// basr r14, r14 2-bytes
|
||||
patcher.masm()->mov(v8::internal::r14,
|
||||
Operand(reinterpret_cast<intptr_t>(code->entry())));
|
||||
patcher.masm()->basr(v8::internal::r14, v8::internal::r14);
|
||||
}
|
||||
|
||||
bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) {
|
||||
Instr current_instr = Assembler::instr_at(pc);
|
||||
return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
|
||||
}
|
||||
|
||||
void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
|
||||
DebugBreakCallHelperMode mode) {
|
||||
__ RecordComment("Debug break");
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
|
||||
// Load padding words on stack.
|
||||
__ LoadSmiLiteral(ip, Smi::FromInt(LiveEdit::kFramePaddingValue));
|
||||
for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
|
||||
__ push(ip);
|
||||
}
|
||||
__ LoadSmiLiteral(ip, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
|
||||
__ push(ip);
|
||||
|
||||
if (mode == SAVE_RESULT_REGISTER) __ push(r2);
|
||||
|
||||
__ mov(r2, Operand::Zero()); // no arguments
|
||||
__ mov(r3,
|
||||
Operand(ExternalReference(
|
||||
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
|
||||
|
||||
CEntryStub ceb(masm->isolate(), 1);
|
||||
__ CallStub(&ceb);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
for (int i = 0; i < kNumJSCallerSaved; i++) {
|
||||
Register reg = {JSCallerSavedCode(i)};
|
||||
__ mov(reg, Operand(kDebugZapValue));
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == SAVE_RESULT_REGISTER) __ pop(r2);
|
||||
|
||||
// Don't bother removing padding bytes pushed on the stack
|
||||
// as the frame is going to be restored right away.
|
||||
|
||||
// Leave the internal frame.
|
||||
}
|
||||
|
||||
// Now that the break point has been handled, resume normal execution by
|
||||
// jumping to the target address intended by the caller and that was
|
||||
// overwritten by the address of DebugBreakXXX.
|
||||
ExternalReference after_break_target =
|
||||
ExternalReference::debug_after_break_target_address(masm->isolate());
|
||||
__ mov(ip, Operand(after_break_target));
|
||||
__ LoadP(ip, MemOperand(ip));
|
||||
__ JumpToJSEntry(ip);
|
||||
}
|
||||
|
||||
void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
|
||||
// Load the function pointer off of our current stack frame.
|
||||
__ LoadP(r3, MemOperand(fp, StandardFrameConstants::kConstantPoolOffset -
|
||||
kPointerSize));
|
||||
|
||||
// Pop return address and frame
|
||||
__ LeaveFrame(StackFrame::INTERNAL);
|
||||
|
||||
ParameterCount dummy(0);
|
||||
__ FloodFunctionIfStepping(r3, no_reg, dummy, dummy);
|
||||
|
||||
// Load context from the function.
|
||||
__ LoadP(cp, FieldMemOperand(r3, JSFunction::kContextOffset));
|
||||
|
||||
// Clear new.target as a safety measure.
|
||||
__ LoadRoot(r5, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
// Get function code.
|
||||
__ LoadP(ip, FieldMemOperand(r3, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ LoadP(ip, FieldMemOperand(ip, SharedFunctionInfo::kCodeOffset));
|
||||
__ AddP(ip, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// Re-run JSFunction, r3 is function, cp is context.
|
||||
__ Jump(ip);
|
||||
}
|
||||
|
||||
const bool LiveEdit::kFrameDropperSupported = true;
|
||||
|
||||
#undef __
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_TARGET_ARCH_S390
|
41
src/ic/s390/access-compiler-s390.cc
Normal file
41
src/ic/s390/access-compiler-s390.cc
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/v8.h"
|
||||
|
||||
#if V8_TARGET_ARCH_S390
|
||||
|
||||
#include "src/ic/access-compiler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
void PropertyAccessCompiler::GenerateTailCall(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
Register* PropertyAccessCompiler::load_calling_convention() {
|
||||
// receiver, name, scratch1, scratch2, scratch3, scratch4.
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register name = LoadDescriptor::NameRegister();
|
||||
static Register registers[] = {receiver, name, r5, r2, r6, r7};
|
||||
return registers;
|
||||
}
|
||||
|
||||
Register* PropertyAccessCompiler::store_calling_convention() {
|
||||
// receiver, name, scratch1, scratch2, scratch3.
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register name = StoreDescriptor::NameRegister();
|
||||
static Register registers[] = {receiver, name, r5, r6, r7};
|
||||
return registers;
|
||||
}
|
||||
|
||||
#undef __
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_TARGET_ARCH_S390
|
758
src/ic/s390/handler-compiler-s390.cc
Normal file
758
src/ic/s390/handler-compiler-s390.cc
Normal file
@ -0,0 +1,758 @@
|
||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#if V8_TARGET_ARCH_S390
|
||||
|
||||
#include "src/ic/handler-compiler.h"
|
||||
|
||||
#include "src/field-type.h"
|
||||
#include "src/ic/call-optimization.h"
|
||||
#include "src/ic/ic.h"
|
||||
#include "src/isolate-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
|
||||
MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
|
||||
int accessor_index, int expected_arguments, Register scratch) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r2 : receiver
|
||||
// -- r4 : name
|
||||
// -- lr : return address
|
||||
// -----------------------------------
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
|
||||
if (accessor_index >= 0) {
|
||||
DCHECK(!holder.is(scratch));
|
||||
DCHECK(!receiver.is(scratch));
|
||||
// Call the JavaScript getter with the receiver on the stack.
|
||||
if (map->IsJSGlobalObjectMap()) {
|
||||
// Swap in the global receiver.
|
||||
__ LoadP(scratch,
|
||||
FieldMemOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
|
||||
receiver = scratch;
|
||||
}
|
||||
__ Push(receiver);
|
||||
ParameterCount actual(0);
|
||||
ParameterCount expected(expected_arguments);
|
||||
__ LoadAccessor(r3, holder, accessor_index, ACCESSOR_GETTER);
|
||||
__ InvokeFunction(r3, expected, actual, CALL_FUNCTION,
|
||||
CheckDebugStepCallWrapper());
|
||||
} else {
|
||||
// If we generate a global code snippet for deoptimization only, remember
|
||||
// the place to continue after deoptimization.
|
||||
masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
|
||||
}
|
||||
|
||||
// Restore context register.
|
||||
__ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
}
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
|
||||
MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
|
||||
int accessor_index, int expected_arguments, Register scratch) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- lr : return address
|
||||
// -----------------------------------
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
|
||||
// Save value register, so we can restore it later.
|
||||
__ Push(value());
|
||||
|
||||
if (accessor_index >= 0) {
|
||||
DCHECK(!holder.is(scratch));
|
||||
DCHECK(!receiver.is(scratch));
|
||||
DCHECK(!value().is(scratch));
|
||||
// Call the JavaScript setter with receiver and value on the stack.
|
||||
if (map->IsJSGlobalObjectMap()) {
|
||||
// Swap in the global receiver.
|
||||
__ LoadP(scratch,
|
||||
FieldMemOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
|
||||
receiver = scratch;
|
||||
}
|
||||
__ Push(receiver, value());
|
||||
ParameterCount actual(1);
|
||||
ParameterCount expected(expected_arguments);
|
||||
__ LoadAccessor(r3, holder, accessor_index, ACCESSOR_SETTER);
|
||||
__ InvokeFunction(r3, expected, actual, CALL_FUNCTION,
|
||||
CheckDebugStepCallWrapper());
|
||||
} else {
|
||||
// If we generate a global code snippet for deoptimization only, remember
|
||||
// the place to continue after deoptimization.
|
||||
masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
|
||||
}
|
||||
|
||||
// We have to return the passed value, not the return value of the setter.
|
||||
__ Pop(r2);
|
||||
|
||||
// Restore context register.
|
||||
__ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
}
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
void PropertyHandlerCompiler::PushVectorAndSlot(Register vector,
|
||||
Register slot) {
|
||||
MacroAssembler* masm = this->masm();
|
||||
__ Push(vector, slot);
|
||||
}
|
||||
|
||||
void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) {
|
||||
MacroAssembler* masm = this->masm();
|
||||
__ Pop(vector, slot);
|
||||
}
|
||||
|
||||
void PropertyHandlerCompiler::DiscardVectorAndSlot() {
|
||||
MacroAssembler* masm = this->masm();
|
||||
// Remove vector and slot.
|
||||
__ la(sp, MemOperand(sp, 2 * kPointerSize));
|
||||
}
|
||||
|
||||
void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
|
||||
MacroAssembler* masm, Label* miss_label, Register receiver,
|
||||
Handle<Name> name, Register scratch0, Register scratch1) {
|
||||
DCHECK(name->IsUniqueName());
|
||||
DCHECK(!receiver.is(scratch0));
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1);
|
||||
__ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
|
||||
|
||||
Label done;
|
||||
|
||||
const int kInterceptorOrAccessCheckNeededMask =
|
||||
(1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
|
||||
|
||||
// Bail out if the receiver has a named interceptor or requires access checks.
|
||||
Register map = scratch1;
|
||||
__ LoadP(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
|
||||
__ LoadlB(scratch0, FieldMemOperand(map, Map::kBitFieldOffset));
|
||||
__ AndP(r0, scratch0, Operand(kInterceptorOrAccessCheckNeededMask));
|
||||
__ bne(miss_label);
|
||||
|
||||
// Check that receiver is a JSObject.
|
||||
// TODO(joransiu): Merge into SI compare
|
||||
__ LoadlB(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset));
|
||||
__ CmpP(scratch0, Operand(FIRST_JS_RECEIVER_TYPE));
|
||||
__ blt(miss_label);
|
||||
|
||||
// Load properties array.
|
||||
Register properties = scratch0;
|
||||
__ LoadP(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
// Check that the properties array is a dictionary.
|
||||
__ LoadP(map, FieldMemOperand(properties, HeapObject::kMapOffset));
|
||||
__ CompareRoot(map, Heap::kHashTableMapRootIndex);
|
||||
__ bne(miss_label);
|
||||
|
||||
// Restore the temporarily used register.
|
||||
__ LoadP(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
|
||||
NameDictionaryLookupStub::GenerateNegativeLookup(
|
||||
masm, miss_label, &done, receiver, properties, name, scratch1);
|
||||
__ bind(&done);
|
||||
__ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
|
||||
}
|
||||
|
||||
void NamedLoadHandlerCompiler::GenerateDirectLoadGlobalFunctionPrototype(
|
||||
MacroAssembler* masm, int index, Register result, Label* miss) {
|
||||
__ LoadNativeContextSlot(index, result);
|
||||
// Load its initial map. The global functions all have initial maps.
|
||||
__ LoadP(result,
|
||||
FieldMemOperand(result, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
// Load the prototype from the initial map.
|
||||
__ LoadP(result, FieldMemOperand(result, Map::kPrototypeOffset));
|
||||
}
|
||||
|
||||
void NamedLoadHandlerCompiler::GenerateLoadFunctionPrototype(
|
||||
MacroAssembler* masm, Register receiver, Register scratch1,
|
||||
Register scratch2, Label* miss_label) {
|
||||
__ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label);
|
||||
__ LoadRR(r2, scratch1);
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
// Generate code to check that a global property cell is empty. Create
|
||||
// the property cell at compilation time if no cell exists for the
|
||||
// property.
|
||||
void PropertyHandlerCompiler::GenerateCheckPropertyCell(
|
||||
MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name,
|
||||
Register scratch, Label* miss) {
|
||||
Handle<PropertyCell> cell = JSGlobalObject::EnsurePropertyCell(global, name);
|
||||
DCHECK(cell->value()->IsTheHole());
|
||||
Handle<WeakCell> weak_cell = masm->isolate()->factory()->NewWeakCell(cell);
|
||||
__ LoadWeakValue(scratch, weak_cell, miss);
|
||||
__ LoadP(scratch, FieldMemOperand(scratch, PropertyCell::kValueOffset));
|
||||
__ CompareRoot(scratch, Heap::kTheHoleValueRootIndex);
|
||||
__ bne(miss);
|
||||
}
|
||||
|
||||
static void PushInterceptorArguments(MacroAssembler* masm, Register receiver,
|
||||
Register holder, Register name,
|
||||
Handle<JSObject> holder_obj) {
|
||||
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
|
||||
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
|
||||
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
|
||||
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
|
||||
__ Push(name);
|
||||
__ Push(receiver);
|
||||
__ Push(holder);
|
||||
}
|
||||
|
||||
static void CompileCallLoadPropertyWithInterceptor(
|
||||
MacroAssembler* masm, Register receiver, Register holder, Register name,
|
||||
Handle<JSObject> holder_obj, Runtime::FunctionId id) {
|
||||
DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
|
||||
Runtime::FunctionForId(id)->nargs);
|
||||
PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
|
||||
__ CallRuntime(id);
|
||||
}
|
||||
|
||||
// Generate call to api function.
|
||||
void PropertyHandlerCompiler::GenerateApiAccessorCall(
|
||||
MacroAssembler* masm, const CallOptimization& optimization,
|
||||
Handle<Map> receiver_map, Register receiver, Register scratch_in,
|
||||
bool is_store, Register store_parameter, Register accessor_holder,
|
||||
int accessor_index) {
|
||||
DCHECK(!accessor_holder.is(scratch_in));
|
||||
DCHECK(!receiver.is(scratch_in));
|
||||
__ Push(receiver);
|
||||
// Write the arguments to stack frame.
|
||||
if (is_store) {
|
||||
DCHECK(!receiver.is(store_parameter));
|
||||
DCHECK(!scratch_in.is(store_parameter));
|
||||
__ Push(store_parameter);
|
||||
}
|
||||
DCHECK(optimization.is_simple_api_call());
|
||||
|
||||
// Abi for CallApiFunctionStub.
|
||||
Register callee = r2;
|
||||
Register data = r6;
|
||||
Register holder = r4;
|
||||
Register api_function_address = r3;
|
||||
|
||||
// Put callee in place.
|
||||
__ LoadAccessor(callee, accessor_holder, accessor_index,
|
||||
is_store ? ACCESSOR_SETTER : ACCESSOR_GETTER);
|
||||
|
||||
// Put holder in place.
|
||||
CallOptimization::HolderLookup holder_lookup;
|
||||
int holder_depth = 0;
|
||||
optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup,
|
||||
&holder_depth);
|
||||
switch (holder_lookup) {
|
||||
case CallOptimization::kHolderIsReceiver:
|
||||
__ Move(holder, receiver);
|
||||
break;
|
||||
case CallOptimization::kHolderFound:
|
||||
__ LoadP(holder, FieldMemOperand(receiver, HeapObject::kMapOffset));
|
||||
__ LoadP(holder, FieldMemOperand(holder, Map::kPrototypeOffset));
|
||||
for (int i = 1; i < holder_depth; i++) {
|
||||
__ LoadP(holder, FieldMemOperand(holder, HeapObject::kMapOffset));
|
||||
__ LoadP(holder, FieldMemOperand(holder, Map::kPrototypeOffset));
|
||||
}
|
||||
break;
|
||||
case CallOptimization::kHolderNotFound:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
Isolate* isolate = masm->isolate();
|
||||
Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
|
||||
bool call_data_undefined = false;
|
||||
// Put call data in place.
|
||||
if (api_call_info->data()->IsUndefined()) {
|
||||
call_data_undefined = true;
|
||||
__ LoadRoot(data, Heap::kUndefinedValueRootIndex);
|
||||
} else {
|
||||
if (optimization.is_constant_call()) {
|
||||
__ LoadP(data,
|
||||
FieldMemOperand(callee, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ LoadP(data,
|
||||
FieldMemOperand(data, SharedFunctionInfo::kFunctionDataOffset));
|
||||
__ LoadP(data,
|
||||
FieldMemOperand(data, FunctionTemplateInfo::kCallCodeOffset));
|
||||
} else {
|
||||
__ LoadP(data,
|
||||
FieldMemOperand(callee, FunctionTemplateInfo::kCallCodeOffset));
|
||||
}
|
||||
__ LoadP(data, FieldMemOperand(data, CallHandlerInfo::kDataOffset));
|
||||
}
|
||||
|
||||
if (api_call_info->fast_handler()->IsCode()) {
|
||||
// Just tail call into the fast handler if present.
|
||||
__ Jump(handle(Code::cast(api_call_info->fast_handler())),
|
||||
RelocInfo::CODE_TARGET);
|
||||
return;
|
||||
}
|
||||
|
||||
// Put api_function_address in place.
|
||||
Address function_address = v8::ToCData<Address>(api_call_info->callback());
|
||||
ApiFunction fun(function_address);
|
||||
ExternalReference::Type type = ExternalReference::DIRECT_API_CALL;
|
||||
ExternalReference ref = ExternalReference(&fun, type, masm->isolate());
|
||||
__ mov(api_function_address, Operand(ref));
|
||||
|
||||
// Jump to stub.
|
||||
CallApiAccessorStub stub(isolate, is_store, call_data_undefined,
|
||||
!optimization.is_constant_call());
|
||||
__ TailCallStub(&stub);
|
||||
}
|
||||
|
||||
static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
__ Push(StoreDescriptor::ReceiverRegister(), StoreDescriptor::NameRegister(),
|
||||
StoreDescriptor::ValueRegister(),
|
||||
VectorStoreICDescriptor::SlotRegister(),
|
||||
VectorStoreICDescriptor::VectorRegister());
|
||||
}
|
||||
|
||||
void NamedStoreHandlerCompiler::GenerateSlow(MacroAssembler* masm) {
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// The slow case calls into the runtime to complete the store without causing
|
||||
// an IC miss that would otherwise cause a transition to the generic stub.
|
||||
__ TailCallRuntime(Runtime::kStoreIC_Slow);
|
||||
}
|
||||
|
||||
void ElementHandlerCompiler::GenerateStoreSlow(MacroAssembler* masm) {
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// The slow case calls into the runtime to complete the store without causing
|
||||
// an IC miss that would otherwise cause a transition to the generic stub.
|
||||
__ TailCallRuntime(Runtime::kKeyedStoreIC_Slow);
|
||||
}
|
||||
|
||||
#undef __
|
||||
#define __ ACCESS_MASM(masm())
|
||||
|
||||
void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label,
|
||||
Handle<Name> name) {
|
||||
if (!label->is_unused()) {
|
||||
__ bind(label);
|
||||
__ mov(this->name(), Operand(name));
|
||||
}
|
||||
}
|
||||
|
||||
void NamedStoreHandlerCompiler::GenerateRestoreName(Handle<Name> name) {
|
||||
__ mov(this->name(), Operand(name));
|
||||
}
|
||||
|
||||
void NamedStoreHandlerCompiler::RearrangeVectorAndSlot(
|
||||
Register current_map, Register destination_map) {
|
||||
DCHECK(false); // Not implemented.
|
||||
}
|
||||
|
||||
void NamedStoreHandlerCompiler::GenerateRestoreMap(Handle<Map> transition,
|
||||
Register map_reg,
|
||||
Register scratch,
|
||||
Label* miss) {
|
||||
Handle<WeakCell> cell = Map::WeakCellForMap(transition);
|
||||
DCHECK(!map_reg.is(scratch));
|
||||
__ LoadWeakValue(map_reg, cell, miss);
|
||||
if (transition->CanBeDeprecated()) {
|
||||
__ LoadlW(scratch, FieldMemOperand(map_reg, Map::kBitField3Offset));
|
||||
__ DecodeField<Map::Deprecated>(r0, scratch);
|
||||
__ bne(miss);
|
||||
}
|
||||
}
|
||||
|
||||
void NamedStoreHandlerCompiler::GenerateConstantCheck(Register map_reg,
|
||||
int descriptor,
|
||||
Register value_reg,
|
||||
Register scratch,
|
||||
Label* miss_label) {
|
||||
DCHECK(!map_reg.is(scratch));
|
||||
DCHECK(!map_reg.is(value_reg));
|
||||
DCHECK(!value_reg.is(scratch));
|
||||
__ LoadInstanceDescriptors(map_reg, scratch);
|
||||
__ CmpP(value_reg, FieldMemOperand(
|
||||
scratch, DescriptorArray::GetValueOffset(descriptor)));
|
||||
__ bne(miss_label);
|
||||
}
|
||||
|
||||
void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(FieldType* field_type,
|
||||
Register value_reg,
|
||||
Label* miss_label) {
|
||||
Register map_reg = scratch1();
|
||||
Register scratch = scratch2();
|
||||
DCHECK(!value_reg.is(map_reg));
|
||||
DCHECK(!value_reg.is(scratch));
|
||||
__ JumpIfSmi(value_reg, miss_label);
|
||||
if (field_type->IsClass()) {
|
||||
__ LoadP(map_reg, FieldMemOperand(value_reg, HeapObject::kMapOffset));
|
||||
__ CmpWeakValue(map_reg, Map::WeakCellForMap(field_type->AsClass()),
|
||||
scratch);
|
||||
__ bne(miss_label);
|
||||
}
|
||||
}
|
||||
|
||||
Register PropertyHandlerCompiler::CheckPrototypes(
|
||||
Register object_reg, Register holder_reg, Register scratch1,
|
||||
Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
|
||||
ReturnHolder return_what) {
|
||||
Handle<Map> receiver_map = map();
|
||||
|
||||
// Make sure there's no overlap between holder and object registers.
|
||||
DCHECK(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
|
||||
DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
|
||||
!scratch2.is(scratch1));
|
||||
|
||||
if (FLAG_eliminate_prototype_chain_checks) {
|
||||
Handle<Cell> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
|
||||
if (!validity_cell.is_null()) {
|
||||
DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid),
|
||||
validity_cell->value());
|
||||
__ mov(scratch1, Operand(validity_cell));
|
||||
__ LoadP(scratch1, FieldMemOperand(scratch1, Cell::kValueOffset));
|
||||
__ CmpSmiLiteral(scratch1, Smi::FromInt(Map::kPrototypeChainValid), r0);
|
||||
__ bne(miss);
|
||||
}
|
||||
|
||||
// The prototype chain of primitives (and their JSValue wrappers) depends
|
||||
// on the native context, which can't be guarded by validity cells.
|
||||
// |object_reg| holds the native context specific prototype in this case;
|
||||
// we need to check its map.
|
||||
if (check == CHECK_ALL_MAPS) {
|
||||
__ LoadP(scratch1, FieldMemOperand(object_reg, HeapObject::kMapOffset));
|
||||
Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map);
|
||||
__ CmpWeakValue(scratch1, cell, scratch2);
|
||||
__ b(ne, miss);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep track of the current object in register reg.
|
||||
Register reg = object_reg;
|
||||
int depth = 0;
|
||||
|
||||
Handle<JSObject> current = Handle<JSObject>::null();
|
||||
if (receiver_map->IsJSGlobalObjectMap()) {
|
||||
current = isolate()->global_object();
|
||||
}
|
||||
// Check access rights to the global object. This has to happen after
|
||||
// the map check so that we know that the object is actually a global
|
||||
// object.
|
||||
// This allows us to install generated handlers for accesses to the
|
||||
// global proxy (as opposed to using slow ICs). See corresponding code
|
||||
// in LookupForRead().
|
||||
if (receiver_map->IsJSGlobalProxyMap()) {
|
||||
__ CheckAccessGlobalProxy(reg, scratch2, miss);
|
||||
}
|
||||
|
||||
Handle<JSObject> prototype = Handle<JSObject>::null();
|
||||
Handle<Map> current_map = receiver_map;
|
||||
Handle<Map> holder_map(holder()->map());
|
||||
// Traverse the prototype chain and check the maps in the prototype chain for
|
||||
// fast and global objects or do negative lookup for normal objects.
|
||||
while (!current_map.is_identical_to(holder_map)) {
|
||||
++depth;
|
||||
|
||||
// Only global objects and objects that do not require access
|
||||
// checks are allowed in stubs.
|
||||
DCHECK(current_map->IsJSGlobalProxyMap() ||
|
||||
!current_map->is_access_check_needed());
|
||||
|
||||
prototype = handle(JSObject::cast(current_map->prototype()));
|
||||
if (current_map->is_dictionary_map() &&
|
||||
!current_map->IsJSGlobalObjectMap()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (!name->IsUniqueName()) {
|
||||
DCHECK(name->IsString());
|
||||
name = factory()->InternalizeString(Handle<String>::cast(name));
|
||||
}
|
||||
DCHECK(current.is_null() ||
|
||||
current->property_dictionary()->FindEntry(name) ==
|
||||
NameDictionary::kNotFound);
|
||||
|
||||
if (FLAG_eliminate_prototype_chain_checks && depth > 1) {
|
||||
// TODO(jkummerow): Cache and re-use weak cell.
|
||||
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
|
||||
}
|
||||
GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
|
||||
scratch2);
|
||||
if (!FLAG_eliminate_prototype_chain_checks) {
|
||||
__ LoadP(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
|
||||
__ LoadP(holder_reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
|
||||
}
|
||||
} else {
|
||||
Register map_reg = scratch1;
|
||||
if (!FLAG_eliminate_prototype_chain_checks) {
|
||||
__ LoadP(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset));
|
||||
}
|
||||
if (current_map->IsJSGlobalObjectMap()) {
|
||||
GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
|
||||
name, scratch2, miss);
|
||||
} else if (!FLAG_eliminate_prototype_chain_checks &&
|
||||
(depth != 1 || check == CHECK_ALL_MAPS)) {
|
||||
Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
|
||||
__ CmpWeakValue(map_reg, cell, scratch2);
|
||||
__ bne(miss);
|
||||
}
|
||||
if (!FLAG_eliminate_prototype_chain_checks) {
|
||||
__ LoadP(holder_reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
|
||||
}
|
||||
}
|
||||
|
||||
reg = holder_reg; // From now on the object will be in holder_reg.
|
||||
// Go to the next object in the prototype chain.
|
||||
current = prototype;
|
||||
current_map = handle(current->map());
|
||||
}
|
||||
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap());
|
||||
|
||||
// Log the check depth.
|
||||
LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
|
||||
|
||||
if (!FLAG_eliminate_prototype_chain_checks &&
|
||||
(depth != 0 || check == CHECK_ALL_MAPS)) {
|
||||
// Check the holder map.
|
||||
__ LoadP(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
|
||||
Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
|
||||
__ CmpWeakValue(scratch1, cell, scratch2);
|
||||
__ bne(miss);
|
||||
}
|
||||
|
||||
bool return_holder = return_what == RETURN_HOLDER;
|
||||
if (FLAG_eliminate_prototype_chain_checks && return_holder && depth != 0) {
|
||||
__ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
|
||||
}
|
||||
|
||||
// Return the register containing the holder.
|
||||
return return_holder ? reg : no_reg;
|
||||
}
|
||||
|
||||
void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
|
||||
if (!miss->is_unused()) {
|
||||
Label success;
|
||||
__ b(&success);
|
||||
__ bind(miss);
|
||||
if (IC::ICUseVector(kind())) {
|
||||
DCHECK(kind() == Code::LOAD_IC);
|
||||
PopVectorAndSlot();
|
||||
}
|
||||
TailCallBuiltin(masm(), MissBuiltin(kind()));
|
||||
__ bind(&success);
|
||||
}
|
||||
}
|
||||
|
||||
void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
|
||||
if (!miss->is_unused()) {
|
||||
Label success;
|
||||
__ b(&success);
|
||||
GenerateRestoreName(miss, name);
|
||||
if (IC::ICUseVector(kind())) PopVectorAndSlot();
|
||||
TailCallBuiltin(masm(), MissBuiltin(kind()));
|
||||
__ bind(&success);
|
||||
}
|
||||
}
|
||||
|
||||
void NamedLoadHandlerCompiler::GenerateLoadConstant(Handle<Object> value) {
|
||||
// Return the constant value.
|
||||
__ Move(r2, value);
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
void NamedLoadHandlerCompiler::GenerateLoadCallback(
|
||||
Register reg, Handle<AccessorInfo> callback) {
|
||||
DCHECK(!AreAliased(scratch2(), scratch3(), scratch4(), receiver()));
|
||||
DCHECK(!AreAliased(scratch2(), scratch3(), scratch4(), reg));
|
||||
|
||||
// Build v8::PropertyCallbackInfo::args_ array on the stack and push property
|
||||
// name below the exit frame to make GC aware of them.
|
||||
STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0);
|
||||
STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1);
|
||||
STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2);
|
||||
STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3);
|
||||
STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4);
|
||||
STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5);
|
||||
STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6);
|
||||
STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7);
|
||||
|
||||
__ Push(receiver());
|
||||
// Push data from AccessorInfo.
|
||||
Handle<Object> data(callback->data(), isolate());
|
||||
if (data->IsUndefined() || data->IsSmi()) {
|
||||
__ Move(scratch2(), data);
|
||||
} else {
|
||||
Handle<WeakCell> cell =
|
||||
isolate()->factory()->NewWeakCell(Handle<HeapObject>::cast(data));
|
||||
// The callback is alive if this instruction is executed,
|
||||
// so the weak cell is not cleared and points to data.
|
||||
__ GetWeakValue(scratch2(), cell);
|
||||
}
|
||||
__ push(scratch2());
|
||||
__ LoadRoot(scratch2(), Heap::kUndefinedValueRootIndex);
|
||||
__ Push(scratch2(), scratch2());
|
||||
__ mov(scratch2(), Operand(ExternalReference::isolate_address(isolate())));
|
||||
// should_throw_on_error -> false
|
||||
__ mov(scratch3(), Operand(Smi::FromInt(0)));
|
||||
__ Push(scratch2(), reg, scratch3(), name());
|
||||
|
||||
// Abi for CallApiGetter
|
||||
Register getter_address_reg = ApiGetterDescriptor::function_address();
|
||||
|
||||
Address getter_address = v8::ToCData<Address>(callback->getter());
|
||||
ApiFunction fun(getter_address);
|
||||
ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL;
|
||||
ExternalReference ref = ExternalReference(&fun, type, isolate());
|
||||
__ mov(getter_address_reg, Operand(ref));
|
||||
|
||||
CallApiGetterStub stub(isolate());
|
||||
__ TailCallStub(&stub);
|
||||
}
|
||||
|
||||
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
|
||||
LookupIterator* it, Register holder_reg) {
|
||||
DCHECK(holder()->HasNamedInterceptor());
|
||||
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
|
||||
|
||||
// Compile the interceptor call, followed by inline code to load the
|
||||
// property from further up the prototype chain if the call fails.
|
||||
// Check that the maps haven't changed.
|
||||
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
|
||||
|
||||
// Preserve the receiver register explicitly whenever it is different from the
|
||||
// holder and it is needed should the interceptor return without any result.
|
||||
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
|
||||
// case might cause a miss during the prototype check.
|
||||
bool must_perform_prototype_check =
|
||||
!holder().is_identical_to(it->GetHolder<JSObject>());
|
||||
bool must_preserve_receiver_reg =
|
||||
!receiver().is(holder_reg) &&
|
||||
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
|
||||
|
||||
// Save necessary data before invoking an interceptor.
|
||||
// Requires a frame to make GC aware of pushed pointers.
|
||||
{
|
||||
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
|
||||
if (must_preserve_receiver_reg) {
|
||||
__ Push(receiver(), holder_reg, this->name());
|
||||
} else {
|
||||
__ Push(holder_reg, this->name());
|
||||
}
|
||||
InterceptorVectorSlotPush(holder_reg);
|
||||
// Invoke an interceptor. Note: map checks from receiver to
|
||||
// interceptor's holder has been compiled before (see a caller
|
||||
// of this method.)
|
||||
CompileCallLoadPropertyWithInterceptor(
|
||||
masm(), receiver(), holder_reg, this->name(), holder(),
|
||||
Runtime::kLoadPropertyWithInterceptorOnly);
|
||||
|
||||
// Check if interceptor provided a value for property. If it's
|
||||
// the case, return immediately.
|
||||
Label interceptor_failed;
|
||||
__ CompareRoot(r2, Heap::kNoInterceptorResultSentinelRootIndex);
|
||||
__ beq(&interceptor_failed, Label::kNear);
|
||||
frame_scope.GenerateLeaveFrame();
|
||||
__ Ret();
|
||||
|
||||
__ bind(&interceptor_failed);
|
||||
InterceptorVectorSlotPop(holder_reg);
|
||||
__ Pop(this->name());
|
||||
__ Pop(holder_reg);
|
||||
if (must_preserve_receiver_reg) {
|
||||
__ Pop(receiver());
|
||||
}
|
||||
// Leave the internal frame.
|
||||
}
|
||||
|
||||
GenerateLoadPostInterceptor(it, holder_reg);
|
||||
}
|
||||
|
||||
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
|
||||
// Call the runtime system to load the interceptor.
|
||||
DCHECK(holder()->HasNamedInterceptor());
|
||||
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
|
||||
PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
|
||||
holder());
|
||||
|
||||
__ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
|
||||
}
|
||||
|
||||
Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
|
||||
Handle<JSObject> object, Handle<Name> name, Handle<AccessorInfo> callback,
|
||||
LanguageMode language_mode) {
|
||||
Register holder_reg = Frontend(name);
|
||||
|
||||
__ Push(receiver(), holder_reg); // receiver
|
||||
|
||||
// If the callback cannot leak, then push the callback directly,
|
||||
// otherwise wrap it in a weak cell.
|
||||
if (callback->data()->IsUndefined() || callback->data()->IsSmi()) {
|
||||
__ mov(ip, Operand(callback));
|
||||
} else {
|
||||
Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
|
||||
__ mov(ip, Operand(cell));
|
||||
}
|
||||
__ Push(ip);
|
||||
__ mov(ip, Operand(name));
|
||||
__ Push(ip, value());
|
||||
__ Push(Smi::FromInt(language_mode));
|
||||
|
||||
// Do tail-call to the runtime system.
|
||||
__ TailCallRuntime(Runtime::kStoreCallbackProperty);
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(kind(), Code::FAST, name);
|
||||
}
|
||||
|
||||
Handle<Code> NamedStoreHandlerCompiler::CompileStoreInterceptor(
|
||||
Handle<Name> name) {
|
||||
__ Push(receiver(), this->name(), value());
|
||||
|
||||
// Do tail-call to the runtime system.
|
||||
__ TailCallRuntime(Runtime::kStorePropertyWithInterceptor);
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(kind(), Code::FAST, name);
|
||||
}
|
||||
|
||||
Register NamedStoreHandlerCompiler::value() {
|
||||
return StoreDescriptor::ValueRegister();
|
||||
}
|
||||
|
||||
Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
|
||||
Handle<PropertyCell> cell, Handle<Name> name, bool is_configurable) {
|
||||
Label miss;
|
||||
if (IC::ICUseVector(kind())) {
|
||||
PushVectorAndSlot();
|
||||
}
|
||||
FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
|
||||
|
||||
// Get the value from the cell.
|
||||
Register result = StoreDescriptor::ValueRegister();
|
||||
Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell);
|
||||
__ LoadWeakValue(result, weak_cell, &miss);
|
||||
__ LoadP(result, FieldMemOperand(result, PropertyCell::kValueOffset));
|
||||
|
||||
// Check for deleted property if property can actually be deleted.
|
||||
if (is_configurable) {
|
||||
__ CompareRoot(result, Heap::kTheHoleValueRootIndex);
|
||||
__ beq(&miss);
|
||||
}
|
||||
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->ic_named_load_global_stub(), 1, r3, r5);
|
||||
if (IC::ICUseVector(kind())) {
|
||||
DiscardVectorAndSlot();
|
||||
}
|
||||
__ Ret();
|
||||
|
||||
FrontendFooter(name, &miss);
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(kind(), Code::NORMAL, name);
|
||||
}
|
||||
|
||||
#undef __
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_TARGET_ARCH_ARM
|
29
src/ic/s390/ic-compiler-s390.cc
Normal file
29
src/ic/s390/ic-compiler-s390.cc
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#if V8_TARGET_ARCH_S390
|
||||
|
||||
#include "src/ic/ic.h"
|
||||
#include "src/ic/ic-compiler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
void PropertyICCompiler::GenerateRuntimeSetProperty(
|
||||
MacroAssembler* masm, LanguageMode language_mode) {
|
||||
__ mov(r0, Operand(Smi::FromInt(language_mode)));
|
||||
__ Push(StoreDescriptor::ReceiverRegister(), StoreDescriptor::NameRegister(),
|
||||
StoreDescriptor::ValueRegister(), r0);
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kSetProperty);
|
||||
}
|
||||
|
||||
#undef __
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_TARGET_ARCH_S390
|
897
src/ic/s390/ic-s390.cc
Normal file
897
src/ic/s390/ic-s390.cc
Normal file
@ -0,0 +1,897 @@
|
||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#if V8_TARGET_ARCH_S390
|
||||
|
||||
#include "src/ic/ic.h"
|
||||
#include "src/codegen.h"
|
||||
#include "src/ic/ic-compiler.h"
|
||||
#include "src/ic/stub-cache.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Static IC stub generators.
|
||||
//
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, Register type,
|
||||
Label* global_object) {
|
||||
// Register usage:
|
||||
// type: holds the receiver instance type on entry.
|
||||
__ CmpP(type, Operand(JS_GLOBAL_OBJECT_TYPE));
|
||||
__ beq(global_object);
|
||||
__ CmpP(type, Operand(JS_GLOBAL_PROXY_TYPE));
|
||||
__ beq(global_object);
|
||||
}
|
||||
|
||||
// Helper function used from LoadIC GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// result: Register for the result. It is only updated if a jump to the miss
|
||||
// label is not done. Can be the same as elements or name clobbering
|
||||
// one of these in the case of not jumping to the miss label.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register result, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry check that the value is a normal
|
||||
// property.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ LoadP(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ LoadRR(r0, scratch2);
|
||||
__ LoadSmiLiteral(scratch2, Smi::FromInt(PropertyDetails::TypeField::kMask));
|
||||
__ AndP(scratch2, scratch1);
|
||||
__ bne(miss);
|
||||
__ LoadRR(scratch2, r0);
|
||||
|
||||
// Get the value at the masked, scaled index and return.
|
||||
__ LoadP(result,
|
||||
FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
|
||||
}
|
||||
|
||||
// Helper function used from StoreIC::GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// value: The value to store.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register value, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry in the dictionary check that the value
|
||||
// is a normal property that is not read only.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
int kTypeAndReadOnlyMask =
|
||||
PropertyDetails::TypeField::kMask |
|
||||
PropertyDetails::AttributesField::encode(READ_ONLY);
|
||||
__ LoadP(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ LoadRR(r0, scratch2);
|
||||
__ LoadSmiLiteral(scratch2, Smi::FromInt(kTypeAndReadOnlyMask));
|
||||
__ AndP(scratch2, scratch1);
|
||||
__ bne(miss /*, cr0*/);
|
||||
__ LoadRR(scratch2, r0);
|
||||
|
||||
// Store the value at the masked, scaled index and return.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ AddP(scratch2, Operand(kValueOffset - kHeapObjectTag));
|
||||
__ StoreP(value, MemOperand(scratch2));
|
||||
|
||||
// Update the write barrier. Make sure not to clobber the value.
|
||||
__ LoadRR(scratch1, value);
|
||||
__ RecordWrite(elements, scratch2, scratch1, kLRHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
// Checks the receiver for special cases (value type, slow case bits).
|
||||
// Falls through for regular JS object.
|
||||
static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
|
||||
Register receiver, Register map,
|
||||
Register scratch,
|
||||
int interceptor_bit, Label* slow) {
|
||||
// Check that the object isn't a smi.
|
||||
__ JumpIfSmi(receiver, slow);
|
||||
// Get the map of the receiver.
|
||||
__ LoadP(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
|
||||
// Check bit field.
|
||||
__ LoadlB(scratch, FieldMemOperand(map, Map::kBitFieldOffset));
|
||||
DCHECK(((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit)) < 0x8000);
|
||||
__ mov(r0,
|
||||
Operand((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit)));
|
||||
__ AndP(r0, scratch);
|
||||
__ bne(slow /*, cr0*/);
|
||||
// Check that the object is some kind of JS object EXCEPT JS Value type.
|
||||
// In the case that the object is a value-wrapper object,
|
||||
// we enter the runtime system to make sure that indexing into string
|
||||
// objects work as intended.
|
||||
DCHECK(JS_OBJECT_TYPE > JS_VALUE_TYPE);
|
||||
__ LoadlB(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
|
||||
__ CmpP(scratch, Operand(JS_OBJECT_TYPE));
|
||||
__ blt(slow);
|
||||
}
|
||||
|
||||
// Loads an indexed element from a fast case array.
|
||||
static void GenerateFastArrayLoad(MacroAssembler* masm, Register receiver,
|
||||
Register key, Register elements,
|
||||
Register scratch1, Register scratch2,
|
||||
Register result, Label* slow) {
|
||||
// Register use:
|
||||
//
|
||||
// receiver - holds the receiver on entry.
|
||||
// Unchanged unless 'result' is the same register.
|
||||
//
|
||||
// key - holds the smi key on entry.
|
||||
// Unchanged unless 'result' is the same register.
|
||||
//
|
||||
// result - holds the result on exit if the load succeeded.
|
||||
// Allowed to be the the same as 'receiver' or 'key'.
|
||||
// Unchanged on bailout so 'receiver' and 'key' can be safely
|
||||
// used by further computation.
|
||||
//
|
||||
// Scratch registers:
|
||||
//
|
||||
// elements - holds the elements of the receiver and its protoypes.
|
||||
//
|
||||
// scratch1 - used to hold elements length, bit fields, base addresses.
|
||||
//
|
||||
// scratch2 - used to hold maps, prototypes, and the loaded value.
|
||||
Label check_prototypes, check_next_prototype;
|
||||
Label done, in_bounds, absent;
|
||||
|
||||
__ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
||||
__ AssertFastElements(elements);
|
||||
|
||||
// Check that the key (index) is within bounds.
|
||||
__ LoadP(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
|
||||
__ CmpLogicalP(key, scratch1);
|
||||
__ blt(&in_bounds, Label::kNear);
|
||||
// Out-of-bounds. Check the prototype chain to see if we can just return
|
||||
// 'undefined'.
|
||||
__ CmpP(key, Operand::Zero());
|
||||
__ blt(slow); // Negative keys can't take the fast OOB path.
|
||||
__ bind(&check_prototypes);
|
||||
__ LoadP(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset));
|
||||
__ bind(&check_next_prototype);
|
||||
__ LoadP(scratch2, FieldMemOperand(scratch2, Map::kPrototypeOffset));
|
||||
// scratch2: current prototype
|
||||
__ CompareRoot(scratch2, Heap::kNullValueRootIndex);
|
||||
__ beq(&absent, Label::kNear);
|
||||
__ LoadP(elements, FieldMemOperand(scratch2, JSObject::kElementsOffset));
|
||||
__ LoadP(scratch2, FieldMemOperand(scratch2, HeapObject::kMapOffset));
|
||||
// elements: elements of current prototype
|
||||
// scratch2: map of current prototype
|
||||
__ CompareInstanceType(scratch2, scratch1, JS_OBJECT_TYPE);
|
||||
__ blt(slow);
|
||||
__ LoadlB(scratch1, FieldMemOperand(scratch2, Map::kBitFieldOffset));
|
||||
__ AndP(r0, scratch1, Operand((1 << Map::kIsAccessCheckNeeded) |
|
||||
(1 << Map::kHasIndexedInterceptor)));
|
||||
__ bne(slow);
|
||||
__ CompareRoot(elements, Heap::kEmptyFixedArrayRootIndex);
|
||||
__ bne(slow);
|
||||
__ jmp(&check_next_prototype);
|
||||
|
||||
__ bind(&absent);
|
||||
__ LoadRoot(result, Heap::kUndefinedValueRootIndex);
|
||||
__ jmp(&done);
|
||||
|
||||
__ bind(&in_bounds);
|
||||
// Fast case: Do the load.
|
||||
__ AddP(scratch1, elements,
|
||||
Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
// The key is a smi.
|
||||
__ SmiToPtrArrayOffset(scratch2, key);
|
||||
__ LoadP(scratch2, MemOperand(scratch2, scratch1));
|
||||
__ CompareRoot(scratch2, Heap::kTheHoleValueRootIndex);
|
||||
// In case the loaded value is the_hole we have to check the prototype chain.
|
||||
__ beq(&check_prototypes);
|
||||
__ LoadRR(result, scratch2);
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
// Checks whether a key is an array index string or a unique name.
|
||||
// Falls through if a key is a unique name.
|
||||
static void GenerateKeyNameCheck(MacroAssembler* masm, Register key,
|
||||
Register map, Register hash,
|
||||
Label* index_string, Label* not_unique) {
|
||||
// The key is not a smi.
|
||||
Label unique;
|
||||
// Is it a name?
|
||||
__ CompareObjectType(key, map, hash, LAST_UNIQUE_NAME_TYPE);
|
||||
__ bgt(not_unique);
|
||||
STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE);
|
||||
__ beq(&unique, Label::kNear);
|
||||
|
||||
// Is the string an array index, with cached numeric value?
|
||||
__ LoadlW(hash, FieldMemOperand(key, Name::kHashFieldOffset));
|
||||
__ mov(r7, Operand(Name::kContainsCachedArrayIndexMask));
|
||||
__ AndP(r0, hash, r7);
|
||||
__ beq(index_string);
|
||||
|
||||
// Is the string internalized? We know it's a string, so a single
|
||||
// bit test is enough.
|
||||
// map: key map
|
||||
__ LoadlB(hash, FieldMemOperand(map, Map::kInstanceTypeOffset));
|
||||
STATIC_ASSERT(kInternalizedTag == 0);
|
||||
__ tmll(hash, Operand(kIsNotInternalizedMask));
|
||||
__ bne(not_unique);
|
||||
|
||||
__ bind(&unique);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Register dictionary = r2;
|
||||
DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
|
||||
DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
|
||||
|
||||
Label slow;
|
||||
|
||||
__ LoadP(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(),
|
||||
JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryLoad(masm, &slow, dictionary,
|
||||
LoadDescriptor::NameRegister(), r2, r5, r6);
|
||||
__ Ret();
|
||||
|
||||
// Dictionary load failed, go slow (but don't miss).
|
||||
__ bind(&slow);
|
||||
GenerateRuntimeGetProperty(masm);
|
||||
}
|
||||
|
||||
// A register that isn't one of the parameters to the load ic.
|
||||
static const Register LoadIC_TempRegister() { return r5; }
|
||||
|
||||
static void LoadIC_PushArgs(MacroAssembler* masm) {
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register name = LoadDescriptor::NameRegister();
|
||||
Register slot = LoadDescriptor::SlotRegister();
|
||||
Register vector = LoadWithVectorDescriptor::VectorRegister();
|
||||
|
||||
__ Push(receiver, name, slot, vector);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// The return address is in lr.
|
||||
Isolate* isolate = masm->isolate();
|
||||
|
||||
DCHECK(!AreAliased(r6, r7, LoadWithVectorDescriptor::SlotRegister(),
|
||||
LoadWithVectorDescriptor::VectorRegister()));
|
||||
__ IncrementCounter(isolate->counters()->ic_load_miss(), 1, r6, r7);
|
||||
|
||||
LoadIC_PushArgs(masm);
|
||||
|
||||
// Perform tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kLoadIC_Miss);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
// The return address is in lr.
|
||||
|
||||
__ LoadRR(LoadIC_TempRegister(), LoadDescriptor::ReceiverRegister());
|
||||
__ Push(LoadIC_TempRegister(), LoadDescriptor::NameRegister());
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kGetProperty);
|
||||
}
|
||||
|
||||
void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// The return address is in lr.
|
||||
Isolate* isolate = masm->isolate();
|
||||
|
||||
DCHECK(!AreAliased(r6, r7, LoadWithVectorDescriptor::SlotRegister(),
|
||||
LoadWithVectorDescriptor::VectorRegister()));
|
||||
__ IncrementCounter(isolate->counters()->ic_keyed_load_miss(), 1, r6, r7);
|
||||
|
||||
LoadIC_PushArgs(masm);
|
||||
|
||||
// Perform tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kKeyedLoadIC_Miss);
|
||||
}
|
||||
|
||||
void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
// The return address is in lr.
|
||||
|
||||
__ Push(LoadDescriptor::ReceiverRegister(), LoadDescriptor::NameRegister());
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kKeyedGetProperty);
|
||||
}
|
||||
|
||||
void KeyedLoadIC::GenerateMegamorphic(MacroAssembler* masm) {
|
||||
// The return address is in lr.
|
||||
Label slow, check_name, index_smi, index_name, property_array_property;
|
||||
Label probe_dictionary, check_number_dictionary;
|
||||
|
||||
Register key = LoadDescriptor::NameRegister();
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
DCHECK(key.is(r4));
|
||||
DCHECK(receiver.is(r3));
|
||||
|
||||
Isolate* isolate = masm->isolate();
|
||||
|
||||
// Check that the key is a smi.
|
||||
__ JumpIfNotSmi(key, &check_name);
|
||||
__ bind(&index_smi);
|
||||
// Now the key is known to be a smi. This place is also jumped to from below
|
||||
// where a numeric string is converted to a smi.
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(masm, receiver, r2, r5,
|
||||
Map::kHasIndexedInterceptor, &slow);
|
||||
|
||||
// Check the receiver's map to see if it has fast elements.
|
||||
__ CheckFastElements(r2, r5, &check_number_dictionary);
|
||||
|
||||
GenerateFastArrayLoad(masm, receiver, key, r2, r5, r6, r2, &slow);
|
||||
__ IncrementCounter(isolate->counters()->ic_keyed_load_generic_smi(), 1, r6,
|
||||
r5);
|
||||
__ Ret();
|
||||
|
||||
__ bind(&check_number_dictionary);
|
||||
__ LoadP(r6, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
||||
__ LoadP(r5, FieldMemOperand(r6, JSObject::kMapOffset));
|
||||
|
||||
// Check whether the elements is a number dictionary.
|
||||
// r5: elements map
|
||||
// r6: elements
|
||||
__ CompareRoot(r5, Heap::kHashTableMapRootIndex);
|
||||
__ bne(&slow, Label::kNear);
|
||||
__ SmiUntag(r2, key);
|
||||
__ LoadFromNumberDictionary(&slow, r6, key, r2, r2, r5, r7);
|
||||
__ Ret();
|
||||
|
||||
// Slow case, key and receiver still in r2 and r3.
|
||||
__ bind(&slow);
|
||||
__ IncrementCounter(isolate->counters()->ic_keyed_load_generic_slow(), 1, r6,
|
||||
r5);
|
||||
GenerateRuntimeGetProperty(masm);
|
||||
|
||||
__ bind(&check_name);
|
||||
GenerateKeyNameCheck(masm, key, r2, r5, &index_name, &slow);
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(masm, receiver, r2, r5,
|
||||
Map::kHasNamedInterceptor, &slow);
|
||||
|
||||
// If the receiver is a fast-case object, check the stub cache. Otherwise
|
||||
// probe the dictionary.
|
||||
__ LoadP(r5, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
__ LoadP(r6, FieldMemOperand(r5, HeapObject::kMapOffset));
|
||||
__ CompareRoot(r6, Heap::kHashTableMapRootIndex);
|
||||
__ beq(&probe_dictionary);
|
||||
|
||||
// The handlers in the stub cache expect a vector and slot. Since we won't
|
||||
// change the IC from any downstream misses, a dummy vector can be used.
|
||||
Register vector = LoadWithVectorDescriptor::VectorRegister();
|
||||
Register slot = LoadWithVectorDescriptor::SlotRegister();
|
||||
DCHECK(!AreAliased(vector, slot, r6, r7, r8, r9));
|
||||
Handle<TypeFeedbackVector> dummy_vector =
|
||||
TypeFeedbackVector::DummyVector(masm->isolate());
|
||||
int slot_index = dummy_vector->GetIndex(
|
||||
FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedLoadICSlot));
|
||||
__ LoadRoot(vector, Heap::kDummyVectorRootIndex);
|
||||
__ LoadSmiLiteral(slot, Smi::FromInt(slot_index));
|
||||
|
||||
Code::Flags flags = Code::RemoveTypeAndHolderFromFlags(
|
||||
Code::ComputeHandlerFlags(Code::LOAD_IC));
|
||||
masm->isolate()->stub_cache()->GenerateProbe(masm, Code::KEYED_LOAD_IC, flags,
|
||||
receiver, key, r6, r7, r8, r9);
|
||||
// Cache miss.
|
||||
GenerateMiss(masm);
|
||||
|
||||
// Do a quick inline probe of the receiver's dictionary, if it
|
||||
// exists.
|
||||
__ bind(&probe_dictionary);
|
||||
// r5: elements
|
||||
__ LoadP(r2, FieldMemOperand(receiver, HeapObject::kMapOffset));
|
||||
__ LoadlB(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
|
||||
GenerateGlobalInstanceTypeCheck(masm, r2, &slow);
|
||||
// Load the property to r2.
|
||||
GenerateDictionaryLoad(masm, &slow, r5, key, r2, r7, r6);
|
||||
__ IncrementCounter(isolate->counters()->ic_keyed_load_generic_symbol(), 1,
|
||||
r6, r5);
|
||||
__ Ret();
|
||||
|
||||
__ bind(&index_name);
|
||||
__ IndexFromHash(r5, key);
|
||||
// Now jump to the place where smi keys are handled.
|
||||
__ b(&index_smi);
|
||||
}
|
||||
|
||||
static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
__ Push(StoreDescriptor::ReceiverRegister(), StoreDescriptor::NameRegister(),
|
||||
StoreDescriptor::ValueRegister(),
|
||||
VectorStoreICDescriptor::SlotRegister(),
|
||||
VectorStoreICDescriptor::VectorRegister());
|
||||
}
|
||||
|
||||
void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
__ TailCallRuntime(Runtime::kKeyedStoreIC_Miss);
|
||||
}
|
||||
|
||||
static void KeyedStoreGenerateMegamorphicHelper(
|
||||
MacroAssembler* masm, Label* fast_object, Label* fast_double, Label* slow,
|
||||
KeyedStoreCheckMap check_map, KeyedStoreIncrementLength increment_length,
|
||||
Register value, Register key, Register receiver, Register receiver_map,
|
||||
Register elements_map, Register elements) {
|
||||
Label transition_smi_elements;
|
||||
Label finish_object_store, non_double_value, transition_double_elements;
|
||||
Label fast_double_without_map_check;
|
||||
|
||||
// Fast case: Do the store, could be either Object or double.
|
||||
__ bind(fast_object);
|
||||
Register scratch = r6;
|
||||
Register address = r7;
|
||||
DCHECK(!AreAliased(value, key, receiver, receiver_map, elements_map, elements,
|
||||
scratch, address));
|
||||
|
||||
if (check_map == kCheckMap) {
|
||||
__ LoadP(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
|
||||
__ CmpP(elements_map,
|
||||
Operand(masm->isolate()->factory()->fixed_array_map()));
|
||||
__ bne(fast_double);
|
||||
}
|
||||
|
||||
// HOLECHECK: guards "A[i] = V"
|
||||
// We have to go to the runtime if the current value is the hole because
|
||||
// there may be a callback on the element
|
||||
Label holecheck_passed1;
|
||||
// @TODO(joransiu) : Fold AddP into memref of LoadP
|
||||
__ AddP(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
__ SmiToPtrArrayOffset(scratch, key);
|
||||
__ LoadP(scratch, MemOperand(address, scratch));
|
||||
__ CmpP(scratch, Operand(masm->isolate()->factory()->the_hole_value()));
|
||||
__ bne(&holecheck_passed1, Label::kNear);
|
||||
__ JumpIfDictionaryInPrototypeChain(receiver, elements_map, scratch, slow);
|
||||
|
||||
__ bind(&holecheck_passed1);
|
||||
|
||||
// Smi stores don't require further checks.
|
||||
Label non_smi_value;
|
||||
__ JumpIfNotSmi(value, &non_smi_value);
|
||||
|
||||
if (increment_length == kIncrementLength) {
|
||||
// Add 1 to receiver->length.
|
||||
__ AddSmiLiteral(scratch, key, Smi::FromInt(1), r0);
|
||||
__ StoreP(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
||||
}
|
||||
// It's irrelevant whether array is smi-only or not when writing a smi.
|
||||
__ AddP(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
__ SmiToPtrArrayOffset(scratch, key);
|
||||
__ StoreP(value, MemOperand(address, scratch));
|
||||
__ Ret();
|
||||
|
||||
__ bind(&non_smi_value);
|
||||
// Escape to elements kind transition case.
|
||||
__ CheckFastObjectElements(receiver_map, scratch, &transition_smi_elements);
|
||||
|
||||
// Fast elements array, store the value to the elements backing store.
|
||||
__ bind(&finish_object_store);
|
||||
if (increment_length == kIncrementLength) {
|
||||
// Add 1 to receiver->length.
|
||||
__ AddSmiLiteral(scratch, key, Smi::FromInt(1), r0);
|
||||
__ StoreP(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
||||
}
|
||||
__ AddP(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
__ SmiToPtrArrayOffset(scratch, key);
|
||||
__ StoreP(value, MemOperand(address, scratch));
|
||||
__ la(address, MemOperand(address, scratch));
|
||||
// Update write barrier for the elements array address.
|
||||
__ LoadRR(scratch, value); // Preserve the value which is returned.
|
||||
__ RecordWrite(elements, address, scratch, kLRHasNotBeenSaved,
|
||||
kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
|
||||
__ Ret();
|
||||
|
||||
__ bind(fast_double);
|
||||
if (check_map == kCheckMap) {
|
||||
// Check for fast double array case. If this fails, call through to the
|
||||
// runtime.
|
||||
__ CompareRoot(elements_map, Heap::kFixedDoubleArrayMapRootIndex);
|
||||
__ bne(slow);
|
||||
}
|
||||
|
||||
// HOLECHECK: guards "A[i] double hole?"
|
||||
// We have to see if the double version of the hole is present. If so
|
||||
// go to the runtime.
|
||||
// @TODO(joransiu) : Fold AddP Operand into LoadlW
|
||||
__ AddP(address, elements,
|
||||
Operand((FixedDoubleArray::kHeaderSize + Register::kExponentOffset -
|
||||
kHeapObjectTag)));
|
||||
__ SmiToDoubleArrayOffset(scratch, key);
|
||||
__ LoadlW(scratch, MemOperand(address, scratch));
|
||||
__ CmpP(scratch, Operand(kHoleNanUpper32));
|
||||
__ bne(&fast_double_without_map_check, Label::kNear);
|
||||
__ JumpIfDictionaryInPrototypeChain(receiver, elements_map, scratch, slow);
|
||||
|
||||
__ bind(&fast_double_without_map_check);
|
||||
__ StoreNumberToDoubleElements(value, key, elements, scratch, d0,
|
||||
&transition_double_elements);
|
||||
if (increment_length == kIncrementLength) {
|
||||
// Add 1 to receiver->length.
|
||||
__ AddSmiLiteral(scratch, key, Smi::FromInt(1), r0);
|
||||
__ StoreP(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
||||
}
|
||||
__ Ret();
|
||||
|
||||
__ bind(&transition_smi_elements);
|
||||
// Transition the array appropriately depending on the value type.
|
||||
__ LoadP(scratch, FieldMemOperand(value, HeapObject::kMapOffset));
|
||||
__ CompareRoot(scratch, Heap::kHeapNumberMapRootIndex);
|
||||
__ bne(&non_double_value);
|
||||
|
||||
// Value is a double. Transition FAST_SMI_ELEMENTS ->
|
||||
// FAST_DOUBLE_ELEMENTS and complete the store.
|
||||
__ LoadTransitionedArrayMapConditional(
|
||||
FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS, receiver_map, scratch, slow);
|
||||
AllocationSiteMode mode =
|
||||
AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS);
|
||||
ElementsTransitionGenerator::GenerateSmiToDouble(masm, receiver, key, value,
|
||||
receiver_map, mode, slow);
|
||||
__ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
||||
__ b(&fast_double_without_map_check);
|
||||
|
||||
__ bind(&non_double_value);
|
||||
// Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS
|
||||
__ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS,
|
||||
receiver_map, scratch, slow);
|
||||
mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
|
||||
ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
|
||||
masm, receiver, key, value, receiver_map, mode, slow);
|
||||
__ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
||||
__ b(&finish_object_store);
|
||||
|
||||
__ bind(&transition_double_elements);
|
||||
// Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a
|
||||
// HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and
|
||||
// transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS
|
||||
__ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS,
|
||||
receiver_map, scratch, slow);
|
||||
mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
|
||||
ElementsTransitionGenerator::GenerateDoubleToObject(
|
||||
masm, receiver, key, value, receiver_map, mode, slow);
|
||||
__ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
||||
__ b(&finish_object_store);
|
||||
}
|
||||
|
||||
void KeyedStoreIC::GenerateMegamorphic(MacroAssembler* masm,
|
||||
LanguageMode language_mode) {
|
||||
// ---------- S t a t e --------------
|
||||
// -- r2 : value
|
||||
// -- r3 : key
|
||||
// -- r4 : receiver
|
||||
// -- lr : return address
|
||||
// -----------------------------------
|
||||
Label slow, fast_object, fast_object_grow;
|
||||
Label fast_double, fast_double_grow;
|
||||
Label array, extra, check_if_double_array, maybe_name_key, miss;
|
||||
|
||||
// Register usage.
|
||||
Register value = StoreDescriptor::ValueRegister();
|
||||
Register key = StoreDescriptor::NameRegister();
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
DCHECK(receiver.is(r3));
|
||||
DCHECK(key.is(r4));
|
||||
DCHECK(value.is(r2));
|
||||
Register receiver_map = r5;
|
||||
Register elements_map = r8;
|
||||
Register elements = r9; // Elements array of the receiver.
|
||||
// r6 and r7 are used as general scratch registers.
|
||||
|
||||
// Check that the key is a smi.
|
||||
__ JumpIfNotSmi(key, &maybe_name_key);
|
||||
// Check that the object isn't a smi.
|
||||
__ JumpIfSmi(receiver, &slow);
|
||||
// Get the map of the object.
|
||||
__ LoadP(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
|
||||
// Check that the receiver does not require access checks and is not observed.
|
||||
// The generic stub does not perform map checks or handle observed objects.
|
||||
__ LoadlB(ip, FieldMemOperand(receiver_map, Map::kBitFieldOffset));
|
||||
__ AndP(r0, ip,
|
||||
Operand(1 << Map::kIsAccessCheckNeeded | 1 << Map::kIsObserved));
|
||||
__ bne(&slow, Label::kNear);
|
||||
// Check if the object is a JS array or not.
|
||||
__ LoadlB(r6, FieldMemOperand(receiver_map, Map::kInstanceTypeOffset));
|
||||
__ CmpP(r6, Operand(JS_ARRAY_TYPE));
|
||||
__ beq(&array);
|
||||
// Check that the object is some kind of JSObject.
|
||||
__ CmpP(r6, Operand(FIRST_JS_OBJECT_TYPE));
|
||||
__ blt(&slow, Label::kNear);
|
||||
|
||||
// Object case: Check key against length in the elements array.
|
||||
__ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
||||
// Check array bounds. Both the key and the length of FixedArray are smis.
|
||||
__ CmpLogicalP(key, FieldMemOperand(elements, FixedArray::kLengthOffset));
|
||||
__ blt(&fast_object);
|
||||
|
||||
// Slow case, handle jump to runtime.
|
||||
__ bind(&slow);
|
||||
// Entry registers are intact.
|
||||
// r2: value.
|
||||
// r3: key.
|
||||
// r4: receiver.
|
||||
PropertyICCompiler::GenerateRuntimeSetProperty(masm, language_mode);
|
||||
// Never returns to here.
|
||||
|
||||
__ bind(&maybe_name_key);
|
||||
__ LoadP(r6, FieldMemOperand(key, HeapObject::kMapOffset));
|
||||
__ LoadlB(r6, FieldMemOperand(r6, Map::kInstanceTypeOffset));
|
||||
__ JumpIfNotUniqueNameInstanceType(r6, &slow);
|
||||
|
||||
// The handlers in the stub cache expect a vector and slot. Since we won't
|
||||
// change the IC from any downstream misses, a dummy vector can be used.
|
||||
Register vector = VectorStoreICDescriptor::VectorRegister();
|
||||
Register slot = VectorStoreICDescriptor::SlotRegister();
|
||||
DCHECK(!AreAliased(vector, slot, r7, r8, r9, ip));
|
||||
Handle<TypeFeedbackVector> dummy_vector =
|
||||
TypeFeedbackVector::DummyVector(masm->isolate());
|
||||
int slot_index = dummy_vector->GetIndex(
|
||||
FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedStoreICSlot));
|
||||
__ LoadRoot(vector, Heap::kDummyVectorRootIndex);
|
||||
__ LoadSmiLiteral(slot, Smi::FromInt(slot_index));
|
||||
|
||||
Code::Flags flags = Code::RemoveTypeAndHolderFromFlags(
|
||||
Code::ComputeHandlerFlags(Code::STORE_IC));
|
||||
masm->isolate()->stub_cache()->GenerateProbe(masm, Code::STORE_IC, flags,
|
||||
receiver, key, r7, r8, r9, ip);
|
||||
// Cache miss.
|
||||
__ b(&miss);
|
||||
|
||||
// Extra capacity case: Check if there is extra capacity to
|
||||
// perform the store and update the length. Used for adding one
|
||||
// element to the array by writing to array[array.length].
|
||||
__ bind(&extra);
|
||||
// Condition code from comparing key and array length is still available.
|
||||
__ bne(&slow); // Only support writing to writing to array[array.length].
|
||||
// Check for room in the elements backing store.
|
||||
// Both the key and the length of FixedArray are smis.
|
||||
__ CmpLogicalP(key, FieldMemOperand(elements, FixedArray::kLengthOffset));
|
||||
__ bge(&slow);
|
||||
__ LoadP(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
|
||||
__ CmpP(elements_map, Operand(masm->isolate()->factory()->fixed_array_map()));
|
||||
__ bne(&check_if_double_array, Label::kNear);
|
||||
__ b(&fast_object_grow);
|
||||
|
||||
__ bind(&check_if_double_array);
|
||||
__ CmpP(elements_map,
|
||||
Operand(masm->isolate()->factory()->fixed_double_array_map()));
|
||||
__ bne(&slow);
|
||||
__ b(&fast_double_grow);
|
||||
|
||||
// Array case: Get the length and the elements array from the JS
|
||||
// array. Check that the array is in fast mode (and writable); if it
|
||||
// is the length is always a smi.
|
||||
__ bind(&array);
|
||||
__ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
||||
|
||||
// Check the key against the length in the array.
|
||||
__ CmpLogicalP(key, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
||||
__ bge(&extra);
|
||||
|
||||
KeyedStoreGenerateMegamorphicHelper(
|
||||
masm, &fast_object, &fast_double, &slow, kCheckMap, kDontIncrementLength,
|
||||
value, key, receiver, receiver_map, elements_map, elements);
|
||||
KeyedStoreGenerateMegamorphicHelper(masm, &fast_object_grow,
|
||||
&fast_double_grow, &slow, kDontCheckMap,
|
||||
kIncrementLength, value, key, receiver,
|
||||
receiver_map, elements_map, elements);
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register name = StoreDescriptor::NameRegister();
|
||||
DCHECK(receiver.is(r3));
|
||||
DCHECK(name.is(r4));
|
||||
DCHECK(StoreDescriptor::ValueRegister().is(r2));
|
||||
|
||||
// Get the receiver from the stack and probe the stub cache.
|
||||
Code::Flags flags = Code::RemoveTypeAndHolderFromFlags(
|
||||
Code::ComputeHandlerFlags(Code::STORE_IC));
|
||||
|
||||
masm->isolate()->stub_cache()->GenerateProbe(masm, Code::STORE_IC, flags,
|
||||
receiver, name, r5, r6, r7, r8);
|
||||
|
||||
// Cache miss: Jump to runtime.
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// Perform tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kStoreIC_Miss);
|
||||
}
|
||||
|
||||
void StoreIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register name = StoreDescriptor::NameRegister();
|
||||
Register value = StoreDescriptor::ValueRegister();
|
||||
Register dictionary = r7;
|
||||
DCHECK(receiver.is(r3));
|
||||
DCHECK(name.is(r4));
|
||||
DCHECK(value.is(r2));
|
||||
DCHECK(VectorStoreICDescriptor::VectorRegister().is(r5));
|
||||
DCHECK(VectorStoreICDescriptor::SlotRegister().is(r6));
|
||||
|
||||
__ LoadP(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
|
||||
GenerateDictionaryStore(masm, &miss, dictionary, name, value, r8, r9);
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->ic_store_normal_hit(), 1, r8, r9);
|
||||
__ Ret();
|
||||
|
||||
__ bind(&miss);
|
||||
__ IncrementCounter(counters->ic_store_normal_miss(), 1, r8, r9);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
||||
Condition CompareIC::ComputeCondition(Token::Value op) {
|
||||
switch (op) {
|
||||
case Token::EQ_STRICT:
|
||||
case Token::EQ:
|
||||
return eq;
|
||||
case Token::LT:
|
||||
return lt;
|
||||
case Token::GT:
|
||||
return gt;
|
||||
case Token::LTE:
|
||||
return le;
|
||||
case Token::GTE:
|
||||
return ge;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return kNoCondition;
|
||||
}
|
||||
}
|
||||
|
||||
bool CompareIC::HasInlinedSmiCode(Address address) {
|
||||
// The address of the instruction following the call.
|
||||
Address cmp_instruction_address =
|
||||
Assembler::return_address_from_call_start(address);
|
||||
|
||||
// If the instruction following the call is not a CHI, nothing
|
||||
// was inlined.
|
||||
return (Instruction::S390OpcodeValue(cmp_instruction_address) == CHI);
|
||||
}
|
||||
|
||||
//
|
||||
// This code is paired with the JumpPatchSite class in full-codegen-s390.cc
|
||||
//
|
||||
void PatchInlinedSmiCode(Isolate* isolate, Address address,
|
||||
InlinedSmiCheck check) {
|
||||
Address cmp_instruction_address =
|
||||
Assembler::return_address_from_call_start(address);
|
||||
|
||||
// If the instruction following the call is not a cmp rx, #yyy, nothing
|
||||
// was inlined.
|
||||
Instr instr = Assembler::instr_at(cmp_instruction_address);
|
||||
if (Instruction::S390OpcodeValue(cmp_instruction_address) != CHI) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Instruction::S390OpcodeValue(address) != BRASL) {
|
||||
return;
|
||||
}
|
||||
// The delta to the start of the map check instruction and the
|
||||
// condition code uses at the patched jump.
|
||||
int delta = instr & 0x0000ffff;
|
||||
|
||||
// If the delta is 0 the instruction is cmp r0, #0 which also signals that
|
||||
// nothing was inlined.
|
||||
if (delta == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (FLAG_trace_ic) {
|
||||
PrintF("[ patching ic at %p, cmp=%p, delta=%d\n", address,
|
||||
cmp_instruction_address, delta);
|
||||
}
|
||||
|
||||
// Expected sequence to enable by changing the following
|
||||
// CR/CGR Rx, Rx // 2 / 4 bytes
|
||||
// LR R0, R0 // 2 bytes // 31-bit only!
|
||||
// BRC/BRCL // 4 / 6 bytes
|
||||
// into
|
||||
// TMLL Rx, XXX // 4 bytes
|
||||
// BRC/BRCL // 4 / 6 bytes
|
||||
// And vice versa to disable.
|
||||
|
||||
// The following constant is the size of the CR/CGR + LR + LR
|
||||
const int kPatchAreaSizeNoBranch = 4;
|
||||
Address patch_address = cmp_instruction_address - delta;
|
||||
Address branch_address = patch_address + kPatchAreaSizeNoBranch;
|
||||
|
||||
Instr instr_at_patch = Assembler::instr_at(patch_address);
|
||||
SixByteInstr branch_instr = Assembler::instr_at(branch_address);
|
||||
|
||||
// This is patching a conditional "jump if not smi/jump if smi" site.
|
||||
size_t patch_size = 0;
|
||||
if (Instruction::S390OpcodeValue(branch_address) == BRC) {
|
||||
patch_size = kPatchAreaSizeNoBranch + 4;
|
||||
} else if (Instruction::S390OpcodeValue(branch_address) == BRCL) {
|
||||
patch_size = kPatchAreaSizeNoBranch + 6;
|
||||
} else {
|
||||
DCHECK(false);
|
||||
}
|
||||
CodePatcher patcher(isolate, patch_address, patch_size);
|
||||
Register reg;
|
||||
reg.reg_code = instr_at_patch & 0xf;
|
||||
if (check == ENABLE_INLINED_SMI_CHECK) {
|
||||
patcher.masm()->TestIfSmi(reg);
|
||||
} else {
|
||||
// Emit the NOP to ensure sufficient place for patching
|
||||
// (replaced by LR + NILL)
|
||||
DCHECK(check == DISABLE_INLINED_SMI_CHECK);
|
||||
patcher.masm()->CmpP(reg, reg);
|
||||
#ifndef V8_TARGET_ARCH_S390X
|
||||
patcher.masm()->nop();
|
||||
#endif
|
||||
}
|
||||
|
||||
Condition cc = al;
|
||||
if (Instruction::S390OpcodeValue(branch_address) == BRC) {
|
||||
cc = static_cast<Condition>((branch_instr & 0x00f00000) >> 20);
|
||||
DCHECK((cc == ne) || (cc == eq));
|
||||
cc = (cc == ne) ? eq : ne;
|
||||
patcher.masm()->brc(cc, Operand((branch_instr & 0xffff) << 1));
|
||||
} else if (Instruction::S390OpcodeValue(branch_address) == BRCL) {
|
||||
cc = static_cast<Condition>(
|
||||
(branch_instr & (static_cast<uint64_t>(0x00f0) << 32)) >> 36);
|
||||
DCHECK((cc == ne) || (cc == eq));
|
||||
cc = (cc == ne) ? eq : ne;
|
||||
patcher.masm()->brcl(cc, Operand((branch_instr & 0xffffffff) << 1));
|
||||
} else {
|
||||
DCHECK(false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_TARGET_ARCH_S390
|
187
src/ic/s390/stub-cache-s390.cc
Normal file
187
src/ic/s390/stub-cache-s390.cc
Normal file
@ -0,0 +1,187 @@
|
||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#if V8_TARGET_ARCH_S390
|
||||
|
||||
#include "src/ic/stub-cache.h"
|
||||
#include "src/codegen.h"
|
||||
#include "src/ic/ic.h"
|
||||
#include "src/interface-descriptors.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
static void ProbeTable(Isolate* isolate, MacroAssembler* masm,
|
||||
Code::Kind ic_kind, Code::Flags flags,
|
||||
StubCache::Table table, Register receiver, Register name,
|
||||
// Number of the cache entry, not scaled.
|
||||
Register offset, Register scratch, Register scratch2,
|
||||
Register offset_scratch) {
|
||||
ExternalReference key_offset(isolate->stub_cache()->key_reference(table));
|
||||
ExternalReference value_offset(isolate->stub_cache()->value_reference(table));
|
||||
ExternalReference map_offset(isolate->stub_cache()->map_reference(table));
|
||||
|
||||
uintptr_t key_off_addr = reinterpret_cast<uintptr_t>(key_offset.address());
|
||||
uintptr_t value_off_addr =
|
||||
reinterpret_cast<uintptr_t>(value_offset.address());
|
||||
uintptr_t map_off_addr = reinterpret_cast<uintptr_t>(map_offset.address());
|
||||
|
||||
// Check the relative positions of the address fields.
|
||||
DCHECK(value_off_addr > key_off_addr);
|
||||
DCHECK((value_off_addr - key_off_addr) % 4 == 0);
|
||||
DCHECK((value_off_addr - key_off_addr) < (256 * 4));
|
||||
DCHECK(map_off_addr > key_off_addr);
|
||||
DCHECK((map_off_addr - key_off_addr) % 4 == 0);
|
||||
DCHECK((map_off_addr - key_off_addr) < (256 * 4));
|
||||
|
||||
Label miss;
|
||||
Register base_addr = scratch;
|
||||
scratch = no_reg;
|
||||
|
||||
// Multiply by 3 because there are 3 fields per entry (name, code, map).
|
||||
__ ShiftLeftP(offset_scratch, offset, Operand(1));
|
||||
__ AddP(offset_scratch, offset, offset_scratch);
|
||||
|
||||
// Calculate the base address of the entry.
|
||||
__ mov(base_addr, Operand(key_offset));
|
||||
#if V8_TARGET_ARCH_S390X
|
||||
DCHECK(kPointerSizeLog2 > StubCache::kCacheIndexShift);
|
||||
__ ShiftLeftP(offset_scratch, offset_scratch,
|
||||
Operand(kPointerSizeLog2 - StubCache::kCacheIndexShift));
|
||||
#else
|
||||
DCHECK(kPointerSizeLog2 == StubCache::kCacheIndexShift);
|
||||
#endif
|
||||
__ AddP(base_addr, base_addr, offset_scratch);
|
||||
|
||||
// Check that the key in the entry matches the name.
|
||||
__ CmpP(name, MemOperand(base_addr, 0));
|
||||
__ bne(&miss, Label::kNear);
|
||||
|
||||
// Check the map matches.
|
||||
__ LoadP(ip, MemOperand(base_addr, map_off_addr - key_off_addr));
|
||||
__ CmpP(ip, FieldMemOperand(receiver, HeapObject::kMapOffset));
|
||||
__ bne(&miss, Label::kNear);
|
||||
|
||||
// Get the code entry from the cache.
|
||||
Register code = scratch2;
|
||||
scratch2 = no_reg;
|
||||
__ LoadP(code, MemOperand(base_addr, value_off_addr - key_off_addr));
|
||||
|
||||
// Check that the flags match what we're looking for.
|
||||
Register flags_reg = base_addr;
|
||||
base_addr = no_reg;
|
||||
__ LoadlW(flags_reg, FieldMemOperand(code, Code::kFlagsOffset));
|
||||
|
||||
DCHECK(!r0.is(flags_reg));
|
||||
__ AndP(flags_reg, flags_reg, Operand(~Code::kFlagsNotUsedInLookup));
|
||||
__ CmpLogicalP(flags_reg, Operand(flags));
|
||||
__ bne(&miss, Label::kNear);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
|
||||
__ b(&miss, Label::kNear);
|
||||
} else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
|
||||
__ b(&miss, Label::kNear);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Jump to the first instruction in the code stub.
|
||||
// TODO(joransiu): Combine into indirect branch
|
||||
__ la(code, MemOperand(code, Code::kHeaderSize - kHeapObjectTag));
|
||||
__ b(code);
|
||||
|
||||
// Miss: fall through.
|
||||
__ bind(&miss);
|
||||
}
|
||||
|
||||
void StubCache::GenerateProbe(MacroAssembler* masm, Code::Kind ic_kind,
|
||||
Code::Flags flags, Register receiver,
|
||||
Register name, Register scratch, Register extra,
|
||||
Register extra2, Register extra3) {
|
||||
Isolate* isolate = masm->isolate();
|
||||
Label miss;
|
||||
|
||||
#if V8_TARGET_ARCH_S390X
|
||||
// Make sure that code is valid. The multiplying code relies on the
|
||||
// entry size being 24.
|
||||
DCHECK(sizeof(Entry) == 24);
|
||||
#else
|
||||
// Make sure that code is valid. The multiplying code relies on the
|
||||
// entry size being 12.
|
||||
DCHECK(sizeof(Entry) == 12);
|
||||
#endif
|
||||
|
||||
// Make sure the flags does not name a specific type.
|
||||
DCHECK(Code::ExtractTypeFromFlags(flags) == 0);
|
||||
|
||||
// Make sure that there are no register conflicts.
|
||||
DCHECK(!AreAliased(receiver, name, scratch, extra, extra2, extra3));
|
||||
|
||||
// Check scratch, extra and extra2 registers are valid.
|
||||
DCHECK(!scratch.is(no_reg));
|
||||
DCHECK(!extra.is(no_reg));
|
||||
DCHECK(!extra2.is(no_reg));
|
||||
DCHECK(!extra3.is(no_reg));
|
||||
|
||||
#ifdef DEBUG
|
||||
// If vector-based ics are in use, ensure that scratch, extra, extra2 and
|
||||
// extra3 don't conflict with the vector and slot registers, which need
|
||||
// to be preserved for a handler call or miss.
|
||||
if (IC::ICUseVector(ic_kind)) {
|
||||
Register vector, slot;
|
||||
if (ic_kind == Code::STORE_IC || ic_kind == Code::KEYED_STORE_IC) {
|
||||
vector = VectorStoreICDescriptor::VectorRegister();
|
||||
slot = VectorStoreICDescriptor::SlotRegister();
|
||||
} else {
|
||||
vector = LoadWithVectorDescriptor::VectorRegister();
|
||||
slot = LoadWithVectorDescriptor::SlotRegister();
|
||||
}
|
||||
DCHECK(!AreAliased(vector, slot, scratch, extra, extra2, extra3));
|
||||
}
|
||||
#endif
|
||||
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1, extra2,
|
||||
extra3);
|
||||
|
||||
// Check that the receiver isn't a smi.
|
||||
__ JumpIfSmi(receiver, &miss);
|
||||
|
||||
// Get the map of the receiver and compute the hash.
|
||||
__ LoadlW(scratch, FieldMemOperand(name, Name::kHashFieldOffset));
|
||||
__ LoadP(ip, FieldMemOperand(receiver, HeapObject::kMapOffset));
|
||||
__ AddP(scratch, scratch, ip);
|
||||
__ XorP(scratch, scratch, Operand(flags));
|
||||
// The mask omits the last two bits because they are not part of the hash.
|
||||
__ AndP(scratch, scratch,
|
||||
Operand((kPrimaryTableSize - 1) << kCacheIndexShift));
|
||||
|
||||
// Probe the primary table.
|
||||
ProbeTable(isolate, masm, ic_kind, flags, kPrimary, receiver, name, scratch,
|
||||
extra, extra2, extra3);
|
||||
|
||||
// Primary miss: Compute hash for secondary probe.
|
||||
__ SubP(scratch, scratch, name);
|
||||
__ AddP(scratch, scratch, Operand(flags));
|
||||
__ AndP(scratch, scratch,
|
||||
Operand((kSecondaryTableSize - 1) << kCacheIndexShift));
|
||||
|
||||
// Probe the secondary table.
|
||||
ProbeTable(isolate, masm, ic_kind, flags, kSecondary, receiver, name, scratch,
|
||||
extra, extra2, extra3);
|
||||
|
||||
// Cache miss: Fall-through and let caller handle the miss by
|
||||
// entering the runtime system.
|
||||
__ bind(&miss);
|
||||
__ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1, extra2,
|
||||
extra3);
|
||||
}
|
||||
|
||||
#undef __
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_TARGET_ARCH_S390
|
@ -1558,6 +1558,12 @@
|
||||
}],
|
||||
['v8_target_arch=="s390" or v8_target_arch=="s390x"', {
|
||||
'sources': [ ### gcmole(arch:s390) ###
|
||||
'../../src/debug/s390/debug-s390.cc',
|
||||
'../../src/ic/s390/access-compiler-s390.cc',
|
||||
'../../src/ic/s390/handler-compiler-s390.cc',
|
||||
'../../src/ic/s390/ic-compiler-s390.cc',
|
||||
'../../src/ic/s390/ic-s390.cc',
|
||||
'../../src/ic/s390/stub-cache-s390.cc',
|
||||
'../../src/s390/assembler-s390-inl.h',
|
||||
'../../src/s390/assembler-s390.cc',
|
||||
'../../src/s390/assembler-s390.h',
|
||||
|
Loading…
Reference in New Issue
Block a user