mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-16 07:28:52 +00:00
Update skeleton
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
int main() {
|
|
||||||
return _Alignof(const int) == 4;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
int fibonacci(int n) {
|
|
||||||
if (n < 2) {
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fibonacci(n - 2) + fibonacci(n - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
return fibonacci(9) == 34;
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
int main()
|
|
||||||
{
|
|
||||||
int x = 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
548
src/asm/mod.rs
548
src/asm/mod.rs
@@ -49,7 +49,14 @@ impl Function {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Variable {
|
pub struct Variable {
|
||||||
todo: TODO,
|
pub label: Label,
|
||||||
|
pub directives: Vec<Directive>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Variable {
|
||||||
|
pub fn new(label: Label, directives: Vec<Directive>) -> Self {
|
||||||
|
Self { label, directives }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@@ -74,8 +81,39 @@ impl Block {
|
|||||||
pub enum Directive {
|
pub enum Directive {
|
||||||
/// .globl symbol
|
/// .globl symbol
|
||||||
Globl(Label),
|
Globl(Label),
|
||||||
|
/// .section section_type
|
||||||
|
Section(SectionType),
|
||||||
/// .type symbol, symbol_type
|
/// .type symbol, symbol_type
|
||||||
Type(Label, SymbolType),
|
Type(Label, SymbolType),
|
||||||
|
/// .byte value
|
||||||
|
Byte(u8),
|
||||||
|
/// .half value
|
||||||
|
Half(u16),
|
||||||
|
/// .word value
|
||||||
|
Word(u32),
|
||||||
|
/// .quad value
|
||||||
|
Quad(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Directive {
|
||||||
|
pub fn try_from_data_size(data_size: DataSize, value: u64) -> Self {
|
||||||
|
match data_size {
|
||||||
|
DataSize::Byte => Self::Byte(value as u8),
|
||||||
|
DataSize::Half => Self::Half(value as u16),
|
||||||
|
DataSize::Word => Self::Word(value as u32),
|
||||||
|
DataSize::Double => Self::Quad(value),
|
||||||
|
DataSize::SinglePrecision => Self::Word(value as u32),
|
||||||
|
DataSize::DoublePrecision => Self::Quad(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum SectionType {
|
||||||
|
Text,
|
||||||
|
Data,
|
||||||
|
Rodata,
|
||||||
|
Bss,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@@ -92,7 +130,7 @@ pub enum Instruction {
|
|||||||
instr: RType,
|
instr: RType,
|
||||||
rd: Register,
|
rd: Register,
|
||||||
rs1: Register,
|
rs1: Register,
|
||||||
rs2: Register,
|
rs2: Option<Register>,
|
||||||
},
|
},
|
||||||
/// I-type instruction format
|
/// I-type instruction format
|
||||||
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
||||||
@@ -100,7 +138,7 @@ pub enum Instruction {
|
|||||||
instr: IType,
|
instr: IType,
|
||||||
rd: Register,
|
rd: Register,
|
||||||
rs1: Register,
|
rs1: Register,
|
||||||
imm: isize,
|
imm: Immediate,
|
||||||
},
|
},
|
||||||
/// S-type instruction format
|
/// S-type instruction format
|
||||||
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
||||||
@@ -108,7 +146,7 @@ pub enum Instruction {
|
|||||||
instr: SType,
|
instr: SType,
|
||||||
rs1: Register,
|
rs1: Register,
|
||||||
rs2: Register,
|
rs2: Register,
|
||||||
imm: isize,
|
imm: Immediate,
|
||||||
},
|
},
|
||||||
/// B-type instruction format
|
/// B-type instruction format
|
||||||
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
||||||
@@ -118,6 +156,11 @@ pub enum Instruction {
|
|||||||
rs2: Register,
|
rs2: Register,
|
||||||
imm: Label,
|
imm: Label,
|
||||||
},
|
},
|
||||||
|
UType {
|
||||||
|
instr: UType,
|
||||||
|
rd: Register,
|
||||||
|
imm: Immediate,
|
||||||
|
},
|
||||||
Pseudo(Pseudo),
|
Pseudo(Pseudo),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,59 +179,226 @@ pub enum Instruction {
|
|||||||
pub enum RType {
|
pub enum RType {
|
||||||
Add(Option<DataSize>),
|
Add(Option<DataSize>),
|
||||||
Sub(Option<DataSize>),
|
Sub(Option<DataSize>),
|
||||||
|
Sll(Option<DataSize>),
|
||||||
|
Srl(Option<DataSize>),
|
||||||
|
Sra(Option<DataSize>),
|
||||||
Mul(Option<DataSize>),
|
Mul(Option<DataSize>),
|
||||||
Div(Option<DataSize>, bool),
|
Div {
|
||||||
Slt(bool),
|
data_size: Option<DataSize>,
|
||||||
|
is_signed: bool,
|
||||||
|
},
|
||||||
|
Rem {
|
||||||
|
data_size: Option<DataSize>,
|
||||||
|
is_signed: bool,
|
||||||
|
},
|
||||||
|
Slt {
|
||||||
|
is_signed: bool,
|
||||||
|
},
|
||||||
Xor,
|
Xor,
|
||||||
|
Or,
|
||||||
|
And,
|
||||||
|
Fadd(DataSize),
|
||||||
|
Fsub(DataSize),
|
||||||
|
Fmul(DataSize),
|
||||||
|
Fdiv(DataSize),
|
||||||
|
Feq(DataSize),
|
||||||
|
Flt(DataSize),
|
||||||
|
/// fmv.w.x or fmv.d.x
|
||||||
|
FmvIntToFloat {
|
||||||
|
float_data_size: DataSize,
|
||||||
|
},
|
||||||
|
/// fmv.x.w or fmv.x.w
|
||||||
|
FmvFloatToInt {
|
||||||
|
float_data_size: DataSize,
|
||||||
|
},
|
||||||
|
/// fcvt.s.l(u) or fcvt.d.l(u)
|
||||||
|
/// fcvt.s.w(u) or fcvt.d.w(u)
|
||||||
|
FcvtIntToFloat {
|
||||||
|
int_data_size: Option<DataSize>,
|
||||||
|
float_data_size: DataSize,
|
||||||
|
is_signed: bool,
|
||||||
|
},
|
||||||
|
/// fcvt.l(u).s or fcvt.l(u).d
|
||||||
|
/// fcvt.w(u).s or fcvt.w(u).d
|
||||||
|
FcvtFloatToInt {
|
||||||
|
float_data_size: DataSize,
|
||||||
|
int_data_size: Option<DataSize>,
|
||||||
|
is_signed: bool,
|
||||||
|
},
|
||||||
|
/// fcvt.s.d or fcvt.d.s
|
||||||
|
FcvtFloatToFloat {
|
||||||
|
from: DataSize,
|
||||||
|
to: DataSize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RType {
|
impl RType {
|
||||||
pub fn add(dtype: ir::Dtype) -> Self {
|
pub fn add(dtype: ir::Dtype) -> Self {
|
||||||
let data_size =
|
let data_size =
|
||||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
let data_size = if data_size == DataSize::Word {
|
assert!(data_size.is_integer());
|
||||||
Some(data_size)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::Add(data_size)
|
Self::Add(data_size.word())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sub(dtype: ir::Dtype) -> Self {
|
pub fn sub(dtype: ir::Dtype) -> Self {
|
||||||
let data_size =
|
let data_size =
|
||||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
let data_size = if data_size == DataSize::Word {
|
assert!(data_size.is_integer());
|
||||||
Some(data_size)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::Sub(data_size)
|
Self::Sub(data_size.word())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sll(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(data_size.is_integer());
|
||||||
|
|
||||||
|
Self::Sll(data_size.word())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn srl(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(data_size.is_integer());
|
||||||
|
|
||||||
|
Self::Srl(data_size.word())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sra(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(data_size.is_integer());
|
||||||
|
|
||||||
|
Self::Sra(data_size.word())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mul(dtype: ir::Dtype) -> Self {
|
pub fn mul(dtype: ir::Dtype) -> Self {
|
||||||
let data_size =
|
let data_size =
|
||||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
let data_size = if data_size == DataSize::Word {
|
assert!(data_size.is_integer());
|
||||||
Some(data_size)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::Mul(data_size)
|
Self::Mul(data_size.word())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn div(dtype: ir::Dtype, is_signed: bool) -> Self {
|
pub fn div(dtype: ir::Dtype, is_signed: bool) -> Self {
|
||||||
let data_size =
|
let data_size =
|
||||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
let data_size = if data_size == DataSize::Word {
|
assert!(data_size.is_integer());
|
||||||
Some(data_size)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::Div(data_size, is_signed)
|
Self::Div {
|
||||||
|
data_size: data_size.word(),
|
||||||
|
is_signed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rem(dtype: ir::Dtype, is_signed: bool) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(data_size.is_integer());
|
||||||
|
|
||||||
|
Self::Rem {
|
||||||
|
data_size: data_size.word(),
|
||||||
|
is_signed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fadd(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(data_size.is_floating_point());
|
||||||
|
|
||||||
|
Self::Fadd(data_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fsub(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(data_size.is_floating_point());
|
||||||
|
|
||||||
|
Self::Fsub(data_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fmul(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(data_size.is_floating_point());
|
||||||
|
|
||||||
|
Self::Fmul(data_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fdiv(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(data_size.is_floating_point());
|
||||||
|
|
||||||
|
Self::Fdiv(data_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn feq(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(data_size.is_floating_point());
|
||||||
|
|
||||||
|
Self::Feq(data_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flt(dtype: ir::Dtype) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(data_size.is_floating_point());
|
||||||
|
|
||||||
|
Self::Flt(data_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fmv_int_to_float(dtype: ir::Dtype) -> Self {
|
||||||
|
let float_data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(float_data_size.is_floating_point());
|
||||||
|
|
||||||
|
Self::FmvIntToFloat { float_data_size }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fmv_float_to_int(dtype: ir::Dtype) -> Self {
|
||||||
|
let float_data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(float_data_size.is_floating_point());
|
||||||
|
|
||||||
|
Self::FmvFloatToInt { float_data_size }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fcvt_int_to_float(from: ir::Dtype, to: ir::Dtype) -> Self {
|
||||||
|
let is_signed = from.is_int_signed();
|
||||||
|
let int_data_size =
|
||||||
|
DataSize::try_from(from).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(int_data_size.is_integer());
|
||||||
|
|
||||||
|
let float_data_size =
|
||||||
|
DataSize::try_from(to).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(float_data_size.is_floating_point());
|
||||||
|
|
||||||
|
Self::FcvtIntToFloat {
|
||||||
|
int_data_size: int_data_size.word(),
|
||||||
|
float_data_size,
|
||||||
|
is_signed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fcvt_float_to_int(from: ir::Dtype, to: ir::Dtype) -> Self {
|
||||||
|
let float_data_size =
|
||||||
|
DataSize::try_from(from).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(float_data_size.is_floating_point());
|
||||||
|
|
||||||
|
let is_signed = to.is_int_signed();
|
||||||
|
let int_data_size =
|
||||||
|
DataSize::try_from(to).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(int_data_size.is_integer());
|
||||||
|
|
||||||
|
Self::FcvtFloatToInt {
|
||||||
|
float_data_size,
|
||||||
|
int_data_size: int_data_size.word(),
|
||||||
|
is_signed,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,22 +411,49 @@ impl RType {
|
|||||||
/// https://riscv.org/specifications/isa-spec-pdf/ (35p)
|
/// 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 {
|
||||||
|
data_size: DataSize,
|
||||||
|
is_signed: bool,
|
||||||
|
},
|
||||||
Addi(Option<DataSize>),
|
Addi(Option<DataSize>),
|
||||||
|
Xori,
|
||||||
|
Ori,
|
||||||
Andi,
|
Andi,
|
||||||
Slli,
|
Slli,
|
||||||
Srli,
|
Srli,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IType {
|
impl IType {
|
||||||
pub const LW: Self = Self::Load(DataSize::Word);
|
pub const LW: Self = Self::Load {
|
||||||
pub const LD: Self = Self::Load(DataSize::Double);
|
data_size: DataSize::Word,
|
||||||
|
is_signed: true,
|
||||||
|
};
|
||||||
|
pub const LD: Self = Self::Load {
|
||||||
|
data_size: DataSize::Double,
|
||||||
|
is_signed: true,
|
||||||
|
};
|
||||||
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_size =
|
let data_size =
|
||||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
DataSize::try_from(dtype.clone()).expect("`data_size` must be derived from `dtype`");
|
||||||
Self::Load(data_size)
|
|
||||||
|
let is_signed = if dtype.get_int_width().is_some() {
|
||||||
|
dtype.is_int_signed()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_signed = if data_size == DataSize::Double {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
is_signed
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::Load {
|
||||||
|
data_size,
|
||||||
|
is_signed,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,35 +477,54 @@ impl SType {
|
|||||||
pub enum BType {
|
pub enum BType {
|
||||||
Beq,
|
Beq,
|
||||||
Bne,
|
Bne,
|
||||||
Blt(bool),
|
Blt { is_signed: bool },
|
||||||
Bge(bool),
|
Bge { is_signed: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum UType {
|
||||||
|
Lui,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
/// https://github.com/rv8-io/rv8-io.github.io/blob/master/asm.md#assembler-pseudo-instructions
|
/// https://github.com/rv8-io/rv8-io.github.io/blob/master/asm.md#assembler-pseudo-instructions
|
||||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (110p)
|
/// https://riscv.org/specifications/isa-spec-pdf/ (139p)
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Pseudo {
|
pub enum Pseudo {
|
||||||
/// li rd, immediate
|
/// li rd, immediate
|
||||||
Li { rd: Register, imm: isize },
|
Li {
|
||||||
|
rd: Register,
|
||||||
|
// TODO: consider architecture dependency (current: 64-bit architecture)
|
||||||
|
imm: u64,
|
||||||
|
},
|
||||||
/// mv rd, rs
|
/// mv rd, rs
|
||||||
Mv { rd: Register, rs: Register },
|
Mv { rd: Register, rs: Register },
|
||||||
/// neg(w) rd, rs
|
/// neg(w) rd, rs
|
||||||
Neg {
|
Neg {
|
||||||
data_size: Option<DataSize>,
|
data_size: Option<DataSize>,
|
||||||
rs: Register,
|
|
||||||
rd: Register,
|
rd: Register,
|
||||||
|
rs: Register,
|
||||||
},
|
},
|
||||||
/// sext.w rd, rs
|
/// sext.w rd, rs
|
||||||
SextW { rd: Register, rs: Register },
|
SextW { rd: Register, rs: Register },
|
||||||
/// seqz rd, rs
|
/// seqz rd, rs
|
||||||
Seqz { rd: Register, rs: Register },
|
Seqz { rd: Register, rs: Register },
|
||||||
|
/// snez rd, rs
|
||||||
|
Snez { rd: Register, rs: Register },
|
||||||
|
/// fneg.s rd, rs or fneg.d rd, rs
|
||||||
|
Fneg {
|
||||||
|
data_size: DataSize,
|
||||||
|
rd: Register,
|
||||||
|
rs: Register,
|
||||||
|
},
|
||||||
/// j offset
|
/// j offset
|
||||||
J { offset: Label },
|
J { offset: Label },
|
||||||
/// jr rs
|
/// jr rs
|
||||||
Jr { rs: Register },
|
Jr { rs: Register },
|
||||||
|
/// jalr rs
|
||||||
|
Jalr { rs: Register },
|
||||||
/// ret
|
/// ret
|
||||||
Ret,
|
Ret,
|
||||||
/// call offset
|
/// call offset
|
||||||
@@ -276,7 +532,7 @@ pub enum Pseudo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Pseudo {
|
impl Pseudo {
|
||||||
pub fn neg(dtype: ir::Dtype, rs: Register, rd: Register) -> Self {
|
pub fn neg(dtype: ir::Dtype, rd: Register, rs: Register) -> Self {
|
||||||
let data_size =
|
let data_size =
|
||||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
let data_size = if data_size == DataSize::Word {
|
let data_size = if data_size == DataSize::Word {
|
||||||
@@ -285,8 +541,44 @@ impl Pseudo {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::Neg { data_size, rs, rd }
|
Self::Neg { data_size, rd, rs }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fneg(dtype: ir::Dtype, rd: Register, rs: Register) -> Self {
|
||||||
|
let data_size =
|
||||||
|
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||||
|
assert!(data_size.is_floating_point());
|
||||||
|
|
||||||
|
Self::Fneg { data_size, rd, rs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Immediate {
|
||||||
|
// TODO: consider architecture dependency (current: 64-bit architecture)
|
||||||
|
Value(u64),
|
||||||
|
/// %hi(symbol) or %lo(symbol)
|
||||||
|
Relocation {
|
||||||
|
relocation: RelocationFunction,
|
||||||
|
symbol: Label,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Immediate {
|
||||||
|
pub fn relocation(relocation: RelocationFunction, symbol: Label) -> Self {
|
||||||
|
Self::Relocation { relocation, symbol }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The relocation function creates synthesize operand values that are resolved
|
||||||
|
/// at program link time and are used as immediate parameters to specific instructions.
|
||||||
|
/// https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum RelocationFunction {
|
||||||
|
/// %hi
|
||||||
|
HI20,
|
||||||
|
/// %lo
|
||||||
|
LO12,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Label` is used as branch, unconditional jump targets and symbol offsets.
|
/// `Label` is used as branch, unconditional jump targets and symbol offsets.
|
||||||
@@ -307,23 +599,34 @@ pub enum DataSize {
|
|||||||
Half,
|
Half,
|
||||||
Word,
|
Word,
|
||||||
Double,
|
Double,
|
||||||
|
SinglePrecision,
|
||||||
|
DoublePrecision,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<ir::Dtype> for DataSize {
|
impl TryFrom<ir::Dtype> for DataSize {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(dtype: ir::Dtype) -> Result<Self, Self::Error> {
|
fn try_from(dtype: ir::Dtype) -> Result<Self, Self::Error> {
|
||||||
let width = match dtype {
|
let (size, is_float) = match dtype {
|
||||||
ir::Dtype::Int { width, .. } => width,
|
ir::Dtype::Int { width, .. } => {
|
||||||
_ => todo!(),
|
let size = (width - 1) / ir::Dtype::BITS_OF_BYTE + 1;
|
||||||
|
(size, false)
|
||||||
|
}
|
||||||
|
ir::Dtype::Float { width, .. } => {
|
||||||
|
let size = (width - 1) / ir::Dtype::BITS_OF_BYTE + 1;
|
||||||
|
(size, true)
|
||||||
|
}
|
||||||
|
ir::Dtype::Pointer { .. } => (ir::Dtype::SIZE_OF_POINTER, false),
|
||||||
|
_ => todo!("DataSize::try_from: support dtype: {:?}", dtype),
|
||||||
};
|
};
|
||||||
|
|
||||||
let size = (width - 1) / ir::Dtype::BITS_OF_BYTE + 1;
|
let align = match (size, is_float) {
|
||||||
let align = match size {
|
(ir::Dtype::SIZE_OF_CHAR, false) => Self::Byte,
|
||||||
ir::Dtype::SIZE_OF_CHAR => Self::Byte,
|
(ir::Dtype::SIZE_OF_SHORT, false) => Self::Half,
|
||||||
ir::Dtype::SIZE_OF_SHORT => Self::Half,
|
(ir::Dtype::SIZE_OF_INT, false) => Self::Word,
|
||||||
ir::Dtype::SIZE_OF_INT => Self::Word,
|
(ir::Dtype::SIZE_OF_LONG, false) => Self::Double,
|
||||||
ir::Dtype::SIZE_OF_LONG => Self::Double,
|
(ir::Dtype::SIZE_OF_FLOAT, true) => Self::SinglePrecision,
|
||||||
|
(ir::Dtype::SIZE_OF_DOUBLE, true) => Self::DoublePrecision,
|
||||||
_ => panic!("there is no other possible case"),
|
_ => panic!("there is no other possible case"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -331,6 +634,30 @@ impl TryFrom<ir::Dtype> for DataSize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DataSize {
|
||||||
|
pub fn is_integer(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Byte | Self::Half | Self::Word | Self::Double => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_floating_point(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::SinglePrecision | Self::DoublePrecision => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn word(self) -> Option<Self> {
|
||||||
|
if self == DataSize::Word {
|
||||||
|
Some(self)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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 (155p)
|
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (155p)
|
||||||
@@ -342,56 +669,101 @@ pub enum Register {
|
|||||||
Gp,
|
Gp,
|
||||||
Tp,
|
Tp,
|
||||||
/// E.g., t0
|
/// E.g., t0
|
||||||
Temp(usize),
|
Temp(RegisterType, usize),
|
||||||
/// E.g., s0
|
/// E.g., s0
|
||||||
Saved(usize),
|
Saved(RegisterType, usize),
|
||||||
/// E.g., a0
|
/// E.g., a0
|
||||||
Arg(usize),
|
Arg(RegisterType, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Register {
|
impl Register {
|
||||||
pub const T0: Self = Self::Temp(0);
|
pub const T0: Self = Self::Temp(RegisterType::Integer, 0);
|
||||||
pub const T1: Self = Self::Temp(1);
|
pub const T1: Self = Self::Temp(RegisterType::Integer, 1);
|
||||||
pub const T2: Self = Self::Temp(2);
|
pub const T2: Self = Self::Temp(RegisterType::Integer, 2);
|
||||||
pub const T3: Self = Self::Temp(3);
|
pub const T3: Self = Self::Temp(RegisterType::Integer, 3);
|
||||||
pub const T4: Self = Self::Temp(4);
|
pub const T4: Self = Self::Temp(RegisterType::Integer, 4);
|
||||||
pub const T5: Self = Self::Temp(5);
|
pub const T5: Self = Self::Temp(RegisterType::Integer, 5);
|
||||||
pub const T6: Self = Self::Temp(6);
|
pub const T6: Self = Self::Temp(RegisterType::Integer, 6);
|
||||||
|
|
||||||
pub const S0: Self = Self::Saved(0);
|
pub const S0: Self = Self::Saved(RegisterType::Integer, 0);
|
||||||
pub const S1: Self = Self::Saved(1);
|
pub const S1: Self = Self::Saved(RegisterType::Integer, 1);
|
||||||
pub const S2: Self = Self::Saved(2);
|
pub const S2: Self = Self::Saved(RegisterType::Integer, 2);
|
||||||
pub const S3: Self = Self::Saved(3);
|
pub const S3: Self = Self::Saved(RegisterType::Integer, 3);
|
||||||
pub const S4: Self = Self::Saved(4);
|
pub const S4: Self = Self::Saved(RegisterType::Integer, 4);
|
||||||
pub const S5: Self = Self::Saved(5);
|
pub const S5: Self = Self::Saved(RegisterType::Integer, 5);
|
||||||
pub const S6: Self = Self::Saved(6);
|
pub const S6: Self = Self::Saved(RegisterType::Integer, 6);
|
||||||
pub const S7: Self = Self::Saved(7);
|
pub const S7: Self = Self::Saved(RegisterType::Integer, 7);
|
||||||
pub const S8: Self = Self::Saved(8);
|
pub const S8: Self = Self::Saved(RegisterType::Integer, 8);
|
||||||
pub const S9: Self = Self::Saved(9);
|
pub const S9: Self = Self::Saved(RegisterType::Integer, 9);
|
||||||
pub const S10: Self = Self::Saved(10);
|
pub const S10: Self = Self::Saved(RegisterType::Integer, 10);
|
||||||
pub const S11: Self = Self::Saved(11);
|
pub const S11: Self = Self::Saved(RegisterType::Integer, 11);
|
||||||
|
|
||||||
pub const A0: Self = Self::Arg(0);
|
pub const A0: Self = Self::Arg(RegisterType::Integer, 0);
|
||||||
pub const A1: Self = Self::Arg(1);
|
pub const A1: Self = Self::Arg(RegisterType::Integer, 1);
|
||||||
pub const A2: Self = Self::Arg(2);
|
pub const A2: Self = Self::Arg(RegisterType::Integer, 2);
|
||||||
pub const A3: Self = Self::Arg(3);
|
pub const A3: Self = Self::Arg(RegisterType::Integer, 3);
|
||||||
pub const A4: Self = Self::Arg(4);
|
pub const A4: Self = Self::Arg(RegisterType::Integer, 4);
|
||||||
pub const A5: Self = Self::Arg(5);
|
pub const A5: Self = Self::Arg(RegisterType::Integer, 5);
|
||||||
pub const A6: Self = Self::Arg(6);
|
pub const A6: Self = Self::Arg(RegisterType::Integer, 6);
|
||||||
pub const A7: Self = Self::Arg(7);
|
pub const A7: Self = Self::Arg(RegisterType::Integer, 7);
|
||||||
|
|
||||||
pub fn temp(id: usize) -> Self {
|
pub const FT0: Self = Self::Temp(RegisterType::FloatingPoint, 0);
|
||||||
assert!(id <= 6);
|
pub const FT1: Self = Self::Temp(RegisterType::FloatingPoint, 1);
|
||||||
Self::Temp(id)
|
pub const FT2: Self = Self::Temp(RegisterType::FloatingPoint, 2);
|
||||||
|
pub const FT3: Self = Self::Temp(RegisterType::FloatingPoint, 3);
|
||||||
|
pub const FT4: Self = Self::Temp(RegisterType::FloatingPoint, 4);
|
||||||
|
pub const FT5: Self = Self::Temp(RegisterType::FloatingPoint, 5);
|
||||||
|
pub const FT6: Self = Self::Temp(RegisterType::FloatingPoint, 6);
|
||||||
|
pub const FT7: Self = Self::Temp(RegisterType::FloatingPoint, 7);
|
||||||
|
pub const FT8: Self = Self::Temp(RegisterType::FloatingPoint, 8);
|
||||||
|
pub const FT9: Self = Self::Temp(RegisterType::FloatingPoint, 9);
|
||||||
|
pub const FT10: Self = Self::Temp(RegisterType::FloatingPoint, 10);
|
||||||
|
pub const FT11: Self = Self::Temp(RegisterType::FloatingPoint, 11);
|
||||||
|
|
||||||
|
pub const FS0: Self = Self::Saved(RegisterType::FloatingPoint, 0);
|
||||||
|
pub const FS1: Self = Self::Saved(RegisterType::FloatingPoint, 1);
|
||||||
|
pub const FS2: Self = Self::Saved(RegisterType::FloatingPoint, 2);
|
||||||
|
pub const FS3: Self = Self::Saved(RegisterType::FloatingPoint, 3);
|
||||||
|
pub const FS4: Self = Self::Saved(RegisterType::FloatingPoint, 4);
|
||||||
|
pub const FS5: Self = Self::Saved(RegisterType::FloatingPoint, 5);
|
||||||
|
pub const FS6: Self = Self::Saved(RegisterType::FloatingPoint, 6);
|
||||||
|
pub const FS7: Self = Self::Saved(RegisterType::FloatingPoint, 7);
|
||||||
|
pub const FS8: Self = Self::Saved(RegisterType::FloatingPoint, 8);
|
||||||
|
pub const FS9: Self = Self::Saved(RegisterType::FloatingPoint, 9);
|
||||||
|
pub const FS10: Self = Self::Saved(RegisterType::FloatingPoint, 10);
|
||||||
|
pub const FS11: Self = Self::Saved(RegisterType::FloatingPoint, 11);
|
||||||
|
|
||||||
|
pub const FA0: Self = Self::Arg(RegisterType::FloatingPoint, 0);
|
||||||
|
pub const FA1: Self = Self::Arg(RegisterType::FloatingPoint, 1);
|
||||||
|
pub const FA2: Self = Self::Arg(RegisterType::FloatingPoint, 2);
|
||||||
|
pub const FA3: Self = Self::Arg(RegisterType::FloatingPoint, 3);
|
||||||
|
pub const FA4: Self = Self::Arg(RegisterType::FloatingPoint, 4);
|
||||||
|
pub const FA5: Self = Self::Arg(RegisterType::FloatingPoint, 5);
|
||||||
|
pub const FA6: Self = Self::Arg(RegisterType::FloatingPoint, 6);
|
||||||
|
pub const FA7: Self = Self::Arg(RegisterType::FloatingPoint, 7);
|
||||||
|
|
||||||
|
pub fn temp(register_type: RegisterType, id: usize) -> Self {
|
||||||
|
match register_type {
|
||||||
|
RegisterType::Integer => assert!(id <= 6),
|
||||||
|
RegisterType::FloatingPoint => assert!(id <= 11),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn saved(id: usize) -> Self {
|
Self::Temp(register_type, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn saved(register_type: RegisterType, id: usize) -> Self {
|
||||||
assert!(id <= 11);
|
assert!(id <= 11);
|
||||||
Self::Saved(id)
|
Self::Saved(register_type, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arg(id: usize) -> Self {
|
pub fn arg(register_type: RegisterType, id: usize) -> Self {
|
||||||
assert!(id <= 7);
|
assert!(id <= 7);
|
||||||
Self::Arg(id)
|
Self::Arg(register_type, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
||||||
|
pub enum RegisterType {
|
||||||
|
Integer,
|
||||||
|
FloatingPoint,
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,8 +48,14 @@ impl WriteLine for Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WriteLine for Variable {
|
impl WriteLine for Variable {
|
||||||
fn write_line(&self, _indent: usize, _write: &mut dyn Write) -> Result<()> {
|
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
|
||||||
todo!()
|
writeln!(write, "{}:", self.label.0)?;
|
||||||
|
for directive in &self.directives {
|
||||||
|
write_indent(indent + INDENT, write)?;
|
||||||
|
writeln!(write, "{}", directive.write_string())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,10 +81,27 @@ impl WriteString for Directive {
|
|||||||
Self::Type(symbol, symbol_type) => {
|
Self::Type(symbol, symbol_type) => {
|
||||||
format!(".type\t{}, {}", symbol.0, symbol_type.write_string())
|
format!(".type\t{}, {}", symbol.0, symbol_type.write_string())
|
||||||
}
|
}
|
||||||
|
Self::Section(section_type) => format!(".section\t{}", section_type.write_string()),
|
||||||
|
Self::Byte(value) => format!(".byte\t{:#x?}", value),
|
||||||
|
Self::Half(value) => format!(".half\t{:#x?}", value),
|
||||||
|
Self::Word(value) => format!(".word\t{:#x?}", value),
|
||||||
|
Self::Quad(value) => format!(".quad\t{:#x?}", value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WriteString for SectionType {
|
||||||
|
fn write_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Text => ".text",
|
||||||
|
Self::Data => ".data",
|
||||||
|
Self::Rodata => ".rodata",
|
||||||
|
Self::Bss => ".bss",
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl WriteString for SymbolType {
|
impl WriteString for SymbolType {
|
||||||
fn write_string(&self) -> String {
|
fn write_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
@@ -97,25 +120,39 @@ impl WriteString for Instruction {
|
|||||||
rd,
|
rd,
|
||||||
rs1,
|
rs1,
|
||||||
rs2,
|
rs2,
|
||||||
} => format!(
|
} => {
|
||||||
"{}\t{},{},{}",
|
let rounding_mode = if let RType::FcvtFloatToInt { .. } = instr {
|
||||||
|
",rtz"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"{}\t{},{}{}{}",
|
||||||
instr.write_string(),
|
instr.write_string(),
|
||||||
rd.write_string(),
|
rd.write_string(),
|
||||||
rs1.write_string(),
|
rs1.write_string(),
|
||||||
rs2.write_string()
|
if let Some(rs2) = rs2 {
|
||||||
),
|
format!(",{}", rs2.write_string())
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
},
|
||||||
|
rounding_mode
|
||||||
|
)
|
||||||
|
}
|
||||||
Self::IType {
|
Self::IType {
|
||||||
instr,
|
instr,
|
||||||
rd,
|
rd,
|
||||||
rs1,
|
rs1,
|
||||||
imm,
|
imm,
|
||||||
} => {
|
} => {
|
||||||
if let IType::Load(_) = instr {
|
if let IType::Load { .. } = instr {
|
||||||
format!(
|
format!(
|
||||||
"{}\t{},{}({})",
|
"{}\t{},{}({})",
|
||||||
instr.write_string(),
|
instr.write_string(),
|
||||||
rd.write_string(),
|
rd.write_string(),
|
||||||
imm,
|
imm.write_string(),
|
||||||
rs1.write_string()
|
rs1.write_string()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -124,7 +161,7 @@ impl WriteString for Instruction {
|
|||||||
instr.write_string(),
|
instr.write_string(),
|
||||||
rd.write_string(),
|
rd.write_string(),
|
||||||
rs1.write_string(),
|
rs1.write_string(),
|
||||||
imm
|
imm.write_string(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +174,7 @@ impl WriteString for Instruction {
|
|||||||
"{}\t{},{}({})",
|
"{}\t{},{}({})",
|
||||||
instr.write_string(),
|
instr.write_string(),
|
||||||
rs2.write_string(),
|
rs2.write_string(),
|
||||||
imm.to_string(),
|
imm.write_string(),
|
||||||
rs1.write_string()
|
rs1.write_string()
|
||||||
),
|
),
|
||||||
Self::BType {
|
Self::BType {
|
||||||
@@ -152,6 +189,12 @@ impl WriteString for Instruction {
|
|||||||
rs2.write_string(),
|
rs2.write_string(),
|
||||||
imm.0,
|
imm.0,
|
||||||
),
|
),
|
||||||
|
Self::UType { instr, rd, imm } => format!(
|
||||||
|
"{}\t{}, {}",
|
||||||
|
instr.write_string(),
|
||||||
|
rd.write_string(),
|
||||||
|
imm.write_string(),
|
||||||
|
),
|
||||||
Self::Pseudo(pseudo) => pseudo.write_string(),
|
Self::Pseudo(pseudo) => pseudo.write_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,14 +205,101 @@ impl WriteString for RType {
|
|||||||
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::Sub(data_size) => format!("sub{}", data_size.write_string()),
|
||||||
|
Self::Sll(data_size) => format!("sll{}", data_size.write_string()),
|
||||||
|
Self::Srl(data_size) => format!("srl{}", data_size.write_string()),
|
||||||
|
Self::Sra(data_size) => format!("srl{}", 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!(
|
Self::Div {
|
||||||
|
data_size,
|
||||||
|
is_signed,
|
||||||
|
} => format!(
|
||||||
"div{}{}",
|
"div{}{}",
|
||||||
if *is_signed { "" } else { "u" },
|
if *is_signed { "" } else { "u" },
|
||||||
data_size.write_string()
|
data_size.write_string()
|
||||||
),
|
),
|
||||||
Self::Slt(is_signed) => format!("slt{}", if *is_signed { "" } else { "u" }),
|
Self::Rem {
|
||||||
|
data_size,
|
||||||
|
is_signed,
|
||||||
|
} => format!(
|
||||||
|
"rem{}{}",
|
||||||
|
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(),
|
Self::Xor => "xor".to_string(),
|
||||||
|
Self::Or => "or".to_string(),
|
||||||
|
Self::And => "and".to_string(),
|
||||||
|
Self::Fadd(data_size) => format!("fadd.{}", data_size.write_string()),
|
||||||
|
Self::Fsub(data_size) => format!("fsub.{}", data_size.write_string()),
|
||||||
|
Self::Fmul(data_size) => format!("fmul.{}", data_size.write_string()),
|
||||||
|
Self::Fdiv(data_size) => format!("fdiv.{}", data_size.write_string()),
|
||||||
|
Self::Feq(data_size) => format!("feq.{}", data_size.write_string()),
|
||||||
|
Self::Flt(data_size) => format!("flt.{}", data_size.write_string()),
|
||||||
|
Self::FmvFloatToInt { float_data_size } => {
|
||||||
|
assert!(float_data_size.is_floating_point());
|
||||||
|
format!(
|
||||||
|
"fmv.x.{}",
|
||||||
|
if *float_data_size == DataSize::SinglePrecision {
|
||||||
|
"w"
|
||||||
|
} else {
|
||||||
|
"d"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Self::FmvIntToFloat { float_data_size } => {
|
||||||
|
assert!(float_data_size.is_floating_point());
|
||||||
|
format!(
|
||||||
|
"fmv.{}.x",
|
||||||
|
if *float_data_size == DataSize::SinglePrecision {
|
||||||
|
"w"
|
||||||
|
} else {
|
||||||
|
"d"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Self::FcvtFloatToInt {
|
||||||
|
float_data_size,
|
||||||
|
int_data_size,
|
||||||
|
is_signed,
|
||||||
|
} => {
|
||||||
|
assert!(float_data_size.is_floating_point());
|
||||||
|
format!(
|
||||||
|
"fcvt.{}{}.{}",
|
||||||
|
if let Some(int_data_size) = int_data_size {
|
||||||
|
assert_eq!(*int_data_size, DataSize::Word);
|
||||||
|
"w"
|
||||||
|
} else {
|
||||||
|
"l"
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
|
if *is_signed { "" } else { "u" },
|
||||||
|
float_data_size.write_string()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Self::FcvtIntToFloat {
|
||||||
|
int_data_size,
|
||||||
|
float_data_size,
|
||||||
|
is_signed,
|
||||||
|
} => {
|
||||||
|
assert!(float_data_size.is_floating_point());
|
||||||
|
format!(
|
||||||
|
"fcvt.{}.{}{}",
|
||||||
|
float_data_size.write_string(),
|
||||||
|
if let Some(int_data_size) = int_data_size {
|
||||||
|
assert_eq!(*int_data_size, DataSize::Word);
|
||||||
|
"w"
|
||||||
|
} else {
|
||||||
|
"l"
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
|
if *is_signed { "" } else { "u" }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Self::FcvtFloatToFloat { from, to } => {
|
||||||
|
assert!(from.is_floating_point());
|
||||||
|
assert!(to.is_floating_point());
|
||||||
|
format!("fcvt.{}.{}", to.write_string(), from.write_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,8 +307,30 @@ impl WriteString for RType {
|
|||||||
impl WriteString for IType {
|
impl WriteString for IType {
|
||||||
fn write_string(&self) -> String {
|
fn write_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::Load(data_size) => format!("l{}", data_size.write_string()),
|
Self::Load {
|
||||||
|
data_size,
|
||||||
|
is_signed,
|
||||||
|
} => {
|
||||||
|
if data_size.is_integer() {
|
||||||
|
format!(
|
||||||
|
"l{}{}",
|
||||||
|
data_size.write_string(),
|
||||||
|
if *is_signed { "" } else { "u" }
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"fl{}",
|
||||||
|
if *data_size == DataSize::SinglePrecision {
|
||||||
|
"w"
|
||||||
|
} else {
|
||||||
|
"d"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Self::Addi(data_size) => format!("addi{}", data_size.write_string()),
|
Self::Addi(data_size) => format!("addi{}", data_size.write_string()),
|
||||||
|
Self::Xori => "xori".to_string(),
|
||||||
|
Self::Ori => "ori".to_string(),
|
||||||
Self::Andi => "andi".to_string(),
|
Self::Andi => "andi".to_string(),
|
||||||
Self::Slli => "slli".to_string(),
|
Self::Slli => "slli".to_string(),
|
||||||
Self::Srli => "srli".to_string(),
|
Self::Srli => "srli".to_string(),
|
||||||
@@ -189,7 +341,20 @@ impl WriteString for IType {
|
|||||||
impl WriteString for SType {
|
impl WriteString for SType {
|
||||||
fn write_string(&self) -> String {
|
fn write_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::Store(data_size) => format!("s{}", data_size.write_string()),
|
Self::Store(data_size) => {
|
||||||
|
if data_size.is_integer() {
|
||||||
|
format!("s{}", data_size.write_string())
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"fs{}",
|
||||||
|
if *data_size == DataSize::SinglePrecision {
|
||||||
|
"w"
|
||||||
|
} else {
|
||||||
|
"d"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,8 +364,16 @@ impl WriteString for BType {
|
|||||||
match self {
|
match self {
|
||||||
Self::Beq => "beq".to_string(),
|
Self::Beq => "beq".to_string(),
|
||||||
Self::Bne => "bne".to_string(),
|
Self::Bne => "bne".to_string(),
|
||||||
Self::Blt(is_signed) => format!("blt{}", if *is_signed { "" } else { "u" }),
|
Self::Blt { is_signed } => format!("blt{}", if *is_signed { "" } else { "u" }),
|
||||||
Self::Bge(is_signed) => format!("bge{}", if *is_signed { "" } else { "u" }),
|
Self::Bge { is_signed } => format!("bge{}", if *is_signed { "" } else { "u" }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WriteString for UType {
|
||||||
|
fn write_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Lui => "lui".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,9 +381,9 @@ impl WriteString for BType {
|
|||||||
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{},{:#x?}", rd.write_string(), imm),
|
||||||
Self::Mv { rs, rd } => format!("mv\t{},{}", rd.write_string(), rs.write_string()),
|
Self::Mv { rd, rs } => format!("mv\t{},{}", rd.write_string(), rs.write_string()),
|
||||||
Self::Neg { data_size, rs, rd } => format!(
|
Self::Neg { data_size, rd, rs } => format!(
|
||||||
"neg{}\t{},{}",
|
"neg{}\t{},{}",
|
||||||
data_size.write_string(),
|
data_size.write_string(),
|
||||||
rd.write_string(),
|
rd.write_string(),
|
||||||
@@ -219,15 +392,44 @@ impl WriteString for Pseudo {
|
|||||||
Self::SextW { rs, rd } => {
|
Self::SextW { rs, rd } => {
|
||||||
format!("sext.w\t{},{}", rd.write_string(), rs.write_string())
|
format!("sext.w\t{},{}", rd.write_string(), rs.write_string())
|
||||||
}
|
}
|
||||||
Self::Seqz { rs, rd } => format!("seqz\t{},{}", rd.write_string(), rs.write_string()),
|
Self::Seqz { rd, rs } => format!("seqz\t{},{}", rd.write_string(), rs.write_string()),
|
||||||
|
Self::Snez { rd, rs } => format!("snez\t{},{}", rd.write_string(), rs.write_string()),
|
||||||
|
Self::Fneg { data_size, rd, rs } => format!(
|
||||||
|
"fneg.{}\t{},{}",
|
||||||
|
data_size.write_string(),
|
||||||
|
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::Jalr { rs } => format!("jalr\t{}", rs.write_string()),
|
||||||
Self::Ret => "ret".to_string(),
|
Self::Ret => "ret".to_string(),
|
||||||
Self::Call { offset } => format!("call\t{}", offset.0),
|
Self::Call { offset } => format!("call\t{}", offset.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WriteString for Immediate {
|
||||||
|
fn write_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Value(value) => format!("{:#x?}", value),
|
||||||
|
Self::Relocation { relocation, symbol } => {
|
||||||
|
format!("{}({})", relocation.write_string(), symbol.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WriteString for RelocationFunction {
|
||||||
|
fn write_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::HI20 => "%hi",
|
||||||
|
Self::LO12 => "%lo",
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl WriteString for DataSize {
|
impl WriteString for DataSize {
|
||||||
fn write_string(&self) -> String {
|
fn write_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
@@ -235,6 +437,8 @@ impl WriteString for DataSize {
|
|||||||
Self::Half => "h",
|
Self::Half => "h",
|
||||||
Self::Word => "w",
|
Self::Word => "w",
|
||||||
Self::Double => "d",
|
Self::Double => "d",
|
||||||
|
Self::SinglePrecision => "s",
|
||||||
|
Self::DoublePrecision => "d",
|
||||||
}
|
}
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
@@ -248,9 +452,33 @@ impl WriteString for Register {
|
|||||||
Self::Sp => "sp".to_string(),
|
Self::Sp => "sp".to_string(),
|
||||||
Self::Gp => "gp".to_string(),
|
Self::Gp => "gp".to_string(),
|
||||||
Self::Tp => "tp".to_string(),
|
Self::Tp => "tp".to_string(),
|
||||||
Self::Temp(id) => format!("t{}", id),
|
Self::Temp(registr_type, id) => format!(
|
||||||
Self::Saved(id) => format!("s{}", id),
|
"{}t{}",
|
||||||
Self::Arg(id) => format!("a{}", id),
|
if *registr_type == RegisterType::FloatingPoint {
|
||||||
|
"f"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
id
|
||||||
|
),
|
||||||
|
Self::Saved(registr_type, id) => format!(
|
||||||
|
"{}s{}",
|
||||||
|
if *registr_type == RegisterType::FloatingPoint {
|
||||||
|
"f"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
id
|
||||||
|
),
|
||||||
|
Self::Arg(registr_type, id) => format!(
|
||||||
|
"{}a{}",
|
||||||
|
if *registr_type == RegisterType::FloatingPoint {
|
||||||
|
"f"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
id
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -466,7 +466,7 @@ impl TryFrom<&ast::ParameterDeclaration> for Dtype {
|
|||||||
impl Dtype {
|
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 (current: 64-bit architecture)
|
||||||
pub const SIZE_OF_POINTER: usize = 8;
|
pub const SIZE_OF_POINTER: usize = 8;
|
||||||
|
|
||||||
pub const SIZE_OF_CHAR: usize = 1;
|
pub const SIZE_OF_CHAR: usize = 1;
|
||||||
|
|||||||
@@ -807,23 +807,6 @@ mod calculator {
|
|||||||
(value, dtype) => todo!("calculate_typecast ({:?}) {:?}", value, dtype),
|
(value, dtype) => todo!("calculate_typecast ({:?}) {:?}", value, dtype),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn sign_extension(value: u128, width: u128) -> u128 {
|
|
||||||
let base = 1u128 << (width - 1);
|
|
||||||
if value >= base {
|
|
||||||
let bit_mask = -1i128 << (width as i128);
|
|
||||||
value | bit_mask as u128
|
|
||||||
} else {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn trim_unnecessary_bits(value: u128, width: u128) -> u128 {
|
|
||||||
let bit_mask = (1u128 << width) - 1;
|
|
||||||
value & bit_mask
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: delete `allow(dead_code)`
|
// TODO: delete `allow(dead_code)`
|
||||||
|
|||||||
@@ -719,6 +719,80 @@ impl Constant {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn typecast(self, target_dtype: Dtype) -> Self {
|
||||||
|
if self.dtype() == target_dtype {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
match (&self, &target_dtype) {
|
||||||
|
(
|
||||||
|
Constant::Int { value, width, .. },
|
||||||
|
Dtype::Int {
|
||||||
|
width: target_width,
|
||||||
|
is_signed: target_signed,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
let result = if *target_signed {
|
||||||
|
if *width >= *target_width {
|
||||||
|
let value = trim_unnecessary_bits(*value, *target_width as u128);
|
||||||
|
sign_extension(value, *target_width as u128)
|
||||||
|
} else {
|
||||||
|
*value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trim_unnecessary_bits(*value, *target_width as u128)
|
||||||
|
};
|
||||||
|
|
||||||
|
Constant::int(result, target_dtype)
|
||||||
|
}
|
||||||
|
(
|
||||||
|
Constant::Int {
|
||||||
|
value, is_signed, ..
|
||||||
|
},
|
||||||
|
Dtype::Float { .. },
|
||||||
|
) => {
|
||||||
|
let casted_value = if *is_signed {
|
||||||
|
*value as i128 as f64
|
||||||
|
} else {
|
||||||
|
*value as f64
|
||||||
|
};
|
||||||
|
|
||||||
|
Constant::float(casted_value, target_dtype)
|
||||||
|
}
|
||||||
|
(Constant::Float { value, .. }, Dtype::Int { is_signed, .. }) => {
|
||||||
|
let casted_value = if *is_signed {
|
||||||
|
value.into_inner() as i128 as u128
|
||||||
|
} else {
|
||||||
|
value.into_inner() as u128
|
||||||
|
};
|
||||||
|
|
||||||
|
Constant::int(casted_value, target_dtype)
|
||||||
|
}
|
||||||
|
(Constant::Float { value, .. }, Dtype::Float { .. }) => {
|
||||||
|
Constant::float(value.into_inner(), target_dtype)
|
||||||
|
}
|
||||||
|
_ => todo!("typecast ({:?}) {:?}", self, target_dtype),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn sign_extension(value: u128, width: u128) -> u128 {
|
||||||
|
let base = 1u128 << (width - 1);
|
||||||
|
if value >= base {
|
||||||
|
let bit_mask = -1i128 << (width as i128);
|
||||||
|
value | bit_mask as u128
|
||||||
|
} else {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn trim_unnecessary_bits(value: u128, width: u128) -> u128 {
|
||||||
|
let bit_mask = (1u128 << width) - 1;
|
||||||
|
value & bit_mask
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Constant {
|
impl fmt::Display for Constant {
|
||||||
|
|||||||
17
src/tests.rs
17
src/tests.rs
@@ -222,6 +222,17 @@ pub fn test_opt<P1: AsRef<Path>, P2: AsRef<Path>, O: Optimize<ir::TranslationUni
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_asmgen(unit: &TranslationUnit, path: &Path) {
|
pub fn test_asmgen(unit: &TranslationUnit, path: &Path) {
|
||||||
|
// TODO: delete black list in the future
|
||||||
|
let exclusion_list = vec![
|
||||||
|
"examples/c/struct.c",
|
||||||
|
"examples/c/struct2.c",
|
||||||
|
"examples/c/temp2.c",
|
||||||
|
];
|
||||||
|
if exclusion_list.contains(&path.to_str().expect("`path` must be transformed to `&str`")) {
|
||||||
|
println!("skip test");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the file has .c extension
|
// Check if the file has .c extension
|
||||||
assert_eq!(path.extension(), Some(std::ffi::OsStr::new("c")));
|
assert_eq!(path.extension(), Some(std::ffi::OsStr::new("c")));
|
||||||
|
|
||||||
@@ -231,7 +242,11 @@ pub fn test_asmgen(unit: &TranslationUnit, path: &Path) {
|
|||||||
.expect("failed to parse the given program");
|
.expect("failed to parse the given program");
|
||||||
|
|
||||||
let file_path = path.display().to_string();
|
let file_path = path.display().to_string();
|
||||||
let bin_path = path.with_extension("irgen").as_path().display().to_string();
|
let bin_path = path
|
||||||
|
.with_extension("asmgen")
|
||||||
|
.as_path()
|
||||||
|
.display()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
// Compile c file: If fails, test is vacuously success
|
// Compile c file: If fails, test is vacuously success
|
||||||
if !Command::new("gcc")
|
if !Command::new("gcc")
|
||||||
|
|||||||
@@ -102,8 +102,9 @@ fn test_examples_deadcode() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: after implementing IR parser, delete `ignore` mark
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn test_examples_asmgen() {
|
fn test_examples_asmgen() {
|
||||||
test_dir(Path::new("examples/asm"), &OsStr::new("c"), test_asmgen);
|
test_dir(Path::new("examples/c"), &OsStr::new("c"), test_asmgen);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user