TableGen 백엔드를 작성하는 방법을 안내하는 문서로, RecordKeeper, Record, RecTy, Init 등 핵심 데이터 구조와 새 백엔드 생성, 레코드와 필드 접근, 출력 스트림으로의 출력, 오류 보고, 디버깅 및 타이밍 도구를 설명합니다.
URL: https://llvm.org/docs/TableGen/BackGuide.html
Title: 1 TableGen Backend Developer’s Guide — LLVM 22.0.0git documentation
TableGen의 목적은 출력 파일보다 훨씬 쉽게 작성할 수 있는 소스 파일의 정보를 바탕으로 복잡한 출력 파일을 생성하는 것입니다. 이렇게 하면 시간이 지나도 유지 보수와 수정이 용이합니다. 정보는 클래스와 레코드를 포함하는 선언형 스타일로 작성되며, 이후 TableGen이 이를 처리합니다. 내부화된 레코드는 다양한 백엔드로 전달되며, 백엔드는 레코드의 하위 집합에서 정보를 추출하여 출력 파일을 생성합니다. 이러한 출력 파일은 일반적으로 C++용 .inc 파일이지만, 백엔드 개발자가 필요로 하는 어떤 유형의 파일도 될 수 있습니다.
이 문서는 TableGen을 위한 백엔드를 작성하는 가이드입니다. 완전한 참조 매뉴얼이 아니라, 백엔드를 위해 TableGen이 제공하는 기능을 사용하는 방법을 안내합니다. 관련된 다양한 자료 구조와 함수에 대한 완전한 참조는 주요 TableGen 헤더 파일(record.h) 및/또는 Doxygen 문서를 참조하십시오.
이 문서는 TableGen 소스 파일 작성에 대한 자세한 참조를 제공하는 TableGen 프로그래머 참조를 읽었다고 가정합니다. 기존 백엔드에 대한 설명은 TableGen BackEnds를 참조하십시오.
다음 섹션에서는 TableGen 파서가 TableGen 소스 파일에서 수집한 클래스와 레코드를 담고 있는 자료 구조를 설명합니다. 여기서 클래스 는 추상 레코드 클래스를, 레코드 는 구체 레코드를 의미합니다.
별도 언급이 없는 한, 클래스에 연관된 함수는 인스턴스 함수입니다.
RecordKeeper¶RecordKeeper 클래스의 인스턴스는 TableGen이 파싱하고 수집한 모든 클래스와 레코드의 컨테이너 역할을 합니다. RecordKeeper 인스턴스는 TableGen이 백엔드를 호출할 때 백엔드로 전달됩니다. 이 클래스는 보통 RK로 약칭됩니다.
recordkeeper에는 두 개의 맵이 있는데, 하나는 클래스용이고 다른 하나는 레코드(종종 defs 라고도 함)용입니다. 각 맵은 클래스 또는 레코드 이름을 Record 클래스의 인스턴스(참조: Record)에 매핑하며, 이 인스턴스는 해당 클래스 또는 레코드에 대한 모든 정보를 담고 있습니다.
두 개의 맵 외에도 RecordKeeper 인스턴스에는 다음이 포함됩니다.
전역 변수의 이름을 그 값에 매핑하는 맵. 전역 변수는 TableGen 파일의 바깥쪽 defvar 문으로 정의됩니다.
익명 레코드에 이름을 붙이기 위한 카운터.
RecordKeeper 클래스는 몇 가지 유용한 함수를 제공합니다.
전체 클래스 및 레코드 맵을 가져오는 함수.
부모 클래스에 따라 레코드의 하위 집합을 가져오는 함수.
이름으로 개별 클래스, 레코드, 전역 변수를 가져오는 함수.
RecordKeeper 인스턴스는 << 연산자로 출력 스트림에 출력할 수 있습니다.
Record¶TableGen이 구성한 각 클래스나 레코드는 Record 클래스의 인스턴스로 표현됩니다. RecordKeeper 인스턴스는 클래스용 맵 하나와 레코드용 맵 하나를 포함합니다. 레코드의 주요 데이터 멤버는 레코드 이름, 필드 이름과 그 값의 벡터, 그리고 레코드의 슈퍼클래스 벡터입니다.
레코드 이름은 Init(참조: Init)에 대한 포인터로 저장됩니다. Init은 TableGen 값을 보관하는 클래스(때로는 초기화 값 이라고도 함)입니다. 필드 이름과 값은 RecordVal 인스턴스의 벡터(참조: RecordVal)에 저장되며, 각 인스턴스는 필드 이름과 그 값을 모두 포함합니다. 슈퍼클래스 벡터에는 쌍의 시퀀스가 들어 있으며, 각 쌍에는 슈퍼클래스 레코드와 그 소스 파일 위치가 포함되어 있습니다.
그 외에도 Record 인스턴스에는 다음이 포함됩니다.
레코드 정의 자체 및 그 정의에 관여한 모든 멀티클래스의 위치를 포함하는 소스 파일 위치의 벡터.
클래스 레코드의 경우, 클래스의 템플릿 인수 벡터.
이 레코드에 해당하는 DefInit(참조: DefInit) 인스턴스.
고유 레코드 ID.
이것이 클래스 정의인지 여부를 지정하는 불리언.
이것이 익명 레코드인지 여부를 지정하는 불리언.
Record 클래스는 많은 유용한 함수를 제공합니다.
레코드 이름, 필드, 소스 파일 위치, 템플릿 인수, 고유 ID를 가져오는 함수.
레코드의 모든 슈퍼클래스 또는 직접 슈퍼클래스만 가져오는 함수.
다양한 형태로 이름을 지정하여 특정 필드 값을 가져오고, 다양한 형태로 그 값을 반환하는 함수(참조: 레코드 이름과 필드 얻기).
레코드의 다양한 특성을 확인하는 불리언 함수.
Record 인스턴스는 << 연산자로 출력 스트림에 출력할 수 있습니다.
RecordVal¶레코드의 각 필드는 RecordVal 클래스의 인스턴스에 저장됩니다. Record 인스턴스는 이러한 값 인스턴스의 벡터를 포함합니다. RecordVal 인스턴스는 Init 인스턴스에 저장된 필드 이름을 포함합니다. 또한 필드의 값도 포함하며, 이 역시 Init에 저장됩니다. (이 클래스의 더 나은 이름은 RecordField일 수 있습니다.)
이러한 주요 멤버 외에도 RecordVal에는 다른 데이터 멤버가 있습니다.
필드 정의의 소스 파일 위치.
RecTy 클래스의 인스턴스로 저장된 필드의 타입(참조: RecTy).
RecordVal 클래스는 몇 가지 유용한 함수를 제공합니다.
다양한 형태로 필드 이름을 가져오는 함수.
필드의 타입을 가져오는 함수.
필드의 값을 가져오는 함수.
소스 파일 위치를 가져오는 함수.
필드 값은 Record 인스턴스에서 직접 얻는 것이 더 쉽다는 점에 유의하십시오(참조: Record).
RecordVal 인스턴스는 << 연산자로 출력 스트림에 출력할 수 있습니다.
RecTy¶RecTy 클래스는 필드 값의 타입을 나타내는 데 사용됩니다. 사용 가능한 각 필드 타입마다 하나씩의 서브클래스를 가지는 베이스 클래스입니다. RecTy 클래스에는 특정 필드 값의 타입을 지정하는 열거형 타입의 데이터 멤버가 하나 있습니다. (이 클래스의 더 나은 이름은 FieldTy일 수 있습니다.)
RecTy 클래스는 몇 가지 유용한 함수를 제공합니다.
타입 이름을 문자열로 가져오는 가상 함수.
이 타입의 모든 값이 다른 주어진 타입으로 변환될 수 있는지 확인하는 가상 함수.
이 타입이 다른 주어진 타입의 서브타입인지 확인하는 가상 함수.
이 타입의 원소를 갖는 리스트에 해당하는 list 타입을 가져오는 함수. 예를 들어, int 타입으로 호출하면 list<int> 타입을 반환합니다.
RecTy를 상속하는 서브클래스는 BitRecTy, BitsRecTy, CodeRecTy, DagRecTy, IntRecTy, ListRecTy, RecordRecTy, StringRecTy입니다. 이들 중 일부 클래스에는 다음 하위 섹션에서 설명하는 추가 멤버가 있습니다.
RecTy에서 파생된 클래스는 모두 get() 함수를 제공합니다. 이는 파생 클래스에 해당하는 RecTy 인스턴스를 반환합니다. 일부 get() 함수는 어떤 특정 변형 타입이 필요한지 지정하기 위한 인자를 필요로 합니다. 이러한 인자는 다음 하위 섹션에서 설명합니다.
RecTy 인스턴스는 << 연산자로 출력 스트림에 출력할 수 있습니다.
경고
특정 타입의 RecTy 인스턴스가 하나뿐인지 여러 개인지는 규정되어 있지 않습니다.
BitsRecTy¶이 클래스에는 bits 값의 크기와 그 크기를 가져오기 위한 함수가 포함됩니다.
get() 함수는 시퀀스의 길이 n 을 받아서 bits<n>에 해당하는 BitsRecTy 타입을 반환합니다.
ListRecTy¶이 클래스에는 리스트의 원소 타입을 지정하는 데이터 멤버와 그 타입을 가져오는 함수가 포함됩니다.
get() 함수는 리스트 멤버의 RecTy type 을 받아서 list<type>에 해당하는 ListRecTy 타입을 반환합니다.
RecordRecTy¶이 클래스에는 이 레코드의 부모 클래스 목록을 담는 데이터 멤버가 포함됩니다. 또한 클래스 배열을 얻는 함수와 반복자 begin() 및 end() 값을 얻는 두 함수를 제공합니다. 이 클래스는 후자의 두 함수의 반환값 타입을 정의합니다.
using const_record_iterator = Record * const *;
get() 함수는 레코드의 직접 슈퍼클래스에 해당하는 Record 인스턴스 포인터의 ArrayRef를 받아, 해당 슈퍼클래스를 상속하는 레코드에 해당하는 RecordRecTy를 반환합니다.
Init¶Init 클래스는 TableGen 값을 표현하는 데 사용됩니다. 이름은 초기화 값 에서 유래했습니다. 이 클래스는 레코드의 필드(이름과 값 모두)를 표현하는 RecordVal 클래스와 혼동하지 마십시오. Init 클래스는 사용 가능한 값 타입마다 하나씩의 서브클래스를 가지는 베이스 클래스입니다. Init의 주요 데이터 멤버는 값의 특정 타입을 나타내는 열거형 타입입니다.
Init 클래스는 몇 가지 유용한 함수를 제공합니다.
타입 열거값을 가져오는 함수.
값이 완전히 지정되었는지(즉, 초기화되지 않은 하위 값이 없는지) 판별하는 불리언 가상 함수.
값을 문자열로 가져오는 가상 함수.
값을 다른 타입으로 캐스팅하고, TableGen의 비트 범위 기능과 리스트 슬라이스 기능을 구현하는 가상 함수.
값의 특정 비트를 가져오는 가상 함수.
Init로부터 직접 상속받는 서브클래스는 UnsetInit과 TypedInit입니다.
Init 인스턴스는 << 연산자로 출력 스트림에 출력할 수 있습니다.
경고
같은 기본 타입과 값을 가진 두 개의 별도 초기화 값(예: 값이 "Hello"인 두 문자열)이 두 개의 Init으로 표현되는지, 같은 Init을 공유하는지는 규정되어 있지 않습니다.
UnsetInit¶이 클래스는 Init의 서브클래스로, 설정되지 않은(초기화되지 않은) 값을 나타냅니다. 정적 함수 get()을 사용하여 이 타입의 싱글턴 Init을 얻을 수 있습니다.
TypedInit¶이 클래스는 Init의 서브클래스로, 특정 값 타입(설정되지 않은 값을 제외)을 표현하는 클래스들의 부모 클래스 역할을 합니다. 이러한 클래스에는 BitInit, BitsInit, DagInit, DefInit, IntInit, ListInit, StringInit이 포함됩니다. (TableGen 파서에서 사용되는 추가 파생 타입도 있습니다.)
이 클래스에는 값의 RecTy 타입을 지정하는 데이터 멤버가 포함되어 있습니다. 해당 RecTy 타입을 가져오는 함수를 제공합니다.
BitInit¶BitInit 클래스는 TypedInit의 서브클래스입니다. 그 인스턴스는 비트의 가능한 값인 0 또는 1을 나타냅니다. 비트를 담는 데이터 멤버를 포함합니다.
TypedInit에서 파생된 클래스는 모두 다음 함수를 제공합니다.
지정된 값(들)을 나타내는 Init을 반환하는 get()라는 정적 함수. BitInit의 경우, get(true)는 참을 나타내는 BitInit 인스턴스를 반환하고, get(false)는 거짓을 나타내는 인스턴스를 반환합니다. 위에서 언급했듯이, 참(또는 거짓)을 나타내는 BitInit이 정확히 하나인지 하나 이상인지는 규정되어 있지 않습니다.
인스턴스의 값을 보다 직접적인 형태로 반환하는 GetValue()라는 함수. 이 경우 bool로 반환합니다.
BitsInit¶BitsInit 클래스는 TypedInit의 서브클래스입니다. 그 인스턴스는 상위 비트에서 하위 비트로의 비트 시퀀스를 나타냅니다. 시퀀스의 길이를 담는 데이터 멤버와, 비트당 하나의 Init 인스턴스 포인터 벡터를 포함합니다.
이 클래스는 통상의 get() 함수를 제공합니다. getValue() 함수는 제공하지 않습니다.
이 클래스는 다음의 추가 함수를 제공합니다.
시퀀스의 비트 수를 가져오는 함수.
정수 인덱스로 지정된 비트를 가져오는 함수.
DagInit¶DagInit 클래스는 TypedInit의 서브클래스입니다. 그 인스턴스는 가능한 방향 비순환 그래프(dag)를 나타냅니다.
이 클래스에는 DAG 연산자에 대한 Init 포인터와 연산자 이름에 대한 StringInit 포인터가 포함됩니다. 또한 DAG 피연산자 수와 피연산자 이름 수가 포함됩니다. 마지막으로 피연산자에 대한 Init 인스턴스 포인터 벡터와 피연산자 이름에 대한 StringInit 인스턴스 포인터 벡터가 포함됩니다. (DAG 피연산자는 인자(arguments) 라고도 합니다.)
이 클래스는 두 가지 형태의 통상적인 get() 함수를 제공합니다. 통상의 getValue() 함수는 제공하지 않습니다.
이 클래스는 많은 추가 함수를 제공합니다.
연산자를 다양한 형태로 가져오는 함수와 연산자 이름을 다양한 형태로 가져오는 함수.
피연산자가 있는지 여부를 판별하고 피연산자 수를 가져오는 함수.
피연산자를 개별적으로 또는 함께 가져오는 함수.
이름이 있는지 여부를 판별하고 이름의 수를 가져오는 함수.
이름을 개별적으로 또는 함께 가져오는 함수.
피연산자 반복자의 begin() 및 end() 값을 가져오는 함수.
이름 반복자의 begin() 및 end() 값을 가져오는 함수.
이 클래스는 피연산자와 이름 반복자의 반환값 타입 두 가지를 정의합니다.
using const_arg_iterator = SmallVectorImpl<Init*>::const_iterator; using const_name_iterator = SmallVectorImpl<StringInit*>::const_iterator;
DefInit¶DefInit 클래스는 TypedInit의 서브클래스입니다. 그 인스턴스는 TableGen이 수집한 레코드를 나타냅니다. 레코드의 Record 인스턴스에 대한 포인터인 데이터 멤버를 포함합니다.
이 클래스는 통상의 get() 함수를 제공합니다. getValue()는 제공하지 않습니다. 대신 getDef()를 제공하며, 이는 Record 인스턴스를 반환합니다.
IntInit¶IntInit 클래스는 TypedInit의 서브클래스입니다. 그 인스턴스는 64비트 정수의 가능한 값을 나타냅니다. 정수를 담는 데이터 멤버를 포함합니다.
이 클래스는 통상의 get() 및 getValue() 함수를 제공합니다. 후자는 정수를 int64_t로 반환합니다.
또한 이 클래스는 정수 값의 지정된 비트를 얻기 위한 getBit() 함수를 제공합니다.
ListInit¶ListInit 클래스는 TypedInit의 서브클래스입니다. 그 인스턴스는 어떤 타입의 원소 리스트를 나타냅니다. 리스트의 길이를 담는 데이터 멤버와 원소당 하나의 Init 인스턴스 포인터 벡터를 포함합니다.
이 클래스는 통상의 get()과 getValues() 함수를 제공합니다. 후자는 Init 인스턴스 포인터 벡터의 ArrayRef를 반환합니다.
이 클래스는 다음의 추가 함수를 제공합니다.
원소 타입을 가져오는 함수.
벡터의 길이를 가져오고 비어 있는지 판별하는 함수.
정수 인덱스로 지정된 원소를 가져와 다양한 형태로 반환하는 함수.
반복자 begin() 및 end() 값을 가져오는 함수. 이 클래스는 이 두 함수의 반환 타입을 정의합니다.
using const_iterator = Init *const *;
StringInit¶StringInit 클래스는 TypedInit의 서브클래스입니다. 그 인스턴스는 임의 길이의 문자열을 나타냅니다. 값의 StringRef를 담는 데이터 멤버를 포함합니다.
이 클래스는 통상의 get()과 getValue() 함수를 제공합니다. 후자는 StringRef를 반환합니다.
TableGen을 위한 새 백엔드를 만들려면 다음 단계가 필요합니다.
백엔드 C++ 파일의 이름을 정합니다. 예: GenAddressModes.
TableGenBackendSkeleton.cpp 파일을 시작점으로 사용해 새 백엔드를 작성합니다.
어떤 TableGen 인스턴스가 새 백엔드를 필요로 하는지 결정합니다. Clang용 인스턴스 하나와 LLVM용 인스턴스 하나가 있습니다. 또는 직접 자신의 인스턴스를 구축할 수도 있습니다.
백엔드 C++ 파일이 빌드되도록 적절한 CMakeLists.txt 파일에 추가합니다.
C++ 파일을 시스템에 추가합니다.
TableGenBackendSkeleton.cpp 파일은 새 TableGen 백엔드를 작성하기 위한 스켈레톤 C++ 번역 단위를 제공합니다. 파일에 대한 몇 가지 노트는 다음과 같습니다.
포함 목록은 대부분의 백엔드에 필요한 최소 목록입니다.
모든 LLVM C++ 파일과 마찬가지로 using namespace llvm; 문이 있습니다. 또한 익명 네임스페이스가 있으며, 이 안에는 파일 전용 자료구조 정의와 방출기(emitter)의 데이터 멤버 및 함수를 구현하는 클래스가 포함됩니다. GenAddressModes 예를 계속하면, 이 클래스의 이름은 AddressModesEmitter입니다.
방출기 클래스의 생성자는 보통 RK라는 이름의 RecordKeeper 참조를 받습니다. RecordKeeper 참조는 레코드를 얻기 위해 데이터 멤버에 저장됩니다. 이 데이터 멤버는 보통 Records라고 명명됩니다.
함수 하나의 이름은 run입니다. 이는 백엔드의 "메인 함수"가 레코드를 수집하고 출력 파일을 방출하기 위해 호출합니다. 이 함수는 보통 OS라는 이름의 raw_ostream 클래스 인스턴스를 받습니다. 출력 파일은 이 스트림에 쓰는 방식으로 방출됩니다.
run 함수는 방출 파일에 표준 헤더를 포함하기 위해 emitSourceFileHeader 헬퍼 함수를 사용해야 합니다.
llvm/TableGen/TableGenBackend.h를 통해 클래스를 명령줄 옵션으로 등록하십시오.
클래스가 (RK) 생성자와 run(OS) 메서드를 가진다면 llvm::TableGen::Emitter::OptClass<AddressModesEmitter>를 사용합니다.
그렇지 않다면 llvm::TableGen::Emitter::Opt를 사용합니다.
이 문서의 나머지 예제는 스켈레톤 파일에서 사용된 명명 규칙을 따른다고 가정합니다.
RecordKeeper 클래스는 TableGen 파일에서 정의된 클래스에 대한 Record 인스턴스를 얻기 위한 두 가지 함수를 제공합니다.
getClasses()는 모든 클래스에 대한 RecordMap 참조를 반환합니다.
getClass(name)는 지정된 이름의 클래스에 대한 Record 참조를 반환합니다.
모든 클래스 레코드에 대해 반복(iterate)해야 한다면:
for (auto ClassPair : Records.getClasses()) { Record *ClassRec = ClassPair.second.get(); ... }
ClassPair.second로 클래스의 unique_ptr를 얻고, 이어 .get()으로 클래스 Record 자체를 얻습니다.
RecordKeeper 클래스는 TableGen 파일에서 정의된 구체 레코드에 대한 Record 인스턴스를 얻기 위한 네 가지 함수를 제공합니다.
getDefs()는 모든 구체 레코드에 대한 RecordMap 참조를 반환합니다.
getDef(name)는 지정된 이름의 구체 레코드에 대한 Record 참조를 반환합니다.
getAllDerivedDefinitions(classname)은 주어진 클래스로부터 파생된 구체 레코드에 대한 Record 참조 벡터를 반환합니다.
getAllDerivedDefinitions(classnames)은 주어진 클래스들 모두 로부터 파생된 구체 레코드에 대한 Record 참조 벡터를 반환합니다.
다음 문장은 Attribute 클래스로부터 파생된 모든 레코드를 얻고 그 위를 반복합니다.
auto AttrRecords = Records.getAllDerivedDefinitions("Attribute"); for (Record *AttrRec : AttrRecords) { ... }
위에서 설명했듯이(참조: Record), 레코드의 이름을 반환하는 함수는 여러 가지가 있습니다. 특히 유용한 것은 getNameInitAsString()으로, 이름을 std::string으로 반환합니다.
레코드의 필드를 반환하는 함수도 여러 가지가 있습니다. 모든 필드를 얻어 반복하려면:
for (const RecordVal &Field : SomeRec->getValues()) { ... }
RecordVal이 레코드에서 필드 정보를 담는 클래스라는 것을 기억하십시오.
getValue() 함수는 이름으로 지정된 필드에 대한 RecordVal 인스턴스를 반환합니다. 오버로드가 여러 가지 있으며, 일부는 StringRef를 받고 다른 것은 const Init *를 받습니다. 또한 일부 함수는 RecordVal *을 반환하고, 다른 함수는 const RecordVal *을 반환합니다. 필드가 존재하지 않으면 치명적 오류 메시지가 출력됩니다.
대개는 RecordVal의 모든 정보가 아니라, 필드의 값 자체에 관심이 있습니다. 어떤 형태로든 필드 이름을 받아 그 값을 반환하는 함수가 많이 있습니다. 하나의 함수 getValueInit은 값을 Init *로 반환합니다. 또 다른 함수 isValueUnset은 값이 설정되지 않았는지(초기화되지 않았는지)를 나타내는 불리언을 반환합니다.
대부분의 함수는 보다 유용한 형태로 값을 반환합니다. 예를 들어:
std::vector<int64_t> RegCosts = SomeRec->getValueAsListOfInts("RegCosts");
필드 RegCosts는 정수 리스트라고 가정합니다. 이 리스트는 64비트 정수의 std::vector로 반환됩니다. 필드가 정수 리스트가 아니라면 치명적 오류 메시지가 출력됩니다.
다음은 필드 값을 Record로 반환하지만, 필드가 존재하지 않으면 null을 반환하는 함수입니다.
if (Record *BaseRec = SomeRec->getValueAsOptionalDef(BaseFieldName)) { ... }
필드는 다른 레코드를 값으로 가진다고 가정합니다. 해당 레코드는 Record 포인터로 반환됩니다. 필드가 존재하지 않거나 설정되지 않은 경우, 이 함수는 null을 반환합니다.
Record 클래스는 레코드의 직접 슈퍼클래스를 얻는 함수를 제공합니다. 이 함수의 이름은 getDirectSuperClasses이며, std::pair 쌍의 배열에 대한 ArrayRef를 반환합니다. 각 쌍은 슈퍼클래스 레코드의 Record 인스턴스 포인터와 SMRange 클래스의 인스턴스로 이루어집니다. 이 범위는 클래스 정의의 시작과 끝의 소스 파일 위치를 나타냅니다.
다음 예는 Prototype 레코드의 직접 슈퍼클래스를 얻은 다음, 반환된 배열의 쌍들을 반복합니다.
ArrayRef<std::pair<const Record *, SMRange>> Superclasses = Prototype->getDirectSuperClasses(); for (const auto &[Super, Range] : Superclasses) { ... }
Record 클래스는 또한 레코드의 모든 슈퍼클래스를 반환하는 getSuperClasses 함수를 제공합니다. 슈퍼클래스는 포스트오더(post-order)로 제공됩니다. 즉, 레코드에 필드를 복사하는 동안 슈퍼클래스를 방문한 순서입니다.
run 함수에는 출력 파일을 출력할 raw_ostream이 전달됩니다. 관례적으로, 이 스트림은 방출기 클래스의 OS라는 멤버에 저장되지만, 일부 run 함수는 단순하여 저장하지 않고 바로 사용합니다. 출력은 값을 출력 스트림에 직접 쓰거나 std::format() 또는 llvm::formatv() 함수를 사용해 생성할 수 있습니다.
OS << "#ifndef " << NodeName << "\n";
OS << format("0x%0*x, ", Digits, Value);
다음 클래스의 인스턴스는 << 연산자를 사용해 출력할 수 있습니다: RecordKeeper, Record, RecTy, RecordVal, Init.
헬퍼 함수 emitSourceFileHeader()는 모든 출력 파일의 맨 위에 포함되어야 하는 헤더 주석을 출력합니다. 이 호출은 스켈레톤 백엔드 파일 TableGenBackendSkeleton.cpp에 포함되어 있습니다.
TableGen 레코드는 종종 여러 클래스에서 파생되고, 또한 종종 일련의 멀티클래스를 통해 정의됩니다. 이 때문에, 백엔드가 정확한 소스 파일 위치와 함께 명확한 오류 메시지를 보고하기가 어려울 수 있습니다. 오류 보고를 쉽게 하기 위해, 네 가지 오버로드를 각각 가진 다섯 가지 오류 보고 함수가 제공됩니다.
PrintWarning은 경고로 태그된 메시지를 출력합니다.
PrintError는 오류로 태그된 메시지를 출력합니다.
PrintFatalError는 오류로 태그된 메시지를 출력한 후 종료합니다.
PrintNote는 노트를 출력합니다. 종종 앞선 함수들 중 하나 뒤에 추가 정보를 제공하기 위해 사용됩니다.
PrintFatalNote는 노트를 출력한 후 종료합니다.
이 다섯 함수 각각은 네 번 오버로드되어 있습니다.
PrintError(const Twine &Msg): 소스 파일 위치 없이 메시지를 출력합니다.
PrintError(ArrayRef<SMLoc> ErrorLoc, const Twine &Msg): 지정된 소스 줄과 오류 항목을 가리키는 포인터를 메시지 뒤에 출력합니다. 소스 파일 위치의 배열은 보통 Record 인스턴스에서 가져옵니다.
PrintError(const Record *Rec, const Twine &Msg): 지정된 레코드와 연관된 소스 줄을 메시지 뒤에 출력합니다(참조: Record).
PrintError(const RecordVal *RecVal, const Twine &Msg): 지정된 레코드 필드와 연관된 소스 줄을 메시지 뒤에 출력합니다(참조: RecordVal).
이들 함수를 사용하여 가능한 한 가장 구체적인 오류 보고를 하는 것이 목표입니다.
TableGen은 백엔드 디버깅을 돕기 위한 몇 가지 도구를 제공합니다.
PrintRecords 백엔드¶TableGen 명령 옵션 --print-records는 소스 파일에서 정의된 모든 클래스와 레코드를 출력하는 간단한 백엔드를 호출합니다. 이는 기본 백엔드 옵션입니다. 출력 형식은 시간이 지나도 일정함이 보장되어, 테스트에서 출력 비교가 가능하도록 합니다. 출력은 다음과 같습니다:
------------- Classes ----------------- ... class XEntry<string XEntry:str = ?, int XEntry:val1 = ?> { // XBase string Str = XEntry:str; bits<8> Val1 = { !cast<bits<8>>(XEntry:val1){7}, ... }; bit Val3 = 1; } ... ------------- Defs ----------------- def ATable { // GenericTable string FilterClass = "AEntry"; string CppTypeName = "AEntry"; list<string> Fields = ["Str", "Val1", "Val2"]; list<string> PrimaryKey = ["Val1", "Val2"]; string PrimaryKeyName = "lookupATableByValues"; bit PrimaryKeyEarlyOut = 0; } ... def anonymous_0 { // AEntry string Str = "Bob"; bits<8> Val1 = { 0, 0, 0, 0, 0, 1, 0, 1 }; bits<10> Val2 = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 }; }
클래스는 템플릿 인자, 부모 클래스(// 다음), 필드와 함께 표시됩니다. 레코드는 부모 클래스와 필드와 함께 표시됩니다. 익명 레코드는 anonymous_0, anonymous_1 등으로 명명된다는 점에 유의하십시오.
PrintDetailedRecords 백엔드¶TableGen 명령 옵션 --print-detailed-records는 소스 파일에서 정의된 모든 전역 변수, 클래스, 레코드를 출력하는 백엔드를 호출합니다. 출력 형식은 시간이 지나도 일정함이 보장되지 않습니다. 출력은 다음과 같습니다.
DETAILED RECORDS for file llvm-project\llvm\lib\target\arc\arc.td
-------------------- Global Variables (5) --------------------
AMDGPUBufferIntrinsics = [int_amdgcn_s_buffer_load, ... AMDGPUImageDimAtomicIntrinsics = [int_amdgcn_image_atomic_swap_1d, ... ... -------------------- Classes (758) --------------------
AMDGPUBufferLoad |IntrinsicsAMDGPU.td:879| Template args: LLVMType AMDGPUBufferLoad:data_ty = llvm_any_ty |IntrinsicsAMDGPU.td:879| Superclasses: (SDPatternOperator) Intrinsic AMDGPURsrcIntrinsic Fields: list<SDNodeProperty> Properties = [SDNPMemOperand] |Intrinsics.td:348| string LLVMName = "" |Intrinsics.td:343| ... -------------------- Records (12303) --------------------
AMDGPUSample_lz_o |IntrinsicsAMDGPU.td:560| Defm sequence: |IntrinsicsAMDGPU.td:584| |IntrinsicsAMDGPU.td:566| Superclasses: AMDGPUSampleVariant Fields: string UpperCaseMod = "_LZ_O" |IntrinsicsAMDGPU.td:542| string LowerCaseMod = "_lz_o" |IntrinsicsAMDGPU.td:543| ...
바깥쪽 defvar 문으로 정의된 전역 변수는 그 값과 함께 표시됩니다.
클래스는 소스 위치, 템플릿 인자, 슈퍼클래스, 필드와 함께 표시됩니다.
레코드는 소스 위치, defm 시퀀스, 슈퍼클래스, 필드와 함께 표시됩니다.
슈퍼클래스는 처리된 순서대로 표시되며, 간접 슈퍼클래스는 괄호로 표시됩니다. 각 필드는 그 값과 설정된 소스 위치와 함께 표시됩니다. defm 시퀀스는 레코드 생성에 관여한 defm 문들의 위치를, 호출된 순서대로 제공합니다.
TableGen은 소스 파일 파싱과 선택된 백엔드 실행의 다양한 단계가 사용한 시간을 보고하는 단계 타이밍 기능을 제공합니다. 이 기능은 TableGen 명령의 --time-phases 옵션으로 활성화됩니다.
백엔드가 타이밍 계측이 되어 있지 않은 경우, 다음과 같은 보고가 생성됩니다. 이는 AMDGPU 타깃에서 --print-detailed-records 백엔드 실행의 타이밍입니다.
===-------------------------------------------------------------------------=== TableGen Phase Timing ===-------------------------------------------------------------------------=== Total Execution Time: 101.0106 seconds (102.4819 wall clock)
---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name --- 85.5197 ( 84.9%) 0.1560 ( 50.0%) 85.6757 ( 84.8%) 85.7009 ( 83.6%) Backend overall 15.1789 ( 15.1%) 0.0000 ( 0.0%) 15.1789 ( 15.0%) 15.1829 ( 14.8%) Parse, build records 0.0000 ( 0.0%) 0.1560 ( 50.0%) 0.1560 ( 0.2%) 1.5981 ( 1.6%) Write output 100.6986 (100.0%) 0.3120 (100.0%) 101.0106 (100.0%) 102.4819 (100.0%) Total
모든 백엔드 시간이 "Backend overall" 아래에 합쳐져 있다는 점에 유의하십시오.
백엔드가 타이밍 계측되어 있다면, 처리 과정이 단계로 나뉘어 각 단계가 별도로 타이밍됩니다. 이는 AMDGPU 타깃에서 --emit-dag-isel 백엔드 실행의 타이밍입니다.
===-------------------------------------------------------------------------=== TableGen Phase Timing ===-------------------------------------------------------------------------=== Total Execution Time: 746.3868 seconds (747.1447 wall clock)
---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name --- 657.7938 ( 88.1%) 0.1404 ( 90.0%) 657.9342 ( 88.1%) 658.6497 ( 88.2%) Emit matcher table 70.2317 ( 9.4%) 0.0000 ( 0.0%) 70.2317 ( 9.4%) 70.2700 ( 9.4%) Convert to matchers 14.8825 ( 2.0%) 0.0156 ( 10.0%) 14.8981 ( 2.0%) 14.9009 ( 2.0%) Parse, build records 2.1840 ( 0.3%) 0.0000 ( 0.0%) 2.1840 ( 0.3%) 2.1791 ( 0.3%) Sort patterns 1.1388 ( 0.2%) 0.0000 ( 0.0%) 1.1388 ( 0.2%) 1.1401 ( 0.2%) Optimize matchers 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0050 ( 0.0%) Write output 746.2308 (100.0%) 0.1560 (100.0%) 746.3868 (100.0%) 747.1447 (100.0%) Total
백엔드는 네 단계로 나뉘어 각각 타이밍되었습니다.
백엔드를 계측하고 싶다면, 백엔드 DAGISelEmitter.cpp를 참고하고 Records.startTimer를 검색하십시오.