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

Support StringCharCodeAt in hydrogen/lithium.

This patch adds H- and L-variants of StringCharCodeAt and StringLength.

StringCharCodeAt is used to inline a constant function call of
String.prototype.charCodeAt and to implement the corresponding inline
runtime function. It does not yet use the recently introduced extra IC
state. (We can specialize on string encoding and avoid deopts because
of out of bounds accesses.)

StringLength needs more work because the stub version of it also
supports strings wrappers and it matters in some cases. (We have to
separate the string only case.)

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6408 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent f6e177f0
...@@ -1724,6 +1724,18 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { ...@@ -1724,6 +1724,18 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
} }
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
Abort("LStringCharCodeAt instruction not implemented on ARM");
return NULL;
}
LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
Abort("LStringLength instruction not implemented on ARM");
return NULL;
}
LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) {
return MarkAsCall(DefineFixed(new LArrayLiteral, r0), instr); return MarkAsCall(DefineFixed(new LArrayLiteral, r0), instr);
} }
......
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
#include "parser.h" #include "parser.h"
#include "scopes.h" #include "scopes.h"
#include "string-stream.h" #include "string-stream.h"
#include "stub-cache.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -560,20 +559,13 @@ void CaseClause::RecordTypeFeedback(TypeFeedbackOracle* oracle) { ...@@ -560,20 +559,13 @@ void CaseClause::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
} }
static bool CallWithoutIC(Handle<JSFunction> target, int arity) { static bool CanCallWithoutIC(Handle<JSFunction> target, int arity) {
SharedFunctionInfo* info = target->shared(); SharedFunctionInfo* info = target->shared();
if (target->NeedsArgumentsAdaption()) { // If the number of formal parameters of the target function does
// If the number of formal parameters of the target function // not match the number of arguments we're passing, we don't want to
// does not match the number of arguments we're passing, we // deal with it. Otherwise, we can call it directly.
// don't want to deal with it. return !target->NeedsArgumentsAdaption() ||
return info->formal_parameter_count() == arity; info->formal_parameter_count() == arity;
} else {
// If the target doesn't need arguments adaption, we can call
// it directly, but we avoid to do so if it has a custom call
// generator, because that is likely to generate better code.
return !info->HasBuiltinFunctionId() ||
!CallStubCompiler::HasCustomCallGenerator(info->builtin_function_id());
}
} }
...@@ -589,7 +581,7 @@ bool Call::ComputeTarget(Handle<Map> type, Handle<String> name) { ...@@ -589,7 +581,7 @@ bool Call::ComputeTarget(Handle<Map> type, Handle<String> name) {
type = Handle<Map>(holder()->map()); type = Handle<Map>(holder()->map());
} else if (lookup.IsProperty() && lookup.type() == CONSTANT_FUNCTION) { } else if (lookup.IsProperty() && lookup.type() == CONSTANT_FUNCTION) {
target_ = Handle<JSFunction>(lookup.GetConstantFunctionFromMap(*type)); target_ = Handle<JSFunction>(lookup.GetConstantFunctionFromMap(*type));
return CallWithoutIC(target_, arguments()->length()); return CanCallWithoutIC(target_, arguments()->length());
} else { } else {
return false; return false;
} }
...@@ -609,8 +601,8 @@ bool Call::ComputeGlobalTarget(Handle<GlobalObject> global, ...@@ -609,8 +601,8 @@ bool Call::ComputeGlobalTarget(Handle<GlobalObject> global,
Handle<JSFunction> candidate(JSFunction::cast(cell_->value())); Handle<JSFunction> candidate(JSFunction::cast(cell_->value()));
// If the function is in new space we assume it's more likely to // If the function is in new space we assume it's more likely to
// change and thus prefer the general IC code. // change and thus prefer the general IC code.
if (!Heap::InNewSpace(*candidate) if (!Heap::InNewSpace(*candidate) &&
&& CallWithoutIC(candidate, arguments()->length())) { CanCallWithoutIC(candidate, arguments()->length())) {
target_ = candidate; target_ = candidate;
return true; return true;
} }
......
...@@ -81,6 +81,7 @@ class LChunkBuilder; ...@@ -81,6 +81,7 @@ class LChunkBuilder;
// HStoreNamed // HStoreNamed
// HStoreNamedField // HStoreNamedField
// HStoreNamedGeneric // HStoreNamedGeneric
// HStringCharCodeAt
// HBlockEntry // HBlockEntry
// HCall // HCall
// HCallConstantFunction // HCallConstantFunction
...@@ -137,6 +138,7 @@ class LChunkBuilder; ...@@ -137,6 +138,7 @@ class LChunkBuilder;
// HLoadNamedGeneric // HLoadNamedGeneric
// HLoadFunctionPrototype // HLoadFunctionPrototype
// HPushArgument // HPushArgument
// HStringLength
// HTypeof // HTypeof
// HUnaryMathOperation // HUnaryMathOperation
// HUnaryPredicate // HUnaryPredicate
...@@ -248,6 +250,8 @@ class LChunkBuilder; ...@@ -248,6 +250,8 @@ class LChunkBuilder;
V(StoreKeyedGeneric) \ V(StoreKeyedGeneric) \
V(StoreNamedField) \ V(StoreNamedField) \
V(StoreNamedGeneric) \ V(StoreNamedGeneric) \
V(StringCharCodeAt) \
V(StringLength) \
V(Sub) \ V(Sub) \
V(Throw) \ V(Throw) \
V(Typeof) \ V(Typeof) \
...@@ -1579,6 +1583,12 @@ class HCheckInstanceType: public HUnaryOperation { ...@@ -1579,6 +1583,12 @@ class HCheckInstanceType: public HUnaryOperation {
ASSERT(first <= last); ASSERT(first <= last);
set_representation(Representation::Tagged()); set_representation(Representation::Tagged());
SetFlag(kUseGVN); SetFlag(kUseGVN);
if ((FIRST_STRING_TYPE < first && last <= LAST_STRING_TYPE) ||
(FIRST_STRING_TYPE <= first && last < LAST_STRING_TYPE)) {
// A particular string instance type can change because of GC or
// externalization, but the value still remains a string.
SetFlag(kDependsOnMaps);
}
} }
virtual bool IsCheckInstruction() const { return true; } virtual bool IsCheckInstruction() const { return true; }
...@@ -2937,6 +2947,61 @@ class HStoreKeyedGeneric: public HStoreKeyed { ...@@ -2937,6 +2947,61 @@ class HStoreKeyedGeneric: public HStoreKeyed {
}; };
class HStringCharCodeAt: public HBinaryOperation {
public:
HStringCharCodeAt(HValue* string, HValue* index)
: HBinaryOperation(string, index) {
set_representation(Representation::Integer32());
SetFlag(kUseGVN);
}
virtual Representation RequiredInputRepresentation(int index) const {
// The index is supposed to be Integer32.
return (index == 1) ? Representation::Integer32()
: Representation::Tagged();
}
virtual bool DataEquals(HValue* other) const { return true; }
HValue* string() const { return OperandAt(0); }
HValue* index() const { return OperandAt(1); }
DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string_char_code_at")
protected:
virtual Range* InferRange() {
return new Range(0, String::kMaxUC16CharCode);
}
};
class HStringLength: public HUnaryOperation {
public:
explicit HStringLength(HValue* string) : HUnaryOperation(string) {
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
}
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::Tagged();
}
virtual HType CalculateInferredType() const {
STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue);
return HType::Smi();
}
virtual bool DataEquals(HValue* other) const { return true; }
DECLARE_CONCRETE_INSTRUCTION(StringLength, "string_length")
protected:
virtual Range* InferRange() {
return new Range(0, String::kMaxLength);
}
};
class HMaterializedLiteral: public HInstruction { class HMaterializedLiteral: public HInstruction {
public: public:
HMaterializedLiteral(int index, int depth) HMaterializedLiteral(int index, int depth)
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "lithium-allocator.h" #include "lithium-allocator.h"
#include "parser.h" #include "parser.h"
#include "scopes.h" #include "scopes.h"
#include "stub-cache.h"
#if V8_TARGET_ARCH_IA32 #if V8_TARGET_ARCH_IA32
#include "ia32/lithium-codegen-ia32.h" #include "ia32/lithium-codegen-ia32.h"
...@@ -4146,12 +4147,29 @@ void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) { ...@@ -4146,12 +4147,29 @@ void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) {
} }
bool HGraphBuilder::TryMathFunctionInline(Call* expr) { bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr,
HValue* receiver,
Handle<Map> receiver_map,
CheckType check_type) {
ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null());
// Try to inline calls like Math.* as operations in the calling function. // Try to inline calls like Math.* as operations in the calling function.
if (!expr->target()->shared()->IsBuiltinMathFunction()) return false; if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
BuiltinFunctionId id = expr->target()->shared()->builtin_function_id(); BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
int argument_count = expr->arguments()->length() + 1; // Plus receiver. int argument_count = expr->arguments()->length() + 1; // Plus receiver.
switch (id) { switch (id) {
case kStringCharCodeAt:
if (argument_count == 2 && check_type == STRING_CHECK) {
HValue* index = Pop();
HValue* string = Pop();
ASSERT(!expr->holder().is_null());
AddInstruction(new HCheckPrototypeMaps(
oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
expr->holder()));
HStringCharCodeAt* result = BuildStringCharCodeAt(string, index);
ast_context()->ReturnInstruction(result, expr->id());
return true;
}
break;
case kMathRound: case kMathRound:
case kMathFloor: case kMathFloor:
case kMathAbs: case kMathAbs:
...@@ -4159,7 +4177,8 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { ...@@ -4159,7 +4177,8 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) {
case kMathLog: case kMathLog:
case kMathSin: case kMathSin:
case kMathCos: case kMathCos:
if (argument_count == 2) { if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
AddCheckConstantFunction(expr, receiver, receiver_map, true);
HValue* argument = Pop(); HValue* argument = Pop();
Drop(1); // Receiver. Drop(1); // Receiver.
HUnaryMathOperation* op = new HUnaryMathOperation(argument, id); HUnaryMathOperation* op = new HUnaryMathOperation(argument, id);
...@@ -4169,7 +4188,8 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { ...@@ -4169,7 +4188,8 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) {
} }
break; break;
case kMathPow: case kMathPow:
if (argument_count == 3) { if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
AddCheckConstantFunction(expr, receiver, receiver_map, true);
HValue* right = Pop(); HValue* right = Pop();
HValue* left = Pop(); HValue* left = Pop();
Pop(); // Pop receiver. Pop(); // Pop receiver.
...@@ -4179,8 +4199,6 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { ...@@ -4179,8 +4199,6 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) {
double exponent = HConstant::cast(right)->DoubleValue(); double exponent = HConstant::cast(right)->DoubleValue();
if (exponent == 0.5) { if (exponent == 0.5) {
result = new HUnaryMathOperation(left, kMathPowHalf); result = new HUnaryMathOperation(left, kMathPowHalf);
ast_context()->ReturnInstruction(result, expr->id());
return true;
} else if (exponent == -0.5) { } else if (exponent == -0.5) {
HConstant* double_one = HConstant* double_one =
new HConstant(Handle<Object>(Smi::FromInt(1)), new HConstant(Handle<Object>(Smi::FromInt(1)),
...@@ -4193,22 +4211,18 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { ...@@ -4193,22 +4211,18 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) {
// an environment simulation here. // an environment simulation here.
ASSERT(!square_root->HasSideEffects()); ASSERT(!square_root->HasSideEffects());
result = new HDiv(double_one, square_root); result = new HDiv(double_one, square_root);
ast_context()->ReturnInstruction(result, expr->id());
return true;
} else if (exponent == 2.0) { } else if (exponent == 2.0) {
result = new HMul(left, left); result = new HMul(left, left);
ast_context()->ReturnInstruction(result, expr->id());
return true;
} }
} else if (right->IsConstant() && } else if (right->IsConstant() &&
HConstant::cast(right)->HasInteger32Value() && HConstant::cast(right)->HasInteger32Value() &&
HConstant::cast(right)->Integer32Value() == 2) { HConstant::cast(right)->Integer32Value() == 2) {
result = new HMul(left, left); result = new HMul(left, left);
ast_context()->ReturnInstruction(result, expr->id());
return true;
} }
result = new HPower(left, right); if (result == NULL) {
result = new HPower(left, right);
}
ast_context()->ReturnInstruction(result, expr->id()); ast_context()->ReturnInstruction(result, expr->id());
return true; return true;
} }
...@@ -4263,6 +4277,13 @@ bool HGraphBuilder::TryCallApply(Call* expr) { ...@@ -4263,6 +4277,13 @@ bool HGraphBuilder::TryCallApply(Call* expr) {
} }
static bool HasCustomCallGenerator(Handle<JSFunction> function) {
SharedFunctionInfo* info = function->shared();
return info->HasBuiltinFunctionId() &&
CallStubCompiler::HasCustomCallGenerator(info->builtin_function_id());
}
void HGraphBuilder::VisitCall(Call* expr) { void HGraphBuilder::VisitCall(Call* expr) {
Expression* callee = expr->expression(); Expression* callee = expr->expression();
int argument_count = expr->arguments()->length() + 1; // Plus receiver. int argument_count = expr->arguments()->length() + 1; // Plus receiver.
...@@ -4309,30 +4330,44 @@ void HGraphBuilder::VisitCall(Call* expr) { ...@@ -4309,30 +4330,44 @@ void HGraphBuilder::VisitCall(Call* expr) {
expr->RecordTypeFeedback(oracle()); expr->RecordTypeFeedback(oracle());
ZoneMapList* types = expr->GetReceiverTypes(); ZoneMapList* types = expr->GetReceiverTypes();
if (expr->IsMonomorphic() && expr->check_type() == RECEIVER_MAP_CHECK) { if (expr->IsMonomorphic()) {
AddCheckConstantFunction(expr, receiver, types->first(), true); Handle<Map> receiver_map =
(types == NULL) ? Handle<Map>::null() : types->first();
if (TryMathFunctionInline(expr)) { if (TryInlineBuiltinFunction(expr,
return; receiver,
} else if (TryInline(expr)) { receiver_map,
if (subgraph()->HasExit()) { expr->check_type())) {
HValue* return_value = Pop();
// If we inlined a function in a test context then we need to emit
// a simulate here to shadow the ones at the end of the
// predecessor blocks. Those environments contain the return
// value on top and do not correspond to any actual state of the
// unoptimized code.
if (ast_context()->IsEffect()) AddSimulate(expr->id());
ast_context()->ReturnValue(return_value);
}
return; return;
} else {
// Check for bailout, as the TryInline call in the if condition above
// might return false due to bailout during hydrogen processing.
CHECK_BAILOUT;
call = new HCallConstantFunction(expr->target(), argument_count);
} }
if (HasCustomCallGenerator(expr->target()) ||
expr->check_type() != RECEIVER_MAP_CHECK) {
// When the target has a custom call IC generator, use the IC,
// because it is likely to generate better code. Also use the
// IC when a primitive receiver check is required.
call = new HCallNamed(name, argument_count);
} else {
AddCheckConstantFunction(expr, receiver, receiver_map, true);
if (TryInline(expr)) {
if (subgraph()->HasExit()) {
HValue* return_value = Pop();
// If we inlined a function in a test context then we need to emit
// a simulate here to shadow the ones at the end of the
// predecessor blocks. Those environments contain the return
// value on top and do not correspond to any actual state of the
// unoptimized code.
if (ast_context()->IsEffect()) AddSimulate(expr->id());
ast_context()->ReturnValue(return_value);
}
return;
} else {
// Check for bailout, as the TryInline call in the if condition above
// might return false due to bailout during hydrogen processing.
CHECK_BAILOUT;
call = new HCallConstantFunction(expr->target(), argument_count);
}
}
} else if (types != NULL && types->length() > 1) { } else if (types != NULL && types->length() > 1) {
ASSERT(expr->check_type() == RECEIVER_MAP_CHECK); ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
HandlePolymorphicCallNamed(expr, receiver, types, name); HandlePolymorphicCallNamed(expr, receiver, types, name);
...@@ -4720,6 +4755,18 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { ...@@ -4720,6 +4755,18 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
} }
HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string,
HValue* index) {
AddInstruction(new HCheckNonSmi(string));
AddInstruction(new HCheckInstanceType(
string, FIRST_STRING_TYPE, LAST_STRING_TYPE));
HStringLength* length = new HStringLength(string);
AddInstruction(length);
AddInstruction(new HBoundsCheck(index, length));
return new HStringCharCodeAt(string, index);
}
HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
HValue* left, HValue* left,
HValue* right) { HValue* right) {
...@@ -5129,7 +5176,11 @@ void HGraphBuilder::GenerateSetValueOf(int argument_count, int ast_id) { ...@@ -5129,7 +5176,11 @@ void HGraphBuilder::GenerateSetValueOf(int argument_count, int ast_id) {
// Fast support for charCodeAt(n). // Fast support for charCodeAt(n).
void HGraphBuilder::GenerateStringCharCodeAt(int argument_count, int ast_id) { void HGraphBuilder::GenerateStringCharCodeAt(int argument_count, int ast_id) {
BAILOUT("inlined runtime function: StringCharCodeAt"); ASSERT(argument_count == 2);
HValue* index = Pop();
HValue* string = Pop();
HStringCharCodeAt* result = BuildStringCharCodeAt(string, index);
ast_context()->ReturnInstruction(result, ast_id);
} }
......
...@@ -748,7 +748,10 @@ class HGraphBuilder: public AstVisitor { ...@@ -748,7 +748,10 @@ class HGraphBuilder: public AstVisitor {
bool TryArgumentsAccess(Property* expr); bool TryArgumentsAccess(Property* expr);
bool TryCallApply(Call* expr); bool TryCallApply(Call* expr);
bool TryInline(Call* expr); bool TryInline(Call* expr);
bool TryMathFunctionInline(Call* expr); bool TryInlineBuiltinFunction(Call* expr,
HValue* receiver,
Handle<Map> receiver_map,
CheckType check_type);
void TraceInline(Handle<JSFunction> target, bool result); void TraceInline(Handle<JSFunction> target, bool result);
void HandleGlobalVariableAssignment(Variable* var, void HandleGlobalVariableAssignment(Variable* var,
...@@ -772,6 +775,8 @@ class HGraphBuilder: public AstVisitor { ...@@ -772,6 +775,8 @@ class HGraphBuilder: public AstVisitor {
ZoneMapList* types, ZoneMapList* types,
Handle<String> name); Handle<String> name);
HStringCharCodeAt* BuildStringCharCodeAt(HValue* string,
HValue* index);
HInstruction* BuildBinaryOperation(BinaryOperation* expr, HInstruction* BuildBinaryOperation(BinaryOperation* expr,
HValue* left, HValue* left,
HValue* right); HValue* right);
......
...@@ -2650,6 +2650,135 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { ...@@ -2650,6 +2650,135 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
} }
void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
class DeferredStringCharCodeAt: public LDeferredCode {
public:
DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr)
: LDeferredCode(codegen), instr_(instr) { }
virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); }
private:
LStringCharCodeAt* instr_;
};
DeferredStringCharCodeAt* deferred = new DeferredStringCharCodeAt(this,
instr);
Register string = ToRegister(instr->string());
Register index = no_reg;
int const_index = -1;
if (instr->index()->IsConstantOperand()) {
const_index = ToInteger32(LConstantOperand::cast(instr->index()));
} else {
index = ToRegister(instr->index());
}
Register result = ToRegister(instr->result());
NearLabel flat_string, ascii_string, done;
// Fetch the instance type of the receiver into result register.
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
// We need special handling for non-flat strings.
STATIC_ASSERT(kSeqStringTag == 0);
__ test(result, Immediate(kStringRepresentationMask));
__ j(zero, &flat_string);
// Handle non-flat strings.
__ test(result, Immediate(kIsConsStringMask));
__ j(zero, deferred->entry());
// ConsString.
// Check whether the right hand side is the empty string (i.e. if
// this is really a flat string in a cons string). If that is not
// the case we would rather go to the runtime system now to flatten
// the string.
__ cmp(FieldOperand(string, ConsString::kSecondOffset),
Immediate(Factory::empty_string()));
__ j(not_equal, deferred->entry());
// Get the first of the two strings and load its instance type.
__ mov(string, FieldOperand(string, ConsString::kFirstOffset));
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
// If the first cons component is also non-flat, then go to runtime.
STATIC_ASSERT(kSeqStringTag == 0);
__ test(result, Immediate(kStringRepresentationMask));
__ j(not_zero, deferred->entry());
// Check for 1-byte or 2-byte string.
__ bind(&flat_string);
STATIC_ASSERT(kAsciiStringTag != 0);
__ test(result, Immediate(kStringEncodingMask));
__ j(not_zero, &ascii_string);
// 2-byte string.
// Load the 2-byte character code into the result register.
STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
if (index.is_valid()) {
__ movzx_w(result, FieldOperand(string,
index, times_2,
SeqTwoByteString::kHeaderSize));
} else {
__ movzx_w(result, FieldOperand(
string, SeqTwoByteString::kHeaderSize + 2 * const_index));
}
__ jmp(&done);
// ASCII string.
// Load the byte into the result register.
__ bind(&ascii_string);
if (index.is_valid()) {
__ movzx_b(result, FieldOperand(string,
index, times_1,
SeqAsciiString::kHeaderSize));
} else {
__ movzx_b(result, FieldOperand(string,
SeqAsciiString::kHeaderSize + const_index));
}
__ bind(&done);
__ bind(deferred->exit());
}
void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) {
Register string = ToRegister(instr->string());
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();
__ push(string);
// Push the index as a smi.
if (instr->index()->IsConstantOperand()) {
int const_index = ToInteger32(LConstantOperand::cast(instr->index()));
__ push(Immediate(Smi::FromInt(const_index)));
} else {
Register index = ToRegister(instr->index());
__ SmiTag(index);
__ push(index);
}
__ CallRuntimeSaveDoubles(Runtime::kStringCharCodeAt);
RecordSafepointWithRegisters(
instr->pointer_map(), 2, Safepoint::kNoDeoptimizationIndex);
if (FLAG_debug_code) {
__ AbortIfNotSmi(eax);
}
__ SmiUntag(eax);
__ mov(Operand(esp, EspIndexForPushAll(result) * kPointerSize), eax);
__ PopSafepointRegisters();
}
void LCodeGen::DoStringLength(LStringLength* instr) {
Register string = ToRegister(instr->string());
Register result = ToRegister(instr->result());
__ mov(result, FieldOperand(string, String::kLengthOffset));
}
void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) { void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
LOperand* input = instr->InputAt(0); LOperand* input = instr->InputAt(0);
ASSERT(input->IsRegister() || input->IsStackSlot()); ASSERT(input->IsRegister() || input->IsStackSlot());
...@@ -3076,13 +3205,19 @@ void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) { ...@@ -3076,13 +3205,19 @@ void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
InstanceType last = instr->hydrogen()->last(); InstanceType last = instr->hydrogen()->last();
__ mov(temp, FieldOperand(input, HeapObject::kMapOffset)); __ mov(temp, FieldOperand(input, HeapObject::kMapOffset));
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
static_cast<int8_t>(first));
// If there is only one type in the interval check for equality. // If there is only one type in the interval check for equality.
if (first == last) { if (first == last) {
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
static_cast<int8_t>(first));
DeoptimizeIf(not_equal, instr->environment()); DeoptimizeIf(not_equal, instr->environment());
} else { } else if (first == FIRST_STRING_TYPE && last == LAST_STRING_TYPE) {
// String has a dedicated bit in instance type.
__ test_b(FieldOperand(temp, Map::kInstanceTypeOffset), kIsNotStringMask);
DeoptimizeIf(not_zero, instr->environment());
} else {
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
static_cast<int8_t>(first));
DeoptimizeIf(below, instr->environment()); DeoptimizeIf(below, instr->environment());
// Omit check for the last type. // Omit check for the last type.
if (last != LAST_TYPE) { if (last != LAST_TYPE) {
......
...@@ -92,6 +92,7 @@ class LCodeGen BASE_EMBEDDED { ...@@ -92,6 +92,7 @@ class LCodeGen BASE_EMBEDDED {
void DoDeferredTaggedToI(LTaggedToI* instr); void DoDeferredTaggedToI(LTaggedToI* instr);
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr); void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
void DoDeferredStackCheck(LGoto* instr); void DoDeferredStackCheck(LGoto* instr);
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
Label* map_check); Label* map_check);
......
...@@ -1741,6 +1741,20 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { ...@@ -1741,6 +1741,20 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
} }
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
LOperand* string = UseRegister(instr->string());
LOperand* index = UseRegisterOrConstant(instr->index());
return AssignEnvironment(AssignPointerMap(DefineAsRegister(
new LStringCharCodeAt(string, index))));
}
LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
LOperand* string = UseRegisterAtStart(instr->value());
return DefineAsRegister(new LStringLength(string));
}
LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) {
return MarkAsCall(DefineFixed(new LArrayLiteral, eax), instr); return MarkAsCall(DefineFixed(new LArrayLiteral, eax), instr);
} }
......
...@@ -114,6 +114,7 @@ class LCodeGen; ...@@ -114,6 +114,7 @@ class LCodeGen;
// LStoreNamed // LStoreNamed
// LStoreNamedField // LStoreNamedField
// LStoreNamedGeneric // LStoreNamedGeneric
// LStringCharCodeAt
// LBitNotI // LBitNotI
// LCallNew // LCallNew
// LCheckFunction // LCheckFunction
...@@ -141,6 +142,7 @@ class LCodeGen; ...@@ -141,6 +142,7 @@ class LCodeGen;
// LReturn // LReturn
// LSmiTag // LSmiTag
// LStoreGlobal // LStoreGlobal
// LStringLength
// LTaggedToI // LTaggedToI
// LThrow // LThrow
// LTypeof // LTypeof
...@@ -253,6 +255,8 @@ class LCodeGen; ...@@ -253,6 +255,8 @@ class LCodeGen;
V(StoreKeyedGeneric) \ V(StoreKeyedGeneric) \
V(StoreNamedField) \ V(StoreNamedField) \
V(StoreNamedGeneric) \ V(StoreNamedGeneric) \
V(StringCharCodeAt) \
V(StringLength) \
V(SubI) \ V(SubI) \
V(TaggedToI) \ V(TaggedToI) \
V(Throw) \ V(Throw) \
...@@ -1590,6 +1594,34 @@ class LStoreKeyedGeneric: public LStoreKeyed { ...@@ -1590,6 +1594,34 @@ class LStoreKeyedGeneric: public LStoreKeyed {
}; };
class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
public:
LStringCharCodeAt(LOperand* string, LOperand* index) {
inputs_[0] = string;
inputs_[1] = index;
}
DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at")
DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt)
LOperand* string() { return inputs_[0]; }
LOperand* index() { return inputs_[1]; }
};
class LStringLength: public LTemplateInstruction<1, 1, 0> {
public:
explicit LStringLength(LOperand* string) {
inputs_[0] = string;
}
DECLARE_CONCRETE_INSTRUCTION(StringLength, "string-length")
DECLARE_HYDROGEN_ACCESSOR(StringLength)
LOperand* string() { return inputs_[0]; }
};
class LCheckFunction: public LTemplateInstruction<0, 1> { class LCheckFunction: public LTemplateInstruction<0, 1> {
public: public:
explicit LCheckFunction(LOperand* value) { explicit LCheckFunction(LOperand* value) {
......
...@@ -3079,12 +3079,6 @@ bool SharedFunctionInfo::HasBuiltinFunctionId() { ...@@ -3079,12 +3079,6 @@ bool SharedFunctionInfo::HasBuiltinFunctionId() {
} }
bool SharedFunctionInfo::IsBuiltinMathFunction() {
return HasBuiltinFunctionId() &&
builtin_function_id() >= kFirstMathFunctionId;
}
BuiltinFunctionId SharedFunctionInfo::builtin_function_id() { BuiltinFunctionId SharedFunctionInfo::builtin_function_id() {
ASSERT(HasBuiltinFunctionId()); ASSERT(HasBuiltinFunctionId());
return static_cast<BuiltinFunctionId>(Smi::cast(function_data())->value()); return static_cast<BuiltinFunctionId>(Smi::cast(function_data())->value());
......
...@@ -395,6 +395,7 @@ static const char* TypeToString(InstanceType type) { ...@@ -395,6 +395,7 @@ static const char* TypeToString(InstanceType type) {
case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT"; case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT";
case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY"; case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY";
case PROXY_TYPE: return "PROXY"; case PROXY_TYPE: return "PROXY";
case LAST_STRING_TYPE: return "LAST_STRING_TYPE";
#define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME; #define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME;
STRUCT_LIST(MAKE_STRUCT_CASE) STRUCT_LIST(MAKE_STRUCT_CASE)
#undef MAKE_STRUCT_CASE #undef MAKE_STRUCT_CASE
......
...@@ -455,6 +455,7 @@ const uint32_t kShortcutTypeTag = kConsStringTag; ...@@ -455,6 +455,7 @@ const uint32_t kShortcutTypeTag = kConsStringTag;
enum InstanceType { enum InstanceType {
// String types. // String types.
// FIRST_STRING_TYPE
SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kSeqStringTag, SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kSeqStringTag,
ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kSeqStringTag, ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kSeqStringTag,
CONS_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kConsStringTag, CONS_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kConsStringTag,
...@@ -471,6 +472,7 @@ enum InstanceType { ...@@ -471,6 +472,7 @@ enum InstanceType {
EXTERNAL_STRING_TYPE = kTwoByteStringTag | kExternalStringTag, EXTERNAL_STRING_TYPE = kTwoByteStringTag | kExternalStringTag,
EXTERNAL_STRING_WITH_ASCII_DATA_TYPE = EXTERNAL_STRING_WITH_ASCII_DATA_TYPE =
kTwoByteStringTag | kExternalStringTag | kAsciiDataHintTag, kTwoByteStringTag | kExternalStringTag | kAsciiDataHintTag,
// LAST_STRING_TYPE
EXTERNAL_ASCII_STRING_TYPE = kAsciiStringTag | kExternalStringTag, EXTERNAL_ASCII_STRING_TYPE = kAsciiStringTag | kExternalStringTag,
PRIVATE_EXTERNAL_ASCII_STRING_TYPE = EXTERNAL_ASCII_STRING_TYPE, PRIVATE_EXTERNAL_ASCII_STRING_TYPE = EXTERNAL_ASCII_STRING_TYPE,
...@@ -533,6 +535,8 @@ enum InstanceType { ...@@ -533,6 +535,8 @@ enum InstanceType {
LAST_TYPE = JS_FUNCTION_TYPE, LAST_TYPE = JS_FUNCTION_TYPE,
INVALID_TYPE = FIRST_TYPE - 1, INVALID_TYPE = FIRST_TYPE - 1,
FIRST_NONSTRING_TYPE = MAP_TYPE, FIRST_NONSTRING_TYPE = MAP_TYPE,
FIRST_STRING_TYPE = FIRST_TYPE,
LAST_STRING_TYPE = FIRST_NONSTRING_TYPE - 1,
// Boundaries for testing for an external array. // Boundaries for testing for an external array.
FIRST_EXTERNAL_ARRAY_TYPE = EXTERNAL_BYTE_ARRAY_TYPE, FIRST_EXTERNAL_ARRAY_TYPE = EXTERNAL_BYTE_ARRAY_TYPE,
LAST_EXTERNAL_ARRAY_TYPE = EXTERNAL_FLOAT_ARRAY_TYPE, LAST_EXTERNAL_ARRAY_TYPE = EXTERNAL_FLOAT_ARRAY_TYPE,
...@@ -4070,7 +4074,6 @@ class SharedFunctionInfo: public HeapObject { ...@@ -4070,7 +4074,6 @@ class SharedFunctionInfo: public HeapObject {
inline bool IsApiFunction(); inline bool IsApiFunction();
inline FunctionTemplateInfo* get_api_func_data(); inline FunctionTemplateInfo* get_api_func_data();
inline bool HasBuiltinFunctionId(); inline bool HasBuiltinFunctionId();
inline bool IsBuiltinMathFunction();
inline BuiltinFunctionId builtin_function_id(); inline BuiltinFunctionId builtin_function_id();
// [script info]: Script from which the function originates. // [script info]: Script from which the function originates.
......
...@@ -1465,6 +1465,18 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { ...@@ -1465,6 +1465,18 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
} }
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
Abort("Unimplemented: %s", "DoStringCharCodeAt");
return NULL;
}
LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
Abort("Unimplemented: %s", "DoStringLength");
return NULL;
}
LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) {
Abort("Unimplemented: %s", "DoArrayLiteral"); Abort("Unimplemented: %s", "DoArrayLiteral");
return NULL; return NULL;
......
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