mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-14 22:38:46 +00:00
Bump Rust and add skeleton code for irgen.
This commit is contained in:
40
Cargo.lock
generated
40
Cargo.lock
generated
@@ -16,9 +16,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.77"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -195,15 +195,15 @@ checksum = "28a07abd9aba69140d1bb484d9c05119fe2f834f9e2112f93bd61dff4fed62f6"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.137"
|
||||
version = "0.2.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
|
||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
@@ -294,18 +294,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
checksum = "e9d89e5dba24725ae5678020bf8f1357a9aa7ff10736b551adbcd3f8d17d766f"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
checksum = "556d0f47a940e895261e77dc200d5eadfc6ef644c179c6f5edfc105e3a2292c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -360,9 +360,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.4"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23"
|
||||
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -380,9 +380,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.104"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce"
|
||||
checksum = "09ee3a69cd2c7e06684677e5629b3878b253af05e4714964204279c6bc02cf0b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -414,18 +414,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -434,9 +434,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
|
||||
@@ -29,8 +29,8 @@ required-features = ["build-bin"]
|
||||
build-bin = ["clap"]
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.0.22", features = ["derive"], optional = true }
|
||||
thiserror = "1.0.37"
|
||||
clap = { version = "4.0.29", features = ["derive"], optional = true }
|
||||
thiserror = "1.0.38"
|
||||
lang-c = "0.14.0"
|
||||
itertools = "0.10.5"
|
||||
tempfile = "3.3.0"
|
||||
|
||||
@@ -12,6 +12,12 @@ cargo build # debug build
|
||||
cargo build --release # release build
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
```sh
|
||||
cargo doc --document-private-items # built in target/doc
|
||||
cargo doc --open --document-private-items # opens in default broswer. Firefox or Chrome recommended.
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
|
||||
10
bin/kecc.rs
10
bin/kecc.rs
@@ -97,7 +97,7 @@ fn main() {
|
||||
let mut input = ok_or_exit!(IrParse::default().translate(&input), 1);
|
||||
compile_ir(&mut input, &mut output, &matches);
|
||||
} else {
|
||||
panic!("Unsupported file extension: {:?}", ext);
|
||||
panic!("Unsupported file extension: {ext:?}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ fn compile_c(input: &TranslationUnit, output: &mut dyn ::std::io::Write, matches
|
||||
let mut ir = match Irgen::default().translate(input) {
|
||||
Ok(ir) => ir,
|
||||
Err(irgen_error) => {
|
||||
println!("{}", irgen_error);
|
||||
println!("{irgen_error}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -159,16 +159,16 @@ fn compile_ir(
|
||||
// Create the dot file
|
||||
let mut buffer =
|
||||
::std::fs::File::create(dot_path.as_path()).expect("need to success creating file");
|
||||
buffer
|
||||
let _ = buffer
|
||||
.write(dot.as_bytes())
|
||||
.expect("failed to write to dot file");
|
||||
|
||||
// Create the image file
|
||||
let img = ::std::fs::File::create(&img_path_str).expect("need to success creating file");
|
||||
let img = ::std::fs::File::create(img_path_str).expect("need to success creating file");
|
||||
|
||||
// Translate dot file into image
|
||||
if !Command::new("dot")
|
||||
.args(&["-Tpng", &dot_path_str])
|
||||
.args(["-Tpng", &dot_path_str])
|
||||
.stdout(unsafe { Stdio::from_raw_fd(img.into_raw_fd()) })
|
||||
.status()
|
||||
.unwrap()
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.65.0
|
||||
1.66.0
|
||||
|
||||
@@ -8,4 +8,4 @@ cargo fmt --all -- --check # run `cargo fmt` to auto-correct format.
|
||||
cargo clippy
|
||||
|
||||
# Run tests.
|
||||
RUST_MIN_STACK=33554432 cargo test --release test_examples_irgen_small --nocapture
|
||||
RUST_MIN_STACK=33554432 cargo test --release test_examples_irgen_small -- --nocapture
|
||||
|
||||
@@ -117,17 +117,17 @@ impl Directive {
|
||||
impl fmt::Display for Directive {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Align(value) => write!(f, ".align\t{}", value),
|
||||
Self::Globl(label) => write!(f, ".globl\t{}", label),
|
||||
Self::Align(value) => write!(f, ".align\t{value}"),
|
||||
Self::Globl(label) => write!(f, ".globl\t{label}"),
|
||||
Self::Type(symbol, symbol_type) => {
|
||||
write!(f, ".type\t{}, {}", symbol, symbol_type)
|
||||
write!(f, ".type\t{symbol}, {symbol_type}")
|
||||
}
|
||||
Self::Section(section_type) => write!(f, ".section\t{}", section_type),
|
||||
Self::Byte(value) => write!(f, ".byte\t{:#x?}", value),
|
||||
Self::Half(value) => write!(f, ".half\t{:#x?}", value),
|
||||
Self::Word(value) => write!(f, ".word\t{:#x?}", value),
|
||||
Self::Quad(value) => write!(f, ".quad\t{:#x?}", value),
|
||||
Self::Zero(bytes) => write!(f, ".zero\t{:#x?}", bytes),
|
||||
Self::Section(section_type) => write!(f, ".section\t{section_type}"),
|
||||
Self::Byte(value) => write!(f, ".byte\t{value:#x?}"),
|
||||
Self::Half(value) => write!(f, ".half\t{value:#x?}"),
|
||||
Self::Word(value) => write!(f, ".word\t{value:#x?}"),
|
||||
Self::Quad(value) => write!(f, ".quad\t{value:#x?}"),
|
||||
Self::Zero(bytes) => write!(f, ".zero\t{bytes:#x?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,7 @@ impl fmt::Display for Instruction {
|
||||
rd,
|
||||
rs1,
|
||||
if let Some(rs2) = rs2 {
|
||||
format!(",{}", rs2)
|
||||
format!(",{rs2}")
|
||||
} else {
|
||||
"".to_string()
|
||||
},
|
||||
@@ -257,9 +257,9 @@ impl fmt::Display for Instruction {
|
||||
imm,
|
||||
} => {
|
||||
if let IType::Load { .. } = instr {
|
||||
write!(f, "{}\t{},{}({})", instr, rd, imm, rs1)
|
||||
write!(f, "{instr}\t{rd},{imm}({rs1})")
|
||||
} else {
|
||||
write!(f, "{}\t{},{},{}", instr, rd, rs1, imm,)
|
||||
write!(f, "{instr}\t{rd},{rs1},{imm}",)
|
||||
}
|
||||
}
|
||||
Self::SType {
|
||||
@@ -267,15 +267,15 @@ impl fmt::Display for Instruction {
|
||||
rs1,
|
||||
rs2,
|
||||
imm,
|
||||
} => write!(f, "{}\t{},{}({})", instr, rs2, imm, rs1),
|
||||
} => write!(f, "{instr}\t{rs2},{imm}({rs1})"),
|
||||
Self::BType {
|
||||
instr,
|
||||
rs1,
|
||||
rs2,
|
||||
imm,
|
||||
} => write!(f, "{}\t{},{}, {}", instr, rs1, rs2, imm.0,),
|
||||
Self::UType { instr, rd, imm } => write!(f, "{}\t{}, {}", instr, rd, imm,),
|
||||
Self::Pseudo(pseudo) => write!(f, "{}", pseudo),
|
||||
Self::UType { instr, rd, imm } => write!(f, "{instr}\t{rd}, {imm}",),
|
||||
Self::Pseudo(pseudo) => write!(f, "{pseudo}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -541,12 +541,12 @@ impl fmt::Display for RType {
|
||||
Self::Xor => write!(f, "xor"),
|
||||
Self::Or => write!(f, "or"),
|
||||
Self::And => write!(f, "and"),
|
||||
Self::Fadd(data_size) => write!(f, "fadd.{}", data_size),
|
||||
Self::Fsub(data_size) => write!(f, "fsub.{}", data_size),
|
||||
Self::Fmul(data_size) => write!(f, "fmul.{}", data_size),
|
||||
Self::Fdiv(data_size) => write!(f, "fdiv.{}", data_size),
|
||||
Self::Feq(data_size) => write!(f, "feq.{}", data_size),
|
||||
Self::Flt(data_size) => write!(f, "flt.{}", data_size),
|
||||
Self::Fadd(data_size) => write!(f, "fadd.{data_size}"),
|
||||
Self::Fsub(data_size) => write!(f, "fsub.{data_size}"),
|
||||
Self::Fmul(data_size) => write!(f, "fmul.{data_size}"),
|
||||
Self::Fdiv(data_size) => write!(f, "fdiv.{data_size}"),
|
||||
Self::Feq(data_size) => write!(f, "feq.{data_size}"),
|
||||
Self::Flt(data_size) => write!(f, "flt.{data_size}"),
|
||||
Self::FmvFloatToInt { float_data_size } => {
|
||||
assert!(float_data_size.is_floating_point());
|
||||
write!(
|
||||
@@ -612,7 +612,7 @@ impl fmt::Display for RType {
|
||||
Self::FcvtFloatToFloat { from, to } => {
|
||||
assert!(from.is_floating_point());
|
||||
assert!(to.is_floating_point());
|
||||
write!(f, "fcvt.{}.{}", to, from)
|
||||
write!(f, "fcvt.{to}.{from}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -748,7 +748,7 @@ impl fmt::Display for SType {
|
||||
match self {
|
||||
Self::Store(data_size) => {
|
||||
if data_size.is_integer() {
|
||||
write!(f, "s{}", data_size)
|
||||
write!(f, "s{data_size}")
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
@@ -877,24 +877,22 @@ impl Pseudo {
|
||||
impl fmt::Display for Pseudo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::La { rd, symbol } => write!(f, "la\t{},{}", rd, symbol),
|
||||
Self::La { rd, symbol } => write!(f, "la\t{rd},{symbol}"),
|
||||
Self::Li { rd, imm } => write!(f, "li\t{},{}", rd, *imm as i64),
|
||||
Self::Mv { rd, rs } => write!(f, "mv\t{},{}", rd, rs),
|
||||
Self::Fmv { data_size, rd, rs } => write!(f, "fmv.{}\t{},{}", data_size, rd, rs),
|
||||
Self::Mv { rd, rs } => write!(f, "mv\t{rd},{rs}"),
|
||||
Self::Fmv { data_size, rd, rs } => write!(f, "fmv.{data_size}\t{rd},{rs}"),
|
||||
Self::Neg { data_size, rd, rs } => {
|
||||
write!(f, "neg{}\t{},{}", data_size.word().write_string(), rd, rs)
|
||||
}
|
||||
Self::SextW { rs, rd } => {
|
||||
write!(f, "sext.w\t{},{}", rd, rs)
|
||||
}
|
||||
Self::Seqz { rd, rs } => write!(f, "seqz\t{},{}", rd, rs),
|
||||
Self::Snez { rd, rs } => write!(f, "snez\t{},{}", rd, rs),
|
||||
Self::Fneg { data_size, rd, rs } => write!(f, "fneg.{}\t{},{}", data_size, rd, rs),
|
||||
Self::J { offset } => write!(f, "j\t{}", offset),
|
||||
Self::Jr { rs } => write!(f, "jr\t{}", rs),
|
||||
Self::Jalr { rs } => write!(f, "jalr\t{}", rs),
|
||||
Self::SextW { rs, rd } => write!(f, "sext.w\t{rd},{rs}"),
|
||||
Self::Seqz { rd, rs } => write!(f, "seqz\t{rd},{rs}"),
|
||||
Self::Snez { rd, rs } => write!(f, "snez\t{rd},{rs}"),
|
||||
Self::Fneg { data_size, rd, rs } => write!(f, "fneg.{data_size}\t{rd},{rs}"),
|
||||
Self::J { offset } => write!(f, "j\t{offset}"),
|
||||
Self::Jr { rs } => write!(f, "jr\t{rs}"),
|
||||
Self::Jalr { rs } => write!(f, "jalr\t{rs}"),
|
||||
Self::Ret => write!(f, "ret"),
|
||||
Self::Call { offset } => write!(f, "call\t{}", offset),
|
||||
Self::Call { offset } => write!(f, "call\t{offset}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -924,7 +922,7 @@ impl fmt::Display for Immediate {
|
||||
match self {
|
||||
Self::Value(value) => format!("{}", *value as i64),
|
||||
Self::Relocation { relocation, symbol } => {
|
||||
format!("{}({})", relocation, symbol)
|
||||
format!("{relocation}({symbol})")
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -965,7 +963,7 @@ pub struct Label(pub String);
|
||||
impl Label {
|
||||
pub fn new(name: &str, block_id: ir::BlockId) -> Self {
|
||||
let id = block_id.0;
|
||||
Self(format!(".{}_L{}", name, id))
|
||||
Self(format!(".{name}_L{id}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1164,11 +1162,11 @@ impl fmt::Display for Register {
|
||||
Self::Sp => "sp".to_string(),
|
||||
Self::Gp => "gp".to_string(),
|
||||
Self::Tp => "tp".to_string(),
|
||||
Self::Temp(register_type, id) => format!("{}t{}", register_type, id),
|
||||
Self::Saved(register_type, id) => format!("{}s{}", register_type, id),
|
||||
Self::Arg(register_type, id) => format!("{}a{}", register_type, id),
|
||||
Self::Temp(register_type, id) => format!("{register_type}t{id}"),
|
||||
Self::Saved(register_type, id) => format!("{register_type}s{id}"),
|
||||
Self::Arg(register_type, id) => format!("{register_type}a{id}"),
|
||||
};
|
||||
write!(f, "{}", register)
|
||||
write!(f, "{register}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ impl<T: WriteLine> WriteLine for Section<T> {
|
||||
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
|
||||
for directive in &self.header {
|
||||
write_indent(indent + INDENT, write)?;
|
||||
writeln!(write, "{}", directive)?;
|
||||
writeln!(write, "{directive}")?;
|
||||
}
|
||||
self.body.write_line(indent, write)?;
|
||||
|
||||
@@ -52,7 +52,7 @@ impl WriteLine for Variable {
|
||||
writeln!(write, "{}:", self.label.0)?;
|
||||
for directive in &self.directives {
|
||||
write_indent(indent + INDENT, write)?;
|
||||
writeln!(write, "{}", directive)?;
|
||||
writeln!(write, "{directive}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -67,7 +67,7 @@ impl WriteLine for Block {
|
||||
|
||||
for instruction in &self.instructions {
|
||||
write_indent(indent + INDENT, write)?;
|
||||
writeln!(write, "{}", instruction)?;
|
||||
writeln!(write, "{instruction}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -76,84 +76,84 @@ impl WriteLine for Block {
|
||||
|
||||
impl WriteString for Directive {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for SectionType {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for SymbolType {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for Instruction {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for RType {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for IType {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for SType {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for BType {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for UType {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for Pseudo {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for Immediate {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for RelocationFunction {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for DataSize {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for Register {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,6 @@ impl Translate<ir::TranslationUnit> for Asmgen {
|
||||
type Error = ();
|
||||
|
||||
fn translate(&mut self, _source: &ir::TranslationUnit) -> Result<Self::Target, Self::Error> {
|
||||
todo!("homework 7")
|
||||
todo!("Homework: Assembly Generation")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,9 +527,8 @@ pub fn assert_ast_equiv(lhs: &TranslationUnit, rhs: &TranslationUnit) {
|
||||
if !lhs.is_equiv(rhs) {
|
||||
panic!(
|
||||
r#"assertion failed: `(left.is_equiv(right))`
|
||||
left: `{:?}`,
|
||||
right: `{:?}`"#,
|
||||
lhs, rhs
|
||||
left: `{lhs:?}`,
|
||||
right: `{rhs:?}`"#
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ pub enum Error {
|
||||
|
||||
/// TODO(document)
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct Parse {}
|
||||
pub struct Parse;
|
||||
|
||||
impl<P: AsRef<Path>> Translate<P> for Parse {
|
||||
type Target = TranslationUnit;
|
||||
|
||||
@@ -413,7 +413,7 @@ impl TryFrom<BaseDtype> for Dtype {
|
||||
ast::TypeSpecifier::Int => Self::INT,
|
||||
ast::TypeSpecifier::Float => Self::FLOAT,
|
||||
ast::TypeSpecifier::Double => Self::DOUBLE,
|
||||
_ => panic!("Dtype::try_from::<BaseDtype>: {:?} is not a scalar type", t),
|
||||
_ => panic!("Dtype::try_from::<BaseDtype>: {t:?} is not a scalar type"),
|
||||
}
|
||||
} else {
|
||||
Self::default()
|
||||
@@ -452,10 +452,9 @@ impl TryFrom<BaseDtype> for Dtype {
|
||||
let is_signed = match signed_option {
|
||||
ast::TypeSpecifier::Signed => true,
|
||||
ast::TypeSpecifier::Unsigned => false,
|
||||
_ => panic!(
|
||||
"Dtype::try_from::<BaseDtype>: {:?} is not a signed option",
|
||||
signed_option
|
||||
),
|
||||
_ => {
|
||||
panic!("Dtype::try_from::<BaseDtype>: {signed_option:?} is not a signed option")
|
||||
}
|
||||
};
|
||||
|
||||
if dtype.get_int_width().is_none() {
|
||||
@@ -941,7 +940,7 @@ impl Dtype {
|
||||
let struct_type = structs
|
||||
.get(name)
|
||||
.ok_or_else(|| DtypeError::Misc {
|
||||
message: format!("unknown struct name `{}`", name),
|
||||
message: format!("unknown struct name `{name}`"),
|
||||
})?
|
||||
.as_ref()
|
||||
.expect("`struct_type` must have its definition");
|
||||
@@ -1223,7 +1222,7 @@ impl Dtype {
|
||||
let dtype = typedefs
|
||||
.get(&name)
|
||||
.ok_or_else(|| DtypeError::Misc {
|
||||
message: format!("unknown type name `{}`", name),
|
||||
message: format!("unknown type name `{name}`"),
|
||||
})?
|
||||
.clone();
|
||||
let is_const = dtype.is_const() || is_const;
|
||||
@@ -1288,7 +1287,7 @@ impl Dtype {
|
||||
} else {
|
||||
let tempid = *tempid_counter;
|
||||
*tempid_counter += 1;
|
||||
format!("%t{}", tempid)
|
||||
format!("%t{tempid}")
|
||||
};
|
||||
let resolved_struct = Self::structure(Some(name.clone()), Some(fields));
|
||||
let filled_struct =
|
||||
@@ -1297,7 +1296,7 @@ impl Dtype {
|
||||
if let Some(prev_dtype) = structs.insert(name.clone(), Some(filled_struct)) {
|
||||
if prev_dtype.is_some() {
|
||||
return Err(DtypeError::Misc {
|
||||
message: format!("redefinition of {}", name),
|
||||
message: format!("redefinition of {name}"),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1306,11 +1305,11 @@ impl Dtype {
|
||||
} else {
|
||||
let name = name.expect("`name` must exist");
|
||||
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() {
|
||||
return Err(DtypeError::Misc {
|
||||
message: format!("variable has incomplete type 'struct {}'", name),
|
||||
message: format!("variable has incomplete type 'struct {name}'"),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1355,7 +1354,7 @@ impl fmt::Display for Dtype {
|
||||
Self::Pointer { inner, is_const } => {
|
||||
write!(f, "{}*{}", inner, if *is_const { "const" } else { "" })
|
||||
}
|
||||
Self::Array { inner, size, .. } => write!(f, "[{} x {}]", size, inner,),
|
||||
Self::Array { inner, size, .. } => write!(f, "[{size} x {inner}]",),
|
||||
Self::Struct {
|
||||
name,
|
||||
fields,
|
||||
@@ -1374,7 +1373,7 @@ impl fmt::Display for Dtype {
|
||||
field.deref()
|
||||
))
|
||||
});
|
||||
format!(":<{}>", fields)
|
||||
format!(":<{fields}>")
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
@@ -770,8 +770,7 @@ mod calculator {
|
||||
(false, Dtype::SIZE_OF_DOUBLE) => value as f64,
|
||||
_ => panic!(
|
||||
"calculate_typecast: not supported case \
|
||||
typecast int to float when `width` is {}",
|
||||
width
|
||||
typecast int to float when `width` is {width}"
|
||||
),
|
||||
};
|
||||
Ok(Value::float(casted_value, width))
|
||||
@@ -782,8 +781,7 @@ mod calculator {
|
||||
} else {
|
||||
panic!(
|
||||
"calculate_typecast: not support case \
|
||||
typecast int to pointer when `value` is {}",
|
||||
value
|
||||
typecast int to pointer when `value` is {value}"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1023,7 +1021,7 @@ impl Byte {
|
||||
let value_bits: u128 = match size {
|
||||
Dtype::SIZE_OF_FLOAT => (float_value.into_inner() as f32).to_bits() as u128,
|
||||
Dtype::SIZE_OF_DOUBLE => (float_value.into_inner()).to_bits() as u128,
|
||||
_ => panic!("value_to_bytes: {} is not a valid float size", size),
|
||||
_ => panic!("value_to_bytes: {size} is not a valid float size"),
|
||||
};
|
||||
|
||||
Self::u128_to_bytes(value_bits, size)
|
||||
@@ -1206,8 +1204,7 @@ impl<'i> State<'i> {
|
||||
func_name: self.stack_frame.func_name.clone(),
|
||||
pc: self.stack_frame.pc,
|
||||
msg: format!(
|
||||
"fail to translate `Initializer` and `{}` to `Value`",
|
||||
dtype
|
||||
"fail to translate `Initializer` and `{dtype}` to `Value`"
|
||||
),
|
||||
},
|
||||
)?
|
||||
@@ -1430,8 +1427,7 @@ impl<'i> State<'i> {
|
||||
func_name: self.stack_frame.func_name.clone(),
|
||||
pc: self.stack_frame.pc,
|
||||
msg: format!(
|
||||
"fail to store {:?} into memory with bid: {}, offset: {}",
|
||||
value, bid, offset,
|
||||
"fail to store {value:?} into memory with bid: {bid}, offset: {offset}",
|
||||
),
|
||||
})?;
|
||||
Value::Unit
|
||||
|
||||
@@ -313,25 +313,23 @@ impl fmt::Display for Instruction {
|
||||
Instruction::UnaryOp { op, operand, .. } => {
|
||||
write!(f, "{} {}", op.write_operation(), operand)
|
||||
}
|
||||
Instruction::Store { ptr, value } => {
|
||||
write!(f, "store {} {}", value, ptr)
|
||||
}
|
||||
Instruction::Load { ptr } => write!(f, "load {}", ptr),
|
||||
Instruction::Store { ptr, value } => write!(f, "store {value} {ptr}"),
|
||||
Instruction::Load { ptr } => write!(f, "load {ptr}"),
|
||||
Instruction::Call { callee, args, .. } => {
|
||||
write!(
|
||||
f,
|
||||
"call {}({})",
|
||||
callee,
|
||||
args.iter()
|
||||
.format_with(", ", |operand, f| f(&format_args!("{}", operand)))
|
||||
.format_with(", ", |operand, f| f(&format_args!("{operand}")))
|
||||
)
|
||||
}
|
||||
Instruction::TypeCast {
|
||||
value,
|
||||
target_dtype,
|
||||
} => write!(f, "typecast {} to {}", value, target_dtype),
|
||||
} => write!(f, "typecast {value} to {target_dtype}"),
|
||||
Instruction::GetElementPtr { ptr, offset, .. } => {
|
||||
write!(f, "getelementptr {} offset {}", ptr, offset)
|
||||
write!(f, "getelementptr {ptr} offset {offset}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -385,12 +383,12 @@ impl BlockExit {
|
||||
impl fmt::Display for BlockExit {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
BlockExit::Jump { arg } => write!(f, "j {}", arg),
|
||||
BlockExit::Jump { arg } => write!(f, "j {arg}"),
|
||||
BlockExit::ConditionalJump {
|
||||
condition,
|
||||
arg_then,
|
||||
arg_else,
|
||||
} => write!(f, "br {}, {}, {}", condition, arg_then, arg_else),
|
||||
} => write!(f, "br {condition}, {arg_then}, {arg_else}"),
|
||||
BlockExit::Switch {
|
||||
value,
|
||||
default,
|
||||
@@ -407,7 +405,7 @@ impl fmt::Display for BlockExit {
|
||||
b
|
||||
)))
|
||||
),
|
||||
BlockExit::Return { value } => write!(f, "ret {}", value),
|
||||
BlockExit::Return { value } => write!(f, "ret {value}"),
|
||||
BlockExit::Unreachable => write!(f, "<unreachable>\t\t\t\t; error state"),
|
||||
}
|
||||
}
|
||||
@@ -482,7 +480,7 @@ impl fmt::Display for Operand {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Constant(value) => write!(f, "{}:{}", value, value.dtype()),
|
||||
Self::Register { rid, dtype } => write!(f, "{}:{}", rid, dtype),
|
||||
Self::Register { rid, dtype } => write!(f, "{rid}:{dtype}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -548,9 +546,9 @@ impl RegisterId {
|
||||
impl fmt::Display for RegisterId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Local { aid } => write!(f, "%l{}", aid),
|
||||
Self::Arg { bid, aid } => write!(f, "%{}:p{}", bid, aid),
|
||||
Self::Temp { bid, iid } => write!(f, "%{}:i{}", bid, iid),
|
||||
Self::Local { aid } => write!(f, "%l{aid}"),
|
||||
Self::Arg { bid, aid } => write!(f, "%{bid}:p{aid}"),
|
||||
Self::Temp { bid, iid } => write!(f, "%{bid}:i{iid}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -678,8 +676,7 @@ impl TryFrom<&ast::Constant> for Constant {
|
||||
}
|
||||
_ => panic!(
|
||||
"Constant::try_from::<&ast::Constant>: \
|
||||
{:?} is not a pattern of `pat`",
|
||||
pat
|
||||
{pat:?} is not a pattern of `pat`"
|
||||
),
|
||||
};
|
||||
(Dtype::FLOAT, value)
|
||||
@@ -694,8 +691,7 @@ impl TryFrom<&ast::Constant> for Constant {
|
||||
}
|
||||
_ => panic!(
|
||||
"Constant::try_from::<&ast::Constant>: \
|
||||
{:?} is not a pattern of `pat`",
|
||||
pat
|
||||
{pat:?} is not a pattern of `pat`"
|
||||
),
|
||||
};
|
||||
(Dtype::DOUBLE, value)
|
||||
@@ -942,8 +938,8 @@ impl fmt::Display for Constant {
|
||||
value.to_string()
|
||||
}
|
||||
),
|
||||
Self::Float { value, .. } => write!(f, "{}", value),
|
||||
Self::GlobalVariable { name, .. } => write!(f, "@{}", name),
|
||||
Self::Float { value, .. } => write!(f, "{value}"),
|
||||
Self::GlobalVariable { name, .. } => write!(f, "@{name}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,8 +677,7 @@ impl<P: AsRef<Path>> Translate<P> for Parse {
|
||||
|
||||
fn translate(&mut self, source: &P) -> Result<Self::Target, Self::Error> {
|
||||
let ir = fs::read_to_string(source).map_err(Error::Io)?;
|
||||
let ir = ir_parse::translation_unit(&ir).map_err(Error::Parse)?;
|
||||
Ok(ir)
|
||||
ir_parse::translation_unit(&ir).map_err(Error::Parse)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ impl Translate<TranslationUnit> for Visualizer {
|
||||
let from = self.translate_instruction_node(name, *bid, iid);
|
||||
let to = self.translate_callee(name, callee)?;
|
||||
|
||||
edges.push(format!("{} -> {};", from, to));
|
||||
edges.push(format!("{from} -> {to};"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ impl Translate<TranslationUnit> for Visualizer {
|
||||
|
||||
let inner = vec![subgraphs, edges].concat().join("\n");
|
||||
|
||||
Ok(format!("digraph G {{\n{}\n}}", inner))
|
||||
Ok(format!("digraph G {{\n{inner}\n}}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,12 +82,12 @@ impl Visualizer {
|
||||
|
||||
#[inline]
|
||||
fn translate_instruction_node(&self, name: &str, bid: BlockId, iid: usize) -> String {
|
||||
format!("\"{}:{}:i{}\"", name, bid, iid)
|
||||
format!("\"{name}:{bid}:i{iid}\"")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn translate_block_exit_node(&self, name: &str, bid: BlockId) -> String {
|
||||
format!("\"{}:{}:exit\"", name, bid)
|
||||
format!("\"{name}:{bid}:exit\"")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -178,7 +178,7 @@ impl Visualizer {
|
||||
// TODO: Add init information (bid_init, allocations)
|
||||
let inner = vec![subgraphs, vec![label], edges].concat().join("\n");
|
||||
|
||||
Ok(format!("subgraph \"cluster.{}\" {{\n{}\n}}", name, inner))
|
||||
Ok(format!("subgraph \"cluster.{name}\" {{\n{inner}\n}}"))
|
||||
}
|
||||
|
||||
fn translate_block(&mut self, name: &str, bid: &BlockId, block: &Block) -> Result<String, ()> {
|
||||
@@ -186,7 +186,7 @@ impl Visualizer {
|
||||
header.push("style=filled;".to_string());
|
||||
header.push("color=lightgrey;".to_string());
|
||||
header.push("node [shape=record];".to_string());
|
||||
header.push(format!("label=\"{}\";", bid));
|
||||
header.push(format!("label=\"{bid}\";"));
|
||||
|
||||
let mut nodes = Vec::new();
|
||||
|
||||
@@ -223,9 +223,6 @@ impl Visualizer {
|
||||
|
||||
let inner = vec![header, nodes, vec![edges]].concat().join("\n");
|
||||
|
||||
Ok(format!(
|
||||
"subgraph \"cluster.{}.{}\" {{\n{}\n}}",
|
||||
name, bid, inner
|
||||
))
|
||||
Ok(format!("subgraph \"cluster.{name}.{bid}\" {{\n{inner}\n}}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ impl WriteLine for TranslationUnit {
|
||||
))
|
||||
});
|
||||
|
||||
format!("{{ {} }}", fields)
|
||||
format!("{{ {fields} }}")
|
||||
} else {
|
||||
"opaque".to_string()
|
||||
};
|
||||
|
||||
writeln!(write, "struct {} : {}", name, definition)?;
|
||||
writeln!(write, "struct {name} : {definition}")?;
|
||||
}
|
||||
|
||||
for (name, decl) in &self.decls {
|
||||
@@ -93,7 +93,7 @@ impl WriteLine for (&String, &Declaration) {
|
||||
i,
|
||||
a.deref(),
|
||||
if let Some(name) = a.name() {
|
||||
format!(":{}", name)
|
||||
format!(":{name}")
|
||||
} else {
|
||||
"".into()
|
||||
}
|
||||
@@ -101,7 +101,7 @@ impl WriteLine for (&String, &Declaration) {
|
||||
)?;
|
||||
|
||||
for (id, block) in &definition.blocks {
|
||||
writeln!(write, "\nblock {}:", id)?;
|
||||
writeln!(write, "\nblock {id}:")?;
|
||||
(id, block).write_line(indent + 1, write)?;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ impl WriteLine for (&BlockId, &Block) {
|
||||
RegisterId::arg(*self.0, i),
|
||||
phi.deref(),
|
||||
if let Some(name) = phi.name() {
|
||||
format!(":{}", name)
|
||||
format!(":{name}")
|
||||
} else {
|
||||
"".into()
|
||||
}
|
||||
@@ -143,7 +143,7 @@ impl WriteLine for (&BlockId, &Block) {
|
||||
RegisterId::temp(*self.0, i),
|
||||
instr.dtype(),
|
||||
if let Some(name) = instr.name() {
|
||||
format!(":{}", name)
|
||||
format!(":{name}")
|
||||
} else {
|
||||
"".into()
|
||||
},
|
||||
@@ -160,18 +160,18 @@ impl WriteLine for (&BlockId, &Block) {
|
||||
|
||||
impl WriteString for Instruction {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for Operand {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for BlockExit {
|
||||
fn write_string(&self) -> String {
|
||||
format!("{}", self)
|
||||
format!("{self}")
|
||||
}
|
||||
}
|
||||
|
||||
761
src/irgen/mod.rs
761
src/irgen/mod.rs
@@ -1,18 +1,108 @@
|
||||
//! # Homework: IR Generation
|
||||
//!
|
||||
//! The goal of this homework is to translate the components of a C file into KECC IR. While doing
|
||||
//! so, you will familarize yourself with the structure of KECC IR, and understand the semantics of
|
||||
//! C in terms of KECC.
|
||||
//!
|
||||
//! We highly recommend checking out the [slides][slides] and [github repo][github-qna-irgen] for
|
||||
//! useful information.
|
||||
//!
|
||||
//! ## Guide
|
||||
//!
|
||||
//! ### High Level Guide
|
||||
//!
|
||||
//! Please watch the following video from 2020 along the lecture slides.
|
||||
//! - [Intermediate Representation][ir]
|
||||
//! - [IRgen (Overview)][irgen-overview]
|
||||
//!
|
||||
//! ### Coding Guide
|
||||
//!
|
||||
//! We highly recommend you copy-and-paste the code given in the following lecture videos from 2020:
|
||||
//! - [IRgen (Code, Variable Declaration)][irgen-var-decl]
|
||||
//! - [IRgen (Code, Function Definition)][irgen-func-def]
|
||||
//! - [IRgen (Code, Statement 1)][irgen-stmt-1]
|
||||
//! - [IRgen (Code, Statement 2)][irgen-stmt-2]
|
||||
//!
|
||||
//! The skeleton code roughly consists of the code for the first two videos, but you should still
|
||||
//! watch them to have an idea of what the code is like.
|
||||
//!
|
||||
//! [slides]: https://docs.google.com/presentation/d/1SqtU-Cn60Sd1jkbO0OSsRYKPMIkul0eZoYG9KpMugFE/edit?usp=sharing
|
||||
//! [ir]: https://youtu.be/7CY_lX5ZroI
|
||||
//! [irgen-overview]: https://youtu.be/YPtnXlKDSYo
|
||||
//! [irgen-var-decl]: https://youtu.be/HjARCUoK08s
|
||||
//! [irgen-func-def]: https://youtu.be/Rszt9x0Xu_0
|
||||
//! [irgen-stmt-1]: https://youtu.be/jFahkyxm994
|
||||
//! [irgen-stmt-2]: https://youtu.be/UkaXaNw462U
|
||||
//! [github-qna-irgen]: https://github.com/kaist-cp/cs420/labels/homework%20-%20irgen
|
||||
#![allow(dead_code)]
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::mem;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::ops::Deref;
|
||||
|
||||
use lang_c::ast::*;
|
||||
use lang_c::driver::Parse;
|
||||
use lang_c::span::Node;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::ir::{DtypeError, HasDtype, Named};
|
||||
use crate::*;
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct Irgen {}
|
||||
use itertools::izip;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct IrgenError {}
|
||||
#[derive(Debug)]
|
||||
pub struct IrgenError {
|
||||
pub code: String,
|
||||
pub message: IrgenErrorMessage,
|
||||
}
|
||||
|
||||
impl IrgenError {
|
||||
pub fn new(code: String, message: IrgenErrorMessage) -> Self {
|
||||
Self { code, message }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IrgenError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "IrgenError")
|
||||
write!(f, "error: {}\r\n\r\ncode: {}", self.message, self.code)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Error)]
|
||||
pub enum IrgenErrorMessage {
|
||||
/// For uncommon error
|
||||
#[error("{message}")]
|
||||
Misc { message: String },
|
||||
#[error("called object `{callee:?}` is not a function or function pointer")]
|
||||
NeedFunctionOrFunctionPointer { callee: ir::Operand },
|
||||
#[error("redefinition, `{name}`")]
|
||||
Redefinition { name: String },
|
||||
#[error("`{dtype}` conflicts prototype's dtype, `{protorype_dtype}`")]
|
||||
ConflictingDtype {
|
||||
dtype: ir::Dtype,
|
||||
protorype_dtype: ir::Dtype,
|
||||
},
|
||||
#[error("{dtype_error}")]
|
||||
InvalidDtype { dtype_error: DtypeError },
|
||||
#[error("l-value required as {message}")]
|
||||
RequireLvalue { message: String },
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Irgen {
|
||||
decls: BTreeMap<String, ir::Declaration>,
|
||||
typedefs: HashMap<String, ir::Dtype>,
|
||||
structs: HashMap<String, Option<ir::Dtype>>,
|
||||
struct_tempid_counter: usize,
|
||||
}
|
||||
|
||||
impl Translate<Parse> for Irgen {
|
||||
type Target = ir::TranslationUnit;
|
||||
type Error = IrgenError;
|
||||
|
||||
fn translate(&mut self, source: &Parse) -> Result<Self::Target, Self::Error> {
|
||||
self.translate(&source.unit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +110,664 @@ impl Translate<TranslationUnit> for Irgen {
|
||||
type Target = ir::TranslationUnit;
|
||||
type Error = IrgenError;
|
||||
|
||||
fn translate(&mut self, _unit: &TranslationUnit) -> Result<Self::Target, Self::Error> {
|
||||
todo!("homework 2")
|
||||
fn translate(&mut self, source: &TranslationUnit) -> Result<Self::Target, Self::Error> {
|
||||
for ext_decl in &source.0 {
|
||||
match ext_decl.node {
|
||||
ExternalDeclaration::Declaration(ref var) => {
|
||||
self.add_declaration(&var.node)?;
|
||||
}
|
||||
ExternalDeclaration::StaticAssert(_) => {
|
||||
panic!("ExternalDeclaration::StaticAssert is unsupported")
|
||||
}
|
||||
ExternalDeclaration::FunctionDefinition(ref func) => {
|
||||
self.add_function_definition(&func.node)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let decls = mem::take(&mut self.decls);
|
||||
let structs = mem::take(&mut self.structs);
|
||||
Ok(Self::Target { decls, structs })
|
||||
}
|
||||
}
|
||||
|
||||
impl Irgen {
|
||||
const BID_INIT: ir::BlockId = ir::BlockId(0);
|
||||
// `0` is used to create `BID_INIT`
|
||||
const BID_COUNTER_INIT: usize = 1;
|
||||
const TEMPID_COUNTER_INIT: usize = 0;
|
||||
|
||||
/// Add a declaration. It can be either a struct, typedef, or a variable.
|
||||
fn add_declaration(&mut self, source: &Declaration) -> Result<(), IrgenError> {
|
||||
let (base_dtype, is_typedef) =
|
||||
ir::Dtype::try_from_ast_declaration_specifiers(&source.specifiers).map_err(|e| {
|
||||
IrgenError::new(
|
||||
format!("{source:#?}"),
|
||||
IrgenErrorMessage::InvalidDtype { dtype_error: e },
|
||||
)
|
||||
})?;
|
||||
let base_dtype = base_dtype.resolve_typedefs(&self.typedefs).map_err(|e| {
|
||||
IrgenError::new(
|
||||
format!("{source:#?}"),
|
||||
IrgenErrorMessage::InvalidDtype { dtype_error: e },
|
||||
)
|
||||
})?;
|
||||
|
||||
let base_dtype = if let ir::Dtype::Struct { name, fields, .. } = &base_dtype {
|
||||
if let Some(name) = name {
|
||||
let _ = self.structs.entry(name.to_string()).or_insert(None);
|
||||
}
|
||||
|
||||
if fields.is_some() {
|
||||
base_dtype
|
||||
.resolve_structs(&mut self.structs, &mut self.struct_tempid_counter)
|
||||
.map_err(|e| {
|
||||
IrgenError::new(
|
||||
format!("{source:#?}"),
|
||||
IrgenErrorMessage::InvalidDtype { dtype_error: e },
|
||||
)
|
||||
})?
|
||||
} else {
|
||||
base_dtype
|
||||
}
|
||||
} else {
|
||||
base_dtype
|
||||
};
|
||||
|
||||
for init_decl in &source.declarators {
|
||||
let declarator = &init_decl.node.declarator.node;
|
||||
let name = name_of_declarator(declarator);
|
||||
let dtype = base_dtype
|
||||
.clone()
|
||||
.with_ast_declarator(declarator)
|
||||
.map_err(|e| {
|
||||
IrgenError::new(
|
||||
format!("{source:#?}"),
|
||||
IrgenErrorMessage::InvalidDtype { dtype_error: e },
|
||||
)
|
||||
})?
|
||||
.deref()
|
||||
.clone();
|
||||
let dtype = dtype.resolve_typedefs(&self.typedefs).map_err(|e| {
|
||||
IrgenError::new(
|
||||
format!("{source:#?}"),
|
||||
IrgenErrorMessage::InvalidDtype { dtype_error: e },
|
||||
)
|
||||
})?;
|
||||
if !is_typedef && is_invalid_structure(&dtype, &self.structs) {
|
||||
return Err(IrgenError::new(
|
||||
format!("{source:#?}"),
|
||||
IrgenErrorMessage::Misc {
|
||||
message: "incomplete struct type".to_string(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
if is_typedef {
|
||||
// Add new typedef if nothing has been declared before
|
||||
let prev_dtype = self
|
||||
.typedefs
|
||||
.entry(name.clone())
|
||||
.or_insert_with(|| dtype.clone());
|
||||
|
||||
if prev_dtype != &dtype {
|
||||
return Err(IrgenError::new(
|
||||
format!("{source:#?}"),
|
||||
IrgenErrorMessage::ConflictingDtype {
|
||||
dtype,
|
||||
protorype_dtype: prev_dtype.clone(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Creates a new declaration based on the dtype.
|
||||
let mut decl = ir::Declaration::try_from(dtype.clone()).map_err(|e| {
|
||||
IrgenError::new(
|
||||
format!("{source:#?}"),
|
||||
IrgenErrorMessage::InvalidDtype { dtype_error: e },
|
||||
)
|
||||
})?;
|
||||
|
||||
// If `initializer` exists, convert initializer to a constant value
|
||||
if let Some(initializer) = init_decl.node.initializer.as_ref() {
|
||||
if !is_valid_initializer(&initializer.node, &dtype, &self.structs) {
|
||||
return Err(IrgenError::new(
|
||||
format!("{source:#?}"),
|
||||
IrgenErrorMessage::Misc {
|
||||
message: "initializer is not valid".to_string(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
match &mut decl {
|
||||
ir::Declaration::Variable {
|
||||
initializer: var_initializer,
|
||||
..
|
||||
} => {
|
||||
if var_initializer.is_some() {
|
||||
return Err(IrgenError::new(
|
||||
format!("{source:#?}"),
|
||||
IrgenErrorMessage::Redefinition { name },
|
||||
));
|
||||
}
|
||||
*var_initializer = Some(initializer.node.clone());
|
||||
}
|
||||
ir::Declaration::Function { .. } => {
|
||||
return Err(IrgenError::new(
|
||||
format!("{source:#?}"),
|
||||
IrgenErrorMessage::Misc {
|
||||
message: "illegal initializer (only variables can be initialized)"
|
||||
.to_string(),
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.add_decl(&name, decl)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a function definition.
|
||||
fn add_function_definition(&mut self, source: &FunctionDefinition) -> Result<(), IrgenError> {
|
||||
// Creates name and signature.
|
||||
let specifiers = &source.specifiers;
|
||||
let declarator = &source.declarator.node;
|
||||
|
||||
let name = name_of_declarator(declarator);
|
||||
let name_of_params = name_of_params_from_function_declarator(declarator)
|
||||
.expect("declarator is not from function definition");
|
||||
|
||||
let (base_dtype, is_typedef) = ir::Dtype::try_from_ast_declaration_specifiers(specifiers)
|
||||
.map_err(|e| {
|
||||
IrgenError::new(
|
||||
format!("specs: {specifiers:#?}\ndecl: {declarator:#?}"),
|
||||
IrgenErrorMessage::InvalidDtype { dtype_error: e },
|
||||
)
|
||||
})?;
|
||||
|
||||
if is_typedef {
|
||||
return Err(IrgenError::new(
|
||||
format!("specs: {specifiers:#?}\ndecl: {declarator:#?}"),
|
||||
IrgenErrorMessage::Misc {
|
||||
message: "function definition declared typedef".into(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
let dtype = base_dtype
|
||||
.with_ast_declarator(declarator)
|
||||
.map_err(|e| {
|
||||
IrgenError::new(
|
||||
format!("specs: {specifiers:#?}\ndecl: {declarator:#?}"),
|
||||
IrgenErrorMessage::InvalidDtype { dtype_error: e },
|
||||
)
|
||||
})?
|
||||
.deref()
|
||||
.clone();
|
||||
let dtype = dtype.resolve_typedefs(&self.typedefs).map_err(|e| {
|
||||
IrgenError::new(
|
||||
format!("specs: {specifiers:#?}\ndecl: {declarator:#?}"),
|
||||
IrgenErrorMessage::InvalidDtype { dtype_error: e },
|
||||
)
|
||||
})?;
|
||||
|
||||
let signature = ir::FunctionSignature::new(dtype.clone());
|
||||
|
||||
// Adds new declaration if nothing has been declared before
|
||||
let decl = ir::Declaration::try_from(dtype).unwrap();
|
||||
self.add_decl(&name, decl)?;
|
||||
|
||||
// Prepare scope for global variable
|
||||
let global_scope: HashMap<_, _> = self
|
||||
.decls
|
||||
.iter()
|
||||
.map(|(name, decl)| {
|
||||
let dtype = decl.dtype();
|
||||
let pointer = ir::Constant::global_variable(name.clone(), dtype);
|
||||
let operand = ir::Operand::constant(pointer);
|
||||
(name.clone(), operand)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Prepares for irgen pass.
|
||||
let mut irgen = IrgenFunc {
|
||||
return_type: signature.ret.clone(),
|
||||
bid_init: Irgen::BID_INIT,
|
||||
phinodes_init: Vec::new(),
|
||||
allocations: Vec::new(),
|
||||
blocks: BTreeMap::new(),
|
||||
bid_counter: Irgen::BID_COUNTER_INIT,
|
||||
tempid_counter: Irgen::TEMPID_COUNTER_INIT,
|
||||
typedefs: &self.typedefs,
|
||||
structs: &self.structs,
|
||||
// Initial symbol table has scope for global variable already
|
||||
symbol_table: vec![global_scope],
|
||||
};
|
||||
let mut context = Context::new(irgen.bid_init);
|
||||
|
||||
// Enter variable scope for alloc registers matched with function parameters
|
||||
irgen.enter_scope();
|
||||
|
||||
// Creates the init block that stores arguments.
|
||||
irgen
|
||||
.translate_parameter_decl(&signature, irgen.bid_init, &name_of_params, &mut context)
|
||||
.map_err(|e| {
|
||||
IrgenError::new(format!("specs: {specifiers:#?}\ndecl: {declarator:#?}"), e)
|
||||
})?;
|
||||
|
||||
// Translates statement.
|
||||
irgen.translate_stmt(&source.statement.node, &mut context, None, None)?;
|
||||
|
||||
// Creates the end block
|
||||
let ret = signature.ret.set_const(false);
|
||||
let value = if ret == ir::Dtype::unit() {
|
||||
ir::Operand::constant(ir::Constant::unit())
|
||||
} else if ret == ir::Dtype::INT {
|
||||
// If "main" function, default return value is `0` when return type is `int`
|
||||
if name == "main" {
|
||||
ir::Operand::constant(ir::Constant::int(0, ret))
|
||||
} else {
|
||||
ir::Operand::constant(ir::Constant::undef(ret))
|
||||
}
|
||||
} else {
|
||||
ir::Operand::constant(ir::Constant::undef(ret))
|
||||
};
|
||||
|
||||
// Last Block of the function
|
||||
irgen.insert_block(context, ir::BlockExit::Return { value });
|
||||
|
||||
// Exit variable scope created above
|
||||
irgen.exit_scope();
|
||||
|
||||
let func_def = ir::FunctionDefinition {
|
||||
allocations: irgen.allocations,
|
||||
blocks: irgen.blocks,
|
||||
bid_init: irgen.bid_init,
|
||||
};
|
||||
|
||||
let decl = self
|
||||
.decls
|
||||
.get_mut(&name)
|
||||
.unwrap_or_else(|| panic!("The declaration of `{name}` must exist"));
|
||||
if let ir::Declaration::Function { definition, .. } = decl {
|
||||
if definition.is_some() {
|
||||
return Err(IrgenError::new(
|
||||
format!("specs: {specifiers:#?}\ndecl: {declarator:#?}"),
|
||||
IrgenErrorMessage::Misc {
|
||||
message: format!("the name `{name}` is defined multiple time"),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
// Update function definition
|
||||
*definition = Some(func_def);
|
||||
} else {
|
||||
panic!("`{name}` must be function declaration")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds a possibly existing declaration.
|
||||
///
|
||||
/// Returns error if the previous declearation is incompatible with `decl`.
|
||||
fn add_decl(&mut self, name: &str, decl: ir::Declaration) -> Result<(), IrgenError> {
|
||||
let old_decl = some_or!(
|
||||
self.decls.insert(name.to_string(), decl.clone()),
|
||||
return Ok(())
|
||||
);
|
||||
|
||||
// Check if type is conflicting for pre-declared one
|
||||
if !old_decl.is_compatible(&decl) {
|
||||
return Err(IrgenError::new(
|
||||
name.to_string(),
|
||||
IrgenErrorMessage::ConflictingDtype {
|
||||
dtype: old_decl.dtype(),
|
||||
protorype_dtype: decl.dtype(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Storage for instructions up to the insertion of a block
|
||||
#[derive(Debug)]
|
||||
struct Context {
|
||||
/// The block id of the current context.
|
||||
bid: ir::BlockId,
|
||||
/// Current instructions of the block.
|
||||
instrs: Vec<Named<ir::Instruction>>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Create a new context with block number bid
|
||||
fn new(bid: ir::BlockId) -> Self {
|
||||
Self {
|
||||
bid,
|
||||
instrs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Adds `instr` to the current context.
|
||||
fn insert_instruction(
|
||||
&mut self,
|
||||
instr: ir::Instruction,
|
||||
) -> Result<ir::Operand, IrgenErrorMessage> {
|
||||
let dtype = instr.dtype();
|
||||
self.instrs.push(Named::new(None, instr));
|
||||
|
||||
Ok(ir::Operand::register(
|
||||
ir::RegisterId::temp(self.bid, self.instrs.len() - 1),
|
||||
dtype,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// A C function being translated.
|
||||
struct IrgenFunc<'i> {
|
||||
/// return type of the function.
|
||||
return_type: ir::Dtype,
|
||||
/// initial block id for the function, typically 0.
|
||||
bid_init: ir::BlockId,
|
||||
/// arguments represented as initial phinodes. Order must be the same of that given in the C
|
||||
/// function.
|
||||
phinodes_init: Vec<Named<ir::Dtype>>,
|
||||
/// local allocations.
|
||||
allocations: Vec<Named<ir::Dtype>>,
|
||||
/// Map from block id to basic blocks
|
||||
blocks: BTreeMap<ir::BlockId, ir::Block>,
|
||||
/// current block id. `blocks` must have an entry for all ids less then this
|
||||
bid_counter: usize,
|
||||
/// current temporary id. Used to create temporary names in the IR for e.g,
|
||||
tempid_counter: usize,
|
||||
/// Usable definitions
|
||||
typedefs: &'i HashMap<String, ir::Dtype>,
|
||||
/// Usable structs
|
||||
// TODO: Add examples on how to use properly use this field.
|
||||
structs: &'i HashMap<String, Option<ir::Dtype>>,
|
||||
/// Current symbol table. The initial symbol table has the global variables.
|
||||
symbol_table: Vec<HashMap<String, ir::Operand>>,
|
||||
}
|
||||
|
||||
impl IrgenFunc<'_> {
|
||||
/// Allocate a new block id.
|
||||
fn alloc_bid(&mut self) -> ir::BlockId {
|
||||
let bid = self.bid_counter;
|
||||
self.bid_counter += 1;
|
||||
ir::BlockId(bid)
|
||||
}
|
||||
|
||||
/// Allocate a new temporary id.
|
||||
fn alloc_tempid(&mut self) -> String {
|
||||
let tempid = self.tempid_counter;
|
||||
self.tempid_counter += 1;
|
||||
format!("t{tempid}")
|
||||
}
|
||||
|
||||
/// Create a new allocation with type given by `alloc`.
|
||||
fn insert_alloc(&mut self, alloc: Named<ir::Dtype>) -> usize {
|
||||
self.allocations.push(alloc);
|
||||
self.allocations.len() - 1
|
||||
}
|
||||
|
||||
/// Insert a new block `context` with exit instruction `exit`.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panics if another block with the same bid as `context` already existed.
|
||||
fn insert_block(&mut self, context: Context, exit: ir::BlockExit) {
|
||||
let block = ir::Block {
|
||||
phinodes: if context.bid == self.bid_init {
|
||||
self.phinodes_init.clone()
|
||||
} else {
|
||||
Vec::new()
|
||||
},
|
||||
instructions: context.instrs,
|
||||
exit,
|
||||
};
|
||||
if self.blocks.insert(context.bid, block).is_some() {
|
||||
panic!("the bid `{}` is defined multiple time", context.bid)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter a scope and create a new symbol table entry, i.e, we are at a `{` in the function.
|
||||
fn enter_scope(&mut self) {
|
||||
self.symbol_table.push(HashMap::new());
|
||||
}
|
||||
|
||||
/// Exit a scope and remove the a oldest symbol table entry. i.e, we are at a `}` in the
|
||||
/// function.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panics if there are no scopes to exit, i.e, the function has a unmatched `}`.
|
||||
fn exit_scope(&mut self) {
|
||||
let _unused = self.symbol_table.pop().unwrap();
|
||||
}
|
||||
|
||||
/// Inserts `var` with `value` to the current symbol table.
|
||||
///
|
||||
/// Returns Ok() if the current scope has no previously-stored entry for a given variable.
|
||||
fn insert_symbol_table_entry(
|
||||
&mut self,
|
||||
var: String,
|
||||
value: ir::Operand,
|
||||
) -> Result<(), IrgenErrorMessage> {
|
||||
let cur_scope = self
|
||||
.symbol_table
|
||||
.last_mut()
|
||||
.expect("symbol table has no valid scope");
|
||||
if cur_scope.insert(var.clone(), value).is_some() {
|
||||
return Err(IrgenErrorMessage::Redefinition { name: var });
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Transalte a C statement `stmt` under the current block `context`, with `continue` block
|
||||
/// `bid_continue` and break block `bid_break`.
|
||||
fn translate_stmt(
|
||||
&mut self,
|
||||
_stmt: &Statement,
|
||||
_context: &mut Context,
|
||||
_bid_continue: Option<ir::BlockId>,
|
||||
_bid_break: Option<ir::BlockId>,
|
||||
) -> Result<(), IrgenError> {
|
||||
todo!("Homework: IR Generation")
|
||||
}
|
||||
|
||||
/// Translate parameter declaration of the functions to IR.
|
||||
///
|
||||
/// For example, given the following C function from [`foo.c`][foo]:
|
||||
///
|
||||
/// ```C
|
||||
/// int foo(int x, int y, int z) {
|
||||
/// if (x == y) { return y; }
|
||||
/// else { return z; }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The IR before this function looks roughly as follows:
|
||||
///
|
||||
/// ```text
|
||||
/// fun i32 @foo (i32, i32, i32) {
|
||||
/// init:
|
||||
/// bid: b0
|
||||
/// allocations:
|
||||
///
|
||||
/// block b0:
|
||||
/// %b0:p0:i32:x
|
||||
/// %b0:p1:i32:y
|
||||
/// %b0:p2:i32:z
|
||||
/// ```
|
||||
///
|
||||
/// With the following arguments :
|
||||
/// ```ignore
|
||||
/// signature = FunctionSignature { ret: ir::INT, params: vec![ir::INT, ir::INT, ir::INT] }
|
||||
/// bid_init = 0
|
||||
/// name_of_params = ["x", "y", "z"]
|
||||
/// context = // omitted
|
||||
/// ```
|
||||
///
|
||||
/// Resulting IR after this function should be roughly follows:
|
||||
/// ```text
|
||||
/// fun i32 @foo (i32, i32, i32) {
|
||||
/// init:
|
||||
/// bid: b0
|
||||
/// allocations:
|
||||
/// %l0:i32:x
|
||||
/// %l1:i32:y
|
||||
/// %l2:i32:z
|
||||
///
|
||||
/// block b0:
|
||||
/// %b0:p0:i32:x
|
||||
/// %b0:p1:i32:y
|
||||
/// %b0:p2:i32:z
|
||||
/// %b0:i0:unit = store %b0:p0:i32 %l0:i32*
|
||||
/// %b0:i1:unit = store %b0:p1:i32 %l1:i32*
|
||||
/// %b0:i2:unit = store %b0:p2:i32 %l2:i32*
|
||||
/// ```
|
||||
///
|
||||
/// In particular, note that it is added to the local allocation list and store them to the
|
||||
/// initial phinodes.
|
||||
///
|
||||
/// Note that the resulting IR is **a** solution. If you can think of a better way to
|
||||
/// translate parameters, feel free to do so.
|
||||
///
|
||||
/// [foo]: https://github.com/kaist-cp/kecc-public/blob/main/examples/c/foo.c
|
||||
fn translate_parameter_decl(
|
||||
&mut self,
|
||||
_signature: &ir::FunctionSignature,
|
||||
_bid_init: ir::BlockId,
|
||||
_name_of_params: &[String],
|
||||
_context: &mut Context,
|
||||
) -> Result<(), IrgenErrorMessage> {
|
||||
todo!("Homework: IR Generation")
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn name_of_declarator(declarator: &Declarator) -> String {
|
||||
let declarator_kind = &declarator.kind;
|
||||
match &declarator_kind.node {
|
||||
DeclaratorKind::Abstract => panic!("DeclaratorKind::Abstract is unsupported"),
|
||||
DeclaratorKind::Identifier(identifier) => identifier.node.name.clone(),
|
||||
DeclaratorKind::Declarator(declarator) => name_of_declarator(&declarator.node),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn name_of_params_from_function_declarator(declarator: &Declarator) -> Option<Vec<String>> {
|
||||
let declarator_kind = &declarator.kind;
|
||||
match &declarator_kind.node {
|
||||
DeclaratorKind::Abstract => panic!("DeclaratorKind::Abstract is unsupported"),
|
||||
DeclaratorKind::Identifier(_) => {
|
||||
name_of_params_from_derived_declarators(&declarator.derived)
|
||||
}
|
||||
DeclaratorKind::Declarator(next_declarator) => {
|
||||
name_of_params_from_function_declarator(&next_declarator.node)
|
||||
.or_else(|| name_of_params_from_derived_declarators(&declarator.derived))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn name_of_params_from_derived_declarators(
|
||||
derived_decls: &[Node<DerivedDeclarator>],
|
||||
) -> Option<Vec<String>> {
|
||||
for derived_decl in derived_decls {
|
||||
match &derived_decl.node {
|
||||
DerivedDeclarator::Function(func_decl) => {
|
||||
let name_of_params = func_decl
|
||||
.node
|
||||
.parameters
|
||||
.iter()
|
||||
.map(|p| name_of_parameter_declaration(&p.node))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_default();
|
||||
return Some(name_of_params);
|
||||
}
|
||||
DerivedDeclarator::KRFunction(_kr_func_decl) => {
|
||||
// K&R function is allowed only when it has no parameter
|
||||
return Some(Vec::new());
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn name_of_parameter_declaration(parameter_declaration: &ParameterDeclaration) -> Option<String> {
|
||||
let declarator = some_or!(parameter_declaration.declarator.as_ref(), return None);
|
||||
Some(name_of_declarator(&declarator.node))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_valid_initializer(
|
||||
initializer: &Initializer,
|
||||
dtype: &ir::Dtype,
|
||||
structs: &HashMap<String, Option<ir::Dtype>>,
|
||||
) -> bool {
|
||||
match initializer {
|
||||
Initializer::Expression(expr) => match dtype {
|
||||
ir::Dtype::Int { .. } | ir::Dtype::Float { .. } | ir::Dtype::Pointer { .. } => {
|
||||
match &expr.node {
|
||||
Expression::Constant(_) => true,
|
||||
Expression::UnaryOperator(unary) => matches!(
|
||||
&unary.node.operator.node,
|
||||
UnaryOperator::Minus | UnaryOperator::Plus
|
||||
),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
Initializer::List(items) => match dtype {
|
||||
ir::Dtype::Array { inner, .. } => items
|
||||
.iter()
|
||||
.all(|i| is_valid_initializer(&i.node.initializer.node, inner, structs)),
|
||||
ir::Dtype::Struct { name, .. } => {
|
||||
let name = name.as_ref().expect("struct should have its name");
|
||||
let struct_type = structs
|
||||
.get(name)
|
||||
.expect("struct type matched with `name` must exist")
|
||||
.as_ref()
|
||||
.expect("`struct_type` must have its definition");
|
||||
let fields = struct_type
|
||||
.get_struct_fields()
|
||||
.expect("`struct_type` must be struct type")
|
||||
.as_ref()
|
||||
.expect("`fields` must be `Some`");
|
||||
|
||||
izip!(fields, items).all(|(f, i)| {
|
||||
is_valid_initializer(&i.node.initializer.node, f.deref(), structs)
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_invalid_structure(dtype: &ir::Dtype, structs: &HashMap<String, Option<ir::Dtype>>) -> bool {
|
||||
// When `dtype` is `Dtype::Struct`, `structs` has real definition of `dtype`
|
||||
if let ir::Dtype::Struct { name, fields, .. } = dtype {
|
||||
assert!(name.is_some() && fields.is_none());
|
||||
let name = name.as_ref().unwrap();
|
||||
let struct_type = some_or!(structs.get(name), return true);
|
||||
|
||||
struct_type.is_none()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! KECC: KAIST Educational C Compiler.
|
||||
|
||||
#![deny(clippy::all)]
|
||||
#![deny(rustdoc::all)]
|
||||
// #![deny(rustdoc::all)]
|
||||
#![deny(warnings)]
|
||||
// Tries to deny all rustc allow lints.
|
||||
// <https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html>
|
||||
|
||||
@@ -9,6 +9,6 @@ pub struct DeadcodeInner {}
|
||||
|
||||
impl Optimize<FunctionDefinition> for DeadcodeInner {
|
||||
fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool {
|
||||
todo!("homework 6")
|
||||
todo!("Homework: Deadcode Elimination")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@ pub struct GvnInner {}
|
||||
|
||||
impl Optimize<ir::FunctionDefinition> for GvnInner {
|
||||
fn optimize(&mut self, _code: &mut ir::FunctionDefinition) -> bool {
|
||||
todo!("homework 5")
|
||||
todo!("Homework: Global Variable Numbering")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ pub struct Mem2regInner {}
|
||||
|
||||
impl Optimize<FunctionDefinition> for Mem2regInner {
|
||||
fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool {
|
||||
todo!("homework 4")
|
||||
todo!("Homework: Register Promotion")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ pub type O0 = Null;
|
||||
pub type O1 = Repeat<(SimplifyCfg, (Mem2reg, (Gvn, Deadcode)))>;
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct Null {}
|
||||
pub struct Null;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Repeat<O> {
|
||||
|
||||
@@ -27,24 +27,24 @@ pub struct SimplifyCfgEmpty {}
|
||||
|
||||
impl Optimize<FunctionDefinition> for SimplifyCfgConstProp {
|
||||
fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool {
|
||||
todo!("homework 3")
|
||||
todo!("Homework: Simplify CFG")
|
||||
}
|
||||
}
|
||||
|
||||
impl Optimize<FunctionDefinition> for SimplifyCfgReach {
|
||||
fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool {
|
||||
todo!("homework 3")
|
||||
todo!("Homework: Simplify CFG")
|
||||
}
|
||||
}
|
||||
|
||||
impl Optimize<FunctionDefinition> for SimplifyCfgMerge {
|
||||
fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool {
|
||||
todo!("homework 3")
|
||||
todo!("Homework: Simplify CFG")
|
||||
}
|
||||
}
|
||||
|
||||
impl Optimize<FunctionDefinition> for SimplifyCfgEmpty {
|
||||
fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool {
|
||||
todo!("homework 3")
|
||||
todo!("Homework: Simplify CFG")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ fn modify_c(path: &Path, rand_num: i32) -> String {
|
||||
.expect("`src` must be converted to string");
|
||||
drop(src);
|
||||
|
||||
let from = format!("int {} = 1", NONCE_NAME);
|
||||
let to = format!("int {} = {}", NONCE_NAME, rand_num);
|
||||
let from = format!("int {NONCE_NAME} = 1");
|
||||
let to = format!("int {NONCE_NAME} = {rand_num}");
|
||||
data.replace(&from, &to)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ where
|
||||
continue;
|
||||
}
|
||||
|
||||
println!("[testing {:?}]", path);
|
||||
println!("[testing {path:?}]");
|
||||
f(&path);
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ fn test_opt_between_dirs<O: Optimize<ir::TranslationUnit>>(from: &Path, to: &Pat
|
||||
assert!(to_file_path.exists());
|
||||
assert!(to_file_path.is_file());
|
||||
|
||||
println!("[testing {:?} to {:?}]", from_file_path, to_file_path);
|
||||
println!("[testing {from_file_path:?} to {to_file_path:?}]");
|
||||
test_opt(&from_file_path, &to_file_path, opt);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user