Commit 3e55df5a authored by lrn@chromium.org's avatar lrn@chromium.org

Tracks static type of strings in frame elements and results.

Uses static string type to optimize string additions.


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1675 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 339e2e52
......@@ -163,10 +163,10 @@ class Expression: public Node {
virtual void MarkAsStatement() { /* do nothing */ }
// Static type information for this expression.
StaticType* type() { return &type_; }
SmiAnalysis* type() { return &type_; }
private:
StaticType type_;
SmiAnalysis type_;
};
......
......@@ -124,6 +124,8 @@ namespace v8 { namespace internal {
V(TO_OBJECT, 0) \
V(TO_NUMBER, 0) \
V(TO_STRING, 0) \
V(STRING_ADD_LEFT, 1) \
V(STRING_ADD_RIGHT, 1) \
V(APPLY_PREPARE, 1) \
V(APPLY_OVERFLOW, 1)
......
......@@ -807,7 +807,7 @@ void DeferredInlineBinaryOperation::Generate() {
void CodeGenerator::GenericBinaryOperation(Token::Value op,
StaticType* type,
SmiAnalysis* type,
OverwriteMode overwrite_mode) {
Comment cmnt(masm_, "[ BinaryOperation");
Comment cmnt_token(masm_, Token::String(op));
......@@ -845,6 +845,34 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op,
Result right = frame_->Pop();
Result left = frame_->Pop();
if (op == Token::ADD) {
bool left_is_string = left.static_type().is_jsstring();
bool right_is_string = right.static_type().is_jsstring();
if (left_is_string || right_is_string) {
frame_->Push(&left);
frame_->Push(&right);
Result answer(this);
if (left_is_string) {
if (right_is_string) {
// TODO(lrn): if (left.is_constant() && right.is_constant())
// -- do a compile time cons, if allocation during codegen is allowed.
answer = frame_->CallRuntime(Runtime::kStringAdd, 2);
} else {
answer =
frame_->InvokeBuiltin(Builtins::STRING_ADD_LEFT, CALL_FUNCTION, 2);
}
} else if (right_is_string) {
answer =
frame_->InvokeBuiltin(Builtins::STRING_ADD_RIGHT, CALL_FUNCTION, 2);
}
answer.set_static_type(StaticType::jsstring());
frame_->Push(&answer);
return;
}
// Neither operand is known to be a string.
}
bool left_is_smi = left.is_constant() && left.handle()->IsSmi();
bool left_is_non_smi = left.is_constant() && !left.handle()->IsSmi();
bool right_is_smi = right.is_constant() && right.handle()->IsSmi();
......@@ -1189,7 +1217,7 @@ void DeferredInlineSmiSubReversed::Generate() {
void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
Result* operand,
Handle<Object> value,
StaticType* type,
SmiAnalysis* type,
bool reversed,
OverwriteMode overwrite_mode) {
// NOTE: This is an attempt to inline (a bit) more of the code for
......@@ -3499,8 +3527,8 @@ void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
void CodeGenerator::VisitLiteral(Literal* node) {
Comment cmnt(masm_, "[ Literal");
frame_->Push(node->handle());
}
frame_->Push(node->handle());
}
void CodeGenerator::LoadUnsafeSmi(Register target, Handle<Object> value) {
......
......@@ -431,8 +431,9 @@ class CodeGenerator: public AstVisitor {
// control destination.
void ToBoolean(ControlDestination* destination);
void GenericBinaryOperation(Token::Value op,
StaticType* type,
void GenericBinaryOperation(
Token::Value op,
SmiAnalysis* type,
const OverwriteMode overwrite_mode = NO_OVERWRITE);
// If possible, combine two constant smi values using op to produce
......@@ -445,7 +446,7 @@ class CodeGenerator: public AstVisitor {
void ConstantSmiBinaryOperation(Token::Value op,
Result* operand,
Handle<Object> constant_operand,
StaticType* type,
SmiAnalysis* type,
bool reversed,
OverwriteMode overwrite_mode);
......
......@@ -72,7 +72,7 @@ void JumpTarget::Initialize(CodeGenerator* cgen, Directionality direction) {
void JumpTarget::Unuse() {
// We should not deallocate jump targets that have unresolved jumps
// to them. In the event of a compile-time stack overflow or an
// unitialized jump target, we don't care.
// uninitialized jump target, we don't care.
ASSERT(!is_linked() || cgen_ == NULL || cgen_->HasStackOverflow());
for (int i = 0; i < reaching_frames_.length(); i++) {
delete reaching_frames_[i];
......@@ -103,6 +103,7 @@ FrameElement* JumpTarget::Combine(FrameElement* left, FrameElement* right) {
// If they have the same value, the result is the same. If either
// is unsynced, the result is.
if (left->is_memory() && right->is_memory()) return left;
if (left->is_register() && right->is_register() &&
......@@ -165,8 +166,7 @@ void JumpTarget::ComputeEntryFrame(int mergable_elements) {
for (int i = 0; i < length; i++) {
FrameElement element = initial_frame->elements_[i];
// We do not allow copies or constants in bidirectional frames.
if (direction_ == BIDIRECTIONAL &&
i > high_water_mark &&
if (direction_ == BIDIRECTIONAL && i > high_water_mark &&
(element.is_constant() || element.is_copy())) {
elements.Add(NULL);
} else {
......@@ -271,12 +271,25 @@ void JumpTarget::ComputeEntryFrame(int mergable_elements) {
// the backing store of copies is always lower in the frame.
// Set the register locations to their index in the frame.
for (int i = 0; i < length; i++) {
FrameElement current = entry_frame_->elements_[i];
entry_frame_->elements_[i].clear_copied();
if (current.is_copy()) {
entry_frame_->elements_[current.index()].set_copied();
} else if (current.is_register()) {
entry_frame_->register_locations_[current.reg().code()] = i;
FrameElement* current = &entry_frame_->elements_[i];
current->clear_copied();
if (current->is_copy()) {
entry_frame_->elements_[current->index()].set_copied();
} else if (current->is_register()) {
entry_frame_->register_locations_[current->reg().code()] = i;
}
if (direction_ == BIDIRECTIONAL && i >= high_water_mark) {
current->set_static_type(StaticType::unknown());
} else {
StaticType merged_type = reaching_frames_[0]->elements_[i].static_type();
for (int j = 1, n = reaching_frames_.length();
!merged_type.is_unknown() && j < n;
j++) {
merged_type =
merged_type.merge(reaching_frames_[j]->elements_[i].static_type());
}
current->set_static_type(merged_type);
}
}
......
......@@ -602,11 +602,11 @@ class IndentedScope BASE_EMBEDDED {
ast_printer_->inc_indent();
}
explicit IndentedScope(const char* txt, StaticType* type = NULL) {
explicit IndentedScope(const char* txt, SmiAnalysis* type = NULL) {
ast_printer_->PrintIndented(txt);
if ((type != NULL) && (type->IsKnown())) {
ast_printer_->Print(" (type = ");
ast_printer_->Print(StaticType::Type2String(type));
ast_printer_->Print(SmiAnalysis::Type2String(type));
ast_printer_->Print(")");
}
ast_printer_->Print("\n");
......@@ -665,7 +665,7 @@ void AstPrinter::PrintLiteralIndented(const char* info,
void AstPrinter::PrintLiteralWithModeIndented(const char* info,
Variable* var,
Handle<Object> value,
StaticType* type) {
SmiAnalysis* type) {
if (var == NULL) {
PrintLiteralIndented(info, value, true);
} else {
......@@ -673,7 +673,7 @@ void AstPrinter::PrintLiteralWithModeIndented(const char* info,
if (type->IsKnown()) {
OS::SNPrintF(buf, "%s (mode = %s, type = %s)", info,
Variable::Mode2String(var->mode()),
StaticType::Type2String(type));
SmiAnalysis::Type2String(type));
} else {
OS::SNPrintF(buf, "%s (mode = %s)", info,
Variable::Mode2String(var->mode()));
......@@ -1066,7 +1066,7 @@ void AstPrinter::VisitCountOperation(CountOperation* node) {
OS::SNPrintF(buf, "%s %s (type = %s)",
(node->is_prefix() ? "PRE" : "POST"),
Token::Name(node->op()),
StaticType::Type2String(node->type()));
SmiAnalysis::Type2String(node->type()));
} else {
OS::SNPrintF(buf, "%s %s", (node->is_prefix() ? "PRE" : "POST"),
Token::Name(node->op()));
......
......@@ -102,7 +102,7 @@ class AstPrinter: public PrettyPrinter {
void PrintLiteralWithModeIndented(const char* info,
Variable* var,
Handle<Object> value,
StaticType* type);
SmiAnalysis* type);
void PrintLabelsIndented(const char* info, ZoneStringList* labels);
void inc_indent() { indent_++; }
......
......@@ -28,6 +28,7 @@
#ifndef V8_REGISTER_ALLOCATOR_INL_H_
#define V8_REGISTER_ALLOCATOR_INL_H_
#include "register-allocator.h"
#include "virtual-frame.h"
namespace v8 { namespace internal {
......
......@@ -36,8 +36,19 @@ namespace v8 { namespace internal {
// Result implementation.
Result::Result(Register reg, CodeGenerator* cgen)
: type_(REGISTER),
cgen_(cgen) {
: static_type_(),
type_(REGISTER),
cgen_(cgen) {
data_.reg_ = reg;
ASSERT(reg.is_valid());
cgen_->allocator()->Use(reg);
}
Result::Result(Register reg, CodeGenerator* cgen, StaticType static_type)
: static_type_(static_type),
type_(REGISTER),
cgen_(cgen) {
data_.reg_ = reg;
ASSERT(reg.is_valid());
cgen_->allocator()->Use(reg);
......@@ -45,6 +56,7 @@ Result::Result(Register reg, CodeGenerator* cgen)
void Result::CopyTo(Result* destination) const {
destination->static_type_ = static_type_;
destination->type_ = type();
destination->cgen_ = cgen_;
......
......@@ -32,6 +32,78 @@
namespace v8 { namespace internal {
// -------------------------------------------------------------------------
// StaticType
//
// StaticType represent the type of an expression or a word at runtime.
// The types are ordered by knowledge, so that if a value can come about
// in more than one way, and there are different static types inferred
// for the different ways, the types can be combined to a type that we
// are still certain of (possibly just "unknown").
class StaticType BASE_EMBEDDED {
public:
StaticType() : static_type_(UNKNOWN_TYPE) {}
static StaticType unknown() { return StaticType(); }
static StaticType smi() { return StaticType(SMI_TYPE); }
static StaticType jsstring() { return StaticType(STRING_TYPE); }
static StaticType heap_object() { return StaticType(HEAP_OBJECT_TYPE); }
// Accessors
bool is_unknown() { return static_type_ == UNKNOWN_TYPE; }
bool is_smi() { return static_type_ == SMI_TYPE; }
bool is_heap_object() { return (static_type_ & HEAP_OBJECT_TYPE) != 0; }
bool is_jsstring() { return static_type_ == STRING_TYPE; }
bool operator==(StaticType other) const {
return static_type_ == other.static_type_;
}
// Find the best approximating type for a value.
// The argument must not be NULL.
static StaticType TypeOf(Object* object) {
// Remember to make the most specific tests first. A string is also a heap
// object, so test for string-ness first.
if (object->IsSmi()) return smi();
if (object->IsString()) return jsstring();
if (object->IsHeapObject()) return heap_object();
return unknown();
}
// Merges two static types to a type that combines the knowledge
// of both. If there is no way to combine (e.g., being a string *and*
// being a smi), the resulting type is unknown.
StaticType merge(StaticType other) {
StaticType x(
static_cast<StaticTypeEnum>(static_type_ & other.static_type_));
return x;
}
private:
enum StaticTypeEnum {
// Numbers are chosen so that least upper bound of the following
// partial order is implemented by bitwise "and":
//
// string
// |
// heap-object smi
// \ /
// unknown
//
UNKNOWN_TYPE = 0x00,
SMI_TYPE = 0x01,
HEAP_OBJECT_TYPE = 0x02,
STRING_TYPE = 0x04 | HEAP_OBJECT_TYPE
};
explicit StaticType(StaticTypeEnum static_type) : static_type_(static_type) {}
// StaticTypeEnum static_type_;
byte static_type_;
};
// -------------------------------------------------------------------------
// Results
//
......@@ -47,14 +119,24 @@ class Result BASE_EMBEDDED {
};
// Construct an invalid result.
explicit Result(CodeGenerator* cgen) : type_(INVALID), cgen_(cgen) {}
explicit Result(CodeGenerator* cgen)
: static_type_(),
type_(INVALID),
cgen_(cgen) {}
// Construct a register Result.
Result(Register reg, CodeGenerator* cgen);
Result(Register reg,
CodeGenerator* cgen);
// Construct a register Result with a known static type.
Result(Register reg,
CodeGenerator* cgen,
StaticType static_type);
// Construct a Result whose value is a compile-time constant.
Result(Handle<Object> value, CodeGenerator * cgen)
: type_(CONSTANT),
: static_type_(StaticType::TypeOf(*value)),
type_(CONSTANT),
cgen_(cgen) {
data_.handle_ = value.location();
}
......@@ -77,7 +159,10 @@ class Result BASE_EMBEDDED {
inline void Unuse();
Type type() const { return type_; }
StaticType static_type() const { return static_type_; }
void set_static_type(StaticType static_type) { static_type_ = static_type; }
Type type() const { return static_cast<Type>(type_); }
bool is_valid() const { return type() != INVALID; }
bool is_register() const { return type() == REGISTER; }
......@@ -104,7 +189,8 @@ class Result BASE_EMBEDDED {
void ToRegister(Register reg);
private:
Type type_;
StaticType static_type_;
byte type_;
union {
Register reg_;
......
......@@ -327,7 +327,7 @@ void AstOptimizer::VisitAssignment(Assignment* node) {
if (proxy != NULL) {
Variable* var = proxy->AsVariable();
if (var != NULL) {
StaticType* var_type = var->type();
SmiAnalysis* var_type = var->type();
if (var_type->IsUnknown()) {
var_type->CopyFrom(node->type());
} else if (var_type->IsLikelySmi()) {
......
......@@ -163,6 +163,20 @@ function ADD(x) {
}
// Left operand (this) is already a string.
function STRING_ADD_LEFT(x) {
x = %ToString(%ToPrimitive(x, NO_HINT));
return %StringAdd(this, x);
}
// Right operand (x) is already a string.
function STRING_ADD_RIGHT(x) {
var a = %ToString(%ToPrimitive(this, NO_HINT));
return %StringAdd(a, x);
}
// ECMA-262, section 11.6.2, page 50.
function SUB(x) {
return %NumberSub(%ToNumber(this), %ToNumber(x));
......@@ -275,7 +289,7 @@ function IN(x) {
// ECMA-262, section 11.8.6, page 54. To make the implementation more
// efficient, the return value should be zero if the 'this' is an
// efficient, the return value should be zero if the 'this' is an
// instance of F, and non-zero if not. This makes it possible to avoid
// an expensive ToBoolean conversion in the generated code.
function INSTANCE_OF(F) {
......
......@@ -85,10 +85,10 @@ void UseCount::Print() {
// ----------------------------------------------------------------------------
// Implementation StaticType.
// Implementation SmiAnalysis.
const char* StaticType::Type2String(StaticType* type) {
const char* SmiAnalysis::Type2String(SmiAnalysis* type) {
switch (type->kind_) {
case UNKNOWN:
return "UNKNOWN";
......
......@@ -64,14 +64,14 @@ class UseCount BASE_EMBEDDED {
// Variables and AST expression nodes can track their "type" to enable
// optimizations and removal of redundant checks when generating code.
class StaticType BASE_EMBEDDED {
class SmiAnalysis {
public:
enum Kind {
UNKNOWN,
LIKELY_SMI
};
StaticType() : kind_(UNKNOWN) {}
SmiAnalysis() : kind_(UNKNOWN) {}
bool Is(Kind kind) const { return kind_ == kind; }
......@@ -79,11 +79,11 @@ class StaticType BASE_EMBEDDED {
bool IsUnknown() const { return Is(UNKNOWN); }
bool IsLikelySmi() const { return Is(LIKELY_SMI); }
void CopyFrom(StaticType* other) {
void CopyFrom(SmiAnalysis* other) {
kind_ = other->kind_;
}
static const char* Type2String(StaticType* type);
static const char* Type2String(SmiAnalysis* type);
// LIKELY_SMI accessors
void SetAsLikelySmi() {
......@@ -99,7 +99,7 @@ class StaticType BASE_EMBEDDED {
private:
Kind kind_;
DISALLOW_COPY_AND_ASSIGN(StaticType);
DISALLOW_COPY_AND_ASSIGN(SmiAnalysis);
};
......@@ -185,7 +185,7 @@ class Variable: public ZoneObject {
Expression* rewrite() const { return rewrite_; }
Slot* slot() const;
StaticType* type() { return &type_; }
SmiAnalysis* type() { return &type_; }
private:
Variable(Scope* scope, Handle<String> name, Mode mode, bool is_valid_LHS,
......@@ -205,7 +205,7 @@ class Variable: public ZoneObject {
UseCount obj_uses_; // uses of the object the variable points to
// Static type information
StaticType type_;
SmiAnalysis type_;
// Code generation.
// rewrite_ is usually a Slot or a Property, but maybe any expression.
......
......@@ -312,12 +312,11 @@ class VirtualFrame : public Malloced {
void EmitPush(Register reg);
// Push an element on the virtual frame.
void Push(Register reg);
void Push(Register reg, StaticType static_type = StaticType());
void Push(Handle<Object> value);
void Push(Smi* value) { Push(Handle<Object>(value)); }
// Pushing a result invalidates it (its contents become owned by the
// frame).
// Pushing a result invalidates it (its contents become owned by the frame).
void Push(Result* result);
// Nip removes zero or more elements from immediately below the top
......
......@@ -191,9 +191,10 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) {
if (elements_[i].is_copy()) {
elements_[elements_[i].index()].set_copied();
}
elements_[i].set_static_type(target.static_type());
}
// Adjust the stack point downard if necessary.
// Adjust the stack pointer downward if necessary.
if (stack_pointer_ > expected->stack_pointer_) {
int difference = stack_pointer_ - expected->stack_pointer_;
stack_pointer_ = expected->stack_pointer_;
......@@ -950,6 +951,7 @@ Result VirtualFrame::Pop() {
if (element.is_memory()) {
Result temp = cgen_->allocator()->Allocate();
ASSERT(temp.is_valid());
temp.set_static_type(element.static_type());
__ pop(temp.reg());
return temp;
}
......@@ -981,11 +983,12 @@ Result VirtualFrame::Pop() {
FrameElement::RegisterElement(temp.reg(), FrameElement::SYNCED);
// Preserve the copy flag on the element.
if (element.is_copied()) new_element.set_copied();
new_element.set_static_type(element.static_type());
elements_[index] = new_element;
__ mov(temp.reg(), Operand(ebp, fp_relative(index)));
return Result(temp.reg(), cgen_);
return Result(temp.reg(), cgen_, element.static_type());
} else if (element.is_register()) {
return Result(element.reg(), cgen_);
return Result(element.reg(), cgen_, element.static_type());
} else {
ASSERT(element.is_constant());
return Result(element.handle(), cgen_);
......
......@@ -321,7 +321,7 @@ class VirtualFrame : public Malloced {
void EmitPush(Immediate immediate);
// Push an element on the virtual frame.
void Push(Register reg);
void Push(Register reg, StaticType static_type = StaticType());
void Push(Handle<Object> value);
void Push(Smi* value) { Push(Handle<Object>(value)); }
......
......@@ -93,10 +93,10 @@ FrameElement VirtualFrame::CopyElementAt(int index) {
case FrameElement::MEMORY: // Fall through.
case FrameElement::REGISTER:
// All copies are backed by memory or register locations.
result.type_ =
FrameElement::TypeField::encode(FrameElement::COPY)
| FrameElement::IsCopiedField::encode(false)
| FrameElement::SyncField::encode(FrameElement::NOT_SYNCED);
result.set_static_type(target.static_type());
result.type_ = FrameElement::COPY;
result.copied_ = false;
result.synced_ = false;
result.data_.index_ = index;
elements_[index].set_copied();
break;
......@@ -208,6 +208,7 @@ void VirtualFrame::SpillElementAt(int index) {
if (elements_[index].is_register()) {
Unuse(elements_[index].reg());
}
new_element.set_static_type(elements_[index].static_type());
elements_[index] = new_element;
}
......@@ -388,6 +389,8 @@ void VirtualFrame::SetElementAt(int index, Result* value) {
// register element, or the new element at frame_index, must be made
// a copy.
int i = register_index(value->reg());
ASSERT(value->static_type() == elements_[i].static_type());
if (i < frame_index) {
// The register FrameElement is lower in the frame than the new copy.
elements_[frame_index] = CopyElementAt(i);
......@@ -413,7 +416,8 @@ void VirtualFrame::SetElementAt(int index, Result* value) {
Use(value->reg(), frame_index);
elements_[frame_index] =
FrameElement::RegisterElement(value->reg(),
FrameElement::NOT_SYNCED);
FrameElement::NOT_SYNCED,
value->static_type());
}
} else {
ASSERT(value->is_constant());
......@@ -437,25 +441,33 @@ Result VirtualFrame::CallStub(CodeStub* stub, int arg_count) {
}
void VirtualFrame::Push(Register reg) {
void VirtualFrame::Push(Register reg, StaticType static_type) {
if (is_used(reg)) {
elements_.Add(CopyElementAt(register_index(reg)));
int index = register_index(reg);
FrameElement element = CopyElementAt(index);
ASSERT(static_type.merge(element.static_type()) == element.static_type());
elements_.Add(element);
} else {
Use(reg, elements_.length());
elements_.Add(FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED));
FrameElement element =
FrameElement::RegisterElement(reg,
FrameElement::NOT_SYNCED,
static_type);
elements_.Add(element);
}
}
void VirtualFrame::Push(Handle<Object> value) {
elements_.Add(FrameElement::ConstantElement(value,
FrameElement::NOT_SYNCED));
FrameElement element =
FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED);
elements_.Add(element);
}
void VirtualFrame::Push(Result* result) {
if (result->is_register()) {
Push(result->reg());
Push(result->reg(), result->static_type());
} else {
ASSERT(result->is_constant());
Push(result->handle());
......@@ -476,7 +488,9 @@ void VirtualFrame::Nip(int num_dropped) {
bool FrameElement::Equals(FrameElement other) {
if (type_ != other.type_) return false;
if (type_ != other.type_ ||
copied_ != other.copied_ ||
synced_ != other.synced_) return false;
if (is_register()) {
if (!reg().is(other.reg())) return false;
......
......@@ -47,13 +47,14 @@ namespace v8 { namespace internal {
class FrameElement BASE_EMBEDDED {
public:
enum SyncFlag {
SYNCED,
NOT_SYNCED
NOT_SYNCED,
SYNCED
};
// The default constructor creates an invalid frame element.
FrameElement() {
Initialize(INVALID, no_reg, NOT_SYNCED);
FrameElement()
: static_type_(), type_(INVALID), copied_(false), synced_(false) {
data_.reg_ = no_reg;
}
// Factory function to construct an invalid frame element.
......@@ -69,9 +70,10 @@ class FrameElement BASE_EMBEDDED {
}
// Factory function to construct an in-register frame element.
static FrameElement RegisterElement(Register reg, SyncFlag is_synced) {
FrameElement result(REGISTER, reg, is_synced);
return result;
static FrameElement RegisterElement(Register reg,
SyncFlag is_synced,
StaticType static_type = StaticType()) {
return FrameElement(REGISTER, reg, is_synced, static_type);
}
// Factory function to construct a frame element whose value is known at
......@@ -82,16 +84,16 @@ class FrameElement BASE_EMBEDDED {
return result;
}
bool is_synced() const { return SyncField::decode(type_) == SYNCED; }
bool is_synced() const { return synced_; }
void set_sync() {
ASSERT(type() != MEMORY);
type_ = (type_ & ~SyncField::mask()) | SyncField::encode(SYNCED);
synced_ = true;
}
void clear_sync() {
ASSERT(type() != MEMORY);
type_ = (type_ & ~SyncField::mask()) | SyncField::encode(NOT_SYNCED);
synced_ = false;
}
bool is_valid() const { return type() != INVALID; }
......@@ -100,15 +102,9 @@ class FrameElement BASE_EMBEDDED {
bool is_constant() const { return type() == CONSTANT; }
bool is_copy() const { return type() == COPY; }
bool is_copied() const { return IsCopiedField::decode(type_); }
void set_copied() {
type_ = (type_ & ~IsCopiedField::mask()) | IsCopiedField::encode(true);
}
void clear_copied() {
type_ = (type_ & ~IsCopiedField::mask()) | IsCopiedField::encode(false);
}
bool is_copied() const { return copied_; }
void set_copied() { copied_ = true; }
void clear_copied() { copied_ = false; }
Register reg() const {
ASSERT(is_register());
......@@ -127,6 +123,14 @@ class FrameElement BASE_EMBEDDED {
bool Equals(FrameElement other);
StaticType static_type() { return static_type_; }
void set_static_type(StaticType static_type) {
// TODO(lrn): If it's s copy, it would be better to update the real one,
// but we can't from here. The caller must handle this.
static_type_ = static_type;
}
private:
enum Type {
INVALID,
......@@ -136,17 +140,19 @@ class FrameElement BASE_EMBEDDED {
COPY
};
// BitField is <type, shift, size>.
class SyncField : public BitField<SyncFlag, 0, 1> {};
class IsCopiedField : public BitField<bool, 1, 1> {};
class TypeField : public BitField<Type, 2, 32 - 2> {};
Type type() const { return static_cast<Type>(type_); }
StaticType static_type_;
Type type() const { return TypeField::decode(type_); }
// The element's type.
byte type_;
// The element's type and a dirty bit. The dirty bit can be cleared
bool copied_;
// The element's dirty-bit. The dirty bit can be cleared
// for non-memory elements to indicate that the element agrees with
// the value in memory in the actual frame.
int type_;
bool synced_;
union {
Register reg_;
......@@ -155,15 +161,30 @@ class FrameElement BASE_EMBEDDED {
} data_;
// Used to construct memory and register elements.
FrameElement(Type type, Register reg, SyncFlag is_synced) {
Initialize(type, reg, is_synced);
FrameElement(Type type, Register reg, SyncFlag is_synced)
: static_type_(),
type_(type),
copied_(false),
synced_(is_synced != NOT_SYNCED) {
data_.reg_ = reg;
}
// Used to construct constant elements.
inline FrameElement(Handle<Object> value, SyncFlag is_synced);
FrameElement(Type type, Register reg, SyncFlag is_synced, StaticType stype)
: static_type_(stype),
type_(type),
copied_(false),
synced_(is_synced != NOT_SYNCED) {
data_.reg_ = reg;
}
// Used to initialize invalid, memory, and register elements.
inline void Initialize(Type type, Register reg, SyncFlag is_synced);
// Used to construct constant elements.
FrameElement(Handle<Object> value, SyncFlag is_synced)
: static_type_(StaticType::TypeOf(*value)),
type_(CONSTANT),
copied_(false),
synced_(is_synced != NOT_SYNCED) {
data_.handle_ = value.location();
}
void set_index(int new_index) {
ASSERT(is_copy());
......@@ -182,25 +203,4 @@ class FrameElement BASE_EMBEDDED {
#include "virtual-frame-ia32.h"
#endif
namespace v8 { namespace internal {
FrameElement::FrameElement(Handle<Object> value, SyncFlag is_synced) {
type_ = TypeField::encode(CONSTANT)
| IsCopiedField::encode(false)
| SyncField::encode(is_synced);
data_.handle_ = value.location();
}
void FrameElement::Initialize(Type type, Register reg, SyncFlag is_synced) {
type_ = TypeField::encode(type)
| IsCopiedField::encode(false)
| SyncField::encode(is_synced);
data_.reg_ = reg;
}
} } // namespace v8::internal
#endif // V8_VIRTUAL_FRAME_H_
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
assertEquals("ab", "a" + "b", "ll");
assertEquals("12", "1" + "2", "dd");
assertEquals("123", "1" + "2" + "3", "ddd");
assertEquals("123", 1 + "2" + "3", "ndd");
assertEquals("123", "1" + 2 + "3", "dnd");
assertEquals("123", "1" + "2" + 3, "ddn");
assertEquals("123", "1" + 2 + 3, "dnn");
assertEquals("123", 1 + "2" + 3, "ndn");
assertEquals("33", 1 + 2 + "3", "nnd");
var x = "1";
assertEquals("12", x + 2, "vn");
assertEquals("12", x + "2", "vd");
assertEquals("21", 2 + x, "nv");
assertEquals("21", "2" + x, "dv");
var y = "2";
assertEquals("12", x + y, "vdvd");
x = 1;
assertEquals("12", x + y, "vnvd");
y = 2;
assertEquals(3, x + y, "vnvn");
x = "1";
assertEquals("12", x + y, "vdvn");
y = "2";
assertEquals("12", x + y, "vdvd2");
(function(x, y) {
var z = "3";
var w = "4";
assertEquals("11", x + x, "xx");
assertEquals("12", x + y, "xy");
assertEquals("13", x + z, "xz");
assertEquals("14", x + w, "xw");
assertEquals("21", y + x, "yx");
assertEquals("22", y + y, "yy");
assertEquals("23", y + z, "yz");
assertEquals("24", y + w, "yw");
assertEquals("31", z + x, "zx");
assertEquals("32", z + y, "zy");
assertEquals("33", z + z, "zz");
assertEquals("34", z + w, "zw");
assertEquals("41", w + x, "wx");
assertEquals("42", w + y, "wy");
assertEquals("43", w + z, "wz");
assertEquals("44", w + w, "ww");
(function(){x = 1; z = 3;})();
assertEquals(2, x + x, "x'x");
assertEquals("12", x + y, "x'y");
assertEquals(4, x + z, "x'z'");
assertEquals("14", x + w, "x'w");
assertEquals("21", y + x, "yx'");
assertEquals("22", y + y, "yy");
assertEquals("23", y + z, "yz'");
assertEquals("24", y + w, "yw");
assertEquals(4, z + x, "z'x'");
assertEquals("32", z + y, "z'y");
assertEquals(6, z + z, "z'z'");
assertEquals("34", z + w, "z'w");
assertEquals("41", w + x, "wx'");
assertEquals("42", w + y, "wy");
assertEquals("43", w + z, "wz'");
assertEquals("44", w + w, "ww");
})("1", "2");
assertEquals("142", "1" + new Number(42), "sN");
assertEquals("421", new Number(42) + "1", "Ns");
assertEquals(84, new Number(42) + new Number(42), "NN");
assertEquals("142", "1" + new String("42"), "sS");
assertEquals("421", new String("42") + "1", "Ss");
assertEquals("142", "1" + new String("42"), "sS");
assertEquals("4242", new String("42") + new String("42"), "SS");
assertEquals("1true", "1" + true, "sb");
assertEquals("true1", true + "1", "bs");
assertEquals(2, true + true, "bs");
assertEquals("1true", "1" + new Boolean(true), "sB");
assertEquals("true1", new Boolean(true) + "1", "Bs");
assertEquals(2, new Boolean(true) + new Boolean(true), "Bs");
assertEquals("1undefined", "1" + void 0, "sv");
assertEquals("undefined1", (void 0) + "1", "vs");
assertTrue(isNaN(void 0 + void 0), "vv");
assertEquals("1null", "1" + null, "su");
assertEquals("null1", null + "1", "us");
assertEquals(0, null + null, "uu");
(function (i) {
// Check that incoming frames are merged correctly.
var x;
var y;
var z;
var w;
switch (i) {
case 1: x = 42; y = "stry"; z = "strz"; w = 42; break;
default: x = "strx", y = 42; z = "strz"; w = 42; break;
}
var resxx = x + x;
var resxy = x + y;
var resxz = x + z;
var resxw = x + w;
var resyx = y + x;
var resyy = y + y;
var resyz = y + z;
var resyw = y + w;
var reszx = z + x;
var reszy = z + y;
var reszz = z + z;
var reszw = z + w;
var reswx = w + x;
var reswy = w + y;
var reswz = w + z;
var resww = w + w;
assertEquals(84, resxx, "swxx");
assertEquals("42stry", resxy, "swxy");
assertEquals("42strz", resxz, "swxz");
assertEquals(84, resxw, "swxw");
assertEquals("stry42", resyx, "swyx");
assertEquals("strystry", resyy, "swyy");
assertEquals("strystrz", resyz, "swyz");
assertEquals("stry42", resyw, "swyw");
assertEquals("strz42", reszx, "swzx");
assertEquals("strzstry", reszy, "swzy");
assertEquals("strzstrz", reszz, "swzz");
assertEquals("strz42", reszw, "swzw");
assertEquals(84, reswx, "swwx");
assertEquals("42stry", reswy, "swwy");
assertEquals("42strz", reswz, "swwz");
assertEquals(84, resww, "swww");
})(1);
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