Commit 62f7cd85 authored by vitalyr@chromium.org's avatar vitalyr@chromium.org

Handle argument conversion in StringAddStub.

In case one of the arguments is known to be a string we emit a few
fast conversion attempts for the other.  This allows using the
StringAddStub instead of STRING_ADD_{LEFT,RIGHT} builtins.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5405 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent bb782505
...@@ -868,7 +868,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { ...@@ -868,7 +868,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
FloatingPointHelper::LoadSSE2Operands(masm); FloatingPointHelper::LoadSSE2Operands(masm);
} }
} else { } else {
FloatingPointHelper::LoadSSE2Operands(masm, &call_runtime); FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
} }
switch (op_) { switch (op_) {
...@@ -889,7 +889,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { ...@@ -889,7 +889,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
__ AbortIfNotNumber(eax); __ AbortIfNotNumber(eax);
} }
} else { } else {
FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx); FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
} }
FloatingPointHelper::LoadFloatOperands( FloatingPointHelper::LoadFloatOperands(
masm, masm,
...@@ -1001,10 +1001,15 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { ...@@ -1001,10 +1001,15 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
} }
} }
// Avoid hitting the string ADD code below when allocation fails in
// the floating point code above.
if (op_ != Token::ADD) {
__ bind(&call_runtime);
}
// If all else fails, use the runtime system to get the correct // If all else fails, use the runtime system to get the correct
// result. If arguments was passed in registers now place them on the // result. If arguments was passed in registers now place them on the
// stack in the correct order below the return address. // stack in the correct order below the return address.
__ bind(&call_runtime);
if (HasArgsInRegisters()) { if (HasArgsInRegisters()) {
GenerateRegisterArgsPush(masm); GenerateRegisterArgsPush(masm);
} }
...@@ -1012,7 +1017,6 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { ...@@ -1012,7 +1017,6 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
switch (op_) { switch (op_) {
case Token::ADD: { case Token::ADD: {
// Test for string arguments before calling runtime. // Test for string arguments before calling runtime.
Label not_strings, not_string1, string1, string1_smi2;
// If this stub has already generated FP-specific code then the arguments // If this stub has already generated FP-specific code then the arguments
// are already in edx, eax // are already in edx, eax
...@@ -1030,49 +1034,31 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { ...@@ -1030,49 +1034,31 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
rhs = eax; rhs = eax;
} }
// Test if first argument is a string. // Test if left operand is a string.
Label lhs_not_string;
__ test(lhs, Immediate(kSmiTagMask)); __ test(lhs, Immediate(kSmiTagMask));
__ j(zero, &not_string1); __ j(zero, &lhs_not_string);
__ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, ecx); __ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, ecx);
__ j(above_equal, &not_string1); __ j(above_equal, &lhs_not_string);
// First argument is a string, test second.
__ test(rhs, Immediate(kSmiTagMask));
__ j(zero, &string1_smi2);
__ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, ecx);
__ j(above_equal, &string1);
// First and second argument are strings. Jump to the string add stub.
StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
__ TailCallStub(&string_add_stub);
__ bind(&string1_smi2); StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
// First argument is a string, second is a smi. Try to lookup the number __ TailCallStub(&string_add_left_stub);
// string for the smi in the number string cache.
NumberToStringStub::GenerateLookupNumberStringCache(
masm, rhs, edi, ebx, ecx, true, &string1);
// Replace second argument on stack and tailcall string add stub to make // Left operand is not a string, test right.
// the result. __ bind(&lhs_not_string);
__ mov(Operand(esp, 1 * kPointerSize), edi);
__ TailCallStub(&string_add_stub);
// Only first argument is a string.
__ bind(&string1);
__ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION);
// First argument was not a string, test second.
__ bind(&not_string1);
__ test(rhs, Immediate(kSmiTagMask)); __ test(rhs, Immediate(kSmiTagMask));
__ j(zero, &not_strings); __ j(zero, &call_runtime);
__ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, ecx); __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, ecx);
__ j(above_equal, &not_strings); __ j(above_equal, &call_runtime);
// Only second argument is a string. StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
__ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION); __ TailCallStub(&string_add_right_stub);
__ bind(&not_strings);
// Neither argument is a string. // Neither argument is a string.
__ bind(&call_runtime);
if (HasArgsInRegisters()) {
GenerateRegisterArgsPush(masm);
}
__ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION); __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
break; break;
} }
...@@ -3765,14 +3751,15 @@ void StringCharAtGenerator::GenerateSlow( ...@@ -3765,14 +3751,15 @@ void StringCharAtGenerator::GenerateSlow(
void StringAddStub::Generate(MacroAssembler* masm) { void StringAddStub::Generate(MacroAssembler* masm) {
Label string_add_runtime; Label string_add_runtime, call_builtin;
Builtins::JavaScript builtin_id = Builtins::ADD;
// Load the two arguments. // Load the two arguments.
__ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument. __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
__ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument. __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
// Make sure that both arguments are strings if not known in advance. // Make sure that both arguments are strings if not known in advance.
if (string_check_) { if (flags_ == NO_STRING_ADD_FLAGS) {
__ test(eax, Immediate(kSmiTagMask)); __ test(eax, Immediate(kSmiTagMask));
__ j(zero, &string_add_runtime); __ j(zero, &string_add_runtime);
__ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx); __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
...@@ -3783,6 +3770,20 @@ void StringAddStub::Generate(MacroAssembler* masm) { ...@@ -3783,6 +3770,20 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ j(zero, &string_add_runtime); __ j(zero, &string_add_runtime);
__ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx); __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
__ j(above_equal, &string_add_runtime); __ j(above_equal, &string_add_runtime);
} else {
// Here at least one of the arguments is definitely a string.
// We convert the one that is not known to be a string.
if ((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) == 0) {
ASSERT((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) != 0);
GenerateConvertArgument(masm, 2 * kPointerSize, eax, ebx, ecx, edi,
&call_builtin);
builtin_id = Builtins::STRING_ADD_RIGHT;
} else if ((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) == 0) {
ASSERT((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) != 0);
GenerateConvertArgument(masm, 1 * kPointerSize, edx, ebx, ecx, edi,
&call_builtin);
builtin_id = Builtins::STRING_ADD_LEFT;
}
} }
// Both arguments are strings. // Both arguments are strings.
...@@ -4016,6 +4017,56 @@ void StringAddStub::Generate(MacroAssembler* masm) { ...@@ -4016,6 +4017,56 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// Just jump to runtime to add the two strings. // Just jump to runtime to add the two strings.
__ bind(&string_add_runtime); __ bind(&string_add_runtime);
__ TailCallRuntime(Runtime::kStringAdd, 2, 1); __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
if (call_builtin.is_linked()) {
__ bind(&call_builtin);
__ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
}
}
void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
int stack_offset,
Register arg,
Register scratch1,
Register scratch2,
Register scratch3,
Label* slow) {
// First check if the argument is already a string.
Label not_string, done;
__ test(arg, Immediate(kSmiTagMask));
__ j(zero, &not_string);
__ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1);
__ j(below, &done);
// Check the number to string cache.
Label not_cached;
__ bind(&not_string);
// Puts the cached result into scratch1.
NumberToStringStub::GenerateLookupNumberStringCache(masm,
arg,
scratch1,
scratch2,
scratch3,
false,
&not_cached);
__ mov(arg, scratch1);
__ mov(Operand(esp, stack_offset), arg);
__ jmp(&done);
// Check if the argument is a safe string wrapper.
__ bind(&not_cached);
__ test(arg, Immediate(kSmiTagMask));
__ j(zero, slow);
__ CmpObjectType(arg, JS_VALUE_TYPE, scratch1); // map -> scratch1.
__ j(not_equal, slow);
__ test_b(FieldOperand(scratch1, Map::kBitField2Offset),
1 << Map::kStringWrapperSafeForDefaultValueOf);
__ j(zero, slow);
__ mov(arg, FieldOperand(arg, JSValue::kValueOffset));
__ mov(Operand(esp, stack_offset), arg);
__ bind(&done);
} }
......
...@@ -272,24 +272,35 @@ class StringHelper : public AllStatic { ...@@ -272,24 +272,35 @@ class StringHelper : public AllStatic {
// Flag that indicates how to generate code for the stub StringAddStub. // Flag that indicates how to generate code for the stub StringAddStub.
enum StringAddFlags { enum StringAddFlags {
NO_STRING_ADD_FLAGS = 0, NO_STRING_ADD_FLAGS = 0,
NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub. // Omit left string check in stub (left is definitely a string).
NO_STRING_CHECK_LEFT_IN_STUB = 1 << 0,
// Omit right string check in stub (right is definitely a string).
NO_STRING_CHECK_RIGHT_IN_STUB = 1 << 1,
// Omit both string checks in stub.
NO_STRING_CHECK_IN_STUB =
NO_STRING_CHECK_LEFT_IN_STUB | NO_STRING_CHECK_RIGHT_IN_STUB
}; };
class StringAddStub: public CodeStub { class StringAddStub: public CodeStub {
public: public:
explicit StringAddStub(StringAddFlags flags) { explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0);
}
private: private:
Major MajorKey() { return StringAdd; } Major MajorKey() { return StringAdd; }
int MinorKey() { return string_check_ ? 0 : 1; } int MinorKey() { return flags_; }
void Generate(MacroAssembler* masm); void Generate(MacroAssembler* masm);
// Should the stub check whether arguments are strings? void GenerateConvertArgument(MacroAssembler* masm,
bool string_check_; int stack_offset,
Register arg,
Register scratch1,
Register scratch2,
Register scratch3,
Label* slow);
const StringAddFlags flags_;
}; };
......
...@@ -1411,12 +1411,12 @@ void CodeGenerator::GenericBinaryOperation(BinaryOperation* expr, ...@@ -1411,12 +1411,12 @@ void CodeGenerator::GenericBinaryOperation(BinaryOperation* expr,
StringAddStub stub(NO_STRING_CHECK_IN_STUB); StringAddStub stub(NO_STRING_CHECK_IN_STUB);
answer = frame_->CallStub(&stub, 2); answer = frame_->CallStub(&stub, 2);
} else { } else {
answer = StringAddStub stub(NO_STRING_CHECK_LEFT_IN_STUB);
frame_->InvokeBuiltin(Builtins::STRING_ADD_LEFT, CALL_FUNCTION, 2); answer = frame_->CallStub(&stub, 2);
} }
} else if (right_is_string) { } else if (right_is_string) {
answer = StringAddStub stub(NO_STRING_CHECK_RIGHT_IN_STUB);
frame_->InvokeBuiltin(Builtins::STRING_ADD_RIGHT, CALL_FUNCTION, 2); answer = frame_->CallStub(&stub, 2);
} }
answer.set_type_info(TypeInfo::String()); answer.set_type_info(TypeInfo::String());
frame_->Push(&answer); frame_->Push(&answer);
......
...@@ -25,21 +25,38 @@ ...@@ -25,21 +25,38 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/**
* @fileoverview Check that a mod where the stub code hits a failure
* in heap number allocation still works.
*/
// Flags: --max-new-space-size=262144 // Flags: --max-new-space-size=262144
// Check that a mod where the stub code hits a failure in heap number
// allocation still works.
function f(x) { function f(x) {
return x % 3; return x % 3;
} }
function test() { function testMod() {
for (var i = 0; i < 40000; i++) { for (var i = 0; i < 40000; i++) {
assertEquals(-1 / 0, 1 / f(-3)); assertEquals(-1 / 0, 1 / f(-3));
} }
} }
test(); testMod();
// Check that an add where the stub code hits a failure in heap number
// allocation still works.
function g(x, y) {
return x + y;
}
function testAdd() {
var lhs = 17.42;
var rhs = 42.17;
for (var i = 0; i < 40000; i++) {
assertEquals(59.59, g(lhs, rhs));
}
}
testAdd();
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