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) {
}
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) {
VirtualFrame::SpilledScope spilled_scope;
ASSERT(args->length() == 2);
......
......@@ -366,6 +366,9 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args);
// Fast support for StringAdd.
void GenerateStringAdd(ZoneList<Expression*>* args);
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
......
......@@ -36,6 +36,7 @@ namespace internal {
#define CODE_STUB_LIST_ALL_PLATFORMS(V) \
V(CallFunction) \
V(GenericBinaryOp) \
V(StringAdd) \
V(SmiOp) \
V(Compare) \
V(RecordWrite) \
......
......@@ -346,6 +346,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
{&CodeGenerator::GenerateMathCos, "_Math_cos"},
{&CodeGenerator::GenerateIsObject, "_IsObject"},
{&CodeGenerator::GenerateIsFunction, "_IsFunction"},
{&CodeGenerator::GenerateStringAdd, "_StringAdd"},
};
......
This diff is collapsed.
......@@ -546,6 +546,9 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args);
// Fast support for StringAdd.
void GenerateStringAdd(ZoneList<Expression*>* args);
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
......@@ -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
#endif // V8_IA32_CODEGEN_IA32_H_
......@@ -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,
Register result,
Register op,
......@@ -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) {
ASSERT(argc >= 1 && generating_stub());
ret((argc - 1) * kPointerSize);
......
......@@ -183,7 +183,7 @@ class MacroAssembler: public Assembler {
// 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
// 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
// AllocateInNewSpace). If result_contains_top_on_entry is true scratch
// should be no_reg as it is never used.
......@@ -225,6 +225,32 @@ class MacroAssembler: public Assembler {
Register scratch2,
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.
......@@ -262,6 +288,9 @@ class MacroAssembler: public Assembler {
// Call a code stub.
void CallStub(CodeStub* stub);
// Tail call a code stub (jump).
void TailCallStub(CodeStub* stub);
// Return from a code stub after popping its arguments.
void StubReturn(int argc);
......
......@@ -3783,6 +3783,7 @@ static Object* Runtime_StringAdd(Arguments args) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(String, str1, args[0]);
CONVERT_CHECKED(String, str2, args[1]);
Counters::string_add_runtime.Increment();
return Heap::AllocateConsString(str1, str2);
}
......
......@@ -146,16 +146,16 @@ function COMPARE(x, ncr) {
function ADD(x) {
// Fast case: Check for number operands and do the addition.
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.
var a = %ToPrimitive(this, NO_HINT);
var b = %ToPrimitive(x, NO_HINT);
if (IS_STRING(a)) {
return %StringAdd(a, %ToString(b));
return %_StringAdd(a, %ToString(b));
} else if (IS_STRING(b)) {
return %StringAdd(%ToString(a), b);
return %_StringAdd(%ToString(a), b);
} else {
return %NumberAdd(%ToNumber(a), %ToNumber(b));
}
......@@ -173,7 +173,7 @@ function STRING_ADD_LEFT(y) {
: %ToString(%ToPrimitive(y, NO_HINT));
}
}
return %StringAdd(this, y);
return %_StringAdd(this, y);
}
......@@ -189,7 +189,7 @@ function STRING_ADD_RIGHT(y) {
: %ToString(%ToPrimitive(x, NO_HINT));
}
}
return %StringAdd(x, y);
return %_StringAdd(x, y);
}
......
......@@ -153,8 +153,9 @@ namespace internal {
SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
SC(compute_entry_frame, V8.ComputeEntryFrame) \
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.
class Counters : AllStatic {
......
......@@ -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) {
ASSERT(args->length() == 1);
JumpTarget leave, null, function, non_function_constructor;
......
......@@ -544,6 +544,9 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(ZoneList<Expression*>* args);
// Fast support for StringAdd.
void GenerateStringAdd(ZoneList<Expression*>* args);
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
......
......@@ -376,7 +376,7 @@ TEST(ExternalShortStringAdd) {
ascii_external_strings->Set(v8::Integer::New(i), ascii_external_string);
uc16* non_ascii = Zone::NewArray<uc16>(i + 1);
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
// string data.
......@@ -389,20 +389,44 @@ TEST(ExternalShortStringAdd) {
// Add the arrays with the short external strings in the global object.
v8::Handle<v8::Object> global = env->Global();
global->Set(v8_str("ascii"), ascii_external_strings);
global->Set(v8_str("non_ascii"), non_ascii_external_strings);
global->Set(v8_str("external_ascii"), 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.
static const char* source =
"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++) {"
" if (non_ascii[i] != (non_ascii[j] + non_ascii[i - j])) return false;"
" if (ascii[i] != (ascii[j] + ascii[i - j])) return false;"
" if (external_ascii[i] !="
" (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()";
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");
assertEquals("42strz", reswz, "swwz");
assertEquals(84, resww, "swww");
})(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