Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making a JIT Compiler and adding Optimization Passes #7

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,28 @@ jobs:
with:
submodules: recursive

- name: Install LLVM
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 16
sudo apt-get remove -y llvm-12 lldb-12 llvm-12-dev libllvm12 llvm-12-runtime
sudo apt-get remove -y llvm-13 lldb-13 llvm-13-dev libllvm13 llvm-13-runtime

- name: Install Boost
# Build your program with the given configuration
run: sudo apt-get install -y libboost-all-dev

- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
run: cmake -B ${{github.workspace}}/cmake-build-debug -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}

- name: Build
# Build your program with the given configuration
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
run: cmake --build ${{github.workspace}}/cmake-build-debug --config ${{env.BUILD_TYPE}}

- name: Test
working-directory: ${{github.workspace}}/build/test
working-directory: ${{github.workspace}}/cmake-build-debug/test
# Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ${{github.workspace}}/build/test/compiler_tests
run: ${{github.workspace}}/cmake-build-debug/test/compiler_tests
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# C++ Build files
build/
cmake-build-debug/

# Editor files
.idea/
7 changes: 5 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
[submodule "deps/spdlog"]
path = deps/spdlog
url = git@github.com:gabime/spdlog.git
url = https://github.com/gabime/spdlog.git
[submodule "deps/gtest"]
path = deps/gtest
url = [email protected]:google/googletest.git
url = https://github.com/google/googletest.git
[submodule "deps/termcolor"]
path = deps/termcolor
url = https://github.com/ikalnytskyi/termcolor
2 changes: 1 addition & 1 deletion .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "linux-clang-x64",
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
"compileCommands": "${workspaceFolder}/cmake-build-debug/compile_commands.json",
"compilerPath": "/usr/bin/g++",
"configurationProvider": "ms-vscode.cmake-tools"
}
Expand Down
13 changes: 5 additions & 8 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Run compiler",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/src/compiler",
"program": "${workspaceFolder}/cmake-build-debug/src/compiler",
"args": ["--file", "../../test/sample_programs/test_simple.kld"],
"stopAtEntry": false,
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"cwd": "${workspaceFolder}/build/src",
"cwd": "${workspaceFolder}/cmake-build-debug/src",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
Expand All @@ -32,13 +29,13 @@
"name": "Run interpreter",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/src/compiler",
"program": "${workspaceFolder}/cmake-build-debug/src/compiler",
"args": [],
"stopAtEntry": false,
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"cwd": "${workspaceFolder}/build/src",
"cwd": "${workspaceFolder}/cmake-build-debug/src",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
Expand All @@ -53,4 +50,4 @@
]
}
]
}
}
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"label": "build",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}/build"
"cwd": "${workspaceRoot}/cmake-build-debug"
},
"command": "cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_BUILD_TYPE=Debug ${workspaceRoot} & cmake --build . -j 32",
"problemMatcher": {
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ add_subdirectory(test)

add_subdirectory(deps/spdlog)
add_subdirectory(deps/gtest)
add_subdirectory(deps/termcolor)
1 change: 1 addition & 0 deletions deps/termcolor
Submodule termcolor added at b3cb0f
4 changes: 2 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
set(SOURCE_FILES lexer.cpp ast.cpp parser.cpp codegen.cpp)
set(SOURCE_FILES lexer.cpp ast.cpp parser.cpp codegen.cpp evaluator.cpp jit.cpp)

add_library(compiler_lib ${SOURCE_FILES})
target_link_libraries(compiler_lib Boost::program_options spdlog::spdlog gtest
${LLVM_LIBS})

add_executable(compiler compiler.cpp)
target_link_libraries(compiler compiler_lib Boost::program_options
spdlog::spdlog)
spdlog::spdlog termcolor::termcolor)
65 changes: 37 additions & 28 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace kccani
{

CodegenContentType CodeGeneratorLLVM::operator()(std::unique_ptr<ExprAST>&& ast)
llvm::Value* CodeGeneratorLLVM::codegen_expr(std::unique_ptr<ExprAST>&& ast)
{
switch (ast->get_type())
{
Expand All @@ -25,8 +25,8 @@ CodegenContentType CodeGeneratorLLVM::operator()(std::unique_ptr<ExprAST>&& ast)
case ExprAST::ExpressionType::BINARY_EXPR:
{
auto expr = std::unique_ptr<BinaryExprAST>(static_cast<BinaryExprAST*>(ast.release()));
llvm::Value* l = std::get<llvm::Value*>((*this)(std::move(expr->lhs)));
llvm::Value* r = std::get<llvm::Value*>((*this)(std::move(expr->rhs)));
llvm::Value* l = this->codegen_expr(std::move(expr->lhs));
llvm::Value* r = this->codegen_expr(std::move(expr->rhs));
if (!l || !r)
return (llvm::Value*) nullptr;
switch (expr->opcode)
Expand All @@ -52,41 +52,56 @@ CodegenContentType CodeGeneratorLLVM::operator()(std::unique_ptr<ExprAST>&& ast)
{
auto expr = std::unique_ptr<FunctionCallExprAST>(static_cast<FunctionCallExprAST*>(ast.release()));

llvm::Function *callee_func = this->module->getFunction(expr->callee);
llvm::Function *callee_func = this->get_function(expr->callee);
if (!callee_func)
{
spdlog::error("Undefined function with name: " + expr->callee);
return (llvm::Value*) nullptr;
return nullptr;
}

if (callee_func->arg_size() != expr->args.size())
{
spdlog::error("Incorrect number of arguments passed");
return (llvm::Value*) nullptr;
return nullptr;
}
std::vector<llvm::Value*> args_llvm_values;
for (unsigned i = 0, e = expr->args.size(); i != e; ++i) {
args_llvm_values.push_back(std::get<llvm::Value*>((*this)(std::move(expr->args[i]))));
args_llvm_values.push_back(this->codegen_expr(std::move(expr->args[i])));
if (!args_llvm_values.back())
return (llvm::Value*) nullptr;
return nullptr;
}

return this->builder->CreateCall(callee_func, args_llvm_values, "calltmp");
}
default:
spdlog::error("Invalid expression type, all expression types should be in switch above");
return (llvm::Value*) nullptr;
return nullptr;
}
}

CodegenContentType CodeGeneratorLLVM::operator()(std::unique_ptr<FunctionAST>&& ast)
llvm::Function* CodeGeneratorLLVM::get_function(const std::string& name)
{
return this->module->getFunction(name);
}


llvm::Function* CodeGeneratorLLVM::operator()(std::unique_ptr<ExprAST>&& ast)
{
auto fn_proto = std::make_unique<FunctionPrototypeAST>("__anon_expr", std::vector<std::string>());
auto fn_ast = std::make_unique<FunctionAST>(std::move(fn_proto), std::move(ast));
auto fn_llvm_ir = (*this)(std::move(fn_ast));
fn_llvm_ir->eraseFromParent();
return fn_llvm_ir;
}

llvm::Function* CodeGeneratorLLVM::operator()(std::unique_ptr<FunctionAST>&& ast)
{
// First, check for an existing function from a previous 'extern' declaration.
llvm::Function *the_function = this->module->getFunction(ast->prototype->name);
llvm::Function* the_function = this->get_function(ast->prototype->name);
if (!the_function)
the_function = std::get<llvm::Function*>((*this)(std::move(ast->prototype)));
the_function = (*this)(std::move(ast->prototype));
if (!the_function)
return (llvm::Function*) nullptr;
return nullptr;

// Create a new basic block to start insertion into.
llvm::BasicBlock *basic_block = llvm::BasicBlock::Create(*this->context, "entry", the_function);
Expand All @@ -97,7 +112,7 @@ CodegenContentType CodeGeneratorLLVM::operator()(std::unique_ptr<FunctionAST>&&
for (auto &arg : the_function->args())
this->named_values[std::string(arg.getName())] = &arg;

if (llvm::Value *return_value = std::get<llvm::Value*>((*this)(std::move(ast->body)))) {
if (llvm::Value *return_value = this->codegen_expr(std::move(ast->body))) {
// Finish off the function.
this->builder->CreateRet(return_value);
// Validate the generated code, checking for consistency.
Expand All @@ -106,10 +121,10 @@ CodegenContentType CodeGeneratorLLVM::operator()(std::unique_ptr<FunctionAST>&&
}
// Error reading body, remove function.
the_function->eraseFromParent();
return (llvm::Function*) nullptr;
return nullptr;
}

CodegenContentType CodeGeneratorLLVM::operator()(std::unique_ptr<FunctionPrototypeAST>&& ast)
llvm::Function* CodeGeneratorLLVM::operator()(std::unique_ptr<FunctionPrototypeAST>&& ast)
{
std::vector<llvm::Type*> doubles(ast->args.size(), llvm::Type::getDoubleTy(*this->context));
llvm::FunctionType *function_type = llvm::FunctionType::get(
Expand All @@ -124,9 +139,9 @@ CodegenContentType CodeGeneratorLLVM::operator()(std::unique_ptr<FunctionPrototy
return function;
}

CodegenContentType CodeGeneratorLLVM::operator()(std::monostate&& invalid)
llvm::Function* CodeGeneratorLLVM::operator()(std::monostate&& invalid)
{
return std::monostate{};
return nullptr;
}

void CodeGeneratorLLVM::print() const
Expand All @@ -142,22 +157,16 @@ std::string CodeGeneratorLLVM::to_string() const
return llvm_output;
}

void CodeGeneratorLLVM::print(CodegenContentType generated_code)
void CodeGeneratorLLVM::print(llvm::Function* generated_code)
{
if (std::holds_alternative<llvm::Function*>(generated_code))
std::get<llvm::Function*>(generated_code)->print(llvm::errs());
else if (std::holds_alternative<llvm::Value*>(generated_code))
std::get<llvm::Value*>(generated_code)->print(llvm::errs());
generated_code->print(llvm::errs());
}

std::string CodeGeneratorLLVM::to_string(CodegenContentType generated_code)
std::string CodeGeneratorLLVM::to_string(llvm::Function* generated_code)
{
std::string llvm_output;
llvm::raw_string_ostream rso(llvm_output);
if (std::holds_alternative<llvm::Function*>(generated_code))
std::get<llvm::Function*>(generated_code)->print(rso);
else if (std::holds_alternative<llvm::Value*>(generated_code))
std::get<llvm::Value*>(generated_code)->print(rso);
generated_code->print(rso);
return llvm_output;
}

Expand Down
26 changes: 15 additions & 11 deletions src/codegen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,32 @@
namespace kccani
{

using CodegenContentType = std::variant<
llvm::Value*,
llvm::Function*,
std::monostate>;

class CodeGeneratorLLVM
{
protected:
std::unique_ptr<llvm::LLVMContext> context{std::make_unique<llvm::LLVMContext>()};
std::unique_ptr<llvm::IRBuilder<>> builder{std::make_unique<llvm::IRBuilder<>>(*context)};
std::unique_ptr<llvm::Module> module{std::make_unique<llvm::Module>("kccani_jit", *context)};
std::map<std::string, llvm::Value*> named_values;

// Evaluating the bodies of functions down to a value
virtual llvm::Value* codegen_expr(std::unique_ptr<ExprAST>&& ast);
virtual llvm::Function* get_function(const std::string& name);

public:
CodegenContentType operator()(std::unique_ptr<ExprAST>&& ast);
CodegenContentType operator()(std::unique_ptr<FunctionAST>&& ast);
CodegenContentType operator()(std::unique_ptr<FunctionPrototypeAST>&& ast);
CodegenContentType operator()(std::monostate&& ast);
// Handing top-level expressions
virtual llvm::Function* operator()(std::unique_ptr<ExprAST>&& ast);
// Handling function definitions
virtual llvm::Function* operator()(std::unique_ptr<FunctionAST>&& ast);
// Handling external linkage
virtual llvm::Function* operator()(std::unique_ptr<FunctionPrototypeAST>&& ast);
// Handling statements with parsing errors in them
virtual llvm::Function* operator()(std::monostate&& ast);

void print() const;
std::string to_string() const;
static void print(CodegenContentType generated_code);
static std::string to_string(CodegenContentType generated_code);
static void print(llvm::Function* generated_code);
static std::string to_string(llvm::Function* generated_code);
};

}
8 changes: 5 additions & 3 deletions src/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
#include <variant>

#include <boost/program_options.hpp>
#include <termcolor/termcolor.hpp>

#include "lexer.hpp"
#include "ast.hpp"
#include "parser.hpp"
#include "codegen.hpp"
#include "evaluator.hpp"


int main(int argc, char* argv[])
Expand Down Expand Up @@ -54,14 +56,14 @@ int main(int argc, char* argv[])
{
auto lexer = kccani::Lexer(std::cin);
auto parser = kccani::Parser(lexer);
kccani::CodeGeneratorLLVM codegen;
kccani::JITEvaluatorLLVM codegen;
while (true)
{
std::cout << "kccani> ";
std::cout << termcolor::red << "kccani> " << termcolor::reset;
auto ast = parser.get();

auto result = std::visit(std::ref(codegen), std::move(ast));
kccani::CodeGeneratorLLVM::print(result);
kccani::CodeGeneratorLLVM::print(std::move(result));
std::cout << std::endl;
}
}
Expand Down
Loading
Loading