Implementing inline caches for binary operations (ia32).

This is a subset of a CL reviewed earlier(http://codereview.chromium.org/551093).
The register usage optimisation part has been reviewed and submitted separately.
Two fast cases supported: HeapNumber operands and String operands for ADD.


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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3988 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent d4cb1ba3
......@@ -31,6 +31,7 @@
#include "codegen-inl.h"
#include "compiler.h"
#include "debug.h"
#include "ic-inl.h"
#include "parser.h"
#include "register-allocator-inl.h"
#include "runtime.h"
......@@ -6235,6 +6236,11 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
}
Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
return Handle<Code>::null();
}
void StackCheckStub::Generate(MacroAssembler* masm) {
// Do tail-call to runtime routine. Runtime routines expect at least one
// argument, so give it a Smi.
......
......@@ -83,6 +83,11 @@ void CodeStub::RecordCodeGeneration(Code* code, MacroAssembler* masm) {
}
int CodeStub::GetCodeKind() {
return Code::STUB;
}
Handle<Code> CodeStub::GetCode() {
Code* code;
if (!FindCodeInCache(&code)) {
......@@ -97,7 +102,10 @@ Handle<Code> CodeStub::GetCode() {
masm.GetCode(&desc);
// Copy the generated code into a heap object.
Code::Flags flags = Code::ComputeFlags(Code::STUB, InLoop());
Code::Flags flags = Code::ComputeFlags(
static_cast<Code::Kind>(GetCodeKind()),
InLoop(),
GetICState());
Handle<Code> new_object =
Factory::NewCode(desc, NULL, flags, masm.CodeObject());
RecordCodeGeneration(*new_object, &masm);
......@@ -132,7 +140,10 @@ Object* CodeStub::TryGetCode() {
masm.GetCode(&desc);
// Try to copy the generated code into a heap object.
Code::Flags flags = Code::ComputeFlags(Code::STUB, InLoop());
Code::Flags flags = Code::ComputeFlags(
static_cast<Code::Kind>(GetCodeKind()),
InLoop(),
GetICState());
Object* new_object =
Heap::CreateCode(desc, NULL, flags, masm.CodeObject());
if (new_object->IsFailure()) return new_object;
......
......@@ -28,6 +28,8 @@
#ifndef V8_CODE_STUBS_H_
#define V8_CODE_STUBS_H_
#include "globals.h"
namespace v8 {
namespace internal {
......@@ -139,6 +141,14 @@ class CodeStub BASE_EMBEDDED {
// lazily generated function should be fully optimized or not.
virtual InLoopFlag InLoop() { return NOT_IN_LOOP; }
// GenericBinaryOpStub needs to override this.
virtual int GetCodeKind();
// GenericBinaryOpStub needs to override this.
virtual InlineCacheState GetICState() {
return UNINITIALIZED;
}
// Returns a name for logging/debugging purposes.
virtual const char* GetName() { return MajorName(MajorKey(), false); }
......
......@@ -123,7 +123,9 @@ void BreakLocationIterator::Next() {
if (RelocInfo::IsCodeTarget(rmode())) {
Address target = original_rinfo()->target_address();
Code* code = Code::GetCodeFromTargetAddress(target);
if (code->is_inline_cache_stub() || RelocInfo::IsConstructCall(rmode())) {
if ((code->is_inline_cache_stub() &&
code->kind() != Code::BINARY_OP_IC) ||
RelocInfo::IsConstructCall(rmode())) {
break_point_++;
return;
}
......@@ -1337,24 +1339,26 @@ Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) {
// Find the builtin debug break function matching the calling convention
// used by the call site.
if (code->is_inline_cache_stub()) {
if (code->is_call_stub()) {
return ComputeCallDebugBreak(code->arguments_count());
}
if (code->is_load_stub()) {
return Handle<Code>(Builtins::builtin(Builtins::LoadIC_DebugBreak));
}
if (code->is_store_stub()) {
return Handle<Code>(Builtins::builtin(Builtins::StoreIC_DebugBreak));
}
if (code->is_keyed_load_stub()) {
Handle<Code> result =
Handle<Code>(Builtins::builtin(Builtins::KeyedLoadIC_DebugBreak));
return result;
}
if (code->is_keyed_store_stub()) {
Handle<Code> result =
Handle<Code>(Builtins::builtin(Builtins::KeyedStoreIC_DebugBreak));
return result;
switch (code->kind()) {
case Code::CALL_IC:
return ComputeCallDebugBreak(code->arguments_count());
case Code::LOAD_IC:
return Handle<Code>(Builtins::builtin(Builtins::LoadIC_DebugBreak));
case Code::STORE_IC:
return Handle<Code>(Builtins::builtin(Builtins::StoreIC_DebugBreak));
case Code::KEYED_LOAD_IC:
return Handle<Code>(
Builtins::builtin(Builtins::KeyedLoadIC_DebugBreak));
case Code::KEYED_STORE_IC:
return Handle<Code>(
Builtins::builtin(Builtins::KeyedStoreIC_DebugBreak));
default:
UNREACHABLE();
}
}
if (RelocInfo::IsConstructCall(mode)) {
......
This diff is collapsed.
......@@ -28,6 +28,8 @@
#ifndef V8_IA32_CODEGEN_IA32_H_
#define V8_IA32_CODEGEN_IA32_H_
#include "ic-inl.h"
namespace v8 {
namespace internal {
......@@ -699,12 +701,25 @@ class GenericBinaryOpStub: public CodeStub {
flags_(flags),
args_in_registers_(false),
args_reversed_(false),
name_(NULL),
operands_type_(operands_type) {
static_operands_type_(operands_type),
runtime_operands_type_(BinaryOpIC::DEFAULT),
name_(NULL) {
use_sse3_ = CpuFeatures::IsSupported(SSE3);
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo runtime_operands_type)
: op_(OpBits::decode(key)),
mode_(ModeBits::decode(key)),
flags_(FlagBits::decode(key)),
args_in_registers_(ArgsInRegistersBits::decode(key)),
args_reversed_(ArgsReversedBits::decode(key)),
use_sse3_(SSE3Bits::decode(key)),
static_operands_type_(StaticTypeInfoBits::decode(key)),
runtime_operands_type_(runtime_operands_type),
name_(NULL) {
}
// Generate code to call the stub with the supplied arguments. This will add
// code at the call site to prepare arguments either in registers or on the
// stack together with the actual call.
......@@ -724,8 +739,14 @@ class GenericBinaryOpStub: public CodeStub {
bool args_in_registers_; // Arguments passed in registers not on the stack.
bool args_reversed_; // Left and right argument are swapped.
bool use_sse3_;
// Number type information of operands, determined by code generator.
NumberInfo::Type static_operands_type_;
// Operand type information determined at runtime.
BinaryOpIC::TypeInfo runtime_operands_type_;
char* name_;
NumberInfo::Type operands_type_; // Number type information of operands.
const char* GetName();
......@@ -739,29 +760,31 @@ class GenericBinaryOpStub: public CodeStub {
static_cast<int>(flags_),
static_cast<int>(args_in_registers_),
static_cast<int>(args_reversed_),
NumberInfo::ToString(operands_type_));
NumberInfo::ToString(static_operands_type_));
}
#endif
// Minor key encoding in 16 bits NNNFRASOOOOOOOMM.
// Minor key encoding in 18 bits RRNNNFRASOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 7> {};
class SSE3Bits: public BitField<bool, 9, 1> {};
class ArgsInRegistersBits: public BitField<bool, 10, 1> {};
class ArgsReversedBits: public BitField<bool, 11, 1> {};
class FlagBits: public BitField<GenericBinaryFlags, 12, 1> {};
class NumberInfoBits: public BitField<NumberInfo::Type, 13, 3> {};
class StaticTypeInfoBits: public BitField<NumberInfo::Type, 13, 3> {};
class RuntimeTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 16, 2> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
// Encode the parameters in a unique 16 bit value.
// Encode the parameters in a unique 18 bit value.
return OpBits::encode(op_)
| ModeBits::encode(mode_)
| FlagBits::encode(flags_)
| SSE3Bits::encode(use_sse3_)
| ArgsInRegistersBits::encode(args_in_registers_)
| ArgsReversedBits::encode(args_reversed_)
| NumberInfoBits::encode(operands_type_);
| StaticTypeInfoBits::encode(static_operands_type_)
| RuntimeTypeInfoBits::encode(runtime_operands_type_);
}
void Generate(MacroAssembler* masm);
......@@ -769,6 +792,8 @@ class GenericBinaryOpStub: public CodeStub {
void GenerateLoadArguments(MacroAssembler* masm);
void GenerateReturn(MacroAssembler* masm);
void GenerateHeapResultAllocation(MacroAssembler* masm, Label* alloc_failure);
void GenerateRegisterArgsPush(MacroAssembler* masm);
void GenerateTypeTransition(MacroAssembler* masm);
bool ArgsInRegistersSupported() {
return op_ == Token::ADD || op_ == Token::SUB
......@@ -783,6 +808,22 @@ class GenericBinaryOpStub: public CodeStub {
bool HasSmiCodeInStub() { return (flags_ & NO_SMI_CODE_IN_STUB) == 0; }
bool HasArgsInRegisters() { return args_in_registers_; }
bool HasArgsReversed() { return args_reversed_; }
bool ShouldGenerateSmiCode() {
return HasSmiCodeInStub() &&
runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
runtime_operands_type_ != BinaryOpIC::STRINGS;
}
bool ShouldGenerateFPCode() {
return runtime_operands_type_ != BinaryOpIC::STRINGS;
}
virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
virtual InlineCacheState GetICState() {
return BinaryOpIC::ToState(runtime_operands_type_);
}
};
......
......@@ -222,6 +222,7 @@ void IC::Clear(Address address) {
case Code::STORE_IC: return StoreIC::Clear(address, target);
case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target);
case Code::CALL_IC: return CallIC::Clear(address, target);
case Code::BINARY_OP_IC: return BinaryOpIC::Clear(address, target);
default: UNREACHABLE();
}
}
......@@ -1416,6 +1417,112 @@ Object* KeyedStoreIC_Miss(Arguments args) {
}
void BinaryOpIC::patch(Code* code) {
set_target(code);
}
void BinaryOpIC::Clear(Address address, Code* target) {
if (target->ic_state() == UNINITIALIZED) return;
// At the end of a fast case stub there should be a reference to
// a corresponding UNINITIALIZED stub, so look for the last reloc info item.
RelocInfo* rinfo = NULL;
for (RelocIterator it(target, RelocInfo::kCodeTargetMask);
!it.done(); it.next()) {
rinfo = it.rinfo();
}
ASSERT(rinfo != NULL);
Code* uninit_stub = Code::GetCodeFromTargetAddress(rinfo->target_address());
ASSERT(uninit_stub->ic_state() == UNINITIALIZED &&
uninit_stub->kind() == Code::BINARY_OP_IC);
SetTargetAtAddress(address, uninit_stub);
}
const char* BinaryOpIC::GetName(TypeInfo type_info) {
switch (type_info) {
case DEFAULT: return "Default";
case GENERIC: return "Generic";
case HEAP_NUMBERS: return "HeapNumbers";
case STRINGS: return "Strings";
default: return "Invalid";
}
}
BinaryOpIC::State BinaryOpIC::ToState(TypeInfo type_info) {
switch (type_info) {
// DEFAULT is mapped to UNINITIALIZED so that calls to DEFAULT stubs
// are not cleared at GC.
case DEFAULT: return UNINITIALIZED;
// Could have mapped GENERIC to MONOMORPHIC just as well but MEGAMORPHIC is
// conceptually closer.
case GENERIC: return MEGAMORPHIC;
default: return MONOMORPHIC;
}
}
BinaryOpIC::TypeInfo BinaryOpIC::GetTypeInfo(Object* left,
Object* right) {
// Patching is never requested for the two smis.
ASSERT(!left->IsSmi() || !right->IsSmi());
if (left->IsNumber() && right->IsNumber()) {
return HEAP_NUMBERS;
}
if (left->IsString() || right->IsString()) {
// Patching for fast string ADD makes sense even if only one of the
// arguments is a string.
return STRINGS;
}
return GENERIC;
}
// defined in codegen-<arch>.cc
Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info);
Object* BinaryOp_Patch(Arguments args) {
ASSERT(args.length() == 6);
Handle<Object> left = args.at<Object>(0);
Handle<Object> right = args.at<Object>(1);
Handle<Object> result = args.at<Object>(2);
int key = Smi::cast(args[3])->value();
#ifdef DEBUG
Token::Value op = static_cast<Token::Value>(Smi::cast(args[4])->value());
BinaryOpIC::TypeInfo prev_type_info =
static_cast<BinaryOpIC::TypeInfo>(Smi::cast(args[5])->value());
#endif // DEBUG
{ HandleScope scope;
BinaryOpIC::TypeInfo type_info = BinaryOpIC::GetTypeInfo(*left, *right);
Handle<Code> code = GetBinaryOpStub(key, type_info);
if (!code.is_null()) {
BinaryOpIC ic;
ic.patch(*code);
#ifdef DEBUG
if (FLAG_trace_ic) {
PrintF("[BinaryOpIC (%s->%s)#%s]\n",
BinaryOpIC::GetName(prev_type_info),
BinaryOpIC::GetName(type_info),
Token::Name(op));
}
#endif // DEBUG
}
}
return *result;
}
static Address IC_utilities[] = {
#define ADDR(name) FUNCTION_ADDR(name),
IC_UTIL_LIST(ADDR)
......
......@@ -55,7 +55,8 @@ enum DictionaryCheck { CHECK_DICTIONARY, DICTIONARY_CHECK_DONE };
ICU(LoadPropertyWithInterceptorForLoad) \
ICU(LoadPropertyWithInterceptorForCall) \
ICU(KeyedLoadPropertyWithInterceptor) \
ICU(StoreInterceptorProperty)
ICU(StoreInterceptorProperty) \
ICU(BinaryOp_Patch)
//
// IC is the base class for LoadIC, StoreIC, CallIC, KeyedLoadIC,
......@@ -444,6 +445,30 @@ class KeyedStoreIC: public IC {
};
class BinaryOpIC: public IC {
public:
enum TypeInfo {
DEFAULT, // Initial state. When first executed, patches to one
// of the following states depending on the operands types.
HEAP_NUMBERS, // Both arguments are HeapNumbers.
STRINGS, // At least one of the arguments is String.
GENERIC // Non-specialized case (processes any type combination).
};
BinaryOpIC() : IC(NO_EXTRA_FRAME) { }
void patch(Code* code);
static void Clear(Address address, Code* target);
static const char* GetName(TypeInfo type_info);
static State ToState(TypeInfo type_info);
static TypeInfo GetTypeInfo(Object* left, Object* right);
};
} } // namespace v8::internal
#endif // V8_IC_H_
......@@ -1267,6 +1267,8 @@ void Logger::LogCodeObject(Object* object) {
switch (code_object->kind()) {
case Code::FUNCTION:
return; // We log this later using LogCompiledFunctions.
case Code::BINARY_OP_IC:
// fall through
case Code::STUB:
description = CodeStub::MajorName(code_object->major_key(), true);
if (description == NULL)
......
......@@ -2149,14 +2149,14 @@ int Code::arguments_count() {
CodeStub::Major Code::major_key() {
ASSERT(kind() == STUB);
ASSERT(kind() == STUB || kind() == BINARY_OP_IC);
return static_cast<CodeStub::Major>(READ_BYTE_FIELD(this,
kStubMajorKeyOffset));
}
void Code::set_major_key(CodeStub::Major major) {
ASSERT(kind() == STUB);
ASSERT(kind() == STUB || kind() == BINARY_OP_IC);
ASSERT(0 <= major && major < 256);
WRITE_BYTE_FIELD(this, kStubMajorKeyOffset, major);
}
......
......@@ -5126,6 +5126,7 @@ const char* Code::Kind2String(Kind kind) {
case STORE_IC: return "STORE_IC";
case KEYED_STORE_IC: return "KEYED_STORE_IC";
case CALL_IC: return "CALL_IC";
case BINARY_OP_IC: return "BINARY_OP_IC";
}
UNREACHABLE();
return NULL;
......
......@@ -2618,13 +2618,14 @@ class Code: public HeapObject {
CALL_IC,
STORE_IC,
KEYED_STORE_IC,
// No more than eight kinds. The value currently encoded in three bits in
BINARY_OP_IC,
// No more than 16 kinds. The value currently encoded in four bits in
// Flags.
// Pseudo-kinds.
REGEXP = BUILTIN,
FIRST_IC_KIND = LOAD_IC,
LAST_IC_KIND = KEYED_STORE_IC
LAST_IC_KIND = BINARY_OP_IC
};
enum {
......@@ -2670,7 +2671,7 @@ class Code: public HeapObject {
inline bool is_keyed_store_stub() { return kind() == KEYED_STORE_IC; }
inline bool is_call_stub() { return kind() == CALL_IC; }
// [major_key]: For kind STUB, the major key.
// [major_key]: For kind STUB or BINARY_OP_IC, the major key.
inline CodeStub::Major major_key();
inline void set_major_key(CodeStub::Major major);
......@@ -2777,14 +2778,14 @@ class Code: public HeapObject {
static const int kFlagsICStateShift = 0;
static const int kFlagsICInLoopShift = 3;
static const int kFlagsKindShift = 4;
static const int kFlagsTypeShift = 7;
static const int kFlagsArgumentsCountShift = 10;
static const int kFlagsICStateMask = 0x00000007; // 0000000111
static const int kFlagsICInLoopMask = 0x00000008; // 0000001000
static const int kFlagsKindMask = 0x00000070; // 0001110000
static const int kFlagsTypeMask = 0x00000380; // 1110000000
static const int kFlagsArgumentsCountMask = 0xFFFFFC00;
static const int kFlagsTypeShift = 8;
static const int kFlagsArgumentsCountShift = 11;
static const int kFlagsICStateMask = 0x00000007; // 00000000111
static const int kFlagsICInLoopMask = 0x00000008; // 00000001000
static const int kFlagsKindMask = 0x000000F0; // 00011110000
static const int kFlagsTypeMask = 0x00000700; // 11100000000
static const int kFlagsArgumentsCountMask = 0xFFFFF800;
static const int kFlagsNotUsedInLookup =
(kFlagsICInLoopMask | kFlagsTypeMask);
......
......@@ -1379,6 +1379,7 @@ static void ReportCodeKindStatistics() {
CASE(STORE_IC);
CASE(KEYED_STORE_IC);
CASE(CALL_IC);
CASE(BINARY_OP_IC);
}
}
......
......@@ -8898,6 +8898,11 @@ void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) {
}
Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
return Handle<Code>::null();
}
int CompareStub::MinorKey() {
// Encode the three parameters in a unique 16 bit value.
ASSERT(static_cast<unsigned>(cc_) < (1 << 14));
......
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