FPGA開発日記

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

サイクル精度シミュレータSniperでRegion of Interestの領域をベンチマーク側から設定する方法

サイクル精度シミュレータSniperは、トレースファイルをベースとしたサイクル精度解析シミュレータだ。 RISC-Vのサポートにおいては、SpikeからSIFTファイルを生成し、それをSniperに加えることでサイクル計算を行う。

特定の領域のみのサイクル情報を取得したいのだけれども、その設定がよくわからない。 いろいろ調べていると、ベンチマークにAnnotationを挿入することで何とかなる機能を見つけた。

http://snipersim.org/w/Multiple_regions_of_interest

上記の記事によれば、測定対象範囲に以下のようなマクロを挿入する。

(setup code)
 
SimMarker(1, 1001);
(region 1001 code)
SimMarker(2, 1001);
 
(uninteresting code)
 
SimMarker(1, 2345);
(region 2345 code)
SimMarker(2, 2345);
 
(fini code)

SimMaker(1, RegionID)は測定対象範囲の開始、SimMaker(2, RegionID)は測定対象範囲の終了地点を意味する。この範囲のみの情報を取得してくれるということになるが、これの実体はsinper/include/sim_api.hに定義されているマクロで、

#if defined(__aarch64__)
#define SimMagic2(cmd, arg0, arg1) ({        \
   unsigned long _cmd = (cmd), _arg0 = (arg0), _arg1 = (arg1), _res; \
   asm volatile (           \
   "mov x1, %[x]\n"         \
   "\tmov x2, %[y]\n"       \
   "\tmov x3, %[z]\n"       \
   "\tbfm x0, x0, 0, 0\n"   \
   : [ret]"=r"(_res)        \
   : [x]"r"(_cmd),          \
     [y]"r"(_arg0),          \
     [z]"r"(_arg1)          \
   : "x1", "x2", "x3"                   \
   );                       \
})
#define SimMarker(arg0, arg1)     SimMagic2(SIM_CMD_MARKER, arg0, arg1)

という感じで、AArch64の場合はレジスタに特定の値を設定して特殊な命令を呼ぶ、という形式になる。

で、この特殊な命令を検出すると、レジスタの値を読み取って測定開始のハンドラが呼ばれるという形のようだ。

  • sniper/frontend/dr-frontend/dr_frontend.cc
    IF_AARCH64
    (
      if(instr_get_opcode(instr) == OP_bfm &&
         instr_reg_in_src(instr, DR_REG_X0) &&
         instr_reg_in_dst(instr, DR_REG_X0) &&
         instr_num_srcs(instr) == 4 &&
         opnd_get_immed_int(instr_get_src(instr, 2)) == 0 &&
         opnd_get_immed_int(instr_get_src(instr, 3)) == 0 )
      {
        // Insert clean call to handle magic instructions
        dr_insert_clean_call( drcontext, bb, instr,
                              (void *)magic_clean_call, false, 0);      }
    )
  IF_AARCH64
  (
    // Invoke the handleMagic callback with command (X1) and arguments (X2, X3)
    m_callbacks->handleMagic(map_threadids[dr_get_thread_id(drcontext)],
                           reg_get_value(DR_REG_X1, &mc),
                           reg_get_value(DR_REG_X2, &mc),
                           reg_get_value(DR_REG_X3, &mc) );
  )

だとすると、これをRISC-V対応でどのように実現するか、という話になるのだが、一つの考え方として、

  • 使わない命令 add x0, x0, x0とかを、トリガー命令に使用する
  • 引数に最大3つのレジスタが必要となる。x10, x11, x12が最適だがこれを書きつぶしてしまうのは良くない。
  • そこで、一時的に対比する領域を設けておく。

長ったらしいがこんな感じで、save0, save1, save2に現在のx10, x11, x12の値を保存しておき、ハンドラを呼び出した後に復帰するということにしておく。とりあえずこれで何が起きるか調査してみよう (時間切れ)。

unsigned long save0;
unsigned long save1;
unsigned long save2;

#define SimMagic2(cmd, arg0, arg1) ({        \
   unsigned long _cmd = (cmd), _arg0 = (arg0), _arg1 = (arg1), _res; \
   asm volatile (                                                    \
       "\tsd x10, %[save0]\n"           \
       "\tsd x11, %[save1]\n"           \
       "\tsd x12, %[save2]\n"           \
       "\tli x10, %[x]\n"               \
       "\tli x11, %[y]\n"               \
       "\tli x12, %[z]\n"               \
       "\tadd x0, x0, x0\n"             \
       "\tld x10, %[save0]\n"           \
       "\tld x11, %[save1]\n"           \
       "\tld x12, %[save2]\n"           \
       : [ret]"=r"(_res)                \
       : [x]"i"(_cmd),                  \
         [y]"i"(_arg0),                 \
         [z]"i"(_arg1),                 \
         [save0]"m"(save0),              \
         [save1]"m"(save1),              \
         [save2]"m"(save2)              \
       : "x1", "x2", "x3"               \
                                                                     ); \
    })