forked from OSchip/llvm-project
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:
parent
315edb0216
commit
bb6805d263
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue