Skip to content

Commit 06aaa35

Browse files
author
Akshay Deodhar
committed
Finished adding JIT support, basic JIT REPL works
1 parent c7ebd9f commit 06aaa35

File tree

2 files changed

+120
-47
lines changed

2 files changed

+120
-47
lines changed

kaleidoscope.cpp

Lines changed: 113 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "KaleidoscopeJIT.h"
12
#include "llvm/ADT/APFloat.h"
23
#include "llvm/ADT/STLExtras.h"
34
#include "llvm/IR/BasicBlock.h"
@@ -24,10 +25,11 @@
2425
#include <cstdio>
2526
#include <cstdlib>
2627
#include <unordered_map>
27-
#include "./KaleidoscopeJIT.h"
2828

2929
using namespace llvm;
3030

31+
using namespace llvm::orc;
32+
3133
#define IRGEN true
3234

3335
// ---Lexer---
@@ -119,6 +121,9 @@ static std::unique_ptr<Module> TheModule; // to hold blocks, definitions? (TODO)
119121
static std::unique_ptr<IRBuilder<>> Builder; // for creating instructions, constants, etc
120122
static std::unique_ptr<legacy::FunctionPassManager> TheFPM; // Function pass manager
121123
static std::unordered_map<std::string, Value *> Symbols; // Maps names inside function context to LLVM "values"
124+
static std::unique_ptr<KaleidoscopeJIT> TheJIT; // JIT engine for Kaleidoscope
125+
// Prototypes will be codegened in _each_ module, again and again? TODO: check
126+
static ExitOnError ExitOnErr;
122127

123128

124129

@@ -658,7 +663,7 @@ static std::unique_ptr<FunctionAST> ParseTopLevelExpr() {
658663

659664
if (auto E = ParseExpression()) {
660665

661-
auto Proto = std::make_unique<PrototypeAST>("", std::vector<std::string>());
666+
auto Proto = std::make_unique<PrototypeAST>("__anon_expr", std::vector<std::string>());
662667

663668
//fprintf(stderr, "debug: toplevelexpr\n");
664669
return std::make_unique<FunctionAST>(std::move(Proto), std::move(E));
@@ -667,8 +672,53 @@ static std::unique_ptr<FunctionAST> ParseTopLevelExpr() {
667672
return nullptr;
668673
}
669674

675+
static std::unordered_map<std::string, std::unique_ptr<PrototypeAST>> FunctionProtos; // Function Name -> PrototypeAST Node map
676+
670677
// -- Code Generator --
671678

679+
static void InitializeModuleAndPassManager() {
680+
TheContext = std::make_unique<LLVMContext>();
681+
TheModule = std::make_unique<Module>("kaleidoscope", *TheContext);
682+
TheModule->setDataLayout(TheJIT->getDataLayout());
683+
684+
Builder = std::make_unique<IRBuilder<>>(*TheContext);
685+
686+
// Why .get? Ahh- I want to pass a pointer. What about uniqueness?
687+
TheFPM = std::make_unique<legacy::FunctionPassManager>(TheModule.get());
688+
689+
// Peephole optimizations
690+
TheFPM->add(createInstructionCombiningPass());
691+
692+
// ?
693+
TheFPM->add(createReassociatePass());
694+
695+
// Global value numbering-> common subexpression elimination. Global is actually per-function
696+
TheFPM->add(createGVNPass());
697+
698+
// Dead code elimination pass;
699+
TheFPM->add(createCFGSimplificationPass());
700+
701+
// Run initalizers for all passes added to pass manager
702+
TheFPM->doInitialization();
703+
}
704+
705+
Function *getOrCreateFunction(const std::string& Name) {
706+
// Check whether declaration is present in current module
707+
if (auto *F = TheModule->getFunction(Name)) {
708+
// Hypothesis: When each function is created in a new module, this will never happen
709+
return F;
710+
}
711+
712+
// Check whether this function has been declared previously
713+
auto F_itr = FunctionProtos.find(Name);
714+
if (F_itr != FunctionProtos.end()) {
715+
// If yes, codegen declaration to _this module_.
716+
return F_itr->second->codegen();
717+
}
718+
719+
return nullptr;
720+
}
721+
672722
// Create a new constant of type "double"
673723
Value* NumExprAST::codegen() {
674724
return ConstantFP::get(Builder->getDoubleTy(), Val);
@@ -688,7 +738,7 @@ Value* VariableExprAST::codegen() {
688738
Value* CallExprAST::codegen() {
689739

690740
// Obtain function with name `Callee` from Module
691-
Function *func = TheModule->getFunction(Callee);
741+
Function *func = getOrCreateFunction(Callee);
692742
if (!func) {
693743
return LogErrorV((std::string("undefined function: ") + Callee).c_str());
694744
}
@@ -776,11 +826,13 @@ Function* FunctionAST::codegen() {
776826

777827
// TODO: why are we doing this? This codegen method will never be called
778828
// for an extern function, right? Why else do I need to check?
779-
Function *func = TheModule->getFunction(Proto->GetName());
829+
const std::string& func_name = Proto->GetName();
780830

781-
if (!func) {
782-
func = Proto->codegen();
783-
}
831+
// Make global FunctionProto map the owner of function prototype node
832+
// This ensures that declaration can be codegened in different modules
833+
FunctionProtos[func_name] = std::move(Proto);
834+
835+
Function *func = getOrCreateFunction(func_name);
784836

785837
if (!func) {
786838
return nullptr;
@@ -832,6 +884,13 @@ static void HandleDefinition() {
832884
#if IRGEN
833885
if (Function *func = def->codegen()) {
834886
func->print(errs());
887+
888+
ExitOnErr(TheJIT->addModule(
889+
ThreadSafeModule(std::move(TheModule), std::move(TheContext))
890+
));
891+
892+
InitializeModuleAndPassManager();
893+
835894
fprintf(stderr, "\n");
836895
fprintf(stderr, "Read a function definition\n");
837896
}
@@ -844,7 +903,7 @@ static void HandleDefinition() {
844903

845904

846905
static void HandleExtern() {
847-
if (const auto extn = ParseExtern()) {
906+
if (auto extn = ParseExtern()) {
848907

849908
#if DEBUGPARSE
850909
LispPrintVisitor lvt;
@@ -856,6 +915,7 @@ static void HandleExtern() {
856915
func->print(errs());
857916
fprintf(stderr, "\n");
858917
fprintf(stderr, "Read an extern\n");
918+
FunctionProtos[extn->GetName()] = std::move(extn);
859919
}
860920
#endif
861921

@@ -878,7 +938,30 @@ static void HandleTopLevelExpression() {
878938
fprintf(stderr, "\n");
879939
fprintf(stderr, "Parsed a top level expression\n");
880940

881-
func->eraseFromParent();
941+
// TODO: how do I know which functions to call? In this case, I have the
942+
// tutorial for reference. What if I don't know what does what?
943+
auto RT = TheJIT->getMainJITDylib().createResourceTracker();
944+
945+
// TODO: wasn't the context supposed to be unique for the
946+
// program? If this context is now owned by the JIT, then
947+
// will each top level expression (and even each function)
948+
// be created in a new context?
949+
auto TSM = ThreadSafeModule(std::move(TheModule), std::move(TheContext));
950+
951+
ExitOnErr(TheJIT->addModule(std::move(TSM), RT));
952+
953+
// Now, the next function will be placed in a new Module?
954+
InitializeModuleAndPassManager();
955+
956+
auto ExprSymbol = ExitOnErr(TheJIT->lookup("__anon_expr"));
957+
958+
// TODO: why do I need intptr_t
959+
double (*Fn)() = (double(*)())(intptr_t)ExprSymbol.getAddress();
960+
961+
fprintf(stderr, "Evaluated to %lf\n", Fn());
962+
963+
ExitOnErr(RT->remove());
964+
882965
}
883966
#endif
884967

@@ -887,28 +970,6 @@ static void HandleTopLevelExpression() {
887970
}
888971
}
889972

890-
static void InitializeModuleAndPassManager() {
891-
TheContext = std::make_unique<LLVMContext>();
892-
TheModule = std::make_unique<Module>("kaleidoscope", *TheContext);
893-
// Why .get? Ahh- I want to pass a pointer. What about uniqueness?
894-
TheFPM = std::make_unique<legacy::FunctionPassManager>(TheModule.get());
895-
Builder = std::make_unique<IRBuilder<>>(*TheContext);
896-
897-
// Peephole optimizations
898-
TheFPM->add(createInstructionCombiningPass());
899-
900-
// ?
901-
TheFPM->add(createReassociatePass());
902-
903-
// Global value numbering-> common subexpression elimination. Global is actually per-function
904-
TheFPM->add(createGVNPass());
905-
906-
// Dead code elimination pass;
907-
TheFPM->add(createCFGSimplificationPass());
908-
909-
// Run initalizers for all passes added to pass manager
910-
TheFPM->doInitialization();
911-
}
912973

913974
// top = definition | expression | external | ;
914975
static void MainLoop() {
@@ -964,31 +1025,37 @@ int oldmain(void) {
9641025
}
9651026

9661027
int main(void) {
967-
BinopPrecedence['>'] = 10;
968-
BinopPrecedence['<'] = 10;
969-
BinopPrecedence['+'] = 20;
970-
BinopPrecedence['-'] = 20;
971-
BinopPrecedence['*'] = 40;
972-
BinopPrecedence['/'] = 40;
9731028

974-
fprintf(stderr, "ready>");
975-
getNextToken();
1029+
InitializeNativeTarget();
1030+
InitializeNativeTargetAsmParser();
1031+
InitializeNativeTargetAsmPrinter();
1032+
1033+
BinopPrecedence['>'] = 10;
1034+
BinopPrecedence['<'] = 10;
1035+
BinopPrecedence['+'] = 20;
1036+
BinopPrecedence['-'] = 20;
1037+
BinopPrecedence['*'] = 40;
1038+
BinopPrecedence['/'] = 40;
1039+
1040+
fprintf(stderr, "ready>");
1041+
getNextToken();
9761042

9771043
#if IRGEN
978-
InitializeModuleAndPassManager();
1044+
TheJIT = ExitOnErr(KaleidoscopeJIT::Create());
1045+
InitializeModuleAndPassManager();
9791046
#endif
9801047

981-
MainLoop();
982-
//oldmain();
1048+
MainLoop();
1049+
//oldmain();
9831050

9841051
#if IRGEN
985-
verifyModule(*TheModule, &errs());
1052+
verifyModule(*TheModule, &errs());
9861053

987-
TheModule->print(errs(), nullptr);
988-
fprintf(stderr, "\n");
1054+
TheModule->print(errs(), nullptr);
1055+
fprintf(stderr, "\n");
9891056
#endif
9901057

991-
return 0;
1058+
return 0;
9921059
}
9931060

9941061

notes.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,10 @@
4747
tokprec = 40, BinOp = *, o
4848
- The bug was: I was trying to look at precedence of next token before parsing the Primary Expression
4949

50-
-
50+
- How the "separate modules for each function" thing works:
51+
> The final line above actually creates the IR Function corresponding to the
52+
> Prototype. This indicates the type, linkage and name to use, as well as which
53+
> module to insert into. “external linkage” means that the function may be defined
54+
> outside the current module and/or that it is callable by functions outside the
55+
> module. The Name passed in is the name the user specified: since “TheModule” is
56+
> specified, this name is registered in “TheModule”s symbol table.

0 commit comments

Comments
 (0)