Update Kaleidoscope tutorial and improve Windows support

Many quoted code blocks were not in sync with the actual toy.cpp
files. Improve tutorial text slightly in several places.
Added some step descriptions crucial to avoid crashes (like
InitializeNativeTarget* calls).
Solve/workaround problems with Windows (JIT'ed method not found, using
custom and standard library functions from host process).

Patch by: Moritz Kroll <moritz.kroll@gmx.de>

Differential Revision: https://reviews.llvm.org/D29864

llvm-svn: 294870
This commit is contained in:
Mehdi Amini 2017-02-11 21:26:52 +00:00
parent 315edb0216
commit bb6805d263
16 changed files with 327 additions and 196 deletions

View File

@ -125,14 +125,12 @@ usual include guards and #includes [2]_, we get to the definition of our class:
class KaleidoscopeJIT {
private:
std::unique_ptr<TargetMachine> TM;
const DataLayout DL;
ObjectLinkingLayer<> ObjectLayer;
IRCompileLayer<decltype(ObjectLayer)> CompileLayer;
public:
typedef decltype(CompileLayer)::ModuleSetHandleT ModuleHandleT;
Our class begins with four members: A TargetMachine, TM, which will be used
@ -152,16 +150,16 @@ compiling it, and passing the resulting in-memory object files down to the
object linking layer below.
That's it for member variables, after that we have a single typedef:
ModuleHandle. This is the handle type that will be returned from our JIT's
ModuleHandleT. This is the handle type that will be returned from our JIT's
addModule method, and can be passed to the removeModule method to remove a
module. The IRCompileLayer class already provides a convenient handle type
(IRCompileLayer::ModuleSetHandleT), so we just alias our ModuleHandle to this.
(IRCompileLayer::ModuleSetHandleT), so we just alias our ModuleHandleT to this.
.. code-block:: c++
KaleidoscopeJIT()
: TM(EngineBuilder().selectTarget()), DL(TM->createDataLayout()),
CompileLayer(ObjectLayer, SimpleCompiler(*TM)) {
CompileLayer(ObjectLayer, SimpleCompiler(*TM)) {
llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr);
}
@ -200,7 +198,7 @@ available for execution.
return JITSymbol(nullptr);
});
// Build a singlton module set to hold our module.
// Build a singleton module set to hold our module.
std::vector<std::unique_ptr<Module>> Ms;
Ms.push_back(std::move(M));
@ -259,16 +257,16 @@ were linked into a single, ever-growing logical dylib. To implement this our
first lambda (the one defining findSymbolInLogicalDylib) will just search for
JIT'd code by calling the CompileLayer's findSymbol method. If we don't find a
symbol in the JIT itself we'll fall back to our second lambda, which implements
findSymbol. This will use the RTDyldMemoyrManager::getSymbolAddressInProcess
findSymbol. This will use the RTDyldMemoryManager::getSymbolAddressInProcess
method to search for the symbol within the program itself. If we can't find a
symbol definition via either of these paths the JIT will refuse to accept our
symbol definition via either of these paths, the JIT will refuse to accept our
module, returning a "symbol not found" error.
Now that we've built our symbol resolver we're ready to add our module to the
Now that we've built our symbol resolver, we're ready to add our module to the
JIT. We do this by calling the CompileLayer's addModuleSet method [4]_. Since
we only have a single Module and addModuleSet expects a collection, we will
create a vector of modules and add our module as the only member. Since we
have already typedef'd our ModuleHandle type to be the same as the
have already typedef'd our ModuleHandleT type to be the same as the
CompileLayer's handle type, we can return the handle from addModuleSet
directly from our addModule method.
@ -304,7 +302,7 @@ treated as a duplicate definition when the next top-level expression is
entered. It is generally good to free any module that you know you won't need
to call further, just to free up the resources dedicated to it. However, you
don't strictly need to do this: All resources will be cleaned up when your
JIT class is destructed, if the haven't been freed before then.
JIT class is destructed, if they haven't been freed before then.
This brings us to the end of Chapter 1 of Building a JIT. You now have a basic
but fully functioning JIT stack that you can use to take LLVM IR and make it

View File

@ -119,6 +119,8 @@ way to talk about functions themselves:
public:
PrototypeAST(const std::string &name, std::vector<std::string> Args)
: Name(name), Args(std::move(Args)) {}
const std::string &getName() const { return Name; }
};
/// FunctionAST - This class represents a function definition itself.

View File

@ -122,7 +122,7 @@ First we'll do numeric literals:
.. code-block:: c++
Value *NumberExprAST::codegen() {
return ConstantFP::get(LLVMContext, APFloat(Val));
return ConstantFP::get(TheContext, APFloat(Val));
}
In the LLVM IR, numeric constants are represented with the
@ -171,7 +171,7 @@ variables <LangImpl7.html#user-defined-local-variables>`_.
case '<':
L = Builder.CreateFCmpULT(L, R, "cmptmp");
// Convert bool 0/1 to double 0.0 or 1.0
return Builder.CreateUIToFP(L, Type::getDoubleTy(LLVMContext),
return Builder.CreateUIToFP(L, Type::getDoubleTy(TheContext),
"booltmp");
default:
return LogErrorV("invalid binary operator");
@ -270,9 +270,9 @@ with:
Function *PrototypeAST::codegen() {
// Make the function type: double(double,double) etc.
std::vector<Type*> Doubles(Args.size(),
Type::getDoubleTy(LLVMContext));
Type::getDoubleTy(TheContext));
FunctionType *FT =
FunctionType::get(Type::getDoubleTy(LLVMContext), Doubles, false);
FunctionType::get(Type::getDoubleTy(TheContext), Doubles, false);
Function *F =
Function::Create(FT, Function::ExternalLinkage, Name, TheModule);
@ -346,7 +346,7 @@ assert that the function is empty (i.e. has no body yet) before we start.
.. code-block:: c++
// Create a new basic block to start insertion into.
BasicBlock *BB = BasicBlock::Create(LLVMContext, "entry", TheFunction);
BasicBlock *BB = BasicBlock::Create(TheContext, "entry", TheFunction);
Builder.SetInsertPoint(BB);
// Record the function arguments in the NamedValues map.
@ -533,7 +533,8 @@ This shows an extern for the libm "cos" function, and a call to it.
ret double %calltmp
}
When you quit the current demo, it dumps out the IR for the entire
When you quit the current demo (by sending an EOF via CTRL+D on Linux
or CTRL+Z and ENTER on Windows), it dumps out the IR for the entire
module generated. Here you can see the big picture with all the
functions referencing each other.

View File

@ -131,33 +131,29 @@ for us:
void InitializeModuleAndPassManager(void) {
// Open a new module.
Context LLVMContext;
TheModule = llvm::make_unique<Module>("my cool jit", LLVMContext);
TheModule->setDataLayout(TheJIT->getTargetMachine().createDataLayout());
TheModule = llvm::make_unique<Module>("my cool jit", TheContext);
// Create a new pass manager attached to it.
TheFPM = llvm::make_unique<FunctionPassManager>(TheModule.get());
// Provide basic AliasAnalysis support for GVN.
TheFPM.add(createBasicAliasAnalysisPass());
// Do simple "peephole" optimizations and bit-twiddling optzns.
TheFPM.add(createInstructionCombiningPass());
TheFPM->add(createInstructionCombiningPass());
// Reassociate expressions.
TheFPM.add(createReassociatePass());
TheFPM->add(createReassociatePass());
// Eliminate Common SubExpressions.
TheFPM.add(createGVNPass());
TheFPM->add(createGVNPass());
// Simplify the control flow graph (deleting unreachable blocks, etc).
TheFPM.add(createCFGSimplificationPass());
TheFPM->add(createCFGSimplificationPass());
TheFPM.doInitialization();
TheFPM->doInitialization();
}
This code initializes the global module ``TheModule``, and the function pass
manager ``TheFPM``, which is attached to ``TheModule``. Once the pass manager is
set up, we use a series of "add" calls to add a bunch of LLVM passes.
In this case, we choose to add five passes: one analysis pass (alias analysis),
and four optimization passes. The passes we choose here are a pretty standard set
In this case, we choose to add four optimization passes.
The passes we choose here are a pretty standard set
of "cleanup" optimizations that are useful for a wide variety of code. I won't
delve into what they do but, believe me, they are a good starting place :).
@ -227,8 +223,10 @@ expressions they type in. For example, if they type in "1 + 2;", we
should evaluate and print out 3. If they define a function, they should
be able to call it from the command line.
In order to do this, we first declare and initialize the JIT. This is
done by adding a global variable ``TheJIT``, and initializing it in
In order to do this, we first prepare the environment to create code for
the current native target and declare and initialize the JIT. This is
done by calling some ``InitializeNativeTarget\*`` functions and
adding a global variable ``TheJIT``, and initializing it in
``main``:
.. code-block:: c++
@ -236,7 +234,21 @@ done by adding a global variable ``TheJIT``, and initializing it in
static std::unique_ptr<KaleidoscopeJIT> TheJIT;
...
int main() {
..
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
InitializeNativeTargetAsmParser();
// Install standard binary operators.
// 1 is lowest precedence.
BinopPrecedence['<'] = 10;
BinopPrecedence['+'] = 20;
BinopPrecedence['-'] = 20;
BinopPrecedence['*'] = 40; // highest.
// Prime the first token.
fprintf(stderr, "ready> ");
getNextToken();
TheJIT = llvm::make_unique<KaleidoscopeJIT>();
// Run the main "interpreter loop" now.
@ -245,9 +257,24 @@ done by adding a global variable ``TheJIT``, and initializing it in
return 0;
}
We also need to setup the data layout for the JIT:
.. code-block:: c++
void InitializeModuleAndPassManager(void) {
// Open a new module.
TheModule = llvm::make_unique<Module>("my cool jit", TheContext);
TheModule->setDataLayout(TheJIT->getTargetMachine().createDataLayout());
// Create a new pass manager attached to it.
TheFPM = llvm::make_unique<FunctionPassManager>(TheModule.get());
...
The KaleidoscopeJIT class is a simple JIT built specifically for these
tutorials. In later chapters we will look at how it works and extend it with
new features, but for now we will take it as given. Its API is very simple::
tutorials, available inside the LLVM source code
at llvm-src/examples/Kaleidoscope/include/KaleidoscopeJIT.h.
In later chapters we will look at how it works and extend it with
new features, but for now we will take it as given. Its API is very simple:
``addModule`` adds an LLVM IR module to the JIT, making its functions
available for execution; ``removeModule`` removes a module, freeing any
memory associated with the code in that module; and ``findSymbol`` allows us
@ -554,7 +581,10 @@ most recent to the oldest, to find the newest definition. If no definition is
found inside the JIT, it falls back to calling "``dlsym("sin")``" on the
Kaleidoscope process itself. Since "``sin``" is defined within the JIT's
address space, it simply patches up calls in the module to call the libm
version of ``sin`` directly.
version of ``sin`` directly. But in some cases this even goes further:
as sin and cos are names of standard math functions, the constant folder
will directly evaluate the function calls to the correct result when called
with constants like in the "``sin(1.0)``" above.
In the future we'll see how tweaking this symbol resolution rule can be used to
enable all sorts of useful features, from security (restricting the set of
@ -567,12 +597,21 @@ if we add:
.. code-block:: c++
#ifdef LLVM_ON_WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
/// putchard - putchar that takes a double and returns 0.
extern "C" double putchard(double X) {
extern "C" DLLEXPORT double putchard(double X) {
fputc((char)X, stderr);
return 0;
}
Note, that for Windows we need to actually export the functions because
the dynamic symbol loader will use GetProcAddress to find the symbols.
Now we can produce simple output to the console by using things like:
"``extern putchard(x); putchard(120);``", which prints a lowercase 'x'
on the console (120 is the ASCII code for 'x'). Similar code could be

View File

@ -103,7 +103,8 @@ To represent the new expression we add a new AST node for it:
IfExprAST(std::unique_ptr<ExprAST> Cond, std::unique_ptr<ExprAST> Then,
std::unique_ptr<ExprAST> Else)
: Cond(std::move(Cond)), Then(std::move(Then)), Else(std::move(Else)) {}
virtual Value *codegen();
Value *codegen() override;
};
The AST node just has pointers to the various subexpressions.
@ -290,9 +291,9 @@ for ``IfExprAST``:
if (!CondV)
return nullptr;
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
CondV = Builder.CreateFCmpONE(
CondV, ConstantFP::get(LLVMContext, APFloat(0.0)), "ifcond");
CondV, ConstantFP::get(TheContext, APFloat(0.0)), "ifcond");
This code is straightforward and similar to what we saw before. We emit
the expression for the condition, then compare that value to zero to get
@ -305,9 +306,9 @@ a truth value as a 1-bit (bool) value.
// Create blocks for the then and else cases. Insert the 'then' block at the
// end of the function.
BasicBlock *ThenBB =
BasicBlock::Create(LLVMContext, "then", TheFunction);
BasicBlock *ElseBB = BasicBlock::Create(LLVMContext, "else");
BasicBlock *MergeBB = BasicBlock::Create(LLVMContext, "ifcont");
BasicBlock::Create(TheContext, "then", TheFunction);
BasicBlock *ElseBB = BasicBlock::Create(TheContext, "else");
BasicBlock *MergeBB = BasicBlock::Create(TheContext, "ifcont");
Builder.CreateCondBr(CondV, ThenBB, ElseBB);
@ -400,7 +401,7 @@ code:
TheFunction->getBasicBlockList().push_back(MergeBB);
Builder.SetInsertPoint(MergeBB);
PHINode *PN =
Builder.CreatePHI(Type::getDoubleTy(LLVMContext), 2, "iftmp");
Builder.CreatePHI(Type::getDoubleTy(TheContext), 2, "iftmp");
PN->addIncoming(ThenV, ThenBB);
PN->addIncoming(ElseV, ElseBB);
@ -433,7 +434,7 @@ something more aggressive, a 'for' expression:
::
extern putchard(char)
extern putchard(char);
def printstar(n)
for i = 1, i < n, 1.0 in
putchard(42); # ascii 42 = '*'
@ -500,7 +501,8 @@ variable name and the constituent expressions in the node.
std::unique_ptr<ExprAST> Body)
: VarName(VarName), Start(std::move(Start)), End(std::move(End)),
Step(std::move(Step)), Body(std::move(Body)) {}
virtual Value *codegen();
Value *codegen() override;
};
Parser Extensions for the 'for' Loop
@ -561,6 +563,27 @@ value to null in the AST node:
std::move(Body));
}
And again we hook it up as a primary expression:
.. code-block:: c++
static std::unique_ptr<ExprAST> ParsePrimary() {
switch (CurTok) {
default:
return LogError("unknown token when expecting an expression");
case tok_identifier:
return ParseIdentifierExpr();
case tok_number:
return ParseNumberExpr();
case '(':
return ParseParenExpr();
case tok_if:
return ParseIfExpr();
case tok_for:
return ParseForExpr();
}
}
LLVM IR for the 'for' Loop
--------------------------
@ -610,7 +633,8 @@ expression for the loop value:
Value *ForExprAST::codegen() {
// Emit the start code first, without 'variable' in scope.
Value *StartVal = Start->codegen();
if (StartVal == 0) return 0;
if (!StartVal)
return nullptr;
With this out of the way, the next step is to set up the LLVM basic
block for the start of the loop body. In the case above, the whole loop
@ -625,7 +649,7 @@ expression).
Function *TheFunction = Builder.GetInsertBlock()->getParent();
BasicBlock *PreheaderBB = Builder.GetInsertBlock();
BasicBlock *LoopBB =
BasicBlock::Create(LLVMContext, "loop", TheFunction);
BasicBlock::Create(TheContext, "loop", TheFunction);
// Insert an explicit fall through from the current block to the LoopBB.
Builder.CreateBr(LoopBB);
@ -642,7 +666,7 @@ the two blocks.
Builder.SetInsertPoint(LoopBB);
// Start the PHI node with an entry for Start.
PHINode *Variable = Builder.CreatePHI(Type::getDoubleTy(LLVMContext),
PHINode *Variable = Builder.CreatePHI(Type::getDoubleTy(TheContext),
2, VarName.c_str());
Variable->addIncoming(StartVal, PreheaderBB);
@ -693,7 +717,7 @@ table.
return nullptr;
} else {
// If not specified, use 1.0.
StepVal = ConstantFP::get(LLVMContext, APFloat(1.0));
StepVal = ConstantFP::get(TheContext, APFloat(1.0));
}
Value *NextVar = Builder.CreateFAdd(Variable, StepVal, "nextvar");
@ -710,9 +734,9 @@ iteration of the loop.
if (!EndCond)
return nullptr;
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
EndCond = Builder.CreateFCmpONE(
EndCond, ConstantFP::get(LLVMContext, APFloat(0.0)), "loopcond");
EndCond, ConstantFP::get(TheContext, APFloat(0.0)), "loopcond");
Finally, we evaluate the exit value of the loop, to determine whether
the loop should exit. This mirrors the condition evaluation for the
@ -723,7 +747,7 @@ if/then/else statement.
// Create the "after loop" block and insert it.
BasicBlock *LoopEndBB = Builder.GetInsertBlock();
BasicBlock *AfterBB =
BasicBlock::Create(LLVMContext, "afterloop", TheFunction);
BasicBlock::Create(TheContext, "afterloop", TheFunction);
// Insert the conditional branch into the end of LoopEndBB.
Builder.CreateCondBr(EndCond, LoopBB, AfterBB);
@ -751,7 +775,7 @@ insertion position to it.
NamedValues.erase(VarName);
// for expr always returns 0.0.
return Constant::getNullValue(Type::getDoubleTy(LLVMContext));
return Constant::getNullValue(Type::getDoubleTy(TheContext));
}
The final code handles various cleanups: now that we have the "NextVar"
@ -772,7 +796,7 @@ Full Code Listing
=================
Here is the complete code listing for our running example, enhanced with
the if/then/else and for expressions.. To build this example, use:
the if/then/else and for expressions. To build this example, use:
.. code-block:: bash

View File

@ -31,7 +31,7 @@ User-defined Operators: the Idea
================================
The "operator overloading" that we will add to Kaleidoscope is more
general than languages like C++. In C++, you are only allowed to
general than in languages like C++. In C++, you are only allowed to
redefine existing operators: you can't programmatically change the
grammar, introduce new operators, change precedence levels, etc. In this
chapter, we will add this capability to Kaleidoscope, which will let the
@ -41,8 +41,8 @@ The point of going into user-defined operators in a tutorial like this
is to show the power and flexibility of using a hand-written parser.
Thus far, the parser we have been implementing uses recursive descent
for most parts of the grammar and operator precedence parsing for the
expressions. See `Chapter 2 <LangImpl2.html>`_ for details. Without
using operator precedence parsing, it would be very difficult to allow
expressions. See `Chapter 2 <LangImpl2.html>`_ for details. By
using operator precedence parsing, it is very easy to allow
the programmer to introduce new operators into the grammar: the grammar
is dynamically extensible as the JIT runs.
@ -143,17 +143,18 @@ this:
: Name(name), Args(std::move(Args)), IsOperator(IsOperator),
Precedence(Prec) {}
Function *codegen();
const std::string &getName() const { return Name; }
bool isUnaryOp() const { return IsOperator && Args.size() == 1; }
bool isBinaryOp() const { return IsOperator && Args.size() == 2; }
char getOperatorName() const {
assert(isUnaryOp() || isBinaryOp());
return Name[Name.size()-1];
return Name[Name.size() - 1];
}
unsigned getBinaryPrecedence() const { return Precedence; }
Function *codegen();
};
Basically, in addition to knowing a name for the prototype, we now keep
@ -194,7 +195,7 @@ user-defined operator, we need to parse it:
// Read the precedence if present.
if (CurTok == tok_number) {
if (NumVal < 1 || NumVal > 100)
return LogErrorP("Invalid precedecnce: must be 1..100");
return LogErrorP("Invalid precedence: must be 1..100");
BinaryPrecedence = (unsigned)NumVal;
getNextToken();
}
@ -225,7 +226,7 @@ This is all fairly straightforward parsing code, and we have already
seen a lot of similar code in the past. One interesting part about the
code above is the couple lines that set up ``FnName`` for binary
operators. This builds names like "binary@" for a newly defined "@"
operator. This then takes advantage of the fact that symbol names in the
operator. It then takes advantage of the fact that symbol names in the
LLVM symbol table are allowed to have any character in them, including
embedded nul characters.
@ -251,7 +252,7 @@ default case for our existing binary operator node:
case '<':
L = Builder.CreateFCmpULT(L, R, "cmptmp");
// Convert bool 0/1 to double 0.0 or 1.0
return Builder.CreateUIToFP(L, Type::getDoubleTy(LLVMContext),
return Builder.CreateUIToFP(L, Type::getDoubleTy(TheContext),
"booltmp");
default:
break;
@ -259,7 +260,7 @@ default case for our existing binary operator node:
// If it wasn't a builtin binary operator, it must be a user defined one. Emit
// a call to it.
Function *F = TheModule->getFunction(std::string("binary") + Op);
Function *F = getFunction(std::string("binary") + Op);
assert(F && "binary operator not found!");
Value *Ops[2] = { L, R };
@ -277,22 +278,21 @@ The final piece of code we are missing, is a bit of top-level magic:
.. code-block:: c++
Function *FunctionAST::codegen() {
NamedValues.clear();
Function *TheFunction = Proto->codegen();
// Transfer ownership of the prototype to the FunctionProtos map, but keep a
// reference to it for use below.
auto &P = *Proto;
FunctionProtos[Proto->getName()] = std::move(Proto);
Function *TheFunction = getFunction(P.getName());
if (!TheFunction)
return nullptr;
// If this is an operator, install it.
if (Proto->isBinaryOp())
BinopPrecedence[Proto->getOperatorName()] = Proto->getBinaryPrecedence();
if (P.isBinaryOp())
BinopPrecedence[P.getOperatorName()] = P.getBinaryPrecedence();
// Create a new basic block to start insertion into.
BasicBlock *BB = BasicBlock::Create(LLVMContext, "entry", TheFunction);
Builder.SetInsertPoint(BB);
if (Value *RetVal = Body->codegen()) {
...
BasicBlock *BB = BasicBlock::Create(TheContext, "entry", TheFunction);
...
Basically, before codegening a function, if it is a user-defined
operator, we register it in the precedence table. This allows the binary
@ -323,7 +323,8 @@ that, we need an AST node:
public:
UnaryExprAST(char Opcode, std::unique_ptr<ExprAST> Operand)
: Opcode(Opcode), Operand(std::move(Operand)) {}
virtual Value *codegen();
Value *codegen() override;
};
This AST node is very simple and obvious by now. It directly mirrors the
@ -345,7 +346,7 @@ simple: we'll add a new function to do it:
int Opc = CurTok;
getNextToken();
if (auto Operand = ParseUnary())
return llvm::unique_ptr<UnaryExprAST>(Opc, std::move(Operand));
return llvm::make_unique<UnaryExprAST>(Opc, std::move(Operand));
return nullptr;
}
@ -433,7 +434,7 @@ unary operators. It looks like this:
if (!OperandV)
return nullptr;
Function *F = TheModule->getFunction(std::string("unary")+Opcode);
Function *F = getFunction(std::string("unary") + Opcode);
if (!F)
return LogErrorV("Unknown unary operator");
@ -461,7 +462,7 @@ newline):
declare double @printd(double)
ready> def binary : 1 (x y) 0; # Low-precedence operator that ignores operands.
..
...
ready> printd(123) : printd(456) : printd(789);
123.000000
456.000000
@ -518,10 +519,9 @@ denser the character:
::
ready>
extern putchard(char)
def printdensity(d)
ready> extern putchard(char);
...
ready> def printdensity(d)
if d > 8 then
putchard(32) # ' '
else if d > 4 then
@ -538,9 +538,9 @@ denser the character:
Evaluated to 0.000000
Based on these simple primitive operations, we can start to define more
interesting things. For example, here's a little function that solves
for the number of iterations it takes a function in the complex plane to
converge:
interesting things. For example, here's a little function that determines
the number of iterations it takes for a certain function in the complex
plane to diverge:
::
@ -742,7 +742,7 @@ Full Code Listing
=================
Here is the complete code listing for our running example, enhanced with
the if/then/else and for expressions.. To build this example, use:
the support for user-defined operators. To build this example, use:
.. code-block:: bash

View File

@ -327,7 +327,7 @@ to update:
static std::map<std::string, AllocaInst*> NamedValues;
Also, since we will need to create these alloca's, we'll use a helper
Also, since we will need to create these allocas, we'll use a helper
function that ensures that the allocas are created in the entry block of
the function:
@ -339,7 +339,7 @@ the function:
const std::string &VarName) {
IRBuilder<> TmpB(&TheFunction->getEntryBlock(),
TheFunction->getEntryBlock().begin());
return TmpB.CreateAlloca(Type::getDoubleTy(LLVMContext), 0,
return TmpB.CreateAlloca(Type::getDoubleTy(TheContext), 0,
VarName.c_str());
}
@ -348,7 +348,7 @@ the first instruction (.begin()) of the entry block. It then creates an
alloca with the expected name and returns it. Because all values in
Kaleidoscope are doubles, there is no need to pass in a type to use.
With this in place, the first functionality change we want to make is to
With this in place, the first functionality change we want to make belongs to
variable references. In our new scheme, variables live on the stack, so
code generating a reference to them actually needs to produce a load
from the stack slot:
@ -377,7 +377,7 @@ the unabridged code):
// Create an alloca for the variable in the entry block.
AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, VarName);
// Emit the start code first, without 'variable' in scope.
// Emit the start code first, without 'variable' in scope.
Value *StartVal = Start->codegen();
if (!StartVal)
return nullptr;
@ -408,21 +408,25 @@ them. The code for this is also pretty simple:
.. code-block:: c++
/// CreateArgumentAllocas - Create an alloca for each argument and register the
/// argument in the symbol table so that references to it will succeed.
void PrototypeAST::CreateArgumentAllocas(Function *F) {
Function::arg_iterator AI = F->arg_begin();
for (unsigned Idx = 0, e = Args.size(); Idx != e; ++Idx, ++AI) {
Function *FunctionAST::codegen() {
...
Builder.SetInsertPoint(BB);
// Record the function arguments in the NamedValues map.
NamedValues.clear();
for (auto &Arg : TheFunction->args()) {
// Create an alloca for this variable.
AllocaInst *Alloca = CreateEntryBlockAlloca(F, Args[Idx]);
AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, Arg.getName());
// Store the initial value into the alloca.
Builder.CreateStore(AI, Alloca);
Builder.CreateStore(&Arg, Alloca);
// Add arguments to variable symbol table.
NamedValues[Args[Idx]] = Alloca;
NamedValues[Arg.getName()] = Alloca;
}
}
if (Value *RetVal = Body->codegen()) {
...
For each argument, we make an alloca, store the input value to the
function into the alloca, and register the alloca as the memory location
@ -434,15 +438,13 @@ get good codegen once again:
.. code-block:: c++
// Set up the optimizer pipeline. Start with registering info about how the
// target lays out data structures.
OurFPM.add(new DataLayout(*TheExecutionEngine->getDataLayout()));
// Promote allocas to registers.
OurFPM.add(createPromoteMemoryToRegisterPass());
TheFPM->add(createPromoteMemoryToRegisterPass());
// Do simple "peephole" optimizations and bit-twiddling optzns.
OurFPM.add(createInstructionCombiningPass());
TheFPM->add(createInstructionCombiningPass());
// Reassociate expressions.
OurFPM.add(createReassociatePass());
TheFPM->add(createReassociatePass());
...
It is interesting to see what the code looks like before and after the
mem2reg optimization runs. For example, this is the before/after code
@ -454,7 +456,7 @@ for our recursive fib function. Before the optimization:
entry:
%x1 = alloca double
store double %x, double* %x1
%x2 = load double* %x1
%x2 = load double, double* %x1
%cmptmp = fcmp ult double %x2, 3.000000e+00
%booltmp = uitofp i1 %cmptmp to double
%ifcond = fcmp one double %booltmp, 0.000000e+00
@ -464,10 +466,10 @@ for our recursive fib function. Before the optimization:
br label %ifcont
else: ; preds = %entry
%x3 = load double* %x1
%x3 = load double, double* %x1
%subtmp = fsub double %x3, 1.000000e+00
%calltmp = call double @fib(double %subtmp)
%x4 = load double* %x1
%x4 = load double, double* %x1
%subtmp5 = fsub double %x4, 2.000000e+00
%calltmp6 = call double @fib(double %subtmp5)
%addtmp = fadd double %calltmp, %calltmp6
@ -677,10 +679,10 @@ var/in, it looks like this:
public:
VarExprAST(std::vector<std::pair<std::string, std::unique_ptr<ExprAST>>> VarNames,
std::unique_ptr<ExprAST> body)
: VarNames(std::move(VarNames)), Body(std::move(Body)) {}
std::unique_ptr<ExprAST> Body)
: VarNames(std::move(VarNames)), Body(std::move(Body)) {}
virtual Value *codegen();
Value *codegen() override;
};
var/in allows a list of names to be defined all at once, and each name
@ -812,7 +814,7 @@ previous value that we replace in OldBindings.
if (!InitVal)
return nullptr;
} else { // If not specified, use 0.0.
InitVal = ConstantFP::get(LLVMContext, APFloat(0.0));
InitVal = ConstantFP::get(TheContext, APFloat(0.0));
}
AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, VarName);

View File

@ -18,7 +18,7 @@ Source level debugging uses formatted data that helps a debugger
translate from binary and the state of the machine back to the
source that the programmer wrote. In LLVM we generally use a format
called `DWARF <http://dwarfstd.org>`_. DWARF is a compact encoding
that represents types, source locations, and variable locations.
that represents types, source locations, and variable locations.
The short summary of this chapter is that we'll go through the
various things you have to add to a programming language to
@ -94,14 +94,14 @@ Then we're going to remove the command line code wherever it exists:
return;
@@ -1184,7 +1183,6 @@ int main() {
BinopPrecedence['*'] = 40; // highest.
// Prime the first token.
- fprintf(stderr, "ready> ");
getNextToken();
Lastly we're going to disable all of the optimization passes and the JIT so
that the only thing that happens after we're done parsing and generating
code is that the llvm IR goes to standard error:
code is that the LLVM IR goes to standard error:
.. code-block:: udiff
@ -140,7 +140,7 @@ code is that the llvm IR goes to standard error:
-
+ #endif
OurFPM.doInitialization();
// Set the global so the code gen can use this.
This relatively small set of changes get us to the point that we can compile
@ -166,8 +166,8 @@ DWARF Emission Setup
Similar to the ``IRBuilder`` class we have a
`DIBuilder <http://llvm.org/doxygen/classllvm_1_1DIBuilder.html>`_ class
that helps in constructing debug metadata for an llvm IR file. It
corresponds 1:1 similarly to ``IRBuilder`` and llvm IR, but with nicer names.
that helps in constructing debug metadata for an LLVM IR file. It
corresponds 1:1 similarly to ``IRBuilder`` and LLVM IR, but with nicer names.
Using it does require that you be more familiar with DWARF terminology than
you needed to be with ``IRBuilder`` and ``Instruction`` names, but if you
read through the general documentation on the
@ -194,7 +194,7 @@ expressions:
} KSDbgInfo;
DIType *DebugInfo::getDoubleTy() {
if (DblTy.isValid())
if (DblTy)
return DblTy;
DblTy = DBuilder->createBasicType("double", 64, 64, dwarf::DW_ATE_float);
@ -214,7 +214,7 @@ There are a couple of things to note here. First, while we're producing a
compile unit for a language called Kaleidoscope we used the language
constant for C. This is because a debugger wouldn't necessarily understand
the calling conventions or default ABI for a language it doesn't recognize
and we follow the C ABI in our llvm code generation so it's the closest
and we follow the C ABI in our LLVM code generation so it's the closest
thing to accurate. This ensures we can actually call functions from the
debugger and have them execute. Secondly, you'll see the "fib.ks" in the
call to ``createCompileUnit``. This is a default hard coded value since
@ -259,10 +259,11 @@ information) and construct our function definition:
unsigned LineNo = 0;
unsigned ScopeLine = 0;
DISubprogram *SP = DBuilder->createFunction(
FContext, Name, StringRef(), Unit, LineNo,
CreateFunctionType(Args.size(), Unit), false /* internal linkage */,
true /* definition */, ScopeLine, DINode::FlagPrototyped, false);
F->setSubprogram(SP);
FContext, P.getName(), StringRef(), Unit, LineNo,
CreateFunctionType(TheFunction->arg_size(), Unit),
false /* internal linkage */, true /* definition */, ScopeLine,
DINode::FlagPrototyped, false);
TheFunction->setSubprogram(SP);
and we now have an DISubprogram that contains a reference to all of our
metadata for the function.
@ -326,10 +327,9 @@ that we pass down through when we create a new expression:
giving us locations for each of our expressions and variables.
From this we can make sure to tell ``DIBuilder`` when we're at a new source
location so it can use that when we generate the rest of our code and make
sure that each instruction has source location information. We do this
by constructing another small function:
To make sure that every instruction gets proper source location information,
we have to tell ``Builder`` whenever we're at a new source location.
We use a small helper function for this:
.. code-block:: c++
@ -343,40 +343,23 @@ by constructing another small function:
DebugLoc::get(AST->getLine(), AST->getCol(), Scope));
}
that both tells the main ``IRBuilder`` where we are, but also what scope
we're in. Since we've just created a function above we can either be in
the main file scope (like when we created our function), or now we can be
in the function scope we just created. To represent this we create a stack
of scopes:
This both tells the main ``IRBuilder`` where we are, but also what scope
we're in. The scope can either be on compile-unit level or be the nearest
enclosing lexical block like the current function.
To represent this we create a stack of scopes:
.. code-block:: c++
std::vector<DIScope *> LexicalBlocks;
std::map<const PrototypeAST *, DIScope *> FnScopeMap;
and keep a map of each function to the scope that it represents (an
DISubprogram is also an DIScope).
Then we make sure to:
and push the scope (function) to the top of the stack when we start
generating the code for each function:
.. code-block:: c++
KSDbgInfo.emitLocation(this);
KSDbgInfo.LexicalBlocks.push_back(SP);
emit the location every time we start to generate code for a new AST, and
also:
.. code-block:: c++
KSDbgInfo.FnScopeMap[this] = SP;
store the scope (function) when we create it and use it:
KSDbgInfo.LexicalBlocks.push_back(&KSDbgInfo.FnScopeMap[Proto]);
when we start generating the code for each function.
also, don't forget to pop the scope back off of your scope stack at the
Also, we may not forget to pop the scope back off of the scope stack at the
end of the code generation for the function:
.. code-block:: c++
@ -385,6 +368,13 @@ end of the code generation for the function:
// unconditionally.
KSDbgInfo.LexicalBlocks.pop_back();
Then we make sure to emit the location every time we start to generate code
for a new AST object:
.. code-block:: c++
KSDbgInfo.emitLocation(this);
Variables
=========
@ -392,25 +382,37 @@ Now that we have functions, we need to be able to print out the variables
we have in scope. Let's get our function arguments set up so we can get
decent backtraces and see how our functions are being called. It isn't
a lot of code, and we generally handle it when we're creating the
argument allocas in ``PrototypeAST::CreateArgumentAllocas``.
argument allocas in ``FunctionAST::codegen``.
.. code-block:: c++
DIScope *Scope = KSDbgInfo.LexicalBlocks.back();
DIFile *Unit = DBuilder->createFile(KSDbgInfo.TheCU.getFilename(),
KSDbgInfo.TheCU.getDirectory());
DILocalVariable D = DBuilder->createParameterVariable(
Scope, Args[Idx], Idx + 1, Unit, Line, KSDbgInfo.getDoubleTy(), true);
// Record the function arguments in the NamedValues map.
NamedValues.clear();
unsigned ArgIdx = 0;
for (auto &Arg : TheFunction->args()) {
// Create an alloca for this variable.
AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, Arg.getName());
DBuilder->insertDeclare(Alloca, D, DBuilder->createExpression(),
DebugLoc::get(Line, 0, Scope),
Builder.GetInsertBlock());
// Create a debug descriptor for the variable.
DILocalVariable *D = DBuilder->createParameterVariable(
SP, Arg.getName(), ++ArgIdx, Unit, LineNo, KSDbgInfo.getDoubleTy(),
true);
Here we're doing a few things. First, we're grabbing our current scope
for the variable so we can say what range of code our variable is valid
through. Second, we're creating the variable, giving it the scope,
DBuilder->insertDeclare(Alloca, D, DBuilder->createExpression(),
DebugLoc::get(LineNo, 0, SP),
Builder.GetInsertBlock());
// Store the initial value into the alloca.
Builder.CreateStore(&Arg, Alloca);
// Add arguments to variable symbol table.
NamedValues[Arg.getName()] = Alloca;
}
Here we're first creating the variable, giving it the scope (``SP``),
the name, source location, type, and since it's an argument, the argument
index. Third, we create an ``lvm.dbg.declare`` call to indicate at the IR
index. Next, we create an ``lvm.dbg.declare`` call to indicate at the IR
level that we've got a variable in an alloca (and it gives a starting
location for the variable), and setting a source location for the
beginning of the scope on the declare.
@ -420,7 +422,7 @@ assumptions based on how code and debug information was generated for them
in the past. In this case we need to do a little bit of a hack to avoid
generating line information for the function prologue so that the debugger
knows to skip over those instructions when setting a breakpoint. So in
``FunctionAST::CodeGen`` we add a couple of lines:
``FunctionAST::CodeGen`` we add some more lines:
.. code-block:: c++
@ -434,7 +436,7 @@ body of the function:
.. code-block:: c++
KSDbgInfo.emitLocation(Body);
KSDbgInfo.emitLocation(Body.get());
With this we have enough debug information to set breakpoints in functions,
print out argument variables, and call functions. Not too bad for just a

View File

@ -140,6 +140,8 @@ class PrototypeAST {
public:
PrototypeAST(const std::string &Name, std::vector<std::string> Args)
: Name(Name), Args(std::move(Args)) {}
const std::string &getName() const { return Name; }
};
/// FunctionAST - This class represents a function definition itself.

View File

@ -650,14 +650,20 @@ static void MainLoop() {
// "Library" functions that can be "extern'd" from user code.
//===----------------------------------------------------------------------===//
#ifdef LLVM_ON_WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
/// putchard - putchar that takes a double and returns 0.
extern "C" double putchard(double X) {
extern "C" DLLEXPORT double putchard(double X) {
fputc((char)X, stderr);
return 0;
}
/// printd - printf that takes a double prints it as "%f\n", returning 0.
extern "C" double printd(double X) {
extern "C" DLLEXPORT double printd(double X) {
fprintf(stderr, "%f\n", X);
return 0;
}

View File

@ -622,7 +622,7 @@ Value *IfExprAST::codegen() {
if (!CondV)
return nullptr;
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
CondV = Builder.CreateFCmpONE(
CondV, ConstantFP::get(TheContext, APFloat(0.0)), "ifcond");
@ -736,7 +736,7 @@ Value *ForExprAST::codegen() {
if (!EndCond)
return nullptr;
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
EndCond = Builder.CreateFCmpONE(
EndCond, ConstantFP::get(TheContext, APFloat(0.0)), "loopcond");
@ -924,14 +924,20 @@ static void MainLoop() {
// "Library" functions that can be "extern'd" from user code.
//===----------------------------------------------------------------------===//
#ifdef LLVM_ON_WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
/// putchard - putchar that takes a double and returns 0.
extern "C" double putchard(double X) {
extern "C" DLLEXPORT double putchard(double X) {
fputc((char)X, stderr);
return 0;
}
/// printd - printf that takes a double prints it as "%f\n", returning 0.
extern "C" double printd(double X) {
extern "C" DLLEXPORT double printd(double X) {
fprintf(stderr, "%f\n", X);
return 0;
}

View File

@ -567,7 +567,7 @@ static std::unique_ptr<PrototypeAST> ParsePrototype() {
// Read the precedence if present.
if (CurTok == tok_number) {
if (NumVal < 1 || NumVal > 100)
return LogErrorP("Invalid precedecnce: must be 1..100");
return LogErrorP("Invalid precedence: must be 1..100");
BinaryPrecedence = (unsigned)NumVal;
getNextToken();
}
@ -734,7 +734,7 @@ Value *IfExprAST::codegen() {
if (!CondV)
return nullptr;
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
CondV = Builder.CreateFCmpONE(
CondV, ConstantFP::get(TheContext, APFloat(0.0)), "ifcond");
@ -848,7 +848,7 @@ Value *ForExprAST::codegen() {
if (!EndCond)
return nullptr;
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
EndCond = Builder.CreateFCmpONE(
EndCond, ConstantFP::get(TheContext, APFloat(0.0)), "loopcond");
@ -1043,14 +1043,20 @@ static void MainLoop() {
// "Library" functions that can be "extern'd" from user code.
//===----------------------------------------------------------------------===//
#ifdef LLVM_ON_WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
/// putchard - putchar that takes a double and returns 0.
extern "C" double putchard(double X) {
extern "C" DLLEXPORT double putchard(double X) {
fputc((char)X, stderr);
return 0;
}
/// printd - printf that takes a double prints it as "%f\n", returning 0.
extern "C" double printd(double X) {
extern "C" DLLEXPORT double printd(double X) {
fprintf(stderr, "%f\n", X);
return 0;
}

View File

@ -639,7 +639,7 @@ static std::unique_ptr<PrototypeAST> ParsePrototype() {
// Read the precedence if present.
if (CurTok == tok_number) {
if (NumVal < 1 || NumVal > 100)
return LogErrorP("Invalid precedecnce: must be 1..100");
return LogErrorP("Invalid precedence: must be 1..100");
BinaryPrecedence = (unsigned)NumVal;
getNextToken();
}
@ -840,7 +840,7 @@ Value *IfExprAST::codegen() {
if (!CondV)
return nullptr;
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
CondV = Builder.CreateFCmpONE(
CondV, ConstantFP::get(TheContext, APFloat(0.0)), "ifcond");
@ -963,7 +963,7 @@ Value *ForExprAST::codegen() {
Value *NextVar = Builder.CreateFAdd(CurVar, StepVal, "nextvar");
Builder.CreateStore(NextVar, Alloca);
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
EndCond = Builder.CreateFCmpONE(
EndCond, ConstantFP::get(TheContext, APFloat(0.0)), "loopcond");
@ -1115,6 +1115,8 @@ static void InitializeModuleAndPassManager() {
// Create a new pass manager attached to it.
TheFPM = llvm::make_unique<legacy::FunctionPassManager>(TheModule.get());
// Promote allocas to registers.
TheFPM->add(createPromoteMemoryToRegisterPass());
// Do simple "peephole" optimizations and bit-twiddling optzns.
TheFPM->add(createInstructionCombiningPass());
// Reassociate expressions.
@ -1210,14 +1212,20 @@ static void MainLoop() {
// "Library" functions that can be "extern'd" from user code.
//===----------------------------------------------------------------------===//
#ifdef LLVM_ON_WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
/// putchard - putchar that takes a double and returns 0.
extern "C" double putchard(double X) {
extern "C" DLLEXPORT double putchard(double X) {
fputc((char)X, stderr);
return 0;
}
/// printd - printf that takes a double prints it as "%f\n", returning 0.
extern "C" double printd(double X) {
extern "C" DLLEXPORT double printd(double X) {
fprintf(stderr, "%f\n", X);
return 0;
}

View File

@ -642,7 +642,7 @@ static std::unique_ptr<PrototypeAST> ParsePrototype() {
// Read the precedence if present.
if (CurTok == tok_number) {
if (NumVal < 1 || NumVal > 100)
return LogErrorP("Invalid precedecnce: must be 1..100");
return LogErrorP("Invalid precedence: must be 1..100");
BinaryPrecedence = (unsigned)NumVal;
getNextToken();
}
@ -841,7 +841,7 @@ Value *IfExprAST::codegen() {
if (!CondV)
return nullptr;
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
CondV = Builder.CreateFCmpONE(
CondV, ConstantFP::get(TheContext, APFloat(0.0)), "ifcond");
@ -964,7 +964,7 @@ Value *ForExprAST::codegen() {
Value *NextVar = Builder.CreateFAdd(CurVar, StepVal, "nextvar");
Builder.CreateStore(NextVar, Alloca);
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
EndCond = Builder.CreateFCmpONE(
EndCond, ConstantFP::get(TheContext, APFloat(0.0)), "loopcond");
@ -1173,14 +1173,20 @@ static void MainLoop() {
// "Library" functions that can be "extern'd" from user code.
//===----------------------------------------------------------------------===//
#ifdef LLVM_ON_WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
/// putchard - putchar that takes a double and returns 0.
extern "C" double putchard(double X) {
extern "C" DLLEXPORT double putchard(double X) {
fputc((char)X, stderr);
return 0;
}
/// printd - printf that takes a double prints it as "%f\n", returning 0.
extern "C" double printd(double X) {
extern "C" DLLEXPORT double printd(double X) {
fprintf(stderr, "%f\n", X);
return 0;
}

View File

@ -756,7 +756,7 @@ static std::unique_ptr<PrototypeAST> ParsePrototype() {
// Read the precedence if present.
if (CurTok == tok_number) {
if (NumVal < 1 || NumVal > 100)
return LogErrorP("Invalid precedecnce: must be 1..100");
return LogErrorP("Invalid precedence: must be 1..100");
BinaryPrecedence = (unsigned)NumVal;
getNextToken();
}
@ -1004,7 +1004,7 @@ Value *IfExprAST::codegen() {
if (!CondV)
return nullptr;
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
CondV = Builder.CreateFCmpONE(
CondV, ConstantFP::get(TheContext, APFloat(0.0)), "ifcond");
@ -1129,7 +1129,7 @@ Value *ForExprAST::codegen() {
Value *NextVar = Builder.CreateFAdd(CurVar, StepVal, "nextvar");
Builder.CreateStore(NextVar, Alloca);
// Convert condition to a bool by comparing equal to 0.0.
// Convert condition to a bool by comparing non-equal to 0.0.
EndCond = Builder.CreateFCmpONE(
EndCond, ConstantFP::get(TheContext, APFloat(0.0)), "loopcond");
@ -1379,14 +1379,20 @@ static void MainLoop() {
// "Library" functions that can be "extern'd" from user code.
//===----------------------------------------------------------------------===//
#ifdef LLVM_ON_WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
/// putchard - putchar that takes a double and returns 0.
extern "C" double putchard(double X) {
extern "C" DLLEXPORT double putchard(double X) {
fputc((char)X, stderr);
return 0;
}
/// printd - printf that takes a double prints it as "%f\n", returning 0.
extern "C" double printd(double X) {
extern "C" DLLEXPORT double printd(double X) {
fprintf(stderr, "%f\n", X);
return 0;
}

View File

@ -97,17 +97,40 @@ private:
}
JITSymbol findMangledSymbol(const std::string &Name) {
#ifdef LLVM_ON_WIN32
// The symbol lookup of ObjectLinkingLayer uses the SymbolRef::SF_Exported
// flag to decide whether a symbol will be visible or not, when we call
// IRCompileLayer::findSymbolIn with ExportedSymbolsOnly set to true.
//
// But for Windows COFF objects, this flag is currently never set.
// For a potential solution see: https://reviews.llvm.org/rL258665
// For now, we allow non-exported symbols on Windows as a workaround.
const bool ExportedSymbolsOnly = false;
#else
const bool ExportedSymbolsOnly = true;
#endif
// Search modules in reverse order: from last added to first added.
// This is the opposite of the usual search order for dlsym, but makes more
// sense in a REPL where we want to bind to the newest available definition.
for (auto H : make_range(ModuleHandles.rbegin(), ModuleHandles.rend()))
if (auto Sym = CompileLayer.findSymbolIn(H, Name, true))
if (auto Sym = CompileLayer.findSymbolIn(H, Name, ExportedSymbolsOnly))
return Sym;
// If we can't find the symbol in the JIT, try looking in the host process.
if (auto SymAddr = RTDyldMemoryManager::getSymbolAddressInProcess(Name))
return JITSymbol(SymAddr, JITSymbolFlags::Exported);
#ifdef LLVM_ON_WIN32
// For Windows retry without "_" at begining, as RTDyldMemoryManager uses
// GetProcAddress and standard libraries like msvcrt.dll use names
// with and without "_" (for example "_itoa" but "sin").
if (Name.length() > 2 && Name[0] == '_')
if (auto SymAddr =
RTDyldMemoryManager::getSymbolAddressInProcess(Name.substr(1)))
return JITSymbol(SymAddr, JITSymbolFlags::Exported);
#endif
return nullptr;
}