Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ClangにReflection提案っぽいものを実装してみた話

D4d4d55a0d86c4dcca60a2a415dfb22e?s=47 Koudai Iwahori
April 17, 2019
470

 ClangにReflection提案っぽいものを実装してみた話

D4d4d55a0d86c4dcca60a2a415dfb22e?s=128

Koudai Iwahori

April 17, 2019
Tweet

Transcript

  1. ClangにReflection提案っぽい ものを実装してみた話 @HO_RI9191 C++ MIX #3

  2. お詫び • 今日はMeta classの話はしません • MetaClassっぽいものは実装はしましたがReflectionの話でスライドがいっぱいに なってしまいました

  3. 何したの? • C++コンパイラのお勉強がてらReflection提案(P1240)とMetaclass提案 (P0707) を雑にClangに実装してみた ◦ https://github.com/ho-ri1991/clang-reflection-metaclass • コンパイラ初心者なのでガチプロの方はマサカリをご準備ください ※真面目にメンテが必要なプロジェクトの場合は

    Clang Toolingなどで頑張る方が良いと思います c.f. CppCon 2018 “Building a C++ Reflection System in One Weekend Using Clang and LLVM” ※提案者(Andrew Sutton)の実装はこれ https://github.com/asutton/clang (読んでいないが、間違いなくこっちのほうが良い実装になっていると思います)
  4. Clang概観

  5. Clang/LLVM https://www.aosabook.org/en/llvm.html#fig.llvm.lcom

  6. Clang Parser Lexer AST CodeGen Sema main.cpp “int” “main” (“

    ...... LLVM IR https://github.com/llvm-mirror/clang/tree/master/include/clang
  7. Clang Parser Lexer AST CodeGen Sema main.cpp “int” “main” “(“

    “)” LLVM IR clang -Xclang -dump-tokens -fsyntax-only main.cpp
  8. Clang clang main.cpp -Xclang -ast-dump -fsyntax-only Parser Lexer AST CodeGen

    Sema main.cpp “int” “main” “(“ “)” LLVM IR
  9. Clang clang main.cpp -S -emit-llvm -o - Parser Lexer AST

    CodeGen Sema main.cpp “int” “main” “(“ “)” LLVM IR
  10. Reflection

  11. Reflection? • プログラムがプログラム自身の構造を読み取れるようにする言語機能 • メタプログラミングを可能にする ◦ ソースコードをデータとして取り込む ◦ 何らかの操作をしてコード生成をする •

    Dynamic Reflectionはいろんな言語で入っている ◦ なんだかんだあると便利だがコンパイル時計算とかには使えない
  12. Reflection in C++ • ご存知のようにコンパイル時計算の需要がある ◦ Type traitsとかでいろいろ情報がとれるし 頑張ればいろいろ情報がとれる ◦

    Conceptも入る • ただ、取れないものも多い ◦ クラス名 ◦ メンバ変数・関数、enumフィールドのリスト ◦ 足りないものが多い
  13. Reflection TS (N4766, P0385) isocpp.org/std/status Approved for publication @ Kona

    meeting https://isocpp.org/files/img/wg21-timeline-2019-04.png
  14. Reflection TS (N4766, P0385) • Reflection TSではStatic Reflectionを入れようとしている ◦ より包括的なリフレクションのサポート

    • コンパイル時にクラス・名前空間などのメタ情報を取れるようになる ◦ クラス名、Enumerator名、名前空間名 ◦ メンバ変数/関数のリスト (名前と型)、public/private、ポインタなど ◦ 基本は名前がついているものから取れる • 式の構文木は取れない • TMPをベースとした設計 • 今回は省略
  15. Reflection TSの構文 • reflexpr ( operand ) という構文 ◦ メタ情報が入った実装依存の型が返ってくる

    ※非表示スライド
  16. 具体的に ※非表示スライド

  17. Reflection TSの構文 • 名前を持ったローカルではないものであればReflectionを取れる ◦ 何が取れるかは今後変わると思います 1+1とかの式はダメ function-call-expr func-type-conv-exprだけ 関数名、テンプレート名はダメ

    ローカルなEntityもダメ 関数引数、ラムダキャプチャは OK ※非表示スライド
  18. Value Based Reflection (P1240) • 最近のconstexpr強化提案を踏まえてconstexprベースのStatic Reflectionにし ようとしている ◦ TMPはコンパイル時にメモリを食いまくる

    ◦ TMPはできれば読みたくない ◦ constexpr vectorやExpansion Statementを前提として作っている
  19. Value Based Reflectionの構文 • reflexpr ( operand ) という構文 ◦

    メタ情報が入った実装依存の値が返ってくる
  20. Enum to String (Value Based Reflection) (P1240)

  21. Enum to String (Value Based Reflection) Enumのメタ情報を保持した値

  22. Enum to String (Value Based Reflection) EnumのreflectoinからEnumメンバのreflectionが入っ たconstexpr vector(P0784)を構築してくれるconstexpr 関数

    Enumのメタ情報を保持した値
  23. Enum to String (Value Based Reflection) Enumメンバの情報が入った constexprな vector(P0784)が返ってくる reflectionを値・型などに戻す演算子

    Enumのメタ情報を保持した値
  24. Enum to String (Value Based Reflection) Expansion Statement (P1306) (コンパイル時vectorやtupleのloop

    expansion) Enumメンバの情報が入った constexprな vector(P0784)が返ってくる Enumのメタ情報を保持した値
  25. Enum to String (Value Based Reflection)

  26. Enum to String (Value Based Reflection)

  27. Value Based Reflectionを実装してみる

  28. Value Based Reflectionを実装してみる Enumのメタ情報を保持した値

  29. Enumのメタ情報を保持した値 ? • コンパイラ内部のASTノードのポインタ = std::intptr_t (※実装例の一つ) ◦ P1240, P0721では関係ない整数型への暗黙の型変換を防ぐために

    enum classやstructしてる clang main.cpp -Xclang -ast-dump -fsyntax-only
  30. Enumのメタ情報を保持した値 ? • コンパイラ内部のASTノードのポインタ = std::intptr_t (※実装例の一つ) ◦ P1240, P0721では関係ない整数型への暗黙の型変換を防ぐために

    enum classやstructしてる clang main.cpp -Xclang -ast-dump -fsyntax-only ASTノードのアドレス
  31. Enumのメタ情報を保持した値 ? ASTノードのポインタを返すようにする このASTポインタを用いてReflectionのライブラリ関数群を実装する

  32. 構文を追加する Parser Lexer AST CodeGen Sema main.cpp “int” “main” “{“

    ...... LLVM IR このあたりを拡張する Parser, Semaは省略 ※予約後にする場合はLexerも必要 ※Reflectionはコンパイル時計算なので  コード生成は触らなくてもできる ※実際どうやるのが正攻法かはわからない
  33. Parser • Parse*()というメンバ関数が大量に生えている(再帰下降パーサ) • ParseAssignmentExpressionが走る直前にreflexprかどうかチェックする ◦ reflexprだったらreflexpr(T);まで読んでからSemaのメンバ関数を呼ぶ ◦ 具体的な方針は “Syntax

    macros - a case study in extending Clang - LLVM Cauldron 2016” https://clang.llvm.org/doxygen/classclang_1_1Parser.html ※非表示スライド
  34. Sema • ActOn*()/Build*()というメンバ関数が大量に生えている(AST組み立て) • reflexpr用の意味解析メンバ関数を生やす ◦ Parserから呼ばれてreflexpr用のASTノードを作る https://clang.llvm.org/doxygen/classclang_1_1Sema.html ※非表示スライド

  35. AST (Node) • clang::Stmt ◦ If/For/BinaryOp/IntegerLiteralなどの文/式を表すASTノードの基底クラス • clang::Decl ◦ Enum/EnumField/class/struct/variable/Functionなどを表すASTノードの基底クラス

    clang::EnumDecl clang::Decl clang::EnumConstantDecl
  36. sizeof/alignofを参考にする • reflexprの構造はsizeofとだいたい同じ clang main.cpp -Xclang -ast-dump -fsyntax-only

  37. sizeof/alignofのASTノード

  38. reflexprのASTノード • siezof/alignofのコピペ clang::Reflexpr

  39. コンパイル時評価方法を実装する コンパイル時計算のときにASTノードのポインタを返すようにする このASTを用いてReflectionのライブラリ関数群を実装する

  40. Constexpr関数/変数の評価 • Constexprの評価は再帰的にASTノードを評価することでできる ◦ 各ノードの評価方法を実装する( c.f. clang/lib/AST/ExprConstant.cpp) • if statmentの具体例は次のページ

  41. ※実際のコードとは少し違います if condition Then else

  42. コンパイル時評価方法を実装する • 戻り値の型はstd::uintptr_t • 値は型T(もしくは式)のAST(もしくはそれ導出できる内部ハンドル)

  43. リフレクションライブラリ関数 • ASTポインタを整数にしたものをポインタに戻してから計算を行う ◦ 本当はちょっと面倒なことがいくつかあるが略 ※enum_sizeなんてものは  多分作られないと思います

  44. Enum to String in Value Base Reflection Expansion Statement (P1306)

    (コンパイル時vectorやtupleのloop expansion)
  45. Expansion Statement (P1360) • Reflection自体は最終的なバイナリには残ってはいけない ◦ コンパイラ内のASTのポインタをバイナリに残してもアクセスできない • loop expansionしないと実行時にASTが必要になってしまう

  46. 偽Expansion Statementを実装する • constexpr vectorを作るのはちょっと大変 • やりたいことはメタ情報のリストを返すリフレクションのloop expansionなのでそれ だけ対応する •

    やることは単純 ◦ forのbodyをパースしてbodyのASTを作る ◦ リストサイズの分だけその bodyをコピーする ◦ ループ変数の初期値をそれぞれ書き換える ▪ 実際には細かいところで結構ハマった
  47. Reflection TSを越えて • identifierを生成して変数・関数宣言 ◦ 今はプリプロセッサマクロでしかできない ◦ Type/Value Based Reflection

    (P0385/P1240)提案に含まれている • より簡単なCode Injectionの機構 (P0712, p0633) • Metaclass ◦ constexpr関数でクラスの生成規則を作る
  48. References • clang ◦ https://clang.llvm.org/doxygen/index.html ◦ https://clang.llvm.org/docs/InternalsManual.html ◦ https://llvm.org/devmtg/2016-09/slides/Rink-SyntaxMacros.pdf ◦

    https://www.youtube.com/watch?v=DUiUBt-fqEY • Reflection ◦ Reflection TS : n4750 ◦ Type Based Reflection : p0385 ◦ Value Based Reflection : p1240 ◦ Expansion Statements : p1306 ◦ p0712 ◦ p0993 ◦ https://github.com/asutton/clang