TableGen Programmer's Reference
日本語の資料が無くて辛かったので,公式ドキュメントの意訳・要約をしていきます.
agenda
- とは
- Lexical_Analysis
- Type
- Values_and_Expressions
- Statement
- Additional_Details
- Using_Classes_as_Subroutines
- Preprocessing_Facilities
- Appendix: Bang_Operators
- Appendix: Paste_Operator_Examples
- Appendix: Sample_Record
とは
TableGenはLLVM project内で使われるDSLで,主にターゲットマシンに依存したコード等の生成に用いられている.
TableGen Overview TableGen Programmer’s Reference
Lexical_Analysis
include
もある.
全体的に構文はPythonのものを模倣している.
- + [ ] { } ( ) < > : ; . ... = ? #
Literals
数値リテラル
TokInteger ::= DecimalInteger | HexInteger | BinInteger
DecimalInteger ::= ["+" | "-"] ("0"..."9")+
HexInteger ::= "0x" ("0"..."9" | "a"..."f" | "A"..."F")+
BinInteger ::= "0b" ("0" | "1")+
DecimalInteger
では+
や-
が単項演算子としては扱われない.
文字列リテラル
TokString ::= '"' (non-'"' characters and escapes) '"'
TokCode ::= "[{" (shortest text not containing "}]") "}]"
エスケープは\
で行う
\\ \' \" \t \n
Identifiers
ualpha ::= "a"..."z" | "A"..."Z" | "_"
TokIdentifier ::= ("0"..."9")* ualpha (ualpha | "0"..."9")*
TokVarName ::= "$" ualpha (ualpha | "0"..."9")*
case-sensitive
予約語
assert bit bits class code
dag def else false foreach
defm defset defvar field if
in include int let list
multiclass string then true
Bang operators
BangOperator ::= one of
!add !and !cast !con !dag
!empty !eq !filter !find !foldl
!foreach !ge !getdagop !gt !head
!if !interleave !isa !le !listconcat
!listsplat !lt !mul !ne !not
!or !setdagop !shl !size !sra
!srl !strconcat !sub !subst !substr
!tail !xor
Bang演算子とは別に,condもある
CondOperator :: =!cond
Include files
IncludeDirective ::= "include" TokString
Cのincludeと同じ
PreprocessorDirective ::= "#define" | "#ifdef" | "#ifndef"
等のプリプロセッサディレクティブもある
Type
型システムもある.
Type ::= "bit" | "int" | "string" | "dag"
| "bits" "<" TokInteger ">"
| "list" "<" Type ">"
| ClassID
ClassID ::= TokIdentifier
bit
bit
は0 or 1のbool値
int
int
は64bitの整数
string
string
は任意長の文字列
bits
bits
は任意の長さn
bitの固定サイズの整数で,各bitに個別にアクセスが可能.
list
list
は<type>
で指定された型の要素を持つリスト.zero-indexed
dag
nestable directed acyclic graph(有向非巡回グラフ)
各ノードはOperatorと0個以上の引数(またはオペランド)を持つ
ClassID
型コンテキストでクラス名を指定する際には,定義された値の型が指定されたクラスのサブクラスでなければいけない.
例えば,list<Register>
にはRegister
クラスから派生した定義のみを含める事ができる.
Values_and_Expressions
TableGenのステートメントには,値が必要なコンテキストが多くある.
Value ::= SimpleValue ValueSuffix*
| Value "#" Value
ValueSuffix ::= "{" RangeList "}"
| "[" RangeList "]"
| "." TokIdentifier
RangeList ::= RangePiece ("," RangePiece)*
RangePiece ::= TokInteger
| TokInteger "..." TokInteger
| TokInteger "-" TokInteger
| TokInteger TokInteger
RangePiece
が特殊なのは-
がTokInteger
に含まれている為で,1-5
が"1", "-", "5"
ではなく,"1", "-5"
として扱われるから.
範囲指定には-
ではなく...
を用いる事が推奨されている.
Simple_Values
SimpleValue
には様々な形がある
SimpleValue ::= TokInteger | TokString+ | TokCode
Valueは,整数リテラル,文字列リテラル,またはコードリテラルで,隣接する複数の文字列リテラルはCやC++のように連結される. また,コードリテラルはparse後には文字列となる.
SimpleValue2 ::= "true" | "false"
true
とfalse
はCのように基本的に1と0のalias.
parse後には整数となる.
SimpleValue3 ::= "?"
?
は初期化されていない値を示す.
SimpleValue4 ::= "{" [ValueList] "}"
ValueList ::= ValueListNE
ValueListNE ::= Value ("," Value)*
bits<n>
の初期化に用いられる.合計でn個のbitを示す必要がある.
SimpleValue5 ::= "[" ValueList "]" ["<" Type ">"]
list
の初期化に用いられる.Type
で型を示す事ができるが,そうでない場合は推論が行われる.
SimpleValue6 ::= "(" DagArg [DagArgList] ")"
DagArgList ::= DagArg ("," DagArg)*
DagArg ::= Value [":" TokVarName] | TokVarName
DAGの初期化に用いられる.最初のDagArg
はDAGの演算子と呼ばれ,レコードでなければいけない.
SimpleValue7 ::= TokIdentifier
parse後は,識別子で指定されたエンティティの値となる.
以下は例
class
のテンプレート引数
class Foo <int Bar> {
int Baz = Bar;
}
class
またはmulticlass
の定義における暗黙のテンプレート引数class
内のローカルなフィールド
class Foo {
int Bar = 5;
int Baz = Bar;
}
- レコードの定義に用いる名前
def Bar : SomeClass {
int X = 5;
}
def Foo {
SomeClass Baz = Bar;
}
- レコードの定義内のローカルなフィールド
def Foo {
int Bar = 5;
int Baz = Bar;
}
レコードの親クラスから継承されたフィールドにも同様にアクセスできる.
multiclass
のテンプレート引数
multiclass Foo <int Bar> {
def : SomeClass<Bar>;
}
defvar
やdefset
で定義された変数foreach
のiterationに用いる変数
foreach i = 0...5 in
def Foo#i;
SimpleValue8 ::= ClassID "<" ValueListNE ">"
新しく無名のレコード定義を作成し,そのパース後の値は作成されたレコードとなる. このようにしてクラスを呼び出す事で,サブルーチンの様な機能を提供できる.
SimpleValue9 ::= BangOperator ["<" Type ">"] "(" ValueListNE ")"
| CondOperator "(" CondClause ("," CondClause)* ")"
CondClause ::= Value ":" Value
BangOperator
は他のSimpleValue
には無い機能を持つ.
!cond
を除き,BangOperator
は()
で囲まれた引数となるリストを受け取り,それらの引数に対して何らかの関数を実行し,値を生成する.
!cond
は:
で区切られた引数のペアのリストを受け取る.
関数マクロみたいな感じ
Suffixed_values
上記のSimple Valueの値は,特定のsuffixで指定する事ができる.
あるValueのsubvalue
を求める為にsuffixを用いる.以下は例
value{17}
value
(整数値)の17bit目
value{8...15}
value
の8~15bit.{15...8}
の様に指定する事で逆順にもできる.
value[4]
listの4つめの要素(zero-indexed)
value[4...7,17,2...3,4]
listの値をスライスした新しいlist.
この場合,listには,要素4,5,6,7,17,2,3,4
が含まれる.
要素は複数回,任意の順序で含める事ができる.
value.field
指定したレコード値の指定したフィールドの値.
The_paste_operator
paste演算子#
はTableGen唯一の中置演算子.
この演算子では,文字列やリストを連結できる.
Def
またはDefm
文でレコードを生成するときにも使用できるが,その場合は文字列を構築する必要がある.
オペランドが未定義の名前(TokIdentifier
)やグローバルなDefvar
, Defset
の場合,そのまま文字列として扱われる.グローバルな名前は展開されない.
paste演算子は他のすべての値の式で使用し,文字列またはリストを構築する事ができる.
右辺値が未定義の名前またはグローバルな名前の場合は前述の通りそのままの文字列として扱われるが,左辺値は通常通り扱われる.
Statement
以下の文はTableGenのソースファイルのトップレベルに現れることがある.
TableGenFile ::= Statement*
Statement ::= Assert | Class | Def | Defm | Defset | Defvar
| Foreach | If | Let | MultiClass
class
define an abstract record class
class
文は他のクラスやレコードが参照できる抽象的なレコードクラスを定義する.
Class ::= "class" ClassID [TemplateArgList] RecordBody
TemplateArgList ::= "<" TemplateArgDecl ("," TemplateArgDecl)* ">"
TemplateArgDecl ::= Type TokIdentifier ["=" Value]
クラスはテンプレート引数のリストでパラメータ化する事ができ,その値はクラスのレコード本体で使用できる.
テンプレート引数に=
でデフォルトの値が指定されていない場合,その引数は初期化されない.
(?
が指定されている場合はオプションで,指定する必要は無い)
また,宣言において,総ての必須テンプレート引数はオプション引数より先に無ければいけない.
また,テンプレート引数のデフォルト値は,左から右に評価される.
RecordBody
は,現在のクラスが継承する親クラスのリストや,フィールド定義等を含める事が出来る.
クラスCが別のクラスDを継承した場合,Dのフィールドは事実上Cのフィールドに統合される.
クラス文は,以下のいずれかが成り立つ時,クラスを定義したとみなされる.
TemplateArgList
が存在するParentClassList
がRecordBody
に存在するRecordBody
のBody
が存在し,空でない
空のTemplateArgList
とRecordBody
を指定する事で,空のクラスを定義する事ができる.
これは,前方宣言の制限された値として使用できる.
前方宣言されたクラスから派生したレコードは,そのクラスからフィールドを継承しない.
これらのレコードは,宣言がparseされる時に構築されるため,クラスが実際に定義される前に構築されるからである.
すべてのクラスには,NAME
という暗黙のテンプレート引数があり,これはそのクラスを継承するDef
やDefm
の名前に束縛される.
クラスが無名のレコードに継承されている場合,名前は特定されないが,グローバルに一意である.
Record Bodies
Record Bodyはクラス定義とレコード定義の両方に現れる.
Record Bodyには,親クラスのリストを含める事ができ,現在のクラスやレコードがフィールドを継承するクラスを指定できる.
レコード本体には,クラスやレコードのフィールドの仕様を記載した定義の本体も含まれる.
RecordBody ::= ParentClassList Body
ParentClassList ::= [":" ParentClassListNE]
ParentClassListNE ::= ClassRef ("," ClassRef)*
ClassRef ::= (ClassID | MultiClassID) ["<" [ValueList] ">"]
MultiClassID
を含むParentClassList
は,defm
文のクラスのリストでのみ有効で,IDはマルチクラスの名前でなければならない.
Body ::= ";" | "{" BodyItem* "}"
BodyItem ::= (Type | "code") TokIdentifier ["=" Value] ";"
| "let" TokIdentifier ["{" RangeList "}"] "=" Value ";"
| "defvar" TokIdentifier "=" Value ";"
| Assert
ボディ内のフィールド定義では,クラスやレコードに含まれるフィールドを指定する.
初期値が指定されていない場合,そのフィールドの値は初期化されない.
型は必ず指定する必要があり,ここでは型推論は行われない.
"code"
は,フィールドがコードである文字列値を持つことを強調する為に用いる事が出来る.
let
は,フィールドを新しい値にセットするのに使用する.
これはボディで直接定義されたフィールドや,親クラスから継承されたフィールドに対して行うことが出来る.
bits<n>
なフィールドの特定のビットをリセットするためにRangeList
を指定する事もできる.
defvar
では,変数を定義し,その値をボディ内の他の値の式で使用する事ができる.
この変数はフィールドでなく,定義されたクラスやレコードのフィールドにはならない.
変数は,ボディの処理中に一時的な値を保持する為に用意されている.
C2クラスがC1クラスを継承すると,C1クラスのすべてのフィールド定義を取得する. それらの定義がC2クラスにマージされると,C2からC1に渡されたテンプレート引数はすべて定数に置き換えられる. つまり,C1で定義されたレコードの抽象フィールドは,C2にマージされる前にテンプレート引数で拡張される.
def
def
文は,新しく具体レコードを定義する.
Def ::= "def" [NameValue] RecordBody
NameValue ::= Value (parsed in a special mode)
NameValue
はオプションで,指定された場合,未定義の識別子を文字列リテラルとしてparseする特殊なモードでparseが行われる.
また,defvar
やdefset
で定義されるグローバルな識別子はparseされない.レコード名にはnull文字を指定できる.
NameValue
が与えられていない場合,レコードは匿名となる.匿名レコードの名前は特定されないが,グローバルに一意である.
def
がmulticlass
文の中に現れた場合,特殊な処理が行われる.(multiclass)
レコードは,レコード本体の先頭にParentClassList
を指定する事で,1つ以上のクラスを継承できる.
親クラスのすべてのフィールドがレコードに追加され,複数の親クラスが同じフィールドを提供する場合,レコードは最後のクラスのフィールドの値で更新される.
特殊なケースとして,レコードの名前をテンプレート引数として,そのレコードの親クラスに渡すことができる.
例えば,
class A <dag d> {
dag the_dag = d;
}
def rec1 : A<(ops rec1)>
DAG(ops rec1)
はクラスAのテンプレート引数として渡される.DAG
には,定義されるレコードであるrec
が含まれている.
新しいレコードを定義する手順はHow records are builtを参照
例は次節
Examples: classes and records
ここでは,1つのクラスと2つのレコード定義を持つ単純なTableGenファイルを示す.
class C {
bit V = true;
}
def X : C;
def Y : C {
let V = false;
string Greeting = "Hello!";
}
まず,抽象クラスCを定義する.このクラスにはV
というbit
のフィールドがあり,true
で初期化されている.
次に,クラスCから派生した2つのレコードが定義される.これらはクラスCを親クラスとしている為,両方ともV
というフィールドを持つ.
レコードYは,さらにGreeting
というフィールドを定義しており,これは"Hello!"で初期化されている.
また,レコードYは
Vを
false`で上書きしている.
クラスは,複数のレコードに共通する機能を一箇所に集めるのに便利. 共通のフィールドをデフォルト値で初期化できるが,そのクラスを継承したレコードはデフォルト値を上書きできる.
TableGenは,パラメータ化されたクラスだけでなく,パラメータ化されていないクラスの定義もサポートしている. パラメータ化されたクラスは,他のクラスやレコードの親クラスとして指定された時に,束縛される変数宣言のリストを指定する
class FPFormat <bits<3> val> {
bits<3> Value = val;
}
def NotFP : FPFormat<0>;
def ZeroArgFP : FPFormat<1>;
def OneArgFP : FPFormat<2>;
def OneArgFPRW : FPFormat<3>;
def TwoArgFP : FPFormat<4>;
def CompareFP : FPFormat<5>;
def CondMovFP : FPFormat<6>;
def SpecialFP : FPFormat<7>;
FPFormat
クラスは,ある種の列挙型として機能する.
このクラスは,3bitの数値を保持するValue
という1つのフィールドを提供する.
テンプレート引数のval
はValue
へ値を設定するのに用いられる.
8つのレコードはそれぞれFPFormat
を親クラスとするよう定義されている.
テンプレート引数は,<>
で渡され,各レコードは与えられた値でValue
フィールドを継承する.
次に,テンプレート引数を持つクラスのより複雑な例を示す.
まず,上記のFPFormat
と同様のクラスを定義する.
このクラスはテンプレート引数を受け取り,それを使ってValue
というフィールドを初期化sるう.
次に,4つの異なる整数値を持つValue
フィールドを継承した4つのレコードを定義する.
class ModRefVal <bits<2> val> {
bits<2> Value = val;
}
def None : ModRefVal<0>;
def Mod : ModRefVal<1>;
def Ref : ModRefVal<2>;
def ModRef : ModRefVal<3>;
ここで,Value
フィールドの2つのビットをそれぞれ調べたいとする.
そんな時,ModRefVal
レコードをテンプレート引数として受け取り,その値を1ビットずつ2つのフィールドに分割するクラスを定義すれば良い.
そして,ModRefBits
を継承したレコードを定義し,テンプレート引数として渡されたModRefVal
レコードの各ビットに対応する2つのフィールドをModRefBits
から取得すれば良い.
class ModRefBits <ModRefVal mrv> {
// Break the value up into its bits, which can provide a nice
// interface to the ModRefVal values.
bit isMod = mrv.Value{0};
bit isRef = mrv.Value{1};
}
// Example uses.
def foo : ModRefBits<Mod>;
def bar : ModRefBits<Ref>;
def snork : ModRefBits<ModRef>;
これは,あるクラスを定義して別のクラスのフィールドを再編成することで,その別のクラスの内部表現を隠す方法の一つである.
この例でllvm-tblgenを実行すると,以下の用な定義が出力される
def bar { // ModRefBits
bit isMod = 0;
bit isRev = 1;
}
def foo { // ModRefBits
bit isMod = 1;
bit isRev = 0;
}
def snork { // ModRefBits
bit isMod = 1;
bit isRev = 1;
}
let
let
文は,文中で定義されているすべてのクラスとレコードのフィールド値に束縛を適用する.
Let ::= "let" LetList "in" "{" Statement* "}"
| "let" LetList "in" Statement
LetList ::= LetItem ("," LetItem)*
LetItem ::= TokIdentifier ["<" RangeList ">"] "=" Value
let
文はスコープを設定する.
スコープは,{}
で囲まれた一連の文,もしくは{}
のない単一の文で構成される.
LetList
の束縛は,そのスコープ内の文に適用される.
LetList
のフィールド名は,文で定義されたクラスやレコードが継承するクラスのフィールド名でなければならない.
フィールド値は,レコードがその親クラスからすべてのフィールドを継承した後に,クラスとレコードに適用される.
そのため,let
は継承されたフィールド値をオーバーライドする役割を果たす.
しかし,let
は,テンプレート引数の値を上書きすることはできない.
トップレベルのlet
文は,いくつかのレコードでいくつかのフィールドをオーバーライドする必要がある場合に役立つ.
また,let
文はねストできる.
以下に2つの例を示す.
let isTerminator = true, isReturn = true, isBarrier = true, hasCtrlDep = true in
def RET : I<0xC3, RawFrm, (outs), (ins), "ret", [(X86retflag 0)]>;
let isCall = true in
// All calls clobber the non-callee saved registers...
let Defs = [EAX, ECX, EDX, FP0, FP1, FP2, FP3, FP4, FP5, FP6, ST0,
MM0, MM1, MM2, MM3, MM4, MM5, MM6, MM7, XMM0, XMM1, XMM2,
XMM3, XMM4, XMM5, XMM6, XMM7, EFLAGS] in {
def CALLpcrel32 : Ii32<0xE8, RawFrm, (outs), (ins i32imm:$dst, variable_ops),
"call\t${dst:call}", []>;
def CALL32r : I<0xFF, MRM2r, (outs), (ins GR32:$dst, variable_ops),
"call\t{*}$dst", [(X86call GR32:$dst)]>;
def CALL32m : I<0xFF, MRM2m, (outs), (ins i32mem:$dst, variable_ops),
"call\t{*}$dst", []>;
}
トップレベルのlet
は,クラスやレコード自体に定義されたフィールドを上書きしない.
multiclass
テンプレート引数を持つクラスは,複数のレコード間の共通性を考慮するのに適しているが,multiclass
は一度に多くのレコードを定義するのに便利.
例えば,3アドレスの命令アーキテクチャで命令が2つの形式であるとする.
reg = reg op reg
reg = reg op imm
このような2つの共通したフォーマットが存在する事を一箇所で指定し,別の場所ですべての演算を指定したいとする.
この目的を達成するのがmulticlass
とdefm
文である.
マルチクラスは,複数のレコードに展開されるマクロやテンプレートと考える事が出来る.
MultiClass ::= "multiclass" TokIdentifier [TemplateArgList]
[":" ParentMultiClassList]
"{" MultiClassStatement+ "}"
ParentMultiClassList ::= MultiClassID ("," MultiClassID)*
MultiClassID ::= TokIdentifier
MultiClassStatement ::= Assert | Def | Defm | Defvar | Foreach | If | Let
通常のクラスと同様に,マルチクラスは名前を持ち,テンプレート引数を受け取る事ができる.
マルチクラスは他のマルチクラスを継承する事ができ,その場合,他のマルチクラスは拡張され,継承したマルチクラスのレコード定義に寄与する.
マルチクラスのボディには,Def
とDefm
を使ってレコードを定義する一連の文が含まれる.
さらに,Defvar
,Foreach
,Let
文を使って,共通の要素をくくり出す事ができる.
また,If
文やAssert
文も使える.
また,通常のクラスと同様に,マルチクラスは暗黙のテンプレート引数NAME
を持ち,名前付きの(匿名でない)レコードがマルチクラスで定義され,レコードの名前にテンプレート引数NAME
を使用した名前が含まれていない場合,自動的に名前が付加される.
def Foo ...
def NAME # Foo ...
マルチクラスで定義されたレコードは,マルチクラスの定義の外にあるdefm
文によって,マルチクラスがインスタンス化または呼び出された時に作成される.
マルチクラス内のdef
文はそれぞれレコードを生成する.
トップレベルのdef
文と同様に,これらの定義はふくすの親クラスから継承することができる.
参照: Examples: multiclass and defm
defm
マルチクラスが定義されると,defm
文を使ってマルチクラスを呼び出し,そのマルチクラスの複数のレコード定義を処理する.
これらのレコード定義は,マルチクラスのdef
文で指定され,defm
文で間接的に指定される.
Defm ::= "defm" [NameValue] ParentClassList ";"
オプションのNameValue
は,def
の名前と同様に形成される.
ParentClassList
はコロンの後に少なくとも1つのマルチクラスと任意の数の通常のクラスのリストを続ける.
マルチクラスは,通常のクラスよりも前に無ければいけない.
なお,defm
はボディを持たない.
この文は,def
文によって直接,またはdefm
文によって間接的に,指定されたすべてのマルチクラスで定義されたすべてのレコードをインスタンス化する.
これらのレコードは,親クラスのリストに含まれる通常のクラスで定義されたフィールドも受け取る.
これは,defm
によって作成されたすべてのレコードに共通のフィールドを追加するのに便利.
名前はdef
で使用されるのと同じ,特殊なモードでparseされる.
名前が含まれていない場合は,グローバルにユニークな名前が割り当てられる.
つまり,以下の例では異なる名前が付けられる
defm : SomeMultiClass<...>; // A globally unique name.
defm "" : SomeMultiClass<...>; // An empty name.
defm
文は,マルチクラスのボディで使用する事ができる.
このような場合,2番目のヴァリアントは以下と同等である
defm NAME : SomeMultiClass<...>;
より一般的には,defm
がマルチクラスの中で使用され,その名前に暗黙のテンプレート引数NAME
が含まれていない場合,NAME
は自動的に割り当てられる.
つまり,次の例はマルチクラスの中では等価である.
defm Foo : SomeMultiClass<...>;
defm NAME # Foo : SomeMultiClass<...>;
参照: Examples: multiclass and defm
Examples_multiclass_and_defm
ここでは,multiclassとdefmを使った簡単な例を紹介する.
3アドレスの命令アーキテクチャで,命令にはreg = reg op reg
とreg = reg op imm
(immediate)の2つの形式があると考える.
このようなアーキテクチャの例としては,SPARCがある.
def ops;
def GPR;
def Imm;
class inst <int opc, string asmstr, dag operandlist>;
multiclass ri_inst <int opc, string asmstr> {
def _rr : inst<opc, !strconcat(asmstr, " $dst, $src1, $src2"),
(ops GPR:$dst, GPR:$src1, GPR:$src2)>;
def _ri : inst<opc, !strconcat(asmstr, " $dst, $src1, $src2"),
(ops GPR:$dst, GPR:$src1, Imm:$src2)>;
}
// Define records for each instruction in the RR and RI formats.
defm ADD : ri_inst<0b111, "add">;
defm SUB : ri_inst<0b101, "sub">;
defm MUL : ri_inst<0b100, "mul">;
ri_inst
マルチクラスを使用すると,prefixが_rr
のものと_ri
のものの2つのレコードが定義される.
マルチクラスを使用するdefm
の名前は,そのマルチクラスで定義されたレコードの名前の前に付けられるので,結果として得られる定義の名前は
ADD_rr, ADD_ri
SUB_rr, SUB_ri
MUL_rr, MUL_ri
マルチクラス機能がなければ,命令は次のように定義する必要がある.
def ops;
def GPR;
def Imm;
class inst <int opc, string asmstr, dag operandlist>;
class rrinst <int opc, string asmstr>
: inst<opc, !strconcat(asmstr, " $dst, $src1, $src2"),
(ops GPR:$dst, GPR:$src1, GPR:$src2)>;
class riinst <int opc, string asmstr>
: inst<opc, !strconcat(asmstr, " $dst, $src1, $src2"),
(ops GPR:$dst, GPR:$src1, Imm:$src2)>;
// Define records for each instruction in the RR and RI formats.
def ADD_rr : rrinst<0b111, "add">;
def ADD_ri : riinst<0b111, "add">;
def SUB_rr : rrinst<0b101, "sub">;
def SUB_ri : riinst<0b101, "sub">;
def MUL_rr : rrinst<0b100, "mul">;
def MUL_ri : riinst<0b100, "mul">;
defmをマルチクラスで使用すると,他のマルチクラスを呼び出すことができ,現在のマルチクラスで定義されているレコードに加えて,それらのマルチクラスで定義されているレコードを作成することができる.
以下の例では,basic_s
およびbasic_p
マルチクラスには,basic_r
マルチクラスを参照するdefm文が含まれている.
basic_r
マルチクラスにはdef文しかありません.
class Instruction <bits<4> opc, string Name> {
bits<4> opcode = opc;
string name = Name;
}
multiclass basic_r <bits<4> opc> {
def rr : Instruction<opc, "rr">;
def rm : Instruction<opc, "rm">;
}
multiclass basic_s <bits<4> opc> {
defm SS : basic_r<opc>;
defm SD : basic_r<opc>;
def X : Instruction<opc, "x">;
}
multiclass basic_p <bits<4> opc> {
defm PS : basic_r<opc>;
defm PD : basic_r<opc>;
def Y : Instruction<opc, "y">;
}
defm ADD : basic_s<0xf>, basic_p<0xf>;
最後のdefmでは,basic_s
,basic_p
両方のマルチクラスから5つの,以下のレコードが作成される.
ADDSSrr, ADDSSrm
ADDSDrr, ADDSDrm
ADDX
ADDPSrr, ADDPSrm
ADDPDrr, ADDPDrm
ADDY
defm文は,トップレベルでもマルチクラスでも,マルチクラスに加えて通常のクラスを継承することができる. 規則は,通常のクラスがマルチクラスの後に記載されていなければならず,少なくとも1つのマルチクラスが存在しなければならない.
class XD {
bits<4> Prefix = 11;
}
class XS {
bits<4> Prefix = 12;
}
class I <bits<4> op> {
bits<4> opcode = op;
}
multiclass R {
def rr : I<4>;
def rm : I<2>;
}
multiclass Y {
defm SS : R, XD; // First multiclass R, then regular class XD.
defm SD : R, XS;
}
defm Instr : Y;
この例では,4つのレコードが作成され,アルファベット順にフィールドが表示される.
def InstrSDrm {
bits<4> opcode = { 0, 0, 1, 0 };
bits<4> Prefix = { 1, 1, 0, 0 };
}
def InstrSDrr {
bits<4> opcode = { 0, 1, 0, 0 };
bits<4> Prefix = { 1, 1, 0, 0 };
}
def InstrSSrm {
bits<4> opcode = { 0, 0, 1, 0 };
bits<4> Prefix = { 1, 0, 1, 1 };
}
def InstrSSrr {
bits<4> opcode = { 0, 1, 0, 0 };
bits<4> Prefix = { 1, 0, 1, 1 };
}
また,マルチクラスの内部でlet文を使用することも可能で,特に複数レベルのマルチクラスのインスタンスを使用する場合に,レコードから共通性を除外する別の方法を提供する.
multiclass basic_r <bits<4> opc> {
let Predicates = [HasSSE2] in {
def rr : Instruction<opc, "rr">;
def rm : Instruction<opc, "rm">;
}
let Predicates = [HasSSE3] in
def rx : Instruction<opc, "rx">;
}
multiclass basic_ss <bits<4> opc> {
let IsDouble = false in
defm SS : basic_r<opc>;
let IsDouble = true in
defm SD : basic_r<opc>;
}
defm ADD : basic_ss<0xf>;
defset
defset
文は,レコードのセットをグローバルなレコードのリストに集めるために使用される.
Defset ::= "defset" Type TokIdentifier "=" "{" Statement* "}"
中括弧の中でdef
やdefm
を介して定義されたレコードはすべて通常通り定義され,さらにそれらは与えられた名前(TokIdentifier)のグローバルリストに集められる.
指定された型はlist<class>
でなければならず,class
は何らかのレコード・クラスである.
defset
文は,その文のスコープを確立する.
defset
のスコープ内でclass型ではないレコードを定義するとエラーになる.
defset
文はネストすることができる.
内側のdefset
はレコードを自分のセットに追加し,それらのレコードはすべて外側のセットにも追加される.
ClassID<...>
構文を使用した初期化式の内部で作成された匿名レコードは,セットに収集されない.
defvar
defvar文
は,グローバル変数を定義する.
その値は,定義の後に続く文の中で使用することができる.
Defvar ::= "defvar" TokIdentifier "=" Value ";"
左側の識別子はグローバル変数として定義され,その値は右側の値式で与えられる.
一度定義された変数は,他の値に設定することはできない.
トップレベルのforeach
で定義された変数は,各ループの繰り返しの最後にスコープから外れ,ある繰り返しで定義された値は次の繰り返しでは使用できない.
次のようなdefvarは動作しない.
defvar i = !add(i, 1)
変数はレコード本文の中でdefvarを使って定義することもできる. 詳しくはDefvar in a Record Bodyを参照.
foreach
foreach
文は,一連の文を繰り返し実行し,変数を一連の値に変化させる.
Foreach ::= "foreach" ForeachIterator "in" "{" Statement* "}"
| "foreach" ForeachIterator "in" Statement
ForeachIterator ::= TokIdentifier "=" ("{" RangeList "}" | RangePiece | Value)
foreachの本体は,中括弧で囲まれた一連の文,または中括弧のない単一の文です.
文は,RangeList
,RangePiece
,または単一の値の各値に対して1回再評価される.
反復のたびに,TokIdentifier
変数に値が設定され,文中で使用できる.
文のリストは内側のスコープを確立する.
foreach
にローカルな変数は,ループの各反復の終わりにスコープから外れるので,その値は反復から次の反復へと引き継がれない.
foreachループはネストする事が出来る.
foreach文は,レコードボディでも使用できる.
foreach i = [0, 1, 2, 3] in {
def R#i : Register<...>;
def F#i : Register<...>;
}
このループでは,R0
,R1
,R2
,R3
というレコードと,F0
,F1
,F2
,F3
が定義されている.
if
if文では,式の値に基づいて2つのステートメントグループのうち1つを選択することができる.
If ::= "if" Value "then" IfBody
| "if" Value "then" IfBody "else" IfBody
IfBody ::= "{" Statement* "}" | Statement
値式が評価される.
評価結果が(bang
演算子と同じ意味で)trueであれば,then
予約語に続く文が処理されます.
そうでない場合,else
予約語があれば,else
に続く文が処理されます.
値が偽で,else
の腕がない場合は,どの文も処理されません.
if v1 then if v2 then {...} else {...}
のようなケースでは,elseは外側のifではなく内側のifに関連付けられる.
ifのthenとelseのifBody
は,内側のスコープを確立する.
ボディ内で定義されたdefvar
変数は,ボディが終了するとスコープから外れる(詳細はDefvar_in_a_Record_Bodyを参照).
if文はレコードボディの中でも使うことができる.
assert
assert文は,条件が真であるかどうかをチェックし,真でない場合はエラーメッセージを表示します.
Assert ::= "assert" condition "," message ";"
条件が真の場合,文は何もしない.
条件が偽の場合は,致命的ではないエラーメッセージを表示する.
このメッセージは,任意の文字列表現で,エラーメッセージの中にメモとして含まれる.
assert
文の正確な動作は,その配置によって異なる.
- トップレベルでは,assertは直ちにチェックされる.
- レコード定義では,ステートメントは保存され,レコードが完全に構築された後にすべてのアサーションがチェックされる.
- クラス定義では,アサーションは保存され,そのクラスを継承するすべてのサブクラスとレコードに継承される. アサーションは,レコードが完全に構築されたときにチェックされる.
- マルチクラスの定義では,アサーションはマルチクラスの他のコンポーネントと共に保存され,マルチクラスが
defm
でインスタンス化されるたびにチェックされる.
TableGenファイルでアサーションを使用すると,TableGenバックエンドでのレコードのチェックが簡単になる. ここでは,2 つのクラス定義でアサートを使用した例を示す.
class PersonName<string name> {
assert !le(!size(name), 32), "person name is too long: " # name;
string Name = name;
}
class Person<string name, int age> : PersonName<name> {
assert !and(!ge(age, 1), !le(age, 120)), "person age is invalid: " # age;
int Age = age;
}
def Rec20 : Person<"Donald Knuth", 60> {
...
}
Additional_Details
Directed_Acyclic_Graphs
directed acyclic graph は,TableGen ではdag
データ型を使って直接表現できる.
DAGノードは,1つの演算子と0個以上の引数(またはオペランド)から構成される.
各引数は,任意の型にすることができる.
別のDAGノードを引数として使用することで,DAGノードの任意のグラフを構築することができる.
dagインスタンスの構文は以下の通り.
( operator argument1, argument2, ... )
operatorは必ず存在し,レコードである必要がある. 引数は0個以上で,カンマで区切ることができる. 演算子と引数には3つの形式がある.
Format | Meaning |
---|---|
value | argument value |
value:name | argument value and associated name |
name | argument name with unset(uninitialized) value |
value
は,TableGenの任意の値を指定できます.
名前がある場合は,ドル記号($)で始まるTokVarName
でなければならない.
名前の目的は,DAG内の演算子や引数に特定の意味を持たせたり,あるDAG内の引数を別のDAG内の同じ名前の引数と関連付けたりすること.
DAGを扱う際には,以下のbang演算子が便利
!con
, !dag
, !empty
, !foreach
, !getdagop
, !setdagop
, !size
.
Defvar_in_a_Record_Body
defvar文は,グローバル変数の定義に加えて,クラスやレコード定義のBody内でローカル変数を定義することができる. 変数のスコープはdefvar文からボディの最後までとなる. スコープ内で異なる値に設定することはできない. defvar文はforeachのステートメントリストでも使用でき,スコープを確立する.
内側のスコープにあるV
という変数は,外側のスコープにある変数V
をシャドウイングする(隠す).
特に,レコードボディ内のV
はグローバルなV
のshadowとなり,foreach文のリスト内のV
は周囲のレコードやグローバルスコープ内のV
のshadowとなる.
foreachの中で定義された変数は,各ループの繰り返しの終わりにスコープから外れるので,ある繰り返しでの値は次の繰り返しでは利用できない. 以下のようなdefvarは動作しない.
defvar i = !add(i, 1)
How_records_are_built
レコードが作成されると,TableGenは次のステップへ移る. クラスは単に抽象的なレコードなので,同じステップをたどることになる.
- レコード名(NameValue)を構築し,空のレコードを作成する
ParentClassList
の親クラスを左から右に解決し,各親クラスの祖先クラスを上から下にたどる.- a. 親クラスのフィールドをレコードに追加する
- b. これらのフィールドにテンプレート引数を代入する
- c. レコードの継承クラスのリストに親クラスを追加する
- トップレベルの let バインディングをレコードに適用する. トップレベルのバインディングは,継承されたフィールドにのみ適用される.
- レコード本文をパースする.
- 任意のフィールドをレコードに追加する
- ローカルのlet文に従ってフィールドの値を変更する
- defvar変数を定義する
- すべてのフィールドをパスして,フィールド間の参照を解決する
- レコードをマスターレコードリストに追加する
let結合(ステップ3)を適用した後にフィールド間の参照を解決(ステップ5)するので,let文は非常に強力. たとえば,以下のようになる
class C <int x> {
int Y = x;
int Yplus1 = !add(Y, 1);
int xplus1 = !add(x, 1);
}
let Y = 10 in {
def rec1 : C<5> {
}
}
def rec2 : C<5> {
let Y = 10;
}
トップレベルのletでY
を結合する場合と,ローカルのlet
で同じことをする場合,どちらの場合も結果は
def rec1 { // C
int Y = 10;
int Yplus1 = 11;
int xplus1 = 6;
}
def rec2 { // C
int Y = 10;
int Yplus1 = 11;
int xplus1 = 6;
}
Yplus1
が11
なのは,add(Y, 1)
が解決する前にlet Y
が実行されるためである.
Using_Classes_as_Subroutines
Simple Valuesで説明したように,クラスは式の中で呼び出され,テンプレート引数を渡すことができる. これにより,TableGenはそのクラスを継承した新しい無名レコードを作成する. 通常通り,レコードはそのクラスで定義されたすべてのフィールドを受け取る.
この機能は,単純なサブルーチン機能として利用することができる. クラスは,テンプレート引数を使用してさまざまな変数やフィールドを定義でき,それらは匿名レコードに入る. これらのフィールドは,次のようにクラスを呼び出す式の中で取り出すことができる. フィールドretにはサブルーチンの最終値が入っているとする.
int Result = ... CalcValue<arg>.ret ...;
CalcValue
クラスは,テンプレート引数arg
と共に呼び出される.
このクラスは,ret
フィールドの値を計算し,それをResult
フィールドの初期化におけるpoint of callで取得する.
この例で作成された匿名レコードは,結果の値を保持する以外の目的はない.
次に,実際の例を示す.
isValidSize
クラスは,指定されたバイト数が有効なデータサイズであるかどうかを判断する.
bit ret
が適切に設定される.
ValidSize
フィールドは,データサイズを指定してisValidSize
を呼び出し,結果の無名レコードからret
フィールドを取得することにより,その初期値を得る.
class isValidSize<int size> {
bit ret = !cond(!eq(size, 1): 1,
!eq(size, 2): 1,
!eq(size, 4): 1,
!eq(size, 8): 1,
!eq(size, 16): 1,
true: 0);
}
def Data1 {
int Size = ...;
bit ValidSize = isValidSize<Size>.ret;
}
Preprocessing_Facilities
TableGen に組み込まれているプリプロセッサは,単純な条件付きコンパイルのみを目的としている. このプリプロセッサは以下のディレクティブをサポートしているが,これらは多少非公式に指定されている.
LineBegin ::= beginning of line
LineEnd ::= newline | return | EOF
WhiteSpace ::= space | tab
CComment ::= "/*" ... "*/"
BCPLComment ::= "//" ... LineEnd
WhiteSpaceOrCComment ::= WhiteSpace | CComment
WhiteSpaceOrAnyComment ::= WhiteSpace | CComment | BCPLComment
MacroName ::= ualpha (ualpha | "0"..."9")*
PreDefine ::= LineBegin (WhiteSpaceOrCComment)*
"#define" (WhiteSpace)+ MacroName
(WhiteSpaceOrAnyComment)* LineEnd
PreIfdef ::= LineBegin (WhiteSpaceOrCComment)*
("#ifdef" | "#ifndef") (WhiteSpace)+ MacroName
(WhiteSpaceOrAnyComment)* LineEnd
PreElse ::= LineBegin (WhiteSpaceOrCComment)*
"#else" (WhiteSpaceOrAnyComment)* LineEnd
PreEndif ::= LineBegin (WhiteSpaceOrCComment)*
"#endif" (WhiteSpaceOrAnyComment)* LineEnd
MacroName
は,TableGenファイル内の任意の場所に定義できる.
この名前には値がなく,定義されているかどうかをテストすることしかできない.
マクロのテスト領域は,#ifdef
または#ifndef
指示子で始まる.
マクロ名が定義されている場合(#ifdef
),または未定義の場合(#ifndef
),指令と対応する#else
または#endif
の間のソースコードが処理される.
テストが失敗しても#else
節がある場合は,#else
と#endif
の間のソースコードが処理される.
テストが失敗し,#else
節がない場合は,テスト領域内のソースコードは処理されない.
テスト領域はネストできるが,適切に行う必要がある.
あるファイルで始まった領域は,そのファイルで終わらなければならない.
つまり,同じファイルに#endif
がなければならない.
MacroName
は,*-tblgen
コマンドラインの-D
オプションを使用して外部で定義することができる.
llvm-tblgen self-reference.td -Dmacro1 -Dmacro3
Appendix_Bang_Operators
bang演算子は,値の式の中で関数として機能する. bang演算子は1つまたは複数の引数を取り,それらを操作して結果を出す. 演算子が真偽値を生成する場合,結果の値は真なら1,偽なら0になる. 演算子が真偽値の引数をテストする場合,0は偽,0以外は真と解釈される.
!add(a, b, ...)
引数として与えられたa,b,...
を加算し,結果を出力する.
!and(a, b, ...)
引数a,b,...
に対してBit毎のAND演算を行い,結果を出力する.
すべての引数が0または1の場合は論理AND演算となる.
!cast<type>(a)
a
に対してtype
型へのキャストを行い,結果を出力する.
a
が文字列でない場合,int
とbit
の間,あるいはレコード型の間など,単純なキャストが行われる.
これにより,レコードをクラスにキャストすることが出来る.
レコードがstring
にキャストされた場合,レコードの名前が出力される.
a
が文字列の場合,それはレコード名として扱われ,すべての定義されたレコードのリストで検索される.
結果として得られるレコードは,指定された型であることが期待される.
例えば,マルチクラスの定義,またはマルチクラスの定義の中でインスタンス化されたクラスに!cast<type>(name)
が現れ,その名前がマルチクラスのテンプレート引数を参照していない場合,その名前のレコードはソースファイルの中で以前にインスタンス化されたものでなければならない.
name
がテンプレート引数を参照している場合,参照の解決は,マルチクラスをインスタンス化するdefm
文まで遅延される(defm
が別のマルチクラスで発生し,name
が参照する内側のマルチクラスのテンプレート引数が,外側のマルチクラスのテンプレート引数への参照を含む値で置換されている場合は,それ以降になりる).
a
の型がtype
と一致しない場合,TableGenはエラーを発生させる.
!con(a, b, ...)
DAGのノードa
,b
などを連結する.
これらの演算子は等しくなければならない.
!con((op a1:$name1, a2:$name2), (op b1:$name3))
この例では,DAGノード(op a1:$name1, a2:$name2, b1:$name3)
を生成する.
!cond(cond1: val1, cond2: val2, ..., condn: valn)
cond1
をテストし,その結果が真であればval1
を返す.
偽の場合,この演算子はcond2
をテストし,その結果が真であればval2
を返す.
どの条件も真でない場合はエラーになる.
この例では,整数の符号を生成している.
!cond(!lt(x, 0) : "negative", !eq(x, 0) : "zero", true : "positive")
!dag(op, arguments, names)
与えられた演算子と引数を持つDAGノードを作成する.
引数と名前の引数は,同じ長さのリストか,初期化されていない(?
)でなければならない.
names
引数はlist<string>
型でなければならない.
型システムの制限により,引数は共通の型を持つアイテムのリストでなければならない.
実際には,同じ型を持つか,共通の親クラスを持つレコードでなければならないことを意味する.
dagとdagでないアイテムを混在させることはできない.
ただし,?
は使用できる.
例
!dag(op, [a1, a2, ?], ["name1", "name2", "name3"])
の結果は,
(op a1-value:$name1, a2-value:$name2, ?:$name3).
!empty(a)
string
,list
,またはDAG
のa
が空であれば1を,そうでなければ0を出力する.
DAGが引数を持たない場合は空となる.
!eq(a, b)
aがbと等しい場合は1を,そうでない場合は0を出力する.
引数は,bit
,bits
,int
,string
,record
のいずれか.
他のタイプのオブジェクトを比較するには,!cast<string>
を使う.
!filter(var, list, predicate)
リスト内の要素をフィルタリングして新しいリストを作成する.
フィルタリングを実行するために,TableGenは各要素に変数var
をバインドし,次にvar
を参照していると思われるpredicate
を評価する.
predicate
は,bool値(bit,bits,int
を生成しなければならない.
その値は!if
と同様に解釈される.
もしその値が0であれば,その要素は新しいリストに追加されない.
もし,値が0以外であれば,その要素を新しいリストに追加する.
!find(string1, string2[, start])
string1
の中からstring2
を検索し,その位置を出力する.
検索の開始位置はstart1で指定することができ,
0から
string1`の長さまでの範囲で指定することができる.
!foldl(init, list, acc, var, expr)
list
の項目に対してfoldl
を行う.
変数acc
はアキュムレータとして動作し,init
で初期化される.
変数var
はlist
の各要素に束縛される.
式は各要素で評価され,acc
とvar
を使って累積値を計算し,foldl
はacc
に再代入する.
acc
の型はinit
と同じで,var
の型はlist
の要素と同じで,expr
はinit
と同じ型でなければならない.
次の例では,RecList
のレコードリストのNumber
フィールドの合計を計算している.
int x = !foldl(0, RecList, total, rec, !add(total, rec.Number));
リストをフィルタリングして,一部の要素だけを含む新しいリストを作成することが目的の場合は,!filterを参照.
!foreach(var, sequence, expr)
各要素がsequence list/tagの対応する要素の関数となる新しいlist/tagを作成する.
関数を実行するために,TableGenは変数var
を要素にバインドし,その後,式を評価する.
式は推定的に変数var
を参照し,結果の値を計算する.
単純に,同じ値が複数回繰り返される一定の長さのリストを作成したい場合は,!listsplatを参照.
!ge(a, b)
a
がb
以上の場合は1を,そうでない場合は0を出力する.
引数は,bit
,bits
,int
,string
のいずれか.
!getdagop(dag)
or !getdagop<type>(dag)
与えられたdagノードの演算子を生成する.
例
!getdagop((foo 1, 2))
の結果はfoo
.
DAGの演算子は常にレコードである.
!getdagop
の結果は,レコードクラスが何でも許される文脈では,直接使用することができる(典型的には,他のdag値に配置します).
しかし,それ以外の文脈では,明示的に特定のクラスにキャストする必要がある.
これを簡単に行うために,<type>
構文が用意されている.
たとえば,結果をBaseClass
型の値に代入するには,次のいずれかを書けばよい.
BaseClass b = !getdagop<BaseClass>(someDag);
BaseClass b = !cast<BaseClass>(!getdagop(someDag));
しかし,他のDAGノードの演算子を再利用して新しいDAGノードを作る時には,キャストは必要ない.
dag d = !dag(!getdagop(someDag), args, names);
!gt(a, b)
この演算子は,a
がb
より大きい場合は1を,そうでない場合は0を出力する.
引数は,bit
,bits
,int
,string
のいずれか.
!head(a)
list a
の0番目の要素を生成する(!tailも参照)
!if(test, then, else)
bit
またはint
を生成する必要のあるテストを評価する.
結果が0でない場合はthen
式,そうでない場合はelse
式が生成される.
!interleave(list, delim)
list
のアイテムを連結し,各ペアの間にdelim
文字列を挿入して,結果の文字列を出力する.
list
には,string
,int
,bits
,bit
のリストを指定する.
空のlistの時は空の文字列になる.
delim
は,空の文字列でも良い.
!isa<type>(a)
a
の型がtype
であれば1を,そうでなければ0を出力する.
!le(a, b)
a
がb
以下であれば1を,そうでなければ0を出力する.
!listconcat(list1, list2, ...)
引数として与えられたlist1, list2, ...
を連結し,その結果を出力する.
それらのリストの要素は同じ型でなければならない.
!listsplat(value, count)
要素がすべてvalue
に等しい長さcount
のlistを出力する.
例: !listsplat(42, 3)
は[42, 42, 42]
を出力する.
!lt(a, b)
a
がb
より小さければ1を,そうでなければ0を出力する.
!mul(a, b, ...)
a, b, ...
を掛け合わせ,結果を出力する.
!ne(a, b)
a
がb
に等しくない場合は1を,そうでない場合は0を出力する.
引数は,bit
,bits
,int
,string
,record
のいずれかの値.
他の型のオブジェクトを比較するには,!cast<string>
を使う.
!not(a)
論理的なNOT演算を行う. 0を引数に渡すと,1(true)が,1を引数に渡すと,0(false)が出力される.
!op(a, b, ...)
a, b...
に対してビット単位のORを行い,結果を出力する.
すべての引数が0または1の場合は,論理的なORを行うことができる.
!setdagop(dag, po)
dag
と同じ引数を持ち,その演算子がop
に置き換えられたDAGノードを生成する
例: !setdagop((foo 1, 2), bar)
の結果は,(bar 1, 2)
となる.
!shl(a, count)
a
を論理的にcount
bitだけ左シフトし,その結果を出力する.
演算は64ビットの整数に対して行われ,0~63
以外のcount
では結果は不定となる.
!size(a)
string
,list
,またはdag
であるa
のサイズを出力する.
DAGのサイズは,その引数の数であり,演算子はカウントされない.
!sra(a, count)
a
を論理的にcount
bitだけ右シフトし,その結果を出力する.
演算は64ビットの整数に対して行われ,0~63
以外のcount
では結果は不定となる.
!strconcat(str1, str2, ...)
string
である引数str1, str2, ...
を結合し,結果を出力する.
!sub(a, b)
引数として与えられたa,b,...
を減算し,結果を出力する.
!subst(target, repl, value)
value
の中のtarget
のすべての出現箇所をrepl
で置き換え,結果の値を出力する.
value
は文字列でもよく,その場合は部分文字列置換が行われる.
この場合,演算子は,ターゲット・レコード名がバリュー・レコード名と等しい場合はrepl
レコードを生成し,そうでない場合はvalue
を生成する.
!substr(string, start[, length])
与えられた文字列の部分文字列を抽出する.
部分文字列の開始位置はstart
で指定され,0から文字列の長さまでの範囲で指定できる.
部分文字列の長さはlength
で指定され,指定されなければ文字列の残りの部分が抽出される.
start
とlength
は整数でなければならない.
!tail(a)
list
a
の要素のうち,0番目の要素を除いたすべての要素を持つ新しいリストを出力する.
!xor(a, b, ...)
a,b, ...
のビットごとのXOR演算を行い,その結果を出力する.
論理的なXORは,すべての引数が0または1の場合に実行できる.
Appendix_Paste_Operator_Examples
ここでは,レコード名にpaste
演算子を使用した例を紹介する.
defvar suffix = "_suffstring";
defvar some_ints = [0, 1, 2, 3];
def name # suffix {
}
foreach i = [1, 2] in {
def rec # i {
}
}
}
最初のdefは,suffix
の値を使用しない.
2番目のdefでは,グローバル名ではないので,i
の値を使う.
次のようなレコードが生成される
def namesuffix {
}
def rec1 {
}
def rec2 {
}
次に,フィールドの値,式でのpaste
演算子の例を紹介する.
def test {
string strings = suffix # suffix;
list<int> integers = some_ints # [4, 5, 6];
}
文字列フィールド式では,paste
演算子の両側でsuffix
を使用します.
左側では正常に評価されるが,右側ではそのままの形で評価される.
整数フィールド式では,some_ints
変数の値とリテラルリストを使用している.
次のようなレコードが生成される.
def test {
string strings = "_suffstringsuffix";
list<int> ints = [0, 1, 2, 3, 4, 5, 6];
}
Appendix_Sample_Record
LLVMがサポートしているターゲット・マシンの1つにIntel x86がある. TableGenの次の出力は,32ビットのレジスタ間ADD命令を表すために作成されたレコードを示している.
def ADD32rr { // InstructionEncoding Instruction X86Inst I ITy Sched BinOpRR BinOpRR_RF
int Size = 0;
string DecoderNamespace = "";
list<Predicate> Predicates = [];
string DecoderMethod = "";
bit hasCompleteDecoder = 1;
string Namespace = "X86";
dag OutOperandList = (outs GR32:$dst);
dag InOperandList = (ins GR32:$src1, GR32:$src2);
string AsmString = "add{l} {$src2, $src1|$src1, $src2}";
EncodingByHwMode EncodingInfos = ?;
list<dag> Pattern = [(set GR32:$dst, EFLAGS, (X86add_flag GR32:$src1, GR32:$src2))];
list<Register> Uses = [];
list<Register> Defs = [EFLAGS];
int CodeSize = 3;
int AddedComplexity = 0;
bit isPreISelOpcode = 0;
bit isReturn = 0;
bit isBranch = 0;
bit isEHScopeReturn = 0;
bit isIndirectBranch = 0;
bit isCompare = 0;
bit isMoveImm = 0;
bit isMoveReg = 0;
bit isBitcast = 0;
bit isSelect = 0;
bit isBarrier = 0;
bit isCall = 0;
bit isAdd = 0;
bit isTrap = 0;
bit canFoldAsLoad = 0;
bit mayLoad = ?;
bit mayStore = ?;
bit mayRaiseFPException = 0;
bit isConvertibleToThreeAddress = 1;
bit isCommutable = 1;
bit isTerminator = 0;
bit isReMaterializable = 0;
bit isPredicable = 0;
bit isUnpredicable = 0;
bit hasDelaySlot = 0;
bit usesCustomInserter = 0;
bit hasPostISelHook = 0;
bit hasCtrlDep = 0;
bit isNotDuplicable = 0;
bit isConvergent = 0;
bit isAuthenticated = 0;
bit isAsCheapAsAMove = 0;
bit hasExtraSrcRegAllocReq = 0;
bit hasExtraDefRegAllocReq = 0;
bit isRegSequence = 0;
bit isPseudo = 0;
bit isExtractSubreg = 0;
bit isInsertSubreg = 0;
bit variadicOpsAreDefs = 0;
bit hasSideEffects = ?;
bit isCodeGenOnly = 0;
bit isAsmParserOnly = 0;
bit hasNoSchedulingInfo = 0;
InstrItinClass Itinerary = NoItinerary;
list<SchedReadWrite> SchedRW = [WriteALU];
string Constraints = "$src1 = $dst";
string DisableEncoding = "";
string PostEncoderMethod = "";
bits<64> TSFlags = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0 };
string AsmMatchConverter = "";
string TwoOperandAliasConstraint = "";
string AsmVariantName = "";
bit UseNamedOperandTable = 0;
bit FastISelShouldIgnore = 0;
bits<8> Opcode = { 0, 0, 0, 0, 0, 0, 0, 1 };
Format Form = MRMDestReg;
bits<7> FormBits = { 0, 1, 0, 1, 0, 0, 0 };
ImmType ImmT = NoImm;
bit ForceDisassemble = 0;
OperandSize OpSize = OpSize32;
bits<2> OpSizeBits = { 1, 0 };
AddressSize AdSize = AdSizeX;
bits<2> AdSizeBits = { 0, 0 };
Prefix OpPrefix = NoPrfx;
bits<3> OpPrefixBits = { 0, 0, 0 };
Map OpMap = OB;
bits<3> OpMapBits = { 0, 0, 0 };
bit hasREX_WPrefix = 0;
FPFormat FPForm = NotFP;
bit hasLockPrefix = 0;
Domain ExeDomain = GenericDomain;
bit hasREPPrefix = 0;
Encoding OpEnc = EncNormal;
bits<2> OpEncBits = { 0, 0 };
bit HasVEX_W = 0;
bit IgnoresVEX_W = 0;
bit EVEX_W1_VEX_W0 = 0;
bit hasVEX_4V = 0;
bit hasVEX_L = 0;
bit ignoresVEX_L = 0;
bit hasEVEX_K = 0;
bit hasEVEX_Z = 0;
bit hasEVEX_L2 = 0;
bit hasEVEX_B = 0;
bits<3> CD8_Form = { 0, 0, 0 };
int CD8_EltSize = 0;
bit hasEVEX_RC = 0;
bit hasNoTrackPrefix = 0;
bits<7> VectSize = { 0, 0, 1, 0, 0, 0, 0 };
bits<7> CD8_Scale = { 0, 0, 0, 0, 0, 0, 0 };
string FoldGenRegForm = ?;
string EVEX2VEXOverride = ?;
bit isMemoryFoldable = 1;
bit notEVEX2VEXConvertible = 0;
}
レコードの1行目を見ると,ADD32rr
レコードが8つのクラスを継承していることがわかる.
継承階層は複雑だが,親クラスを使用することで,各命令に109個の個別フィールドを指定するよりもはるかに簡単になる.
以下は,ADD32rr
をはじめとする複数のADD命令を定義するために使用されるコードである.
defm ADD : ArithBinOp_RF<0x00, 0x02, 0x04, "add", MRM0r, MRM0m,
X86add_flag, add, 1, 1, 1>;
defm
式は,ArithBinOp_RF
がマルチクラスであり,BinOpRR_RF
を継承した複数の具象レコード定義を含むことをTableGenに伝える.
このクラスは,BinOpRR
を継承し,BinOpRR
はITy
とSched
を継承する,というようになっている.
フィールドはすべての親クラスから継承されている.
例えば,IsIndirectBranchはInstruction
クラスから継承されている.
Comments