Commit cc782be6 authored by vitalyr@chromium.org's avatar vitalyr@chromium.org

Support string add in crankshaft:

o The type recording binary stub got a new type for string + string.

o Added HStringAdd and LStringAdd based on the new type info.

o Started using HValue types to avoid unneccesary checks.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7622 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 3b23719d
......@@ -1817,6 +1817,9 @@ void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
case TRBinaryOpIC::ODDBALL:
GenerateOddballStub(masm);
break;
case TRBinaryOpIC::BOTH_STRING:
GenerateBothStringStub(masm);
break;
case TRBinaryOpIC::STRING:
GenerateStringStub(masm);
break;
......@@ -2257,6 +2260,36 @@ void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
}
void TypeRecordingBinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
Label call_runtime;
ASSERT(operands_type_ == TRBinaryOpIC::BOTH_STRING);
ASSERT(op_ == Token::ADD);
// If both arguments are strings, call the string add stub.
// Otherwise, do a transition.
// Registers containing left and right operands respectively.
Register left = r1;
Register right = r0;
// Test if left operand is a string.
__ JumpIfSmi(left, &call_runtime);
__ CompareObjectType(left, r2, r2, FIRST_NONSTRING_TYPE);
__ b(ge, &call_runtime);
// Test if right operand is a string.
__ JumpIfSmi(right, &call_runtime);
__ CompareObjectType(right, r2, r2, FIRST_NONSTRING_TYPE);
__ b(ge, &call_runtime);
StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
GenerateRegisterArgsPush(masm);
__ TailCallStub(&string_add_stub);
__ bind(&call_runtime);
GenerateTypeTransition(masm);
}
void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
ASSERT(operands_type_ == TRBinaryOpIC::INT32);
......
......@@ -158,6 +158,7 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateOddballStub(MacroAssembler* masm);
void GenerateStringStub(MacroAssembler* masm);
void GenerateBothStringStub(MacroAssembler* masm);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateAddStrings(MacroAssembler* masm);
void GenerateCallRuntime(MacroAssembler* masm);
......
......@@ -1945,6 +1945,13 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
}
LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
LOperand* left = UseRegisterAtStart(instr->left());
LOperand* right = UseRegisterAtStart(instr->right());
return MarkAsCall(DefineFixed(new LStringAdd(left, right), r0), instr);
}
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
LOperand* string = UseRegister(instr->string());
LOperand* index = UseRegisterOrConstant(instr->index());
......
......@@ -152,6 +152,7 @@ class LCodeGen;
V(StoreKeyedSpecializedArrayElement) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
......@@ -1706,6 +1707,22 @@ class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> {
};
class LStringAdd: public LTemplateInstruction<1, 2, 0> {
public:
LStringAdd(LOperand* left, LOperand* right) {
inputs_[0] = left;
inputs_[1] = right;
}
DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
DECLARE_HYDROGEN_ACCESSOR(StringAdd)
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
};
class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
public:
LStringCharCodeAt(LOperand* string, LOperand* index) {
......
......@@ -3223,6 +3223,14 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
}
void LCodeGen::DoStringAdd(LStringAdd* instr) {
__ push(ToRegister(instr->left()));
__ push(ToRegister(instr->right()));
StringAddStub stub(NO_STRING_CHECK_IN_STUB);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
class DeferredStringCharCodeAt: public LDeferredCode {
public:
......
......@@ -155,6 +155,7 @@ class LChunkBuilder;
V(StoreKeyedGeneric) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
......@@ -1707,6 +1708,16 @@ class HCheckInstanceType: public HUnaryOperation {
virtual void Verify();
#endif
virtual HValue* Canonicalize() {
if (!value()->type().IsUninitialized() &&
value()->type().IsString() &&
first() == FIRST_STRING_TYPE &&
last() == LAST_STRING_TYPE) {
return NULL;
}
return this;
}
static HCheckInstanceType* NewIsJSObjectOrJSFunction(HValue* value);
InstanceType first() const { return first_; }
......@@ -1748,6 +1759,18 @@ class HCheckNonSmi: public HUnaryOperation {
virtual void Verify();
#endif
virtual HValue* Canonicalize() {
HType value_type = value()->type();
if (!value_type.IsUninitialized() &&
(value_type.IsHeapNumber() ||
value_type.IsString() ||
value_type.IsBoolean() ||
value_type.IsNonPrimitive())) {
return NULL;
}
return this;
}
DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check_non_smi")
protected:
......@@ -3407,6 +3430,29 @@ class HStoreKeyedGeneric: public HTemplateInstruction<4> {
};
class HStringAdd: public HBinaryOperation {
public:
HStringAdd(HValue* left, HValue* right) : HBinaryOperation(left, right) {
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
SetFlag(kDependsOnMaps);
}
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::Tagged();
}
virtual HType CalculateInferredType() {
return HType::String();
}
DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string_add")
protected:
virtual bool DataEquals(HValue* other) { return true; }
};
class HStringCharCodeAt: public HBinaryOperation {
public:
HStringCharCodeAt(HValue* string, HValue* index)
......
......@@ -4888,10 +4888,21 @@ HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string,
HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
HValue* left,
HValue* right) {
TypeInfo info = oracle()->BinaryType(expr);
HInstruction* instr = NULL;
switch (expr->op()) {
case Token::ADD:
instr = new(zone()) HAdd(left, right);
if (info.IsString()) {
AddInstruction(new(zone()) HCheckNonSmi(left));
AddInstruction(new(zone()) HCheckInstanceType(
left, FIRST_STRING_TYPE, LAST_STRING_TYPE));
AddInstruction(new(zone()) HCheckNonSmi(right));
AddInstruction(new(zone()) HCheckInstanceType(
right, FIRST_STRING_TYPE, LAST_STRING_TYPE));
instr = new(zone()) HStringAdd(left, right);
} else {
instr = new(zone()) HAdd(left, right);
}
break;
case Token::SUB:
instr = new(zone()) HSub(left, right);
......@@ -4926,7 +4937,6 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
default:
UNREACHABLE();
}
TypeInfo info = oracle()->BinaryType(expr);
// If we hit an uninitialized binary op stub we will get type info
// for a smi operation. If one of the operands is a constant string
// do not generate code assuming it is a smi operation.
......
......@@ -446,6 +446,9 @@ void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
case TRBinaryOpIC::ODDBALL:
GenerateOddballStub(masm);
break;
case TRBinaryOpIC::BOTH_STRING:
GenerateBothStringStub(masm);
break;
case TRBinaryOpIC::STRING:
GenerateStringStub(masm);
break;
......@@ -909,6 +912,38 @@ void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
}
void TypeRecordingBinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
Label call_runtime;
ASSERT(operands_type_ == TRBinaryOpIC::BOTH_STRING);
ASSERT(op_ == Token::ADD);
// If both arguments are strings, call the string add stub.
// Otherwise, do a transition.
// Registers containing left and right operands respectively.
Register left = edx;
Register right = eax;
// Test if left operand is a string.
__ test(left, Immediate(kSmiTagMask));
__ j(zero, &call_runtime);
__ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
__ j(above_equal, &call_runtime);
// Test if right operand is a string.
__ test(right, Immediate(kSmiTagMask));
__ j(zero, &call_runtime);
__ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
__ j(above_equal, &call_runtime);
StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
GenerateRegisterArgsPush(masm);
__ TailCallStub(&string_add_stub);
__ bind(&call_runtime);
GenerateTypeTransition(masm);
}
void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
Label call_runtime;
ASSERT(operands_type_ == TRBinaryOpIC::INT32);
......
......@@ -153,6 +153,7 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateOddballStub(MacroAssembler* masm);
void GenerateStringStub(MacroAssembler* masm);
void GenerateBothStringStub(MacroAssembler* masm);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateAddStrings(MacroAssembler* masm);
......
......@@ -3308,6 +3308,22 @@ void LCodeGen::DoStringLength(LStringLength* instr) {
}
void LCodeGen::DoStringAdd(LStringAdd* instr) {
if (instr->left()->IsConstantOperand()) {
__ push(ToImmediate(instr->left()));
} else {
__ push(ToOperand(instr->left()));
}
if (instr->right()->IsConstantOperand()) {
__ push(ToImmediate(instr->right()));
} else {
__ push(ToOperand(instr->right()));
}
StringAddStub stub(NO_STRING_CHECK_IN_STUB);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
}
void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
LOperand* input = instr->InputAt(0);
ASSERT(input->IsRegister() || input->IsStackSlot());
......
......@@ -2001,6 +2001,13 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
}
LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
LOperand* left = UseOrConstantAtStart(instr->left());
LOperand* right = UseOrConstantAtStart(instr->right());
return MarkAsCall(DefineFixed(new LStringAdd(left, right), eax), instr);
}
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
LOperand* string = UseRegister(instr->string());
LOperand* index = UseRegisterOrConstant(instr->index());
......
......@@ -39,6 +39,7 @@ namespace internal {
// Forward declarations.
class LCodeGen;
#define LITHIUM_ALL_INSTRUCTION_LIST(V) \
V(ControlInstruction) \
V(Call) \
......@@ -154,6 +155,7 @@ class LCodeGen;
V(StoreKeyedSpecializedArrayElement) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
......@@ -1769,6 +1771,21 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 4, 0> {
};
class LStringAdd: public LTemplateInstruction<1, 2, 0> {
public:
LStringAdd(LOperand* left, LOperand* right) {
inputs_[0] = left;
inputs_[1] = right;
}
DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
DECLARE_HYDROGEN_ACCESSOR(StringAdd)
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
};
class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
public:
LStringCharCodeAt(LOperand* string, LOperand* index) {
......
......@@ -1990,6 +1990,7 @@ const char* TRBinaryOpIC::GetName(TypeInfo type_info) {
case INT32: return "Int32s";
case HEAP_NUMBER: return "HeapNumbers";
case ODDBALL: return "Oddball";
case BOTH_STRING: return "BothStrings";
case STRING: return "Strings";
case GENERIC: return "Generic";
default: return "Invalid";
......@@ -2005,6 +2006,7 @@ TRBinaryOpIC::State TRBinaryOpIC::ToState(TypeInfo type_info) {
case INT32:
case HEAP_NUMBER:
case ODDBALL:
case BOTH_STRING:
case STRING:
return MONOMORPHIC;
case GENERIC:
......@@ -2019,12 +2021,17 @@ TRBinaryOpIC::TypeInfo TRBinaryOpIC::JoinTypes(TRBinaryOpIC::TypeInfo x,
TRBinaryOpIC::TypeInfo y) {
if (x == UNINITIALIZED) return y;
if (y == UNINITIALIZED) return x;
if (x == STRING && y == STRING) return STRING;
if (x == STRING || y == STRING) return GENERIC;
if (x >= y) return x;
if (x == y) return x;
if (x == BOTH_STRING && y == STRING) return STRING;
if (x == STRING && y == BOTH_STRING) return STRING;
if (x == STRING || x == BOTH_STRING || y == STRING || y == BOTH_STRING) {
return GENERIC;
}
if (x > y) return x;
return y;
}
TRBinaryOpIC::TypeInfo TRBinaryOpIC::GetTypeInfo(Handle<Object> left,
Handle<Object> right) {
::v8::internal::TypeInfo left_type =
......@@ -2046,9 +2053,11 @@ TRBinaryOpIC::TypeInfo TRBinaryOpIC::GetTypeInfo(Handle<Object> left,
return HEAP_NUMBER;
}
if (left_type.IsString() || right_type.IsString()) {
// Patching for fast string ADD makes sense even if only one of the
// arguments is a string.
// Patching for fast string ADD makes sense even if only one of the
// arguments is a string.
if (left_type.IsString()) {
return right_type.IsString() ? BOTH_STRING : STRING;
} else if (right_type.IsString()) {
return STRING;
}
......@@ -2081,11 +2090,11 @@ RUNTIME_FUNCTION(MaybeObject*, TypeRecordingBinaryOp_Patch) {
TRBinaryOpIC::TypeInfo type = TRBinaryOpIC::GetTypeInfo(left, right);
type = TRBinaryOpIC::JoinTypes(type, previous_type);
TRBinaryOpIC::TypeInfo result_type = TRBinaryOpIC::UNINITIALIZED;
if (type == TRBinaryOpIC::STRING && op != Token::ADD) {
if ((type == TRBinaryOpIC::STRING || type == TRBinaryOpIC::BOTH_STRING) &&
op != Token::ADD) {
type = TRBinaryOpIC::GENERIC;
}
if (type == TRBinaryOpIC::SMI &&
previous_type == TRBinaryOpIC::SMI) {
if (type == TRBinaryOpIC::SMI && previous_type == TRBinaryOpIC::SMI) {
if (op == Token::DIV || op == Token::MUL || kSmiValueSize == 32) {
// Arithmetic on two Smi inputs has yielded a heap number.
// That is the only way to get here from the Smi stub.
......@@ -2097,8 +2106,7 @@ RUNTIME_FUNCTION(MaybeObject*, TypeRecordingBinaryOp_Patch) {
result_type = TRBinaryOpIC::INT32;
}
}
if (type == TRBinaryOpIC::INT32 &&
previous_type == TRBinaryOpIC::INT32) {
if (type == TRBinaryOpIC::INT32 && previous_type == TRBinaryOpIC::INT32) {
// We must be here because an operation on two INT32 types overflowed.
result_type = TRBinaryOpIC::HEAP_NUMBER;
}
......
......@@ -586,6 +586,7 @@ class TRBinaryOpIC: public IC {
INT32,
HEAP_NUMBER,
ODDBALL,
BOTH_STRING, // Only used for addition operation.
STRING, // Only used for addition operation. At least one string operand.
GENERIC
};
......
......@@ -275,6 +275,8 @@ TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr) {
return TypeInfo::Integer32();
case TRBinaryOpIC::HEAP_NUMBER:
return TypeInfo::Double();
case TRBinaryOpIC::BOTH_STRING:
return TypeInfo::String();
case TRBinaryOpIC::STRING:
case TRBinaryOpIC::GENERIC:
return unknown;
......
......@@ -372,6 +372,9 @@ void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
case TRBinaryOpIC::ODDBALL:
GenerateOddballStub(masm);
break;
case TRBinaryOpIC::BOTH_STRING:
GenerateBothStringStub(masm);
break;
case TRBinaryOpIC::STRING:
GenerateStringStub(masm);
break;
......@@ -771,6 +774,36 @@ void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
}
void TypeRecordingBinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
Label call_runtime;
ASSERT(operands_type_ == TRBinaryOpIC::BOTH_STRING);
ASSERT(op_ == Token::ADD);
// If both arguments are strings, call the string add stub.
// Otherwise, do a transition.
// Registers containing left and right operands respectively.
Register left = rdx;
Register right = rax;
// Test if left operand is a string.
__ JumpIfSmi(left, &call_runtime);
__ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
__ j(above_equal, &call_runtime);
// Test if right operand is a string.
__ JumpIfSmi(right, &call_runtime);
__ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
__ j(above_equal, &call_runtime);
StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
GenerateRegisterArgsPush(masm);
__ TailCallStub(&string_add_stub);
__ bind(&call_runtime);
GenerateTypeTransition(masm);
}
void TypeRecordingBinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
Label call_runtime;
......
......@@ -152,6 +152,7 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateOddballStub(MacroAssembler* masm);
void GenerateStringStub(MacroAssembler* masm);
void GenerateBothStringStub(MacroAssembler* masm);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateHeapResultAllocation(MacroAssembler* masm, Label* alloc_failure);
......
......@@ -2512,14 +2512,7 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
void LCodeGen::DoPushArgument(LPushArgument* instr) {
LOperand* argument = instr->InputAt(0);
if (argument->IsConstantOperand()) {
EmitPushConstantOperand(argument);
} else if (argument->IsRegister()) {
__ push(ToRegister(argument));
} else {
ASSERT(!argument->IsDoubleRegister());
__ push(ToOperand(argument));
}
EmitPushTaggedOperand(argument);
}
......@@ -3113,6 +3106,14 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
}
void LCodeGen::DoStringAdd(LStringAdd* instr) {
EmitPushTaggedOperand(instr->left());
EmitPushTaggedOperand(instr->right());
StringAddStub stub(NO_STRING_CHECK_IN_STUB);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
class DeferredStringCharCodeAt: public LDeferredCode {
public:
......@@ -3772,14 +3773,7 @@ void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) {
void LCodeGen::DoTypeof(LTypeof* instr) {
LOperand* input = instr->InputAt(0);
if (input->IsConstantOperand()) {
__ Push(ToHandle(LConstantOperand::cast(input)));
} else if (input->IsRegister()) {
__ push(ToRegister(input));
} else {
ASSERT(input->IsStackSlot());
__ push(ToOperand(input));
}
EmitPushTaggedOperand(input);
CallRuntime(Runtime::kTypeof, 1, instr);
}
......@@ -3807,19 +3801,14 @@ void LCodeGen::DoTypeofIs(LTypeofIs* instr) {
}
void LCodeGen::EmitPushConstantOperand(LOperand* operand) {
ASSERT(operand->IsConstantOperand());
LConstantOperand* const_op = LConstantOperand::cast(operand);
Handle<Object> literal = chunk_->LookupLiteral(const_op);
Representation r = chunk_->LookupLiteralRepresentation(const_op);
if (r.IsInteger32()) {
ASSERT(literal->IsNumber());
__ push(Immediate(static_cast<int32_t>(literal->Number())));
} else if (r.IsDouble()) {
Abort("unsupported double immediate");
void LCodeGen::EmitPushTaggedOperand(LOperand* operand) {
ASSERT(!operand->IsDoubleRegister());
if (operand->IsConstantOperand()) {
__ Push(ToHandle(LConstantOperand::cast(operand)));
} else if (operand->IsRegister()) {
__ push(ToRegister(operand));
} else {
ASSERT(r.IsTagged());
__ Push(literal);
__ push(ToOperand(operand));
}
}
......@@ -3965,20 +3954,8 @@ void LCodeGen::DoDeoptimize(LDeoptimize* instr) {
void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
LOperand* obj = instr->object();
LOperand* key = instr->key();
// Push object.
if (obj->IsRegister()) {
__ push(ToRegister(obj));
} else {
__ push(ToOperand(obj));
}
// Push key.
if (key->IsConstantOperand()) {
EmitPushConstantOperand(key);
} else if (key->IsRegister()) {
__ push(ToRegister(key));
} else {
__ push(ToOperand(key));
}
EmitPushTaggedOperand(obj);
EmitPushTaggedOperand(key);
ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment());
LPointerMap* pointers = instr->pointer_map();
LEnvironment* env = instr->deoptimization_environment();
......
......@@ -268,8 +268,9 @@ class LCodeGen BASE_EMBEDDED {
Handle<Map> type,
Handle<String> name);
// Emits code for pushing a constant operand.
void EmitPushConstantOperand(LOperand* operand);
// Emits code for pushing either a tagged constant, a (non-double)
// register, or a stack slot operand.
void EmitPushTaggedOperand(LOperand* operand);
struct JumpTableEntry {
explicit inline JumpTableEntry(Address entry)
......
......@@ -1940,6 +1940,13 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
}
LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
LOperand* left = UseOrConstantAtStart(instr->left());
LOperand* right = UseOrConstantAtStart(instr->right());
return MarkAsCall(DefineFixed(new LStringAdd(left, right), rax), instr);
}
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
LOperand* string = UseRegister(instr->string());
LOperand* index = UseRegisterOrConstant(instr->index());
......
......@@ -152,6 +152,7 @@ class LCodeGen;
V(StoreKeyedSpecializedArrayElement) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
......@@ -1684,6 +1685,21 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> {
};
class LStringAdd: public LTemplateInstruction<1, 2, 0> {
public:
LStringAdd(LOperand* left, LOperand* right) {
inputs_[0] = left;
inputs_[1] = right;
}
DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
DECLARE_HYDROGEN_ACCESSOR(StringAdd)
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
};
class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
public:
LStringCharCodeAt(LOperand* string, LOperand* index) {
......
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