add IR code generation to the AST

This commit is contained in:
Chris Cromer 2022-11-21 21:24:44 -03:00
parent 43f8f292ff
commit 71156924c6
16 changed files with 192 additions and 7 deletions

20
src/ast/ast.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef OBELISK_AST_AST_H
#define OBELISK_AST_AST_H
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Type.h>
#include <map>
#include <memory>
namespace obelisk
{
static std::unique_ptr<llvm::LLVMContext> TheContext;
static std::unique_ptr<llvm::Module> TheModule;
static std::unique_ptr<llvm::IRBuilder<>> Builder;
static std::map<std::string, llvm::Value *> NamedValues;
} // namespace obelisk
#endif

View File

@ -0,0 +1,31 @@
#include "ast/ast.h"
#include "ast/call_expression_ast.h"
#include "ast/error.h"
llvm::Value *obelisk::CallExpressionAST::codegen()
{
// Look up the name in the global module table.
llvm::Function *calleeF = TheModule->getFunction(callee_);
if (!calleeF)
{
return LogErrorV("Unknown function referenced");
}
// If argument mismatch error.
if (calleeF->arg_size() != args_.size())
{
return LogErrorV("Incorrect # arguments passed");
}
std::vector<llvm::Value *> argsV;
for (unsigned i = 0, e = args_.size(); i != e; ++i)
{
argsV.push_back(args_[i]->codegen());
if (!argsV.back())
{
return nullptr;
}
}
return Builder->CreateCall(calleeF, argsV, "calltmp");
}

View File

@ -28,6 +28,8 @@ namespace obelisk
args_(std::move(args))
{
}
llvm::Value *codegen() override;
};
} // namespace obelisk

13
src/ast/error.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "ast/error.h"
std::unique_ptr<obelisk::ExpressionAST> obelisk::LogError(const char *Str)
{
fprintf(stderr, "Error: %s\n", Str);
return nullptr;
}
llvm::Value *obelisk::LogErrorV(const char *Str)
{
LogError(Str);
return nullptr;
}

14
src/ast/error.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef OBELISK_AST_ERROR_H
#define OBELISK_AST_ERROR_H
#include "ast/expression_ast.h"
#include <memory>
namespace obelisk
{
std::unique_ptr<ExpressionAST> LogError(const char *Str);
llvm::Value *LogErrorV(const char *Str);
} // namespace obelisk
#endif

View File

@ -1,12 +1,15 @@
#ifndef OBELISK_AST_EXPRESSION_AST_H
#define OBELISK_AST_EXPRESSION_AST_H
#include <llvm/IR/Constants.h>
namespace obelisk
{
class ExpressionAST
{
public:
virtual ~ExpressionAST() = default;
virtual ~ExpressionAST() = default;
virtual llvm::Value *codegen() = 0;
};
} // namespace obelisk

39
src/ast/function_ast.cpp Normal file
View File

@ -0,0 +1,39 @@
#include "ast/ast.h"
#include "ast/function_ast.h"
#include <llvm/IR/Verifier.h>
llvm::Function *obelisk::FunctionAST::codegen()
{
llvm::Function *theFunction = TheModule->getFunction(prototype_->getName());
if (!theFunction)
{
theFunction = prototype_->codegen();
}
if (!theFunction)
{
return nullptr;
}
llvm::BasicBlock *bB
= llvm::BasicBlock::Create(*TheContext, "entry", theFunction);
Builder->SetInsertPoint(bB);
NamedValues.clear();
for (auto &arg : theFunction->args())
{
NamedValues[std::string(arg.getName())] = &arg;
}
if (llvm::Value *retVal = body_->codegen())
{
Builder->CreateRet(retVal);
llvm::verifyFunction(*theFunction);
return theFunction;
}
theFunction->eraseFromParent();
return nullptr;
}

View File

@ -24,6 +24,8 @@ namespace obelisk
body_(std::move(body))
{
}
llvm::Function *codegen();
};
} // namespace obelisk

View File

@ -1,8 +1,8 @@
obelisk_ast_sources = files(
'call_expression_ast.h',
'expression_ast.h',
'function_ast.h',
'number_expression_ast.h',
'prototype_ast.h',
'variable_expression_ast.h'
'call_expression_ast.cpp',
'error.cpp',
'function_ast.cpp',
'number_expression_ast.cpp',
'prototype_ast.cpp',
'variable_expression_ast.cpp'
)

View File

@ -0,0 +1,7 @@
#include "ast/ast.h"
#include "ast/number_expression_ast.h"
llvm::Value *obelisk::NumberExpressionAST::codegen()
{
return llvm::ConstantFP::get(*TheContext, llvm::APFloat(number_));
}

View File

@ -18,6 +18,8 @@ namespace obelisk
number_(number)
{
}
llvm::Value *codegen() override;
};
} // namespace obelisk

25
src/ast/prototype_ast.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "ast/ast.h"
#include "ast/prototype_ast.h"
llvm::Function *obelisk::PrototypeAST::codegen()
{
std::vector<llvm::Type *> doubles(args_.size(),
llvm::Type::getDoubleTy(*TheContext));
llvm::FunctionType *FT
= llvm::FunctionType::get(llvm::Type::getDoubleTy(*TheContext),
doubles,
false);
llvm::Function *F = llvm::Function::Create(FT,
llvm::Function::ExternalLinkage,
name_,
obelisk::TheModule.get());
unsigned idx = 0;
for (auto &arg : F->args())
{
arg.setName(args_[idx++]);
}
return F;
}

View File

@ -1,6 +1,8 @@
#ifndef OBELISK_AST_PROTOTYPE_AST_H
#define OBELISK_AST_PROTOTYPE_AST_H
#include <llvm/IR/Function.h>
#include <string>
#include <vector>
@ -28,6 +30,8 @@ namespace obelisk
{
return name_;
}
llvm::Function* codegen();
};
} //namespace obelisk

View File

@ -0,0 +1,13 @@
#include "ast/ast.h"
#include "ast/error.h"
#include "ast/variable_expression_ast.h"
llvm::Value *obelisk::VariableExpressionAST::codegen()
{
llvm::Value *V = NamedValues[name_];
if (!V)
{
return obelisk::LogErrorV("Unknown variable name");
}
return V;
}

View File

@ -19,6 +19,8 @@ namespace obelisk
name_(name)
{
}
llvm::Value *codegen() override;
};
} //namespace obelisk

View File

@ -13,8 +13,16 @@ obelisk_sources += obelisk_ast_sources
subdir('models')
obelisk_sources += obelisk_model_sources
r = run_command('llvm-config', '--cppflags', check : true)
cpp_args = ' ' + r.stdout().replace('\n', ' ').replace('-I', '-isystem')
r = run_command('llvm-config', '--ldflags', '--system-libs', '--libs', 'core', check : true)
link_args = ' ' + r.stdout().replace('\n', ' ')
executable('obelisk',
obelisk_sources,
dependencies : [sqlite3],
cpp_args : cpp_args.split(),
link_args : link_args.split(),
install : true
)