Commit 53fbd593 authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

Perform string add in generated code on IA-32 platforms

This adds a code stub which can do most of what Heap::AllocateConsString can do. It bails out if the result cannot fit in new space or if the result is a short (flat) string and one argument is an ascii string and the other a two byte string. It also bails out if adding two one character strings as Heap::AllocateConsString has special handling of this utilizing the symbol table. The stub is used both for the binary add operation and for StringAdd calls from runtime JavaScript files. Extended the string add test to cover all sizes of flat result stings.
Review URL: http://codereview.chromium.org/442024

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3400 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 73ebe80b
...@@ -3505,6 +3505,17 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) { ...@@ -3505,6 +3505,17 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) {
} }
void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
ASSERT_EQ(2, args->length());
Load(args->at(0));
Load(args->at(1));
frame_->CallRuntime(Runtime::kStringAdd, 2);
frame_->EmitPush(r0);
}
void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) { void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
VirtualFrame::SpilledScope spilled_scope; VirtualFrame::SpilledScope spilled_scope;
ASSERT(args->length() == 2); ASSERT(args->length() == 2);
......
...@@ -366,6 +366,9 @@ class CodeGenerator: public AstVisitor { ...@@ -366,6 +366,9 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args); inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args); inline void GenerateMathCos(ZoneList<Expression*>* args);
// Fast support for StringAdd.
void GenerateStringAdd(ZoneList<Expression*>* args);
// Simple condition analysis. // Simple condition analysis.
enum ConditionAnalysis { enum ConditionAnalysis {
ALWAYS_TRUE, ALWAYS_TRUE,
......
...@@ -36,6 +36,7 @@ namespace internal { ...@@ -36,6 +36,7 @@ namespace internal {
#define CODE_STUB_LIST_ALL_PLATFORMS(V) \ #define CODE_STUB_LIST_ALL_PLATFORMS(V) \
V(CallFunction) \ V(CallFunction) \
V(GenericBinaryOp) \ V(GenericBinaryOp) \
V(StringAdd) \
V(SmiOp) \ V(SmiOp) \
V(Compare) \ V(Compare) \
V(RecordWrite) \ V(RecordWrite) \
......
...@@ -346,6 +346,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = { ...@@ -346,6 +346,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
{&CodeGenerator::GenerateMathCos, "_Math_cos"}, {&CodeGenerator::GenerateMathCos, "_Math_cos"},
{&CodeGenerator::GenerateIsObject, "_IsObject"}, {&CodeGenerator::GenerateIsObject, "_IsObject"},
{&CodeGenerator::GenerateIsFunction, "_IsFunction"}, {&CodeGenerator::GenerateIsFunction, "_IsFunction"},
{&CodeGenerator::GenerateStringAdd, "_StringAdd"},
}; };
......
This diff is collapsed.
...@@ -546,6 +546,9 @@ class CodeGenerator: public AstVisitor { ...@@ -546,6 +546,9 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args); inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args); inline void GenerateMathCos(ZoneList<Expression*>* args);
// Fast support for StringAdd.
void GenerateStringAdd(ZoneList<Expression*>* args);
// Simple condition analysis. // Simple condition analysis.
enum ConditionAnalysis { enum ConditionAnalysis {
ALWAYS_TRUE, ALWAYS_TRUE,
...@@ -737,6 +740,37 @@ class GenericBinaryOpStub: public CodeStub { ...@@ -737,6 +740,37 @@ class GenericBinaryOpStub: public CodeStub {
}; };
// Flag that indicates how to generate code for the stub StringAddStub.
enum StringAddFlags {
NO_STRING_ADD_FLAGS = 0,
NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub.
};
class StringAddStub: public CodeStub {
public:
explicit StringAddStub(StringAddFlags flags) {
string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0);
}
private:
Major MajorKey() { return StringAdd; }
int MinorKey() { return string_check_ ? 0 : 1; }
void Generate(MacroAssembler* masm);
void GenerateCopyCharacters(MacroAssembler* masm,
Register desc,
Register src,
Register count,
Register scratch,
bool ascii);
// Should the stub check whether arguments are strings?
bool string_check_;
};
} } // namespace v8::internal } } // namespace v8::internal
#endif // V8_IA32_CODEGEN_IA32_H_ #endif // V8_IA32_CODEGEN_IA32_H_
...@@ -820,6 +820,104 @@ void MacroAssembler::AllocateHeapNumber(Register result, ...@@ -820,6 +820,104 @@ void MacroAssembler::AllocateHeapNumber(Register result,
} }
void MacroAssembler::AllocateTwoByteString(Register result,
Register length,
Register scratch1,
Register scratch2,
Register scratch3,
Label* gc_required) {
// Calculate the number of words needed for the number of characters in the
// string
mov(scratch1, length);
add(Operand(scratch1), Immediate(1));
shr(scratch1, 1);
// Allocate two byte string in new space.
AllocateInNewSpace(SeqTwoByteString::kHeaderSize,
times_4,
scratch1,
result,
scratch2,
scratch3,
gc_required,
TAG_OBJECT);
// Set the map, length and hash field.
mov(FieldOperand(result, HeapObject::kMapOffset),
Immediate(Factory::string_map()));
mov(FieldOperand(result, String::kLengthOffset), length);
mov(FieldOperand(result, String::kHashFieldOffset),
Immediate(String::kEmptyHashField));
}
void MacroAssembler::AllocateAsciiString(Register result,
Register length,
Register scratch1,
Register scratch2,
Register scratch3,
Label* gc_required) {
// Calculate the number of words needed for the number of characters in the
// string
mov(scratch1, length);
add(Operand(scratch1), Immediate(3));
shr(scratch1, 2);
// Allocate ascii string in new space.
AllocateInNewSpace(SeqAsciiString::kHeaderSize,
times_4,
scratch1,
result,
scratch2,
scratch3,
gc_required,
TAG_OBJECT);
// Set the map, length and hash field.
mov(FieldOperand(result, HeapObject::kMapOffset),
Immediate(Factory::ascii_string_map()));
mov(FieldOperand(result, String::kLengthOffset), length);
mov(FieldOperand(result, String::kHashFieldOffset),
Immediate(String::kEmptyHashField));
}
void MacroAssembler::AllocateConsString(Register result,
Register scratch1,
Register scratch2,
Label* gc_required) {
// Allocate heap number in new space.
AllocateInNewSpace(ConsString::kSize,
result,
scratch1,
scratch2,
gc_required,
TAG_OBJECT);
// Set the map. The other fields are left uninitialized.
mov(FieldOperand(result, HeapObject::kMapOffset),
Immediate(Factory::cons_string_map()));
}
void MacroAssembler::AllocateAsciiConsString(Register result,
Register scratch1,
Register scratch2,
Label* gc_required) {
// Allocate heap number in new space.
AllocateInNewSpace(ConsString::kSize,
result,
scratch1,
scratch2,
gc_required,
TAG_OBJECT);
// Set the map. The other fields are left uninitialized.
mov(FieldOperand(result, HeapObject::kMapOffset),
Immediate(Factory::cons_ascii_string_map()));
}
void MacroAssembler::NegativeZeroTest(CodeGenerator* cgen, void MacroAssembler::NegativeZeroTest(CodeGenerator* cgen,
Register result, Register result,
Register op, Register op,
...@@ -913,6 +1011,12 @@ void MacroAssembler::CallStub(CodeStub* stub) { ...@@ -913,6 +1011,12 @@ void MacroAssembler::CallStub(CodeStub* stub) {
} }
void MacroAssembler::TailCallStub(CodeStub* stub) {
ASSERT(allow_stub_calls()); // calls are not allowed in some stubs
jmp(stub->GetCode(), RelocInfo::CODE_TARGET);
}
void MacroAssembler::StubReturn(int argc) { void MacroAssembler::StubReturn(int argc) {
ASSERT(argc >= 1 && generating_stub()); ASSERT(argc >= 1 && generating_stub());
ret((argc - 1) * kPointerSize); ret((argc - 1) * kPointerSize);
......
...@@ -183,7 +183,7 @@ class MacroAssembler: public Assembler { ...@@ -183,7 +183,7 @@ class MacroAssembler: public Assembler {
// scratch can be passed as no_reg in which case an additional object // scratch can be passed as no_reg in which case an additional object
// reference will be added to the reloc info. The returned pointers in result // reference will be added to the reloc info. The returned pointers in result
// and result_end have not yet been tagged as heap objects. If // and result_end have not yet been tagged as heap objects. If
// result_contains_top_on_entry is true the contnt of result is known to be // result_contains_top_on_entry is true the content of result is known to be
// the allocation top on entry (could be result_end from a previous call to // the allocation top on entry (could be result_end from a previous call to
// AllocateInNewSpace). If result_contains_top_on_entry is true scratch // AllocateInNewSpace). If result_contains_top_on_entry is true scratch
// should be no_reg as it is never used. // should be no_reg as it is never used.
...@@ -225,6 +225,32 @@ class MacroAssembler: public Assembler { ...@@ -225,6 +225,32 @@ class MacroAssembler: public Assembler {
Register scratch2, Register scratch2,
Label* gc_required); Label* gc_required);
// Allocate a sequential string. All the header fields of the string object
// are initialized.
void AllocateTwoByteString(Register result,
Register length,
Register scratch1,
Register scratch2,
Register scratch3,
Label* gc_required);
void AllocateAsciiString(Register result,
Register length,
Register scratch1,
Register scratch2,
Register scratch3,
Label* gc_required);
// Allocate a raw cons string object. Only the map field of the result is
// initialized.
void AllocateConsString(Register result,
Register scratch1,
Register scratch2,
Label* gc_required);
void AllocateAsciiConsString(Register result,
Register scratch1,
Register scratch2,
Label* gc_required);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Support functions. // Support functions.
...@@ -262,6 +288,9 @@ class MacroAssembler: public Assembler { ...@@ -262,6 +288,9 @@ class MacroAssembler: public Assembler {
// Call a code stub. // Call a code stub.
void CallStub(CodeStub* stub); void CallStub(CodeStub* stub);
// Tail call a code stub (jump).
void TailCallStub(CodeStub* stub);
// Return from a code stub after popping its arguments. // Return from a code stub after popping its arguments.
void StubReturn(int argc); void StubReturn(int argc);
......
...@@ -3783,6 +3783,7 @@ static Object* Runtime_StringAdd(Arguments args) { ...@@ -3783,6 +3783,7 @@ static Object* Runtime_StringAdd(Arguments args) {
ASSERT(args.length() == 2); ASSERT(args.length() == 2);
CONVERT_CHECKED(String, str1, args[0]); CONVERT_CHECKED(String, str1, args[0]);
CONVERT_CHECKED(String, str2, args[1]); CONVERT_CHECKED(String, str2, args[1]);
Counters::string_add_runtime.Increment();
return Heap::AllocateConsString(str1, str2); return Heap::AllocateConsString(str1, str2);
} }
......
...@@ -146,16 +146,16 @@ function COMPARE(x, ncr) { ...@@ -146,16 +146,16 @@ function COMPARE(x, ncr) {
function ADD(x) { function ADD(x) {
// Fast case: Check for number operands and do the addition. // Fast case: Check for number operands and do the addition.
if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x); if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x);
if (IS_STRING(this) && IS_STRING(x)) return %StringAdd(this, x); if (IS_STRING(this) && IS_STRING(x)) return %_StringAdd(this, x);
// Default implementation. // Default implementation.
var a = %ToPrimitive(this, NO_HINT); var a = %ToPrimitive(this, NO_HINT);
var b = %ToPrimitive(x, NO_HINT); var b = %ToPrimitive(x, NO_HINT);
if (IS_STRING(a)) { if (IS_STRING(a)) {
return %StringAdd(a, %ToString(b)); return %_StringAdd(a, %ToString(b));
} else if (IS_STRING(b)) { } else if (IS_STRING(b)) {
return %StringAdd(%ToString(a), b); return %_StringAdd(%ToString(a), b);
} else { } else {
return %NumberAdd(%ToNumber(a), %ToNumber(b)); return %NumberAdd(%ToNumber(a), %ToNumber(b));
} }
...@@ -173,7 +173,7 @@ function STRING_ADD_LEFT(y) { ...@@ -173,7 +173,7 @@ function STRING_ADD_LEFT(y) {
: %ToString(%ToPrimitive(y, NO_HINT)); : %ToString(%ToPrimitive(y, NO_HINT));
} }
} }
return %StringAdd(this, y); return %_StringAdd(this, y);
} }
...@@ -189,7 +189,7 @@ function STRING_ADD_RIGHT(y) { ...@@ -189,7 +189,7 @@ function STRING_ADD_RIGHT(y) {
: %ToString(%ToPrimitive(x, NO_HINT)); : %ToString(%ToPrimitive(x, NO_HINT));
} }
} }
return %StringAdd(x, y); return %_StringAdd(x, y);
} }
......
...@@ -153,8 +153,9 @@ namespace internal { ...@@ -153,8 +153,9 @@ namespace internal {
SC(zone_segment_bytes, V8.ZoneSegmentBytes) \ SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
SC(compute_entry_frame, V8.ComputeEntryFrame) \ SC(compute_entry_frame, V8.ComputeEntryFrame) \
SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \ SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \
SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \
SC(string_add_runtime, V8.StringAddRuntime) \
SC(string_add_native, V8.StringAddNative)
// This file contains all the v8 counters that are in use. // This file contains all the v8 counters that are in use.
class Counters : AllStatic { class Counters : AllStatic {
......
...@@ -3996,6 +3996,17 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) { ...@@ -3996,6 +3996,17 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) {
} }
void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
ASSERT_EQ(2, args->length());
Load(args->at(0));
Load(args->at(1));
Result answer = frame_->CallRuntime(Runtime::kStringAdd, 2);
frame_->Push(&answer);
}
void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) { void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1); ASSERT(args->length() == 1);
JumpTarget leave, null, function, non_function_constructor; JumpTarget leave, null, function, non_function_constructor;
......
...@@ -544,6 +544,9 @@ class CodeGenerator: public AstVisitor { ...@@ -544,6 +544,9 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args); inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args); inline void GenerateMathCos(ZoneList<Expression*>* args);
// Fast support for StringAdd.
void GenerateStringAdd(ZoneList<Expression*>* args);
// Simple condition analysis. // Simple condition analysis.
enum ConditionAnalysis { enum ConditionAnalysis {
ALWAYS_TRUE, ALWAYS_TRUE,
......
...@@ -376,7 +376,7 @@ TEST(ExternalShortStringAdd) { ...@@ -376,7 +376,7 @@ TEST(ExternalShortStringAdd) {
ascii_external_strings->Set(v8::Integer::New(i), ascii_external_string); ascii_external_strings->Set(v8::Integer::New(i), ascii_external_string);
uc16* non_ascii = Zone::NewArray<uc16>(i + 1); uc16* non_ascii = Zone::NewArray<uc16>(i + 1);
for (int j = 0; j < i; j++) { for (int j = 0; j < i; j++) {
non_ascii[j] = 1234; non_ascii[j] = 0x1234;
} }
// Terminating '\0' is left out on purpose. It is not required for external // Terminating '\0' is left out on purpose. It is not required for external
// string data. // string data.
...@@ -389,20 +389,44 @@ TEST(ExternalShortStringAdd) { ...@@ -389,20 +389,44 @@ TEST(ExternalShortStringAdd) {
// Add the arrays with the short external strings in the global object. // Add the arrays with the short external strings in the global object.
v8::Handle<v8::Object> global = env->Global(); v8::Handle<v8::Object> global = env->Global();
global->Set(v8_str("ascii"), ascii_external_strings); global->Set(v8_str("external_ascii"), ascii_external_strings);
global->Set(v8_str("non_ascii"), non_ascii_external_strings); global->Set(v8_str("external_non_ascii"), non_ascii_external_strings);
global->Set(v8_str("max_length"), v8::Integer::New(kMaxLength));
// Add short external ascii and non-ascii strings checking the result. // Add short external ascii and non-ascii strings checking the result.
static const char* source = static const char* source =
"function test() {" "function test() {"
" for (var i = 0; i <= 20; i++) {" " var ascii_chars = 'aaaaaaaaaaaaaaaaaaaa';"
" var non_ascii_chars = '\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234';" //NOLINT
" if (ascii_chars.length != max_length) return 1;"
" if (non_ascii_chars.length != max_length) return 2;"
" var ascii = Array(max_length + 1);"
" var non_ascii = Array(max_length + 1);"
" for (var i = 0; i <= max_length; i++) {"
" ascii[i] = ascii_chars.substring(0, i);"
" non_ascii[i] = non_ascii_chars.substring(0, i);"
" };"
" for (var i = 0; i <= max_length; i++) {"
" if (ascii[i] != external_ascii[i]) return 3;"
" if (non_ascii[i] != external_non_ascii[i]) return 4;"
" for (var j = 0; j < i; j++) {" " for (var j = 0; j < i; j++) {"
" if (non_ascii[i] != (non_ascii[j] + non_ascii[i - j])) return false;" " if (external_ascii[i] !="
" if (ascii[i] != (ascii[j] + ascii[i - j])) return false;" " (external_ascii[j] + external_ascii[i - j])) return 5;"
" if (external_non_ascii[i] !="
" (external_non_ascii[j] + external_non_ascii[i - j])) return 6;"
" if (non_ascii[i] != (non_ascii[j] + non_ascii[i - j])) return 7;"
" if (ascii[i] != (ascii[j] + ascii[i - j])) return 8;"
" if (ascii[i] != (external_ascii[j] + ascii[i - j])) return 9;"
" if (ascii[i] != (ascii[j] + external_ascii[i - j])) return 10;"
" if (non_ascii[i] !="
" (external_non_ascii[j] + non_ascii[i - j])) return 11;"
" if (non_ascii[i] !="
" (non_ascii[j] + external_non_ascii[i - j])) return 12;"
" }" " }"
" }" " }"
" return true;" " return 0;"
"};" "};"
"test()"; "test()";
CHECK(v8::Script::Compile(v8::String::New(source))->Run()->BooleanValue()); CHECK_EQ(0,
v8::Script::Compile(v8::String::New(source))->Run()->Int32Value());
} }
...@@ -173,3 +173,23 @@ assertEquals(0, null + null, "uu"); ...@@ -173,3 +173,23 @@ assertEquals(0, null + null, "uu");
assertEquals("42strz", reswz, "swwz"); assertEquals("42strz", reswz, "swwz");
assertEquals(84, resww, "swww"); assertEquals(84, resww, "swww");
})(1); })(1);
// Generate ascii and non ascii strings from length 0 to 20.
var ascii = 'aaaaaaaaaaaaaaaaaaaa';
var non_ascii = '\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234';
assertEquals(20, ascii.length);
assertEquals(20, non_ascii.length);
var a = Array(21);
var b = Array(21);
for (var i = 0; i <= 20; i++) {
a[i] = ascii.substring(0, i);
b[i] = non_ascii.substring(0, i);
}
// Add ascii and non-ascii strings generating strings with length from 0 to 20.
for (var i = 0; i <= 20; i++) {
for (var j = 0; j < i; j++) {
assertEquals(a[i], a[j] + a[i - j])
assertEquals(b[i], b[j] + b[i - j])
}
}
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