Skip to content

Commit

Permalink
Merge pull request #26 from LangProc/dev
Browse files Browse the repository at this point in the history
Merge last year dev changes to main
  • Loading branch information
simon-staal authored Jan 24, 2025
2 parents d66832b + 12198c4 commit 8b8ee31
Show file tree
Hide file tree
Showing 35 changed files with 339 additions and 427 deletions.
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"ms-vscode.makefile-tools",
"ms-python.python",
"daohong-emilio.yash",
"EditorConfig.EditorConfig"
"EditorConfig.EditorConfig",
"llvm-vs-code-extensions.vscode-clangd"
]
}
}
Expand Down
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ RUN apt-get update && apt-get install -y --fix-missing \
curl \
device-tree-compiler \
lcov \
nano
nano \
valgrind \
clang \
bear

# Install RISC-V Toolchain
WORKDIR /tmp
Expand Down
7 changes: 3 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

CXXFLAGS := -std=c++20 # use the 2020 version of the C++ standard
CXXFLAGS += -g # generate debugging information
CXXFLAGS += -Wall # enable most warnings, except those about ...
CXXFLAGS += -Wno-unused-parameter # ... unused function parameters, ...
CXXFLAGS += -Wno-unused-variable # ... unused variables, ...
CXXFLAGS += -Wno-unused-function # ... or unused functions.
CXXFLAGS += -Wall # enable most warnings
CXXFLAGS += -Wextra # enable extra warnings
CXXFLAGS += -Werror # treat all warnings as errors
CXXFLAGS += -fsanitize=address # enable address sanitization
CXXFLAGS += -static-libasan # statically link with Address Sanitizer
CXXFLAGS += -O0 # perform minimal optimisations
Expand Down
216 changes: 68 additions & 148 deletions debugging/README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions debugging/example-backtrace-3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
#include <iostream>
#include <vector>

const char *ARRAY_OF_NUMBERS[] = { "1", "2" , "99" };
const char* ARRAY_OF_NUMBERS[] = { "1", "2" , "99" };

static int process_arguments(int argc, const char *argv[])
static int process_arguments(int argc, const char* argv[])
{
std::vector<int> numbers(argc - 1);
for (int i = 1 ; i < argc ; i++) {
Expand Down
2 changes: 1 addition & 1 deletion docs/assembler_directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ In the [`scripts/test.py`](../scripts/test.py) script, when running testcases, t

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.**

![Assembler directives](./assembler_directives.png)
![Assembler directives](./assets/assembler_directives.png)
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
2 changes: 1 addition & 1 deletion docs/basic_compiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ int f() {

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.

![int_main_return_tree](./int_main_return_5_tree.png)
![int_main_return_tree](./assets/int_main_return_5_tree.png)


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.
Expand Down
42 changes: 16 additions & 26 deletions docs/c_compiler.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
Main coursework: A compiler for the C language
==============================================
# Main coursework: A compiler for the C language

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

Environment
-----------
## Environment
[How to set up your environment?](./environment_guide.md)

Developing your compiler
------------------------
## Developing your compiler

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).

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

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

Program build and execution
---------------------------
## Program build and execution

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

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

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).

Input
-----
## Input

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.

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

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.

Features
-------
## Features

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

Expand Down Expand Up @@ -117,7 +111,7 @@ Here is a list of more advanced features like you might like to implement once t
* 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)
* functions that take more than 8 parameters
* mutually recursive function calls
* locally scoped variable declarations (e.g. a variable that is declared inside the body of a while loop, such as `while(...) { int x = ...; ... }`.
* locally scoped variable declarations (e.g. a variable that is declared inside the body of a while loop, such as `while(...) { int x = ...; ... }`).
* the `typedef` keyword
* the `sizeof(...)` function (which takes either a type or a variable)
* taking the address of a variable using the `&` operator
Expand Down Expand Up @@ -145,19 +139,17 @@ Here is a (partial) list of features that will not be tested.
* 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
* the `static` keyword

Test cases
----------
## Test cases

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.

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.

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).**

![Testcase distribution](./testcase_distribution.png)
![Testcase distribution](./assets/testcase_distribution.png)

Output Format
-------------
## Output Format

The output format should be RISC-V assembly code.

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

This command should produce the exit code `0`.

Assembler directives
---------------
## Assembler directives
[You will need to consider assembler directives in your output](./assembler_directives.md)

Useful links
------------
## Useful links
* [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.

* [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.
Expand All @@ -222,10 +212,10 @@ Useful links

* [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.

Getting started
---------------
## Getting started
[How to get started? (previous students' perspectives)](./starting_guide.md)

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


5 changes: 2 additions & 3 deletions docs/coverage.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
Coverage information
====================
# Coverage information

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`.

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.

![Index.html screenshot](./coverage_example.png)
![Index.html screenshot](./assets/coverage_example.png)

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`.

Expand Down
7 changes: 2 additions & 5 deletions include/ast.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#ifndef AST_HPP
#define AST_HPP
#pragma once

#include <iostream>
#include <string>
Expand All @@ -14,6 +13,4 @@
#include "ast_constant.hpp"
#include "ast_context.hpp"

extern Node *ParseAST(std::string file_name);

#endif
ast::NodePtr ParseAST(std::string file_name);
11 changes: 6 additions & 5 deletions include/ast_constant.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#ifndef AST_CONSTANT_HPP
#define AST_CONSTANT_HPP
#pragma once

#include "ast_node.hpp"

namespace ast {

class IntConstant : public Node
{
private:
Expand All @@ -11,8 +12,8 @@ class IntConstant : public Node
public:
IntConstant(int value) : value_(value) {}

void EmitRISC(std::ostream &stream, Context &context) const override;
void Print(std::ostream &stream) const override;
void EmitRISC(std::ostream& stream, Context& context) const override;
void Print(std::ostream& stream) const override;
};

#endif
} // namespace ast
8 changes: 4 additions & 4 deletions include/ast_context.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#ifndef AST_CONTEXT_HPP
#define AST_CONTEXT_HPP
#pragma once

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

#endif
} // namespace ast
20 changes: 9 additions & 11 deletions include/ast_direct_declarator.hpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
#ifndef AST_DIRECT_DECLARATOR_HPP
#define AST_DIRECT_DECLARATOR_HPP
#pragma once

#include "ast_node.hpp"

namespace ast {

class DirectDeclarator : public Node
{
private:
Node *identifier_;
NodePtr identifier_;

public:
DirectDeclarator(Node *identifier) : identifier_(identifier){};
~DirectDeclarator()
{
delete identifier_;
};
void EmitRISC(std::ostream &stream, Context &context) const override;
void Print(std::ostream &stream) const override;
DirectDeclarator(NodePtr identifier) : identifier_(std::move(identifier)){};

void EmitRISC(std::ostream& stream, Context& context) const override;
void Print(std::ostream& stream) const override;
};

#endif
} // namespace ast
39 changes: 12 additions & 27 deletions include/ast_function_definition.hpp
Original file line number Diff line number Diff line change
@@ -1,37 +1,22 @@
#ifndef AST_FUNCTION_DEFINITION_HPP
#define AST_FUNCTION_DEFINITION_HPP
#pragma once

#include "ast_node.hpp"
#include "ast_type_specifier.hpp"

namespace ast {

class FunctionDefinition : public Node
{

private:

Node *declaration_specifiers_;
Node *declarator_;
Node *compound_statement_;
const TypeSpecifier declaration_specifiers_;
NodePtr declarator_;
NodePtr compound_statement_;

public:

FunctionDefinition
(Node *declaration_specifiers,
Node *declarator,
Node *compound_statement) :
declaration_specifiers_(declaration_specifiers),
declarator_(declarator),
compound_statement_(compound_statement){};

~FunctionDefinition()
{
delete declaration_specifiers_;
delete declarator_;
delete compound_statement_;
};

void EmitRISC(std::ostream &stream, Context &context) const override;

void Print(std::ostream &stream) const override;
FunctionDefinition(TypeSpecifier declaration_specifiers, NodePtr declarator, NodePtr compound_statement) : declaration_specifiers_(declaration_specifiers), declarator_(std::move(declarator)), compound_statement_(std::move(compound_statement)){};

void EmitRISC(std::ostream& stream, Context& context) const override;
void Print(std::ostream& stream) const override;
};

#endif
} // namespace ast
15 changes: 8 additions & 7 deletions include/ast_identifier.hpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
#ifndef AST_IDENTIFIER_HPP
#define AST_IDENTIFIER_HPP
#pragma once

#include "ast_node.hpp"

namespace ast {

class Identifier : public Node
{
private:
std::string identifier_;

public:
Identifier(std::string identifier) : identifier_(identifier){};
~Identifier(){};
void EmitRISC(std::ostream &stream, Context &context) const override;
void Print(std::ostream &stream) const override;
Identifier(std::string identifier) : identifier_(std::move(identifier)){};

void EmitRISC(std::ostream& stream, Context& context) const override;
void Print(std::ostream& stream) const override;
};

#endif
} // namespace ast
19 changes: 8 additions & 11 deletions include/ast_jump_statement.hpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
#ifndef AST_JUMP_STATEMENT_HPP
#define AST_JUMP_STATEMENT_HPP
#pragma once

#include "ast_node.hpp"

namespace ast {

class ReturnStatement : public Node
{
private:
Node *expression_;
NodePtr expression_;

public:
ReturnStatement(Node *expression) : expression_(expression) {}
~ReturnStatement()
{
delete expression_;
};
ReturnStatement(NodePtr expression) : expression_(std::move(expression)) {}

void EmitRISC(std::ostream &stream, Context &context) const override;
void Print(std::ostream &stream) const override;
void EmitRISC(std::ostream& stream, Context& context) const override;
void Print(std::ostream& stream) const override;
};

#endif
} // namespace ast
Loading

0 comments on commit 8b8ee31

Please sign in to comment.