Revision: 6285
Author: [email protected]
Date: Wed Jan 12 06:14:14 2011
Log: Allow arguments in safepoints with registers.

This should enable calling runtime functions with arguments from
deferred lithium code.

Review URL: http://codereview.chromium.org/6125007
http://code.google.com/p/v8/source/detail?r=6285

Modified:
 /branches/bleeding_edge/src/arm/deoptimizer-arm.cc
 /branches/bleeding_edge/src/frames.cc
 /branches/bleeding_edge/src/frames.h
 /branches/bleeding_edge/src/ia32/deoptimizer-ia32.cc
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/safepoint-table.cc
 /branches/bleeding_edge/src/safepoint-table.h

=======================================
--- /branches/bleeding_edge/src/arm/deoptimizer-arm.cc Tue Dec 7 04:43:23 2010 +++ /branches/bleeding_edge/src/arm/deoptimizer-arm.cc Wed Jan 12 06:14:14 2011
@@ -55,8 +55,9 @@
   SafepointTable table(function->code());
   for (unsigned i = 0; i < table.length(); i++) {
     unsigned pc_offset = table.GetPcOffset(i);
-    int deoptimization_index = table.GetDeoptimizationIndex(i);
-    int gap_code_size = table.GetGapCodeSize(i);
+    SafepointEntry safepoint_entry = table.GetEntry(i);
+    int deoptimization_index = safepoint_entry.deoptimization_index();
+    int gap_code_size = safepoint_entry.gap_code_size();
     // Check that we did not shoot past next safepoint.
     // TODO(srdjan): How do we guarantee that safepoint code does not
     // overlap other safepoint patching code?
=======================================
--- /branches/bleeding_edge/src/frames.cc       Tue Dec  7 03:53:19 2010
+++ /branches/bleeding_edge/src/frames.cc       Wed Jan 12 06:14:14 2011
@@ -329,21 +329,20 @@


 Code* StackFrame::GetSafepointData(Address pc,
-                                   uint8_t** safepoint_entry,
+                                   SafepointEntry* safepoint_entry,
                                    unsigned* stack_slots) {
PcToCodeCache::PcToCodeCacheEntry* entry = PcToCodeCache::GetCacheEntry(pc);
-  uint8_t* cached_safepoint_entry = entry->safepoint_entry;
-  if (cached_safepoint_entry == NULL) {
-    cached_safepoint_entry = entry->code->GetSafepointEntry(pc);
-    ASSERT(cached_safepoint_entry != NULL);  // No safepoint found.
-    entry->safepoint_entry = cached_safepoint_entry;
+  SafepointEntry cached_safepoint_entry = entry->safepoint_entry;
+  if (!entry->safepoint_entry.is_valid()) {
+    entry->safepoint_entry = entry->code->GetSafepointEntry(pc);
+    ASSERT(entry->safepoint_entry.is_valid());
   } else {
-    ASSERT(cached_safepoint_entry == entry->code->GetSafepointEntry(pc));
+ ASSERT(entry->safepoint_entry.Equals(entry->code->GetSafepointEntry(pc)));
   }

   // Fill in the results and return the code.
   Code* code = entry->code;
-  *safepoint_entry = cached_safepoint_entry;
+  *safepoint_entry = entry->safepoint_entry;
   *stack_slots = code->stack_slots();
   return code;
 }
@@ -536,7 +535,7 @@

   // Compute the safepoint information.
   unsigned stack_slots = 0;
-  uint8_t* safepoint_entry = NULL;
+  SafepointEntry safepoint_entry;
   Code* code = StackFrame::GetSafepointData(
       pc(), &safepoint_entry, &stack_slots);
   unsigned slot_space = stack_slots * kPointerSize;
@@ -547,11 +546,18 @@
   Object** parameters_base = &Memory::Object_at(sp());
   Object** parameters_limit = &Memory::Object_at(
       fp() + JavaScriptFrameConstants::kFunctionOffset - slot_space);
+
+  // Visit the parameters that may be on top of the saved registers.
+  if (safepoint_entry.argument_count() > 0) {
+    v->VisitPointers(parameters_base,
+                     parameters_base + safepoint_entry.argument_count());
+    parameters_base += safepoint_entry.argument_count();
+  }

   // Visit the registers that contain pointers if any.
-  if (SafepointTable::HasRegisters(safepoint_entry)) {
+  if (safepoint_entry.HasRegisters()) {
     for (int i = kNumSafepointRegisters - 1; i >=0; i--) {
-      if (SafepointTable::HasRegisterAt(safepoint_entry, i)) {
+      if (safepoint_entry.HasRegisterAt(i)) {
int reg_stack_index = MacroAssembler::SafepointRegisterStackIndex(i);
         v->VisitPointer(parameters_base + reg_stack_index);
       }
@@ -561,7 +567,8 @@
   }

   // We're done dealing with the register bits.
-  safepoint_entry += kNumSafepointRegisters >> kBitsPerByteLog2;
+  uint8_t* safepoint_bits = safepoint_entry.bits();
+  safepoint_bits += kNumSafepointRegisters >> kBitsPerByteLog2;

   // Visit the rest of the parameters.
   v->VisitPointers(parameters_base, parameters_limit);
@@ -570,7 +577,7 @@
   for (unsigned index = 0; index < stack_slots; index++) {
     int byte_index = index >> kBitsPerByteLog2;
     int bit_index = index & (kBitsPerByte - 1);
-    if ((safepoint_entry[byte_index] & (1U << bit_index)) != 0) {
+    if ((safepoint_bits[byte_index] & (1U << bit_index)) != 0) {
       v->VisitPointer(parameters_limit + index);
     }
   }
@@ -778,14 +785,8 @@
   ASSERT(code != NULL);
   ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION);

-  SafepointTable table(code);
- unsigned pc_offset = static_cast<unsigned>(pc() - code->instruction_start());
-  for (unsigned i = 0; i < table.length(); i++) {
-    if (table.GetPcOffset(i) == pc_offset) {
-      *deopt_index = table.GetDeoptimizationIndex(i);
-      break;
-    }
-  }
+  SafepointEntry safepoint_entry = code->GetSafepointEntry(pc());
+  *deopt_index = safepoint_entry.deoptimization_index();
   ASSERT(*deopt_index != AstNode::kNoNumber);

   return DeoptimizationInputData::cast(code->deoptimization_data());
@@ -1150,7 +1151,7 @@
     // been set. Otherwise, we risk trying to use a cache entry before
     // the code has been computed.
     entry->code = GcSafeFindCodeForPc(pc);
-    entry->safepoint_entry = NULL;
+    entry->safepoint_entry.Reset();
     entry->pc = pc;
   }
   return entry;
=======================================
--- /branches/bleeding_edge/src/frames.h        Tue Dec  7 03:31:57 2010
+++ /branches/bleeding_edge/src/frames.h        Wed Jan 12 06:14:14 2011
@@ -28,6 +28,8 @@
 #ifndef V8_FRAMES_H_
 #define V8_FRAMES_H_

+#include "safepoint-table.h"
+
 namespace v8 {
 namespace internal {

@@ -51,7 +53,7 @@
   struct PcToCodeCacheEntry {
     Address pc;
     Code* code;
-    uint8_t* safepoint_entry;
+    SafepointEntry safepoint_entry;
   };

   static PcToCodeCacheEntry* cache(int index) {
@@ -208,7 +210,7 @@
   // safepoint entry and the number of stack slots. The pc must be at
   // a safepoint.
   static Code* GetSafepointData(Address pc,
-                                uint8_t** safepoint_entry,
+                                SafepointEntry* safepoint_entry,
                                 unsigned* stack_slots);

   virtual void Iterate(ObjectVisitor* v) const = 0;
=======================================
--- /branches/bleeding_edge/src/ia32/deoptimizer-ia32.cc Thu Jan 6 05:48:12 2011 +++ /branches/bleeding_edge/src/ia32/deoptimizer-ia32.cc Wed Jan 12 06:14:14 2011
@@ -56,8 +56,9 @@
   SafepointTable table(function->code());
   for (unsigned i = 0; i < table.length(); i++) {
     unsigned pc_offset = table.GetPcOffset(i);
-    int deoptimization_index = table.GetDeoptimizationIndex(i);
-    int gap_code_size = table.GetGapCodeSize(i);
+    SafepointEntry safepoint_entry = table.GetEntry(i);
+    int deoptimization_index = safepoint_entry.deoptimization_index();
+    int gap_code_size = safepoint_entry.gap_code_size();
 #ifdef DEBUG
     // Destroy the code which is not supposed to run again.
     unsigned instructions = pc_offset - last_pc_offset;
=======================================
--- /branches/bleeding_edge/src/objects.cc      Thu Jan  6 06:00:50 2011
+++ /branches/bleeding_edge/src/objects.cc      Wed Jan 12 06:14:14 2011
@@ -5987,14 +5987,9 @@
 }


-uint8_t* Code::GetSafepointEntry(Address pc) {
+SafepointEntry Code::GetSafepointEntry(Address pc) {
   SafepointTable table(this);
-  unsigned pc_offset = static_cast<unsigned>(pc - instruction_start());
-  for (unsigned i = 0; i < table.length(); i++) {
-    // TODO(kasperl): Replace the linear search with binary search.
-    if (table.GetPcOffset(i) == pc_offset) return table.GetEntry(i);
-  }
-  return NULL;
+  return table.FindEntry(pc);
 }


@@ -6265,12 +6260,15 @@
PrintF(out, "%p %4d ", (instruction_start() + pc_offset), pc_offset);
       table.PrintEntry(i);
       PrintF(out, " (sp -> fp)");
-      int deoptimization_index = table.GetDeoptimizationIndex(i);
-      if (deoptimization_index != Safepoint::kNoDeoptimizationIndex) {
-        PrintF(out, "  %6d", deoptimization_index);
+      SafepointEntry entry = table.GetEntry(i);
+ if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) {
+        PrintF(out, "  %6d", entry.deoptimization_index());
       } else {
         PrintF(out, "  <none>");
       }
+      if (entry.argument_count() > 0) {
+        PrintF(out, " argc: %d", entry.argument_count());
+      }
       PrintF(out, "\n");
     }
     PrintF(out, "\n");
=======================================
--- /branches/bleeding_edge/src/objects.h       Fri Jan  7 02:06:28 2011
+++ /branches/bleeding_edge/src/objects.h       Wed Jan 12 06:14:14 2011
@@ -3121,6 +3121,9 @@
 };


+class SafepointEntry;
+
+
 // Code describes objects with on-the-fly generated machine code.
 class Code: public HeapObject {
  public:
@@ -3268,9 +3271,8 @@
   inline byte compare_state();
   inline void set_compare_state(byte value);

-  // Get the safepoint entry for the given pc. Returns NULL for
-  // non-safepoint pcs.
-  uint8_t* GetSafepointEntry(Address pc);
+  // Get the safepoint entry for the given pc.
+  SafepointEntry GetSafepointEntry(Address pc);

   // Mark this code object as not having a stack check table.  Assumes kind
   // is FUNCTION.
=======================================
--- /branches/bleeding_edge/src/safepoint-table.cc      Tue Dec  7 03:31:57 2010
+++ /branches/bleeding_edge/src/safepoint-table.cc      Wed Jan 12 06:14:14 2011
@@ -26,11 +26,34 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 #include "safepoint-table.h"
+
 #include "disasm.h"
+#include "macro-assembler.h"

 namespace v8 {
 namespace internal {

+
+bool SafepointEntry::HasRegisters() const {
+  ASSERT(is_valid());
+  ASSERT(IsAligned(kNumSafepointRegisters, kBitsPerByte));
+  const int num_reg_bytes = kNumSafepointRegisters >> kBitsPerByteLog2;
+  for (int i = 0; i < num_reg_bytes; i++) {
+    if (bits_[i] != SafepointTable::kNoRegisters) return true;
+  }
+  return false;
+}
+
+
+bool SafepointEntry::HasRegisterAt(int reg_index) const {
+  ASSERT(is_valid());
+  ASSERT(reg_index >= 0 && reg_index < kNumSafepointRegisters);
+  int byte_index = reg_index >> kBitsPerByteLog2;
+  int bit_index = reg_index & (kBitsPerByte - 1);
+  return (bits_[byte_index] & (1 << bit_index)) != 0;
+}
+
+
 SafepointTable::SafepointTable(Code* code) {
   ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION);
   code_ = code;
@@ -41,45 +64,39 @@
   entries_ = pc_and_deoptimization_indexes_ +
             (length_ * kPcAndDeoptimizationIndexSize);
   ASSERT(entry_size_ > 0);
- ASSERT_EQ(DeoptimizationIndexField::max(), Safepoint::kNoDeoptimizationIndex);
+  ASSERT_EQ(SafepointEntry::DeoptimizationIndexField::max(),
+            Safepoint::kNoDeoptimizationIndex);
 }


-bool SafepointTable::HasRegisters(uint8_t* entry) {
-  ASSERT(IsAligned(kNumSafepointRegisters, kBitsPerByte));
-  const int num_reg_bytes = kNumSafepointRegisters >> kBitsPerByteLog2;
-  for (int i = 0; i < num_reg_bytes; i++) {
-    if (entry[i] != kNoRegisters) return true;
-  }
-  return false;
-}
-
-
-bool SafepointTable::HasRegisterAt(uint8_t* entry, int reg_index) {
-  ASSERT(reg_index >= 0 && reg_index < kNumSafepointRegisters);
-  int byte_index = reg_index >> kBitsPerByteLog2;
-  int bit_index = reg_index & (kBitsPerByte - 1);
-  return (entry[byte_index] & (1 << bit_index)) != 0;
+SafepointEntry SafepointTable::FindEntry(Address pc) const {
+ unsigned pc_offset = static_cast<unsigned>(pc - code_->instruction_start());
+  for (unsigned i = 0; i < length(); i++) {
+    // TODO(kasperl): Replace the linear search with binary search.
+    if (GetPcOffset(i) == pc_offset) return GetEntry(i);
+  }
+  return SafepointEntry();
 }


 void SafepointTable::PrintEntry(unsigned index) const {
   disasm::NameConverter converter;
-  uint8_t* entry = GetEntry(index);
+  SafepointEntry entry = GetEntry(index);
+  uint8_t* bits = entry.bits();

   // Print the stack slot bits.
   if (entry_size_ > 0) {
     ASSERT(IsAligned(kNumSafepointRegisters, kBitsPerByte));
     const int first = kNumSafepointRegisters >> kBitsPerByteLog2;
     int last = entry_size_ - 1;
-    for (int i = first; i < last; i++) PrintBits(entry[i], kBitsPerByte);
+    for (int i = first; i < last; i++) PrintBits(bits[i], kBitsPerByte);
     int last_bits = code_->stack_slots() - ((last - first) * kBitsPerByte);
-    PrintBits(entry[last], last_bits);
+    PrintBits(bits[last], last_bits);

     // Print the registers (if any).
-    if (!HasRegisters(entry)) return;
+    if (!entry.HasRegisters()) return;
     for (int j = 0; j < kNumSafepointRegisters; j++) {
-      if (HasRegisterAt(entry, j)) {
+      if (entry.HasRegisterAt(j)) {
         PrintF(" | %s", converter.NameOfCPURegister(j));
       }
     }
@@ -93,6 +110,11 @@
     PrintF("%c", ((byte & (1 << i)) == 0) ? '0' : '1');
   }
 }
+
+
+void Safepoint::DefinePointerRegister(Register reg) {
+  registers_->Add(reg.code());
+}


 Safepoint SafepointTableBuilder::DefineSafepoint(Assembler* assembler,
@@ -102,6 +124,7 @@
   pc_and_deoptimization_index.pc = assembler->pc_offset();
   pc_and_deoptimization_index.deoptimization_index = deoptimization_index;
   pc_and_deoptimization_index.pc_after_gap = assembler->pc_offset();
+  pc_and_deoptimization_index.arguments = 0;
   deoptimization_info_.Add(pc_and_deoptimization_index);
   indexes_.Add(new ZoneList<int>(8));
   registers_.Add(NULL);
@@ -112,11 +135,12 @@
 Safepoint SafepointTableBuilder::DefineSafepointWithRegisters(
     Assembler* assembler, int arguments, int deoptimization_index) {
   ASSERT(deoptimization_index != -1);
-  ASSERT(arguments == 0);  // Only case that works for now.
+  ASSERT(arguments >= 0);
   DeoptimizationInfo pc_and_deoptimization_index;
   pc_and_deoptimization_index.pc = assembler->pc_offset();
   pc_and_deoptimization_index.deoptimization_index = deoptimization_index;
   pc_and_deoptimization_index.pc_after_gap = assembler->pc_offset();
+  pc_and_deoptimization_index.arguments = arguments;
   deoptimization_info_.Add(pc_and_deoptimization_index);
   indexes_.Add(new ZoneList<int>(8));
   registers_.Add(new ZoneList<int>(4));
@@ -152,7 +176,7 @@
   // pc after gap information.
   for (int i = 0; i < length; i++) {
     assembler->dd(deoptimization_info_[i].pc);
- assembler->dd(EncodeDeoptimizationIndexAndGap(deoptimization_info_[i]));
+    assembler->dd(EncodeExceptPC(deoptimization_info_[i]));
   }

   // Emit table of bitmaps.
@@ -197,12 +221,12 @@
 }


-uint32_t SafepointTableBuilder::EncodeDeoptimizationIndexAndGap(
-    DeoptimizationInfo info) {
+uint32_t SafepointTableBuilder::EncodeExceptPC(const DeoptimizationInfo& info) {
   unsigned index = info.deoptimization_index;
   unsigned gap_size = info.pc_after_gap - info.pc;
- uint32_t encoding = SafepointTable::DeoptimizationIndexField::encode(index);
-  encoding |= SafepointTable::GapCodeSizeField::encode(gap_size);
+ uint32_t encoding = SafepointEntry::DeoptimizationIndexField::encode(index);
+  encoding |= SafepointEntry::GapCodeSizeField::encode(gap_size);
+  encoding |= SafepointEntry::ArgumentsField::encode(info.arguments);
   return encoding;
 }

=======================================
--- /branches/bleeding_edge/src/safepoint-table.h       Mon Jan 10 13:10:51 2011
+++ /branches/bleeding_edge/src/safepoint-table.h       Wed Jan 12 06:14:14 2011
@@ -30,56 +30,102 @@

 #include "v8.h"

-#include "macro-assembler.h"
+#include "heap.h"
 #include "zone.h"
 #include "zone-inl.h"

 namespace v8 {
 namespace internal {

-class SafepointTable BASE_EMBEDDED {
+struct Register;
+
+class SafepointEntry BASE_EMBEDDED {
  public:
-  explicit SafepointTable(Code* code);
-
-  int size() const {
-    return kHeaderSize +
-           (length_ * (kPcAndDeoptimizationIndexSize + entry_size_)); }
-  unsigned length() const { return length_; }
-  unsigned entry_size() const { return entry_size_; }
-
-  unsigned GetPcOffset(unsigned index) const {
-    ASSERT(index < length_);
-    return Memory::uint32_at(GetPcOffsetLocation(index));
+  SafepointEntry() : info_(0), bits_(NULL) {}
+
+  SafepointEntry(unsigned info, uint8_t* bits) : info_(info), bits_(bits) {
+    ASSERT(is_valid());
+  }
+
+  bool is_valid() const { return bits_ != NULL; }
+
+  bool Equals(const SafepointEntry& other) const {
+    return info_ == other.info_ && bits_ == other.bits_;
   }

-  int GetDeoptimizationIndex(unsigned index) const {
-    ASSERT(index < length_);
-    unsigned value = Memory::uint32_at(GetDeoptimizationLocation(index));
-    return DeoptimizationIndexField::decode(value);
+  void Reset() {
+    info_ = 0;
+    bits_ = NULL;
   }

-  unsigned GetGapCodeSize(unsigned index) const {
-    ASSERT(index < length_);
-    unsigned value = Memory::uint32_at(GetDeoptimizationLocation(index));
-    return GapCodeSizeField::decode(value);
+  int deoptimization_index() const {
+    ASSERT(is_valid());
+    return DeoptimizationIndexField::decode(info_);
   }

-  uint8_t* GetEntry(unsigned index) const {
-    ASSERT(index < length_);
-    return &Memory::uint8_at(entries_ + (index * entry_size_));
+  int gap_code_size() const {
+    ASSERT(is_valid());
+    return GapCodeSizeField::decode(info_);
   }

+  int argument_count() const {
+    ASSERT(is_valid());
+    return ArgumentsField::decode(info_);
+  }
+
+  uint8_t* bits() {
+    ASSERT(is_valid());
+    return bits_;
+  }
+
+  bool HasRegisters() const;
+  bool HasRegisterAt(int reg_index) const;
+
   // Reserve 13 bits for the gap code size. On ARM a constant pool can be
// emitted when generating the gap code. The size of the const pool is less // than what can be represented in 12 bits, so 13 bits gives room for having
   // instructions before potentially emitting a constant pool.
   static const int kGapCodeSizeBits = 13;
-  static const int kDeoptIndexBits = 32 - kGapCodeSizeBits;
+  static const int kArgumentsFieldBits = 3;
+  static const int kDeoptIndexBits =
+      32 - kGapCodeSizeBits - kArgumentsFieldBits;
class GapCodeSizeField: public BitField<unsigned, 0, kGapCodeSizeBits> {}; - class DeoptimizationIndexField: public BitField<int, kGapCodeSizeBits, kDeoptIndexBits> {}; // NOLINT
-
-  static bool HasRegisters(uint8_t* entry);
-  static bool HasRegisterAt(uint8_t* entry, int reg_index);
+  class DeoptimizationIndexField: public BitField<int,
+                                                  kGapCodeSizeBits,
+ kDeoptIndexBits> {}; // NOLINT
+  class ArgumentsField: public BitField<unsigned,
+                                        kGapCodeSizeBits + kDeoptIndexBits,
+                                        kArgumentsFieldBits> {};  // NOLINT
+ private:
+  unsigned info_;
+  uint8_t* bits_;
+};
+
+
+class SafepointTable BASE_EMBEDDED {
+ public:
+  explicit SafepointTable(Code* code);
+
+  int size() const {
+    return kHeaderSize +
+           (length_ * (kPcAndDeoptimizationIndexSize + entry_size_)); }
+  unsigned length() const { return length_; }
+  unsigned entry_size() const { return entry_size_; }
+
+  unsigned GetPcOffset(unsigned index) const {
+    ASSERT(index < length_);
+    return Memory::uint32_at(GetPcOffsetLocation(index));
+  }
+
+  SafepointEntry GetEntry(unsigned index) const {
+    ASSERT(index < length_);
+    unsigned info = Memory::uint32_at(GetInfoLocation(index));
+    uint8_t* bits = &Memory::uint8_at(entries_ + (index * entry_size_));
+    return SafepointEntry(info, bits);
+  }
+
+  // Returns the entry for the given pc.
+  SafepointEntry FindEntry(Address pc) const;

   void PrintEntry(unsigned index) const;

@@ -100,7 +146,7 @@
            (index * kPcAndDeoptimizationIndexSize);
   }

-  Address GetDeoptimizationLocation(unsigned index) const {
+  Address GetInfoLocation(unsigned index) const {
     return GetPcOffsetLocation(index) + kPcSize;
   }

@@ -115,16 +161,19 @@
   Address entries_;

   friend class SafepointTableBuilder;
+  friend class SafepointEntry;
+
+  DISALLOW_COPY_AND_ASSIGN(SafepointTable);
 };


 class Safepoint BASE_EMBEDDED {
  public:
   static const int kNoDeoptimizationIndex =
-      (1 << (SafepointTable::kDeoptIndexBits)) - 1;
+      (1 << (SafepointEntry::kDeoptIndexBits)) - 1;

   void DefinePointerSlot(int index) { indexes_->Add(index); }
-  void DefinePointerRegister(Register reg) { registers_->Add(reg.code()); }
+  void DefinePointerRegister(Register reg);

  private:
   Safepoint(ZoneList<int>* indexes, ZoneList<int>* registers) :
@@ -177,9 +226,10 @@
     unsigned pc;
     unsigned deoptimization_index;
     unsigned pc_after_gap;
+    unsigned arguments;
   };

-  uint32_t EncodeDeoptimizationIndexAndGap(DeoptimizationInfo info);
+  uint32_t EncodeExceptPC(const DeoptimizationInfo& info);

   ZoneList<DeoptimizationInfo> deoptimization_info_;
   ZoneList<ZoneList<int>*> indexes_;

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to