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

Support LLVM backend #171

Merged
merged 84 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
e5de4b1
Set up initial testing configuration for llvm
leewei05 Jun 16, 2024
490766b
Create llvm IR generator
leewei05 Jun 16, 2024
c312c58
Write LLVM IR to files
leewei05 Jun 16, 2024
3e90edc
Generate return statement and constant expression LLVM IR
leewei05 Jun 18, 2024
ef1879c
Integrate lli for testing LLVM IR
leewei05 Jun 18, 2024
eca3d05
Update turnt config file and printf with parameter
leewei05 Jun 22, 2024
fcc5838
Handle simple binary expression
leewei05 Jun 22, 2024
2975b36
Update TransUnitNode and ExternDeclNode
leewei05 Jun 24, 2024
c3603aa
Insert __builtin_print_format global variable
leewei05 Jun 24, 2024
9e85f46
Print output with llvm interpreter
leewei05 Jun 24, 2024
c903cc9
Simplify IntConstExprNode by returning integer const directly
leewei05 Jun 24, 2024
df0f59a
Code generation for variable declaration and id expression
leewei05 Jun 24, 2024
2d46738
Refactor i32Ty with Util class
leewei05 Jun 24, 2024
af47ebd
Binary and comparison operator code generation
leewei05 Jun 27, 2024
ada573d
Implement simple assignment expression code generation
leewei05 Jun 27, 2024
690db76
Implement prefix increment and decrement code generation
leewei05 Jun 28, 2024
af5678e
Implement logical AND and OR operation code generation
leewei05 Jun 28, 2024
b4a9c16
Implement Bitwise complement operator code generation
leewei05 Jun 28, 2024
4b250f1
Implement postfix increment and decrement operator code generation
leewei05 Jun 28, 2024
e983617
Implement if else statement code generation
leewei05 Jun 29, 2024
7ccc800
Implement while and do while statement code generation
leewei05 Jun 29, 2024
bc5c7a5
Implement for statement code generation
leewei05 Jun 29, 2024
6bfb6d0
Implement break and continue statement code generation
leewei05 Jun 29, 2024
65b7af0
Rename integer type name
leewei05 Jun 29, 2024
563fba5
Implement pointer code generation
leewei05 Jun 29, 2024
fc9f7d4
Remove hardcode pointer size
leewei05 Jun 29, 2024
158ac6e
Implement function call expression code generation
leewei05 Jun 29, 2024
07706ec
Implement condition expression code generation
leewei05 Jun 29, 2024
c93394b
Implement array code generation
leewei05 Jun 30, 2024
fd78532
Implement function pointer code generation
leewei05 Jun 30, 2024
ceede3e
Implement Record type declaration code generation
leewei05 Jun 30, 2024
f5aecb6
Implement function pointer parameter code generation
leewei05 Jun 30, 2024
2dfce85
Implement goto and id label statement code generation
leewei05 Jun 30, 2024
98a05f3
Add newline at the end of the file
leewei05 Jun 30, 2024
99b7073
Implement switch, case, default statement code generation
leewei05 Jun 30, 2024
5de045d
Use simpler builder function
leewei05 Jun 30, 2024
4730d88
Add GetFieldTypes to get field types of record type variable
leewei05 Jul 1, 2024
d7c92a8
Implement Struct and Union variable declaration code generation
leewei05 Jul 1, 2024
e304306
Add comments and use smart pointers for module
leewei05 Jul 1, 2024
6cc3e40
Update binary operator helper functions
leewei05 Jul 1, 2024
70f5a48
Rename struct and update comments
leewei05 Jul 1, 2024
afef598
Separate LLVM util class header and source file
leewei05 Jul 1, 2024
605ad92
Rename util and type names
leewei05 Jul 1, 2024
0e58922
Refactor VarDeclNode with GetLLVMType
leewei05 Jul 1, 2024
0e2ed54
Refactor ArrDeclNode and RecordVarDeclNode with GetLLVMType
leewei05 Jul 1, 2024
5667659
Change return_type() of FuncType to unique_ptr
leewei05 Jul 1, 2024
c5c10d8
Refactor goto and id label statement with find basic block function
leewei05 Jul 1, 2024
25d215f
Refactor with CurrFunc to get the current inserting function
leewei05 Jul 1, 2024
65a6b01
Small refactor and remove unused statements
leewei05 Jul 1, 2024
ff27229
Update TODOs in makefile and main.cpp
leewei05 Jul 1, 2024
28d9087
Update Makefile with llvm-config flags and CI
leewei05 Jul 1, 2024
9f5ee56
Update LLVM installed version
leewei05 Jul 1, 2024
4061c19
Include missing headers
leewei05 Jul 1, 2024
eca43dd
Rename basic block from BB to bb
leewei05 Jul 1, 2024
4b8a5bd
Change public member to public functions
leewei05 Jul 1, 2024
6c9f9c9
Fix Clang tidy errors and ignore one check due to crash
leewei05 Jul 1, 2024
09edb47
Remove unused code
leewei05 Jul 1, 2024
d648869
Update comments
leewei05 Jul 1, 2024
4355ca5
Revert "Change return_type() of FuncType to unique_ptr"
leewei05 Jul 2, 2024
7d70f29
Refactor GetLLVMType with Type reference
leewei05 Jul 2, 2024
3b87b34
Refactor function signature and id expression with GetLLVMType
leewei05 Jul 2, 2024
2599d8a
Remove redundant val_to_type map
leewei05 Jul 2, 2024
fdd8c90
Throw exception if type is unknown
leewei05 Jul 2, 2024
9e5384a
Refactor function parameters declaration code generation
leewei05 Jul 2, 2024
7886b4d
Update comments
leewei05 Jul 2, 2024
3099ea2
Rename helper functions
leewei05 Jul 5, 2024
4e1b634
Rename builder helper class and builder reference
leewei05 Jul 5, 2024
f598de2
Inline builder integer and pointer type
leewei05 Jul 5, 2024
1f8ca0a
Refactor helper function for basic blocks
leewei05 Jul 5, 2024
aa98013
Include missing headers
leewei05 Jul 5, 2024
e22a126
Throw errors for operator helper functions
leewei05 Jul 5, 2024
89b08d1
Rename LabelInfo struct and vector
leewei05 Jul 5, 2024
6b89405
Update comments and remove redundant arguments
leewei05 Jul 5, 2024
49d3ab0
Refactor create basic block if not exist logic
leewei05 Jul 5, 2024
93dd326
Refactor IdExprNode
leewei05 Jul 5, 2024
cd17d35
Record return value of printf function
leewei05 Jul 5, 2024
bc6e894
Rename variable
leewei05 Jul 5, 2024
015682e
Refactor with GetLLVMType
leewei05 Jul 5, 2024
3fa5700
Fix clang-tidy
leewei05 Jul 5, 2024
a2ce9e0
Move HasTerminator outside of BuilderHelper class
leewei05 Jul 5, 2024
9c0088f
Rename builder_helper_ member and update comments
leewei05 Jul 5, 2024
6e50cce
Pass constant string reference
leewei05 Jul 5, 2024
c030086
Refactor LLVM IR generator data members
leewei05 Jul 5, 2024
f212231
Update README
leewei05 Jul 5, 2024
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
8 changes: 8 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@
#
# -cppcoreguidelines-explicit-virtual-functions:
# It's an alias of "modernize-use-override".
#
# -bugprone-unchecked-optional-access:
# Clang-tidy will crash due to unknown reasons.
#
# -bugprone-easily-swappable-parameters
# Nothing we can do about it.

# Warnings are easily be overlooked when they are not treated as errors.
WarningsAsErrors: "*"

Checks: >
-*,
bugprone-*,
-bugprone-unchecked-optional-access,
-bugprone-easily-swappable-parameters,
performance-*,
clang-analyzer-*,
cppcoreguidelines-*,
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- name: Install LLVM
run: |
sudo add-apt-repository -y 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main'
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install -q -y llvm-18 lld-18 llvm-18-runtime
- name: Install QBE
run: scripts/install-qbe.sh
- name: Install cxxopts
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ lex.yy.*
# QBE IR files
*.ssa

# LLVM IR files
*.ll

# Python environments
.env
.venv
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ TARGET := vitaminc
CXX := g++
CC = $(CXX)
CLANG_TIDY ?= clang-tidy
CXXFLAGS = -g3 -std=c++17 -Wall -MMD -Iinclude -Werror
CXXFLAGS = -g3 -std=c++17 -Wall -MMD -Iinclude -I$(shell llvm-config-18 --includedir) -Werror
CFLAGS = $(CXXFLAGS)
LDLIBS = -lfmt
LDLIBS = -lfmt $(shell llvm-config-18 --libs core)
LEX = lex
# C++ features are used, yacc doesn't suffice
YACC = bison
Expand Down Expand Up @@ -87,7 +87,7 @@ coverage-report: coverage
@echo "Open $(COVERAGE_DIR)/index.html in your browser to view the coverage report."

clean:
$(RM) -r *.s *.o lex.yy.* y.tab.* *.output *.ssa *.out $(TARGET) $(OBJS) $(DEPS) \
$(RM) -r *.s *.o lex.yy.* y.tab.* *.output *.ssa *.out *.ll $(TARGET) $(OBJS) $(DEPS) \
$(OBJS:.o=.gcda) $(OBJS:.o=.gcno) *.gcov $(COVERAGE_DIR)
cd test/ && $(MAKE) clean

Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ VitaminC 🍋 is an educational C compiler frontend written in C++. As a fronten
> [!WARNING]
> This project is still under development. Many features are not yet implemented. Currently, we do not support preprocessor directives, and only the `int` type and its pointer, as well as object types, are supported.

The main goal of this project is to demonstrate how a compiler frontend works with [the LLVM compiler infrastructure](https://llvm.org/) and generates LLVM IR. However, we are not yet there. Currently, we generate [QBE](https://c9x.me/compile/) IR manually.
The main goal of this project is to demonstrate how a compiler frontend works with [the LLVM compiler infrastructure](https://llvm.org/) and generates LLVM IR. Currently, this compiler can generate [QBE](https://c9x.me/compile/) IR and [LLVM](https://llvm.org/docs/LangRef.html) IR.

We are not aiming to be a fully compliant C compiler, although we strive to be as compliant as possible with C89 and support common C99 features.

## Prerequisites

- A C++ compiler that supports C++17.
- [GNU Make](https://www.gnu.org/software/make/): for building the project.
- [QBE](https://c9x.me/compile/releases.html): for compiling the QBE IR down to assembly.
- [QBE](https://c9x.me/compile/releases.html): for compiling QBE IR to assembly.
- [LLVM-18](https://releases.llvm.org/): for generating LLVM IR and compiling it to assembly.
- [cxxopts](https://github.com/jarro2783/cxxopts): for command-line argument parsing.
- [fmt](https://fmt.dev/latest/index.html): for modern C++ formatting.
- (test-only) [turnt](https://github.com/cucapra/turnt): for snapshot testing.
Expand Down Expand Up @@ -49,10 +50,10 @@ A simple C compiler.
Usage:
./vitaminc [options] file

-o, --output <file> Write output to <file> (default: a.out)
-d, --dump Dump the abstract syntax tree
-t, --target [qbe] Specify target IR (default: qbe)
-h, --help Display available options
-o, --output <file> Write output to <file> (default: a.out)
-d, --dump Dump the abstract syntax tree
-t, --target [qbe|llvm] Specify target IR (default: qbe)
-h, --help Display available options
```

## License
Expand Down
60 changes: 60 additions & 0 deletions include/llvm/util.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#ifndef LLVM_UTIL_HPP_
#define LLVM_UTIL_HPP_

#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Type.h>

#include <string>

#include "type.hpp"

namespace util {

/// @brief Check if a basic block has a terminator instruction.
/// @return `true` if terminator instruction is found, `false` otherwise.
bool HasTerminator(llvm::BasicBlock* bb);

/// @brief A collection of wrappers of LLVM types and functions.
class LLVMIRBuilderHelper {
public:
/// @brief Every LLVM basic block can only have one terminator instruction.
/// This function can check if there are terminator instructions before the
/// current insert point. If no, then it will create an unconditional branch
/// to the next basic block. If yes, then it will not create branch
/// instruction.
void CreateBrIfNoBrBefore(llvm::BasicBlock* next_bb);

/// @brief Create a branch instruction to the next basic block.
void CurrBBFallThroughNextBB(llvm::BasicBlock* curr_bb,
llvm::BasicBlock* next_bb);

/// @brief Find the basic block with the same name as `id` within the current
/// function.
/// @param id The name of the target basic block.
/// @return A pointer to basic block if found. `nullptr` if not found.
llvm::BasicBlock* FindBBWithNameOf(const std::string& id);

/// @brief Get the current function.
/// @return A pointer to the current function.
llvm::Function* CurrFunc();

/// @brief Get the corresponding LLVM type from our type.
/// @note For Function Pointers, even though it is a pointer type, we return
/// `FunctionType` instead of `PointerType` because `FunctionType` is needed
/// for creating LLVM IR function call.
/// @throw `std::runtime_error` if the `type` is not unknown.
llvm::Type* GetLLVMType(const Type& type);

LLVMIRBuilderHelper(llvm::IRBuilder<>& builder) : builder_{builder} {}

private:
/// @brief Stores a reference from the original builder.
llvm::IRBuilder<>&
builder_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
leewei05 marked this conversation as resolved.
Show resolved Hide resolved
};

} // namespace util

#endif // LLVM_UTIL_HPP_
87 changes: 87 additions & 0 deletions include/llvm_ir_generator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#ifndef LLVM_IR_GENERATOR_HPP_
#define LLVM_IR_GENERATOR_HPP_

#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Type.h>
#include <llvm/Support/raw_os_ostream.h>

#include <ostream>
#include <string>

#include "ast.hpp"
#include "llvm/util.hpp"
#include "visitor.hpp"

class LLVMIRGenerator : public NonModifyingVisitor {
public:
void Visit(const DeclStmtNode&) override;
void Visit(const LoopInitNode&) override;
void Visit(const VarDeclNode&) override;
void Visit(const ArrDeclNode&) override;
void Visit(const RecordDeclNode&) override;
void Visit(const FieldNode&) override;
void Visit(const RecordVarDeclNode&) override;
void Visit(const ParamNode&) override;
void Visit(const FuncDefNode&) override;
void Visit(const CompoundStmtNode&) override;
void Visit(const TransUnitNode&) override;
void Visit(const ExternDeclNode&) override;
void Visit(const IfStmtNode&) override;
void Visit(const WhileStmtNode&) override;
void Visit(const ForStmtNode&) override;
void Visit(const ReturnStmtNode&) override;
void Visit(const GotoStmtNode&) override;
void Visit(const BreakStmtNode&) override;
void Visit(const ContinueStmtNode&) override;
void Visit(const SwitchStmtNode&) override;
void Visit(const IdLabeledStmtNode&) override;
void Visit(const CaseStmtNode&) override;
void Visit(const DefaultStmtNode&) override;
void Visit(const ExprStmtNode&) override;
void Visit(const InitExprNode&) override;
void Visit(const ArrDesNode&) override;
void Visit(const IdDesNode&) override;
void Visit(const NullExprNode&) override;
void Visit(const IdExprNode&) override;
void Visit(const IntConstExprNode&) override;
void Visit(const ArgExprNode&) override;
void Visit(const ArrSubExprNode&) override;
void Visit(const CondExprNode&) override;
void Visit(const FuncCallExprNode&) override;
void Visit(const RecordMemExprNode&) override;
void Visit(const PostfixArithExprNode&) override;
void Visit(const UnaryExprNode&) override;
void Visit(const BinaryExprNode&) override;
void Visit(const SimpleAssignmentExprNode&) override;

LLVMIRGenerator(std::ostream& output, const std::string& filename)
: output_{output},
context_{},
builder_{llvm::IRBuilder<>(context_)},
module_{llvm::Module(filename, context_)},
builder_helper_{util::LLVMIRBuilderHelper(builder_)} {}

/// @brief Print LLVM IR to output.
void PrintIR() {
module_.print(output_, nullptr);
}

private:
/// @brief A LLVM ostream wrapper for writing to output.
llvm::raw_os_ostream output_;
/// @brief A LLVM object that includes core LLVM infrastructure.
llvm::LLVMContext context_;
/// @brief Provides LLVM Builder API for constructing IR. By default, Constant
/// folding is enabled and we have more flexibility for inserting
/// instructions.
llvm::IRBuilder<> builder_;
/// @brief Stores global variables, function lists, and the constructed IR.
llvm::Module module_;
/// @brief Wrapping IR builder to provide handy LLVM types and functions for
/// IR generation.
util::LLVMIRBuilderHelper builder_helper_;
};

#endif // LLVM_IR_GENERATOR_HPP_
16 changes: 16 additions & 0 deletions include/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,20 @@ class RecordType : public Type {
/// @return The type id.
virtual std::string id() // NOLINT(readability-identifier-naming)
const noexcept = 0;
/// @return The fields of a record type.
virtual const std::vector<std::unique_ptr<Field>>&
fields() // NOLINT(readability-identifier-naming)
const noexcept = 0;
/// @brief Checks if `id` is a member of the record type.
virtual bool IsMember(const std::string& id) const noexcept = 0;
/// @return The type of a member in struct or union. The unknown type if the
/// `id` is not a member of the record type.
virtual std::unique_ptr<Type> MemberType(
const std::string& id) const noexcept = 0;
/// @note Every member in union shares the same index 0.
/// @return The index of a member in struct or union.
/// @throw `std::runtime_error` if the `id` is not a member of the record.
virtual std::size_t MemberIndex(const std::string& id) const = 0;
/// @note Every member in union shares the same offset 0.
/// @return The type offset in the record based on `id`.
/// @throw `std::runtime_error` if the `id` is not a member of the record.
Expand All @@ -214,10 +222,14 @@ class StructType : public RecordType {
StructType(std::string id, std::vector<std::unique_ptr<Field>> fields)
: id_{std::move(id)}, fields_{std::move(fields)} {}

const std::vector<std::unique_ptr<Field>>& fields() const noexcept override {
return fields_;
}
std::string id() const noexcept override;
bool IsMember(const std::string& id) const noexcept override;
std::unique_ptr<Type> MemberType(
const std::string& id) const noexcept override;
std::size_t MemberIndex(const std::string& id) const override;
std::size_t OffsetOf(const std::string& id) const override;
std::size_t OffsetOf(std::size_t index) const override;
std::size_t SlotCount() const noexcept override;
Expand All @@ -243,10 +255,14 @@ class UnionType : public RecordType {
UnionType(std::string id, std::vector<std::unique_ptr<Field>> fields)
: id_{std::move(id)}, fields_{std::move(fields)} {}

const std::vector<std::unique_ptr<Field>>& fields() const noexcept override {
return fields_;
}
std::string id() const noexcept override;
bool IsMember(const std::string& id) const noexcept override;
std::unique_ptr<Type> MemberType(
const std::string& id) const noexcept override;
std::size_t MemberIndex(const std::string& id) const override;
std::size_t OffsetOf(const std::string& id) const override;
std::size_t OffsetOf(std::size_t index) const override;
std::size_t SlotCount() const noexcept override;
Expand Down
Loading
Loading