basic LLVM IR building and parsing

This commit is contained in:
apio 2022-07-15 14:28:05 +02:00
parent b11a108e5f
commit 7522858897
14 changed files with 177 additions and 90 deletions

View File

@ -52,6 +52,8 @@ add_executable(
src/Parser.cpp
src/Parser.h
src/sapphirepch.h
src/IRBuilder.cpp
src/IRBuilder.h
)
target_include_directories(sapphirec PUBLIC src)

View File

@ -1 +1 @@
1 + 2 * 4
1+2+3*5

View File

@ -1,9 +1,14 @@
#pragma once
#include <memory>
#include "IRBuilder.h"
#include "llvm/IR/Value.h"
class IRBuilder;
class ASTNode
{
public:
ASTNode();
~ASTNode();
virtual llvm::Value* codegen(IRBuilder* generator) = 0;
};

View File

@ -3,8 +3,22 @@
MulNode::MulNode(std::shared_ptr<ExprNode> left, std::shared_ptr<ExprNode> right, char op)
: BinaryOpNode(left, right), op(op)
{
assert(op == '*' || op == '/');
}
MulNode::~MulNode()
{
}
llvm::Value* MulNode::codegen(IRBuilder* generator)
{
switch (op)
{
case '*':
return generator->getBuilder()->CreateMul(left->codegen(generator), right->codegen(generator));
case '/':
return generator->getBuilder()->CreateSDiv(left->codegen(generator), right->codegen(generator));
default:
assert(0 && "unreachable");
}
}

View File

@ -8,4 +8,6 @@ class MulNode final : public BinaryOpNode
public:
MulNode(std::shared_ptr<ExprNode> left, std::shared_ptr<ExprNode> right, char op);
~MulNode();
llvm::Value* codegen(IRBuilder* generator) override;
};

View File

@ -39,6 +39,19 @@ template<typename T> class BasicNumberNode : public NumberNode
{
return std::is_integral<T>::value;
}
llvm::Value* codegen(IRBuilder* generator) override
{
if (is_integral())
{
return llvm::ConstantInt::getSigned(llvm::IntegerType::getInt32Ty(generator->getBuilder()->getContext()),
value);
}
else
{
return llvm::ConstantFP::get(llvm::Type::getFloatTy(generator->getBuilder()->getContext()), value);
}
}
};
using IntegerNode = BasicNumberNode<int>;

View File

@ -3,8 +3,22 @@
SumNode::SumNode(std::shared_ptr<ExprNode> left, std::shared_ptr<ExprNode> right, char op)
: BinaryOpNode(left, right), op(op)
{
assert(op == '+' || op == '-');
}
SumNode::~SumNode()
{
}
llvm::Value* SumNode::codegen(IRBuilder* generator)
{
switch (op)
{
case '+':
return generator->getBuilder()->CreateAdd(left->codegen(generator), right->codegen(generator));
case '-':
return generator->getBuilder()->CreateSub(left->codegen(generator), right->codegen(generator));
default:
assert(0 && "unreachable");
}
}

View File

@ -8,4 +8,6 @@ class SumNode final : public BinaryOpNode
public:
SumNode(std::shared_ptr<ExprNode> left, std::shared_ptr<ExprNode> right, char op);
~SumNode();
llvm::Value* codegen(IRBuilder* generator) override;
};

42
src/IRBuilder.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "IRBuilder.h"
#include "Arguments.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/Verifier.h"
IRBuilder::IRBuilder()
{
context = std::make_unique<llvm::LLVMContext>();
builder = std::unique_ptr<llvm::IRBuilder<>>(new llvm::IRBuilder<>(*context));
module = std::make_unique<llvm::Module>(Arguments::input_fname, *context);
}
llvm::IRBuilder<>* IRBuilder::getBuilder()
{
return builder.get();
}
void IRBuilder::create_main_function(std::shared_ptr<ASTNode> expression)
{
llvm::FunctionType* mainType =
llvm::FunctionType::get(llvm::IntegerType::getInt32Ty(*context), std::vector<llvm::Type*>(), false);
llvm::Function* main = llvm::Function::Create(mainType, llvm::GlobalValue::ExternalLinkage, "main", module.get());
llvm::BasicBlock* entryBlock = llvm::BasicBlock::Create(*context, "entry", main);
builder->SetInsertPoint(entryBlock);
llvm::Value* returnValue = expression->codegen(this);
builder->CreateRet(returnValue);
llvm::verifyFunction(*main);
}
std::string IRBuilder::getGeneratedIR()
{
std::string str;
llvm::raw_string_ostream oss(str);
module->print(oss, nullptr);
return oss.str();
}

23
src/IRBuilder.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include "AST/ASTNode.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
class ASTNode;
class IRBuilder
{
std::unique_ptr<llvm::LLVMContext> context;
std::unique_ptr<llvm::IRBuilder<>> builder;
std::unique_ptr<llvm::Module> module;
public:
IRBuilder();
void create_main_function(std::shared_ptr<ASTNode> expression);
llvm::IRBuilder<>* getBuilder();
std::string getGeneratedIR();
};

View File

@ -1,13 +1,11 @@
#include "Parser.h"
#include "AST/MulNode.h"
#include "AST/SumNode.h"
Parser::Parser(const TokenStream& tokens) : tokens(tokens)
{
}
Parser::~Parser()
{
}
std::shared_ptr<Parser> Parser::new_parser(const TokenStream& tokens)
{
return std::shared_ptr<Parser>(
@ -16,45 +14,59 @@ std::shared_ptr<Parser> Parser::new_parser(const TokenStream& tokens)
std::shared_ptr<ASTNode> Parser::parse()
{
auto result = walk_expr();
if (result.is_error())
{
result.ethrow();
}
return result.get();
advance();
return expr();
}
Parser::ErrorOr<ExprNode> Parser::walk_expr()
int Parser::advance()
{
return ErrorOr<ExprNode>(new ExprNode()); // constructor does not want to accept a shared_ptr<T> in the argument
// list, thats why im not using make_shared here
++index;
if (index < tokens.size())
{
current_token = &tokens[index];
}
return 1;
}
Parser::ErrorOr<NumberNode> Parser::walk_number()
std::shared_ptr<NumberNode> Parser::factor()
{
Token& current_token = tokens[m_index++];
if (current_token.tk_type == TT_Number)
Token token = *current_token;
if (token.tk_type == TT_Number)
{
return ErrorOr<NumberNode>(new IntegerNode(current_token.int_value));
advance();
return std::make_shared<IntegerNode>(token.int_value);
}
else if (current_token.tk_type == TT_Float)
if (token.tk_type == TT_Float)
{
return ErrorOr<NumberNode>(new FloatNode(current_token.float_value));
}
else
{
return ErrorOr<NumberNode>("expected a number", current_token);
advance();
return std::make_shared<FloatNode>(token.float_value);
}
}
void Parser::save_current_position()
std::shared_ptr<ExprNode> Parser::term()
{
saved_m_index = m_index;
std::shared_ptr<ExprNode> left = factor();
while (current_token->tk_type == TT_Mul || current_token->tk_type == TT_Div)
{
Token op = *current_token;
advance();
std::shared_ptr<ExprNode> right = factor();
left = std::make_shared<MulNode>(left, right, op.tk_type == TT_Mul ? '*' : '/');
}
return left;
}
void Parser::restore_current_position()
std::shared_ptr<ExprNode> Parser::expr()
{
m_index = saved_m_index;
std::shared_ptr<ExprNode> left = term();
while (current_token->tk_type == TT_Plus || current_token->tk_type == TT_Minus)
{
Token op = *current_token;
advance();
std::shared_ptr<ExprNode> right = term();
left = std::make_shared<SumNode>(left, right, op.tk_type == TT_Plus ? '+' : '-');
}
return left;
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "AST/NumberNode.h"
#include "AST/SumNode.h"
#include "Error.h"
#include "Lexer.h"
#include "sapphirepch.h"
@ -7,64 +8,18 @@
/* Parser class for the Sapphire compiler. */
class Parser
{
/* Struct to store a parsing result which can be either a parsing error or a success, in which case it contains a
* pointer to the result. */
template<typename T> struct ErrorOr
{
/* Return the stored pointer. */
std::shared_ptr<T> get()
{
assert(!m_is_error);
return m_ptr;
}
/* Call Error::throw_error() with the stored error's location, line text, and the error string provided to this
* struct instance. */
void ethrow()
{
assert(m_is_error);
Error::throw_error(error_tok->loc, error_tok->line(), m_error);
}
/* Construct a new successful ErrorOr with a heap-allocated pointer to the result class. */
ErrorOr(T* ptr) : m_ptr(ptr), m_is_error(false)
{
}
/* Construct a new failed ErrorOr with the error details and the token where parsing failed. */
ErrorOr(const std::string& error, const Token& error_tok)
: m_error(error), m_is_error(true), error_tok(error_tok)
{
}
/* Is this ErrorOr instance successful or failed? */
bool is_error()
{
return m_is_error;
}
private:
bool m_is_error;
std::string m_error;
std::unique_ptr<Token> error_tok;
std::shared_ptr<T> m_ptr;
};
private:
Parser(const TokenStream& tokens);
TokenStream tokens;
int index = -1;
int advance();
Token* current_token;
ErrorOr<ExprNode> walk_expr();
ErrorOr<NumberNode> walk_number();
int m_index;
int saved_m_index;
void save_current_position();
void restore_current_position();
std::shared_ptr<NumberNode> factor();
std::shared_ptr<ExprNode> expr();
std::shared_ptr<ExprNode> term();
public:
~Parser();
/* Construct a new Parser with the given TokenStream. */
static std::shared_ptr<Parser> new_parser(const TokenStream& tokens);
/* Parse the stored TokenStream and return the top-level node of the result Abstract Syntax Tree. */

View File

@ -1,8 +1,10 @@
#include "Arguments.h"
#include "FileIO.h"
#include "IRBuilder.h"
#include "Importer.h"
#include "Lexer.h"
#include "Normalizer.h"
#include "Parser.h"
#include "sapphirepch.h"
#include "utils.h"
@ -31,12 +33,13 @@ int main(int argc, char** argv)
result = Normalizer::normalize(result);
}
for (int i = 0; i < result.size(); i++)
{
std::cout << result[i].to_string() << std::endl;
}
auto parser = Parser::new_parser(result);
std::cout << "Output filename: " << Arguments::output_fname << std::endl;
auto ast = parser->parse();
std::cout << "Output target triple: " << Arguments::TargetTriple.getTriple() << std::endl;
IRBuilder builder;
builder.create_main_function(ast);
std::cout << builder.getGeneratedIR();
}

View File

@ -1,4 +1,4 @@
const outln from @'core/io';
const { outln } from @'core/io';
let @main in {
outln('Hello, world!');