Add checks to the ARM simulator to ensure that we flush the icache all
authorerik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 22 Apr 2010 12:41:10 +0000 (12:41 +0000)
committererik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 22 Apr 2010 12:41:10 +0000 (12:41 +0000)
the places we should.
Review URL: http://codereview.chromium.org/1523030

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4474 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/arm/cpu-arm.cc
src/arm/simulator-arm.cc
src/arm/simulator-arm.h
src/serialize.cc

index 55f31d46f2ec87079b0c91967b5962fdc39d80a9..d50c2038aa5c616207d712e7f8e4edf137aa0e84 100644 (file)
@@ -26,7 +26,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // CPU specific code for arm independent of OS goes here.
-#if defined(__arm__)
+#ifdef __arm__
 #include <sys/syscall.h>  // for cache flushing.
 #endif
 
 #include "cpu.h"
 #include "macro-assembler.h"
 
+#ifndef __arm__
+#include "simulator-arm.h"  // for cache flushing.
+#endif
+
 namespace v8 {
 namespace internal {
 
@@ -46,9 +50,11 @@ void CPU::Setup() {
 void CPU::FlushICache(void* start, size_t size) {
 #if !defined (__arm__)
   // Not generating ARM instructions for C-code. This means that we are
-  // building an ARM emulator based target. No I$ flushes are necessary.
+  // building an ARM emulator based target.  We should notify the simulator
+  // that the Icache was flushed.
   // None of this code ends up in the snapshot so there are no issues
   // around whether or not to generate the code when building snapshots.
+  assembler::arm::Simulator::FlushICache(start, size);
 #else
   // Ideally, we would call
   //   syscall(__ARM_NR_cacheflush, start,
index 827a88f35630f7b7ade23bd5ba46a299a8b09d4a..b44a73804b1050dc2b351df361d38b9338f16f16 100644 (file)
@@ -474,6 +474,96 @@ void Debugger::Debug() {
 }
 
 
+static bool ICacheMatch(void* one, void* two) {
+  ASSERT((reinterpret_cast<intptr_t>(one) & CachePage::kPageMask) == 0);
+  ASSERT((reinterpret_cast<intptr_t>(two) & CachePage::kPageMask) == 0);
+  return one == two;
+}
+
+
+static uint32_t ICacheHash(void* key) {
+  return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(key)) >> 2;
+}
+
+
+static bool AllOnOnePage(uintptr_t start, int size) {
+  intptr_t start_page = (start & ~CachePage::kPageMask);
+  intptr_t end_page = ((start + size) & ~CachePage::kPageMask);
+  return start_page == end_page;
+}
+
+
+void Simulator::FlushICache(void* start_addr, size_t size) {
+  intptr_t start = reinterpret_cast<intptr_t>(start_addr);
+  int intra_line = (start & CachePage::kLineMask);
+  start -= intra_line;
+  size += intra_line;
+  size = ((size - 1) | CachePage::kLineMask) + 1;
+  int offset = (start & CachePage::kPageMask);
+  while (!AllOnOnePage(start, size - 1)) {
+    int bytes_to_flush = CachePage::kPageSize - offset;
+    FlushOnePage(start, bytes_to_flush);
+    start += bytes_to_flush;
+    size -= bytes_to_flush;
+    ASSERT_EQ(0, start & CachePage::kPageMask);
+    offset = 0;
+  }
+  if (size != 0) {
+    FlushOnePage(start, size);
+  }
+}
+
+
+CachePage* Simulator::GetCachePage(void* page) {
+  v8::internal::HashMap::Entry* entry = i_cache_->Lookup(page,
+                                                         ICacheHash(page),
+                                                         true);
+  if (entry->value == NULL) {
+    CachePage* new_page = new CachePage();
+    entry->value = new_page;
+  }
+  return reinterpret_cast<CachePage*>(entry->value);
+}
+
+
+// Flush from start up to and not including start + size.
+void Simulator::FlushOnePage(intptr_t start, int size) {
+  ASSERT(size <= CachePage::kPageSize);
+  ASSERT(AllOnOnePage(start, size - 1));
+  ASSERT((start & CachePage::kLineMask) == 0);
+  ASSERT((size & CachePage::kLineMask) == 0);
+  void* page = reinterpret_cast<void*>(start & (~CachePage::kPageMask));
+  int offset = (start & CachePage::kPageMask);
+  CachePage* cache_page = GetCachePage(page);
+  char* valid_bytemap = cache_page->ValidityByte(offset);
+  memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift);
+}
+
+
+void Simulator::CheckICache(Instr* instr) {
+#ifdef DEBUG
+  intptr_t address = reinterpret_cast<intptr_t>(instr);
+  void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
+  void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
+  int offset = (address & CachePage::kPageMask);
+  CachePage* cache_page = GetCachePage(page);
+  char* cache_valid_byte = cache_page->ValidityByte(offset);
+  bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
+  char* cached_line = cache_page->CachedData(offset & ~CachePage::kLineMask);
+  if (cache_hit) {
+    // Check that the data in memory matches the contents of the I-cache.
+    CHECK(memcmp(reinterpret_cast<void*>(instr),
+                 cache_page->CachedData(offset),
+                 Instr::kInstrSize) == 0);
+  } else {
+    // Cache miss.  Load memory into the cache.
+    memcpy(cached_line, line, CachePage::kLineLength);
+    *cache_valid_byte = CachePage::LINE_VALID;
+  }
+#endif
+}
+
+
 // Create one simulator per thread and keep it in thread local storage.
 static v8::internal::Thread::LocalStorageKey simulator_key;
 
@@ -489,7 +579,13 @@ void Simulator::Initialize() {
 }
 
 
+v8::internal::HashMap* Simulator::i_cache_ = NULL;
+
+
 Simulator::Simulator() {
+  if (i_cache_ == NULL) {
+    i_cache_ = new v8::internal::HashMap(&ICacheMatch);
+  }
   Initialize();
   // Setup simulator support first. Some of this information is needed to
   // setup the architecture state.
@@ -554,6 +650,9 @@ class Redirection {
         swi_instruction_((AL << 28) | (0xf << 24) | call_rt_redirected),
         fp_return_(fp_return),
         next_(list_) {
+    Simulator::current()->
+        FlushICache(reinterpret_cast<void*>(&swi_instruction_),
+                      Instr::kInstrSize);
     list_ = this;
   }
 
@@ -2342,6 +2441,7 @@ void Simulator::DecodeType6CoprocessorIns(Instr* instr) {
 
 // Executes the current instruction.
 void Simulator::InstructionDecode(Instr* instr) {
+  CheckICache(instr);
   pc_modified_ = false;
   if (::v8::internal::FLAG_trace_sim) {
     disasm::NameConverter converter;
@@ -2536,7 +2636,6 @@ uintptr_t Simulator::PopAddress() {
   return address;
 }
 
-
 } }  // namespace assembler::arm
 
 #endif  // __arm__
index 4ee9070ffd1d26639886983673529592e8f96b91..91614ea2d719346039896120d3fb71fb68dfabf6 100644 (file)
@@ -89,11 +89,43 @@ class SimulatorStack : public v8::internal::AllStatic {
 
 
 #include "constants-arm.h"
+#include "hashmap.h"
 
 
 namespace assembler {
 namespace arm {
 
+class CachePage {
+ public:
+  static const int LINE_VALID = 0;
+  static const int LINE_INVALID = 1;
+
+  static const int kPageShift = 12;
+  static const int kPageSize = 1 << kPageShift;
+  static const int kPageMask = kPageSize - 1;
+  static const int kLineShift = 2;  // The cache line is only 4 bytes right now.
+  static const int kLineLength = 1 << kLineShift;
+  static const int kLineMask = kLineLength - 1;
+
+  CachePage() {
+    memset(&validity_map_, LINE_INVALID, sizeof(validity_map_));
+  }
+
+  char* ValidityByte(int offset) {
+    return &validity_map_[offset >> kLineShift];
+  }
+
+  char* CachedData(int offset) {
+    return &data_[offset];
+  }
+
+ private:
+  char data_[kPageSize];   // The cached data.
+  static const int kValidityMapSize = kPageSize >> kLineShift;
+  char validity_map_[kValidityMapSize];  // One byte per line.
+};
+
+
 class Simulator {
  public:
   friend class Debugger;
@@ -162,6 +194,9 @@ class Simulator {
   // Pop an address from the JS stack.
   uintptr_t PopAddress();
 
+  // ICache checking.
+  static void FlushICache(void* start, size_t size);
+
  private:
   enum special_values {
     // Known bad pc value to ensure that the simulator does not execute
@@ -239,6 +274,11 @@ class Simulator {
   // Executes one instruction.
   void InstructionDecode(Instr* instr);
 
+  // ICache.
+  static void CheckICache(Instr* instr);
+  static void FlushOnePage(intptr_t start, int size);
+  static CachePage* GetCachePage(void* page);
+
   // Runtime call support.
   static void* RedirectExternalReference(void* external_function,
                                          bool fp_return);
@@ -276,6 +316,9 @@ class Simulator {
   int icount_;
   static bool initialized_;
 
+  // Icache simulation
+  static v8::internal::HashMap* i_cache_;
+
   // Registered breakpoints.
   Instr* break_pc_;
   instr_t break_instr_;
index 1c0b7312bc38bdc69d32892818bda5d809b634cb..68412677f49bb0c76a25f2a1bd337c59ef7c1159 100644 (file)
@@ -836,6 +836,9 @@ void Deserializer::ReadChunk(Object** current,
       case START_NEW_PAGE_SERIALIZATION: {
         int space = source_->Get();
         pages_[space].Add(last_object_address_);
+        if (space == CODE_SPACE) {
+          CPU::FlushICache(last_object_address_, Page::kPageSize);
+        }
         break;
       }
       case NATIVES_STRING_RESOURCE: {