Commit a71231e6 authored by Gustav Hansson's avatar Gustav Hansson

home exam Gustav Hansson

parent a59c0278
Pipeline #176 canceled with stages
File added
......@@ -4,66 +4,200 @@
## Your repo
- Link to your repo here:
- Link to your repo here: https://github.com/97gushan/D7050E
## Your syntax
- Give an as complete as possible EBNF grammar for your language
### EBNF
The EBNF grammar used can be seen in EBNF.pdf.
- Give an example that showcases all rules of your EBNF. The program should "do" something as used in the next excercise.
### Example
```rust
fn foo() -> i32{
let a: i32 = 1;
- If you support pointers, make sure your example covers pointers as well.
while(a < 10){
a = a + 1;
}
- Compare your solution to the requirements (as stated in the README.md). What are your contributions to the implementation.
if(a >= 10 || 9 == 20 - 5 && false){
return a;
}else{
return a + 5 * (2 - 5);
}
}
fn main() -> i32 {
let a: i32 = foo();
return foo();
}
```
### Pointers
No support for pointers
### Comparison
The requirement of the gramar stated that for a minimal subset of Rust the following needed to be implemented:
- Function definitions
- Commands (let, assignment, if then (else), while)
- Expressions (includig function calls)
- Primitive types (boolean, i32) and their literals
- Explicit types everywhere
- Explicit return(s)
All of these have been completed and works as intended.
To help begin the implementation I used the Lalrpop tutorial on how to implement an AST: (https://lalrpop.github.io/lalrpop/tutorial/006_building_asts.html).
Everythin else was designed and implemented by myself.
The parser is implemented with Lalrpop. It supports parantesized sub expressions with operator precedence. Error recovery and Spans are not implemented.
## Your semantics
- Give an as complete as possible Structural Operetional Semantics (SOS) for your language
### SOS
The Structural Operational Semantic of this compiler can be seen in SOS.pdf.
### Interpretation
The execution of the foo-function will use the following steps:
- Store the value '1' in variable 'a' (SOS rule 10)
- Run a while-loop while the condition 'a is less then 10' is true (SOS rule 16)
- - Assign the value 'a+1' to 'a'
- When the condition is false continue to next state (SOS rule 15)
- If the condition 'a greater or equal to 10 OR 9 equals 20 AND false' (SOS rules, 4,5,6,7,8, 13)
- - return the value 'a' (SOS rule 9)
- Else (SOS rule 14)
- - return value of 'a + 5 * (2 - 5)' (SOS rule 1,2,3,9)
If executed this program will return '-5'.
- Explain (in text) what an interpretation of your example should produce, do that by dry running your given example step by step. Relate back to the SOS rules. You may skip repetions to avoid cluttering.
- Compare your solution to the requirements (as stated in the README.md). What are your contributions to the implementation.
### Comparison
The interpreter can run every program that the parser can parse. Thus it supports the whole minimal subset of Rust stated earlier. The whole implementation is done by me.
## Your type checker
- Give an as complete as possible set of Type Checking Rules for your language (those rules look very much like the SOS rules, but over types not values).
The Type Rules of this typechecker can be seen in TypeRules.pdf.
- Demonstrate each "type rule" by an example.
### Type Rules Example
- Compare your solution to the requirements (as stated in the README.md). What are your contributions to the implementation.
```
n: Number
b: Boolean
op: +,-,*,/
nop: ==,!=,<,>,<=,>=
bop: &&, ||
## Your borrrow checker
n op n => n
1 + 2 = 3
- Give a specification for well versus ill formed borrows. (What are the rules the borrow checker should check).
n nop n => b
1 == 1 = true
- Demonstrate the cases of ill formed borrows that your borrow checker is able to detect and reject.
b bop b => b
true && true = true
- Compare your solution to the requirements (as stated in the README.md). What are your contributions to the implementation.
n op b => TypeError
1 + false = TypeError
## Your LLVM backend
n nop b => TypeError
1 < true = TypeError
- Let your backend produces LLVM-IR for your example program.
n bop b => TypeError
1 && true = TypeError
```
- Describe where and why you introduced allocations and phi nodes.
- If you have added optimization passes and/or attributed your code for better optimization (using e.g., `noalias`).
### Comparison
The Type Checker works as intended and rejects ill-typed programs. It does not use spans or multiple errors. The implementation follows the interpreter heavily and is also implemented by me alone.
- Compare your solution to the requirements (as stated in the README.md). What are your contributions to the implementation.
## Overal course goals and learning outcomes.
## Your borrrow checker
Comment on the alignment of the concrete course goals (taken from the course description) to the theory presented, work You have done and knowledge You have gained. (I have put some comments in [...]).
Not implemented.
- Lexical analysis, syntax analysis, and translation into abstract syntax.
## Your LLVM backend
### LLVM-IR example
```
Typechecker passed, interpret program
; ModuleID = 'program'
source_filename = "program"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
define i32 @foo() {
entry:
%a = alloca i32
store i32 1, i32* %a
%a1 = load i32, i32* %a
%les = icmp slt i32 %a1, 10
br i1 %les, label %b1, label %cont
b1: ; preds = %b1, %entry
%a2 = load i32, i32* %a
%add = add i32 %a2, 1
store i32 %add, i32* %a
br i1 %les, label %b1, label %cont
cont: ; preds = %b1, %entry
%whiletmp = phi i32 [ 11, %b1 ], [ 10, %cont ]
%a3 = load i32, i32* %a
%geq = icmp sge i32 %a3, 10
%and = and i1 %geq, false
br i1 %and, label %b14, label %b2
b14: ; preds = %cont
%a6 = load i32, i32* %a
ret i32 %a6
br label %cont5
b2: ; preds = %cont
%a7 = load i32, i32* %a
%add8 = add i32 %a7, -15
ret i32 %add8
br label %cont5
cont5: ; preds = %b2, %b14
%iftmp = phi i32 [ 11, %b14 ], [ 10, %b2 ]
}
define i32 @main() {
entry:
%a = alloca i32
%foo = call i32 @foo()
store i32 %foo, i32* %a
%foo1 = call i32 @foo()
ret i32 %foo1
}
```
### Allocations and Phi Nodes
Allocations are introduced when a new variable is stored so that LLVM gets a pointer to where in memory that variable should be stored.
Phi nodes are introduced after conditional branches like if and if/else statements and while loops. These nodes are used to handle the SSA form that the LLVM-IR uses. This is so that a variable can be changed inside an if-statement.
### Optimization
No optimization were made.
### Comparison
The final work on the LLVM backend is heavily based on the example on WIP branch on Gitlab and the Inkwell example. I tried to do my own implementation from scratch but I got stuck on some segmentation faults. These were still a problem after I rewrote with the Gitlab example as a guide. I solved it in the end and could keep going forward. My final contribution to the LLVM backend was compilation of the different kinds of expression as well as conditional branches, while loops and function calls.
## Overal course goals and learning outcomes.
### Lexical Analysis and Syntax Analysis
After working with the parser I've got a good understanding of how a lexer and parser work through a text file. I've gotten an understanding on the tokens that makes up a file and how to construct it into something a computer can understand.
- Regular expressions and grammars, context-free languages and grammars, lexer and parser generators. [Nom is lexer/parser library (and replaces the need for a generator, while lalr-pop is a classical parser generator)]
### Grammars and Parser
In the beggining of this course I felt that Nom had a very steep learing curve thus I switched to Lalrpop. It was a bit to much to learn Rust, Nom and how to structure and build a good AST at the same time.
- Identifier handling and symbol table organization. Type-checking, logical inference systems. [SOS is a logical inference system]
Now however with the knowledge I have gathered during this course I'm confident that I could go back and rebuild the parser in Nom without to much hasle. I believe that I've gathered a good understanding on how language parsers work.
- Intermediate representations and transformations for different languages. [LLVM is a cross language compiler infrastructure]
### Type Checking and Logical Inference Systems
It felt like the lectures on Logical Inference Systems and other theoretical subjects were rushed a bit. It felt like I did not have time to process the lectures and thus it felt like they didn't give much. However when I sat down to make my own SOS it went quite well, some Googling and an insight in discrete math helped quite alot too.
- Code optimization and register allocation. Machine code generation for common architectures. [LLVM is a cross target compiler infrastructure, doing the "dirty work" of optimazation/register allocation leveraging the SSA form of the LLVM-IR]
### LLVM
It has been very hard but very giving to work with the LLVM backend. It has given a lot of insight in how LLVM works with its SSA form and use of Phi nodes.
Comment on additional things that you have experienced and learned throughout the course.
### Other Thoughts
Apart from the course goals the major thing that this course has given me is more practical coding experience. It has been a great way to work on a project, building it from scratch and developing it over the weeks. It has been a lot of work and looking back there are some things I would have changed in the design. But in the end this is a project I feel that I given my best within the relative short timeframe we've had to work on it.
\ No newline at end of file
File added
File added
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment