basic LLVM IR building and parsing
This commit is contained in:
parent
b11a108e5f
commit
7522858897
@ -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)
|
||||||
|
@ -1 +1 @@
|
|||||||
1 + 2 * 4
|
1+2+3*5
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
};
|
};
|
@ -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>;
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
@ -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
42
src/IRBuilder.cpp
Normal 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
23
src/IRBuilder.h
Normal 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();
|
||||||
|
};
|
@ -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;
|
||||||
}
|
}
|
||||||
|
59
src/Parser.h
59
src/Parser.h
@ -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. */
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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!');
|
||||||
|
Loading…
Reference in New Issue
Block a user