brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.2 KiB · e7e8630 Raw
659 lines · plain
1===========2Clang-Repl3===========4 5**Clang-Repl** is an interactive C++ interpreter that allows for incremental6compilation. It supports interactive programming for C++ in a7read-evaluate-print-loop (REPL) style. It uses Clang as a library to compile the8high level programming language into LLVM IR. Then the LLVM IR is executed by9the LLVM just-in-time (JIT) infrastructure.10 11Clang-Repl is suitable for exploratory programming and in places where time12to insight is important. Clang-Repl is a project inspired by the work in13`Cling <https://github.com/root-project/cling>`_, a LLVM-based C/C++ interpreter14developed by the field of high energy physics and used by the scientific data15analysis framework `ROOT <https://root.cern/>`_. Clang-Repl allows to move parts16of Cling upstream, making them useful and available to a broader audience.17 18 19Clang-Repl Basic Data Flow20==========================21 22.. image:: ClangRepl_design.png23   :align: center24   :alt: ClangRepl design25 26Clang-Repl data flow can be divided into roughly 8 phases:27 281. Clang-Repl controls the input infrastructure by an interactive prompt or by29   an interface allowing the incremental processing of input.30 312. Then it sends the input to the underlying incremental facilities in Clang32   infrastructure.33 343. Clang compiles the input into an AST representation.35 364. When required the AST can be further transformed in order to attach specific37   behavior.38 395. The AST representation is then lowered to LLVM IR.40 416. The LLVM IR is the input format for LLVM’s JIT compilation infrastructure.42   The tool will instruct the JIT to run specified functions, translating them43   into machine code targeting the underlying device architecture (eg. Intel44   x86 or NVPTX).45 467. The LLVM JIT lowers the LLVM IR to machine code.47 488. The machine code is then executed.49 50Build Instructions:51===================52 53 54.. code-block:: console55 56   $ cd llvm-project57   $ mkdir build58   $ cd build59   $ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLLVM_ENABLE_PROJECTS=clang -G "Unix Makefiles" ../llvm60 61**Note here**, above RelWithDebInfo - Debug / Release62 63.. code-block:: console64 65   cmake --build . --target clang clang-repl -j n66      OR67   cmake --build . --target clang clang-repl68 69**Clang-repl** is built under llvm-project/build/bin. Proceed into the directory **llvm-project/build/bin**70 71.. code-block:: console72 73   ./clang-repl74   clang-repl>75 76 77Clang-Repl Usage78================79 80**Clang-Repl** is an interactive C++ interpreter that allows for incremental81compilation. It supports interactive programming for C++ in a82read-evaluate-print-loop (REPL) style. It uses Clang as a library to compile the83high level programming language into LLVM IR. Then the LLVM IR is executed by84the LLVM just-in-time (JIT) infrastructure.85 86 87Basic:88======89 90.. code-block:: text91 92  clang-repl> #include <iostream>93  clang-repl> int f() { std::cout << "Hello Interpreted World!\n"; return 0; }94  clang-repl> auto r = f();95   // Prints Hello Interpreted World!96 97.. code-block:: text98 99   clang-repl> #include<iostream>100   clang-repl> using namespace std;101   clang-repl> std::cout << "Welcome to CLANG-REPL" << std::endl;102   Welcome to CLANG-REPL103   // Prints Welcome to CLANG-REPL104 105 106Function Definitions and Calls:107===============================108 109.. code-block:: text110 111   clang-repl> #include <iostream>112   clang-repl> int sum(int a, int b){ return a+b; };113   clang-repl> int c = sum(9,10);114   clang-repl> std::cout << c << std::endl;115   19116   clang-repl>117 118Iterative Structures:119=====================120 121.. code-block:: text122 123   clang-repl> #include <iostream>124   clang-repl> for (int i = 0;i < 3;i++){ std::cout << i << std::endl;}125   0126   1127   2128   clang-repl> while(i < 7){ i++; std::cout << i << std::endl;}129   4130   5131   6132   7133 134Classes and Structures:135=======================136 137.. code-block:: text138 139   clang-repl> #include <iostream>140   clang-repl> class Rectangle {int width, height; public: void set_values (int,int);\141   clang-repl... int area() {return width*height;}};142   clang-repl>  void Rectangle::set_values (int x, int y) { width = x;height = y;}143   clang-repl> int main () { Rectangle rect;rect.set_values (3,4);\144   clang-repl... std::cout << "area: " << rect.area() << std::endl;\145   clang-repl... return 0;}146   clang-repl> main();147   area: 12148   clang-repl>149   // Note: This '\' can be used for continuation of the statements in the next line150 151Lamdas:152=======153 154.. code-block:: text155 156   clang-repl> #include <iostream>157   clang-repl> using namespace std;158   clang-repl> auto welcome = []()  { std::cout << "Welcome to REPL" << std::endl;};159   clang-repl> welcome();160   Welcome to REPL161 162Comments:163=========164 165.. code-block:: text166 167   clang-repl> // Comments in Clang-Repl168   clang-repl> /* Comments in Clang-Repl */169 170Built in Commands:171==================172clang-repl has some special commands that are of the form ``%<something>``. To list all these commands, use the ``%help`` command:173 174Help:175-----176The ``%help`` command lists all built in clang-repl commands.177 178.. code-block:: text179 180   clang-repl> %help181 182Using Dynamic Libraries:183------------------------184The ``%lib`` command links a dynamic library.185 186.. code-block:: text187 188   clang-repl> %lib print.so189   clang-repl> #include"print.hpp"190   clang-repl> print(9);191   9192 193**Generation of dynamic library**194 195.. code-block:: text196 197   // print.cpp198   #include <iostream>199   #include "print.hpp"200 201   void print(int a)202   {203      std::cout << a << std::endl;204   }205 206   // print.hpp207   void print (int a);208 209   // Commands210   clang++-17  -c -o print.o print.cpp211   clang-17 -shared print.o -o print.so212 213Undo:214-----215The ``%undo`` command undoes the previous input.216 217.. code-block:: text218 219   clang-repl> int a = 1; a220   (int) 1221   clang-repl> %undo222   clang-repl> a223   In file included from <<< inputs >>>:1:224   input_line_2:1:1: error: use of undeclared identifier 'a'225   1 | a226   * | ^227   error: Parsing failed.228 229Quit:230-----231The ``%quit`` command terminates clang-repl.232 233.. code-block:: text234 235   clang-repl> %quit236 237 238 239Just like Clang, Clang-Repl can be integrated in existing applications as a library240(using the clangInterpreter library). This turns your C++ compiler into a service that241can incrementally consume and execute code. The **Compiler as A Service** (**CaaS**)242concept helps support advanced use cases such as template instantiations on demand and243automatic language interoperability. It also helps static languages such as C/C++ become244apt for data science.245 246Execution Results Handling in Clang-Repl247========================================248 249Execution Results Handling features discussed below help extend the Clang-Repl250functionality by creating an interface between the execution results of a251program and the compiled program.252 2531. **Capture Execution Results**: This feature helps capture the execution results254of a program and bring them back to the compiled program.255 2562. **Dump Captured Execution Results**: This feature helps create a temporary dump257for Value Printing/Automatic Printf, that is, to display the value and type of258the captured data.259 260 2611. Capture Execution Results262============================263 264In many cases, it is useful to bring back the program execution result to the265compiled program. This result can be stored in an object of type **Value**.266 267How Execution Results are captured (Value Synthesis):268-----------------------------------------------------269 270The synthesizer chooses which expression to synthesize, and then it replaces271the original expression with the synthesized expression. Depending on the272expression type, it may choose to save an object (``LastValue``) of type 'value'273while allocating memory to it (``SetValueWithAlloc()``), or not (274``SetValueNoAlloc()``).275 276.. graphviz::277    :name: valuesynthesis278    :caption: Value Synthesis279    :alt: Shows how an object of type 'Value' is synthesized280    :align: center281 282     digraph "valuesynthesis" {283         rankdir="LR";284         graph [fontname="Verdana", fontsize="12"];285         node [fontname="Verdana", fontsize="12"];286         edge [fontname="Sans", fontsize="9"];287 288         start [label=" Create an Object \n 'Last Value' \n of type 'Value' ", shape="note", fontcolor=white, fillcolor="#3333ff", style=filled];289         assign [label=" Assign the result \n to the 'LastValue' \n (based on respective \n Memory Allocation \n scenario) ", shape="box"]290         print [label=" Pretty Print \n the Value Object ", shape="Msquare", fillcolor="yellow", style=filled];291         start -> assign;292         assign -> print;293 294           subgraph SynthesizeExpression {295             synth [label=" SynthesizeExpr() ", shape="note", fontcolor=white, fillcolor="#3333ff", style=filled];296             mem [label=" New Memory \n Allocation? ", shape="diamond"];297             withaloc [label=" SetValueWithAlloc() ", shape="box"];298             noaloc [label=" SetValueNoAlloc() ", shape="box"];299             right [label=" 1. RValue Structure \n (a temporary value)", shape="box"];300             left2 [label=" 2. LValue Structure \n (a variable with \n an address)", shape="box"];301             left3 [label=" 3. Built-In Type \n (int, float, etc.)", shape="box"];302             output [label=" move to 'Assign' step ", shape="box"];303 304             synth -> mem;305             mem -> withaloc [label="Yes"];306             mem -> noaloc [label="No"];307             withaloc -> right;308             noaloc -> left2;309             noaloc -> left3;310             right -> output;311             left2 -> output;312             left3 -> output;313      }314            output -> assign315      }316 317Where is the captured result stored?318------------------------------------319 320``LastValue`` holds the last result of the value printing. It is a class member321because it can be accessed even after subsequent inputs.322 323**Note:** If no value printing happens, then it is in an invalid state.324 325Improving Efficiency and User Experience326----------------------------------------327 328The Value object is essentially used to create a mapping between an expression329'type' and the allocated 'memory'. Built-in types (bool, char, int,330float, double, etc.) are copyable. Their memory allocation size is known331and the Value object can introduce a small-buffer optimization.332In case of objects, the ``Value`` class provides reference-counted memory333management.334 335The implementation maps the type as written and the Clang Type to be able to use336the preprocessor to synthesize the relevant cast operations. For example,337``X(char, Char_S)``, where ``char`` is the type from the language's type system338and ``Char_S`` is the Clang builtin type which represents it. This mapping helps339to import execution results from the interpreter in a compiled program and vice340versa. The ``Value.h`` header file can be included at runtime and this is why it341has a very low token count and was developed with strict constraints in mind.342 343This also enables the user to receive the computed 'type' back in their code344and then transform the type into something else (e.g., re-cast a double into345a float). Normally, the compiler can handle these conversions transparently,346but in interpreter mode, the compiler cannot see all the 'from' and 'to' types,347so it cannot implicitly do the conversions. So this logic enables providing348these conversions on request.349 350On-request conversions can help improve the user experience, by allowing351conversion to a desired 'to' type, when the 'from' type is unknown or unclear.352 353Significance of this Feature354----------------------------355 356The 'Value' object enables wrapping a memory region that comes from the357JIT, and bringing it back to the compiled code (and vice versa).358This is a very useful functionality when:359 360- connecting an interpreter to the compiled code, or361- connecting an interpreter in another language.362 363For example, this feature helps transport values across boundaries. A notable364example is the cppyy project code makes use of this feature to enable running C++365within Python. It enables transporting values/information between C++366and Python.367 368Note: `cppyy <https://github.com/wlav/cppyy/>`_ is an automatic, run-time,369Python-to-C++ bindings generator, for calling C++ from Python and Python from C++.370It uses LLVM along with a C++ interpreter (e.g., Cling) to enable features like371run-time instantiation of C++ templates, cross-inheritance, callbacks,372auto-casting, transparent use of smart pointers, etc.373 374In a nutshell, this feature enables a new way of developing code, paving the375way for language interoperability and easier interactive programming.376 377Implementation Details378======================379 380Interpreter as a REPL vs. as a Library381--------------------------------------382 3831 - If we're using the interpreter in interactive (REPL) mode, it will dump384the value (i.e., value printing).385 386.. code-block:: console387 388  if (LastValue.isValid()) {389    if (!V) {390      LastValue.dump();391      LastValue.clear();392    } else393      *V = std::move(LastValue);394  }395 396 3972 - If we're using the interpreter as a library, then it will pass the value398to the user.399 400Incremental AST Consumer401------------------------402 403The ``IncrementalASTConsumer`` class wraps the original code generator404``ASTConsumer`` and it performs a hook, to traverse all the top-level decls, to405look for expressions to synthesize, based on the ``isSemiMissing()`` condition.406 407If this condition is found to be true, then ``Interp.SynthesizeExpr()`` will be408invoked.409 410**Note:** Following is a sample code snippet. Actual code may vary over time.411 412.. code-block:: console413 414    for (Decl *D : DGR)415      if (auto *TSD = llvm::dyn_cast<TopLevelStmtDecl>(D);416          TSD && TSD->isSemiMissing())417        TSD->setStmt(Interp.SynthesizeExpr(cast<Expr>(TSD->getStmt())));418 419    return Consumer->HandleTopLevelDecl(DGR);420 421The synthesizer will then choose the relevant expression, based on its type.422 423Communication between Compiled Code and Interpreted Code424--------------------------------------------------------425 426In Clang-Repl there is **interpreted code**, and this feature adds a 'value'427runtime that can talk to the **compiled code**.428 429Following is an example where the compiled code interacts with the interpreter430code. The execution results of an expression are stored in the object 'V' of431type Value. This value is then printed, effectively helping the interpreter432use a value from the compiled code.433 434.. code-block:: console435 436    int Global = 42;437    void setGlobal(int val) { Global = val; }438    int getGlobal() { return Global; }439    Interp.ParseAndExecute(“void setGlobal(int val);”);440    Interp.ParseAndExecute(“int getGlobal();”);441    Value V;442    Interp.ParseAndExecute(“getGlobal()”, &V);443    std::cout << V.getAs<int>() << “\n”; // Prints 42444 445 446**Note:** Above is an example of interoperability between the compiled code and447the interpreted code. Interoperability between languages (e.g., C++ and Python)448works similarly.449 450 4512. Dump Captured Execution Results452==================================453 454This feature helps create a temporary dump to display the value and type455(pretty print) of the desired data. This is a good way to interact with the456interpreter during interactive programming.457 458How value printing is simplified (Automatic Printf)459---------------------------------------------------460 461The ``Automatic Printf`` feature makes it easy to display variable values during462program execution. Using the ``printf`` function repeatedly is not required.463This is achieved using an extension in the ``libclangInterpreter`` library.464 465To automatically print the value of an expression, simply write the expression466in the global scope **without a semicolon**.467 468.. graphviz::469    :name: automaticprintf470    :caption: Automatic PrintF471    :alt: Shows how Automatic PrintF can be used472    :align: center473 474     digraph "AutomaticPrintF" {475         size="6,4";476         rankdir="LR";477         graph [fontname="Verdana", fontsize="12"];478         node [fontname="Verdana", fontsize="12"];479         edge [fontname="Sans", fontsize="9"];480 481         manual [label=" Manual PrintF ", shape="box"];482         int1 [label=" int ( &) 42 ", shape="box"]483         auto [label=" Automatic PrintF ", shape="box"];484         int2 [label=" int ( &) 42 ", shape="box"]485 486         auto -> int2 [label="int x = 42; \n x"];487         manual -> int1 [label="int x = 42; \n printf(&quot;(int &) %d \\n&quot;, x);"];488     }489 490 491Significance of this feature492----------------------------493 494Inspired by a similar implementation in `Cling <https://github.com/root-project/cling>`_,495this feature added to upstream Clang repo has essentially extended the syntax of496C++, so that it can be more helpful for people that are writing code for data497science applications.498 499This is useful, for example, when you want to experiment with a set of values500against a set of functions, and you'd like to know the results right away.501This is similar to how Python works (hence its popularity in data science502research), but the superior performance of C++, along with this flexibility503makes it a more attractive option.504 505Implementation Details506======================507 508Parsing mechanism:509------------------510 511The Interpreter in Clang-Repl (``Interpreter.cpp``) includes the function512``ParseAndExecute()`` that can accept a 'Value' parameter to capture the result.513But if the value parameter is made optional and it is omitted (i.e., that the514user does not want to utilize it elsewhere), then the last value can be515validated and pushed into the ``dump()`` function.516 517.. graphviz::518    :name: parsing519    :caption: Parsing Mechanism520    :alt: Shows the Parsing Mechanism for Pretty Printing521    :align: center522 523 524     digraph "prettyprint" {525         rankdir="LR";526         graph [fontname="Verdana", fontsize="12"];527         node [fontname="Verdana", fontsize="12"];528         edge [fontname="Verdana", fontsize="9"];529 530         parse [label=" ParseAndExecute() \n in Clang ", shape="box"];531         capture [label=" Capture 'Value' parameter \n for processing? ", shape="diamond"];532         use [label="  Use for processing  ", shape="box"];533         dump [label="  Validate and push  \n to dump()", shape="box"];534         callp [label="  call print() function ", shape="box"];535         type [label="  Print the Type \n ReplPrintTypeImpl()", shape="box"];536         data [label="  Print the Data \n ReplPrintDataImpl() ", shape="box"];537         output [label="  Output Pretty Print \n to the user  ", shape="box", fontcolor=white, fillcolor="#3333ff", style=filled];538 539         parse -> capture [label="Optional 'Value' Parameter"];540         capture -> use [label="Yes"];541         use -> End;542         capture -> dump [label="No"];543         dump -> callp;544         callp -> type;545         callp -> data;546         type -> output;547         data -> output;548      }549 550**Note:** Following is a sample code snippet. Actual code may vary over time.551 552.. code-block:: console553 554    llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) {555 556    auto PTU = Parse(Code);557    if (!PTU)558        return PTU.takeError();559    if (PTU->TheModule)560        if (llvm::Error Err = Execute(*PTU))561        return Err;562 563    if (LastValue.isValid()) {564        if (!V) {565        LastValue.dump();566        LastValue.clear();567        } else568        *V = std::move(LastValue);569    }570    return llvm::Error::success();571    }572 573The ``dump()`` function (in ``value.cpp``) calls the ``print()`` function.574 575Printing the Data and Type are handled in their respective functions:576``ReplPrintDataImpl()`` and ``ReplPrintTypeImpl()``.577 578Annotation Token (annot_repl_input_end)579---------------------------------------580 581This feature uses a new token (``annot_repl_input_end``) to consider printing the582value of an expression if it doesn't end with a semicolon. When parsing an583Expression Statement, if the last semicolon is missing, then the code will584pretend that there one and set a marker there for later utilization, and585continue parsing.586 587A semicolon is normally required in C++, but this feature expands the C++588syntax to handle cases where a missing semicolon is expected (i.e., when589handling an expression statement). It also makes sure that an error is not590generated for the missing semicolon in this specific case.591 592This is accomplished by identifying the end position of the user input593(expression statement). This helps store and return the expression statement594effectively, so that it can be printed (displayed to the user automatically).595 596**Note:** This logic is only available for C++ for now, since part of the597implementation itself requires C++ features. Future versions may support more598languages.599 600.. code-block:: console601 602  Token *CurTok = nullptr;603  // If the semicolon is missing at the end of REPL input, consider if604  // we want to do value printing. Note this is only enabled in C++ mode605  // since part of the implementation requires C++ language features.606  // Note we shouldn't eat the token since the callback needs it.607  if (Tok.is(tok::annot_repl_input_end) && Actions.getLangOpts().CPlusPlus)608    CurTok = &Tok;609  else610    // Otherwise, eat the semicolon.611    ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);612 613  StmtResult R = handleExprStmt(Expr, StmtCtx);614  if (CurTok && !R.isInvalid())615    CurTok->setAnnotationValue(R.get());616 617  return R;618    }619 620AST Transformation621-------------------622 623When Sema encounters the ``annot_repl_input_end`` token, it knows to transform624the AST before the real CodeGen process. It will consume the token and set a625'semi missing' bit in the respective decl.626 627.. code-block:: console628 629    if (Tok.is(tok::annot_repl_input_end) &&630        Tok.getAnnotationValue() != nullptr) {631        ConsumeAnnotationToken();632        cast<TopLevelStmtDecl>(DeclsInGroup.back())->setSemiMissing();633    }634 635In the AST Consumer, traverse all the Top Level Decls, to look for expressions636to synthesize. If the current Decl is the Top Level Statement637Decl(``TopLevelStmtDecl``) and has a semicolon missing, then ask the interpreter638to synthesize another expression (an internal function call) to replace this639original expression.640 641 642Detailed RFC and Discussion:643----------------------------644 645For more technical details, community discussion and links to patches related646to these features,647Please visit: `RFC on LLVM Discourse <https://discourse.llvm.org/t/rfc-handle-execution-results-in-clang-repl/68493>`_.648 649Some logic presented in the RFC (e.g. ValueGetter()) may be outdated,650compared to the final developed solution.651 652Related Reading653===============654`Cling Transitions to LLVM's Clang-Repl <https://root.cern/blog/cling-in-llvm/>`_655 656`Moving (parts of) the Cling REPL in Clang <https://lists.llvm.org/pipermail/llvm-dev/2020-July/143257.html>`_657 658`GPU Accelerated Automatic Differentiation With Clad <https://arxiv.org/pdf/2203.06139.pdf>`_659