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("(int &) %d \\n", 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