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:
vitalyr@chromium.org 2011-03-14 15:36:00 +00:00
parent 1dd8136dd6
commit 269b19b447
16 changed files with 267 additions and 9 deletions

View File

@ -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));

View File

@ -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) {

View File

@ -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());

View File

@ -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);

View File

@ -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()) {

View File

@ -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_;

View File

@ -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) {

View File

@ -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());
}

View File

@ -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());

View File

@ -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);

View File

@ -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));

View File

@ -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) {

View File

@ -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());

View File

@ -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);

View File

@ -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));

View File

@ -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) {