mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-16 07:28:52 +00:00
Update skeleton
This commit is contained in:
@@ -16,10 +16,11 @@ cargo build --release # release build
|
|||||||
## Run
|
## Run
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo run -- -h # print options
|
cargo run -- -h # print options
|
||||||
cargo run -- -p examples/c/fibonacci.c # parse
|
cargo run -- -p examples/c/fibonacci.c # parse
|
||||||
cargo run -- -i examples/c/fibonacci.c # irgen
|
cargo run -- -i examples/c/fibonacci.c # irgen
|
||||||
cargo run -- examples/c/fibonacci.c # compile
|
cargo run -- -O --iroutput examples/c/fibonacci.c # optimize
|
||||||
|
cargo run -- examples/c/fibonacci.c # compile
|
||||||
|
|
||||||
cargo run -- --irrun examples/c/fibonacci.c # interprets the IR
|
cargo run -- --irrun examples/c/fibonacci.c # interprets the IR
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ fn main() {
|
|||||||
|
|
||||||
if matches.is_present("print") {
|
if matches.is_present("print") {
|
||||||
kecc::test_write_c(&unit, Path::new(&input));
|
kecc::test_write_c(&unit, Path::new(&input));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches.is_present("irgen") {
|
if matches.is_present("irgen") {
|
||||||
kecc::test_irgen(&unit, Path::new(&input));
|
kecc::test_irgen(&unit, Path::new(&input));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches.is_present("simplify-cfg") {
|
if matches.is_present("simplify-cfg") {
|
||||||
@@ -43,4 +45,6 @@ fn main() {
|
|||||||
if matches.is_present("gvn") {
|
if matches.is_present("gvn") {
|
||||||
todo!("test gvn");
|
todo!("test gvn");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kecc::test_asmgen(&unit, Path::new(&input));
|
||||||
}
|
}
|
||||||
|
|||||||
3
examples/asm/alignof.c
Normal file
3
examples/asm/alignof.c
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
int main() {
|
||||||
|
return _Alignof(const int) == 4;
|
||||||
|
}
|
||||||
10
examples/asm/bar.c
Normal file
10
examples/asm/bar.c
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
int bar(int x, int y, int z){
|
||||||
|
int arith_mean = (x + y + z) / 3;
|
||||||
|
int ugly_mean = (((x + y) / 2) * 2 + z) / 3;
|
||||||
|
if (x == y) { return y; }
|
||||||
|
else { return z; }
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
18
examples/asm/cmp.c
Normal file
18
examples/asm/cmp.c
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
int int_greater_than(int i, unsigned int j) {
|
||||||
|
if (i > j) return 1;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int char_greater_than(char i, unsigned char j) {
|
||||||
|
if (i > j) return 1;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// cmp ugt
|
||||||
|
int r1 = int_greater_than(-1, 1);
|
||||||
|
// cmp sgt
|
||||||
|
int r2 = char_greater_than(-1, 1);
|
||||||
|
|
||||||
|
return r1 == 1 && r2 == 0;
|
||||||
|
}
|
||||||
11
examples/asm/fibonacci.c
Normal file
11
examples/asm/fibonacci.c
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
int fibonacci(int n) {
|
||||||
|
if (n < 2) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fibonacci(n - 2) + fibonacci(n - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return fibonacci(9) == 34;
|
||||||
|
}
|
||||||
164
src/asm/mod.rs
164
src/asm/mod.rs
@@ -87,7 +87,7 @@ pub enum SymbolType {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
/// R-type instruction format
|
/// R-type instruction format
|
||||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (11p, 104p)
|
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
||||||
RType {
|
RType {
|
||||||
instr: RType,
|
instr: RType,
|
||||||
rd: Register,
|
rd: Register,
|
||||||
@@ -95,7 +95,7 @@ pub enum Instruction {
|
|||||||
rs2: Register,
|
rs2: Register,
|
||||||
},
|
},
|
||||||
/// I-type instruction format
|
/// I-type instruction format
|
||||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (11p, 104p)
|
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
||||||
IType {
|
IType {
|
||||||
instr: IType,
|
instr: IType,
|
||||||
rd: Register,
|
rd: Register,
|
||||||
@@ -103,26 +103,109 @@ pub enum Instruction {
|
|||||||
imm: isize,
|
imm: isize,
|
||||||
},
|
},
|
||||||
/// S-type instruction format
|
/// S-type instruction format
|
||||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (11p, 104p)
|
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
||||||
SType {
|
SType {
|
||||||
instr: SType,
|
instr: SType,
|
||||||
rs1: Register,
|
rs1: Register,
|
||||||
rs2: Register,
|
rs2: Register,
|
||||||
imm: isize,
|
imm: isize,
|
||||||
},
|
},
|
||||||
|
/// B-type instruction format
|
||||||
|
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
||||||
|
BType {
|
||||||
|
instr: BType,
|
||||||
|
rs1: Register,
|
||||||
|
rs2: Register,
|
||||||
|
imm: Label,
|
||||||
|
},
|
||||||
Pseudo(Pseudo),
|
Pseudo(Pseudo),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the enum variant contains `Option<DataSize>`,
|
||||||
|
/// it means that the instructions used may vary according to `DataSize`.
|
||||||
|
/// Use 'Some' if RISC-V ISA provides instruction to support a specific 'DataSize',
|
||||||
|
/// if not, 'None' which means to use default instruction.
|
||||||
|
/// Because KECC uses RV64 (RISC-V ISA for 64-bit architecture),
|
||||||
|
/// KECC uses `Some` if `DataSize` is `Word`, if not, use `None`.
|
||||||
|
/// https://riscv.org/specifications/isa-spec-pdf/ (35p)
|
||||||
|
///
|
||||||
|
/// If the enum variant contains `bool`,
|
||||||
|
/// It means that different instructions exist
|
||||||
|
/// depending on whether the operand is signed or not.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum RType {
|
pub enum RType {
|
||||||
Add(Option<DataSize>),
|
Add(Option<DataSize>),
|
||||||
|
Sub(Option<DataSize>),
|
||||||
Mul(Option<DataSize>),
|
Mul(Option<DataSize>),
|
||||||
|
Div(Option<DataSize>, bool),
|
||||||
|
Slt(bool),
|
||||||
|
Xor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RType {
|
||||||
|
pub fn add(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
let data_size = if data_size == DataSize::Word {
|
||||||
|
Some(data_size)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::Add(data_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sub(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
let data_size = if data_size == DataSize::Word {
|
||||||
|
Some(data_size)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::Sub(data_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mul(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
let data_size = if data_size == DataSize::Word {
|
||||||
|
Some(data_size)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::Mul(data_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn div(dtype: ir::Dtype, is_signed: bool) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
let data_size = if data_size == DataSize::Word {
|
||||||
|
Some(data_size)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::Div(data_size, is_signed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the enum variant contains `Option<DataSize>`,
|
||||||
|
/// it means that the instructions used may vary according to `DataSize`.
|
||||||
|
/// Use 'Some' if RISC-V ISA provides instruction to support a specific 'DataSize',
|
||||||
|
/// if not, 'None' which means to use default instruction.
|
||||||
|
/// Because KECC uses RV64 (RISC-V ISA for 64-bit architecture),
|
||||||
|
/// KECC uses `Some` if `DataSize` is `Word`, if not, use `None`.
|
||||||
|
/// https://riscv.org/specifications/isa-spec-pdf/ (35p)
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum IType {
|
pub enum IType {
|
||||||
Load(DataSize),
|
Load(DataSize),
|
||||||
Addi(Option<DataSize>),
|
Addi(Option<DataSize>),
|
||||||
|
Andi,
|
||||||
|
Slli,
|
||||||
|
Srli,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IType {
|
impl IType {
|
||||||
@@ -131,9 +214,9 @@ impl IType {
|
|||||||
pub const ADDI: Self = Self::Addi(None);
|
pub const ADDI: Self = Self::Addi(None);
|
||||||
|
|
||||||
pub fn load(dtype: ir::Dtype) -> Self {
|
pub fn load(dtype: ir::Dtype) -> Self {
|
||||||
let data_align =
|
let data_size =
|
||||||
DataSize::try_from(dtype).expect("`data_align` must be derived from `dtype`");
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
Self::Load(data_align)
|
Self::Load(data_size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,12 +230,20 @@ impl SType {
|
|||||||
pub const SD: Self = Self::Store(DataSize::Double);
|
pub const SD: Self = Self::Store(DataSize::Double);
|
||||||
|
|
||||||
pub fn store(dtype: ir::Dtype) -> Self {
|
pub fn store(dtype: ir::Dtype) -> Self {
|
||||||
let data_align =
|
let data_size =
|
||||||
DataSize::try_from(dtype).expect("`data_align` must be derived from `dtype`");
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
Self::Store(data_align)
|
Self::Store(data_size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum BType {
|
||||||
|
Beq,
|
||||||
|
Bne,
|
||||||
|
Blt(bool),
|
||||||
|
Bge(bool),
|
||||||
|
}
|
||||||
|
|
||||||
/// The assembler implements a number of convenience psuedo-instructions that are formed from
|
/// The assembler implements a number of convenience psuedo-instructions that are formed from
|
||||||
/// instructions in the base ISA, but have implicit arguments or in some case reversed arguments,
|
/// instructions in the base ISA, but have implicit arguments or in some case reversed arguments,
|
||||||
/// that result in distinct semantics.
|
/// that result in distinct semantics.
|
||||||
@@ -163,9 +254,17 @@ pub enum Pseudo {
|
|||||||
/// li rd, immediate
|
/// li rd, immediate
|
||||||
Li { rd: Register, imm: isize },
|
Li { rd: Register, imm: isize },
|
||||||
/// mv rd, rs
|
/// mv rd, rs
|
||||||
Mv { rs: Register, rd: Register },
|
Mv { rd: Register, rs: Register },
|
||||||
|
/// neg(w) rd, rs
|
||||||
|
Neg {
|
||||||
|
data_size: Option<DataSize>,
|
||||||
|
rs: Register,
|
||||||
|
rd: Register,
|
||||||
|
},
|
||||||
/// sext.w rd, rs
|
/// sext.w rd, rs
|
||||||
SextW { rs: Register, rd: Register },
|
SextW { rd: Register, rs: Register },
|
||||||
|
/// seqz rd, rs
|
||||||
|
Seqz { rd: Register, rs: Register },
|
||||||
/// j offset
|
/// j offset
|
||||||
J { offset: Label },
|
J { offset: Label },
|
||||||
/// jr rs
|
/// jr rs
|
||||||
@@ -176,6 +275,20 @@ pub enum Pseudo {
|
|||||||
Call { offset: Label },
|
Call { offset: Label },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Pseudo {
|
||||||
|
pub fn neg(dtype: ir::Dtype, rs: Register, rd: Register) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
let data_size = if data_size == DataSize::Word {
|
||||||
|
Some(data_size)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::Neg { data_size, rs, rd }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `Label` is used as branch, unconditional jump targets and symbol offsets.
|
/// `Label` is used as branch, unconditional jump targets and symbol offsets.
|
||||||
/// https://github.com/rv8-io/rv8-io.github.io/blob/master/asm.md#labels
|
/// https://github.com/rv8-io/rv8-io.github.io/blob/master/asm.md#labels
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
@@ -220,7 +333,7 @@ impl TryFrom<ir::Dtype> for DataSize {
|
|||||||
|
|
||||||
// TODO: Add calling convention information (caller/callee-save registers)
|
// TODO: Add calling convention information (caller/callee-save registers)
|
||||||
/// ABI name for RISC-V integer and floating-point register
|
/// ABI name for RISC-V integer and floating-point register
|
||||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (109p)
|
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (155p)
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
||||||
pub enum Register {
|
pub enum Register {
|
||||||
Zero,
|
Zero,
|
||||||
@@ -237,10 +350,35 @@ pub enum Register {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Register {
|
impl Register {
|
||||||
// TODO: add all possible registers in the future
|
pub const T0: Self = Self::Temp(0);
|
||||||
|
pub const T1: Self = Self::Temp(1);
|
||||||
|
pub const T2: Self = Self::Temp(2);
|
||||||
|
pub const T3: Self = Self::Temp(3);
|
||||||
|
pub const T4: Self = Self::Temp(4);
|
||||||
|
pub const T5: Self = Self::Temp(5);
|
||||||
|
pub const T6: Self = Self::Temp(6);
|
||||||
|
|
||||||
pub const S0: Self = Self::Saved(0);
|
pub const S0: Self = Self::Saved(0);
|
||||||
|
pub const S1: Self = Self::Saved(1);
|
||||||
|
pub const S2: Self = Self::Saved(2);
|
||||||
|
pub const S3: Self = Self::Saved(3);
|
||||||
|
pub const S4: Self = Self::Saved(4);
|
||||||
|
pub const S5: Self = Self::Saved(5);
|
||||||
|
pub const S6: Self = Self::Saved(6);
|
||||||
|
pub const S7: Self = Self::Saved(7);
|
||||||
|
pub const S8: Self = Self::Saved(8);
|
||||||
|
pub const S9: Self = Self::Saved(9);
|
||||||
|
pub const S10: Self = Self::Saved(10);
|
||||||
|
pub const S11: Self = Self::Saved(11);
|
||||||
|
|
||||||
pub const A0: Self = Self::Arg(0);
|
pub const A0: Self = Self::Arg(0);
|
||||||
|
pub const A1: Self = Self::Arg(1);
|
||||||
|
pub const A2: Self = Self::Arg(2);
|
||||||
|
pub const A3: Self = Self::Arg(3);
|
||||||
|
pub const A4: Self = Self::Arg(4);
|
||||||
pub const A5: Self = Self::Arg(5);
|
pub const A5: Self = Self::Arg(5);
|
||||||
|
pub const A6: Self = Self::Arg(6);
|
||||||
|
pub const A7: Self = Self::Arg(7);
|
||||||
|
|
||||||
pub fn temp(id: usize) -> Self {
|
pub fn temp(id: usize) -> Self {
|
||||||
assert!(id <= 6);
|
assert!(id <= 6);
|
||||||
|
|||||||
@@ -137,9 +137,21 @@ impl WriteString for Instruction {
|
|||||||
"{}\t{},{}({})",
|
"{}\t{},{}({})",
|
||||||
instr.write_string(),
|
instr.write_string(),
|
||||||
rs2.write_string(),
|
rs2.write_string(),
|
||||||
imm,
|
imm.to_string(),
|
||||||
rs1.write_string()
|
rs1.write_string()
|
||||||
),
|
),
|
||||||
|
Self::BType {
|
||||||
|
instr,
|
||||||
|
rs1,
|
||||||
|
rs2,
|
||||||
|
imm,
|
||||||
|
} => format!(
|
||||||
|
"{}\t{},{}, {}",
|
||||||
|
instr.write_string(),
|
||||||
|
rs1.write_string(),
|
||||||
|
rs2.write_string(),
|
||||||
|
imm.0,
|
||||||
|
),
|
||||||
Self::Pseudo(pseudo) => pseudo.write_string(),
|
Self::Pseudo(pseudo) => pseudo.write_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,7 +161,15 @@ impl WriteString for RType {
|
|||||||
fn write_string(&self) -> String {
|
fn write_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::Add(data_size) => format!("add{}", data_size.write_string()),
|
Self::Add(data_size) => format!("add{}", data_size.write_string()),
|
||||||
|
Self::Sub(data_size) => format!("sub{}", data_size.write_string()),
|
||||||
Self::Mul(data_size) => format!("mul{}", data_size.write_string()),
|
Self::Mul(data_size) => format!("mul{}", data_size.write_string()),
|
||||||
|
Self::Div(data_size, is_signed) => format!(
|
||||||
|
"div{}{}",
|
||||||
|
if *is_signed { "" } else { "u" },
|
||||||
|
data_size.write_string()
|
||||||
|
),
|
||||||
|
Self::Slt(is_signed) => format!("slt{}", if *is_signed { "" } else { "u" }),
|
||||||
|
Self::Xor => "xor".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,6 +179,9 @@ impl WriteString for IType {
|
|||||||
match self {
|
match self {
|
||||||
Self::Load(data_size) => format!("l{}", data_size.write_string()),
|
Self::Load(data_size) => format!("l{}", data_size.write_string()),
|
||||||
Self::Addi(data_size) => format!("addi{}", data_size.write_string()),
|
Self::Addi(data_size) => format!("addi{}", data_size.write_string()),
|
||||||
|
Self::Andi => "andi".to_string(),
|
||||||
|
Self::Slli => "slli".to_string(),
|
||||||
|
Self::Srli => "srli".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,12 +194,32 @@ impl WriteString for SType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WriteString for BType {
|
||||||
|
fn write_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Beq => "beq".to_string(),
|
||||||
|
Self::Bne => "bne".to_string(),
|
||||||
|
Self::Blt(is_signed) => format!("blt{}", if *is_signed { "" } else { "u" }),
|
||||||
|
Self::Bge(is_signed) => format!("bge{}", if *is_signed { "" } else { "u" }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl WriteString for Pseudo {
|
impl WriteString for Pseudo {
|
||||||
fn write_string(&self) -> String {
|
fn write_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::Li { rd, imm } => format!("li\t{},{}", rd.write_string(), imm),
|
Self::Li { rd, imm } => format!("li\t{},{}", rd.write_string(), imm),
|
||||||
Self::Mv { rs, rd } => format!("mv\t{},{}", rd.write_string(), rs.write_string()),
|
Self::Mv { rs, rd } => format!("mv\t{},{}", rd.write_string(), rs.write_string()),
|
||||||
Self::SextW { .. } => todo!(),
|
Self::Neg { data_size, rs, rd } => format!(
|
||||||
|
"neg{}\t{},{}",
|
||||||
|
data_size.write_string(),
|
||||||
|
rd.write_string(),
|
||||||
|
rs.write_string()
|
||||||
|
),
|
||||||
|
Self::SextW { rs, rd } => {
|
||||||
|
format!("sext.w\t{},{}", rd.write_string(), rs.write_string())
|
||||||
|
}
|
||||||
|
Self::Seqz { rs, rd } => format!("seqz\t{},{}", rd.write_string(), rs.write_string()),
|
||||||
Self::J { offset } => format!("j\t{}", offset.0),
|
Self::J { offset } => format!("j\t{}", offset.0),
|
||||||
Self::Jr { rs } => format!("jr\t{}", rs.write_string()),
|
Self::Jr { rs } => format!("jr\t{}", rs.write_string()),
|
||||||
Self::Ret => "ret".to_string(),
|
Self::Ret => "ret".to_string(),
|
||||||
|
|||||||
@@ -1,24 +1,7 @@
|
|||||||
use crate::asm;
|
use crate::asm;
|
||||||
use crate::ir;
|
use crate::ir;
|
||||||
use crate::ir::HasDtype;
|
|
||||||
use crate::Translate;
|
use crate::Translate;
|
||||||
|
|
||||||
use core::ops::Deref;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
struct StackInfo {
|
|
||||||
/// Function name for generating the block labels
|
|
||||||
name: String,
|
|
||||||
/// Minimum stack size required to execute function
|
|
||||||
stack_size: usize,
|
|
||||||
/// Stack offsets of registers
|
|
||||||
stack_offsets_registers: HashMap<ir::RegisterId, usize>,
|
|
||||||
/// Stack offset for previous `ra` value
|
|
||||||
stack_offset_ra: usize,
|
|
||||||
/// Stack offset for previous `s0` value
|
|
||||||
stack_offset_s0: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Asmgen {}
|
pub struct Asmgen {}
|
||||||
|
|
||||||
@@ -26,487 +9,7 @@ impl Translate<ir::TranslationUnit> for Asmgen {
|
|||||||
type Target = asm::Asm;
|
type Target = asm::Asm;
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn translate(&mut self, source: &ir::TranslationUnit) -> Result<Self::Target, Self::Error> {
|
fn translate(&mut self, _source: &ir::TranslationUnit) -> Result<Self::Target, Self::Error> {
|
||||||
let mut functions = Vec::new();
|
todo!("homework 7")
|
||||||
let mut _variables = Vec::new();
|
|
||||||
for (name, decl) in &source.decls {
|
|
||||||
match decl {
|
|
||||||
ir::Declaration::Variable { .. } => todo!(),
|
|
||||||
ir::Declaration::Function {
|
|
||||||
signature,
|
|
||||||
definition,
|
|
||||||
} => {
|
|
||||||
let definition = definition
|
|
||||||
.as_ref()
|
|
||||||
.expect("function must have its definition");
|
|
||||||
let function = self.translate_function(
|
|
||||||
name.clone(),
|
|
||||||
signature,
|
|
||||||
definition,
|
|
||||||
&source.structs,
|
|
||||||
)?;
|
|
||||||
functions.push(function);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(asm::Asm {
|
|
||||||
unit: asm::TranslationUnit {
|
|
||||||
functions,
|
|
||||||
variables: _variables,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Asmgen {
|
|
||||||
const STACK_ALIGNMENT: usize = 16;
|
|
||||||
const SIZE_OF_S_ZERO: usize = 8;
|
|
||||||
const SIZE_OF_RETURN_ADDRESS: usize = 8;
|
|
||||||
|
|
||||||
fn translate_function(
|
|
||||||
&self,
|
|
||||||
name: String,
|
|
||||||
_signature: &ir::FunctionSignature,
|
|
||||||
definition: &ir::FunctionDefinition,
|
|
||||||
structs: &HashMap<String, Option<ir::Dtype>>,
|
|
||||||
) -> Result<asm::Section<asm::Function>, ()> {
|
|
||||||
// TODO: need to map function parameters and memory address
|
|
||||||
// Map `RegisterId` and its memory address. The memory address is represented
|
|
||||||
// by a distance from the stack pointer.
|
|
||||||
let mut stack_offsets_registers = HashMap::new();
|
|
||||||
|
|
||||||
// Allocate memory for return address and s0(frame pointer).
|
|
||||||
let mut required_size = Self::SIZE_OF_RETURN_ADDRESS + Self::SIZE_OF_S_ZERO;
|
|
||||||
|
|
||||||
// Allocate memory for local variables.
|
|
||||||
for (i, alloc) in definition.allocations.iter().enumerate() {
|
|
||||||
required_size += alloc.deref().size_align_of(structs).unwrap().0;
|
|
||||||
let rid = ir::RegisterId::local(i);
|
|
||||||
assert_eq!(stack_offsets_registers.insert(rid, required_size), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate memory for saved registers.
|
|
||||||
let required_size = definition
|
|
||||||
.blocks
|
|
||||||
.iter()
|
|
||||||
.fold(required_size, |size, (bid, block)| {
|
|
||||||
block
|
|
||||||
.instructions
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.fold(size, |size, (i, instr)| {
|
|
||||||
let size = size + instr.deref().dtype().size_align_of(structs).unwrap().0;
|
|
||||||
let rid = ir::RegisterId::temp(*bid, i);
|
|
||||||
assert_eq!(stack_offsets_registers.insert(rid, size), None);
|
|
||||||
size
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: Allocate memory for next function args
|
|
||||||
|
|
||||||
let stack_size = ((required_size - 1) / Self::STACK_ALIGNMENT + 1) * Self::STACK_ALIGNMENT;
|
|
||||||
let stack_offset_ra = stack_size - Self::SIZE_OF_RETURN_ADDRESS;
|
|
||||||
let stack_offset_s0 = stack_offset_ra - Self::SIZE_OF_S_ZERO;
|
|
||||||
let stack_info = StackInfo {
|
|
||||||
name,
|
|
||||||
stack_size,
|
|
||||||
stack_offsets_registers,
|
|
||||||
stack_offset_ra,
|
|
||||||
stack_offset_s0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut function_section = asm::Section::new(Vec::new(), asm::Function::new(Vec::new()));
|
|
||||||
|
|
||||||
self.translate_section_header(&stack_info, &mut function_section)?;
|
|
||||||
self.translate_init_block(&stack_info, &mut function_section)?;
|
|
||||||
definition
|
|
||||||
.blocks
|
|
||||||
.iter()
|
|
||||||
.map(|(bid, block)| {
|
|
||||||
self.translate_block(&stack_info, *bid, block, &mut function_section)
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
self.translate_final_block(&stack_info, &mut function_section)?;
|
|
||||||
|
|
||||||
Ok(function_section)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_section_header(
|
|
||||||
&self,
|
|
||||||
stack_info: &StackInfo,
|
|
||||||
function_section: &mut asm::Section<asm::Function>,
|
|
||||||
) -> Result<(), ()> {
|
|
||||||
// .globl func_name
|
|
||||||
function_section
|
|
||||||
.header
|
|
||||||
.push(asm::Directive::Globl(asm::Label(stack_info.name.clone())));
|
|
||||||
// .type func_name, @function
|
|
||||||
function_section.header.push(asm::Directive::Type(
|
|
||||||
asm::Label(stack_info.name.clone()),
|
|
||||||
asm::SymbolType::Function,
|
|
||||||
));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_init_block(
|
|
||||||
&self,
|
|
||||||
stack_info: &StackInfo,
|
|
||||||
function_section: &mut asm::Section<asm::Function>,
|
|
||||||
) -> Result<(), ()> {
|
|
||||||
let mut instrs = Vec::new();
|
|
||||||
// addi sp, sp, -stack_size
|
|
||||||
instrs.push(asm::Instruction::IType {
|
|
||||||
instr: asm::IType::ADDI,
|
|
||||||
rd: asm::Register::Sp,
|
|
||||||
rs1: asm::Register::Sp,
|
|
||||||
imm: -(stack_info.stack_size as isize),
|
|
||||||
});
|
|
||||||
// sd ra, stack_offset_ra(sp)
|
|
||||||
instrs.push(asm::Instruction::SType {
|
|
||||||
instr: asm::SType::SD,
|
|
||||||
rs2: asm::Register::Ra,
|
|
||||||
rs1: asm::Register::Sp,
|
|
||||||
imm: stack_info.stack_offset_ra as isize,
|
|
||||||
});
|
|
||||||
// sd s0, stack_offset_s0(sp)
|
|
||||||
instrs.push(asm::Instruction::SType {
|
|
||||||
instr: asm::SType::SD,
|
|
||||||
rs2: asm::Register::S0,
|
|
||||||
rs1: asm::Register::Sp,
|
|
||||||
imm: stack_info.stack_offset_s0 as isize,
|
|
||||||
});
|
|
||||||
// addi s0, sp, stack_size
|
|
||||||
instrs.push(asm::Instruction::IType {
|
|
||||||
instr: asm::IType::ADDI,
|
|
||||||
rd: asm::Register::S0,
|
|
||||||
rs1: asm::Register::Sp,
|
|
||||||
imm: stack_info.stack_size as isize,
|
|
||||||
});
|
|
||||||
|
|
||||||
let enter_block = asm::Block::new(Some(asm::Label(stack_info.name.clone())), instrs);
|
|
||||||
function_section.body.blocks.push(enter_block);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_final_block(
|
|
||||||
&self,
|
|
||||||
stack_info: &StackInfo,
|
|
||||||
function_section: &mut asm::Section<asm::Function>,
|
|
||||||
) -> Result<(), ()> {
|
|
||||||
let mut instrs = Vec::new();
|
|
||||||
// ld ra, stack_offset_ra(sp)
|
|
||||||
instrs.push(asm::Instruction::IType {
|
|
||||||
instr: asm::IType::LD,
|
|
||||||
rd: asm::Register::Ra,
|
|
||||||
rs1: asm::Register::Sp,
|
|
||||||
imm: stack_info.stack_offset_ra as isize,
|
|
||||||
});
|
|
||||||
// ld s0, stack_offset_s0(sp)
|
|
||||||
instrs.push(asm::Instruction::IType {
|
|
||||||
instr: asm::IType::LD,
|
|
||||||
rd: asm::Register::S0,
|
|
||||||
rs1: asm::Register::Sp,
|
|
||||||
imm: stack_info.stack_offset_s0 as isize,
|
|
||||||
});
|
|
||||||
// addi sp, sp, stack_size
|
|
||||||
instrs.push(asm::Instruction::IType {
|
|
||||||
instr: asm::IType::ADDI,
|
|
||||||
rd: asm::Register::Sp,
|
|
||||||
rs1: asm::Register::Sp,
|
|
||||||
imm: stack_info.stack_size as isize,
|
|
||||||
});
|
|
||||||
// jr ra
|
|
||||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::Jr {
|
|
||||||
rs: asm::Register::Ra,
|
|
||||||
}));
|
|
||||||
|
|
||||||
let exit_label = asm::Label(format!(".{}_END", stack_info.name));
|
|
||||||
let exit_block = asm::Block::new(Some(exit_label), instrs);
|
|
||||||
function_section.body.blocks.push(exit_block);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_block(
|
|
||||||
&self,
|
|
||||||
stack_info: &StackInfo,
|
|
||||||
bid: ir::BlockId,
|
|
||||||
block: &ir::Block,
|
|
||||||
function_section: &mut asm::Section<asm::Function>,
|
|
||||||
) -> Result<(), ()> {
|
|
||||||
let instrs_for_phinodes = block
|
|
||||||
.phinodes
|
|
||||||
.iter()
|
|
||||||
.map(|p| self.translate_phinode(stack_info, p.deref()))
|
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
let instrs_for_instructions = block
|
|
||||||
.instructions
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(iid, instr)| {
|
|
||||||
let dest_rid = ir::RegisterId::temp(bid, iid);
|
|
||||||
self.translate_instruction(stack_info, dest_rid, instr.deref())
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
let instrs_for_block_exit = self.translate_block_exit(stack_info, &block.exit)?;
|
|
||||||
|
|
||||||
let instrs = vec![
|
|
||||||
instrs_for_phinodes,
|
|
||||||
instrs_for_instructions,
|
|
||||||
instrs_for_block_exit,
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let label = asm::Label::new(&stack_info.name, bid);
|
|
||||||
let block = asm::Block::new(Some(label), instrs);
|
|
||||||
function_section.body.blocks.push(block);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_phinode(
|
|
||||||
&self,
|
|
||||||
_stack_info: &StackInfo,
|
|
||||||
_phinode: &ir::Dtype,
|
|
||||||
) -> Result<Vec<asm::Instruction>, ()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_instruction(
|
|
||||||
&self,
|
|
||||||
stack_info: &StackInfo,
|
|
||||||
dest_rid: ir::RegisterId,
|
|
||||||
instruction: &ir::Instruction,
|
|
||||||
) -> Result<Vec<asm::Instruction>, ()> {
|
|
||||||
let (mut instrs, rd) = match instruction {
|
|
||||||
ir::Instruction::Store { ptr, value } => {
|
|
||||||
let instrs = self.translate_store(stack_info, ptr, value)?;
|
|
||||||
return Ok(instrs);
|
|
||||||
}
|
|
||||||
ir::Instruction::Load { ptr } => self.translate_load(stack_info, ptr)?,
|
|
||||||
ir::Instruction::Call { callee, args, .. } => self.translate_call(callee, args)?,
|
|
||||||
_ => todo!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Store `rd` into memory
|
|
||||||
let dtype = instruction.dtype();
|
|
||||||
let dist_s0_to_ptr = stack_info
|
|
||||||
.stack_offsets_registers
|
|
||||||
.get(&dest_rid)
|
|
||||||
.expect("address matched with `rid` must exist");
|
|
||||||
let store_instr = asm::SType::store(dtype);
|
|
||||||
|
|
||||||
instrs.push(asm::Instruction::SType {
|
|
||||||
instr: store_instr,
|
|
||||||
rs2: rd,
|
|
||||||
rs1: asm::Register::S0,
|
|
||||||
imm: -(*dist_s0_to_ptr as isize),
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(instrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_store(
|
|
||||||
&self,
|
|
||||||
stack_info: &StackInfo,
|
|
||||||
ptr: &ir::Operand,
|
|
||||||
value: &ir::Operand,
|
|
||||||
) -> Result<Vec<asm::Instruction>, ()> {
|
|
||||||
let (instrs_for_value, reg_of_value) = self.translate_operand(stack_info, value)?;
|
|
||||||
|
|
||||||
let mut instrs = Vec::new();
|
|
||||||
let (rid, dtype) = ptr.get_register().expect("`ptr` must be register");
|
|
||||||
let dist_s0_to_ptr = stack_info
|
|
||||||
.stack_offsets_registers
|
|
||||||
.get(rid)
|
|
||||||
.expect("address matched with `rid` must exist");
|
|
||||||
let inner_dtype = dtype
|
|
||||||
.get_pointer_inner()
|
|
||||||
.expect("`dtype` must be pointer type");
|
|
||||||
let store_instr = asm::SType::store(inner_dtype.clone());
|
|
||||||
|
|
||||||
instrs.push(asm::Instruction::SType {
|
|
||||||
instr: store_instr,
|
|
||||||
rs2: reg_of_value,
|
|
||||||
rs1: asm::Register::S0,
|
|
||||||
imm: -(*dist_s0_to_ptr as isize),
|
|
||||||
});
|
|
||||||
|
|
||||||
let instrs = vec![instrs_for_value, instrs]
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
Ok(instrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_load(
|
|
||||||
&self,
|
|
||||||
stack_info: &StackInfo,
|
|
||||||
ptr: &ir::Operand,
|
|
||||||
) -> Result<(Vec<asm::Instruction>, asm::Register), ()> {
|
|
||||||
let mut instrs = Vec::new();
|
|
||||||
|
|
||||||
let (rid, dtype) = ptr.get_register().expect("`ptr` must be register");
|
|
||||||
let dist_s0_to_ptr = stack_info
|
|
||||||
.stack_offsets_registers
|
|
||||||
.get(rid)
|
|
||||||
.expect("address matched with `rid` must exist");
|
|
||||||
let inner_dtype = dtype
|
|
||||||
.get_pointer_inner()
|
|
||||||
.expect("`dtype` must be pointer type");
|
|
||||||
let load_instr = asm::IType::load(inner_dtype.clone());
|
|
||||||
|
|
||||||
// TODO: select register which is not occupied
|
|
||||||
let rd = asm::Register::A5;
|
|
||||||
instrs.push(asm::Instruction::IType {
|
|
||||||
instr: load_instr,
|
|
||||||
rd,
|
|
||||||
rs1: asm::Register::S0,
|
|
||||||
imm: -(*dist_s0_to_ptr as isize),
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok((instrs, rd))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_call(
|
|
||||||
&self,
|
|
||||||
callee: &ir::Operand,
|
|
||||||
_args: &[ir::Operand],
|
|
||||||
) -> Result<(Vec<asm::Instruction>, asm::Register), ()> {
|
|
||||||
let mut instrs = Vec::new();
|
|
||||||
|
|
||||||
// TODO: translate pass the args
|
|
||||||
|
|
||||||
match callee {
|
|
||||||
ir::Operand::Constant(constant) => {
|
|
||||||
if let ir::Constant::GlobalVariable { name, dtype } = constant {
|
|
||||||
assert!(dtype.get_function_inner().is_some());
|
|
||||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::Call {
|
|
||||||
offset: asm::Label(name.clone()),
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
panic!("`callee` must be `GlobalVariable`")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: select register which is not occupied
|
|
||||||
let rd = asm::Register::A5;
|
|
||||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::Mv {
|
|
||||||
rs: asm::Register::A0,
|
|
||||||
rd,
|
|
||||||
}));
|
|
||||||
|
|
||||||
Ok((instrs, rd))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_block_exit(
|
|
||||||
&self,
|
|
||||||
stack_info: &StackInfo,
|
|
||||||
block_exit: &ir::BlockExit,
|
|
||||||
) -> Result<Vec<asm::Instruction>, ()> {
|
|
||||||
match block_exit {
|
|
||||||
ir::BlockExit::Return { value } => self.translate_return(stack_info, value),
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_return(
|
|
||||||
&self,
|
|
||||||
stack_info: &StackInfo,
|
|
||||||
value: &ir::Operand,
|
|
||||||
) -> Result<Vec<asm::Instruction>, ()> {
|
|
||||||
let (instrs_for_value, rs) = self.translate_operand(stack_info, value)?;
|
|
||||||
|
|
||||||
let mut instrs = Vec::new();
|
|
||||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::Mv {
|
|
||||||
rs,
|
|
||||||
rd: asm::Register::A0,
|
|
||||||
}));
|
|
||||||
// Jump to exit block
|
|
||||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::J {
|
|
||||||
offset: asm::Label(format!(".{}_END", stack_info.name)),
|
|
||||||
}));
|
|
||||||
|
|
||||||
let instrs = vec![instrs_for_value, instrs]
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
Ok(instrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_operand(
|
|
||||||
&self,
|
|
||||||
stack_info: &StackInfo,
|
|
||||||
operand: &ir::Operand,
|
|
||||||
) -> Result<(Vec<asm::Instruction>, asm::Register), ()> {
|
|
||||||
match operand {
|
|
||||||
ir::Operand::Constant(constant) => self.translate_constant(stack_info, constant),
|
|
||||||
ir::Operand::Register { rid, dtype } => self.translate_register(stack_info, rid, dtype),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_constant(
|
|
||||||
&self,
|
|
||||||
_stack_info: &StackInfo,
|
|
||||||
constant: &ir::Constant,
|
|
||||||
) -> Result<(Vec<asm::Instruction>, asm::Register), ()> {
|
|
||||||
let mut instrs = Vec::new();
|
|
||||||
|
|
||||||
match constant {
|
|
||||||
// TODO: consider width and signed option in the future
|
|
||||||
ir::Constant::Int { value, .. } => {
|
|
||||||
// TODO: select register which is not occupied
|
|
||||||
let rd = asm::Register::A5;
|
|
||||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::Li {
|
|
||||||
rd,
|
|
||||||
imm: *value as isize,
|
|
||||||
}));
|
|
||||||
Ok((instrs, rd))
|
|
||||||
}
|
|
||||||
ir::Constant::Undef { .. } => {
|
|
||||||
// TODO: select register which is not occupied
|
|
||||||
Ok((instrs, asm::Register::A5))
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_register(
|
|
||||||
&self,
|
|
||||||
stack_info: &StackInfo,
|
|
||||||
rid: &ir::RegisterId,
|
|
||||||
dtype: &ir::Dtype,
|
|
||||||
) -> Result<(Vec<asm::Instruction>, asm::Register), ()> {
|
|
||||||
let mut instrs = Vec::new();
|
|
||||||
|
|
||||||
let dist_s0_to_ptr = stack_info
|
|
||||||
.stack_offsets_registers
|
|
||||||
.get(rid)
|
|
||||||
.expect("address matched with `rid` must exist");
|
|
||||||
let load_instr = asm::IType::load(dtype.clone());
|
|
||||||
|
|
||||||
// TODO: select register which is not occupied
|
|
||||||
let rd = asm::Register::A5;
|
|
||||||
instrs.push(asm::Instruction::IType {
|
|
||||||
instr: load_instr,
|
|
||||||
rd,
|
|
||||||
rs1: asm::Register::S0,
|
|
||||||
imm: -(*dist_s0_to_ptr as isize),
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok((instrs, rd))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -467,7 +467,7 @@ impl Dtype {
|
|||||||
pub const BITS_OF_BYTE: usize = 8;
|
pub const BITS_OF_BYTE: usize = 8;
|
||||||
pub const SIZE_OF_BYTE: usize = 1;
|
pub const SIZE_OF_BYTE: usize = 1;
|
||||||
// TODO: consider architecture dependency in the future
|
// TODO: consider architecture dependency in the future
|
||||||
pub const SIZE_OF_POINTER: usize = 4;
|
pub const SIZE_OF_POINTER: usize = 8;
|
||||||
|
|
||||||
pub const SIZE_OF_CHAR: usize = 1;
|
pub const SIZE_OF_CHAR: usize = 1;
|
||||||
pub const SIZE_OF_SHORT: usize = 2;
|
pub const SIZE_OF_SHORT: usize = 2;
|
||||||
|
|||||||
@@ -2,8 +2,12 @@ use crate::ir::*;
|
|||||||
use crate::opt::FunctionPass;
|
use crate::opt::FunctionPass;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub type SimplifyCfg =
|
pub type SimplifyCfg = FunctionPass<
|
||||||
FunctionPass<Repeat<(SimplifyCfgConstProp, (SimplifyCfgReach, (SimplifyCfgMerge, SimplifyCfgEmpty)))>>;
|
Repeat<(
|
||||||
|
SimplifyCfgConstProp,
|
||||||
|
(SimplifyCfgReach, (SimplifyCfgMerge, SimplifyCfgEmpty)),
|
||||||
|
)>,
|
||||||
|
>;
|
||||||
|
|
||||||
/// Simplifies block exits by propagating constants.
|
/// Simplifies block exits by propagating constants.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|||||||
136
src/tests.rs
136
src/tests.rs
@@ -220,3 +220,139 @@ pub fn test_opt<P1: AsRef<Path>, P2: AsRef<Path>, O: Optimize<ir::TranslationUni
|
|||||||
panic!("[test_opt]");
|
panic!("[test_opt]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn test_asmgen(unit: &TranslationUnit, path: &Path) {
|
||||||
|
// Check if the file has .c extension
|
||||||
|
assert_eq!(path.extension(), Some(std::ffi::OsStr::new("c")));
|
||||||
|
|
||||||
|
// Test parse
|
||||||
|
c::Parse::default()
|
||||||
|
.translate(&path)
|
||||||
|
.expect("failed to parse the given program");
|
||||||
|
|
||||||
|
let file_path = path.display().to_string();
|
||||||
|
let bin_path = path.with_extension("irgen").as_path().display().to_string();
|
||||||
|
|
||||||
|
// Compile c file: If fails, test is vacuously success
|
||||||
|
if !Command::new("gcc")
|
||||||
|
.args(&[
|
||||||
|
"-fsanitize=undefined",
|
||||||
|
"-fno-sanitize-recover=all",
|
||||||
|
"-O1",
|
||||||
|
&file_path,
|
||||||
|
"-o",
|
||||||
|
&bin_path,
|
||||||
|
])
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
.status()
|
||||||
|
.unwrap()
|
||||||
|
.success()
|
||||||
|
{
|
||||||
|
::std::process::exit(SKIP_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute compiled executable
|
||||||
|
let mut child = Command::new(fs::canonicalize(bin_path.clone()).unwrap())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to execute the compiled executable");
|
||||||
|
|
||||||
|
Command::new("rm")
|
||||||
|
.arg(bin_path)
|
||||||
|
.status()
|
||||||
|
.expect("failed to remove compiled executable");
|
||||||
|
|
||||||
|
let status = some_or!(
|
||||||
|
child
|
||||||
|
.wait_timeout_ms(500)
|
||||||
|
.expect("failed to obtain exit status from child process"),
|
||||||
|
{
|
||||||
|
println!("timeout occurs");
|
||||||
|
child.kill().unwrap();
|
||||||
|
child.wait().unwrap();
|
||||||
|
::std::process::exit(SKIP_TEST);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if child
|
||||||
|
.stderr
|
||||||
|
.expect("`stderr` of `child` must be `Some`")
|
||||||
|
.bytes()
|
||||||
|
.next()
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
println!("error occurs");
|
||||||
|
::std::process::exit(SKIP_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
let gcc_status = some_or_exit!(status.code(), SKIP_TEST);
|
||||||
|
|
||||||
|
let ir = match Irgen::default().translate(unit) {
|
||||||
|
Ok(ir) => ir,
|
||||||
|
Err(irgen_error) => panic!("{}", irgen_error),
|
||||||
|
};
|
||||||
|
|
||||||
|
let asm = Asmgen::default()
|
||||||
|
.translate(&ir)
|
||||||
|
.expect("fail to create riscv assembly code");
|
||||||
|
let asm_path = path.with_extension("S").as_path().display().to_string();
|
||||||
|
let mut buffer = File::create(Path::new(&asm_path)).expect("need to success creating file");
|
||||||
|
write(&asm, &mut buffer).unwrap();
|
||||||
|
|
||||||
|
// Link to an RISC-V executable
|
||||||
|
let bin_path = path.with_extension("asm").as_path().display().to_string();
|
||||||
|
if !Command::new("riscv64-linux-gnu-gcc-10")
|
||||||
|
.args(&["-static", &asm_path, "-o", &bin_path])
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
.status()
|
||||||
|
.unwrap()
|
||||||
|
.success()
|
||||||
|
{
|
||||||
|
::std::process::exit(SKIP_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::new("rm")
|
||||||
|
.arg(asm_path)
|
||||||
|
.status()
|
||||||
|
.expect("failed to remove assembly code file");
|
||||||
|
|
||||||
|
// Emulate the executable
|
||||||
|
let mut child = Command::new("qemu-riscv64-static")
|
||||||
|
.args(&[&bin_path])
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to execute the compiled executable");
|
||||||
|
|
||||||
|
Command::new("rm")
|
||||||
|
.arg(bin_path)
|
||||||
|
.status()
|
||||||
|
.expect("failed to remove compiled executable");
|
||||||
|
|
||||||
|
let status = some_or!(
|
||||||
|
child
|
||||||
|
.wait_timeout_ms(500)
|
||||||
|
.expect("failed to obtain exit status from child process"),
|
||||||
|
{
|
||||||
|
println!("timeout occurs");
|
||||||
|
child.kill().unwrap();
|
||||||
|
child.wait().unwrap();
|
||||||
|
::std::process::exit(SKIP_TEST);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if child
|
||||||
|
.stderr
|
||||||
|
.expect("`stderr` of `child` must be `Some`")
|
||||||
|
.bytes()
|
||||||
|
.next()
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
println!("error occurs");
|
||||||
|
::std::process::exit(SKIP_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
let kecc_status = some_or_exit!(status.code(), SKIP_TEST);
|
||||||
|
|
||||||
|
println!("gcc: {}, kecc: {}", gcc_status, kecc_status);
|
||||||
|
assert_eq!(gcc_status, kecc_status);
|
||||||
|
}
|
||||||
|
|||||||
@@ -101,3 +101,9 @@ fn test_examples_deadcode() {
|
|||||||
&mut Deadcode::default(),
|
&mut Deadcode::default(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_examples_asmgen() {
|
||||||
|
test_dir(Path::new("examples/asm"), &OsStr::new("c"), test_asmgen);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user