Skip to content

Commit 8b8ee31

Browse files
authored
Merge pull request #26 from LangProc/dev
Merge last year dev changes to main
2 parents d66832b + 12198c4 commit 8b8ee31

35 files changed

+339
-427
lines changed

.devcontainer/devcontainer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"ms-vscode.makefile-tools",
1818
"ms-python.python",
1919
"daohong-emilio.yash",
20-
"EditorConfig.EditorConfig"
20+
"EditorConfig.EditorConfig",
21+
"llvm-vs-code-extensions.vscode-clangd"
2122
]
2223
}
2324
}

Dockerfile

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ RUN apt-get update && apt-get install -y --fix-missing \
2020
curl \
2121
device-tree-compiler \
2222
lcov \
23-
nano
23+
nano \
24+
valgrind \
25+
clang \
26+
bear
2427

2528
# Install RISC-V Toolchain
2629
WORKDIR /tmp

Makefile

+3-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
CXXFLAGS := -std=c++20 # use the 2020 version of the C++ standard
44
CXXFLAGS += -g # generate debugging information
5-
CXXFLAGS += -Wall # enable most warnings, except those about ...
6-
CXXFLAGS += -Wno-unused-parameter # ... unused function parameters, ...
7-
CXXFLAGS += -Wno-unused-variable # ... unused variables, ...
8-
CXXFLAGS += -Wno-unused-function # ... or unused functions.
5+
CXXFLAGS += -Wall # enable most warnings
6+
CXXFLAGS += -Wextra # enable extra warnings
7+
CXXFLAGS += -Werror # treat all warnings as errors
98
CXXFLAGS += -fsanitize=address # enable address sanitization
109
CXXFLAGS += -static-libasan # statically link with Address Sanitizer
1110
CXXFLAGS += -O0 # perform minimal optimisations

debugging/README.md

+68-148
Large diffs are not rendered by default.

debugging/example-backtrace-3.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
#include <iostream>
33
#include <vector>
44

5-
const char *ARRAY_OF_NUMBERS[] = { "1", "2" , "99" };
5+
const char* ARRAY_OF_NUMBERS[] = { "1", "2" , "99" };
66

7-
static int process_arguments(int argc, const char *argv[])
7+
static int process_arguments(int argc, const char* argv[])
88
{
99
std::vector<int> numbers(argc - 1);
1010
for (int i = 1 ; i < argc ; i++) {

docs/assembler_directives.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ In the [`scripts/test.py`](../scripts/test.py) script, when running testcases, t
1212

1313
The below picture offers a quick walk-through of a very simple program with detailed annotations describing the meaning behind the included directives. Some of them a crucial (e.g. section specifiers, labels, data emitting) while others not so much (e.g. file attributes, compiler identifier, symbol types) - you will get a feel for them during the development of the compiler. Most importantly, you only need to set the correct section and provide function directives as long as you deal with local variables. **In other words, you can postpone studying this document in details until you decide to deal with global variables.**
1414

15-
![Assembler directives](./assembler_directives.png)
15+
![Assembler directives](./assets/assembler_directives.png)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

docs/basic_compiler.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ int f() {
1010

1111
The compiler is able to traverse the following AST related to the above program. In order to expand its capabilities, you should develop the parser and the corresponding code generation at the same time -- you are advised not to fully implement one before the other.
1212

13-
![int_main_return_tree](./int_main_return_5_tree.png)
13+
![int_main_return_tree](./assets/int_main_return_5_tree.png)
1414

1515

1616
The lexer and parser are loosely based on the "official" grammar covered [here](https://www.lysator.liu.se/c/ANSI-C-grammar-l.html) and [here](https://www.lysator.liu.se/c/ANSI-C-grammar-y.html) respectively. While they should suffice for a significant portions of features, you might need to improve them to implement the more advanced ones. If you find the grammar too complicated to understand, it is also perfectly fine to create your own simple grammar and build upon it as you add more features.

docs/c_compiler.md

+16-26
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
Main coursework: A compiler for the C language
2-
==============================================
1+
# Main coursework: A compiler for the C language
32

43
Your program should read C source code from a given file, and write corresponding RISC-V assembly to another given file.
54

6-
Environment
7-
-----------
5+
## Environment
86
[How to set up your environment?](./environment_guide.md)
97

10-
Developing your compiler
11-
------------------------
8+
## Developing your compiler
129

1310
If you wish to use C++, then a basic framework for building your compiler has been provided. You are strongly recommended to check out its structure [here](./basic_compiler.md).
1411

@@ -56,8 +53,7 @@ By default, the first [`_example/example.c`](../compiler_tests/_example/example.
5653

5754
This basic framework is only able to compile a very simple program, as described [here](./basic_compiler.md).
5855

59-
Program build and execution
60-
---------------------------
56+
## Program build and execution
6157

6258
Your program should be built by running the following command in the top-level directory of your repo:
6359

@@ -73,8 +69,7 @@ The compilation function is invoked using the flag `-S`, with the source file an
7369

7470
You can assume that the command-line (CLI) arguments will always be in this order, and that there will be no spaces in source or destination paths. Note that the provided starting point in this repository already functions as specified above, so these CLI arguments should work out of the box (unless you decide not to use the provided base compiler).
7571

76-
Input
77-
-----
72+
## Input
7873

7974
The input file will be pre-processed [ANSI C](https://en.wikipedia.org/wiki/ANSI_C), also called C90 or C89. It is what is generally thought of as "classic" or "normal" C, but not the _really_ old one without function prototypes (you may never have come across that). C90 is still often used in embedded systems, and pretty much the entire Linux kernel is in C90.
8075

@@ -84,8 +79,7 @@ The source code will not contain any compiler-specific or platform-specific exte
8479

8580
The test inputs will be a set of files of increasing complexity and variety. The test inputs will not have syntax errors or other programming errors, so your code does not need to handle these gracefully.
8681

87-
Features
88-
-------
82+
## Features
8983

9084
Here is a list of basic features that you might like to implement first.
9185

@@ -117,7 +111,7 @@ Here is a list of more advanced features like you might like to implement once t
117111
* calling externally-defined functions (i.e. the file being compiled declares a function, but its definition is provided in a different file that is linked in later on)
118112
* functions that take more than 8 parameters
119113
* mutually recursive function calls
120-
* locally scoped variable declarations (e.g. a variable that is declared inside the body of a while loop, such as `while(...) { int x = ...; ... }`.
114+
* locally scoped variable declarations (e.g. a variable that is declared inside the body of a while loop, such as `while(...) { int x = ...; ... }`).
121115
* the `typedef` keyword
122116
* the `sizeof(...)` function (which takes either a type or a variable)
123117
* taking the address of a variable using the `&` operator
@@ -145,19 +139,17 @@ Here is a (partial) list of features that will not be tested.
145139
* the `void` type is not tested explicitly, but it appears in some helper functions in the test cases, so your compiler cannot break when it encounters this keyword
146140
* the `static` keyword
147141

148-
Test cases
149-
----------
142+
## Test cases
150143

151144
All test inputs will be valid; that is, you can assume the absence of programmer errors like syntax faults, type mismatches, and array out-of-bounds errors. The entire compilation and testing process (including compilation, assembly, linking, and RISC-V simulation) is expected to complete within ten seconds per program (which should be plenty of time!), and is expected not to use an inordinate amount of memory or disk space. There is no requirement for the generated assembly to be optimised in any way -- the only requirement is that it produces the correct answer.
152145

153146
The [compiler_tests](../compiler_tests) contains a large number of example inputs, divided into various categories, that you might like to use as testcases. Your compiler will be assessed on these "seen" inputs together with a further set of "unseen" inputs that are of a similar form. It is worth emphasising that it is not expected that many compilers will correctly compile all of the "seen" inputs (let alone the "unseen" ones!). You are encouraged to focus on compiling the "basic" features (as listed above) first, before moving on to more advanced features if you have time.
154147

155148
The split between test cases last year can be seen below. Do not assume it will stay the same this year, but you can use it as a rough estimate of what to focus on in case you are running short on time. **Remember that tests for advanced features will also test basic features, so you should implement the basic features first (e.g. without working functions the array tests will fail).**
156149

157-
![Testcase distribution](./testcase_distribution.png)
150+
![Testcase distribution](./assets/testcase_distribution.png)
158151

159-
Output Format
160-
-------------
152+
## Output Format
161153

162154
The output format should be RISC-V assembly code.
163155

@@ -206,12 +198,10 @@ I then use spike to simulate the executable on RISC-V, like so:
206198

207199
This command should produce the exit code `0`.
208200

209-
Assembler directives
210-
---------------
201+
## Assembler directives
211202
[You will need to consider assembler directives in your output](./assembler_directives.md)
212203

213-
Useful links
214-
------------
204+
## Useful links
215205
* [Godbolt](https://godbolt.org/z/vMMnWbsff) - Great tool for viewing what a real (`gcc` in this case) RISC-V compiler would produce for a given snippet of C code. This link is pre-configured for the correct architecture (`RV32IMFD`) and ABI (`ILP32D`) that the coursework targets. Code optimisation is also disabled to best mimic what you might want your compiler to output. You can replicate Godbolt locally by running `riscv64-unknown-elf-gcc -std=c90 -pedantic -ansi -O0 -march=rv32imfd -mabi=ilp32d -S [source-file.c] -o [dest-file.s]`, which might make debugging and directives analysis easier for some.
216206

217207
* [Interactive RISC-V simulator](https://creatorsim.github.io/creator) - Might be helpful when trying to work out the behaviour of certain instructions that Godbolt emits.
@@ -222,10 +212,10 @@ Useful links
222212

223213
* [RISC-V Assembler Reference](https://michaeljclark.github.io/asm.html) - Very useful resource containing information about structuring your output assembly files and most importantly the assembler directives - if you don't know the meaning behind `.data`, `.text`, or `.word` then definitely check this out as well as experiment with Godbolt to see how it actually emits them.
224214

225-
Getting started
226-
---------------
215+
## Getting started
227216
[How to get started? (previous students' perspectives)](./starting_guide.md)
228217

229-
Coverage information
230-
-----------
218+
## Coverage information
231219
[Do you want to know which part of your code is executed when running your compiler on a file?](./coverage.md)
220+
221+

docs/coverage.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
Coverage information
2-
====================
1+
# Coverage information
32

43
If you want to know which part of your code is executed when running your compiler on a file you can run your compiler on the file, then run `make coverage`.
54

65
This will generate a webpage `coverage/index.html` with a listing of all the source files and for each source file a listing of the number of times each line has been executed.
76

8-
![Index.html screenshot](./coverage_example.png)
7+
![Index.html screenshot](./assets/coverage_example.png)
98

109
It can also be used automatically on all test files by running: `./scripts/test.py --coverage` or using the old test script: `COVERAGE=1 ./test.sh`.
1110

include/ast.hpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
#ifndef AST_HPP
2-
#define AST_HPP
1+
#pragma once
32

43
#include <iostream>
54
#include <string>
@@ -14,6 +13,4 @@
1413
#include "ast_constant.hpp"
1514
#include "ast_context.hpp"
1615

17-
extern Node *ParseAST(std::string file_name);
18-
19-
#endif
16+
ast::NodePtr ParseAST(std::string file_name);

include/ast_constant.hpp

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
#ifndef AST_CONSTANT_HPP
2-
#define AST_CONSTANT_HPP
1+
#pragma once
32

43
#include "ast_node.hpp"
54

5+
namespace ast {
6+
67
class IntConstant : public Node
78
{
89
private:
@@ -11,8 +12,8 @@ class IntConstant : public Node
1112
public:
1213
IntConstant(int value) : value_(value) {}
1314

14-
void EmitRISC(std::ostream &stream, Context &context) const override;
15-
void Print(std::ostream &stream) const override;
15+
void EmitRISC(std::ostream& stream, Context& context) const override;
16+
void Print(std::ostream& stream) const override;
1617
};
1718

18-
#endif
19+
} // namespace ast

include/ast_context.hpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
#ifndef AST_CONTEXT_HPP
2-
#define AST_CONTEXT_HPP
1+
#pragma once
32

4-
// An object of class Context is passed between AST nodes during compilation.
3+
namespace ast {
4+
// An object of class Context is passed between ast nodes during compilation.
55
// This can be used to pass around information about what's currently being
66
// compiled (e.g. function scope and variable names).
77
class Context
88
{
99
/* TODO decide what goes inside here */
1010
};
1111

12-
#endif
12+
} // namespace ast

include/ast_direct_declarator.hpp

+9-11
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1-
#ifndef AST_DIRECT_DECLARATOR_HPP
2-
#define AST_DIRECT_DECLARATOR_HPP
1+
#pragma once
32

43
#include "ast_node.hpp"
54

5+
namespace ast {
6+
67
class DirectDeclarator : public Node
78
{
89
private:
9-
Node *identifier_;
10+
NodePtr identifier_;
1011

1112
public:
12-
DirectDeclarator(Node *identifier) : identifier_(identifier){};
13-
~DirectDeclarator()
14-
{
15-
delete identifier_;
16-
};
17-
void EmitRISC(std::ostream &stream, Context &context) const override;
18-
void Print(std::ostream &stream) const override;
13+
DirectDeclarator(NodePtr identifier) : identifier_(std::move(identifier)){};
14+
15+
void EmitRISC(std::ostream& stream, Context& context) const override;
16+
void Print(std::ostream& stream) const override;
1917
};
2018

21-
#endif
19+
} // namespace ast

include/ast_function_definition.hpp

+12-27
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,22 @@
1-
#ifndef AST_FUNCTION_DEFINITION_HPP
2-
#define AST_FUNCTION_DEFINITION_HPP
1+
#pragma once
32

43
#include "ast_node.hpp"
4+
#include "ast_type_specifier.hpp"
5+
6+
namespace ast {
57

68
class FunctionDefinition : public Node
79
{
8-
910
private:
10-
11-
Node *declaration_specifiers_;
12-
Node *declarator_;
13-
Node *compound_statement_;
11+
const TypeSpecifier declaration_specifiers_;
12+
NodePtr declarator_;
13+
NodePtr compound_statement_;
1414

1515
public:
16-
17-
FunctionDefinition
18-
(Node *declaration_specifiers,
19-
Node *declarator,
20-
Node *compound_statement) :
21-
declaration_specifiers_(declaration_specifiers),
22-
declarator_(declarator),
23-
compound_statement_(compound_statement){};
24-
25-
~FunctionDefinition()
26-
{
27-
delete declaration_specifiers_;
28-
delete declarator_;
29-
delete compound_statement_;
30-
};
31-
32-
void EmitRISC(std::ostream &stream, Context &context) const override;
33-
34-
void Print(std::ostream &stream) const override;
16+
FunctionDefinition(TypeSpecifier declaration_specifiers, NodePtr declarator, NodePtr compound_statement) : declaration_specifiers_(declaration_specifiers), declarator_(std::move(declarator)), compound_statement_(std::move(compound_statement)){};
17+
18+
void EmitRISC(std::ostream& stream, Context& context) const override;
19+
void Print(std::ostream& stream) const override;
3520
};
3621

37-
#endif
22+
} // namespace ast

include/ast_identifier.hpp

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
#ifndef AST_IDENTIFIER_HPP
2-
#define AST_IDENTIFIER_HPP
1+
#pragma once
32

43
#include "ast_node.hpp"
54

5+
namespace ast {
6+
67
class Identifier : public Node
78
{
89
private:
910
std::string identifier_;
1011

1112
public:
12-
Identifier(std::string identifier) : identifier_(identifier){};
13-
~Identifier(){};
14-
void EmitRISC(std::ostream &stream, Context &context) const override;
15-
void Print(std::ostream &stream) const override;
13+
Identifier(std::string identifier) : identifier_(std::move(identifier)){};
14+
15+
void EmitRISC(std::ostream& stream, Context& context) const override;
16+
void Print(std::ostream& stream) const override;
1617
};
1718

18-
#endif
19+
} // namespace ast

include/ast_jump_statement.hpp

+8-11
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
1-
#ifndef AST_JUMP_STATEMENT_HPP
2-
#define AST_JUMP_STATEMENT_HPP
1+
#pragma once
32

43
#include "ast_node.hpp"
54

5+
namespace ast {
6+
67
class ReturnStatement : public Node
78
{
89
private:
9-
Node *expression_;
10+
NodePtr expression_;
1011

1112
public:
12-
ReturnStatement(Node *expression) : expression_(expression) {}
13-
~ReturnStatement()
14-
{
15-
delete expression_;
16-
};
13+
ReturnStatement(NodePtr expression) : expression_(std::move(expression)) {}
1714

18-
void EmitRISC(std::ostream &stream, Context &context) const override;
19-
void Print(std::ostream &stream) const override;
15+
void EmitRISC(std::ostream& stream, Context& context) const override;
16+
void Print(std::ostream& stream) const override;
2017
};
2118

22-
#endif
19+
} // namespace ast

0 commit comments

Comments
 (0)