[llvm] [RISCV] Add scheduler definitions for SpacemiT-X60 (PR #137343)

Mikhail R. Gadelha via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 25 09:51:48 PDT 2025


================
@@ -0,0 +1,332 @@
+//=- RISCVSchedSpacemitX60.td - Spacemit X60 Scheduling Defs -*- tablegen -*-=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+//
+// Scheduler model for the SpacemiT-X60 processor based on documentation of the
+// C908 and experiments on real hardware (bpi-f3).
+//
+//===----------------------------------------------------------------------===//
+
+def SpacemitX60Model : SchedMachineModel {
+  let IssueWidth        = 2; // dual-issue
+  let MicroOpBufferSize = 0; // in-order
+  let LoadLatency       = 5; // worse case: >= 3
+  let MispredictPenalty = 9; // nine-stage
+
+  let CompleteModel = 0;
+
+  let UnsupportedFeatures = [HasStdExtZknd, HasStdExtZkne, HasStdExtZknh,
+                             HasStdExtZksed, HasStdExtZksh, HasStdExtZkr];
+}
+
+let SchedModel = SpacemitX60Model in {
+
+//===----------------------------------------------------------------------===//
+// Define processor resources for Spacemit-X60
+
+// Information gathered from the C908 user manual:
+let BufferSize = 0 in {
+  // The LSU supports dual issue for scalar store/load instructions
+  def SMX60_LS : ProcResource<2>;
+
+  // An IEU can decode and issue two instructions at the same time
+  def SMX60_IEU : ProcResource<2>;
+
+  def SMX60_FP : ProcResource<1>;
+}
+
+//===----------------------------------------------------------------------===//
+
+// Branching
+def : WriteRes<WriteJmp, [SMX60_IEU]>;
+def : WriteRes<WriteJal, [SMX60_IEU]>;
+def : WriteRes<WriteJalr, [SMX60_IEU]>;
+
+// Integer arithmetic and logic
+def : WriteRes<WriteIALU32, [SMX60_IEU]>;
+def : WriteRes<WriteIALU, [SMX60_IEU]>;
+def : WriteRes<WriteShiftImm32, [SMX60_IEU]>;
+def : WriteRes<WriteShiftImm, [SMX60_IEU]>;
+def : WriteRes<WriteShiftReg32, [SMX60_IEU]>;
+def : WriteRes<WriteShiftReg, [SMX60_IEU]>;
+
+// Integer multiplication
+let Latency = 4 in {
+  def : WriteRes<WriteIMul, [SMX60_IEU]>;
+  def : WriteRes<WriteIMul32, [SMX60_IEU]>;
+}
+
+// Integer division/remainder
+// Worst case latency is used.
+def : WriteRes<WriteIDiv32, [SMX60_IEU]> { let Latency = 12; }
+def : WriteRes<WriteIDiv, [SMX60_IEU]> { let Latency = 20; }
+def : WriteRes<WriteIRem32, [SMX60_IEU]> { let Latency = 12; }
+def : WriteRes<WriteIRem, [SMX60_IEU]> { let Latency = 20; }
+
+// Bitmanip
+def : WriteRes<WriteRotateImm, [SMX60_IEU]>;
+def : WriteRes<WriteRotateImm32, [SMX60_IEU]>;
+def : WriteRes<WriteRotateReg, [SMX60_IEU]>;
+def : WriteRes<WriteRotateReg32, [SMX60_IEU]>;
+
+def : WriteRes<WriteCLZ, [SMX60_IEU]>;
+def : WriteRes<WriteCLZ32, [SMX60_IEU]>;
+def : WriteRes<WriteCTZ, [SMX60_IEU]>;
+def : WriteRes<WriteCTZ32, [SMX60_IEU]>;
+
+def : WriteRes<WriteCPOP, [SMX60_IEU]>;
+def : WriteRes<WriteCPOP32, [SMX60_IEU]>;
+
+def : WriteRes<WriteORCB, [SMX60_IEU]>;
+
+def : WriteRes<WriteIMinMax, [SMX60_IEU]>;
+
+def : WriteRes<WriteREV8, [SMX60_IEU]>;
+
+def : WriteRes<WriteSHXADD, [SMX60_IEU]>;
+def : WriteRes<WriteSHXADD32, [SMX60_IEU]>;
+
+// Single-bit instructions
+def : WriteRes<WriteSingleBit, [SMX60_IEU]>;
+def : WriteRes<WriteSingleBitImm, [SMX60_IEU]>;
+def : WriteRes<WriteBEXT, [SMX60_IEU]>;
+def : WriteRes<WriteBEXTI, [SMX60_IEU]>;
+
+// Memory/Atomic memory
+let Latency = 3 in {
+  def : WriteRes<WriteSTB, [SMX60_LS]>;
+  def : WriteRes<WriteSTH, [SMX60_LS]>;
+  def : WriteRes<WriteSTW, [SMX60_LS]>;
+  def : WriteRes<WriteSTD, [SMX60_LS]>;
+  def : WriteRes<WriteFST16, [SMX60_LS]>;
+  def : WriteRes<WriteFST32, [SMX60_LS]>;
+  def : WriteRes<WriteFST64, [SMX60_LS]>;
+  def : WriteRes<WriteAtomicSTW, [SMX60_LS]>;
+  def : WriteRes<WriteAtomicSTD, [SMX60_LS]>;
+}
+
+let Latency = 5 in {
+  def : WriteRes<WriteLDB, [SMX60_LS]>;
+  def : WriteRes<WriteLDH, [SMX60_LS]>;
+  def : WriteRes<WriteLDW, [SMX60_LS]>;
+  def : WriteRes<WriteLDD, [SMX60_LS]>;
+  def : WriteRes<WriteFLD16, [SMX60_LS]>;
+  def : WriteRes<WriteFLD32, [SMX60_LS]>;
+  def : WriteRes<WriteFLD64, [SMX60_LS]>;
+}
+
+// Atomics
+let Latency = 5 in {
+  def : WriteRes<WriteAtomicLDW, [SMX60_LS]>;
+  def : WriteRes<WriteAtomicLDD, [SMX60_LS]>;
+  def : WriteRes<WriteAtomicW, [SMX60_LS]>;
+  def : WriteRes<WriteAtomicD, [SMX60_LS]>;
+}
+
+// Floating point units Half precision
+def : WriteRes<WriteFAdd16, [SMX60_FP]> { let Latency = 3; }
+def : WriteRes<WriteFMul16, [SMX60_FP]> { let Latency = 3; }
+def : WriteRes<WriteFMA16, [SMX60_FP]> { let Latency = 4; }
+def : WriteRes<WriteFSGNJ16, [SMX60_FP]> { let Latency = 3; }
+def : WriteRes<WriteFMinMax16, [SMX60_FP]> { let Latency = 3; }
+
+// Worst case latency is used
+let Latency = 7, ReleaseAtCycles = [7] in {
+  def :  WriteRes<WriteFDiv16, [SMX60_FP]>;
+  def :  WriteRes<WriteFSqrt16, [SMX60_FP]>;
+}
+
+// Single precision
+def : WriteRes<WriteFAdd32, [SMX60_FP]> { let Latency = 3; }
+def : WriteRes<WriteFMul32, [SMX60_FP]> { let Latency = 4; }
+def : WriteRes<WriteFMA32, [SMX60_FP]> { let Latency = 5; }
+def : WriteRes<WriteFSGNJ32, [SMX60_FP]> { let Latency = 3; }
+def : WriteRes<WriteFMinMax32, [SMX60_FP]> { let Latency = 3; }
+
+// Worst case latency is used
+let Latency = 10, ReleaseAtCycles = [10] in {
+  def :  WriteRes<WriteFDiv32, [SMX60_FP]>;
+  def :  WriteRes<WriteFSqrt32, [SMX60_FP]>;
+}
+
+// Double precision
+def : WriteRes<WriteFAdd64, [SMX60_FP]> { let Latency = 4; }
+def : WriteRes<WriteFMul64, [SMX60_FP]> { let Latency = 4; }
+def : WriteRes<WriteFMA64, [SMX60_FP]> { let Latency = 5; }
+def : WriteRes<WriteFSGNJ64, [SMX60_FP]> { let Latency = 3; }
+def : WriteRes<WriteFMinMax64, [SMX60_FP]> { let Latency = 3; }
+
+let Latency = 10, ReleaseAtCycles = [10] in {
+  def :  WriteRes<WriteFDiv64, [SMX60_FP]>;
----------------
mikhailramalho wrote:

llvm-exegesis suggests the same latency for single- and double-precision fdiv. I just double-checked locally:
```
$ taskset -c 2 ./llvm-riscv/bin/llvm-exegesis --mode=latency --opcode-name=FDIV_D -mtriple=riscv64 -mcpu=spacemit-x60 --min-instructions=10000
  - { key: latency, value: 10.463, per_snippet_value: 10.463, validation_counters: {} }
$ taskset -c 2 ./llvm-riscv/bin/llvm-exegesis --mode=latency --opcode-name=FDIV_S -mtriple=riscv64 -mcpu=spacemit-x60 --min-instructions=10000
  - { key: latency, value: 10.4673, per_snippet_value: 10.4673, validation_counters: {} }
```
Across multiple runs, the latencies for both instructions range from 10.46xx to 10.49xx

https://github.com/llvm/llvm-project/pull/137343


More information about the llvm-commits mailing list