Crankshaft support for StringCharFromCode.
This allows more efficient implementations of string keyed access, String.prototype.chatAt, and String.fromCharCode. Review URL: http://codereview.chromium.org/6682025 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7160 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
1dd8136dd6
commit
269b19b447
@ -1904,6 +1904,13 @@ LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
|
||||
LOperand* char_code = UseRegister(instr->value());
|
||||
LStringCharFromCode* result = new LStringCharFromCode(char_code);
|
||||
return AssignPointerMap(DefineAsRegister(result));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
|
||||
LOperand* string = UseRegisterAtStart(instr->value());
|
||||
return DefineAsRegister(new LStringLength(string));
|
||||
|
@ -149,6 +149,7 @@ class LCodeGen;
|
||||
V(StoreNamedGeneric) \
|
||||
V(StorePixelArrayElement) \
|
||||
V(StringCharCodeAt) \
|
||||
V(StringCharFromCode) \
|
||||
V(StringLength) \
|
||||
V(SubI) \
|
||||
V(TaggedToI) \
|
||||
@ -1635,6 +1636,19 @@ class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LStringCharFromCode(LOperand* char_code) {
|
||||
inputs_[0] = char_code;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
|
||||
DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
|
||||
|
||||
LOperand* char_code() { return inputs_[0]; }
|
||||
};
|
||||
|
||||
|
||||
class LStringLength: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LStringLength(LOperand* string) {
|
||||
|
@ -3123,6 +3123,56 @@ void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) {
|
||||
class DeferredStringCharFromCode: public LDeferredCode {
|
||||
public:
|
||||
DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
|
||||
: LDeferredCode(codegen), instr_(instr) { }
|
||||
virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
|
||||
private:
|
||||
LStringCharFromCode* instr_;
|
||||
};
|
||||
|
||||
DeferredStringCharFromCode* deferred =
|
||||
new DeferredStringCharFromCode(this, instr);
|
||||
|
||||
ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
|
||||
Register char_code = ToRegister(instr->char_code());
|
||||
Register result = ToRegister(instr->result());
|
||||
ASSERT(!char_code.is(result));
|
||||
|
||||
__ cmp(char_code, Operand(String::kMaxAsciiCharCode));
|
||||
__ b(hi, deferred->entry());
|
||||
__ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex);
|
||||
__ add(result, result, Operand(char_code, LSL, kPointerSizeLog2));
|
||||
__ ldr(result, FieldMemOperand(result, FixedArray::kHeaderSize));
|
||||
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
|
||||
__ cmp(result, ip);
|
||||
__ b(eq, deferred->entry());
|
||||
__ bind(deferred->exit());
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) {
|
||||
Register char_code = ToRegister(instr->char_code());
|
||||
Register result = ToRegister(instr->result());
|
||||
|
||||
// TODO(3095996): Get rid of this. For now, we need to make the
|
||||
// result register contain a valid pointer because it is already
|
||||
// contained in the register pointer map.
|
||||
__ mov(result, Operand(0));
|
||||
|
||||
__ PushSafepointRegisters();
|
||||
__ SmiTag(char_code);
|
||||
__ push(char_code);
|
||||
__ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
|
||||
RecordSafepointWithRegisters(
|
||||
instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
|
||||
__ StoreToSafepointRegisterSlot(r0, result);
|
||||
__ PopSafepointRegisters();
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoStringLength(LStringLength* instr) {
|
||||
Register string = ToRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
|
@ -105,6 +105,7 @@ class LCodeGen BASE_EMBEDDED {
|
||||
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
|
||||
void DoDeferredStackCheck(LGoto* instr);
|
||||
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
|
||||
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
|
||||
void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
||||
Label* map_check);
|
||||
|
||||
|
@ -540,6 +540,8 @@ void Property::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
ZoneMapList* types = oracle->LoadReceiverTypes(this, name);
|
||||
receiver_types_ = types;
|
||||
}
|
||||
} else if (oracle->LoadIsBuiltin(this, Builtins::KeyedLoadIC_String)) {
|
||||
is_string_access_ = true;
|
||||
} else if (is_monomorphic_) {
|
||||
monomorphic_receiver_type_ = oracle->LoadMonomorphicReceiverType(this);
|
||||
if (monomorphic_receiver_type_->has_external_array_elements()) {
|
||||
|
@ -1214,6 +1214,7 @@ class Property: public Expression {
|
||||
is_monomorphic_(false),
|
||||
is_array_length_(false),
|
||||
is_string_length_(false),
|
||||
is_string_access_(false),
|
||||
is_function_prototype_(false),
|
||||
is_arguments_access_(false) { }
|
||||
|
||||
@ -1228,6 +1229,7 @@ class Property: public Expression {
|
||||
bool is_synthetic() const { return type_ == SYNTHETIC; }
|
||||
|
||||
bool IsStringLength() const { return is_string_length_; }
|
||||
bool IsStringAccess() const { return is_string_access_; }
|
||||
bool IsFunctionPrototype() const { return is_function_prototype_; }
|
||||
|
||||
// Marks that this is actually an argument rewritten to a keyed property
|
||||
@ -1265,6 +1267,7 @@ class Property: public Expression {
|
||||
bool is_monomorphic_ : 1;
|
||||
bool is_array_length_ : 1;
|
||||
bool is_string_length_ : 1;
|
||||
bool is_string_access_ : 1;
|
||||
bool is_function_prototype_ : 1;
|
||||
bool is_arguments_access_ : 1;
|
||||
Handle<Map> monomorphic_receiver_type_;
|
||||
|
@ -151,6 +151,7 @@ class LChunkBuilder;
|
||||
V(StoreNamedField) \
|
||||
V(StoreNamedGeneric) \
|
||||
V(StringCharCodeAt) \
|
||||
V(StringCharFromCode) \
|
||||
V(StringLength) \
|
||||
V(Sub) \
|
||||
V(Test) \
|
||||
@ -3268,6 +3269,23 @@ class HStringCharCodeAt: public HBinaryOperation {
|
||||
};
|
||||
|
||||
|
||||
class HStringCharFromCode: public HUnaryOperation {
|
||||
public:
|
||||
explicit HStringCharFromCode(HValue* char_code) : HUnaryOperation(char_code) {
|
||||
set_representation(Representation::Tagged());
|
||||
SetFlag(kUseGVN);
|
||||
}
|
||||
|
||||
virtual Representation RequiredInputRepresentation(int index) const {
|
||||
return Representation::Integer32();
|
||||
}
|
||||
|
||||
virtual bool DataEquals(HValue* other) { return true; }
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string_char_from_code")
|
||||
};
|
||||
|
||||
|
||||
class HStringLength: public HUnaryOperation {
|
||||
public:
|
||||
explicit HStringLength(HValue* string) : HUnaryOperation(string) {
|
||||
|
@ -3703,6 +3703,13 @@ void HGraphBuilder::VisitProperty(Property* expr) {
|
||||
FIRST_STRING_TYPE,
|
||||
LAST_STRING_TYPE));
|
||||
instr = new HStringLength(string);
|
||||
} else if (expr->IsStringAccess()) {
|
||||
VISIT_FOR_VALUE(expr->key());
|
||||
HValue* index = Pop();
|
||||
HValue* string = Pop();
|
||||
HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
|
||||
AddInstruction(char_code);
|
||||
instr = new HStringCharFromCode(char_code);
|
||||
|
||||
} else if (expr->IsFunctionPrototype()) {
|
||||
HValue* function = Pop();
|
||||
@ -4083,6 +4090,7 @@ bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr,
|
||||
int argument_count = expr->arguments()->length() + 1; // Plus receiver.
|
||||
switch (id) {
|
||||
case kStringCharCodeAt:
|
||||
case kStringCharAt:
|
||||
if (argument_count == 2 && check_type == STRING_CHECK) {
|
||||
HValue* index = Pop();
|
||||
HValue* string = Pop();
|
||||
@ -4090,7 +4098,13 @@ bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr,
|
||||
AddInstruction(new HCheckPrototypeMaps(
|
||||
oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
|
||||
expr->holder()));
|
||||
HStringCharCodeAt* result = BuildStringCharCodeAt(string, index);
|
||||
HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
|
||||
if (id == kStringCharCodeAt) {
|
||||
ast_context()->ReturnInstruction(char_code, expr->id());
|
||||
return true;
|
||||
}
|
||||
AddInstruction(char_code);
|
||||
HStringCharFromCode* result = new HStringCharFromCode(char_code);
|
||||
ast_context()->ReturnInstruction(result, expr->id());
|
||||
return true;
|
||||
}
|
||||
@ -5178,19 +5192,24 @@ void HGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
|
||||
|
||||
// Fast support for string.charAt(n) and string[n].
|
||||
void HGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
|
||||
BAILOUT("inlined runtime function: StringCharFromCode");
|
||||
ASSERT(call->arguments()->length() == 1);
|
||||
VISIT_FOR_VALUE(call->arguments()->at(0));
|
||||
HValue* char_code = Pop();
|
||||
HStringCharFromCode* result = new HStringCharFromCode(char_code);
|
||||
ast_context()->ReturnInstruction(result, call->id());
|
||||
}
|
||||
|
||||
|
||||
// Fast support for string.charAt(n) and string[n].
|
||||
void HGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
|
||||
ASSERT_EQ(2, call->arguments()->length());
|
||||
VisitArgumentList(call->arguments());
|
||||
CHECK_BAILOUT;
|
||||
HContext* context = new HContext;
|
||||
AddInstruction(context);
|
||||
HCallStub* result = new HCallStub(context, CodeStub::StringCharAt, 2);
|
||||
Drop(2);
|
||||
ASSERT(call->arguments()->length() == 2);
|
||||
VISIT_FOR_VALUE(call->arguments()->at(0));
|
||||
VISIT_FOR_VALUE(call->arguments()->at(1));
|
||||
HValue* index = Pop();
|
||||
HValue* string = Pop();
|
||||
HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
|
||||
AddInstruction(char_code);
|
||||
HStringCharFromCode* result = new HStringCharFromCode(char_code);
|
||||
ast_context()->ReturnInstruction(result, call->id());
|
||||
}
|
||||
|
||||
|
@ -3030,6 +3030,56 @@ void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) {
|
||||
class DeferredStringCharFromCode: public LDeferredCode {
|
||||
public:
|
||||
DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
|
||||
: LDeferredCode(codegen), instr_(instr) { }
|
||||
virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
|
||||
private:
|
||||
LStringCharFromCode* instr_;
|
||||
};
|
||||
|
||||
DeferredStringCharFromCode* deferred =
|
||||
new DeferredStringCharFromCode(this, instr);
|
||||
|
||||
ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
|
||||
Register char_code = ToRegister(instr->char_code());
|
||||
Register result = ToRegister(instr->result());
|
||||
ASSERT(!char_code.is(result));
|
||||
|
||||
__ cmp(char_code, String::kMaxAsciiCharCode);
|
||||
__ j(above, deferred->entry());
|
||||
__ Set(result, Immediate(Factory::single_character_string_cache()));
|
||||
__ mov(result, FieldOperand(result,
|
||||
char_code, times_pointer_size,
|
||||
FixedArray::kHeaderSize));
|
||||
__ cmp(result, Factory::undefined_value());
|
||||
__ j(equal, deferred->entry());
|
||||
__ bind(deferred->exit());
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) {
|
||||
Register char_code = ToRegister(instr->char_code());
|
||||
Register result = ToRegister(instr->result());
|
||||
|
||||
// TODO(3095996): Get rid of this. For now, we need to make the
|
||||
// result register contain a valid pointer because it is already
|
||||
// contained in the register pointer map.
|
||||
__ Set(result, Immediate(0));
|
||||
|
||||
__ PushSafepointRegisters();
|
||||
__ SmiTag(char_code);
|
||||
__ push(char_code);
|
||||
__ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
|
||||
RecordSafepointWithRegisters(
|
||||
instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
|
||||
__ StoreToSafepointRegisterSlot(result, eax);
|
||||
__ PopSafepointRegisters();
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoStringLength(LStringLength* instr) {
|
||||
Register string = ToRegister(instr->string());
|
||||
Register result = ToRegister(instr->result());
|
||||
|
@ -95,6 +95,7 @@ class LCodeGen BASE_EMBEDDED {
|
||||
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
|
||||
void DoDeferredStackCheck(LGoto* instr);
|
||||
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
|
||||
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
|
||||
void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
||||
Label* map_check);
|
||||
|
||||
|
@ -1943,6 +1943,13 @@ LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
|
||||
LOperand* char_code = UseRegister(instr->value());
|
||||
LStringCharFromCode* result = new LStringCharFromCode(char_code);
|
||||
return AssignPointerMap(DefineAsRegister(result));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
|
||||
LOperand* string = UseRegisterAtStart(instr->value());
|
||||
return DefineAsRegister(new LStringLength(string));
|
||||
|
@ -151,6 +151,7 @@ class LCodeGen;
|
||||
V(StoreNamedGeneric) \
|
||||
V(StorePixelArrayElement) \
|
||||
V(StringCharCodeAt) \
|
||||
V(StringCharFromCode) \
|
||||
V(StringLength) \
|
||||
V(SubI) \
|
||||
V(TaggedToI) \
|
||||
@ -1715,6 +1716,19 @@ class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LStringCharFromCode(LOperand* char_code) {
|
||||
inputs_[0] = char_code;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
|
||||
DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
|
||||
|
||||
LOperand* char_code() { return inputs_[0]; }
|
||||
};
|
||||
|
||||
|
||||
class LStringLength: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LStringLength(LOperand* string) {
|
||||
|
@ -3002,6 +3002,56 @@ void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) {
|
||||
class DeferredStringCharFromCode: public LDeferredCode {
|
||||
public:
|
||||
DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
|
||||
: LDeferredCode(codegen), instr_(instr) { }
|
||||
virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
|
||||
private:
|
||||
LStringCharFromCode* instr_;
|
||||
};
|
||||
|
||||
DeferredStringCharFromCode* deferred =
|
||||
new DeferredStringCharFromCode(this, instr);
|
||||
|
||||
ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
|
||||
Register char_code = ToRegister(instr->char_code());
|
||||
Register result = ToRegister(instr->result());
|
||||
ASSERT(!char_code.is(result));
|
||||
|
||||
__ cmpl(char_code, Immediate(String::kMaxAsciiCharCode));
|
||||
__ j(above, deferred->entry());
|
||||
__ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex);
|
||||
__ movq(result, FieldOperand(result,
|
||||
char_code, times_pointer_size,
|
||||
FixedArray::kHeaderSize));
|
||||
__ CompareRoot(result, Heap::kUndefinedValueRootIndex);
|
||||
__ j(equal, deferred->entry());
|
||||
__ bind(deferred->exit());
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) {
|
||||
Register char_code = ToRegister(instr->char_code());
|
||||
Register result = ToRegister(instr->result());
|
||||
|
||||
// TODO(3095996): Get rid of this. For now, we need to make the
|
||||
// result register contain a valid pointer because it is already
|
||||
// contained in the register pointer map.
|
||||
__ Set(result, 0);
|
||||
|
||||
__ PushSafepointRegisters();
|
||||
__ Integer32ToSmi(char_code, char_code);
|
||||
__ push(char_code);
|
||||
__ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
|
||||
RecordSafepointWithRegisters(
|
||||
instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
|
||||
__ StoreToSafepointRegisterSlot(result, rax);
|
||||
__ PopSafepointRegisters();
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoStringLength(LStringLength* instr) {
|
||||
Register string = ToRegister(instr->string());
|
||||
Register result = ToRegister(instr->result());
|
||||
|
@ -92,6 +92,7 @@ class LCodeGen BASE_EMBEDDED {
|
||||
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
|
||||
void DoDeferredStackCheck(LGoto* instr);
|
||||
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
|
||||
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
|
||||
void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
||||
Label* map_check);
|
||||
|
||||
|
@ -1905,6 +1905,13 @@ LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
|
||||
LOperand* char_code = UseRegister(instr->value());
|
||||
LStringCharFromCode* result = new LStringCharFromCode(char_code);
|
||||
return AssignPointerMap(DefineAsRegister(result));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
|
||||
LOperand* string = UseRegisterAtStart(instr->value());
|
||||
return DefineAsRegister(new LStringLength(string));
|
||||
|
@ -149,6 +149,7 @@ class LCodeGen;
|
||||
V(StoreNamedGeneric) \
|
||||
V(StorePixelArrayElement) \
|
||||
V(StringCharCodeAt) \
|
||||
V(StringCharFromCode) \
|
||||
V(StringLength) \
|
||||
V(SubI) \
|
||||
V(TaggedToI) \
|
||||
@ -1631,6 +1632,19 @@ class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LStringCharFromCode(LOperand* char_code) {
|
||||
inputs_[0] = char_code;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
|
||||
DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
|
||||
|
||||
LOperand* char_code() { return inputs_[0]; }
|
||||
};
|
||||
|
||||
|
||||
class LStringLength: public LTemplateInstruction<1, 1, 0> {
|
||||
public:
|
||||
explicit LStringLength(LOperand* string) {
|
||||
|
Loading…
Reference in New Issue
Block a user