QEMU Decodetree 語法介紹 (Part 1.)
2020-1-27
| 2025-1-12
本文字數 2975閱讀時長 8 分鐘
type
status
date
slug
summary
tags
category
icon
password
⚠️
本文所使用的 QEMU 版本為:v4.2.0
QEMU 在 decode 指令的時候,需要呼叫各平台所定義的 instruction decoders 來解析指令。如在 ARM 平台下,就定義了:disas_arm_insn()disas_thumb_insn()disas_thumb2_insn() 等來分別負責 ARM 32-bits 指令、ARM Thumb 指令及 ARM Thumb2 指令的解析。
而 Decodetree 則是由 Bastian Koppelmann 於 2017 年在 porting RISC-V QEMU 的時候所提出來的機制 (詳見:討論串 1討論串 2)。主因是過往的 instruction decoders (如:ARM) 都是採用一大包的 switch-case 來做判斷。不僅難閱讀,也難以維護。
因此 Bastian Koppelmann 就提出了 Decodetree 的機制,開發者只需要透過 Decodetree 的語法定義各個指令的格式,便可交由 Decodetree 來動態生成對應包含 switch-case 的 instruction decoder .c 檔。
Decodetree 特別適合像 RISC-V 這種具有固定指令格式的 ISA [註1]
  • 因為各欄位都在固定的位置,(如 RISC-V 的 opcode 都是固定在 bits[6..0] 的位置),各指令可重複使用的定義相較於其他的 ISA 來得多。
notion image
 
Decodetree 其實是由 Python script (scripts/decodetree.py) 所撰寫的。其規格說明文件可以參考:docs/devel/decodetree.rst,裡面有詳細定義了其語法的格式。QEMU 在編譯時,會呼叫 Decodetree,根據各平台所定義的 decode 檔,動態生成對應的 decoder。
  • 如 RISC-V 的 instruction decoders 就是被定義在:target/riscv/*.decode 中。其 Makefile.obj 就有如下的宣告:
 
Decodetree 的語法共分為:FieldsArgument SetsFormatsPatternsPattern Groups 五部分。本文將介紹如何透過 Decodetree 的語法,來動態生成一個指令的 decoder。

Fields

Field 定義如何取出一指令中,各欄位 (e.g. rd, rs1, rs2, imm) 的值。
其語法由 % 開頭,隨後緊接著一個 identifier 及零個或多個 unamed_field,並可再加上可選的 !function
  • identifier 可由開發者自訂,如:rdimm... 等。
  • unamed_field 定義了該欄位的所在位元。第一個數字定義了該欄位的 least-significant bit position,第二個數字則定義了該欄位的位元長度。另外可加上可選的 s 字元來標明在取出該欄位後,是否需要做 sign-extended
    • E.g. %rd 7:5 代表 rd 佔了指令中 bits 7 ~ bits 11 的位置 (insn[11:7]),共 5 bits。
  • !function 定義在擷取出該欄位的值後,所會再呼叫的 function。
 
Field (32-bits 指令) 最後會生成對應的 extract32()sextract32() 程式碼 [註2],以用來取得指令中各欄位的值,e.g:
Input
Generated Codes
%disp 0:s16
sextract(i, 0, 16)
%imm9 16:6 10:3
extract(i, 16, 6) << 3 | extract(i, 10, 3)
%disp12 0:s1 1:1 2:10
sextract(i, 0, 1) << 11 | extract(i, 1, 1) << 10 | extract(i, 2, 10)
%shimm8 5:s8 13:1 !function=expand_shimm8
expand_shimm8(sextract(i, 5, 8) << 1 | extract(i, 13, 1))
以 RISC-V 的 U-type 指令為例:
notion image
其中,imminsn[31:12]rdinsn[11:7],且 imm 需要做 sign-extended左移 12 位 (20-bit immediate is shifted left by 12 bits to form U immediates)。因此,如果我們要定義 RISC-V 的 U-type 指令,則可以宣告成:
最後會生成如下的程式碼:
(P.S. static void decode_insn32_extract_u() 是由 Format 定義所生成的,而 arg_u *a 則是由 Argument Set 定義所生成的,將會在後面的部分再做說明)
可以看到:
  • a->imm 是由 insn[31:12] 所取得並做 sign-extended,且會再呼叫 ex_shift_12()左移 12 個 bits
    • P.S. RISC-V 的 ex_shift_12() 是透過定義在 translate.cEX_SH 這個 macro 所展開的:
    • a->rd 是由 insn[11:7] 所取得。
     
    此外,在 Decodetree 的 spec. 中也有提到,我們可以透過只定義 !function 來直接呼叫該 function。在這種情況下,只有 DisasContext 會被傳入該 function。
    💡
    One may use !function with zero unnamed_fields. This case is called a parameter, and the named function is only passed the DisasContext and returns an integral value extracted from there.
    如 ARM Thumb 就有定義:
     
    未包含任何 unnamed_fields!functionField 會被視為錯誤。
    💡
    A field with no unnamed_fields and no !function is in error.

    Argument Sets

    Argument Set 定義用來保存從指令中所擷取出來各欄位的值。
    其語法由 & 開頭,隨後緊接著一個或多個的 identifier ,並可再加上可選的 !extern
    • identifier 可由開發者自訂,如:regsloadstore... 等。
    • !extern 則表示是否在其他地方已經由其他的 decoder 定義過。如果加上的話,就 不會 再次生成對應的 argument set struct

    Examples


    會生成以下的 argument set struct

    則會生成以下的 argument set struct

    因此,以剛剛的 RISC-V U-type 指令為例,我們需要從指令中擷取 immrd 欄位的值,可以宣告其 argument set 如下:
    最後會生成以下的 argument set struct
    argument set struct 會被傳入由 Format 定義所生成的 extract function:
    所傳入的arg_u 會保存從指令中擷取出的 immrd 欄位的值,待後續使用。

    Formats

    Format 定義了指令的格式 (如 RISC-V 中的 RISBUJ-type),並會生成對應的 decode function。
    其語法由 @ 開頭,隨後緊接著一個 identifier 及一個以上的 fmt_elt
    • identifier 可由開發者自訂,如:opropi... 等。
    • fmt_elt 則可以採用以下不同的語法:
      • fixedbit_elt 包含一個或多個 01.-,每一個代表指令中的 1 個 bit。
        • . 代表該 bit 可以用 0 或是 1 來表示。
        • - 代表該 bit 完全被忽略。
      • field_elt 可以用 Field 的語法來宣告。
        • E.g. ra:5rb:5lit:8
      • field_ref 有下列兩種格式 (以下範例參考上文所定義之 Field):
        • '%' identifier:直接參考一個被定義過的 Field
          • 如:%rd,會生成:
        • identifier '=' '%' identifier:直接參考一個被定義過的 Field,但透過第一個 identifier 來重新命名其所對應的 argument 名稱。此方式可以用來指定不同的 argument 名稱來參考至同一個 Field
          • 如:my_rd=%rd,會生成:
      • args_ref 指定所傳入 decode function 的 Argument Set。若沒有指定 args_ref 的話,Decodetree 會根據 field_eltfield_ref 自動生成一個 Argument Set。此外,一個 Format 最多只能包含一個 args_ref
    fixedbit_eltfield_ref 被定義時,該 Foramt 的所有的 bits 都必須被定義 (可透過 fixedbit_elt. 來定義各個 bits,空格會被忽略)。

    Examples

    定義了 opi 這個 Format,其中:
    • insn[31:26] 可為 01
    • insn[25:21] 為 ra
    • insn[20:13] 為 lit
    • insn[12] 固定為 1
    • insn[11:5] 可為 01
    • insn[4:0] 為 rc
    Format 會生成以下的 decode function:
    由於我們沒有指定 args_ref,因此 Decodetree 根據了 field_elt 的定義,自動生成了 arg_decode_insn320 這個 Argument Set

    以 RISC-V I-type 指令為例:
    定義了 i 這個 Format,其中:
    • insn[31:20] 為 imm,且為 sign-extended
    • insn[19:5] 為 rs1
    • insn[11:7] 為 rd
    此外,我們可以看到:
    • Format 指定了 Argument Set&i&i 中必須包含所有有用到的 arguments (也就是:immrs1rd)
    • imm 是透過重新命名的方式來參考 %imm_i 這個 Field
    此範例會生成以下的 decode function:
    相比於第一個範例,由於這次我們有指定 args_ref&i,因此對應的 arg_i 會被傳入 decode function。

    回到先前的 RISC-V U-type 指令,我們可以如同 I-type 指令定義其格式:
    定義了 u 這個 Format,其中:
    • insn[31:12] 為 imm,且為 sign-extended
    • insn[11:7] 為 rd
    會生成以下的 decode function:
    我們可以看到:
    • Format 指定了 Argument Set&u&u 中必須包含所有有用到的 arguments (也就是:immrd)
    • imm 是透過重新命名的方式來參考 %imm_u 這個 Field

    以上就是 DecodetreeFieldsArgument SetsFormats 語法的簡介。剩下的 PatternsPattern Groups 語法,詳見 Part 2.

    decodetree.py 參數

    • —-translate:translator function 的 prefix,預設為 trans。一旦指定後,translator function 的 scope 就不會再是 static
    • —-decode:decode function 的 prefix,預設為 decode,且 scope 為 static。一旦指定後,decode function 的 scope 就不會再是 static
    • —-static-decode:如同 —-decode,不過 decode function 的 scope 仍維持為 static
    • -o / —-output:指定生成的 decoder .c 檔路徑。
    • -w / —-insnwidth:指令長度,eg:32 or 16,預設為 32
    • —-varinsnwidth:指令為不定長度。
    • 最後一個參數為輸入的 decode 檔路徑。
     
    呼叫範例:

    附註

    • 註1:ARM 其實在 Decodetree 引進後,也有部分的 instructions 改採用 Decodetree 來動態生成對應的 instruction decoders
      • 如 Thumb 指令:target/arm/t32.decodetarget/arm/t16.decode
    • 註2:extract32()sextract32() 被定義在 include/qemu/bitops.h
    • QEMU
    • QEMU Decodetree 語法介紹 (Part 2.)Build QEMU environments
      Loading...