Tracks static type of strings in frame elements and results.
authorlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 3 Apr 2009 12:01:56 +0000 (12:01 +0000)
committerlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 3 Apr 2009 12:01:56 +0000 (12:01 +0000)
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

20 files changed:
src/ast.h
src/builtins.h
src/codegen-ia32.cc
src/codegen-ia32.h
src/jump-target.cc
src/prettyprinter.cc
src/prettyprinter.h
src/register-allocator-inl.h
src/register-allocator.cc
src/register-allocator.h
src/rewriter.cc
src/runtime.js
src/variables.cc
src/variables.h
src/virtual-frame-arm.h
src/virtual-frame-ia32.cc
src/virtual-frame-ia32.h
src/virtual-frame.cc
src/virtual-frame.h
test/mjsunit/string-add.js [new file with mode: 0644]

index 0da58f1e415704e4a780d659662d18818e295292..5dfd21d9e4b98d3b53750f461c3df9afbb298ab4 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -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_;
 };
 
 
index fa443fa454d44cef2cd4309cbf45393b1ea8877f..d7b90f69a68d32eaf39b04f9489aa7a0a7eec0fc 100644 (file)
@@ -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)
 
index 0e2a370017b30ce259e6560583dd889ef5f447cd..6f464cf04cd25385979144b7762b8ab875d494d0 100644 (file)
@@ -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) {
index d2a31f56ef3228ea507cc105d7cbadecf4154df2..165f87717fc8a9c362330460d0e4ae882fdf397e 100644 (file)
@@ -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);
 
index 7f515db8f332e3ac03447faa6d8d5b66713f418d..047588bdb198b0412136a9906533357f0c3ded3a 100644 (file)
@@ -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);
     }
   }
 
index 0a1a169ed7c4f6f9fdd4b85c7c46cf6b4eb08ab4..7f8c5678068e7a702da90302a3fe91f69b072591 100644 (file)
@@ -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()));
index 1c94635ef49022b2c258a345a6b19f32255dff42..720fe7b4c939ec217d7b8784049e13f34cdd769c 100644 (file)
@@ -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_++; }
index 8611d6a2519dab39cbdd800368af0ce0408212f8..9e745b52eac3d0a022c363a5b4f3d0002d597c51 100644 (file)
@@ -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 {
index 028baebdfc51708c1391a808a43cf84df1ce401f..94e031fa0bf9e4fff3e01c58865cf69622432fd3 100644 (file)
@@ -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_;
 
index f6db62cf929d9d0de83e050f1fab0ec1be5495d4..dcc2eb7ee6cb408c85680e0404374db1e3707924 100644 (file)
 
 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_;
index c713612be3cc94a8e2972fb74cb289ee41c7263b..1aa24aa4c4172461170a71e392aaf0f0e1962167 100644 (file)
@@ -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()) {
index 5e73d4100d643e35aadda8cbd9ab4ed1c62d541a..030aedb0a8f2877ab7f8a93c03659211b5db4037 100644 (file)
@@ -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) {
index 1d7d6e4c3a2421f2ddd90a3de3f5af30c48a4f58..51eb8caf8e835f39c1d27fc91e21fbe704ad5586 100644 (file)
@@ -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";
index 00ba34557c4d7636ac01df26cbc28ddd8cc08218..275f498e82c5b5bafd91e9b36b7cf26cbd381613 100644 (file)
@@ -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.
index 79102015f32e998cb1b2224763cc4d7f8020d757..f15eec27a616ad10628da7214cc27fd750063536 100644 (file)
@@ -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
index ed0d3d5b3d6ea319731a31ba4498e11547adc5af..067ecec6adb0fafe97aca9ef9e57ffbb2ea2aefe 100644 (file)
@@ -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_);
index 6e5497691b9cb1949368936ac22c03251c73ca74..bd7f44d8b5f948161ea5649ece1f7477fb786b0b 100644 (file)
@@ -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)); }
 
index 73c8b15bd0e54f27de19c39ecc67b127d20b0421..b5682cb13c8f4172e683f81851267b22411535a2 100644 (file)
@@ -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;
index 9f5cf012488e905d63a3ee9afadf73a052b36fec..99b4f76723ae9288ae29aff554d82bfc9407a862 100644 (file)
@@ -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_
diff --git a/test/mjsunit/string-add.js b/test/mjsunit/string-add.js
new file mode 100644 (file)
index 0000000..c42cf79
--- /dev/null
@@ -0,0 +1,175 @@
+// 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);