FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

自作RISC-V OoOコアの分岐予測性能解析用Performance Monitorの作成2

f:id:msyksphinz:20211004232850p:plain

ちまちまと自作RISC-Vコアを実装している。DhrystoneがPASSできるようになったが、まだまだ性能的にはひどいもんだ。

分岐予測の性能を向上させるためにBTBとBIMのデータ幅を増やしている。最初の方はかなり性能が上がってきた。

"               20386" : {
  "commit" : {  "cmt" :   188,   "inst" :   590,   "dead" :   188  },
  "icache" : {  "request" :   529,   "hit" :   496,   "miss" :    33  },
  "dcache" : {
    "port[0]" : {    "req" :     0,     "hit" :     0,     "miss" :     0,     "conflict" :     0    }
    "port[1]" : {    "req" :     0,     "hit" :     0,     "miss" :     0,     "conflict" :     0    }
    "port[2]" : {    "req" :     0,     "hit" :     0,     "miss" :     0,     "conflict" :     0    }
    "port[3]" : {    "req" :    62,     "hit" :    44,     "miss" :     8,     "conflict" :    10    }
    "port[4]" : {    "req" :   138,     "hit" :   116,     "miss" :     3,     "conflict" :    19    }
    "port[5]" : {    "req" :   107,     "hit" :    82,     "miss" :     1,     "conflict" :    24    }
  },
  "branch" : {    "execute" :   196,     "cmp" : { "execute" :   120, "hit" :   104 },     "uncond" : { "execute" :    76, "hit" :    16 },   },

しかしまだ途中で落ちてしまう。要デバッグだな。

20978 : 1361 : PC=[0000000080002bdc] (22,04) 0ffb7b13 andi    s6, s6, 255
GPR[22](15) <= 0000000000000042
20978 : 1362 : PC=[0000000080002be0] (22,08) 0767e163 bltu    a5, s6, pc + 98
20978 : 1363 : PC=[0000000080002be4] (22,10) 04300593 li      a1, 67
GPR[11](188) <= 0000000000000043
20982 : 1364 : PC=[0000000080002be8] (23,01) 0000855a c.mv    a0, s6
GPR[10](98) <= 0000000000000042
20982 : 1365 : PC=[0000000080002bea] (23,02) c68ff0ef jal     pc - 0xb98
GPR[01](52) <= 0000000080002bee
21014 : 1366 : PC=[0000000080002c42] (24,01) f4442783 lw      a5, -188(s0)
==========================================
Wrong PC: RTL = 0000000080002c42, ISS = 0000000080002052
==========================================
===============================
SIMULATION FINISH : FAIL (CODE=100)
===============================

自作RISC-V OoOコアの分岐予測性能解析用Performance Monitorの作成

f:id:msyksphinz:20211004232850p:plain

こういう波形見ると頭がバグってきますよね。

ちまちまと自作RISC-Vコアを実装している。DhrystoneがPASSできるようになったが、まだまだ性能的にはひどいもんだ。

特に分岐予測器を入れていないのがひどい。現在簡単なBTBとBimodal Predictorを実装しているが、適当に実装したところ思ったよりも性能が上がらなかった。 何が起きているのか見てみるために、もう少し詳細なログを取ってみる。

以下はその一端。比較分岐命令を57回実行して30回しかヒットしなかったって。プププ。(それよりもUnconditionalをノーケアなので全く予測できていないのがひどい)。

"               20386" : {
  "commit" : {  "cmt" :   108,   "inst" :   349,   "dead" :   225  },
  "icache" : {  "request" :   439,   "hit" :   409,   "miss" :    30  },
  "dcache" : {
    "port[0]" : {    "req" :     0,     "hit" :     0,     "miss" :     0,     "conflict" :     0    }
    "port[1]" : {    "req" :     0,     "hit" :     0,     "miss" :     0,     "conflict" :     0    }
    "port[2]" : {    "req" :     0,     "hit" :     0,     "miss" :     0,     "conflict" :     0    }
    "port[3]" : {    "req" :    43,     "hit" :    32,     "miss" :     8,     "conflict" :     3    }
    "port[4]" : {    "req" :    82,     "hit" :    68,     "miss" :     5,     "conflict" :     9    }
    "port[5]" : {    "req" :    63,     "hit" :    45,     "miss" :     1,     "conflict" :    17    }
  },
  "branch" : {    "execute" :   138,     "cmp" : { "execute" :    57, "hit" :    30 },     "uncond" : { "execute" :    81, "hit" :     0 },   },
}

やはり分岐命令が実行されたとき、その命令の場所、命令の種類、分岐結果(Taken / NotTaken)、そして予測の結果を一覧で表示すると分かりやすいだろう。早速やってみる。 分岐命令のパイプラインにロガーを突っ込んでひたすらファイルにダンプしていく。以下はそのコード。

integer bim_fp;
initial begin
  bim_fp = $fopen("bru_detail.log", "w");
end

always_ff @ (negedge i_clk, negedge i_reset_n) begin
  if (i_reset_n) begin
    if (ex3_br_upd_if.update) begin
      if (r_ex3_pipe_ctrl.op != OP__) begin
        $fwrite(bim_fp, "%t : pc_vaddr = %08x, target_addr = %08x, %s, bim=%1d, %s, DASM(0x%08x)\n",
                $time,
                r_ex3_issue.pc_addr,
                ex3_br_upd_if.target_vaddr,
                r_ex3_result ? "Taken   " : "NotTaken",
                ex3_br_upd_if.bim_value,
                ex3_br_upd_if.mispredict ? "Miss" : "Succ",
                r_ex3_issue.inst);
      end
    end
  end
end // always_ff @ (negedge i_clk, negedge i_reset_n)

final begin
  $fclose(bim_fp);
end

その結果、下記のようなログが撮れるようになった。命令の表記部分はspike-dasmで加工してある。

               13362 : pc_vaddr = 00800027d8, target_addr = 00800027da, NotTaken, bim=0, Succ, beqz    a5, pc + 20
               13370 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken   , bim=0, Miss, bne     a2, a5, pc - 6
               13374 : pc_vaddr = 00800027fe, target_addr = 0080002802, NotTaken, bim=0, Succ, bgeu    a0, a2, pc - 20
               13406 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken   , bim=1, Miss, bne     a2, a5, pc - 6
               13450 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken   , bim=2, Succ, bne     a2, a5, pc - 6
               13454 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken   , bim=2, Succ, bne     a2, a5, pc - 6
               13462 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken   , bim=2, Succ, bne     a2, a5, pc - 6
               13470 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken   , bim=2, Succ, bne     a2, a5, pc - 6
               13478 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken   , bim=2, Succ, bne     a2, a5, pc - 6
               13486 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken   , bim=2, Succ, bne     a2, a5, pc - 6
               13494 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken   , bim=3, Succ, bne     a2, a5, pc - 6
               13502 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken   , bim=3, Succ, bne     a2, a5, pc - 6
               13510 : pc_vaddr = 00800027e6, target_addr = 00800027e0, Taken   , bim=3, Succ, bne     a2, a5, pc - 6

最初の単純なループは問題なく予測できているようだ。ではどこで大幅な性能低下となっているのか。問題となるのは以下の部分だ。

               17750 : pc_vaddr = 008000290e, target_addr = 0080002900, Taken   , bim=1, Miss, beq     a5, a4, pc - 14
               17818 : pc_vaddr = 008000290c, target_addr = 008000290e, NotTaken, bim=1, Succ, beqz    a5, pc + 14
               17830 : pc_vaddr = 008000290e, target_addr = 0080002900, Taken   , bim=1, Miss, beq     a5, a4, pc - 14
               17902 : pc_vaddr = 008000290c, target_addr = 008000290e, NotTaken, bim=2, Miss, beqz    a5, pc + 14
               17910 : pc_vaddr = 008000290c, target_addr = 008000290e, NotTaken, bim=2, Miss, beqz    a5, pc + 14
               17962 : pc_vaddr = 008000290e, target_addr = 0080002900, Taken   , bim=2, Succ, beq     a5, a4, pc - 14
               17982 : pc_vaddr = 008000290c, target_addr = 008000290e, NotTaken, bim=1, Succ, beqz    a5, pc + 14
               17994 : pc_vaddr = 008000290e, target_addr = 0080002900, Taken   , bim=1, Miss, beq     a5, a4, pc - 14
               18070 : pc_vaddr = 008000290c, target_addr = 008000290e, NotTaken, bim=0, Succ, beqz    a5, pc + 14
               18082 : pc_vaddr = 008000290e, target_addr = 0080002900, Taken   , bim=0, Miss, beq     a5, a4, pc - 14

うえ、0x008000290eの分岐命令が全く学習されていない。そしてこれは「学習していない」のではなく、「0x008000290cの情報更新に引っ張られてしまい正しく学習結果を取得できていない」というのが正しい気がしてきた。 つまり、同じキャッシュライン上で2種類の分岐命令が存在している場合、現在は最初の分岐命令の学習結果のみを抽出してそのキャッシュラインの代表として使っているが、その結果別の分岐命令の学習結果が活用されていない。 この場合、同一キャッシュライン内であっても命令毎に学習結果を取得する必要があるだろう。分岐予測器が大きくなるがやむを得ない。

LLVMに新しいPassを追加してみる試行 (依存関係のあるAnalysisを呼ぶ方法2)

f:id:msyksphinz:20210922000636p:plain

Writing LLVM Passを読んで、LLVMのPassの追加方法が少しわかったので、今度は別のPassを追加してみよう。 参考にしたのは以下のPDF資料。ここでは、BBinLoopsと呼ばれるPassを新たに作成している。 - Writing an LLVM Pass

https://www.inf.ed.ac.uk/teaching/courses/ct/19-20/slides/llvm-2-writing_pass.pdf

前回の実装では最外ループしか検出できていなかったので、再帰的呼び出しを使ってネストループを解析する。

  void countBlocksInLoop (Loop *L, unsigned nesting) {
    unsigned numBlocks = 0;
    Loop::block_iterator bb;
    for (bb = L->block_begin(); bb != L->block_end(); ++bb) {
      numBlocks ++;
    }
    errs() << "Loop level " << nesting << " has " << numBlocks << " blocks\n";
    std::vector <Loop *>subLoops = L->getSubLoops();
    Loop::iterator j, f;
    for (j = subLoops.begin(), f = subLoops.end(); j != f; j++) {
      countBlocksInLoop(*j, nesting + 1);
    }
  }

  virtual bool runOnFunction (Function &F) {
    BBinLoopsCounter ++;

    LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();

    errs() << F.getName() + "\n";
    for (LoopInfo::iterator i = LI.begin(), e = LI.end(); i != e; i++) {
      countBlocksInLoop (*i, 0);
    }

    return false;
  }

Loopイテレータの中にさらにループが存在していると、countBlocksInLoop()を再度呼び出してループ解析を行う。

これで再度ビルドし実行してみる。

$ clang -O0 -emit-llvm loop_sample.c -c -o loop_sample.bc
$ ../bin/opt -enable-new-pm=0 -f -load ../lib/LLVMBBinLoops.so --bbinLoops < ./loop_sample.bc --disable-output
main
Loop level 0 has 10 blocks
Loop level 1 has 5 blocks

最初はループの回数と勘違いして最内ループが5ってどういうことだと思ったけど、これはループ内のブロックの数なので特に問題無いのか。

LLVMに新しいPassを追加してみる試行 (依存関係のあるAnalysisを呼ぶ方法)

f:id:msyksphinz:20210922000636p:plain

Writing LLVM Passを読んで、LLVMのPassの追加方法が少しわかったので、今度は別のPassを追加してみよう。 参考にしたのは以下のPDF資料。ここでは、BBinLoopsと呼ばれるPassを新たに作成している。 - Writing an LLVM Pass

https://www.inf.ed.ac.uk/teaching/courses/ct/19-20/slides/llvm-2-writing_pass.pdf

このPassは、関数内に入っているループを検出し、そのループ内に何個のBasicBlockが入っているかを検出する。 このときにループの解析は別のLoopInfo Passを使って解析することになる。 この依存関係の記述方法を学ぶということになる。

  • llvm/lib/Transforms/BBinLoops/BBinLoops.cpp
 struct BBinLoops : public FunctionPass {
   static char ID;  // Pass identification, replacement for typeid
   BBinLoops(): FunctionPass(ID) {}

   void getAnalysisUsage(AnalysisUsage &AU) const {
     AU.addRequired<LoopInfoWrapperPass>();
     AU.setPreservesAll();
   }

   virtual bool runOnFunction (Function &F) {
     BBinLoopsCounter ++;

     LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();

     int loopCounter = 0;
     errs() << F.getName() + "\n";
     for (LoopInfo::iterator i = LI.begin(), e = LI.end(); i != e; i++) {
       Loop *L = *i;
       int bbCounter = 0;
       loopCounter ++;
       for (Loop::block_iterator bb = L->block_begin(); bb != L->block_end(); ++bb) {
         bbCounter += 1;
       }
       errs() << "Loop ";
       errs() << loopCounter;
       errs() << ": #BBs = ";
       errs() << bbCounter;
       errs() << '\n';
     }

     return false;
   }
 };
 }

 char BBinLoops::ID = 0;
 static RegisterPass<BBinLoops> X("bbinLoops", "Counts basic block in loops");

まずポイントとなるのがgetAnalysisUsageで、ここに依存関係を記述する。 LoopInfo passに対する依存関係をaddRequiredに書けばよいのだが、どうも新しいPassManagerの影響でWrapperを被せたLoopInfoWrapperPassを呼び出す必要があるようだ。これがないとLoopInfo PassにIDが無いとエラーになる。

そして以下がLoopInfoの情報を使った実装となる。これはまだ未完成。ネストされたPassに対するループが実装できていない。

要点は、LoopInfo::iteratorを通じてLoopInfoからの情報を取得し、その中のBasicBlockの数を数えているということ。これで一応所望の実装は出来るようになっている。

   virtual bool runOnFunction (Function &F) {
     BBinLoopsCounter ++;

     LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();

     int loopCounter = 0;
     errs() << F.getName() + "\n";
     for (LoopInfo::iterator i = LI.begin(), e = LI.end(); i != e; i++) {
       Loop *L = *i;
       int bbCounter = 0;
       loopCounter ++;
       for (Loop::block_iterator bb = L->block_begin(); bb != L->block_end(); ++bb) {
         bbCounter += 1;
       }
       errs() << "Loop ";
       errs() << loopCounter;
       errs() << ": #BBs = ";
       errs() << bbCounter;
       errs() << '\n';
     }

     return false;
   }

以下のプログラムを使って試してみる。

int main (int argc, char **argv) {
  int i, j, t = 0;
  for (i = 0; i < 10; i++) {
    for (j = 0; j < 10; j++) {
      if ((i+j) % 7 == 0) {
        break;
      } else {
        t++;
      }
    }
  }
  printf ("%d\n", t);
  return 0;
}
$ clang -O0 -emit-llvm loop_sample.c -c -o loop_sample.bc
$ ../bin/opt -enable-new-pm=0 -f -load ../lib/LLVMBBinLoops.so --bbinLoops < ./loop_sample.bc --disable-output
main
Loop 1: #BBs = 10

まだループのネストに対応していないけれども、とりあえず外周のループについては検出できた。次はこれを拡張していく。

LLVMに新しいPassを追加してみる試行 (CountOp Passを追加する試行)

f:id:msyksphinz:20210922000636p:plain

Writing LLVM Passを読んで、LLVMのPassの追加方法が少しわかったので、今度は別のPassを追加してみよう。 参考にしたのは以下のPDF資料。ここでは、OpCountと呼ばれるPassを新たに作成している。 - Writing an LLVM Pass

https://www.inf.ed.ac.uk/teaching/courses/ct/19-20/slides/llvm-2-writing_pass.pdf

このPassは、LLVM IR中に含まれるOpCodeを数え上げ、それぞれのOpCodeがいくつあるのかを観察するためのものだ。ソースコードを眺めてみる。

  • llvm/lib/Transforms/CountOp/CountOp.cpp
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

#define DEBUG_TYPE "opCounter"

STATISTIC(OpCounter, "Count Number of Operations");

namespace {
// CountOp
struct CountOp : public FunctionPass {
  std::map<std::string, int> opCounter;
  static char ID;  // Pass identification, replacement for typeid
  CountOp () : FunctionPass(ID) {}

  virtual bool runOnFunction (Function &F) {
    ++OpCounter;
    errs() << "Function " << F.getName() << '\n';
    for (Function::iterator bb = F.begin(), e = F.end(); bb != e; ++bb) {
      for (BasicBlock::iterator i = bb->begin(), e = bb->end(); i != e; ++i) {
        if (opCounter.find(i->getOpcodeName()) == opCounter.end()) {
          opCounter[i->getOpcodeName()] = 1;
        } else {
          opCounter[i->getOpcodeName()] += 1;
        }
      }
    }
    std::map <std::string, int>::iterator i = opCounter.begin();
    std::map <std::string, int>::iterator e = opCounter.end();
    while (i != e) {
      errs() << "  " << i->first << ": " << i->second << '\n';
      i++;
    }
    errs() << '\n';
    opCounter.clear();
    return false;
  }
};
}

char CountOp::ID = 0;
static RegisterPass<CountOp> X("opCounter", "Counts opcodes per functions");

一つずつソースコードを読み解いていかなければならないだろう。

STATISTIC(OpCounter, "Count Number of Operations");

これは統計情報表示用のカウンタ。OpCounterという変数が定義されて、これをrunOnFunction()内でカウントアップすることでこのPassが何回呼ばれたかの統計を表示することができる。

struct CountOp : public FunctionPass {
  std::map<std::string, int> opCounter;
  static char ID;  // Pass identification, replacement for typeid
  CountOp () : FunctionPass(ID) {}

クラスにはchar IDが格納されているのだけれども、これはPassに対してIDを付けるため。 そしてopCounterはオペコードとその表示回数をカウントするためのmapだ。これがこのPassの本体みたいなものだ。

runOnFunction()の本体に入っていく。引数Function &Fから関数の情報を取り出すことになる。

Functionの中にはイテレータとしてBasicBlockが入っているようで、これを最初のループで繰り返している。 そしてBasicBlockの中には命令が入っているようで、これを次のforループで取り出している。 最内if文は、上記で定義したmapを探索して過去に同じOpcodeを登録しているかどうかをチェックしている。 登録していればカウンタを+1し、そうでなければカウンタを新規登録する。

  virtual bool runOnFunction (Function &F) {
    ++OpCounter;
    errs() << "Function " << F.getName() << '\n';
    for (Function::iterator bb = F.begin(), e = F.end(); bb != e; ++bb) {
      for (BasicBlock::iterator i = bb->begin(), e = bb->end(); i != e; ++i) {
        if (opCounter.find(i->getOpcodeName()) == opCounter.end()) {
          opCounter[i->getOpcodeName()] = 1;
        } else {
          opCounter[i->getOpcodeName()] += 1;
        }
      }
    }
/* ... 以下省略 ... */

最後に出力部分だ。mapの中身をすべて取り出して出力している。これで完成となる。

    std::map <std::string, int>::iterator i = opCounter.begin();
    std::map <std::string, int>::iterator e = opCounter.end();
    while (i != e) {
      errs() << "  " << i->first << ": " << i->second << '\n';
      i++;
    }
    errs() << '\n';
    opCounter.clear();
    return false;
}

クラスが完成したら次はPassの登録となる。以下はおまじないのような感じで覚えておけばよかろう。

char CountOp::ID = 0;
static RegisterPass<CountOp> X("opCounter", "Counts opcodes per functions");

llvm/lib/Transform/CMakeLists.txtに以下を追加して再度LLVMをビルドする。

diff --git a/llvm/lib/Transforms/CMakeLists.txt b/llvm/lib/Transforms/CMakeLists.txt
index dda5f6de11e3..42d46324ced6 100644
--- a/llvm/lib/Transforms/CMakeLists.txt
+++ b/llvm/lib/Transforms/CMakeLists.txt
@@ -9,3 +9,4 @@ add_subdirectory(Hello)
 add_subdirectory(ObjCARC)
 add_subdirectory(Coroutines)
 add_subdirectory(CFGuard)
+add_subdirectory(CountOp)

ビルドが成功すればOK。さっそく実行してみる。

./bin/opt -enable-new-pm=0 -f -load ./lib/LLVMCountOp.so -opCounter < ./sample/hello.bc > /dev/null
Function main
  call: 1
  ret: 1

動いた。正しく動作したようだ。

LLVMのPassManagerを試す

f:id:msyksphinz:20210922000636p:plain

Writing LLVM Passをまあまあ読んだので、いくつかサンプルプログラムを試してみたくなった。 ドキュメントに書いてあるhello Passを試してみよう。LLVMリポジトリの中でrelease/13.xブランチで試してみる。

llvm.org

LLVMrelease/13.xブランチをビルドすると、ビルドディレクトリに./lib/LLVMHello.soが生成されている。これを使って試してみるわけか。 まずはその通りにコマンドを打ってみる。

./bin/opt  -load ./lib/LLVMHello.so -hello < ./sample/hello.bc > /dev/null
./bin/opt: unknown pass name 'hello'
zsh: exit 1     ./bin/opt -load ./lib/LLVMHello.so -hello < ./sample/hello.bc > /dev/null

あれ?上手く動かない。

色々と調査して、どうやらLLVM13(というかもっと古いバージョンのLLVMから)新しいPass Managerになっているようで、いくつかオプションを調整しなければならないようだ。

./bin/opt -enable-new-pm=0 -f -load ./lib/LLVMHello.so -hello < ./sample/hello.bc > /dev/null
Hello: main

一応動作した。この-enable-new-pm=0-fがポイントのようだ。まずは動くことが確認できた。

riscv-arch-test (RISC-V Architecture Test SIG) を試す

riscv-arch-testは、riscv-testsとは別に管理されているRISC-Vのテストスイートである。

github.com

これまでちゃんと使ったことが無かったので、ここら辺で少し触ってみておこうと思う。 まずはSpikeでの動作確認から。

リポジトリをクローンする。

git clone https://github.com/riscv-non-isa/riscv-arch-test.git

つぎに別のディレクトリにSpikeをクローンしておく。

git clone https://github.com/riscv-software-src/riscv-isa-sim.git

riscv-arch-testの中でターゲットをSpikeに設定し、XLEN=64とする。このためには、riscv-isa-sim/arch_test_targetからMakefile.inをコピーしてriscv-arch-testの直下にコピーする。

その後、以下のように書き換えを行った。

diff --git a/Makefile.include b/Makefile.include
index bb81e0d..d2bfc04 100644
--- a/Makefile.include
+++ b/Makefile.include
@@ -1,23 +1,16 @@
 # set TARGETDIR to point to the directory which contains a sub-folder in the same name as the target
-export TARGETDIR ?= $(ROOTDIR)/riscv-target
+export TARGETDIR ?= /home/msyksphinz/work/riscv/msrh/riscv-isa-sim/arch_test_target

 # set XLEN to max supported XLEN. Allowed values are 32 and 64
-export XLEN               ?= 32
+export XLEN                                                    ?= 64

 # name of the target. Note a folder of the same name must exist in the TARGETDIR directory
 export RISCV_TARGET       ?= spike

これでmake compileを実行すると、まずはSpike用のバイナリが作成される。

============================ VARIABLE INFO ==================================
ROOTDIR: /home/msyksphinz/work/riscv/msrh/riscv-arch-test [origin: file]
WORK: /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work [origin: file]
TARGETDIR: /home/msyksphinz/work/riscv/msrh/riscv-isa-sim/arch_test_target [origin: file]
RISCV_TARGET: spike [origin: file]
XLEN: 64 [origin: file]
RISCV_DEVICE: I [origin: file]
=============================================================================

make -j1 \
        RISCV_TARGET=spike \
        RISCV_DEVICE=I \
        compile -C /home/msyksphinz/work/riscv/msrh/riscv-arch-test/riscv-test-suite/rv64i_m/I
make[1]: Entering directory '/home/msyksphinz/work/riscv/msrh/riscv-arch-test/riscv-test-suite/rv64i_m/I'
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/add-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/addi-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/addiw-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/addw-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/and-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/andi-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/auipc-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/beq-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/bge-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/bgeu-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/blt-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/bltu-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/bne-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/jal-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/jalr-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/lb-align-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/lbu-align-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/ld-align-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/lh-align-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/lhu-align-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/lui-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/lw-align-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/lwu-align-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/or-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/ori-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sb-align-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sd-align-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sh-align-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sll-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/slli-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/slliw-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sllw-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/slt-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/slti-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sltiu-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sltu-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sra-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/srai-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sraiw-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sraw-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/srl-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/srli-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/srliw-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/srlw-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sub-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/subw-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/sw-align-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/xor-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/xori-01.elf
Compile /home/msyksphinz/work/riscv/msrh/riscv-arch-test/work/rv64i_m/I/fence-01.elf
make[1]: Leaving directory '/home/msyksphinz/work/riscv/msrh/riscv-arch-test/riscv-test-suite/rv64i_m/I'