mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-15 06:58:50 +00:00
Lots of improvements.
* Better script names and grammar fix. * Bump Rust * Enforce more lints. * Improve few struct definitions by removing box. * Many minor implementation improvements.
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
/target
|
target
|
||||||
/hw*.zip
|
*.zip
|
||||||
/final.zip
|
rustfmt.toml
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|||||||
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -33,9 +33,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.0.17"
|
version = "4.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06badb543e734a2d6568e19a40af66ed5364360b9226184926f89d229b4b4267"
|
checksum = "91b9970d7505127a162fdaa9b96428d28a479ba78c9ec7550a63a5d9863db682"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
@@ -48,9 +48,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.0.13"
|
version = "4.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c42f169caba89a7d512b5418b09864543eeb4d497416c917d7137863bd2076ad"
|
checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
@@ -178,9 +178,9 @@ checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "ordered-float"
|
||||||
version = "3.3.0"
|
version = "3.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f74e330193f90ec45e2b257fa3ef6df087784157ac1ad2c1e71c62837b03aa7"
|
checksum = "d84eb1409416d254e4a9c8fa56cc24701755025b458f0fcd8e59e1f5f40c23bf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ required-features = ["build-bin"]
|
|||||||
build-bin = ["clap"]
|
build-bin = ["clap"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.0.17", features = ["derive"], optional = true }
|
clap = { version = "4.0.22", features = ["derive"], optional = true }
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
lang-c = "0.14.0"
|
lang-c = "0.14.0"
|
||||||
itertools = "0.10.5"
|
itertools = "0.10.5"
|
||||||
tempfile = "3.3.0"
|
tempfile = "3.3.0"
|
||||||
ordered-float = "3.3.0"
|
ordered-float = "3.4.0"
|
||||||
hexf-parse = "0.2.1"
|
hexf-parse = "0.2.1"
|
||||||
wait-timeout = "0.2.0"
|
wait-timeout = "0.2.0"
|
||||||
peg = "0.8.1"
|
peg = "0.8.1"
|
||||||
|
|||||||
64
README.md
64
README.md
@@ -59,9 +59,11 @@ RUST_MIN_STACK=33554432 cargo nextest run test_examples_end_to_end # run irge
|
|||||||
|
|
||||||
## Fuzzing
|
## Fuzzing
|
||||||
|
|
||||||
We encourage you to do homework using the test-driven development approach (TDD). You randomly
|
We encourage you to do homework using the test-driven development (TDD)approach. You will
|
||||||
generate test input, and if it fails, then reduce it as much as possible and manually inspect the
|
randomly generate a test input, and if it fails,
|
||||||
reduced test input. For example:
|
reduce it as much as possible and
|
||||||
|
manually inspect the reduced test input.
|
||||||
|
For example:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Randomly generates test inputs and tests them
|
# Randomly generates test inputs and tests them
|
||||||
@@ -75,8 +77,7 @@ cat tests/test_reduced.c
|
|||||||
```
|
```
|
||||||
|
|
||||||
`<fuzz-option>` can be `--print` or `--irgen`. It shall be the one used in [Run](#run).
|
`<fuzz-option>` can be `--print` or `--irgen`. It shall be the one used in [Run](#run).
|
||||||
|
For more information, please refer to the [Fuzzer User's Manual](tests/README.md).
|
||||||
For more information on usage, please refer to the [Fuzzer User's Manual](tests/README.md).
|
|
||||||
|
|
||||||
### Install
|
### Install
|
||||||
|
|
||||||
@@ -95,18 +96,18 @@ python3 tests/fuzz.py --help # print options
|
|||||||
python3 tests/fuzz.py --print -n10 # test C AST printer for 10 times
|
python3 tests/fuzz.py --print -n10 # test C AST printer for 10 times
|
||||||
```
|
```
|
||||||
|
|
||||||
We use `csmith` to randomly generate C source codes. `csmith` will be automatically downloaded and
|
We use `csmith` to randomly generate C source codes.
|
||||||
built by the test script. For more information, we refer to the
|
`csmith` will be automatically downloaded and built by the test script.
|
||||||
[Csmith](https://embed.cs.utah.edu/csmith/) homepage.
|
For more information, we refer to the [Csmith](https://embed.cs.utah.edu/csmith/) homepage.
|
||||||
|
|
||||||
### Reduce
|
### Reduce
|
||||||
|
|
||||||
When the fuzzer finds a buggy input program for your compiler, it is highly likely that the input
|
When the fuzzer finds a buggy input program for your compiler,
|
||||||
program is too big to manually inspect. We use `creduce` that reduces the buggy input program as
|
the input program is likely too big to manually inspect.
|
||||||
much as possible.
|
We use `creduce` that reduces the buggy input program as much as possible.
|
||||||
|
|
||||||
Suppose `tests/test_polished.c` is the buggy input program. Then the following script reduces the
|
Suppose `tests/test_polished.c` is the buggy input program.
|
||||||
program to `tests/test_reduced.c`:
|
Then the following script reduces the program to `tests/test_reduced.c`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python3 tests/fuzz.py <fuzz-option> --reduce
|
python3 tests/fuzz.py <fuzz-option> --reduce
|
||||||
@@ -114,15 +115,17 @@ python3 tests/fuzz.py <fuzz-option> --reduce
|
|||||||
|
|
||||||
`<fuzz-option>` can be `--print` or `--irgen`. It shall be the one used in [Run](#run).
|
`<fuzz-option>` can be `--print` or `--irgen`. It shall be the one used in [Run](#run).
|
||||||
|
|
||||||
### How it reduces test case?
|
### How does it reduces the test case?
|
||||||
|
|
||||||
The script performs unguided test-case reduction using `creduce`: given a buggy program, it randomly
|
The script performs unguided test-case reduction using `creduce`: given a buggy program, it
|
||||||
reduces the program; check if the reduced program still fails on the test, and if so, replaces the
|
randomly reduces the program;
|
||||||
given program with the reduced one; repeat until you get a small enough buggy program. For more
|
check if the reduced program still fails on the test, and
|
||||||
information, we refer to the [Creduce](https://embed.cs.utah.edu/creduce/) homepage.
|
if so, replaces the given program with the reduced one;
|
||||||
|
repeat until you get a small enough buggy program.
|
||||||
|
For more information, we refer to the [Creduce](https://embed.cs.utah.edu/creduce/) homepage.
|
||||||
|
|
||||||
**[NOTICE]** The fuzzer supports Ubuntu 20.04 only. It may work for other platforms, but if it
|
**[NOTICE]** The fuzzer only supports Ubuntu 20.04.
|
||||||
doesn't, please run the fuzzer in Ubuntu 20.04.
|
It may work for other platforms, but if it doesn't, please run the fuzzer in Ubuntu 20.04.
|
||||||
|
|
||||||
## Running RISC-V Binaries
|
## Running RISC-V Binaries
|
||||||
|
|
||||||
@@ -156,19 +159,22 @@ echo $?
|
|||||||
|
|
||||||
### Debugging Assembly
|
### Debugging Assembly
|
||||||
|
|
||||||
You can use QEMU's debugging facilities to investigate the generated assembly works correctly.
|
You can use QEMU's debugging facilities to investigate whether the generated assembly works correctly.
|
||||||
|
|
||||||
Open two terminal windows. In one, compile the assembly with `-ggdb` option and starts up gdb server with 8888 port. (If the port 8888 is already in use, then try with different port like 8889, 8890, ...)
|
Open two terminal windows.
|
||||||
|
In one, compile the assembly with `-ggdb` option and start up a gdb server with 8888 port.
|
||||||
|
(If 8888 is already in use, then try with a different port like 8889, 8890, ...)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Link to an RISC-V executable with `-ggdb` option
|
# Link to an RISC-V executable with `-ggdb` option
|
||||||
riscv64-linux-gnu-gcc -ggdb -static hello.S -o hello
|
riscv64-linux-gnu-gcc -ggdb -static hello.S -o hello
|
||||||
|
|
||||||
# Emulate the executable and wait for a debugging connection from GDB
|
# Emulate the executable and wait for a debugging connection from GDB
|
||||||
qemu-riscv64-static -g 8888 hello
|
qemu-riscv64-static -g 8888 hello
|
||||||
```
|
```
|
||||||
|
|
||||||
In the second terminal, run `gdb-multiarch` and set some configurations. You should see something like this,
|
In the second terminal, run `gdb-multiarch` and set some configurations.
|
||||||
|
You should see something like this,
|
||||||
|
|
||||||
```
|
```
|
||||||
$ gdb-multiarch
|
$ gdb-multiarch
|
||||||
@@ -194,7 +200,7 @@ Remote debugging using localhost:8888
|
|||||||
warning: No executable has been specified and target does not support
|
warning: No executable has been specified and target does not support
|
||||||
determining executable automatically. Try using the "file" command.
|
determining executable automatically. Try using the "file" command.
|
||||||
0x0000000000010348 in ?? ()
|
0x0000000000010348 in ?? ()
|
||||||
(gdb) file hello
|
(gdb) file hello
|
||||||
A program is being debugged already.
|
A program is being debugged already.
|
||||||
Are you sure you want to change the file? (y or n) y
|
Are you sure you want to change the file? (y or n) y
|
||||||
Reading symbols from hello...
|
Reading symbols from hello...
|
||||||
@@ -205,10 +211,11 @@ Dump of assembler code for function main:
|
|||||||
0x000000000001044c <+6>: sd s0,96(sp)
|
0x000000000001044c <+6>: sd s0,96(sp)
|
||||||
0x000000000001044e <+8>: addi s0,sp,104
|
0x000000000001044e <+8>: addi s0,sp,104
|
||||||
End of assembler dump.
|
End of assembler dump.
|
||||||
(gdb)
|
(gdb)
|
||||||
```
|
```
|
||||||
|
|
||||||
Now you can debug the assembly using the GDB commands. For more information on GDB commands, see:
|
Now you can debug the assembly using the GDB commands.
|
||||||
|
For more information on GDB commands, see:
|
||||||
|
|
||||||
- Full guide: http://sourceware.org/gdb/current/onlinedocs/gdb/
|
- Full guide: http://sourceware.org/gdb/current/onlinedocs/gdb/
|
||||||
- Cheatsheet: https://cs.brown.edu/courses/cs033/docs/guides/gdb.pdf
|
- Cheatsheet: https://cs.brown.edu/courses/cs033/docs/guides/gdb.pdf
|
||||||
@@ -224,4 +231,5 @@ make run
|
|||||||
## Submission
|
## Submission
|
||||||
|
|
||||||
- Submit the corresponding files to [gg.kaist.ac.kr](https://gg.kaist.ac.kr).
|
- Submit the corresponding files to [gg.kaist.ac.kr](https://gg.kaist.ac.kr).
|
||||||
- Run `./scripts/make-submissions.sh` to generate `hw2.zip` to `final.zip`, which you should submit for homework 2 to final project.
|
- Run `./scripts/make-submissions.sh` to generate `irgen.zip` to `final.zip`,
|
||||||
|
which you should submit for homework 2 to the final project.
|
||||||
|
|||||||
@@ -212,8 +212,8 @@ fn compile_ir(
|
|||||||
assert_eq!(width, 32);
|
assert_eq!(width, 32);
|
||||||
assert!(is_signed);
|
assert!(is_signed);
|
||||||
|
|
||||||
// When obtain status from `gcc` executable process, status value is truncated to byte size.
|
// When obtaining status from `gcc` executable process, the status value is truncated to
|
||||||
// So, we also truncate result value to byte size before printing it.
|
// byte size. So, we also truncate the result value to byte size before printing it.
|
||||||
println!("[result] {:?}", value as u8);
|
println!("[result] {:?}", value as u8);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.64.0
|
1.65.0
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
rsync --exclude=".git" --delete --archive ./ ../kecc-public
|
|
||||||
@@ -8,4 +8,4 @@ cargo fmt --all -- --check # run `cargo fmt` to auto-correct format.
|
|||||||
cargo clippy
|
cargo clippy
|
||||||
|
|
||||||
# Run tests.
|
# Run tests.
|
||||||
RUST_MIN_STACK=33554432 cargo test --release test_examples_asmgen
|
RUST_MIN_STACK=33554432 cargo test --release test_examples_asmgen_small -- --nocapture
|
||||||
@@ -8,4 +8,4 @@ cargo fmt --all -- --check # run `cargo fmt` to auto-correct format.
|
|||||||
cargo clippy
|
cargo clippy
|
||||||
|
|
||||||
# Run tests.
|
# Run tests.
|
||||||
RUST_MIN_STACK=33554432 cargo test --release test_examples_asmgen_small
|
RUST_MIN_STACK=33554432 cargo test --release test_examples_asmgen -- --nocapture
|
||||||
@@ -8,4 +8,4 @@ cargo fmt --all -- --check # run `cargo fmt` to auto-correct format.
|
|||||||
cargo clippy
|
cargo clippy
|
||||||
|
|
||||||
# Run tests.
|
# Run tests.
|
||||||
RUST_MIN_STACK=33554432 cargo test --release test_examples_deadcode
|
RUST_MIN_STACK=33554432 cargo test --release test_examples_deadcode -- --nocapture
|
||||||
@@ -8,4 +8,4 @@ cargo fmt --all -- --check # run `cargo fmt` to auto-correct format.
|
|||||||
cargo clippy
|
cargo clippy
|
||||||
|
|
||||||
# Run tests.
|
# Run tests.
|
||||||
RUST_MIN_STACK=33554432 cargo test --release test_examples_gvn
|
RUST_MIN_STACK=33554432 cargo test --release test_examples_gvn -- --nocapture
|
||||||
@@ -8,4 +8,4 @@ cargo fmt --all -- --check # run `cargo fmt` to auto-correct format.
|
|||||||
cargo clippy
|
cargo clippy
|
||||||
|
|
||||||
# Run tests.
|
# Run tests.
|
||||||
RUST_MIN_STACK=33554432 cargo test --release test_examples_irgen_small
|
RUST_MIN_STACK=33554432 cargo test --release test_examples_irgen_small --nocapture
|
||||||
@@ -8,5 +8,5 @@ cargo fmt --all -- --check # run `cargo fmt` to auto-correct format.
|
|||||||
cargo clippy
|
cargo clippy
|
||||||
|
|
||||||
# Run tests.
|
# Run tests.
|
||||||
RUST_MIN_STACK=33554432 cargo test --release test_examples_irgen
|
RUST_MIN_STACK=33554432 cargo test --release test_examples_irgen -- --nocapture
|
||||||
RUST_MIN_STACK=33554432 python3 tests/fuzz.py --irgen -n80 --seed 22
|
RUST_MIN_STACK=33554432 python3 tests/fuzz.py --irgen -n80 --seed 22
|
||||||
@@ -8,4 +8,4 @@ cargo fmt --all -- --check # run `cargo fmt` to auto-correct format.
|
|||||||
cargo clippy
|
cargo clippy
|
||||||
|
|
||||||
# Run tests.
|
# Run tests.
|
||||||
RUST_MIN_STACK=33554432 cargo test --release test_examples_mem2reg
|
RUST_MIN_STACK=33554432 cargo test --release test_examples_mem2reg -- --nocapture
|
||||||
@@ -8,4 +8,4 @@ cargo fmt --all -- --check # run `cargo fmt` to auto-correct format.
|
|||||||
cargo clippy
|
cargo clippy
|
||||||
|
|
||||||
# Run tests.
|
# Run tests.
|
||||||
RUST_MIN_STACK=33554432 cargo test --release test_examples_simplify_cfg
|
RUST_MIN_STACK=33554432 cargo test --release test_examples_simplify_cfg -- --nocapture
|
||||||
@@ -8,5 +8,5 @@ cargo fmt --all -- --check # run `cargo fmt` to auto-correct format.
|
|||||||
cargo clippy
|
cargo clippy
|
||||||
|
|
||||||
# Run tests.
|
# Run tests.
|
||||||
RUST_MIN_STACK=33554432 cargo test --release test_examples_write_c
|
RUST_MIN_STACK=33554432 cargo test --release test_examples_write_c -- --nocapture
|
||||||
RUST_MIN_STACK=33554432 python3 tests/fuzz.py --print -n80 --seed 22
|
RUST_MIN_STACK=33554432 python3 tests/fuzz.py --print -n80 --seed 22
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
"""Makes public skeleton.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import itertools
|
|
||||||
import argparse
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
for root, dir, files in os.walk("src"):
|
|
||||||
for f in files:
|
|
||||||
(filename, ext) = os.path.splitext(f)
|
|
||||||
|
|
||||||
if ext == ".public":
|
|
||||||
os.rename(os.path.join(root, f), os.path.join(root, filename))
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Clears the previous submissions.
|
# Clears the previous submissions.
|
||||||
rm -rf hw2.zip hw3.zip hw4.zip hw5.zip hw6.zip hw7.zip
|
rm -rf irgen.zip simplify_cfg.zip mem2reg.zip gvn.zip deadcode.zip asmgen.zip final.zip
|
||||||
|
|
||||||
# Creates new submissions.
|
# Creates new submissions.
|
||||||
zip hw2.zip -j src/c/write_c.rs src/irgen/mod.rs
|
zip irgen.zip -j src/c/write_c.rs src/irgen/mod.rs
|
||||||
zip hw3.zip -j src/opt/opt_utils.rs src/opt/simplify_cfg.rs
|
zip simplify_cfg.zip -j src/opt/opt_utils.rs src/opt/simplify_cfg.rs
|
||||||
zip hw4.zip -j src/opt/opt_utils.rs src/opt/mem2reg.rs
|
zip mem2reg.zip -j src/opt/opt_utils.rs src/opt/mem2reg.rs
|
||||||
zip hw5.zip -j src/opt/opt_utils.rs src/opt/gvn.rs
|
zip gvn.zip -j src/opt/opt_utils.rs src/opt/gvn.rs
|
||||||
zip hw6.zip -j src/opt/opt_utils.rs src/opt/deadcode.rs
|
zip deadcode.zip -j src/opt/opt_utils.rs src/opt/deadcode.rs
|
||||||
zip hw7.zip -r src/c/write_c.rs src/irgen/mod.rs src/opt/opt_utils.rs src/asmgen/*.rs
|
zip asmgen.zip -r src/c/write_c.rs src/irgen/mod.rs src/opt/opt_utils.rs src/asmgen/*.rs
|
||||||
zip final.zip -r src/
|
zip final.zip -r src/
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
files="src/asmgen/mod.rs src/c/write_c.rs src/irgen/mod.rs src/opt/deadcode.rs src/opt/gvn.rs src/opt/mem2reg.rs src/opt/opt_utils.rs src/opt/simplify_cfg.rs"
|
|
||||||
|
|
||||||
for file in $files; do
|
|
||||||
mv $file $file.public
|
|
||||||
done
|
|
||||||
|
|
||||||
# deleted: src/asmgen/mod.rs.public
|
|
||||||
# deleted: src/c/write_c.rs.public
|
|
||||||
# deleted: src/irgen/mod.rs.public
|
|
||||||
# deleted: src/opt/deadcode.rs.public
|
|
||||||
# deleted: src/opt/gvn.rs.public
|
|
||||||
# deleted: src/opt/mem2reg.rs.public
|
|
||||||
# deleted: src/opt/opt_utils.rs.public
|
|
||||||
# deleted: src/opt/simplify_cfg.rs.public
|
|
||||||
@@ -6,9 +6,6 @@ use crate::write_base::*;
|
|||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct Todo {}
|
|
||||||
|
|
||||||
/// TODO
|
/// TODO
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Asm {
|
pub struct Asm {
|
||||||
@@ -30,8 +27,8 @@ pub struct Section<T> {
|
|||||||
pub body: T,
|
pub body: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An object file is made up of multiple sections, with each section corresponding to
|
/// An object file is made up of multiple sections, with each section corresponding to distinct
|
||||||
/// distinct types of executable code or data.
|
/// types of executable code or data.
|
||||||
///
|
///
|
||||||
/// For more details: <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#sections>
|
/// For more details: <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#sections>
|
||||||
impl<T> Section<T> {
|
impl<T> Section<T> {
|
||||||
@@ -78,10 +75,10 @@ impl Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The assembler implements a number of directives that control the assembly of instructions
|
/// The assembler implements several directives that control the assembly of instructions into an
|
||||||
/// into an object file.
|
/// object file.
|
||||||
///
|
///
|
||||||
/// For more details: <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#assembler-directives>
|
/// For more information: <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#assembler-directives>
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Directive {
|
pub enum Directive {
|
||||||
/// .align integer
|
/// .align integer
|
||||||
@@ -135,7 +132,7 @@ impl fmt::Display for Directive {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum SectionType {
|
pub enum SectionType {
|
||||||
Text,
|
Text,
|
||||||
Data,
|
Data,
|
||||||
@@ -158,7 +155,7 @@ impl fmt::Display for SectionType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum SymbolType {
|
pub enum SymbolType {
|
||||||
Function,
|
Function,
|
||||||
Object,
|
Object,
|
||||||
@@ -286,7 +283,7 @@ impl fmt::Display for Instruction {
|
|||||||
/// If the enum variant contains `bool`,
|
/// If the enum variant contains `bool`,
|
||||||
/// It means that different instructions exist
|
/// It means that different instructions exist
|
||||||
/// depending on whether the operand is signed or not.
|
/// depending on whether the operand is signed or not.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum RType {
|
pub enum RType {
|
||||||
Add(DataSize),
|
Add(DataSize),
|
||||||
Sub(DataSize),
|
Sub(DataSize),
|
||||||
@@ -621,7 +618,7 @@ impl fmt::Display for RType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum IType {
|
pub enum IType {
|
||||||
Load {
|
Load {
|
||||||
data_size: DataSize,
|
data_size: DataSize,
|
||||||
@@ -730,7 +727,7 @@ impl fmt::Display for IType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum SType {
|
pub enum SType {
|
||||||
Store(DataSize),
|
Store(DataSize),
|
||||||
}
|
}
|
||||||
@@ -768,7 +765,7 @@ impl fmt::Display for SType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum BType {
|
pub enum BType {
|
||||||
Beq,
|
Beq,
|
||||||
Bne,
|
Bne,
|
||||||
@@ -791,7 +788,7 @@ impl fmt::Display for BType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum UType {
|
pub enum UType {
|
||||||
Lui,
|
Lui,
|
||||||
}
|
}
|
||||||
@@ -804,11 +801,11 @@ impl fmt::Display for UType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The assembler implements a number of convenience psuedo-instructions that are formed from
|
/// The assembler implements several convenience psuedo-instructions that are formed from multiple
|
||||||
/// instructions in the base ISA, but have implicit arguments or in some case reversed arguments,
|
/// instructions in the base ISA, but have implicit arguments or reversed arguments that result in
|
||||||
/// that result in distinct semantics.
|
/// distinct semantics.
|
||||||
///
|
///
|
||||||
/// For more details:
|
/// For more information:
|
||||||
/// - <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#assembler-pseudo-instructions>
|
/// - <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#assembler-pseudo-instructions>
|
||||||
/// - <https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf> (110p)
|
/// - <https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf> (110p)
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@@ -935,10 +932,10 @@ impl fmt::Display for Immediate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The relocation function creates synthesize operand values that are resolved
|
/// The relocation function creates synthesize operand values that are resolved
|
||||||
/// at program link time and are used as immediate parameters to specific instructions.
|
/// at program link time and are used as immediate parameters for specific instructions.
|
||||||
///
|
///
|
||||||
/// For more details: <https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md>
|
/// For more details: <https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md>
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum RelocationFunction {
|
pub enum RelocationFunction {
|
||||||
/// %hi
|
/// %hi
|
||||||
Hi20,
|
Hi20,
|
||||||
@@ -1054,10 +1051,10 @@ impl fmt::Display for DataSize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add calling convention information (caller/callee-save registers)
|
|
||||||
/// ABI name for RISC-V integer and floating-point register
|
/// ABI name for RISC-V integer and floating-point register
|
||||||
///
|
///
|
||||||
/// For more details: <https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf> (109p)
|
/// For more details: <https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf> (109p)
|
||||||
|
// TODO: Add calling convention information (caller/callee-save registers)
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
||||||
pub enum Register {
|
pub enum Register {
|
||||||
Zero,
|
Zero,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ impl<T: WriteLine> WriteLine for Section<T> {
|
|||||||
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
|
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
|
||||||
for directive in &self.header {
|
for directive in &self.header {
|
||||||
write_indent(indent + INDENT, write)?;
|
write_indent(indent + INDENT, write)?;
|
||||||
writeln!(write, "{}", directive.write_string())?;
|
writeln!(write, "{}", directive)?;
|
||||||
}
|
}
|
||||||
self.body.write_line(indent, write)?;
|
self.body.write_line(indent, write)?;
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ impl WriteLine for Variable {
|
|||||||
writeln!(write, "{}:", self.label.0)?;
|
writeln!(write, "{}:", self.label.0)?;
|
||||||
for directive in &self.directives {
|
for directive in &self.directives {
|
||||||
write_indent(indent + INDENT, write)?;
|
write_indent(indent + INDENT, write)?;
|
||||||
writeln!(write, "{}", directive.write_string())?;
|
writeln!(write, "{}", directive)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -67,7 +67,7 @@ impl WriteLine for Block {
|
|||||||
|
|
||||||
for instruction in &self.instructions {
|
for instruction in &self.instructions {
|
||||||
write_indent(indent + INDENT, write)?;
|
write_indent(indent + INDENT, write)?;
|
||||||
writeln!(write, "{}", instruction.write_string())?;
|
writeln!(write, "{}", instruction)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::asm;
|
|||||||
use crate::ir;
|
use crate::ir;
|
||||||
use crate::Translate;
|
use crate::Translate;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct Asmgen {}
|
pub struct Asmgen {}
|
||||||
|
|
||||||
impl Translate<ir::TranslationUnit> for Asmgen {
|
impl Translate<ir::TranslationUnit> for Asmgen {
|
||||||
|
|||||||
@@ -495,11 +495,7 @@ impl IsEquiv for Enumerator {
|
|||||||
|
|
||||||
impl IsEquiv for TypeQualifier {
|
impl IsEquiv for TypeQualifier {
|
||||||
fn is_equiv(&self, other: &Self) -> bool {
|
fn is_equiv(&self, other: &Self) -> bool {
|
||||||
#[allow(clippy::match_like_matches_macro)]
|
matches!((self, other), (Self::Const, Self::Const))
|
||||||
match (self, other) {
|
|
||||||
(Self::Const, Self::Const) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// TODO(document)
|
/// TODO(document)
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct Parse {}
|
pub struct Parse {}
|
||||||
|
|
||||||
impl<P: AsRef<Path>> Translate<P> for Parse {
|
impl<P: AsRef<Path>> Translate<P> for Parse {
|
||||||
|
|||||||
344
src/ir/dtype.rs
344
src/ir/dtype.rs
@@ -116,17 +116,15 @@ pub enum Dtype {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BaseDtype {
|
impl BaseDtype {
|
||||||
/// Apply `StorageClassSpecifier` to `BaseDtype`
|
/// Apply `StorageClassSpecifier` to `BaseDtype`.
|
||||||
///
|
///
|
||||||
/// let's say declaration is `typedef int i32_t;`, if `self` represents `int`
|
/// Let's say declaration is `typedef int i32_t;`, if `self` represents `int` and
|
||||||
/// and `type_qualifier` represents `typedef`, `self` is transformed to
|
/// `type_qualifier` represents `typedef`, `self` is transformed to representing `typedef int`.
|
||||||
/// representing `typedef int` after function performs.
|
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `self` - Part that has been converted to 'BaseDtype' on the declaration
|
/// * `self` - Part that has been converted to 'BaseDtype' on the declaration.
|
||||||
/// * `storage_class` - storage class requiring apply to 'self' immediately
|
/// * `storage_class` - storage class requiring to apply to 'self' immediately.
|
||||||
///
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn apply_storage_class(
|
fn apply_storage_class(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -136,24 +134,23 @@ impl BaseDtype {
|
|||||||
ast::StorageClassSpecifier::Typedef => {
|
ast::StorageClassSpecifier::Typedef => {
|
||||||
// duplicate `typedef` is allowed
|
// duplicate `typedef` is allowed
|
||||||
self.is_typedef = true;
|
self.is_typedef = true;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => panic!("unsupported storage class"),
|
scs => Err(DtypeError::Misc {
|
||||||
|
message: format!("unsupported storage class specifier: {scs:#?}"),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply `TypeSpecifier` to `BaseDtype`
|
/// Apply `TypeSpecifier` to `BaseDtype`.
|
||||||
///
|
///
|
||||||
/// let's say declaration is `const int a;`, if `self` represents `int`
|
/// Let's say the declaration is `const int a;`, if `self` represents `int` and
|
||||||
/// and `type_specifier` represents `const`, `self` is transformed to
|
/// `type_specifier` represents `const`, `self` is transformed to representing `const int`.
|
||||||
/// representing `const int` after function performs.
|
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `self` - Part that has been converted to 'BaseDtype' on the declaration
|
/// * `self` - Part that has been converted to 'BaseDtype' on the declaration.
|
||||||
/// * `type_qualifier` - type qualifiers requiring apply to 'self' immediately
|
/// * `type_qualifier` - type qualifiers requiring to apply to 'self' immediately.
|
||||||
///
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn apply_type_specifier(
|
fn apply_type_specifier(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -206,17 +203,15 @@ impl BaseDtype {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply `Typequalifier` to `BaseDtype`
|
/// Apply `Typequalifier` to `BaseDtype`.
|
||||||
///
|
///
|
||||||
/// let's say declaration is `const int a;`, if `self` represents `int`
|
/// Let's say the declaration is `const int a;`, if `self` represents `int` and `type_qualifier`
|
||||||
/// and `type_qualifier` represents `const`, `self` is transformed to
|
/// represents `const`, `self` is transformed to representing `const int`.
|
||||||
/// representing `const int` after function performs.
|
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `self` - Part that has been converted to 'BaseDtype' on the declaration
|
/// * `self` - Part that has been converted to 'BaseDtype' on the declaration.
|
||||||
/// * `type_qualifier` - type qualifiers requiring apply to 'self' immediately
|
/// * `type_qualifier` - type qualifiers requiring to apply to 'self' immediately.
|
||||||
///
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn apply_type_qualifier(
|
fn apply_type_qualifier(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -227,9 +222,12 @@ impl BaseDtype {
|
|||||||
// duplicate `const` is allowed
|
// duplicate `const` is allowed
|
||||||
self.is_const = true;
|
self.is_const = true;
|
||||||
}
|
}
|
||||||
_ => panic!("type qualifier is unsupported except `const`"),
|
tq => {
|
||||||
|
return Err(DtypeError::Misc {
|
||||||
|
message: format!("unsupported typq qualifier: {tq:#?}"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +242,11 @@ impl BaseDtype {
|
|||||||
ast::SpecifierQualifier::TypeQualifier(type_qualifier) => {
|
ast::SpecifierQualifier::TypeQualifier(type_qualifier) => {
|
||||||
self.apply_type_qualifier(&type_qualifier.node)?
|
self.apply_type_qualifier(&type_qualifier.node)?
|
||||||
}
|
}
|
||||||
ast::SpecifierQualifier::Extension(_) => panic!("unsupported specifier qualifier"),
|
sq => {
|
||||||
|
return Err(DtypeError::Misc {
|
||||||
|
message: format!("unsupported specifier qualifier: {sq:#?}"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -264,25 +266,27 @@ impl BaseDtype {
|
|||||||
ast::DeclarationSpecifier::TypeQualifier(type_qualifier) => {
|
ast::DeclarationSpecifier::TypeQualifier(type_qualifier) => {
|
||||||
self.apply_type_qualifier(&type_qualifier.node)?
|
self.apply_type_qualifier(&type_qualifier.node)?
|
||||||
}
|
}
|
||||||
_ => panic!("is_unsupported"),
|
ds => {
|
||||||
|
return Err(DtypeError::Misc {
|
||||||
|
message: format!("unsupported declaration qualifier: {ds:#?}"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply `PointerQualifier` to `BaseDtype`
|
/// Apply `PointerQualifier` to `BaseDtype`.
|
||||||
///
|
///
|
||||||
/// let's say pointer declarator is `* const` of `const int * const a;`.
|
/// let's say pointer declarator is `* const` of `const int * const a;`. If `self` represents
|
||||||
/// If `self` represents nothing, and `pointer_qualifier` represents `const`
|
/// nothing, and `pointer_qualifier` represents `const` between the first and second asterisk,
|
||||||
/// between first and second asterisk, `self` is transformed to
|
/// `self` is transformed to representing `const`. This information is used later when
|
||||||
/// representing `const` after function performs. This information is used later
|
/// generating `Dtype`.
|
||||||
/// when generating `Dtype`.
|
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `self` - Part that has been converted to 'BaseDtype' on the pointer declarator
|
/// * `self` - Part that has been converted to 'BaseDtype' on the pointer declarator.
|
||||||
/// * `pointer_qualifier` - Pointer qualifiers requiring apply to 'BaseDtype' immediately
|
/// * `pointer_qualifier` - Pointer qualifiers required to apply to 'BaseDtype' immediately.
|
||||||
///
|
|
||||||
pub fn apply_pointer_qualifier(
|
pub fn apply_pointer_qualifier(
|
||||||
&mut self,
|
&mut self,
|
||||||
pointer_qualifier: &ast::PointerQualifier,
|
pointer_qualifier: &ast::PointerQualifier,
|
||||||
@@ -291,8 +295,10 @@ impl BaseDtype {
|
|||||||
ast::PointerQualifier::TypeQualifier(type_qualifier) => {
|
ast::PointerQualifier::TypeQualifier(type_qualifier) => {
|
||||||
self.apply_type_qualifier(&type_qualifier.node)?;
|
self.apply_type_qualifier(&type_qualifier.node)?;
|
||||||
}
|
}
|
||||||
ast::PointerQualifier::Extension(_) => {
|
pq => {
|
||||||
panic!("ast::PointerQualifier::Extension is unsupported")
|
return Err(DtypeError::Misc {
|
||||||
|
message: format!("unsupported pointer qualifier: {pq:#?}"),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,8 +335,8 @@ impl TryFrom<BaseDtype> for Dtype {
|
|||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// For declaration is `const unsigned int * p`, `specifiers` is `const unsigned int`,
|
/// For declaration is `const unsigned int * p`, `specifiers` is `const unsigned int`, and the
|
||||||
/// and the result is `Dtype::Int{ width: 4, is_signed: false, is_const: ture }`
|
/// result is `Dtype::Int { width: 4, is_signed: false, is_const: true }`.
|
||||||
fn try_from(spec: BaseDtype) -> Result<Self, DtypeError> {
|
fn try_from(spec: BaseDtype) -> Result<Self, DtypeError> {
|
||||||
assert!(
|
assert!(
|
||||||
!(spec.scalar.is_none()
|
!(spec.scalar.is_none()
|
||||||
@@ -398,7 +404,7 @@ impl TryFrom<BaseDtype> for Dtype {
|
|||||||
return Ok(dtype);
|
return Ok(dtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates `dtype` from scalar.
|
// Creates `dtype` from the scalar.
|
||||||
let mut dtype = if let Some(t) = spec.scalar {
|
let mut dtype = if let Some(t) = spec.scalar {
|
||||||
match t {
|
match t {
|
||||||
ast::TypeSpecifier::Void => Self::unit(),
|
ast::TypeSpecifier::Void => Self::unit(),
|
||||||
@@ -420,7 +426,7 @@ impl TryFrom<BaseDtype> for Dtype {
|
|||||||
ast::TypeSpecifier::Short => Self::SHORT,
|
ast::TypeSpecifier::Short => Self::SHORT,
|
||||||
ast::TypeSpecifier::Long => Self::LONG,
|
ast::TypeSpecifier::Long => Self::LONG,
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"Dtype::try_from::<BaseDtype>: {:?} is not a size modifiers",
|
"Dtype::try_from::<BaseDtype>: {:?} is not a size modifier",
|
||||||
spec.size_modifiers
|
spec.size_modifiers
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -470,14 +476,14 @@ impl TryFrom<BaseDtype> for Dtype {
|
|||||||
impl TryFrom<&ast::TypeName> for Dtype {
|
impl TryFrom<&ast::TypeName> for Dtype {
|
||||||
type Error = DtypeError;
|
type Error = DtypeError;
|
||||||
|
|
||||||
/// Derive a data type from typename.
|
/// Derive a data type from `type_name`.
|
||||||
fn try_from(type_name: &ast::TypeName) -> Result<Self, Self::Error> {
|
fn try_from(type_name: &ast::TypeName) -> Result<Self, Self::Error> {
|
||||||
let mut spec = BaseDtype::default();
|
let mut spec = BaseDtype::default();
|
||||||
BaseDtype::apply_specifier_qualifiers(&mut spec, &type_name.specifiers)?;
|
BaseDtype::apply_specifier_qualifiers(&mut spec, &type_name.specifiers)?;
|
||||||
let mut dtype = Self::try_from(spec)?;
|
let mut dtype = Self::try_from(spec)?;
|
||||||
|
|
||||||
if let Some(declarator) = &type_name.declarator {
|
if let Some(declarator) = &type_name.declarator {
|
||||||
dtype = dtype.with_ast_declarator(&declarator.node)?.deref().clone();
|
dtype = dtype.with_ast_declarator(&declarator.node)?.into_inner();
|
||||||
}
|
}
|
||||||
Ok(dtype)
|
Ok(dtype)
|
||||||
}
|
}
|
||||||
@@ -486,19 +492,20 @@ impl TryFrom<&ast::TypeName> for Dtype {
|
|||||||
impl TryFrom<&ast::ParameterDeclaration> for Dtype {
|
impl TryFrom<&ast::ParameterDeclaration> for Dtype {
|
||||||
type Error = DtypeError;
|
type Error = DtypeError;
|
||||||
|
|
||||||
/// Generate `Dtype` based on parameter declaration
|
/// Generate `Dtype` based on parameter declaration.
|
||||||
fn try_from(parameter_decl: &ast::ParameterDeclaration) -> Result<Self, Self::Error> {
|
fn try_from(parameter_decl: &ast::ParameterDeclaration) -> Result<Self, Self::Error> {
|
||||||
let mut spec = BaseDtype::default();
|
let mut spec = BaseDtype::default();
|
||||||
BaseDtype::apply_declaration_specifiers(&mut spec, ¶meter_decl.specifiers)?;
|
BaseDtype::apply_declaration_specifiers(&mut spec, ¶meter_decl.specifiers)?;
|
||||||
let mut dtype = Self::try_from(spec)?;
|
let mut dtype = Self::try_from(spec)?;
|
||||||
|
|
||||||
if let Some(declarator) = ¶meter_decl.declarator {
|
if let Some(declarator) = ¶meter_decl.declarator {
|
||||||
dtype = dtype.with_ast_declarator(&declarator.node)?.deref().clone();
|
dtype = dtype.with_ast_declarator(&declarator.node)?.into_inner();
|
||||||
|
|
||||||
// A function call with an array argument performs array-to-pointer conversion.
|
// A function call with an array argument performs array-to-pointer conversion. For this
|
||||||
// For this reason, when `declarator` is from function parameter declaration
|
// reason, when `declarator` is from function parameter declaration and `base_dtype` is
|
||||||
// and `base_dtype` is `Dtype::Array`, `base_dtype` is transformed to pointer type.
|
// `Dtype::Array`, `base_dtype` is transformed to pointer type.
|
||||||
// https://www.eskimo.com/~scs/cclass/notes/sx10f.html
|
//
|
||||||
|
// For more information: <https://www.eskimo.com/~scs/cclass/notes/sx10f.html>
|
||||||
if let Some(inner) = dtype.get_array_inner() {
|
if let Some(inner) = dtype.get_array_inner() {
|
||||||
dtype = Self::pointer(inner.clone());
|
dtype = Self::pointer(inner.clone());
|
||||||
}
|
}
|
||||||
@@ -540,7 +547,7 @@ impl Dtype {
|
|||||||
pub const SIZE_OF_DOUBLE: usize = 8;
|
pub const SIZE_OF_DOUBLE: usize = 8;
|
||||||
|
|
||||||
/// TODO(document)
|
/// TODO(document)
|
||||||
// signed option cannot be applied to boolean value
|
/// A boolean value cannot be signed.
|
||||||
pub const BOOL: Self = Self::Int {
|
pub const BOOL: Self = Self::Int {
|
||||||
width: 1,
|
width: 1,
|
||||||
is_signed: false,
|
is_signed: false,
|
||||||
@@ -607,17 +614,16 @@ impl Dtype {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// Suppose the C declaration is `int *a[2][3]`. Then `a`'s `ir::Dtype` should be `[2 x [3 x
|
/// Suppose the C declaration is `int *a[2][3]`. Then `a`'s `ir::Dtype` should be `[2 x [3 x
|
||||||
/// int*]]`. But in the AST, it is parsed as `Array(3, Array(2, Pointer(int)))`, reversing the
|
/// int*]]`. But in the AST, it is parsed as `Array(3, Array(2, Pointer(int)))`, reversing
|
||||||
/// order of `2` and `3`. In the recursive translation of declaration into Dtype, we need to
|
/// the order of `2` and `3`. In the recursive translation of a declaration into Dtype, we
|
||||||
/// insert `3` inside `[2 * int*]`.
|
/// need to insert `3` inside `[2 * int*]`.
|
||||||
pub fn array(base_dtype: Dtype, size: usize) -> Self {
|
pub fn array(base_dtype: Dtype, size: usize) -> Self {
|
||||||
match base_dtype {
|
match base_dtype {
|
||||||
Self::Array {
|
Self::Array {
|
||||||
inner,
|
inner,
|
||||||
size: old_size,
|
size: old_size,
|
||||||
} => {
|
} => {
|
||||||
let inner = inner.deref().clone();
|
let inner = Self::array(*inner, size);
|
||||||
let inner = Self::array(inner, size);
|
|
||||||
Self::Array {
|
Self::Array {
|
||||||
inner: Box::new(inner),
|
inner: Box::new(inner),
|
||||||
size: old_size,
|
size: old_size,
|
||||||
@@ -669,17 +675,17 @@ impl Dtype {
|
|||||||
|
|
||||||
let align_of = fields
|
let align_of = fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| f.deref().size_align_of(structs))
|
.map(|f| f.size_align_of(structs))
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|(_, a)| *a)
|
.map(|(_, a)| a)
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let mut offsets = Vec::new();
|
let mut offsets = Vec::new();
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for field in &fields {
|
for field in &fields {
|
||||||
let (size_of_dtype, align_of_dtype) = field.deref().size_align_of(structs)?;
|
let (size_of_dtype, align_of_dtype) = field.size_align_of(structs)?;
|
||||||
|
|
||||||
let pad = if (offset % align_of_dtype) != 0 {
|
let pad = if (offset % align_of_dtype) != 0 {
|
||||||
align_of_dtype - (offset % align_of_dtype)
|
align_of_dtype - (offset % align_of_dtype)
|
||||||
@@ -702,7 +708,9 @@ impl Dtype {
|
|||||||
size_align_offsets: Some((size_of, align_of, offsets)),
|
size_align_offsets: Some((size_of, align_of, offsets)),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
panic!("struct type is needed")
|
Err(DtypeError::Misc {
|
||||||
|
message: "struct type is needed".to_string(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -801,9 +809,7 @@ impl Dtype {
|
|||||||
pub fn is_scalar(&self) -> bool {
|
pub fn is_scalar(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Unit { .. } => todo!(),
|
Self::Unit { .. } => todo!(),
|
||||||
Self::Int { .. } => true,
|
Self::Int { .. } | Self::Float { .. } | Self::Pointer { .. } => true,
|
||||||
Self::Float { .. } => true,
|
|
||||||
Self::Pointer { .. } => true,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -812,20 +818,19 @@ impl Dtype {
|
|||||||
pub fn is_int_signed(&self) -> bool {
|
pub fn is_int_signed(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Int { is_signed, .. } => *is_signed,
|
Self::Int { is_signed, .. } => *is_signed,
|
||||||
_ => panic!("only `Dtype::Int` can be judged whether it is sigend"),
|
_ => panic!("only `Dtype::Int` can be judged whether it is signed"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_const(&self) -> bool {
|
pub fn is_const(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Unit { is_const } => *is_const,
|
Self::Unit { is_const }
|
||||||
Self::Int { is_const, .. } => *is_const,
|
| Self::Int { is_const, .. }
|
||||||
Self::Float { is_const, .. } => *is_const,
|
| Self::Float { is_const, .. }
|
||||||
Self::Pointer { is_const, .. } => *is_const,
|
| Self::Typedef { is_const, .. }
|
||||||
Self::Array { .. } => true,
|
| Self::Pointer { is_const, .. }
|
||||||
Self::Struct { is_const, .. } => *is_const,
|
| Self::Struct { is_const, .. } => *is_const,
|
||||||
Self::Function { .. } => true,
|
Self::Function { .. } | Self::Array { .. } => true,
|
||||||
Self::Typedef { is_const, .. } => *is_const,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,11 +838,11 @@ impl Dtype {
|
|||||||
/// Check if `Dtype` is constant. if it is constant, the variable of `Dtype` is not assignable.
|
/// Check if `Dtype` is constant. if it is constant, the variable of `Dtype` is not assignable.
|
||||||
pub fn is_immutable(&self, structs: &HashMap<String, Option<Dtype>>) -> bool {
|
pub fn is_immutable(&self, structs: &HashMap<String, Option<Dtype>>) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Unit { is_const } => *is_const,
|
Self::Unit { is_const }
|
||||||
Self::Int { is_const, .. } => *is_const,
|
| Self::Int { is_const, .. }
|
||||||
Self::Float { is_const, .. } => *is_const,
|
| Self::Float { is_const, .. }
|
||||||
Self::Pointer { is_const, .. } => *is_const,
|
| Self::Pointer { is_const, .. } => *is_const,
|
||||||
Self::Array { .. } => true,
|
Self::Array { .. } | Self::Function { .. } => true,
|
||||||
Self::Struct { name, is_const, .. } => {
|
Self::Struct { name, is_const, .. } => {
|
||||||
let name = name.as_ref().expect("`name` must be exist");
|
let name = name.as_ref().expect("`name` must be exist");
|
||||||
let struct_type = structs
|
let struct_type = structs
|
||||||
@@ -856,7 +861,7 @@ impl Dtype {
|
|||||||
|| fields
|
|| fields
|
||||||
.iter()
|
.iter()
|
||||||
.any(|f| {
|
.any(|f| {
|
||||||
// If an array is wrapped in a struct and the array's inner type is not
|
// If an array is wrapped in a struct and the array's inner type is not
|
||||||
// constant, it is assignable to another object of the same struct type.
|
// constant, it is assignable to another object of the same struct type.
|
||||||
if let Self::Array { inner, .. } = f.deref() {
|
if let Self::Array { inner, .. } = f.deref() {
|
||||||
inner.is_immutable_for_array_struct_field_inner(structs)
|
inner.is_immutable_for_array_struct_field_inner(structs)
|
||||||
@@ -865,7 +870,6 @@ impl Dtype {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Self::Function { .. } => true,
|
|
||||||
Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -943,14 +947,16 @@ impl Dtype {
|
|||||||
.expect("`struct_type` must have its definition");
|
.expect("`struct_type` must have its definition");
|
||||||
let (size_of, align_of, _) = struct_type
|
let (size_of, align_of, _) = struct_type
|
||||||
.get_struct_size_align_offsets()
|
.get_struct_size_align_offsets()
|
||||||
.expect("`struct_type` must be stcut type")
|
.expect("`struct_type` must be struct type")
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Ok((*size_of, *align_of))
|
Ok((*size_of, *align_of))
|
||||||
}
|
}
|
||||||
Self::Function { .. } => Ok((0, 1)),
|
Self::Function { .. } => Ok((0, 1)),
|
||||||
Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
Self::Typedef { .. } => Err(DtypeError::Misc {
|
||||||
|
message: "typedef should be replaced by real dtype".to_string(),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -978,10 +984,10 @@ impl Dtype {
|
|||||||
.expect("`offsets` must be `Some`");
|
.expect("`offsets` must be `Some`");
|
||||||
|
|
||||||
assert_eq!(fields.len(), offsets.len());
|
assert_eq!(fields.len(), offsets.len());
|
||||||
for (field, offset) in izip!(fields, offsets) {
|
for (field, &offset) in izip!(fields, offsets) {
|
||||||
if let Some(name) = field.name() {
|
if let Some(name) = field.name() {
|
||||||
if name == field_name {
|
if name == field_name {
|
||||||
return Some((*offset, field.deref().clone()));
|
return Some((offset, field.deref().clone()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let field_dtype = field.deref();
|
let field_dtype = field.deref();
|
||||||
@@ -989,7 +995,7 @@ impl Dtype {
|
|||||||
field_dtype.get_offset_struct_field(field_name, structs),
|
field_dtype.get_offset_struct_field(field_name, structs),
|
||||||
continue
|
continue
|
||||||
);
|
);
|
||||||
return Some((*offset + offset_inner, dtype));
|
return Some((offset + offset_inner, dtype));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,14 +1006,14 @@ impl Dtype {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn set_signed(self, is_signed: bool) -> Self {
|
pub fn set_signed(&self, is_signed: bool) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Int {
|
Self::Int {
|
||||||
width, is_const, ..
|
width, is_const, ..
|
||||||
} => Self::Int {
|
} => Self::Int {
|
||||||
width,
|
width: *width,
|
||||||
is_signed,
|
is_signed,
|
||||||
is_const,
|
is_const: *is_const,
|
||||||
},
|
},
|
||||||
_ => panic!("`signed` and `unsigned` only be applied to `Dtype::Int`"),
|
_ => panic!("`signed` and `unsigned` only be applied to `Dtype::Int`"),
|
||||||
}
|
}
|
||||||
@@ -1025,14 +1031,16 @@ impl Dtype {
|
|||||||
Ok((dtype, is_typedef))
|
Ok((dtype, is_typedef))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive a data type and its name from struct declaration
|
/// Derive a data type and its name from the struct declaration.
|
||||||
pub fn try_from_ast_struct_declaration(
|
pub fn try_from_ast_struct_declaration(
|
||||||
declaration: &ast::StructDeclaration,
|
declaration: &ast::StructDeclaration,
|
||||||
) -> Result<Vec<Named<Self>>, DtypeError> {
|
) -> Result<Vec<Named<Self>>, DtypeError> {
|
||||||
let field_decl = if let ast::StructDeclaration::Field(field_decl) = declaration {
|
let field_decl = if let ast::StructDeclaration::Field(field_decl) = declaration {
|
||||||
&field_decl.node
|
&field_decl.node
|
||||||
} else {
|
} else {
|
||||||
panic!("ast::StructDeclaration::StaticAssert is unsupported")
|
return Err(DtypeError::Misc {
|
||||||
|
message: "ast::StructDeclaration::StaticAssert is unsupported".to_string(),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut spec = BaseDtype::default();
|
let mut spec = BaseDtype::default();
|
||||||
@@ -1050,9 +1058,11 @@ impl Dtype {
|
|||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
if fields.is_empty() {
|
if fields.is_empty() {
|
||||||
// If anonymous field is `Dtype::Struct`, structure type of this field
|
// If an anonymous field is `Dtype::Struct`, the structure type of this field can use
|
||||||
// can use this field's field as its field.
|
// this field's field as its field.
|
||||||
// For exampe, let's `struct A { struct { int f; }} t;`, `t.f` is valid.
|
//
|
||||||
|
// For example, let's `struct A { struct {
|
||||||
|
// int f; }} t;`, `t.f` is valid.
|
||||||
if let Self::Struct { name, .. } = &dtype {
|
if let Self::Struct { name, .. } = &dtype {
|
||||||
if name.is_none() {
|
if name.is_none() {
|
||||||
// Note that `const` qualifier has no effect in this time.
|
// Note that `const` qualifier has no effect in this time.
|
||||||
@@ -1068,18 +1078,15 @@ impl Dtype {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate `Dtype` based on declarator and `self` which has scalar type.
|
/// Generate `Dtype` based on declarator and `self` which has a scalar type.
|
||||||
///
|
///
|
||||||
/// let's say declaration is `const int * const * const a;`.
|
/// Let's say declaration is `const int * const * const a;`. In general `self` start with `const
|
||||||
/// In general `self` start with `const int` which has scalar type and
|
/// int` which has a scalar type and `declarator` represents `* const * const` with
|
||||||
/// `declarator` represents `* const * const` with `ast::Declarator`
|
/// `ast::Declarator`.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `declarator` - Parts requiring conversion to 'Dtype' on the declaration
|
/// * `declarator` - Parts requiring conversion to 'Dtype' on the declaration.
|
||||||
/// * `decl_spec` - Containing information that should be referenced
|
|
||||||
/// when creating `Dtype` from `Declarator`.
|
|
||||||
///
|
|
||||||
pub fn with_ast_declarator(
|
pub fn with_ast_declarator(
|
||||||
mut self,
|
mut self,
|
||||||
declarator: &ast::Declarator,
|
declarator: &ast::Declarator,
|
||||||
@@ -1107,7 +1114,7 @@ impl Dtype {
|
|||||||
|
|
||||||
// If function parameter is (void), remove it
|
// If function parameter is (void), remove it
|
||||||
if params.len() == 1 && params[0] == Dtype::unit() {
|
if params.len() == 1 && params[0] == Dtype::unit() {
|
||||||
let _ = params.pop();
|
let _unused = params.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::function(self, params)
|
Self::function(self, params)
|
||||||
@@ -1133,20 +1140,22 @@ impl Dtype {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates `Dtype` based on declarator and `self` which has scalar type.
|
/// Generates `Dtype` based on declarator and `self` which has a scalar type.
|
||||||
///
|
///
|
||||||
/// Let's say the AST declaration is `int a[2][3]`; `self` represents `int [2]`; and
|
/// Let's say the AST declaration is `int a[2][3]`; `self` represents `int [2]`; and
|
||||||
/// `array_size` is `[3]`. Then this function should return `int [2][3]`.
|
/// `array_size` is `[3]`. Then this function should return `int [2][3]`.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `array_size` - the array size to add to the dtype `self`
|
/// * `array_size` - the array size to add to `self`.
|
||||||
///
|
|
||||||
pub fn with_ast_array_size(self, array_size: &ast::ArraySize) -> Result<Self, DtypeError> {
|
pub fn with_ast_array_size(self, array_size: &ast::ArraySize) -> Result<Self, DtypeError> {
|
||||||
let expr = if let ast::ArraySize::VariableExpression(expr) = array_size {
|
let expr = if let ast::ArraySize::VariableExpression(expr) = array_size {
|
||||||
&expr.node
|
&expr.node
|
||||||
} else {
|
} else {
|
||||||
panic!("`ArraySize` is unsupported except `ArraySize::VariableExpression`")
|
return Err(DtypeError::Misc {
|
||||||
|
message: "`ArraySize` is unsupported except `ArraySize::VariableExpression`"
|
||||||
|
.to_string(),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let constant = Constant::try_from(expr)
|
let constant = Constant::try_from(expr)
|
||||||
@@ -1165,22 +1174,18 @@ impl Dtype {
|
|||||||
Ok(Self::array(self, value as usize))
|
Ok(Self::array(self, value as usize))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_typedefs(
|
pub fn resolve_typedefs(self, typedefs: &HashMap<String, Dtype>) -> Result<Self, DtypeError> {
|
||||||
self,
|
let dtype = match self {
|
||||||
typedefs: &HashMap<String, Dtype>,
|
|
||||||
structs: &HashMap<String, Option<Dtype>>,
|
|
||||||
) -> Result<Self, DtypeError> {
|
|
||||||
let dtype = match &self {
|
|
||||||
Self::Unit { .. } | Self::Int { .. } | Self::Float { .. } => self,
|
Self::Unit { .. } | Self::Int { .. } | Self::Float { .. } => self,
|
||||||
Self::Pointer { inner, is_const } => {
|
Self::Pointer { inner, is_const } => {
|
||||||
let inner = inner.deref().clone().resolve_typedefs(typedefs, structs)?;
|
let inner = inner.resolve_typedefs(typedefs)?;
|
||||||
Self::pointer(inner).set_const(*is_const)
|
Self::pointer(inner).set_const(is_const)
|
||||||
}
|
}
|
||||||
Self::Array { inner, size } => {
|
Self::Array { inner, size } => {
|
||||||
let inner = inner.deref().clone().resolve_typedefs(typedefs, structs)?;
|
let inner = inner.resolve_typedefs(typedefs)?;
|
||||||
Self::Array {
|
Self::Array {
|
||||||
inner: Box::new(inner),
|
inner: Box::new(inner),
|
||||||
size: *size,
|
size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Struct {
|
Self::Struct {
|
||||||
@@ -1189,40 +1194,39 @@ impl Dtype {
|
|||||||
is_const,
|
is_const,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(fields) = fields {
|
let (name, fields) = if let Some(fields) = fields {
|
||||||
let resolved_dtypes = fields
|
let fields = fields
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|f| f.deref().clone().resolve_typedefs(typedefs, structs))
|
.map(|f| {
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
let (d, name) = f.destruct();
|
||||||
|
let d = d.resolve_typedefs(typedefs).unwrap();
|
||||||
assert_eq!(fields.len(), resolved_dtypes.len());
|
Named::new(name, d)
|
||||||
let fields = izip!(fields, resolved_dtypes)
|
})
|
||||||
.map(|(f, d)| Named::new(f.name().cloned(), d))
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
(name, Some(fields))
|
||||||
Self::structure(name.clone(), Some(fields)).set_const(*is_const)
|
|
||||||
} else {
|
} else {
|
||||||
assert!(name.is_some());
|
assert!(name.is_some());
|
||||||
self
|
(name, fields)
|
||||||
}
|
};
|
||||||
|
Self::structure(name, fields).set_const(is_const)
|
||||||
}
|
}
|
||||||
Self::Function { ret, params } => {
|
Self::Function { ret, params } => {
|
||||||
let ret = ret.deref().clone().resolve_typedefs(typedefs, structs)?;
|
let ret = ret.resolve_typedefs(typedefs)?;
|
||||||
let params = params
|
let params = params
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|p| p.clone().resolve_typedefs(typedefs, structs))
|
.map(|p| p.resolve_typedefs(typedefs))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
Self::function(ret, params)
|
Self::function(ret, params)
|
||||||
}
|
}
|
||||||
Self::Typedef { name, is_const } => {
|
Self::Typedef { name, is_const } => {
|
||||||
let dtype = typedefs
|
let dtype = typedefs
|
||||||
.get(name)
|
.get(&name)
|
||||||
.ok_or_else(|| DtypeError::Misc {
|
.ok_or_else(|| DtypeError::Misc {
|
||||||
message: format!("unknown type name `{}`", name),
|
message: format!("unknown type name `{}`", name),
|
||||||
})?
|
})?
|
||||||
.clone();
|
.clone();
|
||||||
let is_const = dtype.is_const() || *is_const;
|
let is_const = dtype.is_const() || is_const;
|
||||||
|
|
||||||
dtype.set_const(is_const)
|
dtype.set_const(is_const)
|
||||||
}
|
}
|
||||||
@@ -1238,33 +1242,29 @@ impl Dtype {
|
|||||||
structs: &mut HashMap<String, Option<Dtype>>,
|
structs: &mut HashMap<String, Option<Dtype>>,
|
||||||
tempid_counter: &mut usize,
|
tempid_counter: &mut usize,
|
||||||
) -> Result<Self, DtypeError> {
|
) -> Result<Self, DtypeError> {
|
||||||
let dtype = match &self {
|
let dtype = match self {
|
||||||
Self::Unit { .. } | Self::Int { .. } | Self::Float { .. } => self,
|
Self::Unit { .. } | Self::Int { .. } | Self::Float { .. } => self,
|
||||||
Self::Pointer { inner, is_const } => {
|
Self::Pointer { inner, is_const } => {
|
||||||
let inner = inner.deref();
|
// Pointer types can have an undeclared struct type as inner.
|
||||||
|
//
|
||||||
// the pointer type can have undeclared struct type as inner.
|
// For example, consider `struct A { struct B *p }`, even if `struct B` has not
|
||||||
// For example, let's `struct A { struct B *p }`, even if `struct B` has not been
|
// been declared before, it can be used as the inner type of the pointer.
|
||||||
// declared before, it can be used as inner type of the pointer.
|
if let Self::Struct { name, fields, .. } = inner.deref() {
|
||||||
if let Self::Struct { name, fields, .. } = inner {
|
|
||||||
if fields.is_none() {
|
if fields.is_none() {
|
||||||
let name = name.as_ref().expect("`name` must be `Some`");
|
let name = name.as_ref().expect("`name` must be `Some`");
|
||||||
let _ = structs.entry(name.to_string()).or_insert(None);
|
let _ = structs.entry(name.to_string()).or_insert(None);
|
||||||
return Ok(self.clone());
|
return Ok(Self::pointer(*inner).set_const(is_const));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let resolved_inner = inner.clone().resolve_structs(structs, tempid_counter)?;
|
let resolved_inner = inner.resolve_structs(structs, tempid_counter)?;
|
||||||
Self::pointer(resolved_inner).set_const(*is_const)
|
Self::pointer(resolved_inner).set_const(is_const)
|
||||||
}
|
}
|
||||||
Self::Array { inner, size } => {
|
Self::Array { inner, size } => {
|
||||||
let inner = inner
|
let inner = inner.resolve_structs(structs, tempid_counter)?;
|
||||||
.deref()
|
|
||||||
.clone()
|
|
||||||
.resolve_structs(structs, tempid_counter)?;
|
|
||||||
Self::Array {
|
Self::Array {
|
||||||
inner: Box::new(inner),
|
inner: Box::new(inner),
|
||||||
size: *size,
|
size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Struct {
|
Self::Struct {
|
||||||
@@ -1273,19 +1273,18 @@ impl Dtype {
|
|||||||
is_const,
|
is_const,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(fields) = fields {
|
let (name, fields) = if let Some(fields) = fields {
|
||||||
let resolved_dtypes = fields
|
let fields = fields
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|f| f.deref().clone().resolve_structs(structs, tempid_counter))
|
.map(|f| {
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
let (d, name) = f.destruct();
|
||||||
|
let d = d.resolve_structs(structs, tempid_counter).unwrap();
|
||||||
assert_eq!(fields.len(), resolved_dtypes.len());
|
Named::new(name, d)
|
||||||
let fields = izip!(fields, resolved_dtypes)
|
})
|
||||||
.map(|(f, d)| Named::new(f.name().cloned(), d))
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let name = if let Some(name) = name {
|
let name = if let Some(name) = name {
|
||||||
name.clone()
|
name
|
||||||
} else {
|
} else {
|
||||||
let tempid = *tempid_counter;
|
let tempid = *tempid_counter;
|
||||||
*tempid_counter += 1;
|
*tempid_counter += 1;
|
||||||
@@ -1295,8 +1294,7 @@ impl Dtype {
|
|||||||
let filled_struct =
|
let filled_struct =
|
||||||
resolved_struct.fill_size_align_offsets_of_struct(structs)?;
|
resolved_struct.fill_size_align_offsets_of_struct(structs)?;
|
||||||
|
|
||||||
let prev_dtype = structs.insert(name.clone(), Some(filled_struct));
|
if let Some(prev_dtype) = structs.insert(name.clone(), Some(filled_struct)) {
|
||||||
if let Some(prev_dtype) = prev_dtype {
|
|
||||||
if prev_dtype.is_some() {
|
if prev_dtype.is_some() {
|
||||||
return Err(DtypeError::Misc {
|
return Err(DtypeError::Misc {
|
||||||
message: format!("redefinition of {}", name),
|
message: format!("redefinition of {}", name),
|
||||||
@@ -1304,10 +1302,10 @@ impl Dtype {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::structure(Some(name), None).set_const(*is_const)
|
(name, None)
|
||||||
} else {
|
} else {
|
||||||
let name = name.as_ref().expect("`name` must be exist");
|
let name = name.expect("`name` must exist");
|
||||||
let struct_type = structs.get(name).ok_or_else(|| DtypeError::Misc {
|
let struct_type = structs.get(&name).ok_or_else(|| DtypeError::Misc {
|
||||||
message: format!("unknown struct name `{}`", name),
|
message: format!("unknown struct name `{}`", name),
|
||||||
})?;
|
})?;
|
||||||
if struct_type.is_none() {
|
if struct_type.is_none() {
|
||||||
@@ -1316,17 +1314,15 @@ impl Dtype {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
(name, fields)
|
||||||
}
|
};
|
||||||
|
Self::structure(Some(name), fields).set_const(is_const)
|
||||||
}
|
}
|
||||||
Self::Function { ret, params } => {
|
Self::Function { ret, params } => {
|
||||||
let ret = ret
|
let ret = ret.resolve_structs(structs, tempid_counter)?;
|
||||||
.deref()
|
|
||||||
.clone()
|
|
||||||
.resolve_structs(structs, tempid_counter)?;
|
|
||||||
let params = params
|
let params = params
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|p| p.clone().resolve_structs(structs, tempid_counter))
|
.map(|p| p.resolve_structs(structs, tempid_counter))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
Self::function(ret, params)
|
Self::function(ret, params)
|
||||||
@@ -1418,7 +1414,7 @@ fn check_no_duplicate_field(fields: &[Named<Dtype>], field_names: &mut HashSet<S
|
|||||||
let field_dtype = field.deref();
|
let field_dtype = field.deref();
|
||||||
let fields = field_dtype
|
let fields = field_dtype
|
||||||
.get_struct_fields()
|
.get_struct_fields()
|
||||||
.expect("`field_dtype` must be struct type")
|
.expect("`field_dtype` must be a struct type")
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("struct type must have its definition");
|
.expect("struct type must have its definition");
|
||||||
if !check_no_duplicate_field(fields, field_names) {
|
if !check_no_duplicate_field(fields, field_names) {
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ fn is_equiv_block_exit(lhs: &BlockExit, rhs: &BlockExit, map: &HashMap<BlockId,
|
|||||||
if !is_equiv_operand(value, value_other, map) {
|
if !is_equiv_operand(value, value_other, map) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if !is_equiv_arg(default.deref(), default_other.deref(), map) {
|
if !is_equiv_arg(default, default_other, map) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if cases.len() != cases_other.len() {
|
if cases.len() != cases_other.len() {
|
||||||
|
|||||||
103
src/ir/interp.rs
103
src/ir/interp.rs
@@ -142,14 +142,14 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_int(self) -> Option<(u128, usize, bool)> {
|
pub fn get_int(&self) -> Option<(u128, usize, bool)> {
|
||||||
if let Value::Int {
|
if let Value::Int {
|
||||||
value,
|
value,
|
||||||
width,
|
width,
|
||||||
is_signed,
|
is_signed,
|
||||||
} = self
|
} = self
|
||||||
{
|
{
|
||||||
Some((value, width, is_signed))
|
Some((*value, *width, *is_signed))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -221,6 +221,7 @@ impl Value {
|
|||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
pub fn try_from_initializer(
|
pub fn try_from_initializer(
|
||||||
initializer: &ast::Initializer,
|
initializer: &ast::Initializer,
|
||||||
dtype: &Dtype,
|
dtype: &Dtype,
|
||||||
@@ -348,20 +349,19 @@ impl RegisterMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, rid: RegisterId, value: Value) {
|
fn write(&mut self, rid: RegisterId, value: Value) {
|
||||||
let _ = self.inner.insert(rid, value);
|
let _unused = self.inner.insert(rid, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bidirectional map between the name of a global variable and memory box id
|
/// Bidirectional map between the name of a global variable and memory box id
|
||||||
#[derive(Default, Debug, PartialEq, Clone)]
|
#[derive(Default, Debug, PartialEq, Clone)]
|
||||||
struct GlobalMap {
|
struct GlobalMap {
|
||||||
/// Map name of a global variable to memory box id
|
/// Map the name of a global variable to the memory box id
|
||||||
///
|
///
|
||||||
/// Since IR treats global variable as `Constant::GlobalVariable`,
|
/// Since IR treats global variables as `Constant::GlobalVariable`, the interpreter should be
|
||||||
/// the interpreter should be able to generate pointer values by infer 'bid'
|
/// able to generate pointer values by inferring `bid` from the `name` of the global variable.
|
||||||
/// from the 'name' of the global variable.
|
|
||||||
var_to_bid: HashMap<String, usize>,
|
var_to_bid: HashMap<String, usize>,
|
||||||
/// Map memory box id to the name of a global variable
|
/// Map the memory box id to the name of a global variable
|
||||||
///
|
///
|
||||||
/// When a function call occurs, the interpreter should be able to find `name` of the function
|
/// When a function call occurs, the interpreter should be able to find `name` of the function
|
||||||
/// from `bid` of the `callee` which is a function pointer.
|
/// from `bid` of the `callee` which is a function pointer.
|
||||||
@@ -481,11 +481,11 @@ mod calculator {
|
|||||||
ast::BinaryOperator::BitwiseXor => lhs ^ rhs,
|
ast::BinaryOperator::BitwiseXor => lhs ^ rhs,
|
||||||
ast::BinaryOperator::BitwiseOr => lhs | rhs,
|
ast::BinaryOperator::BitwiseOr => lhs | rhs,
|
||||||
ast::BinaryOperator::Equals => {
|
ast::BinaryOperator::Equals => {
|
||||||
let result = if lhs == rhs { 1 } else { 0 };
|
let result = (lhs == rhs).into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::NotEquals => {
|
ast::BinaryOperator::NotEquals => {
|
||||||
let result = if lhs != rhs { 1 } else { 0 };
|
let result = (lhs != rhs).into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::Less => {
|
ast::BinaryOperator::Less => {
|
||||||
@@ -494,7 +494,7 @@ mod calculator {
|
|||||||
} else {
|
} else {
|
||||||
lhs < rhs
|
lhs < rhs
|
||||||
};
|
};
|
||||||
let result = if condition { 1 } else { 0 };
|
let result = condition.into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::Greater => {
|
ast::BinaryOperator::Greater => {
|
||||||
@@ -503,7 +503,7 @@ mod calculator {
|
|||||||
} else {
|
} else {
|
||||||
lhs > rhs
|
lhs > rhs
|
||||||
};
|
};
|
||||||
let result = if condition { 1 } else { 0 };
|
let result = condition.into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::LessOrEqual => {
|
ast::BinaryOperator::LessOrEqual => {
|
||||||
@@ -512,7 +512,7 @@ mod calculator {
|
|||||||
} else {
|
} else {
|
||||||
lhs <= rhs
|
lhs <= rhs
|
||||||
};
|
};
|
||||||
let result = if condition { 1 } else { 0 };
|
let result = condition.into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::GreaterOrEqual => {
|
ast::BinaryOperator::GreaterOrEqual => {
|
||||||
@@ -521,7 +521,7 @@ mod calculator {
|
|||||||
} else {
|
} else {
|
||||||
lhs >= rhs
|
lhs >= rhs
|
||||||
};
|
};
|
||||||
let result = if condition { 1 } else { 0 };
|
let result = condition.into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
_ => todo!(
|
_ => todo!(
|
||||||
@@ -566,30 +566,30 @@ mod calculator {
|
|||||||
let order = lhs
|
let order = lhs
|
||||||
.partial_cmp(&rhs)
|
.partial_cmp(&rhs)
|
||||||
.expect("`lhs` and `rhs` must be not NAN");
|
.expect("`lhs` and `rhs` must be not NAN");
|
||||||
let result = if Ordering::Equal == order { 1 } else { 0 };
|
let result = (Ordering::Equal == order).into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::NotEquals => {
|
ast::BinaryOperator::NotEquals => {
|
||||||
let order = lhs
|
let order = lhs
|
||||||
.partial_cmp(&rhs)
|
.partial_cmp(&rhs)
|
||||||
.expect("`lhs` and `rhs` must be not NAN");
|
.expect("`lhs` and `rhs` must be not NAN");
|
||||||
let result = if Ordering::Equal != order { 1 } else { 0 };
|
let result = (Ordering::Equal != order).into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::Less => {
|
ast::BinaryOperator::Less => {
|
||||||
let result = if lhs.lt(&rhs) { 1 } else { 0 };
|
let result = lhs.lt(&rhs).into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::Greater => {
|
ast::BinaryOperator::Greater => {
|
||||||
let result = if lhs.gt(&rhs) { 1 } else { 0 };
|
let result = lhs.gt(&rhs).into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::LessOrEqual => {
|
ast::BinaryOperator::LessOrEqual => {
|
||||||
let result = if lhs.le(&rhs) { 1 } else { 0 };
|
let result = lhs.le(&rhs).into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::GreaterOrEqual => {
|
ast::BinaryOperator::GreaterOrEqual => {
|
||||||
let result = if lhs.ge(&rhs) { 1 } else { 0 };
|
let result = lhs.ge(&rhs).into();
|
||||||
return Ok(Value::int(result, 1, false));
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
_ => todo!(
|
_ => todo!(
|
||||||
@@ -653,19 +653,11 @@ mod calculator {
|
|||||||
},
|
},
|
||||||
) => match op {
|
) => match op {
|
||||||
ast::BinaryOperator::Equals => {
|
ast::BinaryOperator::Equals => {
|
||||||
let result = if bid == other_bid && offset == other_offset {
|
let result = (bid == other_bid && offset == other_offset).into();
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
Ok(Value::int(result, 1, false))
|
Ok(Value::int(result, 1, false))
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::NotEquals => {
|
ast::BinaryOperator::NotEquals => {
|
||||||
let result = if !(bid == other_bid && offset == other_offset) {
|
let result = (!(bid == other_bid && offset == other_offset)).into();
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
Ok(Value::int(result, 1, false))
|
Ok(Value::int(result, 1, false))
|
||||||
}
|
}
|
||||||
_ => todo!(
|
_ => todo!(
|
||||||
@@ -708,7 +700,7 @@ mod calculator {
|
|||||||
ast::UnaryOperator::Negate => {
|
ast::UnaryOperator::Negate => {
|
||||||
// Check if it is boolean
|
// Check if it is boolean
|
||||||
assert!(width == 1);
|
assert!(width == 1);
|
||||||
let result = if value == 0 { 1 } else { 0 };
|
let result = (value == 0).into();
|
||||||
Ok(Value::int(result, width, is_signed))
|
Ok(Value::int(result, width, is_signed))
|
||||||
}
|
}
|
||||||
_ => todo!(
|
_ => todo!(
|
||||||
@@ -786,7 +778,7 @@ mod calculator {
|
|||||||
}
|
}
|
||||||
(Value::Int { value, .. }, Dtype::Pointer { inner, .. }) => {
|
(Value::Int { value, .. }, Dtype::Pointer { inner, .. }) => {
|
||||||
if value == 0 {
|
if value == 0 {
|
||||||
Ok(Value::pointer(None, 0, inner.deref().clone()))
|
Ok(Value::pointer(None, 0, *inner))
|
||||||
} else {
|
} else {
|
||||||
panic!(
|
panic!(
|
||||||
"calculate_typecast: not support case \
|
"calculate_typecast: not support case \
|
||||||
@@ -1074,7 +1066,7 @@ impl Byte {
|
|||||||
izip!(fields, offsets).for_each(|(f, o)| {
|
izip!(fields, offsets).for_each(|(f, o)| {
|
||||||
let result = Self::value_to_bytes(f.deref(), structs);
|
let result = Self::value_to_bytes(f.deref(), structs);
|
||||||
let size_of_data = f.deref().dtype().size_align_of(structs).unwrap().0;
|
let size_of_data = f.deref().dtype().size_align_of(structs).unwrap().0;
|
||||||
let _ = values.splice(*o..(*o + size_of_data), result.iter().cloned());
|
let _unused = values.splice(*o..(*o + size_of_data), result.into_iter());
|
||||||
});
|
});
|
||||||
|
|
||||||
values
|
values
|
||||||
@@ -1144,7 +1136,7 @@ impl Memory {
|
|||||||
let block = self.inner[bid].as_mut().unwrap();
|
let block = self.inner[bid].as_mut().unwrap();
|
||||||
|
|
||||||
if 0 <= offset && end <= block.len() {
|
if 0 <= offset && end <= block.len() {
|
||||||
let _ = block.splice(offset as usize..end, bytes.iter().cloned());
|
let _unused = block.splice(offset as usize..end, bytes.into_iter());
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
@@ -1154,8 +1146,9 @@ impl Memory {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
struct State<'i> {
|
struct State<'i> {
|
||||||
/// A data structure that maps each global variable to a pointer value
|
/// Maps each global variable to a pointer value.
|
||||||
/// When function call occurs, `registers` can be initialized by `global_registers`
|
///
|
||||||
|
/// When a function call occurs, `registers` can be initialized by `global_registers`
|
||||||
pub global_map: GlobalMap,
|
pub global_map: GlobalMap,
|
||||||
pub stack_frame: StackFrame<'i>,
|
pub stack_frame: StackFrame<'i>,
|
||||||
pub stack: Vec<StackFrame<'i>>,
|
pub stack: Vec<StackFrame<'i>>,
|
||||||
@@ -1330,7 +1323,7 @@ impl<'i> State<'i> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
args.iter()
|
args.iter()
|
||||||
.map(|a| self.interp_operand(a.clone()))
|
.map(|a| self.interp_operand(a))
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1349,7 +1342,7 @@ impl<'i> State<'i> {
|
|||||||
|
|
||||||
arg.args
|
arg.args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| self.interp_operand(a.clone()).unwrap())
|
.map(|a| self.interp_operand(a).unwrap())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@@ -1374,7 +1367,7 @@ impl<'i> State<'i> {
|
|||||||
arg_then,
|
arg_then,
|
||||||
arg_else,
|
arg_else,
|
||||||
} => {
|
} => {
|
||||||
let value = self.interp_operand(condition.clone())?;
|
let value = self.interp_operand(condition)?;
|
||||||
let (value, width, _) = value.get_int().expect("`condition` must be `Value::Int`");
|
let (value, width, _) = value.get_int().expect("`condition` must be `Value::Int`");
|
||||||
// Check if it is boolean
|
// Check if it is boolean
|
||||||
assert!(width == 1);
|
assert!(width == 1);
|
||||||
@@ -1386,7 +1379,7 @@ impl<'i> State<'i> {
|
|||||||
default,
|
default,
|
||||||
cases,
|
cases,
|
||||||
} => {
|
} => {
|
||||||
let value = self.interp_operand(value.clone())?;
|
let value = self.interp_operand(value)?;
|
||||||
|
|
||||||
// TODO: consider different integer `width` in the future
|
// TODO: consider different integer `width` in the future
|
||||||
let arg = cases
|
let arg = cases
|
||||||
@@ -1396,7 +1389,7 @@ impl<'i> State<'i> {
|
|||||||
.unwrap_or_else(|| default);
|
.unwrap_or_else(|| default);
|
||||||
self.interp_jump(arg)
|
self.interp_jump(arg)
|
||||||
}
|
}
|
||||||
BlockExit::Return { value } => Ok(Some(self.interp_operand(value.clone())?)),
|
BlockExit::Return { value } => Ok(Some(self.interp_operand(value)?)),
|
||||||
BlockExit::Unreachable => Err(InterpreterError::Unreachable),
|
BlockExit::Unreachable => Err(InterpreterError::Unreachable),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1405,8 +1398,8 @@ impl<'i> State<'i> {
|
|||||||
let result = match instruction {
|
let result = match instruction {
|
||||||
Instruction::Nop => Value::unit(),
|
Instruction::Nop => Value::unit(),
|
||||||
Instruction::BinOp { op, lhs, rhs, .. } => {
|
Instruction::BinOp { op, lhs, rhs, .. } => {
|
||||||
let lhs = self.interp_operand(lhs.clone())?;
|
let lhs = self.interp_operand(lhs)?;
|
||||||
let rhs = self.interp_operand(rhs.clone())?;
|
let rhs = self.interp_operand(rhs)?;
|
||||||
|
|
||||||
calculator::calculate_binary_operator_expression(op, lhs, rhs).map_err(|_| {
|
calculator::calculate_binary_operator_expression(op, lhs, rhs).map_err(|_| {
|
||||||
InterpreterError::Misc {
|
InterpreterError::Misc {
|
||||||
@@ -1417,7 +1410,7 @@ impl<'i> State<'i> {
|
|||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
Instruction::UnaryOp { op, operand, .. } => {
|
Instruction::UnaryOp { op, operand, .. } => {
|
||||||
let operand = self.interp_operand(operand.clone())?;
|
let operand = self.interp_operand(operand)?;
|
||||||
|
|
||||||
calculator::calculate_unary_operator_expression(op, operand).map_err(|_| {
|
calculator::calculate_unary_operator_expression(op, operand).map_err(|_| {
|
||||||
InterpreterError::Misc {
|
InterpreterError::Misc {
|
||||||
@@ -1428,8 +1421,8 @@ impl<'i> State<'i> {
|
|||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
Instruction::Store { ptr, value, .. } => {
|
Instruction::Store { ptr, value, .. } => {
|
||||||
let ptr = self.interp_operand(ptr.clone())?;
|
let ptr = self.interp_operand(ptr)?;
|
||||||
let value = self.interp_operand(value.clone())?;
|
let value = self.interp_operand(value)?;
|
||||||
let (bid, offset, _) = self.interp_ptr(&ptr)?;
|
let (bid, offset, _) = self.interp_ptr(&ptr)?;
|
||||||
self.memory
|
self.memory
|
||||||
.store(bid, offset, &value, &self.ir.structs)
|
.store(bid, offset, &value, &self.ir.structs)
|
||||||
@@ -1444,12 +1437,12 @@ impl<'i> State<'i> {
|
|||||||
Value::Unit
|
Value::Unit
|
||||||
}
|
}
|
||||||
Instruction::Load { ptr, .. } => {
|
Instruction::Load { ptr, .. } => {
|
||||||
let ptr = self.interp_operand(ptr.clone())?;
|
let ptr = self.interp_operand(ptr)?;
|
||||||
let (bid, offset, dtype) = self.interp_ptr(&ptr)?;
|
let (bid, offset, dtype) = self.interp_ptr(&ptr)?;
|
||||||
self.memory.load(bid, offset, &dtype, &self.ir.structs)?
|
self.memory.load(bid, offset, &dtype, &self.ir.structs)?
|
||||||
}
|
}
|
||||||
Instruction::Call { callee, args, .. } => {
|
Instruction::Call { callee, args, .. } => {
|
||||||
let ptr = self.interp_operand(callee.clone())?;
|
let ptr = self.interp_operand(callee)?;
|
||||||
|
|
||||||
// Get function name from pointer
|
// Get function name from pointer
|
||||||
let (bid, _, _) = ptr.get_pointer().expect("`ptr` must be `Value::Pointer`");
|
let (bid, _, _) = ptr.get_pointer().expect("`ptr` must be `Value::Pointer`");
|
||||||
@@ -1503,7 +1496,7 @@ impl<'i> State<'i> {
|
|||||||
value,
|
value,
|
||||||
target_dtype,
|
target_dtype,
|
||||||
} => {
|
} => {
|
||||||
let value = self.interp_operand(value.clone())?;
|
let value = self.interp_operand(value)?;
|
||||||
calculator::calculate_typecast(value, target_dtype.clone()).map_err(|_| {
|
calculator::calculate_typecast(value, target_dtype.clone()).map_err(|_| {
|
||||||
InterpreterError::Misc {
|
InterpreterError::Misc {
|
||||||
func_name: self.stack_frame.func_name.clone(),
|
func_name: self.stack_frame.func_name.clone(),
|
||||||
@@ -1513,10 +1506,10 @@ impl<'i> State<'i> {
|
|||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
Instruction::GetElementPtr { ptr, offset, dtype } => {
|
Instruction::GetElementPtr { ptr, offset, dtype } => {
|
||||||
let ptr = self.interp_operand(ptr.clone())?;
|
let ptr = self.interp_operand(ptr)?;
|
||||||
|
|
||||||
let (value, _, _) = self
|
let (value, _, _) = self
|
||||||
.interp_operand(offset.clone())?
|
.interp_operand(offset)?
|
||||||
.get_int()
|
.get_int()
|
||||||
.expect("`idx` must be `Value::Int`");
|
.expect("`idx` must be `Value::Int`");
|
||||||
|
|
||||||
@@ -1542,12 +1535,10 @@ impl<'i> State<'i> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interp_operand(&self, operand: Operand) -> Result<Value, InterpreterError> {
|
fn interp_operand(&self, operand: &Operand) -> Result<Value, InterpreterError> {
|
||||||
match &operand {
|
match operand {
|
||||||
Operand::Constant(value) => Ok(self.interp_constant(value.clone())),
|
Operand::Constant(value) => Ok(self.interp_constant(value.clone())),
|
||||||
Operand::Register { rid, .. } => {
|
Operand::Register { rid, .. } => Ok(self.stack_frame.registers.read(*rid).clone()),
|
||||||
Ok(self.stack_frame.registers.read(rid.clone()).clone())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
111
src/ir/mod.rs
111
src/ir/mod.rs
@@ -3,7 +3,6 @@
|
|||||||
mod dtype;
|
mod dtype;
|
||||||
mod equiv;
|
mod equiv;
|
||||||
mod interp;
|
mod interp;
|
||||||
#[allow(clippy::all)]
|
|
||||||
mod parse;
|
mod parse;
|
||||||
mod visualize;
|
mod visualize;
|
||||||
mod write_ir;
|
mod write_ir;
|
||||||
@@ -49,14 +48,14 @@ impl TryFrom<Dtype> for Declaration {
|
|||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// If `int g = 0;` is declared, `dtype` is
|
/// If `int g = 0;` is declared, `dtype` is `ir::Dtype::Int{ width:32, is_signed:true,
|
||||||
/// `ir::Dtype::Int{ width:32, is_signed:true, is_const:false }`.
|
/// is_const:false }`.
|
||||||
/// In this case, `ir::Declaration::Variable{ dtype, initializer: Some(Constant::I32(1)) }`
|
|
||||||
/// is generated.
|
|
||||||
///
|
///
|
||||||
/// Conversely, if `int foo();` is declared, `dtype` is
|
/// In this case, `ir::Declaration::Variable{ dtype, initializer:
|
||||||
/// `ir::Dtype::Function{ret: Scalar(Int), params: []}`.
|
/// Some(Constant::I32(1)) }` is generated.
|
||||||
/// Thus, in this case, `ir::Declaration::Function` is generated.
|
///
|
||||||
|
/// Conversely, if `int foo();` is declared, `dtype` is `ir::Dtype::Function{ret: Scalar(Int),
|
||||||
|
/// params: []}`. Thus, in this case, `ir::Declaration::Function` is generated.
|
||||||
fn try_from(dtype: Dtype) -> Result<Self, Self::Error> {
|
fn try_from(dtype: Dtype) -> Result<Self, Self::Error> {
|
||||||
match &dtype {
|
match &dtype {
|
||||||
Dtype::Unit { .. } => Err(DtypeError::Misc {
|
Dtype::Unit { .. } => Err(DtypeError::Misc {
|
||||||
@@ -192,7 +191,6 @@ pub struct Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
#[allow(clippy::large_enum_variant)]
|
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
Nop,
|
Nop,
|
||||||
BinOp {
|
BinOp {
|
||||||
@@ -228,27 +226,30 @@ pub enum Instruction {
|
|||||||
GetElementPtr {
|
GetElementPtr {
|
||||||
ptr: Operand,
|
ptr: Operand,
|
||||||
offset: Operand,
|
offset: Operand,
|
||||||
dtype: Box<Dtype>,
|
dtype: Dtype,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasDtype for Instruction {
|
impl HasDtype for Instruction {
|
||||||
fn dtype(&self) -> Dtype {
|
fn dtype(&self) -> Dtype {
|
||||||
match self {
|
match self {
|
||||||
Self::Nop => Dtype::unit(),
|
Self::Nop | Self::Store { .. } => Dtype::unit(),
|
||||||
Self::BinOp { dtype, .. } => dtype.clone(),
|
Self::BinOp { dtype, .. }
|
||||||
Self::UnaryOp { dtype, .. } => dtype.clone(),
|
| Self::UnaryOp { dtype, .. }
|
||||||
Self::Store { .. } => Dtype::unit(),
|
| Self::Call {
|
||||||
|
return_type: dtype, ..
|
||||||
|
}
|
||||||
|
| Self::TypeCast {
|
||||||
|
target_dtype: dtype,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::GetElementPtr { dtype, .. } => dtype.clone(),
|
||||||
Self::Load { ptr } => ptr
|
Self::Load { ptr } => ptr
|
||||||
.dtype()
|
.dtype()
|
||||||
.get_pointer_inner()
|
.get_pointer_inner()
|
||||||
.expect("Load instruction must have pointer value as operand")
|
.expect("Load instruction must have pointer value as operand")
|
||||||
.deref()
|
|
||||||
.clone()
|
.clone()
|
||||||
.set_const(false),
|
.set_const(false),
|
||||||
Self::Call { return_type, .. } => return_type.clone(),
|
|
||||||
Self::TypeCast { target_dtype, .. } => target_dtype.clone(),
|
|
||||||
Self::GetElementPtr { dtype, .. } => dtype.deref().clone(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,43 +308,30 @@ impl fmt::Display for Instruction {
|
|||||||
match self {
|
match self {
|
||||||
Instruction::Nop => write!(f, "nop"),
|
Instruction::Nop => write!(f, "nop"),
|
||||||
Instruction::BinOp { op, lhs, rhs, .. } => {
|
Instruction::BinOp { op, lhs, rhs, .. } => {
|
||||||
write!(
|
write!(f, "{} {} {}", op.write_operation(), lhs, rhs)
|
||||||
f,
|
|
||||||
"{} {} {}",
|
|
||||||
op.write_operation(),
|
|
||||||
lhs.write_string(),
|
|
||||||
rhs.write_string()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Instruction::UnaryOp { op, operand, .. } => {
|
Instruction::UnaryOp { op, operand, .. } => {
|
||||||
write!(f, "{} {}", op.write_operation(), operand.write_string())
|
write!(f, "{} {}", op.write_operation(), operand)
|
||||||
}
|
}
|
||||||
Instruction::Store { ptr, value } => {
|
Instruction::Store { ptr, value } => {
|
||||||
write!(f, "store {} {}", value.write_string(), ptr.write_string())
|
write!(f, "store {} {}", value, ptr)
|
||||||
}
|
}
|
||||||
Instruction::Load { ptr } => write!(f, "load {}", ptr.write_string()),
|
Instruction::Load { ptr } => write!(f, "load {}", ptr),
|
||||||
Instruction::Call { callee, args, .. } => {
|
Instruction::Call { callee, args, .. } => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"call {}({})",
|
"call {}({})",
|
||||||
callee.write_string(),
|
callee,
|
||||||
args.iter().format_with(", ", |operand, f| f(&format_args!(
|
args.iter()
|
||||||
"{}",
|
.format_with(", ", |operand, f| f(&format_args!("{}", operand)))
|
||||||
operand.write_string()
|
|
||||||
)))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Instruction::TypeCast {
|
Instruction::TypeCast {
|
||||||
value,
|
value,
|
||||||
target_dtype,
|
target_dtype,
|
||||||
} => write!(f, "typecast {} to {}", value.write_string(), target_dtype),
|
} => write!(f, "typecast {} to {}", value, target_dtype),
|
||||||
Instruction::GetElementPtr { ptr, offset, .. } => {
|
Instruction::GetElementPtr { ptr, offset, .. } => {
|
||||||
write!(
|
write!(f, "getelementptr {} offset {}", ptr, offset)
|
||||||
f,
|
|
||||||
"getelementptr {} offset {}",
|
|
||||||
ptr.write_string(),
|
|
||||||
offset.write_string()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,12 +344,12 @@ pub enum BlockExit {
|
|||||||
},
|
},
|
||||||
ConditionalJump {
|
ConditionalJump {
|
||||||
condition: Operand,
|
condition: Operand,
|
||||||
arg_then: Box<JumpArg>,
|
arg_then: JumpArg,
|
||||||
arg_else: Box<JumpArg>,
|
arg_else: JumpArg,
|
||||||
},
|
},
|
||||||
Switch {
|
Switch {
|
||||||
value: Operand,
|
value: Operand,
|
||||||
default: Box<JumpArg>,
|
default: JumpArg,
|
||||||
cases: Vec<(Constant, JumpArg)>,
|
cases: Vec<(Constant, JumpArg)>,
|
||||||
},
|
},
|
||||||
Return {
|
Return {
|
||||||
@@ -402,13 +390,7 @@ impl fmt::Display for BlockExit {
|
|||||||
condition,
|
condition,
|
||||||
arg_then,
|
arg_then,
|
||||||
arg_else,
|
arg_else,
|
||||||
} => write!(
|
} => write!(f, "br {}, {}, {}", condition, arg_then, arg_else),
|
||||||
f,
|
|
||||||
"br {}, {}, {}",
|
|
||||||
condition.write_string(),
|
|
||||||
arg_then,
|
|
||||||
arg_else
|
|
||||||
),
|
|
||||||
BlockExit::Switch {
|
BlockExit::Switch {
|
||||||
value,
|
value,
|
||||||
default,
|
default,
|
||||||
@@ -416,7 +398,7 @@ impl fmt::Display for BlockExit {
|
|||||||
} => write!(
|
} => write!(
|
||||||
f,
|
f,
|
||||||
"switch {} default {} [\n{}\n ]",
|
"switch {} default {} [\n{}\n ]",
|
||||||
value.write_string(),
|
value,
|
||||||
default,
|
default,
|
||||||
cases.iter().format_with("\n", |(v, b), f| f(&format_args!(
|
cases.iter().format_with("\n", |(v, b), f| f(&format_args!(
|
||||||
" {}:{} {}",
|
" {}:{} {}",
|
||||||
@@ -425,7 +407,7 @@ impl fmt::Display for BlockExit {
|
|||||||
b
|
b
|
||||||
)))
|
)))
|
||||||
),
|
),
|
||||||
BlockExit::Return { value } => write!(f, "ret {}", value.write_string()),
|
BlockExit::Return { value } => write!(f, "ret {}", value),
|
||||||
BlockExit::Unreachable => write!(f, "<unreachable>\t\t\t\t; error state"),
|
BlockExit::Unreachable => write!(f, "<unreachable>\t\t\t\t; error state"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -499,8 +481,8 @@ impl Operand {
|
|||||||
impl fmt::Display for Operand {
|
impl fmt::Display for Operand {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Constant(value) => write!(f, "{}", value),
|
Self::Constant(value) => write!(f, "{}:{}", value, value.dtype()),
|
||||||
Self::Register { rid, .. } => write!(f, "{}", rid),
|
Self::Register { rid, dtype } => write!(f, "{}:{}", rid, dtype),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -514,7 +496,7 @@ impl HasDtype for Operand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, Clone)]
|
#[derive(Debug, Eq, Clone, Copy)]
|
||||||
pub enum RegisterId {
|
pub enum RegisterId {
|
||||||
/// Registers holding pointers to local allocations.
|
/// Registers holding pointers to local allocations.
|
||||||
///
|
///
|
||||||
@@ -747,11 +729,12 @@ impl TryFrom<&ast::Expression> for Constant {
|
|||||||
ast::Expression::UnaryOperator(unary) => {
|
ast::Expression::UnaryOperator(unary) => {
|
||||||
let constant = Self::try_from(&unary.node.operand.node)?;
|
let constant = Self::try_from(&unary.node.operand.node)?;
|
||||||
// When an IR is generated, there are cases where some expressions must be
|
// When an IR is generated, there are cases where some expressions must be
|
||||||
// interpreted unconditionally as compile-time constant value. In this case,
|
// interpreted unconditionally as a compile-time constant value. In this case, we
|
||||||
// we need to translate also the expression applied `minus` unary operator
|
// need to also translate the expression applied `minus` unary operator to a
|
||||||
// to compile-time constant value directly.
|
// compile-time constant value directly.
|
||||||
// Let's say expression is `case -1: { .. }`,
|
//
|
||||||
// `-1` must be interpreted to compile-time constant value.
|
// Let's say the expression is `case -1: { .. }`, `-1` must be interpreted to a
|
||||||
|
// compile-time constant value.
|
||||||
match &unary.node.operator.node {
|
match &unary.node.operator.node {
|
||||||
ast::UnaryOperator::Minus => Ok(constant.minus()),
|
ast::UnaryOperator::Minus => Ok(constant.minus()),
|
||||||
ast::UnaryOperator::Plus => Ok(constant),
|
ast::UnaryOperator::Plus => Ok(constant),
|
||||||
@@ -1006,6 +989,14 @@ impl<T> Named<T> {
|
|||||||
pub fn name(&self) -> Option<&String> {
|
pub fn name(&self) -> Option<&String> {
|
||||||
self.name.as_ref()
|
self.name.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn destruct(self) -> (T, Option<String>) {
|
||||||
|
(self.inner, self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: fmt::Display> fmt::Display for Named<T> {
|
impl<T: fmt::Display> fmt::Display for Named<T> {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ peg::parser! {
|
|||||||
rule named_decl() -> Named<Declaration> =
|
rule named_decl() -> Named<Declaration> =
|
||||||
"var" __ dtype:dtype() __ var:global_variable() _ "=" _ initializer:initializer() {
|
"var" __ dtype:dtype() __ var:global_variable() _ "=" _ initializer:initializer() {
|
||||||
Named::new(Some(var), Declaration::Variable {
|
Named::new(Some(var), Declaration::Variable {
|
||||||
dtype: dtype,
|
dtype,
|
||||||
initializer,
|
initializer,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ peg::parser! {
|
|||||||
// For this reason, we need to check the dtype of the result to confirm the dtype
|
// For this reason, we need to check the dtype of the result to confirm the dtype
|
||||||
// of `GetElementPtr` instruction when parsing IR.
|
// of `GetElementPtr` instruction when parsing IR.
|
||||||
let instruction = if let Instruction::GetElementPtr { ptr, offset, .. } = instruction {
|
let instruction = if let Instruction::GetElementPtr { ptr, offset, .. } = instruction {
|
||||||
Instruction::GetElementPtr { ptr, offset, dtype: Box::new(dtype) }
|
Instruction::GetElementPtr { ptr, offset, dtype }
|
||||||
} else {
|
} else {
|
||||||
instruction
|
instruction
|
||||||
};
|
};
|
||||||
@@ -317,7 +317,7 @@ peg::parser! {
|
|||||||
Instruction::GetElementPtr{
|
Instruction::GetElementPtr{
|
||||||
ptr,
|
ptr,
|
||||||
offset,
|
offset,
|
||||||
dtype: Box::new(Dtype::unit()), // TODO
|
dtype: Dtype::unit(), // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/
|
/
|
||||||
@@ -375,11 +375,11 @@ peg::parser! {
|
|||||||
}
|
}
|
||||||
/
|
/
|
||||||
"br" __ condition:operand() _ "," _ arg_then:jump_arg() _ "," _ arg_else:jump_arg() {
|
"br" __ condition:operand() _ "," _ arg_then:jump_arg() _ "," _ arg_else:jump_arg() {
|
||||||
BlockExit::ConditionalJump { condition, arg_then: Box::new(arg_then), arg_else: Box::new(arg_else) }
|
BlockExit::ConditionalJump { condition, arg_then, arg_else }
|
||||||
}
|
}
|
||||||
/
|
/
|
||||||
"switch" __ value:operand() __ "default" __ default:jump_arg() _ "[" _ cases:(switch_case() ** __) _ "]" {
|
"switch" __ value:operand() __ "default" __ default:jump_arg() _ "[" _ cases:(switch_case() ** __) _ "]" {
|
||||||
BlockExit::Switch { value, default: Box::new(default), cases }
|
BlockExit::Switch { value, default, cases }
|
||||||
}
|
}
|
||||||
/
|
/
|
||||||
"ret" __ value:operand() {
|
"ret" __ value:operand() {
|
||||||
@@ -663,12 +663,12 @@ peg::parser! {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
IoError(std::io::Error),
|
Io(std::io::Error),
|
||||||
ParseError(peg::error::ParseError<peg::str::LineCol>),
|
Parse(peg::error::ParseError<peg::str::LineCol>),
|
||||||
ResolveError,
|
Resolve,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct Parse {}
|
pub struct Parse {}
|
||||||
|
|
||||||
impl<P: AsRef<Path>> Translate<P> for Parse {
|
impl<P: AsRef<Path>> Translate<P> for Parse {
|
||||||
@@ -676,8 +676,8 @@ impl<P: AsRef<Path>> Translate<P> for Parse {
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn translate(&mut self, source: &P) -> Result<Self::Target, Self::Error> {
|
fn translate(&mut self, source: &P) -> Result<Self::Target, Self::Error> {
|
||||||
let ir = fs::read_to_string(source).map_err(Error::IoError)?;
|
let ir = fs::read_to_string(source).map_err(Error::Io)?;
|
||||||
let ir = ir_parse::translation_unit(&ir).map_err(Error::ParseError)?;
|
let ir = ir_parse::translation_unit(&ir).map_err(Error::Parse)?;
|
||||||
Ok(ir)
|
Ok(ir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -688,7 +688,8 @@ fn resolve_structs(struct_type: Dtype, structs: &mut HashMap<String, Option<Dtyp
|
|||||||
.get_struct_name()
|
.get_struct_name()
|
||||||
.expect("`struct_type` must be struct type")
|
.expect("`struct_type` must be struct type")
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("`struct_type` must have a name");
|
.expect("`struct_type` must have a name")
|
||||||
|
.clone();
|
||||||
let fields = struct_type
|
let fields = struct_type
|
||||||
.get_struct_fields()
|
.get_struct_fields()
|
||||||
.expect("`struct_type` must be struct type")
|
.expect("`struct_type` must be struct type")
|
||||||
@@ -716,10 +717,9 @@ fn resolve_structs(struct_type: Dtype, structs: &mut HashMap<String, Option<Dtyp
|
|||||||
}
|
}
|
||||||
|
|
||||||
let filled_struct = struct_type
|
let filled_struct = struct_type
|
||||||
.clone()
|
|
||||||
.fill_size_align_offsets_of_struct(structs)
|
.fill_size_align_offsets_of_struct(structs)
|
||||||
.expect("`struct_type` must be struct type");
|
.expect("`struct_type` must be struct type");
|
||||||
|
|
||||||
let result = structs.insert(name.clone(), Some(filled_struct));
|
let result = structs.insert(name, Some(filled_struct));
|
||||||
assert!(result.is_some());
|
assert!(result.is_some());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use crate::ir::*;
|
use crate::ir::*;
|
||||||
use crate::some_or;
|
use crate::some_or;
|
||||||
use crate::write_base::*;
|
|
||||||
use crate::Translate;
|
use crate::Translate;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
@@ -118,7 +117,7 @@ impl Visualizer {
|
|||||||
subgraphs.push(subgraph);
|
subgraphs.push(subgraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = self.function_first_instruction.insert(
|
let _unused = self.function_first_instruction.insert(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
self.get_block_first_instruction(name, definition.bid_init),
|
self.get_block_first_instruction(name, definition.bid_init),
|
||||||
);
|
);
|
||||||
@@ -196,7 +195,7 @@ impl Visualizer {
|
|||||||
nodes.push(format!(
|
nodes.push(format!(
|
||||||
"{} [label=\"{}\"]",
|
"{} [label=\"{}\"]",
|
||||||
self.translate_instruction_node(name, *bid, iid),
|
self.translate_instruction_node(name, *bid, iid),
|
||||||
instruction.write_string()
|
instruction
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +203,7 @@ impl Visualizer {
|
|||||||
nodes.push(format!(
|
nodes.push(format!(
|
||||||
"{} [label=\"{}\"]",
|
"{} [label=\"{}\"]",
|
||||||
self.translate_block_exit_node(name, *bid),
|
self.translate_block_exit_node(name, *bid),
|
||||||
block.exit.write_string()
|
block.exit
|
||||||
));
|
));
|
||||||
|
|
||||||
let edges = (0..block.instructions.len())
|
let edges = (0..block.instructions.len())
|
||||||
@@ -218,7 +217,7 @@ impl Visualizer {
|
|||||||
} else {
|
} else {
|
||||||
self.translate_instruction_node(name, *bid, 0)
|
self.translate_instruction_node(name, *bid, 0)
|
||||||
};
|
};
|
||||||
let _ = self
|
let _unused = self
|
||||||
.block_first_instruction
|
.block_first_instruction
|
||||||
.insert((name.to_string(), *bid), first_instruction);
|
.insert((name.to_string(), *bid), first_instruction);
|
||||||
|
|
||||||
|
|||||||
@@ -147,12 +147,12 @@ impl WriteLine for (&BlockId, &Block) {
|
|||||||
} else {
|
} else {
|
||||||
"".into()
|
"".into()
|
||||||
},
|
},
|
||||||
instr.write_string()
|
instr
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_indent(indent, write)?;
|
write_indent(indent, write)?;
|
||||||
writeln!(write, "{}", self.1.exit.write_string())?;
|
writeln!(write, "{}", self.1.exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ impl WriteString for Instruction {
|
|||||||
|
|
||||||
impl WriteString for Operand {
|
impl WriteString for Operand {
|
||||||
fn write_string(&self) -> String {
|
fn write_string(&self) -> String {
|
||||||
format!("{}:{}", self, self.dtype())
|
format!("{}", self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ use lang_c::ast::*;
|
|||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct Irgen {}
|
pub struct Irgen {}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct IrgenError {}
|
pub struct IrgenError {}
|
||||||
|
|
||||||
impl fmt::Display for IrgenError {
|
impl fmt::Display for IrgenError {
|
||||||
|
|||||||
41
src/lib.rs
41
src/lib.rs
@@ -1,44 +1,51 @@
|
|||||||
//! KECC: KAIST Educational C Compiler.
|
//! KECC: KAIST Educational C Compiler.
|
||||||
|
|
||||||
// Tries to deny all lints (`rustc -W help`).
|
#![deny(clippy::all)]
|
||||||
|
#![deny(rustdoc::all)]
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
|
// Tries to deny all rustc allow lints.
|
||||||
|
// <https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html>
|
||||||
#![deny(absolute_paths_not_starting_with_crate)]
|
#![deny(absolute_paths_not_starting_with_crate)]
|
||||||
#![deny(anonymous_parameters)]
|
// Old, historical lint
|
||||||
// #![deny(box_pointers)]
|
// #![deny(box_pointers)]
|
||||||
#![deny(deprecated_in_future)]
|
|
||||||
#![deny(elided_lifetimes_in_paths)]
|
#![deny(elided_lifetimes_in_paths)]
|
||||||
#![deny(explicit_outlives_requirements)]
|
#![deny(explicit_outlives_requirements)]
|
||||||
#![deny(rustdoc::invalid_html_tags)]
|
|
||||||
#![deny(keyword_idents)]
|
#![deny(keyword_idents)]
|
||||||
|
#![deny(let_underscore_drop)]
|
||||||
#![deny(macro_use_extern_crate)]
|
#![deny(macro_use_extern_crate)]
|
||||||
|
#![deny(meta_variable_misuse)]
|
||||||
|
#![deny(missing_abi)]
|
||||||
|
#![deny(missing_copy_implementations)]
|
||||||
#![deny(missing_debug_implementations)]
|
#![deny(missing_debug_implementations)]
|
||||||
// #![deny(missing_docs)] TODO
|
// TODO
|
||||||
#![deny(rustdoc::missing_doc_code_examples)]
|
// #![deny(missing_docs)]
|
||||||
#![deny(non_ascii_idents)]
|
#![deny(non_ascii_idents)]
|
||||||
|
#![deny(noop_method_call)]
|
||||||
#![deny(pointer_structural_match)]
|
#![deny(pointer_structural_match)]
|
||||||
|
#![deny(rust_2021_incompatible_closure_captures)]
|
||||||
|
#![deny(rust_2021_incompatible_or_patterns)]
|
||||||
|
#![deny(rust_2021_prefixes_incompatible_syntax)]
|
||||||
|
#![deny(rust_2021_prelude_collisions)]
|
||||||
|
// Necessary for skeleton code.
|
||||||
// #![deny(single_use_lifetimes)]
|
// #![deny(single_use_lifetimes)]
|
||||||
|
#![deny(trivial_casts)]
|
||||||
#![deny(trivial_numeric_casts)]
|
#![deny(trivial_numeric_casts)]
|
||||||
#![deny(unaligned_references)]
|
// Necessary for skeleton code.
|
||||||
// #![deny(unreachable_pub)]
|
// #![deny(unreachable_pub)]
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(unsafe_op_in_unsafe_fn)]
|
||||||
#![deny(unstable_features)]
|
#![deny(unstable_features)]
|
||||||
// Necessary for `build-bin` trick.
|
// Necessary for `build-bin` trick.
|
||||||
// #![deny(unused_crate_dependencies)]
|
// #![deny(unused_crate_dependencies)]
|
||||||
#![deny(unused_extern_crates)]
|
#![deny(unused_extern_crates)]
|
||||||
#![deny(unused_import_braces)]
|
#![deny(unused_import_braces)]
|
||||||
#![deny(unused_lifetimes)]
|
#![deny(unused_lifetimes)]
|
||||||
|
#![deny(unused_macro_rules)]
|
||||||
#![deny(unused_qualifications)]
|
#![deny(unused_qualifications)]
|
||||||
#![deny(unused_results)]
|
#![deny(unused_results)]
|
||||||
|
#![deny(unused_tuple_struct_fields)]
|
||||||
|
// Allowed for more flexible variants.
|
||||||
// #![deny(variant_size_differences)]
|
// #![deny(variant_size_differences)]
|
||||||
#![deny(rust_2018_idioms)]
|
|
||||||
#![deny(rustdoc::all)]
|
|
||||||
// Necessary for skeleton code.
|
|
||||||
#![allow(unreachable_code)]
|
|
||||||
// Necessary to allow `iter.fold(false, |l, r| l || r)`. It's used when iteration should not be
|
|
||||||
// short-circuited.
|
|
||||||
#![allow(clippy::unnecessary_fold)]
|
|
||||||
#![allow(clippy::result_unit_err)]
|
|
||||||
#![allow(clippy::vec_init_then_push)]
|
|
||||||
#![allow(clippy::collapsible_match)]
|
|
||||||
|
|
||||||
mod tests;
|
mod tests;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::*;
|
|||||||
|
|
||||||
pub type Deadcode = FunctionPass<Repeat<DeadcodeInner>>;
|
pub type Deadcode = FunctionPass<Repeat<DeadcodeInner>>;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct DeadcodeInner {}
|
pub struct DeadcodeInner {}
|
||||||
|
|
||||||
impl Optimize<FunctionDefinition> for DeadcodeInner {
|
impl Optimize<FunctionDefinition> for DeadcodeInner {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::*;
|
|||||||
|
|
||||||
pub type Gvn = FunctionPass<GvnInner>;
|
pub type Gvn = FunctionPass<GvnInner>;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct GvnInner {}
|
pub struct GvnInner {}
|
||||||
|
|
||||||
impl Optimize<ir::FunctionDefinition> for GvnInner {
|
impl Optimize<ir::FunctionDefinition> for GvnInner {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::*;
|
|||||||
|
|
||||||
pub type Mem2reg = FunctionPass<Mem2regInner>;
|
pub type Mem2reg = FunctionPass<Mem2regInner>;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct Mem2regInner {}
|
pub struct Mem2regInner {}
|
||||||
|
|
||||||
impl Optimize<FunctionDefinition> for Mem2regInner {
|
impl Optimize<FunctionDefinition> for Mem2regInner {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ pub trait Optimize<T> {
|
|||||||
pub type O0 = Null;
|
pub type O0 = Null;
|
||||||
pub type O1 = Repeat<(SimplifyCfg, (Mem2reg, (Gvn, Deadcode)))>;
|
pub type O1 = Repeat<(SimplifyCfg, (Mem2reg, (Gvn, Deadcode)))>;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct Null {}
|
pub struct Null {}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
@@ -66,9 +66,9 @@ where
|
|||||||
{
|
{
|
||||||
fn optimize(&mut self, code: &mut ir::TranslationUnit) -> bool {
|
fn optimize(&mut self, code: &mut ir::TranslationUnit) -> bool {
|
||||||
code.decls
|
code.decls
|
||||||
.iter_mut()
|
.values_mut()
|
||||||
.map(|(_, decl)| self.optimize(decl))
|
.map(|decl| self.optimize(decl))
|
||||||
.fold(false, |l, r| l || r)
|
.fold(false, |l, r| l | r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,19 +10,19 @@ pub type SimplifyCfg = FunctionPass<
|
|||||||
>;
|
>;
|
||||||
|
|
||||||
/// Simplifies block exits by propagating constants.
|
/// Simplifies block exits by propagating constants.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct SimplifyCfgConstProp {}
|
pub struct SimplifyCfgConstProp {}
|
||||||
|
|
||||||
/// Retains only those blocks that are reachable from the init.
|
/// Retains only those blocks that are reachable from the init.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct SimplifyCfgReach {}
|
pub struct SimplifyCfgReach {}
|
||||||
|
|
||||||
/// Merges two blocks if a block is pointed to only by another
|
/// Merges two blocks if a block is pointed to only by another
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct SimplifyCfgMerge {}
|
pub struct SimplifyCfgMerge {}
|
||||||
|
|
||||||
/// Removes empty blocks
|
/// Removes empty blocks
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct SimplifyCfgEmpty {}
|
pub struct SimplifyCfgEmpty {}
|
||||||
|
|
||||||
impl Optimize<FunctionDefinition> for SimplifyCfgConstProp {
|
impl Optimize<FunctionDefinition> for SimplifyCfgConstProp {
|
||||||
|
|||||||
47
src/tests.rs
47
src/tests.rs
@@ -28,7 +28,7 @@ fn ast_initializer(number: i32) -> ast::Initializer {
|
|||||||
let expr = ast::Expression::Constant(Box::new(span::Node::new(
|
let expr = ast::Expression::Constant(Box::new(span::Node::new(
|
||||||
ast::Constant::Integer(ast::Integer {
|
ast::Constant::Integer(ast::Integer {
|
||||||
base: ast::IntegerBase::Decimal,
|
base: ast::IntegerBase::Decimal,
|
||||||
number: Box::from(&number.to_string() as &str),
|
number: Box::from(number.to_string()),
|
||||||
suffix: ast::IntegerSuffix {
|
suffix: ast::IntegerSuffix {
|
||||||
size: ast::IntegerSize::Int,
|
size: ast::IntegerSize::Int,
|
||||||
unsigned: false,
|
unsigned: false,
|
||||||
@@ -125,7 +125,7 @@ pub fn test_irgen(path: &Path) {
|
|||||||
|
|
||||||
// Compile c file: If fails, test is vacuously success
|
// Compile c file: If fails, test is vacuously success
|
||||||
if !Command::new("clang")
|
if !Command::new("clang")
|
||||||
.args(&[
|
.args([
|
||||||
"-fsanitize=float-divide-by-zero",
|
"-fsanitize=float-divide-by-zero",
|
||||||
"-fsanitize=undefined",
|
"-fsanitize=undefined",
|
||||||
"-fno-sanitize-recover=all",
|
"-fno-sanitize-recover=all",
|
||||||
@@ -149,7 +149,7 @@ pub fn test_irgen(path: &Path) {
|
|||||||
|
|
||||||
let status = some_or!(
|
let status = some_or!(
|
||||||
child
|
child
|
||||||
.wait_timeout_ms(500)
|
.wait_timeout_ms(1000)
|
||||||
.expect("failed to obtain exit status from child process"),
|
.expect("failed to obtain exit status from child process"),
|
||||||
{
|
{
|
||||||
println!("timeout occurs");
|
println!("timeout occurs");
|
||||||
@@ -177,15 +177,15 @@ pub fn test_irgen(path: &Path) {
|
|||||||
// Interpret resolved ir
|
// Interpret resolved ir
|
||||||
let args = Vec::new();
|
let args = Vec::new();
|
||||||
let result = ir::interp(&ir, args).unwrap_or_else(|interp_error| panic!("{}", interp_error));
|
let result = ir::interp(&ir, args).unwrap_or_else(|interp_error| panic!("{}", interp_error));
|
||||||
// We only allow main function whose return type is `int`
|
// We only allow a main function whose return type is `int`
|
||||||
let (value, width, is_signed) = result.get_int().expect("non-integer value occurs");
|
let (value, width, is_signed) = result.get_int().expect("non-integer value occurs");
|
||||||
assert_eq!(width, 32);
|
assert_eq!(width, 32);
|
||||||
assert!(is_signed);
|
assert!(is_signed);
|
||||||
|
|
||||||
// When obtain status from `clang` executable process, value is truncated to byte size.
|
// When obtaining status from `clang` executable process, the status value is truncated to byte
|
||||||
// For this reason, we make `fuzzer` generate the C source code which returns value
|
// size. For this reason, we make `fuzzer` generate the C source code which returns values
|
||||||
// typecasted to `unsigned char`. However, during `creduce` reduce the code, typecasting
|
// typecasted to `unsigned char`. However, during `creduce` to reduce the code, typecasting may
|
||||||
// may be deleted. So, we truncate result value to byte size one more time here.
|
// be nullified. So, we truncate the result value to byte size one more time here.
|
||||||
println!("clang: {}, kecc: {}", status as u8, value as u8);
|
println!("clang: {}, kecc: {}", status as u8, value as u8);
|
||||||
assert_eq!(status as u8, value as u8);
|
assert_eq!(status as u8, value as u8);
|
||||||
}
|
}
|
||||||
@@ -199,7 +199,7 @@ pub fn test_irparse(path: &Path) {
|
|||||||
.unwrap_or_else(|_| panic!("parse failed {}", path.display()));
|
.unwrap_or_else(|_| panic!("parse failed {}", path.display()));
|
||||||
|
|
||||||
// Test parse
|
// Test parse
|
||||||
let _ = c::Parse::default()
|
let _unused = c::Parse::default()
|
||||||
.translate(&path)
|
.translate(&path)
|
||||||
.expect("failed to parse the given program");
|
.expect("failed to parse the given program");
|
||||||
|
|
||||||
@@ -226,7 +226,8 @@ pub fn test_irparse(path: &Path) {
|
|||||||
test_irparse_for_optimized_ir(ir1, &temp_dir.path().join("ir2.ir"), Mem2reg::default());
|
test_irparse_for_optimized_ir(ir1, &temp_dir.path().join("ir2.ir"), Mem2reg::default());
|
||||||
let ir3 =
|
let ir3 =
|
||||||
test_irparse_for_optimized_ir(ir2, &temp_dir.path().join("ir3.ir"), Deadcode::default());
|
test_irparse_for_optimized_ir(ir2, &temp_dir.path().join("ir3.ir"), Deadcode::default());
|
||||||
let _ = test_irparse_for_optimized_ir(ir3, &temp_dir.path().join("ir4.ir"), Gvn::default());
|
let _unused =
|
||||||
|
test_irparse_for_optimized_ir(ir3, &temp_dir.path().join("ir4.ir"), Gvn::default());
|
||||||
|
|
||||||
temp_dir.close().expect("temp dir deletion failed");
|
temp_dir.close().expect("temp dir deletion failed");
|
||||||
}
|
}
|
||||||
@@ -327,7 +328,7 @@ pub fn test_asmgen(path: &Path) {
|
|||||||
|
|
||||||
// Compile the assembly code
|
// Compile the assembly code
|
||||||
if !Command::new("riscv64-linux-gnu-gcc")
|
if !Command::new("riscv64-linux-gnu-gcc")
|
||||||
.args(&["-static", &asm_path_str, "-o", &bin_path_str])
|
.args(["-static", &asm_path_str, "-o", &bin_path_str])
|
||||||
.stderr(Stdio::null())
|
.stderr(Stdio::null())
|
||||||
.status()
|
.status()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -338,14 +339,14 @@ pub fn test_asmgen(path: &Path) {
|
|||||||
|
|
||||||
// Emulate the executable
|
// Emulate the executable
|
||||||
let mut child = Command::new("qemu-riscv64-static")
|
let mut child = Command::new("qemu-riscv64-static")
|
||||||
.args(&[&bin_path_str])
|
.args([&bin_path_str])
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("failed to execute the compiled executable");
|
.expect("failed to execute the compiled executable");
|
||||||
|
|
||||||
let status = some_or!(
|
let status = some_or!(
|
||||||
child
|
child
|
||||||
.wait_timeout_ms(500)
|
.wait_timeout_ms(1000)
|
||||||
.expect("failed to obtain exit status from child process"),
|
.expect("failed to obtain exit status from child process"),
|
||||||
{
|
{
|
||||||
println!("timeout occurs");
|
println!("timeout occurs");
|
||||||
@@ -393,7 +394,7 @@ pub fn test_end_to_end(path: &Path) {
|
|||||||
|
|
||||||
// Compile c file: If fails, test is vacuously success
|
// Compile c file: If fails, test is vacuously success
|
||||||
if !Command::new("clang")
|
if !Command::new("clang")
|
||||||
.args(&[
|
.args([
|
||||||
"-fsanitize=float-divide-by-zero",
|
"-fsanitize=float-divide-by-zero",
|
||||||
"-fsanitize=undefined",
|
"-fsanitize=undefined",
|
||||||
"-fno-sanitize-recover=all",
|
"-fno-sanitize-recover=all",
|
||||||
@@ -422,7 +423,7 @@ pub fn test_end_to_end(path: &Path) {
|
|||||||
|
|
||||||
let status = some_or!(
|
let status = some_or!(
|
||||||
child
|
child
|
||||||
.wait_timeout_ms(500)
|
.wait_timeout_ms(1000)
|
||||||
.expect("failed to obtain exit status from child process"),
|
.expect("failed to obtain exit status from child process"),
|
||||||
{
|
{
|
||||||
println!("timeout occurs");
|
println!("timeout occurs");
|
||||||
@@ -452,15 +453,15 @@ pub fn test_end_to_end(path: &Path) {
|
|||||||
let _ = O1::default().optimize(&mut ir);
|
let _ = O1::default().optimize(&mut ir);
|
||||||
let args = Vec::new();
|
let args = Vec::new();
|
||||||
let result = ir::interp(&ir, args).unwrap_or_else(|interp_error| panic!("{}", interp_error));
|
let result = ir::interp(&ir, args).unwrap_or_else(|interp_error| panic!("{}", interp_error));
|
||||||
// We only allow main function whose return type is `int`
|
// We only allow a main function whose return type is `int`
|
||||||
let (value, width, is_signed) = result.get_int().expect("non-integer value occurs");
|
let (value, width, is_signed) = result.get_int().expect("non-integer value occurs");
|
||||||
assert_eq!(width, 32);
|
assert_eq!(width, 32);
|
||||||
assert!(is_signed);
|
assert!(is_signed);
|
||||||
|
|
||||||
// When obtain status from `clang` executable process, value is truncated to byte size.
|
// When obtaining status from `clang` executable process, the status value is truncated to byte
|
||||||
// For this reason, we make `fuzzer` generate the C source code which returns value
|
// size. For this reason, we make `fuzzer` generate the C source code which returns values
|
||||||
// typecasted to `unsigned char`. However, during `creduce` reduce the code, typecasting
|
// typecasted to `unsigned char`. However, during `creduce` to reduce the code, typecasting may
|
||||||
// may be deleted. So, we truncate result value to byte size one more time here.
|
// be nullified. So, we truncate the result value to byte size one more time here.
|
||||||
println!(
|
println!(
|
||||||
"clang: {}, kecc interp: {}",
|
"clang: {}, kecc interp: {}",
|
||||||
clang_status as u8, value as u8
|
clang_status as u8, value as u8
|
||||||
@@ -487,7 +488,7 @@ pub fn test_end_to_end(path: &Path) {
|
|||||||
|
|
||||||
// Compile the assembly code
|
// Compile the assembly code
|
||||||
if !Command::new("riscv64-linux-gnu-gcc")
|
if !Command::new("riscv64-linux-gnu-gcc")
|
||||||
.args(&["-static", &asm_path_str, "-o", &bin_path_str])
|
.args(["-static", &asm_path_str, "-o", &bin_path_str])
|
||||||
.stderr(Stdio::null())
|
.stderr(Stdio::null())
|
||||||
.status()
|
.status()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -498,14 +499,14 @@ pub fn test_end_to_end(path: &Path) {
|
|||||||
|
|
||||||
// Emulate the executable
|
// Emulate the executable
|
||||||
let mut child = Command::new("qemu-riscv64-static")
|
let mut child = Command::new("qemu-riscv64-static")
|
||||||
.args(&[&bin_path_str])
|
.args([&bin_path_str])
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("failed to execute the compiled executable");
|
.expect("failed to execute the compiled executable");
|
||||||
|
|
||||||
let status = some_or!(
|
let status = some_or!(
|
||||||
child
|
child
|
||||||
.wait_timeout_ms(500)
|
.wait_timeout_ms(1000)
|
||||||
.expect("failed to obtain exit status from child process"),
|
.expect("failed to obtain exit status from child process"),
|
||||||
{
|
{
|
||||||
println!("timeout occurs");
|
println!("timeout occurs");
|
||||||
|
|||||||
@@ -205,9 +205,9 @@ fn test_examples_asmgen_small() {
|
|||||||
test_dir(Path::new(dir), OsStr::new("ir"), |path| {
|
test_dir(Path::new(dir), OsStr::new("ir"), |path| {
|
||||||
let file_name = &path
|
let file_name = &path
|
||||||
.file_name()
|
.file_name()
|
||||||
.expect("`path` must have file name")
|
.expect("`path` must have a file name")
|
||||||
.to_str()
|
.to_str()
|
||||||
.expect("must be transformed to `&str`");
|
.expect("must be transformable to `&str`");
|
||||||
if !ASMGEN_SMALL_TEST_IGNORE_LIST.contains(file_name) {
|
if !ASMGEN_SMALL_TEST_IGNORE_LIST.contains(file_name) {
|
||||||
test_asmgen(path)
|
test_asmgen(path)
|
||||||
}
|
}
|
||||||
@@ -221,9 +221,9 @@ fn test_examples_asmgen_large() {
|
|||||||
test_dir(Path::new(dir), OsStr::new("ir"), |path| {
|
test_dir(Path::new(dir), OsStr::new("ir"), |path| {
|
||||||
let file_name = &path
|
let file_name = &path
|
||||||
.file_name()
|
.file_name()
|
||||||
.expect("`path` must have file name")
|
.expect("`path` must have a file name")
|
||||||
.to_str()
|
.to_str()
|
||||||
.expect("must be transformed to `&str`");
|
.expect("must be transformable to `&str`");
|
||||||
if ASMGEN_SMALL_TEST_IGNORE_LIST.contains(file_name) {
|
if ASMGEN_SMALL_TEST_IGNORE_LIST.contains(file_name) {
|
||||||
test_asmgen(path)
|
test_asmgen(path)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user