type
status
date
slug
summary
tags
category
icon
password
本文所使用的 QEMU 版本為:
v4.2.0Patterns
Pattern 實際定義了一個指令的 decode 方式。Decodetree 會根據 Patterns 的定義,來動態產生出對應的 switch-case decode 判斷式。其語法由使用者所定義的
identifier,隨後緊接著一個以上的 pat_elt。identifier可由開發者自訂,如:addl_r、addli... 等。
pat_elt則可以採用以下不同的語法:fixedbit_elt與在Format中fixedbit_elt的定義相同。field_elt與在Format中field_elt的定義相同。field_ref與在Format中field_ref的定義相同。args_ref與在Format中args_ref的定義相同。fmt_ref直接參考一個被定義過的Format。const_elt可以直接指定某一個argument的值。
由於
Pattern 實際定義了一個指令的 decode 方式,因此 所有的 bits 及 arguments (如果有參考 args_ref 的話) 都必須明確的被定義,如果在搭配了所有的 pat_elt 後還有未定義的 bits 或是 arguments 的話,Decodetree 便會報錯。此外,
Pattern 所產生出來的 decoder,最後還會呼叫對應的 translator function。translator function需開發者自行定義。
Examples
定義了
addl_i 這個指令的 Pattern,其中:- insn[31:26] 為
010000。
- insn[11:5] 為
0000000。
- 參考了
@opi這個Format。
- 由於
Pattern的 所有 bits 都必須明確的被定義,因此@opi必須包含其餘insn[25:12]及insn[4:0]的格式定義,否則 Decodetree 便會報錯。
最後
addl_i 的 decoder 還會呼叫 trans_addl_i() 這個 translator function。首先是 RISC-V 的
lui 及 auipc 指令:
會產生以下
lui 及 auipc 的 decoder:回顧到目前為止所介紹的:
Argument Sets:&u這個argument set包含了imm及rd這兩個arguments。
Fields:imm及rd分別位在 insn[31:12] 及 insn[11:7],且imm為sign-extended。最後在擷取出imm的值後,還會呼叫ex_shift_12()。
Formats:@u定義了 RISC-VU-type指令的格式- 參考了
&u這個Argument Set,因此 decode function 會傳入arg_u作為參數。 - insn[31:12] 參考了
imm_u這個Field(並重新命名為imm) - insn[11:7] 參考了
rd這個Field。
Patterns:lui的opcode(insn[6:0]) 為0010111,也就是0x17,在產生出來的switch-case中可以看到其對應的case。lui的 decoder 最後呼叫了trans_lui(),並傳入DisasContext及經由decode_insn32_extract_u()所解析出來的arg_u。auipc的opcode(insn[6:0]) 為0110111,也就是0x37,在產生出來的switch-case中可以看到其對應的case。auipc的 decoder 最後呼叫了trans_auipc(),並傳入DisasContext及經由decode_insn32_extract_u()所解析出來的arg_u。- P.S. 這邊由於 Decodetree 發現
lui及auipc可以共用decode_insn32_extract_u(),因此將其提到了switch-case之外。 Pattern定義了opcode(insn[6:0])。Format參考了imm(insn[31:12]) 及rd(insn[11:7])。
我們另外可以發現,
Pattern + Format 把所有的 32-bits 都給了明確的定義:如果有任何未明確定義的 bits 的話,Decodetree 便會報錯,例如如果我們將
lui 的 opcode 最高 2 個 bits (insn[6:5]) 由 01 改成 ..:Decodetree 在解析時,便會報錯:
Decodetree 提醒我們,insn[6:5] (
0x00000060) 尚未給出明確定義,並會顯示出其錯誤的行數。trans_lui() 和 trans_auipc() 被定義在 target/riscv/insn_trans/trans_rvi.inc.c:可以看到
trans_*() 負責實際指令的 business logics 及產生對應的 TCG codes。如同先前所介紹,
Patterns 的 pat_elt 也可以採用 field_elt 語法,如 RISC-V 的 fence 指令:- insn[27:24] 為
pred。
- insn[23:20] 為
succ。
- insn[14:12] 固定為
000。
- insn[6:0] 為
opcode(0001111)。
- 沒有參考任何的
Format。
- 剩下的 insn[31:28]、insn[19:15]、insn[11:7] 被宣告為
-,因此就算沒有被明確定義也沒有關係。
所生成
fence 的 decoder 如下:值得注意的是,雖然這次我們沒有參考任何的
Argument Set,但 Decodetree 還是替我們生成了一個包含 pred 和 succ 的 arg_decode_insn320 。trans_fence() 同樣是被定義在 trans_rvi.inc.c:Pattern Groups
Pattern Groups 由一個以上的 Patterns 所組成,其主要差別是不同 Patterns 之間的 bits 可以 overlap。當同組中有多個 Patterns 時,會依據該組中各 Pattern 的宣告順序依序判斷目前的指令是否符合其定義。除此之外,當符合的 Pattern 其 trans_*() 回傳值為 false 時,也會被視為 不相符,而繼續判斷該組中的下一個 Pattern。因此 Pattern Groups 非常適合將多個相似格式的指令給組成同一個 Pattern Group。原文說明如下:
Unlike ungrouped patterns, grouped patterns are allowed to overlap.
Conflicts are resolved by selecting the patterns in order. If all
of the
fixedbits for a pattern match, its translate function will
be called. If the translate function returns false, then subsequent
patterns within the group will be matched.各
Pattern Group 以 { 開頭,並以 } 結尾,且允許 nested pattern groups 的存在,其他語法皆與 Pattern 相同。Examples
會產生以下的 decoder:
當指令的值符合
nop 及 copy 這個內層 Pattern Group 時,會先判斷該指令是否符合 nop 指令的定義,且 trans_nop() 的回傳值為 true。否則的話,就會繼續判斷是否符合同組中的 copy 指令。若都不符,就會再判斷是否符合外層 Pattern Group 的 or 指令。若仍不符,才會回傳 false 表示 decode 失敗。與單純使用
Pattern 最大不同的是,當一 Pattern 的 trans_*() 回傳值為 false 時,不會直接回傳 false (代表 decode 失敗),而是會接續著判斷後續的 Patterns 是否相符。RISC-V Compressed-Extension 中的
c.ebreak、c.jalr、及 c.add 指令,由於這三個指令的格式非常相似,因此非常適合使用 Pattern Group 來定義:
RISC-V spec. 中定義:
C.EBREAK shares the opcode with the C.ADD instruction, but with rd and rs2 both zero, thus can also use the CR format.C.JALR is only valid when rs1≠x0; the code point with rs1=x0 corresponds to the C.EBREAK instruction.C.ADD is only valid when rs2≠x0; the code points with rs2=x0 correspond to the C.JALR and C.EBREAK instructions. The code points with rs2̸=x0 and rd=x0 are HINTs.c.ebreak、c.jalr、c.add 三個指令:- insn[15:13]、insn[12]、insn[1:0] 的值皆相同。
- 當 insn[11:7] 且 insn[6:2] 的值皆為
0(rs1=0且rs2=0) 時為c.ebreak指令。
- 當只有 insn[11:7] 的值為
0(rs1=0且rs2≠0) 時為c.jalr指令。
- 否則為
c.add指令 (rs1≠x0且rs2≠0)。
所生成的 decoder 如下:
當指令格式符合
c.ebreak、c.jalr、c.add 的 Pattern Group 時,會依序判斷該指令是否符合 c.ebreak、c.jalr、c.add 的定義以及其對應的 trans_*()。另外值得一提的是,在
c_jalr Format 和 jalr Pattern 中有分別指定其 imm 及 rd 的值為 0,所生成的 codes 也會分別在對應的地方將該值設為 0 (見 codes 註解說明)。以上就是 Decodetree 的語法說明。透過 Decodetree,我們不用再像以前以樣寫一大包的
switch-case 來 decode 指令。將不同類型的指令寫至不同的 decode 檔,不僅方便維護,閱讀起來也更為容易。