mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-23 05:40:09 +00:00
1f02743851
Original commit message: "
Add Documentation for Execution Results Handling in Clang-Repl (#65650)
[clang-repl] Add Documentation for Execution Results Handling.
This patch adds documentation for execution results handling in
Clang-REPL with
the below features:
- Automatic Printf feature
- Value Synthesis feature
- Pretty Printing feature
Continuing this work https://reviews.llvm.org/D156858 with this PR. I am
issuing this patch on behalf of Saqib.
"
This reverts commit ac32d7b87f
.
The new patch resolves the layering violation by simply not trying to look if
`dot` was installed. This is consistent with what we do other extensions.
After this patch we will need `sphinx.ext.graphviz` extension (part of the `dot`
package) which is available via anyway if we build the doxygen-based
documentation.
629 lines
22 KiB
ReStructuredText
629 lines
22 KiB
ReStructuredText
===========
|
||
Clang-Repl
|
||
===========
|
||
|
||
**Clang-Repl** is an interactive C++ interpreter that allows for incremental
|
||
compilation. It supports interactive programming for C++ in a
|
||
read-evaluate-print-loop (REPL) style. It uses Clang as a library to compile the
|
||
high level programming language into LLVM IR. Then the LLVM IR is executed by
|
||
the LLVM just-in-time (JIT) infrastructure.
|
||
|
||
Clang-Repl is suitable for exploratory programming and in places where time
|
||
to insight is important. Clang-Repl is a project inspired by the work in
|
||
`Cling <https://github.com/root-project/cling>`_, a LLVM-based C/C++ interpreter
|
||
developed by the field of high energy physics and used by the scientific data
|
||
analysis framework `ROOT <https://root.cern/>`_. Clang-Repl allows to move parts
|
||
of Cling upstream, making them useful and available to a broader audience.
|
||
|
||
|
||
Clang-Repl Basic Data Flow
|
||
==========================
|
||
|
||
.. image:: ClangRepl_design.png
|
||
:align: center
|
||
:alt: ClangRepl design
|
||
|
||
Clang-Repl data flow can be divided into roughly 8 phases:
|
||
|
||
1. Clang-Repl controls the input infrastructure by an interactive prompt or by
|
||
an interface allowing the incremental processing of input.
|
||
|
||
2. Then it sends the input to the underlying incremental facilities in Clang
|
||
infrastructure.
|
||
|
||
3. Clang compiles the input into an AST representation.
|
||
|
||
4. When required the AST can be further transformed in order to attach specific
|
||
behavior.
|
||
|
||
5. The AST representation is then lowered to LLVM IR.
|
||
|
||
6. The LLVM IR is the input format for LLVM’s JIT compilation infrastructure.
|
||
The tool will instruct the JIT to run specified functions, translating them
|
||
into machine code targeting the underlying device architecture (eg. Intel
|
||
x86 or NVPTX).
|
||
|
||
7. The LLVM JIT lowers the LLVM IR to machine code.
|
||
|
||
8. The machine code is then executed.
|
||
|
||
Build Instructions:
|
||
===================
|
||
|
||
|
||
.. code-block:: console
|
||
|
||
$ cd llvm-project
|
||
$ mkdir build
|
||
$ cd build
|
||
$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLLVM_ENABLE_PROJECTS=clang -G "Unix Makefiles" ../llvm
|
||
|
||
**Note here**, above RelWithDebInfo - Debug / Release
|
||
|
||
.. code-block:: console
|
||
|
||
cmake --build . --target clang clang-repl -j n
|
||
OR
|
||
cmake --build . --target clang clang-repl
|
||
|
||
**Clang-repl** is built under llvm-project/build/bin. Proceed into the directory **llvm-project/build/bin**
|
||
|
||
.. code-block:: console
|
||
|
||
./clang-repl
|
||
clang-repl>
|
||
|
||
|
||
Clang-Repl Usage
|
||
================
|
||
|
||
**Clang-Repl** is an interactive C++ interpreter that allows for incremental
|
||
compilation. It supports interactive programming for C++ in a
|
||
read-evaluate-print-loop (REPL) style. It uses Clang as a library to compile the
|
||
high level programming language into LLVM IR. Then the LLVM IR is executed by
|
||
the LLVM just-in-time (JIT) infrastructure.
|
||
|
||
|
||
Basic:
|
||
======
|
||
|
||
.. code-block:: text
|
||
|
||
clang-repl> #include <iostream>
|
||
clang-repl> int f() { std::cout << "Hello Interpreted World!\n"; return 0; }
|
||
clang-repl> auto r = f();
|
||
// Prints Hello Interpreted World!
|
||
|
||
.. code-block:: text
|
||
|
||
clang-repl> #include<iostream>
|
||
clang-repl> using namespace std;
|
||
clang-repl> std::cout << "Welcome to CLANG-REPL" << std::endl;
|
||
Welcome to CLANG-REPL
|
||
// Prints Welcome to CLANG-REPL
|
||
|
||
|
||
Function Definitions and Calls:
|
||
===============================
|
||
|
||
.. code-block:: text
|
||
|
||
clang-repl> #include <iostream>
|
||
clang-repl> int sum(int a, int b){ return a+b; };
|
||
clang-repl> int c = sum(9,10);
|
||
clang-repl> std::cout << c << std::endl;
|
||
19
|
||
clang-repl>
|
||
|
||
Iterative Structures:
|
||
=====================
|
||
|
||
.. code-block:: text
|
||
|
||
clang-repl> #include <iostream>
|
||
clang-repl> for (int i = 0;i < 3;i++){ std::cout << i << std::endl;}
|
||
0
|
||
1
|
||
2
|
||
clang-repl> while(i < 7){ i++; std::cout << i << std::endl;}
|
||
4
|
||
5
|
||
6
|
||
7
|
||
|
||
Classes and Structures:
|
||
=======================
|
||
|
||
.. code-block:: text
|
||
|
||
clang-repl> #include <iostream>
|
||
clang-repl> class Rectangle {int width, height; public: void set_values (int,int);\
|
||
clang-repl... int area() {return width*height;}};
|
||
clang-repl> void Rectangle::set_values (int x, int y) { width = x;height = y;}
|
||
clang-repl> int main () { Rectangle rect;rect.set_values (3,4);\
|
||
clang-repl... std::cout << "area: " << rect.area() << std::endl;\
|
||
clang-repl... return 0;}
|
||
clang-repl> main();
|
||
area: 12
|
||
clang-repl>
|
||
// Note: This '\' can be used for continuation of the statements in the next line
|
||
|
||
Lamdas:
|
||
=======
|
||
|
||
.. code-block:: text
|
||
|
||
clang-repl> #include <iostream>
|
||
clang-repl> using namespace std;
|
||
clang-repl> auto welcome = []() { std::cout << "Welcome to REPL" << std::endl;};
|
||
clang-repl> welcome();
|
||
Welcome to REPL
|
||
|
||
Using Dynamic Library:
|
||
======================
|
||
|
||
.. code-block:: text
|
||
|
||
clang-repl> %lib print.so
|
||
clang-repl> #include"print.hpp"
|
||
clang-repl> print(9);
|
||
9
|
||
|
||
**Generation of dynamic library**
|
||
|
||
.. code-block:: text
|
||
|
||
// print.cpp
|
||
#include <iostream>
|
||
#include "print.hpp"
|
||
|
||
void print(int a)
|
||
{
|
||
std::cout << a << std::endl;
|
||
}
|
||
|
||
// print.hpp
|
||
void print (int a);
|
||
|
||
// Commands
|
||
clang++-17 -c -o print.o print.cpp
|
||
clang-17 -shared print.o -o print.so
|
||
|
||
Comments:
|
||
=========
|
||
|
||
.. code-block:: text
|
||
|
||
clang-repl> // Comments in Clang-Repl
|
||
clang-repl> /* Comments in Clang-Repl */
|
||
|
||
|
||
Closure or Termination:
|
||
=======================
|
||
|
||
.. code-block:: text
|
||
|
||
clang-repl>%quit
|
||
|
||
|
||
Just like Clang, Clang-Repl can be integrated in existing applications as a library
|
||
(using the clangInterpreter library). This turns your C++ compiler into a service that
|
||
can incrementally consume and execute code. The **Compiler as A Service** (**CaaS**)
|
||
concept helps support advanced use cases such as template instantiations on demand and
|
||
automatic language interoperability. It also helps static languages such as C/C++ become
|
||
apt for data science.
|
||
|
||
Execution Results Handling in Clang-Repl
|
||
========================================
|
||
|
||
Execution Results Handling features discussed below help extend the Clang-Repl
|
||
functionality by creating an interface between the execution results of a
|
||
program and the compiled program.
|
||
|
||
1. **Capture Execution Results**: This feature helps capture the execution results
|
||
of a program and bring them back to the compiled program.
|
||
|
||
2. **Dump Captured Execution Results**: This feature helps create a temporary dump
|
||
for Value Printing/Automatic Printf, that is, to display the value and type of
|
||
the captured data.
|
||
|
||
|
||
1. Capture Execution Results
|
||
============================
|
||
|
||
In many cases, it is useful to bring back the program execution result to the
|
||
compiled program. This result can be stored in an object of type **Value**.
|
||
|
||
How Execution Results are captured (Value Synthesis):
|
||
-----------------------------------------------------
|
||
|
||
The synthesizer chooses which expression to synthesize, and then it replaces
|
||
the original expression with the synthesized expression. Depending on the
|
||
expression type, it may choose to save an object (``LastValue``) of type 'value'
|
||
while allocating memory to it (``SetValueWithAlloc()``), or not (
|
||
``SetValueNoAlloc()``).
|
||
|
||
.. graphviz::
|
||
:name: valuesynthesis
|
||
:caption: Value Synthesis
|
||
:alt: Shows how an object of type 'Value' is synthesized
|
||
:align: center
|
||
|
||
digraph "valuesynthesis" {
|
||
rankdir="LR";
|
||
graph [fontname="Verdana", fontsize="12"];
|
||
node [fontname="Verdana", fontsize="12"];
|
||
edge [fontname="Sans", fontsize="9"];
|
||
|
||
start [label=" Create an Object \n 'Last Value' \n of type 'Value' ", shape="note", fontcolor=white, fillcolor="#3333ff", style=filled];
|
||
assign [label=" Assign the result \n to the 'LastValue' \n (based on respective \n Memory Allocation \n scenario) ", shape="box"]
|
||
print [label=" Pretty Print \n the Value Object ", shape="Msquare", fillcolor="yellow", style=filled];
|
||
start -> assign;
|
||
assign -> print;
|
||
|
||
subgraph SynthesizeExpression {
|
||
synth [label=" SynthesizeExpr() ", shape="note", fontcolor=white, fillcolor="#3333ff", style=filled];
|
||
mem [label=" New Memory \n Allocation? ", shape="diamond"];
|
||
withaloc [label=" SetValueWithAlloc() ", shape="box"];
|
||
noaloc [label=" SetValueNoAlloc() ", shape="box"];
|
||
right [label=" 1. RValue Structure \n (a temporary value)", shape="box"];
|
||
left2 [label=" 2. LValue Structure \n (a variable with \n an address)", shape="box"];
|
||
left3 [label=" 3. Built-In Type \n (int, float, etc.)", shape="box"];
|
||
output [label=" move to 'Assign' step ", shape="box"];
|
||
|
||
synth -> mem;
|
||
mem -> withaloc [label="Yes"];
|
||
mem -> noaloc [label="No"];
|
||
withaloc -> right;
|
||
noaloc -> left2;
|
||
noaloc -> left3;
|
||
right -> output;
|
||
left2 -> output;
|
||
left3 -> output;
|
||
}
|
||
output -> assign
|
||
}
|
||
|
||
Where is the captured result stored?
|
||
------------------------------------
|
||
|
||
``LastValue`` holds the last result of the value printing. It is a class member
|
||
because it can be accessed even after subsequent inputs.
|
||
|
||
**Note:** If no value printing happens, then it is in an invalid state.
|
||
|
||
Improving Efficiency and User Experience
|
||
----------------------------------------
|
||
|
||
The Value object is essentially used to create a mapping between an expression
|
||
'type' and the allocated 'memory'. Built-in types (bool, char, int,
|
||
float, double, etc.) are copyable. Their memory allocation size is known
|
||
and the Value object can introduce a small-buffer optimization.
|
||
In case of objects, the ``Value`` class provides reference-counted memory
|
||
management.
|
||
|
||
The implementation maps the type as written and the Clang Type to be able to use
|
||
the preprocessor to synthesize the relevant cast operations. For example,
|
||
``X(char, Char_S)``, where ``char`` is the type from the language's type system
|
||
and ``Char_S`` is the Clang builtin type which represents it. This mapping helps
|
||
to import execution results from the interpreter in a compiled program and vice
|
||
versa. The ``Value.h`` header file can be included at runtime and this is why it
|
||
has a very low token count and was developed with strict constraints in mind.
|
||
|
||
This also enables the user to receive the computed 'type' back in their code
|
||
and then transform the type into something else (e.g., re-cast a double into
|
||
a float). Normally, the compiler can handle these conversions transparently,
|
||
but in interpreter mode, the compiler cannot see all the 'from' and 'to' types,
|
||
so it cannot implicitly do the conversions. So this logic enables providing
|
||
these conversions on request.
|
||
|
||
On-request conversions can help improve the user experience, by allowing
|
||
conversion to a desired 'to' type, when the 'from' type is unknown or unclear.
|
||
|
||
Significance of this Feature
|
||
----------------------------
|
||
|
||
The 'Value' object enables wrapping a memory region that comes from the
|
||
JIT, and bringing it back to the compiled code (and vice versa).
|
||
This is a very useful functionality when:
|
||
|
||
- connecting an interpreter to the compiled code, or
|
||
- connecting an interpreter in another language.
|
||
|
||
For example, this feature helps transport values across boundaries. A notable
|
||
example is the cppyy project code makes use of this feature to enable running C++
|
||
within Python. It enables transporting values/information between C++
|
||
and Python.
|
||
|
||
Note: `cppyy <https://github.com/wlav/cppyy/>`_ is an automatic, run-time,
|
||
Python-to-C++ bindings generator, for calling C++ from Python and Python from C++.
|
||
It uses LLVM along with a C++ interpreter (e.g., Cling) to enable features like
|
||
run-time instantiation of C++ templates, cross-inheritance, callbacks,
|
||
auto-casting, transparent use of smart pointers, etc.
|
||
|
||
In a nutshell, this feature enables a new way of developing code, paving the
|
||
way for language interoperability and easier interactive programming.
|
||
|
||
Implementation Details
|
||
======================
|
||
|
||
Interpreter as a REPL vs. as a Library
|
||
--------------------------------------
|
||
|
||
1 - If we're using the interpreter in interactive (REPL) mode, it will dump
|
||
the value (i.e., value printing).
|
||
|
||
.. code-block:: console
|
||
|
||
if (LastValue.isValid()) {
|
||
if (!V) {
|
||
LastValue.dump();
|
||
LastValue.clear();
|
||
} else
|
||
*V = std::move(LastValue);
|
||
}
|
||
|
||
|
||
2 - If we're using the interpreter as a library, then it will pass the value
|
||
to the user.
|
||
|
||
Incremental AST Consumer
|
||
------------------------
|
||
|
||
The ``IncrementalASTConsumer`` class wraps the original code generator
|
||
``ASTConsumer`` and it performs a hook, to traverse all the top-level decls, to
|
||
look for expressions to synthesize, based on the ``isSemiMissing()`` condition.
|
||
|
||
If this condition is found to be true, then ``Interp.SynthesizeExpr()`` will be
|
||
invoked.
|
||
|
||
**Note:** Following is a sample code snippet. Actual code may vary over time.
|
||
|
||
.. code-block:: console
|
||
|
||
for (Decl *D : DGR)
|
||
if (auto *TSD = llvm::dyn_cast<TopLevelStmtDecl>(D);
|
||
TSD && TSD->isSemiMissing())
|
||
TSD->setStmt(Interp.SynthesizeExpr(cast<Expr>(TSD->getStmt())));
|
||
|
||
return Consumer->HandleTopLevelDecl(DGR);
|
||
|
||
The synthesizer will then choose the relevant expression, based on its type.
|
||
|
||
Communication between Compiled Code and Interpreted Code
|
||
--------------------------------------------------------
|
||
|
||
In Clang-Repl there is **interpreted code**, and this feature adds a 'value'
|
||
runtime that can talk to the **compiled code**.
|
||
|
||
Following is an example where the compiled code interacts with the interpreter
|
||
code. The execution results of an expression are stored in the object 'V' of
|
||
type Value. This value is then printed, effectively helping the interpreter
|
||
use a value from the compiled code.
|
||
|
||
.. code-block:: console
|
||
|
||
int Global = 42;
|
||
void setGlobal(int val) { Global = val; }
|
||
int getGlobal() { return Global; }
|
||
Interp.ParseAndExecute(“void setGlobal(int val);”);
|
||
Interp.ParseAndExecute(“int getGlobal();”);
|
||
Value V;
|
||
Interp.ParseAndExecute(“getGlobal()”, &V);
|
||
std::cout << V.getAs<int>() << “\n”; // Prints 42
|
||
|
||
|
||
**Note:** Above is an example of interoperability between the compiled code and
|
||
the interpreted code. Interoperability between languages (e.g., C++ and Python)
|
||
works similarly.
|
||
|
||
|
||
2. Dump Captured Execution Results
|
||
==================================
|
||
|
||
This feature helps create a temporary dump to display the value and type
|
||
(pretty print) of the desired data. This is a good way to interact with the
|
||
interpreter during interactive programming.
|
||
|
||
How value printing is simplified (Automatic Printf)
|
||
---------------------------------------------------
|
||
|
||
The ``Automatic Printf`` feature makes it easy to display variable values during
|
||
program execution. Using the ``printf`` function repeatedly is not required.
|
||
This is achieved using an extension in the ``libclangInterpreter`` library.
|
||
|
||
To automatically print the value of an expression, simply write the expression
|
||
in the global scope **without a semicolon**.
|
||
|
||
.. graphviz::
|
||
:name: automaticprintf
|
||
:caption: Automatic PrintF
|
||
:alt: Shows how Automatic PrintF can be used
|
||
:align: center
|
||
|
||
digraph "AutomaticPrintF" {
|
||
size="6,4";
|
||
rankdir="LR";
|
||
graph [fontname="Verdana", fontsize="12"];
|
||
node [fontname="Verdana", fontsize="12"];
|
||
edge [fontname="Sans", fontsize="9"];
|
||
|
||
manual [label=" Manual PrintF ", shape="box"];
|
||
int1 [label=" int ( &) 42 ", shape="box"]
|
||
auto [label=" Automatic PrintF ", shape="box"];
|
||
int2 [label=" int ( &) 42 ", shape="box"]
|
||
|
||
auto -> int2 [label="int x = 42; \n x"];
|
||
manual -> int1 [label="int x = 42; \n printf("(int &) %d \\n", x);"];
|
||
}
|
||
|
||
|
||
Significance of this feature
|
||
----------------------------
|
||
|
||
Inspired by a similar implementation in `Cling <https://github.com/root-project/cling>`_,
|
||
this feature added to upstream Clang repo has essentially extended the syntax of
|
||
C++, so that it can be more helpful for people that are writing code for data
|
||
science applications.
|
||
|
||
This is useful, for example, when you want to experiment with a set of values
|
||
against a set of functions, and you'd like to know the results right away.
|
||
This is similar to how Python works (hence its popularity in data science
|
||
research), but the superior performance of C++, along with this flexibility
|
||
makes it a more attractive option.
|
||
|
||
Implementation Details
|
||
======================
|
||
|
||
Parsing mechanism:
|
||
------------------
|
||
|
||
The Interpreter in Clang-Repl (``Interpreter.cpp``) includes the function
|
||
``ParseAndExecute()`` that can accept a 'Value' parameter to capture the result.
|
||
But if the value parameter is made optional and it is omitted (i.e., that the
|
||
user does not want to utilize it elsewhere), then the last value can be
|
||
validated and pushed into the ``dump()`` function.
|
||
|
||
.. graphviz::
|
||
:name: parsing
|
||
:caption: Parsing Mechanism
|
||
:alt: Shows the Parsing Mechanism for Pretty Printing
|
||
:align: center
|
||
|
||
|
||
digraph "prettyprint" {
|
||
rankdir="LR";
|
||
graph [fontname="Verdana", fontsize="12"];
|
||
node [fontname="Verdana", fontsize="12"];
|
||
edge [fontname="Verdana", fontsize="9"];
|
||
|
||
parse [label=" ParseAndExecute() \n in Clang ", shape="box"];
|
||
capture [label=" Capture 'Value' parameter \n for processing? ", shape="diamond"];
|
||
use [label=" Use for processing ", shape="box"];
|
||
dump [label=" Validate and push \n to dump()", shape="box"];
|
||
callp [label=" call print() function ", shape="box"];
|
||
type [label=" Print the Type \n ReplPrintTypeImpl()", shape="box"];
|
||
data [label=" Print the Data \n ReplPrintDataImpl() ", shape="box"];
|
||
output [label=" Output Pretty Print \n to the user ", shape="box", fontcolor=white, fillcolor="#3333ff", style=filled];
|
||
|
||
parse -> capture [label="Optional 'Value' Parameter"];
|
||
capture -> use [label="Yes"];
|
||
use -> End;
|
||
capture -> dump [label="No"];
|
||
dump -> callp;
|
||
callp -> type;
|
||
callp -> data;
|
||
type -> output;
|
||
data -> output;
|
||
}
|
||
|
||
**Note:** Following is a sample code snippet. Actual code may vary over time.
|
||
|
||
.. code-block:: console
|
||
|
||
llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) {
|
||
|
||
auto PTU = Parse(Code);
|
||
if (!PTU)
|
||
return PTU.takeError();
|
||
if (PTU->TheModule)
|
||
if (llvm::Error Err = Execute(*PTU))
|
||
return Err;
|
||
|
||
if (LastValue.isValid()) {
|
||
if (!V) {
|
||
LastValue.dump();
|
||
LastValue.clear();
|
||
} else
|
||
*V = std::move(LastValue);
|
||
}
|
||
return llvm::Error::success();
|
||
}
|
||
|
||
The ``dump()`` function (in ``value.cpp``) calls the ``print()`` function.
|
||
|
||
Printing the Data and Type are handled in their respective functions:
|
||
``ReplPrintDataImpl()`` and ``ReplPrintTypeImpl()``.
|
||
|
||
Annotation Token (annot_repl_input_end)
|
||
---------------------------------------
|
||
|
||
This feature uses a new token (``annot_repl_input_end``) to consider printing the
|
||
value of an expression if it doesn't end with a semicolon. When parsing an
|
||
Expression Statement, if the last semicolon is missing, then the code will
|
||
pretend that there one and set a marker there for later utilization, and
|
||
continue parsing.
|
||
|
||
A semicolon is normally required in C++, but this feature expands the C++
|
||
syntax to handle cases where a missing semicolon is expected (i.e., when
|
||
handling an expression statement). It also makes sure that an error is not
|
||
generated for the missing semicolon in this specific case.
|
||
|
||
This is accomplished by identifying the end position of the user input
|
||
(expression statement). This helps store and return the expression statement
|
||
effectively, so that it can be printed (displayed to the user automatically).
|
||
|
||
**Note:** This logic is only available for C++ for now, since part of the
|
||
implementation itself requires C++ features. Future versions may support more
|
||
languages.
|
||
|
||
.. code-block:: console
|
||
|
||
Token *CurTok = nullptr;
|
||
// If the semicolon is missing at the end of REPL input, consider if
|
||
// we want to do value printing. Note this is only enabled in C++ mode
|
||
// since part of the implementation requires C++ language features.
|
||
// Note we shouldn't eat the token since the callback needs it.
|
||
if (Tok.is(tok::annot_repl_input_end) && Actions.getLangOpts().CPlusPlus)
|
||
CurTok = &Tok;
|
||
else
|
||
// Otherwise, eat the semicolon.
|
||
ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
|
||
|
||
StmtResult R = handleExprStmt(Expr, StmtCtx);
|
||
if (CurTok && !R.isInvalid())
|
||
CurTok->setAnnotationValue(R.get());
|
||
|
||
return R;
|
||
}
|
||
|
||
AST Transformation
|
||
-------------------
|
||
|
||
When Sema encounters the ``annot_repl_input_end`` token, it knows to transform
|
||
the AST before the real CodeGen process. It will consume the token and set a
|
||
'semi missing' bit in the respective decl.
|
||
|
||
.. code-block:: console
|
||
|
||
if (Tok.is(tok::annot_repl_input_end) &&
|
||
Tok.getAnnotationValue() != nullptr) {
|
||
ConsumeAnnotationToken();
|
||
cast<TopLevelStmtDecl>(DeclsInGroup.back())->setSemiMissing();
|
||
}
|
||
|
||
In the AST Consumer, traverse all the Top Level Decls, to look for expressions
|
||
to synthesize. If the current Decl is the Top Level Statement
|
||
Decl(``TopLevelStmtDecl``) and has a semicolon missing, then ask the interpreter
|
||
to synthesize another expression (an internal function call) to replace this
|
||
original expression.
|
||
|
||
|
||
Detailed RFC and Discussion:
|
||
----------------------------
|
||
|
||
For more technical details, community discussion and links to patches related
|
||
to these features,
|
||
Please visit: `RFC on LLVM Discourse <https://discourse.llvm.org/t/rfc-handle-execution-results-in-clang-repl/68493>`_.
|
||
|
||
Some logic presented in the RFC (e.g. ValueGetter()) may be outdated,
|
||
compared to the final developed solution.
|
||
|
||
Related Reading
|
||
===============
|
||
`Cling Transitions to LLVM's Clang-Repl <https://root.cern/blog/cling-in-llvm/>`_
|
||
|
||
`Moving (parts of) the Cling REPL in Clang <https://lists.llvm.org/pipermail/llvm-dev/2020-July/143257.html>`_
|
||
|
||
`GPU Accelerated Automatic Differentiation With Clad <https://arxiv.org/pdf/2203.06139.pdf>`_
|