From 71156924c6e2093690baabcb6e3120dd76ea58e3 Mon Sep 17 00:00:00 2001 From: Chris Cromer Date: Mon, 21 Nov 2022 21:24:44 -0300 Subject: [PATCH] add IR code generation to the AST --- src/ast/ast.h | 20 +++++++++++++++ src/ast/call_expression_ast.cpp | 31 +++++++++++++++++++++++ src/ast/call_expression_ast.h | 2 ++ src/ast/error.cpp | 13 ++++++++++ src/ast/error.h | 14 +++++++++++ src/ast/expression_ast.h | 5 +++- src/ast/function_ast.cpp | 39 +++++++++++++++++++++++++++++ src/ast/function_ast.h | 2 ++ src/ast/meson.build | 12 ++++----- src/ast/number_expression_ast.cpp | 7 ++++++ src/ast/number_expression_ast.h | 2 ++ src/ast/prototype_ast.cpp | 25 ++++++++++++++++++ src/ast/prototype_ast.h | 4 +++ src/ast/variable_expression_ast.cpp | 13 ++++++++++ src/ast/variable_expression_ast.h | 2 ++ src/meson.build | 8 ++++++ 16 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 src/ast/ast.h create mode 100644 src/ast/call_expression_ast.cpp create mode 100644 src/ast/error.cpp create mode 100644 src/ast/error.h create mode 100644 src/ast/function_ast.cpp create mode 100644 src/ast/number_expression_ast.cpp create mode 100644 src/ast/prototype_ast.cpp create mode 100644 src/ast/variable_expression_ast.cpp diff --git a/src/ast/ast.h b/src/ast/ast.h new file mode 100644 index 0000000..1002ace --- /dev/null +++ b/src/ast/ast.h @@ -0,0 +1,20 @@ +#ifndef OBELISK_AST_AST_H +#define OBELISK_AST_AST_H + +#include +#include +#include +#include + +#include +#include + +namespace obelisk +{ + static std::unique_ptr TheContext; + static std::unique_ptr TheModule; + static std::unique_ptr> Builder; + static std::map NamedValues; +} // namespace obelisk + +#endif diff --git a/src/ast/call_expression_ast.cpp b/src/ast/call_expression_ast.cpp new file mode 100644 index 0000000..eca1830 --- /dev/null +++ b/src/ast/call_expression_ast.cpp @@ -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 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"); +} diff --git a/src/ast/call_expression_ast.h b/src/ast/call_expression_ast.h index 247e0fb..7deaea9 100644 --- a/src/ast/call_expression_ast.h +++ b/src/ast/call_expression_ast.h @@ -28,6 +28,8 @@ namespace obelisk args_(std::move(args)) { } + + llvm::Value *codegen() override; }; } // namespace obelisk diff --git a/src/ast/error.cpp b/src/ast/error.cpp new file mode 100644 index 0000000..779f794 --- /dev/null +++ b/src/ast/error.cpp @@ -0,0 +1,13 @@ +#include "ast/error.h" + +std::unique_ptr obelisk::LogError(const char *Str) +{ + fprintf(stderr, "Error: %s\n", Str); + return nullptr; +} + +llvm::Value *obelisk::LogErrorV(const char *Str) +{ + LogError(Str); + return nullptr; +} diff --git a/src/ast/error.h b/src/ast/error.h new file mode 100644 index 0000000..1770f7f --- /dev/null +++ b/src/ast/error.h @@ -0,0 +1,14 @@ +#ifndef OBELISK_AST_ERROR_H +#define OBELISK_AST_ERROR_H + +#include "ast/expression_ast.h" + +#include + +namespace obelisk +{ + std::unique_ptr LogError(const char *Str); + llvm::Value *LogErrorV(const char *Str); +} // namespace obelisk + +#endif diff --git a/src/ast/expression_ast.h b/src/ast/expression_ast.h index 11ba2d9..a77fff7 100644 --- a/src/ast/expression_ast.h +++ b/src/ast/expression_ast.h @@ -1,12 +1,15 @@ #ifndef OBELISK_AST_EXPRESSION_AST_H #define OBELISK_AST_EXPRESSION_AST_H +#include + namespace obelisk { class ExpressionAST { public: - virtual ~ExpressionAST() = default; + virtual ~ExpressionAST() = default; + virtual llvm::Value *codegen() = 0; }; } // namespace obelisk diff --git a/src/ast/function_ast.cpp b/src/ast/function_ast.cpp new file mode 100644 index 0000000..1bdad9d --- /dev/null +++ b/src/ast/function_ast.cpp @@ -0,0 +1,39 @@ +#include "ast/ast.h" +#include "ast/function_ast.h" + +#include + +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; +} diff --git a/src/ast/function_ast.h b/src/ast/function_ast.h index 1d4ff4a..bdf630e 100644 --- a/src/ast/function_ast.h +++ b/src/ast/function_ast.h @@ -24,6 +24,8 @@ namespace obelisk body_(std::move(body)) { } + + llvm::Function *codegen(); }; } // namespace obelisk diff --git a/src/ast/meson.build b/src/ast/meson.build index afb5a71..82aa247 100644 --- a/src/ast/meson.build +++ b/src/ast/meson.build @@ -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' ) diff --git a/src/ast/number_expression_ast.cpp b/src/ast/number_expression_ast.cpp new file mode 100644 index 0000000..bff01cb --- /dev/null +++ b/src/ast/number_expression_ast.cpp @@ -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_)); +} diff --git a/src/ast/number_expression_ast.h b/src/ast/number_expression_ast.h index 568e2d6..874cb79 100644 --- a/src/ast/number_expression_ast.h +++ b/src/ast/number_expression_ast.h @@ -18,6 +18,8 @@ namespace obelisk number_(number) { } + + llvm::Value *codegen() override; }; } // namespace obelisk diff --git a/src/ast/prototype_ast.cpp b/src/ast/prototype_ast.cpp new file mode 100644 index 0000000..f7caba4 --- /dev/null +++ b/src/ast/prototype_ast.cpp @@ -0,0 +1,25 @@ +#include "ast/ast.h" +#include "ast/prototype_ast.h" + +llvm::Function *obelisk::PrototypeAST::codegen() +{ + std::vector 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; +} diff --git a/src/ast/prototype_ast.h b/src/ast/prototype_ast.h index c461fb8..5e09b8c 100644 --- a/src/ast/prototype_ast.h +++ b/src/ast/prototype_ast.h @@ -1,6 +1,8 @@ #ifndef OBELISK_AST_PROTOTYPE_AST_H #define OBELISK_AST_PROTOTYPE_AST_H +#include + #include #include @@ -28,6 +30,8 @@ namespace obelisk { return name_; } + + llvm::Function* codegen(); }; } //namespace obelisk diff --git a/src/ast/variable_expression_ast.cpp b/src/ast/variable_expression_ast.cpp new file mode 100644 index 0000000..8923fa3 --- /dev/null +++ b/src/ast/variable_expression_ast.cpp @@ -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; +} diff --git a/src/ast/variable_expression_ast.h b/src/ast/variable_expression_ast.h index 6089080..3b51f87 100644 --- a/src/ast/variable_expression_ast.h +++ b/src/ast/variable_expression_ast.h @@ -19,6 +19,8 @@ namespace obelisk name_(name) { } + + llvm::Value *codegen() override; }; } //namespace obelisk diff --git a/src/meson.build b/src/meson.build index 0474fe3..0f4d7b2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -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 )