Support 2/4/6/8-byte instructions
authorAndrew Waterman <waterman@cs.berkeley.edu>
Fri, 5 Dec 2014 07:10:33 +0000 (23:10 -0800)
committerAndrew Waterman <waterman@cs.berkeley.edu>
Fri, 5 Dec 2014 07:32:54 +0000 (23:32 -0800)
Most of the complexity is in instruction address translation, since
instructions may span page boundaries.

hwacha/insns/vf.h
riscv/decode.h
riscv/mmu.h
riscv/processor.cc
spike/riscv-dis.cc

index fafb8b913de75927d18a07c4e09317c40cad2550..395ef1e684c62ff6e2997eaba5467dd1de6e1179 100644 (file)
@@ -10,7 +10,7 @@ vf_loop:
   if (VF_PC & 3)
     h->take_exception(HWACHA_CAUSE_VF_MISALIGNED_FETCH, VF_PC);
 
-  insn_t ut_insn = p->get_mmu()->load_insn(VF_PC).insn.insn;
+  insn_t ut_insn = p->get_mmu()->load_insn(VF_PC).insn;
 
   bool matched = false;
 
@@ -30,7 +30,7 @@ vf_loop:
     if (h->vf_active())
       goto vf_loop;
   } else {
-    fprintf(stderr, "vf block: 0x%016" PRIx64 " (0x%08" PRIx32 ") %s\n",
+    fprintf(stderr, "vf block: 0x%016" PRIx64 " (0x%08" PRIx64 ") %s\n",
       VF_PC, ut_insn.bits(), h->get_ut_disassembler()->disassemble(ut_insn).c_str());
     if (h->vf_active())
       npc = pc;
index 543080ddf4ca2c1a551d41b3f78f50afe1ff24ee..b325c5909357819d89cb3fa2f4f2e8c1e2ae9679 100644 (file)
@@ -44,26 +44,29 @@ const int NFPR = 32;
 #define FSR_NXA  (FPEXC_NX << FSR_AEXC_SHIFT)
 #define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
 
+typedef uint64_t insn_bits_t;
 class insn_t
 {
 public:
-  uint32_t bits() { return b; }
-  int32_t i_imm() { return int32_t(b) >> 20; }
-  int32_t s_imm() { return x(7, 5) + (xs(25, 7) << 5); }
-  int32_t sb_imm() { return (x(8, 4) << 1) + (x(25,6) << 5) + (x(7,1) << 11) + (imm_sign() << 12); }
-  int32_t u_imm() { return int32_t(b) >> 12 << 12; }
-  int32_t uj_imm() { return (x(21, 10) << 1) + (x(20, 1) << 11) + (x(12, 8) << 12) + (imm_sign() << 20); }
-  uint32_t rd() { return x(7, 5); }
-  uint32_t rs1() { return x(15, 5); }
-  uint32_t rs2() { return x(20, 5); }
-  uint32_t rs3() { return x(27, 5); }
-  uint32_t rm() { return x(12, 3); }
-  uint32_t csr() { return x(20, 12); }
+  insn_t() = default;
+  insn_t(insn_bits_t bits) : b(bits) {}
+  insn_bits_t bits() { return b; }
+  int64_t i_imm() { return int64_t(b) >> 20; }
+  int64_t s_imm() { return x(7, 5) + (xs(25, 7) << 5); }
+  int64_t sb_imm() { return (x(8, 4) << 1) + (x(25,6) << 5) + (x(7,1) << 11) + (imm_sign() << 12); }
+  int64_t u_imm() { return int64_t(b) >> 12 << 12; }
+  int64_t uj_imm() { return (x(21, 10) << 1) + (x(20, 1) << 11) + (x(12, 8) << 12) + (imm_sign() << 20); }
+  uint64_t rd() { return x(7, 5); }
+  uint64_t rs1() { return x(15, 5); }
+  uint64_t rs2() { return x(20, 5); }
+  uint64_t rs3() { return x(27, 5); }
+  uint64_t rm() { return x(12, 3); }
+  uint64_t csr() { return x(20, 12); }
 private:
-  uint32_t b;
-  uint32_t x(int lo, int len) { return b << (32-lo-len) >> (32-len); }
-  uint32_t xs(int lo, int len) { return int32_t(b) << (32-lo-len) >> (32-len); }
-  uint32_t imm_sign() { return xs(31, 1); }
+  insn_bits_t b;
+  uint64_t x(int lo, int len) { return (b >> lo) & ((insn_bits_t(1) << len)-1); }
+  uint64_t xs(int lo, int len) { return int64_t(b) << (64-lo-len) >> (64-len); }
+  uint64_t imm_sign() { return xs(63, 1); }
 };
 
 template <class T, size_t N, bool zero_reg>
@@ -76,9 +79,8 @@ public:
   }
   void write(size_t i, T value)
   {
-    data[i] = value;
-    if (zero_reg)
-      data[0] = 0;
+    if (!zero_reg || i != 0)
+      data[i] = value;
   }
   const T& operator [] (size_t i) const
   {
index c3d8f41b78ce458595add5b378e23b6b61b9f4e6..778e5faca197e32f1c687cc2d088954c5f768cb3 100644 (file)
@@ -25,10 +25,7 @@ const reg_t VA_BITS = VPN_BITS + PGSHIFT;
 struct insn_fetch_t
 {
   insn_func_t func;
-  union {
-    insn_t insn;
-    uint_fast32_t pad;
-  } insn;
+  insn_t insn;
 };
 
 struct icache_entry_t {
@@ -77,27 +74,49 @@ public:
   store_func(uint32)
   store_func(uint64)
 
+  inline size_t icache_index(reg_t addr)
+  {
+    // for instruction sizes != 4, this hash still works but is suboptimal
+    return (addr / 4) % ICACHE_SIZE;
+  }
+
   // load instruction from memory at aligned address.
-  inline icache_entry_t* access_icache(reg_t addr)
+  icache_entry_t* access_icache(reg_t addr) __attribute__((always_inline))
   {
-    reg_t idx = (addr / sizeof(insn_t)) % ICACHE_SIZE;
+    reg_t idx = icache_index(addr);
     icache_entry_t* entry = &icache[idx];
     if (likely(entry->tag == addr))
       return entry;
 
-    void* iaddr = translate(addr, sizeof(insn_t), false, true);
-    insn_fetch_t fetch;
-    fetch.insn.pad = *(decltype(fetch.insn.insn.bits())*)iaddr;
-    fetch.func = proc->decode_insn(fetch.insn.insn);
+    char* iaddr = (char*)translate(addr, 2, false, true);
+    insn_bits_t insn = *(uint16_t*)iaddr;
+
+    if (unlikely(insn_length(insn) == 2)) {
+      insn = (int16_t)insn;
+    } else if (likely(insn_length(insn) == 4)) {
+      if (likely((addr & (PGSIZE-1)) < PGSIZE-2))
+        insn |= (insn_bits_t)*(int16_t*)(iaddr + 2) << 16;
+      else
+        insn |= (insn_bits_t)*(int16_t*)translate(addr + 2, 2, false, true) << 16;
+    } else if (insn_length(insn) == 6) {
+      insn |= (insn_bits_t)*(int16_t*)translate(addr + 4, 2, false, true) << 32;
+      insn |= (insn_bits_t)*(uint16_t*)translate(addr + 2, 2, false, true) << 16;
+    } else {
+      static_assert(sizeof(insn_bits_t) == 8, "insn_bits_t must be uint64_t");
+      insn |= (insn_bits_t)*(int16_t*)translate(addr + 6, 2, false, true) << 48;
+      insn |= (insn_bits_t)*(uint16_t*)translate(addr + 4, 2, false, true) << 32;
+      insn |= (insn_bits_t)*(uint16_t*)translate(addr + 2, 2, false, true) << 16;
+    }
 
+    insn_fetch_t fetch = {proc->decode_insn(insn), insn};
     icache[idx].tag = addr;
     icache[idx].data = fetch;
 
-    reg_t paddr = (char*)iaddr - mem;
-    if (!tracer.empty() && tracer.interested_in_range(paddr, paddr + sizeof(insn_t), false, true))
+    reg_t paddr = iaddr - mem;
+    if (!tracer.empty() && tracer.interested_in_range(paddr, paddr + 1, false, true))
     {
       icache[idx].tag = -1;
-      tracer.trace(paddr, sizeof(insn_t), false, true);
+      tracer.trace(paddr, 1, false, true);
     }
     return &icache[idx];
   }
index 956b2d3a4ed1e4d0c41abb79b2a496893bbedf28..08ec2b79594c350be952b910b8ce0cda72626bd1 100644 (file)
@@ -129,13 +129,13 @@ static void commit_log(state_t* state, insn_t insn)
 #ifdef RISCV_ENABLE_COMMITLOG
   if (state->sr & SR_EI) {
     if (state->log_reg_write.addr) {
-      fprintf(stderr, "0x%016" PRIx64 " (0x%08" PRIx32 ") %c%2u 0x%016" PRIx64 "\n",
+      fprintf(stderr, "0x%016" PRIx64 " (0x%08" PRIx64 ") %c%2u 0x%016" PRIx64 "\n",
               state->pc, insn.bits(),
               state->log_reg_write.addr & 1 ? 'f' : 'x',
               state->log_reg_write.addr >> 1, state->log_reg_write.data);
     }
     else {
-      fprintf(stderr, "0x%016" PRIx64 " (0x%08" PRIx32 ")\n",
+      fprintf(stderr, "0x%016" PRIx64 " (0x%08" PRIx64 ")\n",
               state->pc, insn.bits());
     }
   }
@@ -153,8 +153,8 @@ inline void processor_t::update_histogram(size_t pc)
 
 static reg_t execute_insn(processor_t* p, reg_t pc, insn_fetch_t fetch)
 {
-  reg_t npc = fetch.func(p, fetch.insn.insn, pc);
-  commit_log(p->get_state(), fetch.insn.insn);
+  reg_t npc = fetch.func(p, fetch.insn, pc);
+  commit_log(p->get_state(), fetch.insn);
   p->update_histogram(pc);
   return npc;
 }
@@ -192,13 +192,13 @@ void processor_t::step(size_t n)
       while (instret++ < n)
       {
         insn_fetch_t fetch = mmu->load_insn(pc);
-        disasm(fetch.insn.insn);
+        disasm(fetch.insn);
         pc = execute_insn(this, pc, fetch);
       }
     }
     else while (instret < n)
     {
-      size_t idx = (pc / sizeof(insn_t)) % ICACHE_SIZE;
+      size_t idx = _mmu->icache_index(pc);
       auto ic_entry = _mmu->access_icache(pc);
 
       #define ICACHE_ACCESS(idx) { \
@@ -251,9 +251,9 @@ void processor_t::deliver_ipi()
 
 void processor_t::disasm(insn_t insn)
 {
-  // the disassembler is stateless, so we share it
-  fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx32 ") %s\n",
-          id, state.pc, insn.bits(), disassembler->disassemble(insn).c_str());
+  uint64_t bits = insn.bits() & ((1ULL << (8 * insn_length(insn.bits()))) - 1);
+  fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx64 ") %s\n",
+          id, state.pc, bits, disassembler->disassemble(insn).c_str());
 }
 
 void processor_t::set_pcr(int which, reg_t val)
index 89c4b742014ee45200b4c85c80035637c408f8ba..d0af451f737c309d901ef1b253074f6c51b513ef 100644 (file)
@@ -32,10 +32,8 @@ int main(int argc, char** argv)
         break;
 
       size_t numstart = start + strlen("DASM(");
-      uint32_t n = strtoul(&s[numstart], NULL, 16);
-
-      string dis = d.disassemble(*(insn_t*)&n);
-
+      insn_bits_t bits = strtoull(&s[numstart], NULL, 16);
+      string dis = d.disassemble(bits);
       s = s.substr(0, start) + dis + s.substr(end+1);
       start += dis.length();
     }