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.cpp
src/Parser.h src/Parser.h
src/sapphirepch.h src/sapphirepch.h
src/IRBuilder.cpp
src/IRBuilder.h
) )
target_include_directories(sapphirec PUBLIC src) 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 #pragma once
#include <memory> #include "IRBuilder.h"
#include "llvm/IR/Value.h"
class IRBuilder;
class ASTNode class ASTNode
{ {
public: public:
ASTNode(); ASTNode();
~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) MulNode::MulNode(std::shared_ptr<ExprNode> left, std::shared_ptr<ExprNode> right, char op)
: BinaryOpNode(left, right), op(op) : BinaryOpNode(left, right), op(op)
{ {
assert(op == '*' || op == '/');
} }
MulNode::~MulNode() 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: public:
MulNode(std::shared_ptr<ExprNode> left, std::shared_ptr<ExprNode> right, char op); MulNode(std::shared_ptr<ExprNode> left, std::shared_ptr<ExprNode> right, char op);
~MulNode(); ~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; 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>; using IntegerNode = BasicNumberNode<int>;

View File

@ -3,8 +3,22 @@
SumNode::SumNode(std::shared_ptr<ExprNode> left, std::shared_ptr<ExprNode> right, char op) SumNode::SumNode(std::shared_ptr<ExprNode> left, std::shared_ptr<ExprNode> right, char op)
: BinaryOpNode(left, right), op(op) : BinaryOpNode(left, right), op(op)
{ {
assert(op == '+' || op == '-');
} }
SumNode::~SumNode() 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: public:
SumNode(std::shared_ptr<ExprNode> left, std::shared_ptr<ExprNode> right, char op); SumNode(std::shared_ptr<ExprNode> left, std::shared_ptr<ExprNode> right, char op);
~SumNode(); ~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 "Parser.h"
#include "AST/MulNode.h"
#include "AST/SumNode.h"
Parser::Parser(const TokenStream& tokens) : tokens(tokens) Parser::Parser(const TokenStream& tokens) : tokens(tokens)
{ {
} }
Parser::~Parser()
{
}
std::shared_ptr<Parser> Parser::new_parser(const TokenStream& tokens) std::shared_ptr<Parser> Parser::new_parser(const TokenStream& tokens)
{ {
return std::shared_ptr<Parser>( 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() std::shared_ptr<ASTNode> Parser::parse()
{ {
auto result = walk_expr(); advance();
return expr();
if (result.is_error())
{
result.ethrow();
}
return result.get();
} }
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 ++index;
// list, thats why im not using make_shared here 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++]; Token token = *current_token;
if (current_token.tk_type == TT_Number)
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)); advance();
} return std::make_shared<FloatNode>(token.float_value);
else
{
return ErrorOr<NumberNode>("expected a number", current_token);
} }
} }
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 #pragma once
#include "AST/NumberNode.h" #include "AST/NumberNode.h"
#include "AST/SumNode.h"
#include "Error.h" #include "Error.h"
#include "Lexer.h" #include "Lexer.h"
#include "sapphirepch.h" #include "sapphirepch.h"
@ -7,64 +8,18 @@
/* Parser class for the Sapphire compiler. */ /* Parser class for the Sapphire compiler. */
class Parser 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: private:
Parser(const TokenStream& tokens); Parser(const TokenStream& tokens);
TokenStream tokens; TokenStream tokens;
int index = -1;
int advance();
Token* current_token;
ErrorOr<ExprNode> walk_expr(); std::shared_ptr<NumberNode> factor();
ErrorOr<NumberNode> walk_number(); std::shared_ptr<ExprNode> expr();
std::shared_ptr<ExprNode> term();
int m_index;
int saved_m_index;
void save_current_position();
void restore_current_position();
public: public:
~Parser();
/* Construct a new Parser with the given TokenStream. */ /* Construct a new Parser with the given TokenStream. */
static std::shared_ptr<Parser> new_parser(const TokenStream& tokens); 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. */ /* 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 "Arguments.h"
#include "FileIO.h" #include "FileIO.h"
#include "IRBuilder.h"
#include "Importer.h" #include "Importer.h"
#include "Lexer.h" #include "Lexer.h"
#include "Normalizer.h" #include "Normalizer.h"
#include "Parser.h"
#include "sapphirepch.h" #include "sapphirepch.h"
#include "utils.h" #include "utils.h"
@ -31,12 +33,13 @@ int main(int argc, char** argv)
result = Normalizer::normalize(result); result = Normalizer::normalize(result);
} }
for (int i = 0; i < result.size(); i++) auto parser = Parser::new_parser(result);
{
std::cout << result[i].to_string() << std::endl;
}
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 { let @main in {
outln('Hello, world!'); outln('Hello, world!');