diff --git a/Cargo.lock b/Cargo.lock index c6b52e4..1252194 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 4c763df..0fde8a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/README.md b/README.md index 3b60980..b58c535 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/bin/kecc.rs b/bin/kecc.rs index 7c18829..ad0e8ec 100644 --- a/bin/kecc.rs +++ b/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() diff --git a/rust-toolchain b/rust-toolchain index 902c741..b6148bc 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.65.0 +1.66.0 diff --git a/scripts/grade-irgen-small.sh b/scripts/grade-irgen-small.sh index 0781572..c89bb09 100755 --- a/scripts/grade-irgen-small.sh +++ b/scripts/grade-irgen-small.sh @@ -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 diff --git a/src/asm/mod.rs b/src/asm/mod.rs index 3569d82..ac538ee 100644 --- a/src/asm/mod.rs +++ b/src/asm/mod.rs @@ -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}") } } diff --git a/src/asm/write_asm.rs b/src/asm/write_asm.rs index db24075..f0a4771 100644 --- a/src/asm/write_asm.rs +++ b/src/asm/write_asm.rs @@ -29,7 +29,7 @@ impl WriteLine for Section { 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}") } } diff --git a/src/asmgen/mod.rs b/src/asmgen/mod.rs index 1a60f53..121b42c 100644 --- a/src/asmgen/mod.rs +++ b/src/asmgen/mod.rs @@ -10,6 +10,6 @@ impl Translate for Asmgen { type Error = (); fn translate(&mut self, _source: &ir::TranslationUnit) -> Result { - todo!("homework 7") + todo!("Homework: Assembly Generation") } } diff --git a/src/c/ast_equiv.rs b/src/c/ast_equiv.rs index f757b8b..f86a36c 100644 --- a/src/c/ast_equiv.rs +++ b/src/c/ast_equiv.rs @@ -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:?}`"# ) } } diff --git a/src/c/parse.rs b/src/c/parse.rs index ce7d327..b38e998 100644 --- a/src/c/parse.rs +++ b/src/c/parse.rs @@ -17,7 +17,7 @@ pub enum Error { /// TODO(document) #[derive(Default, Clone, Copy, Debug)] -pub struct Parse {} +pub struct Parse; impl> Translate

for Parse { type Target = TranslationUnit; diff --git a/src/ir/dtype.rs b/src/ir/dtype.rs index 10c3776..da8d04a 100644 --- a/src/ir/dtype.rs +++ b/src/ir/dtype.rs @@ -413,7 +413,7 @@ impl TryFrom for Dtype { ast::TypeSpecifier::Int => Self::INT, ast::TypeSpecifier::Float => Self::FLOAT, ast::TypeSpecifier::Double => Self::DOUBLE, - _ => panic!("Dtype::try_from::: {:?} is not a scalar type", t), + _ => panic!("Dtype::try_from::: {t:?} is not a scalar type"), } } else { Self::default() @@ -452,10 +452,9 @@ impl TryFrom for Dtype { let is_signed = match signed_option { ast::TypeSpecifier::Signed => true, ast::TypeSpecifier::Unsigned => false, - _ => panic!( - "Dtype::try_from::: {:?} is not a signed option", - signed_option - ), + _ => { + panic!("Dtype::try_from::: {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() }; diff --git a/src/ir/interp.rs b/src/ir/interp.rs index 0d56bd7..5470f61 100644 --- a/src/ir/interp.rs +++ b/src/ir/interp.rs @@ -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 diff --git a/src/ir/mod.rs b/src/ir/mod.rs index e694e81..09ff076 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -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, "\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}"), } } } diff --git a/src/ir/parse.rs b/src/ir/parse.rs index 6faa95a..9fa6ed4 100644 --- a/src/ir/parse.rs +++ b/src/ir/parse.rs @@ -677,8 +677,7 @@ impl> Translate

for Parse { fn translate(&mut self, source: &P) -> Result { 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) } } diff --git a/src/ir/visualize.rs b/src/ir/visualize.rs index c4541fb..f330728 100644 --- a/src/ir/visualize.rs +++ b/src/ir/visualize.rs @@ -50,7 +50,7 @@ impl Translate 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 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 { @@ -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}}")) } } diff --git a/src/ir/write_ir.rs b/src/ir/write_ir.rs index b4c3584..bc24e33 100644 --- a/src/ir/write_ir.rs +++ b/src/ir/write_ir.rs @@ -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}") } } diff --git a/src/irgen/mod.rs b/src/irgen/mod.rs index 03169c8..041e78c 100644 --- a/src/irgen/mod.rs +++ b/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, + typedefs: HashMap, + structs: HashMap>, + struct_tempid_counter: usize, +} + +impl Translate for Irgen { + type Target = ir::TranslationUnit; + type Error = IrgenError; + + fn translate(&mut self, source: &Parse) -> Result { + self.translate(&source.unit) } } @@ -20,7 +110,664 @@ impl Translate for Irgen { type Target = ir::TranslationUnit; type Error = IrgenError; - fn translate(&mut self, _unit: &TranslationUnit) -> Result { - todo!("homework 2") + fn translate(&mut self, source: &TranslationUnit) -> Result { + 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>, +} + +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 { + 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>, + /// local allocations. + allocations: Vec>, + /// Map from block id to basic blocks + blocks: BTreeMap, + /// 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, + /// Usable structs + // TODO: Add examples on how to use properly use this field. + structs: &'i HashMap>, + /// Current symbol table. The initial symbol table has the global variables. + symbol_table: Vec>, +} + +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) -> 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, + _bid_break: Option, + ) -> 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> { + 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], +) -> Option> { + 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::>>() + .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 { + 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>, +) -> 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>) -> 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 } } diff --git a/src/lib.rs b/src/lib.rs index 97f6dc0..19baac7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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. // diff --git a/src/opt/deadcode.rs b/src/opt/deadcode.rs index fe92b22..fbe4b3f 100644 --- a/src/opt/deadcode.rs +++ b/src/opt/deadcode.rs @@ -9,6 +9,6 @@ pub struct DeadcodeInner {} impl Optimize for DeadcodeInner { fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("homework 6") + todo!("Homework: Deadcode Elimination") } } diff --git a/src/opt/gvn.rs b/src/opt/gvn.rs index 7ce926a..24bbf2e 100644 --- a/src/opt/gvn.rs +++ b/src/opt/gvn.rs @@ -8,6 +8,6 @@ pub struct GvnInner {} impl Optimize for GvnInner { fn optimize(&mut self, _code: &mut ir::FunctionDefinition) -> bool { - todo!("homework 5") + todo!("Homework: Global Variable Numbering") } } diff --git a/src/opt/mem2reg.rs b/src/opt/mem2reg.rs index aff6e86..6be8097 100644 --- a/src/opt/mem2reg.rs +++ b/src/opt/mem2reg.rs @@ -9,6 +9,6 @@ pub struct Mem2regInner {} impl Optimize for Mem2regInner { fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("homework 4") + todo!("Homework: Register Promotion") } } diff --git a/src/opt/mod.rs b/src/opt/mod.rs index f93d0db..cb3c7ad 100644 --- a/src/opt/mod.rs +++ b/src/opt/mod.rs @@ -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 { diff --git a/src/opt/simplify_cfg.rs b/src/opt/simplify_cfg.rs index 3509d42..9be7c10 100644 --- a/src/opt/simplify_cfg.rs +++ b/src/opt/simplify_cfg.rs @@ -27,24 +27,24 @@ pub struct SimplifyCfgEmpty {} impl Optimize for SimplifyCfgConstProp { fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("homework 3") + todo!("Homework: Simplify CFG") } } impl Optimize for SimplifyCfgReach { fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("homework 3") + todo!("Homework: Simplify CFG") } } impl Optimize for SimplifyCfgMerge { fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("homework 3") + todo!("Homework: Simplify CFG") } } impl Optimize for SimplifyCfgEmpty { fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("homework 3") + todo!("Homework: Simplify CFG") } } diff --git a/src/tests.rs b/src/tests.rs index 121de38..1bac2ec 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -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) } diff --git a/tests/test_examples.rs b/tests/test_examples.rs index b4b39e5..dc541a7 100644 --- a/tests/test_examples.rs +++ b/tests/test_examples.rs @@ -16,7 +16,7 @@ where continue; } - println!("[testing {:?}]", path); + println!("[testing {path:?}]"); f(&path); } } @@ -41,7 +41,7 @@ fn test_opt_between_dirs>(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); } }