From: Julian Seward <jseward@acm.org>
Date: Wed, 7 Aug 2024 10:43:53 +0200
Subject: Bug 1855960 - [MIPS64] Implement SummarizeTrapInstruction. r=jseward

Ref: https://bugzilla.mozilla.org/attachment.cgi?id=9365788&action=diff

Patch contributed by gur3n (Jiangjin Wang <the@salted.fish>)

Co-Authored-By: Zhao Jiazhong <zhaojiazhong-hf@loongson.cn>

Differential Revision: https://phabricator.services.mozilla.com/D195052
---
 js/src/wasm/WasmGenerator.cpp     |   7 +-
 js/src/wasm/WasmSummarizeInsn.cpp | 175 ++++++++++++++++++++++++++++++
 2 files changed, 179 insertions(+), 3 deletions(-)

diff --git a/js/src/wasm/WasmGenerator.cpp b/js/src/wasm/WasmGenerator.cpp
index d755c875..5ad977a 100644
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -1081,12 +1081,13 @@ UniqueCodeTier ModuleGenerator::finishCodeTier() {
 
 #if defined(DEBUG) && (defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) ||   \
                        defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_ARM) || \
-                       defined(JS_CODEGEN_LOONG64))
+                       defined(JS_CODEGEN_LOONG64) || \
+                       defined(JS_CODEGEN_MIPS64))
   // Check that each trapsite is associated with a plausible instruction.  The
   // required instruction kind depends on the trapsite kind.
   //
-  // NOTE: currently only enabled on x86_{32,64} and arm{32,64}.  Ideally it
-  // should be extended to riscv, loongson, mips.
+  // NOTE: currently enabled on x86_{32,64}, arm{32,64}, loongson64 and mips64.
+  // Ideally it should be extended to riscv64 too.
   //
   for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
     const TrapSiteVector& trapSites = metadataTier_->trapSites[trap];
diff --git a/js/src/wasm/WasmSummarizeInsn.cpp b/js/src/wasm/WasmSummarizeInsn.cpp
index 99834d6..ae5eb70 100644
--- a/js/src/wasm/WasmSummarizeInsn.cpp
+++ b/js/src/wasm/WasmSummarizeInsn.cpp
@@ -1444,6 +1444,181 @@ Maybe<TrapMachineInsn> SummarizeTrapInstruction(const uint8_t* insnAddr) {
   return Nothing();
 }
 
+// ================================================================ mips64 ====
+
+#  elif defined(JS_CODEGEN_MIPS64)
+
+Maybe<TrapMachineInsn> SummarizeTrapInstruction(const uint8_t* insnAddr) {
+  // Check instruction alignment.
+  MOZ_ASSERT(0 == (uintptr_t(insnAddr) & 3));
+
+  const uint32_t insn = *(uint32_t*)insnAddr;
+
+#    define INSN(_maxIx, _minIx) \
+      ((insn >> (_minIx)) & ((uint32_t(1) << ((_maxIx) - (_minIx) + 1)) - 1))
+
+  // MIPS64R2 instruction encoding document:
+  // https://scc.ustc.edu.cn/_upload/article/files/c6/06/45556c084631b2855f0022175eaf/W020100308600769158777.pdf#G254.1001018
+
+  // MacroAssembler::wasmTrapInstruction uses this to create SIGILL.
+  // teq zero, zero, 0x6
+  if (insn == 0x000001b4) {
+    return Some(TrapMachineInsn::OfficialUD);
+  }
+
+  // MIPS64 Encoding of the Opcode Field of memory access instructions.
+  // +--------+--------------------------------------------------------+
+  // |  bits  |                          28..26                        |
+  // +--------+------+------+------+-------+------+------+------+------+
+  // | 31..29 |  000 | 001  | 010  |  011  |  100 |  101 |  110 |  111 |
+  // +--------+------+------+------+-------+------+------+------+------+
+  // |   010  |      |      |      | COP1X |      |      |      |      |
+  // |   011  |      |      |  LDL |  LDR  |      |      |      |      |
+  // |   100  |  LB  |  LH  |  LWL |   LW  |  LBU |  LHU |  LWR |  LWU |
+  // |   101  |  SB  |  SH  |  SWL |   SW  |  SDL |  SDR |  SWR |      |
+  // |   110  |  LL  | LWC1 | LWC2 |       |  LLD | LDC1 | LDC2 |  LD  |
+  // |   111  |  SC  | SWC1 | SWC2 |       |  SCD | SDC1 | SDC2 |  SD  |
+  // +--------+------+------+------+-------+------+------+------+------+
+  if (INSN(31, 29) == 0b010) {
+    // MIPS64 COP1X Encoding of Function Field of memory access instructions.
+    // +--------+-----------------------------------------------------+
+    // |  bits  |                          2..0                       |
+    // +--------+-------+-------+-----+-----+-----+-------+-----+-----+
+    // |  5..3  |  000  |  001  | 010 | 011 | 100 |  101  | 110 | 111 |
+    // +--------+-------+-------+-----+-----+-----+-------+-----+-----+
+    // |   000  | LWXC1 | LDXC1 |     |     |     | LUXC1 |     |     |
+    // |   001  | SWXC1 | SDXC1 |     |     |     | SUXC1 |     |     |
+    // +--------+-------+-------+-----+-----+-----+-------+-----+-----+
+    switch (INSN(5, 0)) {
+      // lwxc1
+      case 0b000000:
+        return Some(TrapMachineInsn::Load32);
+      // ldxc1
+      case 0b000001:
+      // luxc1
+      case 0b000101:
+        return Some(TrapMachineInsn::Load64);
+      // swxc1
+      case 0b001000:
+        return Some(TrapMachineInsn::Store32);
+      // sdxc1
+      case 0b001001:
+      // suxc1
+      case 0b001101:
+        return Some(TrapMachineInsn::Store64);
+      default:
+        break;
+    }
+  } else if (INSN(31, 29) == 0b011) {
+    switch (INSN(28, 26)) {
+      // ldl
+      case 0b010:
+      // ldr
+      case 0b011:
+        return Some(TrapMachineInsn::Load64);
+      default:
+        break;
+    }
+  } else if (INSN(31, 29) == 0b100) {
+    switch (INSN(28, 26)) {
+      // lb
+      case 0b000:
+        return Some(TrapMachineInsn::Load8);
+      // lh
+      case 0b001:
+        return Some(TrapMachineInsn::Load16);
+      // lwl
+      case 0b010:
+      // lw
+      case 0b011:
+        return Some(TrapMachineInsn::Load32);
+      // lbu
+      case 0b100:
+        return Some(TrapMachineInsn::Load8);
+      // lhu
+      case 0b101:
+        return Some(TrapMachineInsn::Load16);
+      // lwr
+      case 0b110:
+      // lwu
+      case 0b111:
+        return Some(TrapMachineInsn::Load32);
+    }
+  } else if (INSN(31, 29) == 0b101) {
+    switch (INSN(28, 26)) {
+      // sb
+      case 0b000:
+        return Some(TrapMachineInsn::Store8);
+      // sh
+      case 0b001:
+        return Some(TrapMachineInsn::Store16);
+      // swl
+      case 0b010:
+      // sw
+      case 0b011:
+        return Some(TrapMachineInsn::Store32);
+      // sdl
+      case 0b100:
+      // sdr
+      case 0b101:
+        return Some(TrapMachineInsn::Store64);
+      // swr
+      case 0b110:
+        return Some(TrapMachineInsn::Store32);
+      // cache
+      case 0b111:
+        break;
+    }
+  } else if (INSN(31, 29) == 0b110) {
+    switch (INSN(28, 26)) {
+      // ll
+      case 0b000:
+      // lwc1
+      case 0b001:
+      // lwc2
+      case 0b010:
+        return Some(TrapMachineInsn::Load32);
+      // pref
+      case 0b011:
+        break;
+      // lld
+      case 0b100:
+      // ldc1
+      case 0b101:
+      // ldc2
+      case 0b110:
+      // ld
+      case 0b111:
+        return Some(TrapMachineInsn::Load64);
+    }
+  } else if (INSN(31, 29) == 0b111) {
+    switch (INSN(28, 26)) {
+      // sc
+      case 0b000:
+      // swc1
+      case 0b001:
+      // swc2
+      case 0b010:
+        return Some(TrapMachineInsn::Store32);
+      // reserved encoding
+      case 0b011:
+        break;
+      // scd
+      case 0b100:
+      // sdc1
+      case 0b101:
+      // sdc2
+      case 0b110:
+      // sd
+      case 0b111:
+        return Some(TrapMachineInsn::Store64);
+    }
+  }
+
+#    undef INSN
+  return Nothing();
+}
+
 // ================================================================== none ====
 
 #  elif defined(JS_CODEGEN_NONE)
