├── sericumcc ├── examples │ ├── sample.h │ ├── assert.h │ ├── array.c │ ├── ptr.c │ ├── cast1.c │ ├── fibo.c │ ├── sum.c │ ├── str_arr.c │ ├── e_counter.c │ ├── struct.c │ ├── struct2.c │ ├── struct3.c │ ├── collatz.c │ ├── hello.c │ ├── str_arr2.c │ ├── strstr.c │ ├── many_args.c │ ├── struct4.c │ ├── mandelbrot.c │ ├── stdarg.h │ ├── sericumcc.h │ └── game_of_life.c ├── README.md ├── Cargo.toml └── src │ ├── main.rs │ ├── token.rs │ ├── lib.rs │ └── ast.rs ├── src ├── exec │ ├── mod.rs │ └── interpreter │ │ └── mod.rs ├── codegen │ ├── common │ │ ├── asm │ │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── dag │ │ │ ├── mod.rs │ │ │ ├── module.rs │ │ │ ├── basic_block.rs │ │ │ ├── function.rs │ │ │ ├── combine.rs │ │ │ └── mc_convert.rs │ │ ├── machine │ │ │ ├── mod.rs │ │ │ ├── calling_conv.rs │ │ │ ├── module.rs │ │ │ ├── eliminate_fi.rs │ │ │ ├── phi_elimination.rs │ │ │ ├── frame_object.rs │ │ │ ├── const_data.rs │ │ │ └── inst_def.rs │ │ └── types.rs │ ├── x64 │ │ ├── asm │ │ │ └── mod.rs │ │ ├── dag │ │ │ ├── mod.rs │ │ │ ├── node.rs │ │ │ └── convert.rs │ │ ├── exec │ │ │ └── mod.rs │ │ ├── machine │ │ │ ├── mod.rs │ │ │ ├── replace_copy.rs │ │ │ ├── replace_data.rs │ │ │ ├── calc_spill_weight.rs │ │ │ ├── regalloc.rs │ │ │ ├── abi.rs │ │ │ ├── eliminate_fi.rs │ │ │ ├── register.rs │ │ │ └── two_addr.rs │ │ ├── frame_object.rs │ │ └── mod.rs │ ├── aarch64 │ │ ├── asm │ │ │ ├── mod.rs │ │ │ └── assembler.rs │ │ ├── exec │ │ │ └── mod.rs │ │ ├── dag │ │ │ ├── mod.rs │ │ │ ├── node.rs │ │ │ ├── convert.rs │ │ │ └── legalize.rs │ │ ├── machine │ │ │ ├── mod.rs │ │ │ ├── spiller.rs │ │ │ ├── abi.rs │ │ │ ├── replace_copy.rs │ │ │ ├── replace_data.rs │ │ │ ├── calc_spill_weight.rs │ │ │ ├── regalloc.rs │ │ │ ├── eliminate_fi.rs │ │ │ ├── register.rs │ │ │ ├── inst.rs │ │ │ └── validate_frame_index.rs │ │ ├── frame_object.rs │ │ └── mod.rs │ ├── riscv64 │ │ ├── asm │ │ │ ├── mod.rs │ │ │ ├── assembler.rs │ │ │ └── print.rs │ │ ├── exec │ │ │ └── mod.rs │ │ ├── dag │ │ │ ├── mod.rs │ │ │ ├── node.rs │ │ │ └── convert.rs │ │ ├── machine │ │ │ ├── mod.rs │ │ │ ├── spiller.rs │ │ │ ├── regalloc.rs │ │ │ ├── replace_copy.rs │ │ │ ├── register.rs │ │ │ ├── replace_data.rs │ │ │ ├── calc_spill_weight.rs │ │ │ ├── inst.rs │ │ │ └── validate_frame_index.rs │ │ ├── frame_object.rs │ │ └── mod.rs │ ├── internals │ └── mod.rs ├── util │ ├── mod.rs │ ├── allocator.rs │ └── count.rs ├── traits │ ├── mod.rs │ ├── function.rs │ ├── pass.rs │ └── basic_block.rs ├── ir │ ├── prelude.rs │ ├── mod.rs │ ├── verify.rs │ ├── remove_unreachable_block.rs │ ├── alias_analysis.rs │ ├── global_val.rs │ ├── dce.rs │ ├── codegen_prepare.rs │ ├── constant_pool.rs │ ├── merge_ret.rs │ ├── inst_combine.rs │ └── liveness.rs ├── analysis │ └── mod.rs └── lib.rs ├── docs-ja ├── backend │ └── 0-バックエンド入門.md ├── frontend │ ├── 0-フロントエンド入門.md │ └── 2-変数.md └── 0-はじめに.md ├── .gitignore ├── minilang └── Cargo.toml ├── defs ├── Cargo.toml └── src │ └── lib.rs ├── run_kcov.sh ├── Dockerfile_cov ├── Cargo.toml ├── LICENSE ├── tests └── demo.rs ├── README.md └── .circleci └── config.yml /sericumcc/examples/sample.h: -------------------------------------------------------------------------------- 1 | int printf; 2 | -------------------------------------------------------------------------------- /src/exec/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod interpreter; 2 | -------------------------------------------------------------------------------- /docs-ja/backend/0-バックエンド入門.md: -------------------------------------------------------------------------------- 1 | # バックエンド入門 2 | -------------------------------------------------------------------------------- /src/exec/interpreter/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod interp; 2 | -------------------------------------------------------------------------------- /src/codegen/common/asm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod assembler; 2 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod allocator; 2 | pub mod count; 3 | -------------------------------------------------------------------------------- /src/codegen/x64/asm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod assembler; 2 | pub mod print; 3 | -------------------------------------------------------------------------------- /src/codegen/aarch64/asm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod assembler; 2 | pub mod print; 3 | -------------------------------------------------------------------------------- /src/codegen/riscv64/asm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod assembler; 2 | pub mod print; 3 | -------------------------------------------------------------------------------- /src/traits/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod basic_block; 2 | pub mod function; 3 | pub mod pass; 4 | -------------------------------------------------------------------------------- /sericumcc/examples/assert.h: -------------------------------------------------------------------------------- 1 | #define assert(x) if (x) {} else exit(1); 2 | 3 | int exit(int); 4 | -------------------------------------------------------------------------------- /src/codegen/common/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod asm; 2 | pub mod dag; 3 | #[macro_use] 4 | pub mod machine; 5 | pub mod types; 6 | -------------------------------------------------------------------------------- /src/codegen/aarch64/exec/mod.rs: -------------------------------------------------------------------------------- 1 | // pub mod jit; 2 | 3 | pub fn roundup(n: i32, align: i32) -> i32 { 4 | (n + align - 1) & !(align - 1) 5 | } 6 | -------------------------------------------------------------------------------- /src/codegen/riscv64/exec/mod.rs: -------------------------------------------------------------------------------- 1 | // pub mod jit; 2 | 3 | pub fn roundup(n: i32, align: i32) -> i32 { 4 | (n + align - 1) & !(align - 1) 5 | } 6 | -------------------------------------------------------------------------------- /docs-ja/frontend/0-フロントエンド入門.md: -------------------------------------------------------------------------------- 1 | # フロントエンド入門 2 | 3 | ここではcilkのフロントエンドについて,具体的なコードに触れつつ使い方を説明していきます. 4 | 5 | # もくじ 6 | 7 | 1. [まずはreturnから](./1-まずはreturnから.md) 8 | -------------------------------------------------------------------------------- /src/codegen/x64/dag/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod convert; 2 | pub mod isel; 3 | pub mod legalize; 4 | pub mod mc_convert; 5 | pub mod node; 6 | 7 | pub use super::frame_object; 8 | -------------------------------------------------------------------------------- /src/codegen/x64/exec/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod executor; 2 | pub mod jit; 3 | 4 | pub fn roundup(n: i32, align: i32) -> i32 { 5 | (n + align - 1) & !(align - 1) 6 | } 7 | -------------------------------------------------------------------------------- /src/codegen/aarch64/dag/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod convert; 2 | pub mod isel; 3 | pub mod legalize; 4 | pub mod mc_convert; 5 | pub mod node; 6 | 7 | pub use super::frame_object; 8 | -------------------------------------------------------------------------------- /src/codegen/riscv64/dag/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod convert; 2 | pub mod isel; 3 | pub mod legalize; 4 | pub mod mc_convert; 5 | pub mod node; 6 | 7 | pub use super::frame_object; 8 | -------------------------------------------------------------------------------- /sericumcc/examples/array.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int main() { 4 | int a[10]; 5 | a[1] = 2; 6 | a[3] = 5; 7 | assert(a[1] + a[3] == 7); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /src/codegen/aarch64/asm/assembler.rs: -------------------------------------------------------------------------------- 1 | pub use crate::codegen::common::asm::assembler::InstAssembler; 2 | 3 | impl<'a> InstAssembler<'a> { 4 | pub fn assemble(&mut self) {} 5 | } 6 | -------------------------------------------------------------------------------- /src/codegen/riscv64/asm/assembler.rs: -------------------------------------------------------------------------------- 1 | pub use crate::codegen::common::asm::assembler::InstAssembler; 2 | 3 | impl<'a> InstAssembler<'a> { 4 | pub fn assemble(&mut self) {} 5 | } 6 | -------------------------------------------------------------------------------- /src/traits/function.rs: -------------------------------------------------------------------------------- 1 | use super::basic_block::BasicBlocksTrait; 2 | 3 | pub trait FunctionTrait { 4 | type BBS: BasicBlocksTrait; 5 | fn get_basic_blocks(&self) -> &Self::BBS; 6 | } 7 | -------------------------------------------------------------------------------- /src/codegen/common/dag/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod basic_block; 2 | pub mod combine; 3 | pub mod convert; 4 | pub mod function; 5 | pub mod mc_convert; 6 | pub mod module; 7 | pub mod node; 8 | pub mod pat_match; 9 | -------------------------------------------------------------------------------- /src/codegen/riscv64/dag/node.rs: -------------------------------------------------------------------------------- 1 | pub use crate::codegen::common::dag::node::*; 2 | 3 | #[derive(Debug, Clone, PartialEq)] 4 | pub enum MemNodeKind { 5 | FiReg, 6 | ImmReg, 7 | Address, 8 | } 9 | -------------------------------------------------------------------------------- /src/ir/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use super::{ 2 | builder, builder::IRBuilder, function, function::Function, global_val, module::Module, opcode, 3 | opcode::Opcode, types, types::Type, value, value::Value, 4 | }; 5 | -------------------------------------------------------------------------------- /sericumcc/examples/ptr.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int main() { 4 | int a; 5 | int *b; 6 | b = &a; 7 | a = 123; 8 | assert(*b == 123); 9 | *b = 23; 10 | assert(a == 23); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /sericumcc/examples/cast1.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int main() { 4 | int a = 123; 5 | int *b = &a; 6 | char *c = (char *)b; 7 | int *d = (int *)c; 8 | *d = 23; 9 | assert(a == 23); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /sericumcc/examples/fibo.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int fibo(int x) { 4 | if (x <= 2) return 1; 5 | return fibo(x - 1) + fibo(x - 2); 6 | } 7 | 8 | int main() { 9 | assert(fibo(10) == 55); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /sericumcc/examples/sum.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int main() { 4 | int i, sum; 5 | i = 0; sum = 0; 6 | while (i <= 10) { 7 | sum += i; 8 | i += 1; 9 | } 10 | assert(sum == 55); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | defs/target 5 | defs/**/*.rs.bk 6 | defs/Cargo.lock 7 | minilang/target 8 | minilang/**/*.rs.bk 9 | minilang/Cargo.lock 10 | cilkcc/target 11 | cilkcc/**/*.rs.bk 12 | cilkcc/Cargo.lock 13 | -------------------------------------------------------------------------------- /sericumcc/examples/str_arr.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int strcmp(char *, char *); 4 | 5 | int main() { 6 | char *s[2] = {"hello", "world"}; 7 | assert(strcmp(s[0], "hello") == 0); 8 | assert(strcmp(s[1], "world") == 0); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /sericumcc/README.md: -------------------------------------------------------------------------------- 1 | # Sericumcc 2 | 3 | A small C compiler powered by sericum 4 | 5 | # Todo 6 | 7 | - [x] Add tests 8 | - [ ] Implement remaining C features 9 | 10 | # Run 11 | 12 | ``` 13 | # test 14 | cargo test 15 | # run 16 | cargo run --release your.code.c 17 | ``` 18 | -------------------------------------------------------------------------------- /sericumcc/examples/e_counter.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int main() { 4 | char *s = "the quick brown fox jumps over the lazy dog", *p = s; 5 | int i = 0; 6 | while (*p != 0) { 7 | if (*p == 'e') i += 1; 8 | p += 1; 9 | } 10 | assert(i == 3); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /sericumcc/examples/struct.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int main() { 4 | typedef struct { 5 | int x, y, z; 6 | } A; 7 | A a[1]; 8 | A c[2][2]; 9 | a[0].y=1; 10 | A b; 11 | b.x=3; 12 | c[1][1].z=4; 13 | c[0][1].y=1; 14 | assert(b.x+a[0].y+c[1][1].z+c[0][1].y == 9); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /src/analysis/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dom_tree; 2 | pub mod loops; 3 | 4 | use dyn_clone::{clone_trait_object, DynClone}; 5 | use std::any::Any; 6 | 7 | pub trait Analysis: DynClone { 8 | fn as_any(&self) -> &dyn Any; 9 | fn as_any_mut(&mut self) -> &mut dyn Any; 10 | } 11 | 12 | clone_trait_object!(Analysis); 13 | -------------------------------------------------------------------------------- /sericumcc/examples/struct2.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int main() { 4 | typedef struct { 5 | int x, y, z; 6 | } A; 7 | int i = 1; 8 | A a[3]; 9 | a[i].x = 1; 10 | a[i+1].z = 2; 11 | a[i-1].y = 3; 12 | assert(a[1].x == 1); 13 | assert(a[0].y == 3); 14 | assert(a[2].z == 2); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /sericumcc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sericumcc" 3 | version = "0.1.0" 4 | authors = ["uint256_t"] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | sericum = { path = "../", features = ["x86_64"] } 9 | argopt = "= 0.1.1" 10 | structopt = "= 0.3.18" 11 | rustc-hash = "= 1.1.0" 12 | id-arena = "= 2.2.1" 13 | rand = "0.7.3" 14 | -------------------------------------------------------------------------------- /sericumcc/examples/struct3.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | typedef struct { 4 | int x, y, z; 5 | } A; 6 | 7 | int test(A a[3]) { 8 | assert(a[1].y == 2); 9 | return 0; 10 | } 11 | 12 | int main() { 13 | A a[3]; 14 | a[0].x = 1; 15 | a[1].y = 2; 16 | assert(a[0].x == 1); 17 | assert(a[1].y == 2); 18 | test(a); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /sericumcc/examples/collatz.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int printf(char *, int); 4 | 5 | int collatz(int x) { 6 | printf("%d\n", x); 7 | if (x == 1) return 1; 8 | if (x % 2 == 0) return collatz(x / 2); 9 | if (x % 2 == 1) return collatz(3 * x + 1); 10 | return 0; 11 | } 12 | 13 | int main() { 14 | assert(collatz(123) == 1); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /minilang/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minilang" 3 | version = "0.1.0" 4 | authors = ["maekawatoshiki"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | sericum = { path = "../", features = ["x86_64"] } 11 | peg = "0.6.2" 12 | md5 = "0.7.0" 13 | rand = "0.7.3" 14 | -------------------------------------------------------------------------------- /sericumcc/examples/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define master(x, y) main(int x, char ** y) 4 | #define A 10 5 | #define B 20 6 | #if defined (A) + defined B == 2 7 | #define C 0 8 | #else 9 | #define C 100 10 | #endif 11 | 12 | int main() { 13 | int x = 1; 14 | 15 | if (x == 0) { } 16 | 17 | while (x < 2) { } 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /src/codegen/x64/machine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod abi; 2 | pub mod calc_spill_weight; 3 | pub mod eliminate_fi; 4 | pub mod inst; 5 | pub mod inst_def; 6 | pub mod live_interval_splitter; 7 | pub mod pro_epi_inserter; 8 | pub mod regalloc; 9 | pub mod register; 10 | pub mod replace_copy; 11 | pub mod replace_data; 12 | pub mod spiller; 13 | pub mod two_addr; 14 | pub use super::frame_object; 15 | -------------------------------------------------------------------------------- /sericumcc/examples/str_arr2.c: -------------------------------------------------------------------------------- 1 | #include "examples/assert.h" 2 | 3 | int strcmp(char *, char *); 4 | 5 | int main() { 6 | char *s[2][2] = { {"the", "quick"}, {"brown", "fox"} }; 7 | assert(strcmp(s[0][0], "the" ) == 0); 8 | assert(strcmp(s[0][1], "quick" ) == 0); 9 | assert(strcmp(s[1][0], "brown" ) == 0); 10 | assert(strcmp(s[1][1], "fox" ) == 0); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /src/codegen/riscv64/machine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod calc_spill_weight; 2 | pub mod inst; 3 | pub mod pro_epi_inserter; 4 | pub mod reg_coalescer; 5 | pub mod regalloc; 6 | pub mod replace_copy; 7 | pub mod validate_frame_index; 8 | // pub mod replace_data; 9 | pub mod inst_def; 10 | pub mod live_interval_splitter; 11 | pub mod register; 12 | pub mod spiller; 13 | pub use super::frame_object; 14 | -------------------------------------------------------------------------------- /src/codegen/common/machine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod basic_block; 2 | pub mod branch_folding; 3 | pub mod builder; 4 | pub mod reg_coalescer; 5 | pub mod calling_conv; 6 | pub mod const_data; 7 | pub mod eliminate_fi; 8 | pub mod frame_object; 9 | pub mod function; 10 | pub mod inst; 11 | pub mod inst_def; 12 | pub mod liveness; 13 | pub mod module; 14 | pub mod phi_elimination; 15 | pub mod regalloc; 16 | #[macro_use] 17 | pub mod register; 18 | -------------------------------------------------------------------------------- /src/codegen/internals: -------------------------------------------------------------------------------- 1 | [ 2 | "sericum.memset.p0i32.i32", 3 | "sericum.println.i32", 4 | "sericum.print.i32", 5 | "sericum.printch.i32", 6 | "sericum.println.f64", 7 | "sericum.print.f64", 8 | "sericum.sin.f64", 9 | "sericum.cos.f64", 10 | "sericum.sqrt.f64", 11 | "sericum.floor.f64", 12 | "sericum.fabs.f64", 13 | "sericum.i32_to_f64.i32", 14 | "sericum.f64_to_i32.f64", 15 | "sericum.malloc.i32" 16 | ] 17 | -------------------------------------------------------------------------------- /src/codegen/aarch64/machine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod calc_spill_weight; 2 | pub mod eliminate_fi; 3 | pub mod inst; 4 | // pub mod phi_elimination; 5 | pub mod pro_epi_inserter; 6 | pub mod regalloc; 7 | pub mod replace_copy; 8 | // pub mod validate_frame_index; 9 | // pub mod replace_data; 10 | pub mod abi; 11 | pub mod inst_def; 12 | pub mod live_interval_splitter; 13 | pub mod register; 14 | pub mod spiller; 15 | pub use super::frame_object; 16 | -------------------------------------------------------------------------------- /docs-ja/0-はじめに.md: -------------------------------------------------------------------------------- 1 | **I'm sorry for English users, but now I'm focusing on writing documents in Japanese. English version is coming soon** 2 | 3 | # はじめに 4 | 5 | ようこそ,cilkの世界へ. 6 | 7 | cilkはLLVMに触発されて作られた(今のところまだおもちゃの)コンパイラ基盤です. 8 | Rustで書かれており,Rustの便利な機能を使って簡単にコンパイラを作れるよう手助けをします. 9 | これらドキュメント群では,cilkのフロントエンド・バックエンドについて順に説明していきます. 10 | 11 | # もくじ 12 | 13 | 1. [フロントエンド入門](./frontend/0-フロントエンド入門.md) 14 | 2. [バックエンド入門](./backend/0-バックエンド入門.md) 15 | -------------------------------------------------------------------------------- /sericumcc/examples/strstr.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int find(char *s, char *q) { 4 | int s_i = 0, q_i = 0; 5 | while (s[s_i] != 0) { 6 | if (q[q_i] == 0) return s_i-q_i; 7 | if (s[s_i] == q[q_i]) q_i += 1; 8 | else if (q_i > 0) q_i = 0; 9 | s_i += 1; 10 | } 11 | return 0 - 1; 12 | } 13 | 14 | int main() { 15 | char *s = "the quick brown fox jumps over the lazy dog"; 16 | int p = find(s, "brown"); 17 | assert(p == 10); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /defs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "defs" 3 | version = "0.1.0" 4 | authors = ["uint256_t"] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "Macros to define architecture-dependent information for sericum" 8 | repository = "https://github.com/maekawatoshiki/sericum/defs" 9 | 10 | [dependencies] 11 | proc-quote = "0.3.0" 12 | proc-macro-error = "= 1.0.2" 13 | rand = "0.7.3" 14 | syn = { version="1.0.32", features=["full"] } 15 | 16 | [lib] 17 | proc-macro = true 18 | 19 | -------------------------------------------------------------------------------- /src/ir/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod basic_block; 2 | pub mod builder; 3 | pub mod codegen_prepare; 4 | pub mod const_folding; 5 | pub mod constant_pool; 6 | pub mod cse; 7 | pub mod dce; 8 | pub mod function; 9 | pub mod global_val; 10 | pub mod inst_combine; 11 | pub mod licm; 12 | pub mod liveness; 13 | pub mod mem2reg; 14 | pub mod merge_ret; 15 | pub mod module; 16 | pub mod opcode; 17 | pub mod prelude; 18 | pub mod remove_unreachable_block; 19 | pub mod simplify_loop; 20 | pub mod types; 21 | pub mod value; 22 | pub mod verify; 23 | -------------------------------------------------------------------------------- /run_kcov.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd minilang 3 | RUST_BACKTRACE=full cargo test -- --test-threads=1 4 | cd .. 5 | cd sericumcc 6 | RUST_BACKTRACE=full cargo test 7 | cd .. 8 | RUST_BACKTRACE=full cargo test --features x86_64 -- --test-threads=1 9 | REPORT=$(find ./target/debug -maxdepth 2 -regex '.+/deps/.*' -a ! -regex '.+\.\(d\|rlib\|rmeta\|so\)') 10 | for file in $REPORT; do 11 | echo $file 12 | kcov --include-pattern=sericum/src --exclude-pattern=/.cargo ./target/cov "$file" 13 | done 14 | bash <(curl -s https://codecov.io/bash) -s ./target/cov -t $1 15 | -------------------------------------------------------------------------------- /defs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_span)] 2 | 3 | extern crate proc_macro; 4 | use proc_macro::TokenStream; 5 | 6 | extern crate proc_macro_error; 7 | extern crate proc_quote; 8 | extern crate rand; 9 | extern crate syn; 10 | 11 | use proc_macro_error::*; 12 | 13 | mod isel_pat; 14 | mod node_gen; 15 | mod register; 16 | 17 | #[proc_macro] 18 | pub fn registers(item: TokenStream) -> TokenStream { 19 | register::run(item) 20 | } 21 | 22 | #[proc_macro_error] 23 | #[proc_macro] 24 | pub fn isel_pat(item: TokenStream) -> TokenStream { 25 | isel_pat::run(item) 26 | } 27 | 28 | #[proc_macro_error] 29 | #[proc_macro] 30 | pub fn node_gen(item: TokenStream) -> TokenStream { 31 | node_gen::run(item) 32 | } 33 | -------------------------------------------------------------------------------- /Dockerfile_cov: -------------------------------------------------------------------------------- 1 | FROM rustlang/rust:nightly 2 | 3 | RUN set -x && \ 4 | apt-get update && \ 5 | apt-get upgrade -y && \ 6 | apt-get install zlib1g-dev apt-utils -y && \ 7 | apt-get install opt libedit-dev build-essential make -y; \ 8 | apt-get install software-properties-common -y; 9 | 10 | RUN set -x && \ 11 | apt-get install -y cmake g++ clang pkg-config jq && \ 12 | apt-get install -y libcurl4-openssl-dev libelf-dev libdw-dev binutils-dev libiberty-dev && \ 13 | cargo install cargo-kcov && \ 14 | cargo kcov --print-install-kcov-sh | sh 15 | 16 | ADD . /opt/sericum 17 | 18 | WORKDIR /opt/sericum 19 | 20 | RUN set -x && \ 21 | export PATH=~/.cargo/bin:$PATH && \ 22 | export PATH=~/usr/local/bin:$PATH && \ 23 | rustup override set nightly 24 | -------------------------------------------------------------------------------- /sericumcc/examples/many_args.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | int f( 4 | int a0, 5 | int a1, 6 | int a2, 7 | int a3, 8 | int a4, 9 | int a5, 10 | int a6, 11 | int a7, 12 | int a8, 13 | int a9, 14 | int a10, 15 | int a11, 16 | int a12 17 | ){ 18 | return 19 | a0 20 | +a1 21 | +a2 22 | +a3 23 | +a4 24 | +a5 25 | +a6 26 | +a7 27 | +a8 28 | +a9 29 | +a10 30 | +a11 31 | +a12; 32 | } 33 | 34 | int main() { 35 | assert(f( 36 | 1, 37 | 2, 38 | 3, 39 | 4, 40 | 5, 41 | 6, 42 | 7, 43 | 8, 44 | 9, 45 | 10,//55 46 | 11,//66 47 | 12,//78 48 | 13//91 49 | ) == 91); 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /src/codegen/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod common; 3 | #[cfg(feature = "aarch64")] 4 | pub mod aarch64; 5 | #[cfg(feature = "riscv64")] 6 | pub mod riscv64; 7 | #[cfg(feature = "x86_64")] 8 | pub mod x64; 9 | #[cfg(feature = "aarch64")] 10 | pub use aarch64 as arch; 11 | #[cfg(feature = "riscv64")] 12 | pub use riscv64 as arch; 13 | #[cfg(feature = "x86_64")] 14 | pub use x64 as arch; 15 | 16 | thread_local! { 17 | pub static INTERNALS: Vec = include!("internals").iter().map(|s| s.to_string()).collect(); 18 | } 19 | 20 | pub fn is_internal_function(name: &str) -> bool { 21 | INTERNALS.with(|i| i.iter().find(|n| *n == name).is_some()) 22 | } 23 | 24 | pub fn internal_function_names() -> Vec { 25 | INTERNALS.with(|i| i.clone()) 26 | } 27 | -------------------------------------------------------------------------------- /sericumcc/examples/struct4.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | 3 | typedef struct { 4 | int x, y; 5 | } Point; 6 | 7 | int func(Point *p) { 8 | assert(p->x == 1); 9 | assert(p->y == 1); 10 | p->x += 1; 11 | return 0; 12 | } 13 | 14 | int func2(int a, Point p, int b, Point p2, int x, int y, int z) { 15 | assert(a == 12345678); 16 | assert(p.x == 2); 17 | assert(p.y == 1); 18 | assert(b == 321); 19 | assert(p2.x == 2); 20 | assert(p2.y == 3); 21 | assert(x == 3); 22 | assert(y == 4); 23 | assert(z == 5); 24 | return 0; 25 | } 26 | 27 | int main() { 28 | Point p; 29 | p.x = 1; p.y = 1; 30 | Point p2; 31 | p2.x = 2; p2.y = 3; 32 | 33 | assert(p.x == 1); 34 | func(&p); 35 | func2(12345678, p, 321, p2, 3, 4, 5); 36 | assert(p.y == 1); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sericum" 3 | version = "0.2.1" 4 | authors = ["uint256_t"] 5 | edition = "2018" 6 | license-file = "LICENSE" 7 | description = "(Toy) Compiler Infrastructure inspired by LLVM" 8 | readme = "README.md" 9 | repository = "https://github.com/maekawatoshiki/sericum" 10 | 11 | [dependencies] 12 | dyn-clone = "= 1.0.2" 13 | rustc-hash = "= 1.1.0" 14 | id-arena = "= 2.2.1" 15 | dynasm = "= 0.3.2" 16 | dynasmrt = "= 0.3.1" 17 | lazy_static = "= 1.4.0" 18 | rand = "= 0.7.3" 19 | faerie = "= 0.15.0" 20 | target-lexicon = "= 0.10.0" 21 | num = "= 0.3.0" 22 | enum_primitive = "= 0.1.1" 23 | mmap = "= 0.1.1" 24 | defs = { path = "defs", version = "0.1.0" } 25 | 26 | [features] 27 | 28 | x86_64 = [] 29 | riscv64 = [] 30 | aarch64 = [] 31 | -------------------------------------------------------------------------------- /src/ir/verify.rs: -------------------------------------------------------------------------------- 1 | use crate::{function::Function, module::Module}; 2 | use std::{error::Error, fmt}; 3 | 4 | #[derive(Debug)] 5 | pub enum VerifyError { 6 | Message(&'static str), 7 | } 8 | 9 | type Result = ::std::result::Result; 10 | 11 | pub fn verify_module(module: &Module) -> Result<()> { 12 | for (_, func) in &module.functions { 13 | verify_function(func)? 14 | } 15 | 16 | Ok(()) 17 | } 18 | 19 | pub fn verify_function(_func: &Function) -> Result<()> { 20 | Ok(()) 21 | } 22 | 23 | impl fmt::Display for VerifyError { 24 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 25 | match self { 26 | VerifyError::Message(msg) => write!(f, "VerifyError: Message: {}", msg), 27 | } 28 | } 29 | } 30 | 31 | impl Error for VerifyError {} 32 | -------------------------------------------------------------------------------- /sericumcc/examples/mandelbrot.c: -------------------------------------------------------------------------------- 1 | // show mandelbrot set. 2 | 3 | double pow(double, double); 4 | 5 | int mandelbrot(double c_x, double c_y, int n) { 6 | double x_n = 0.0, y_n = 0.0, 7 | x_n_1, y_n_1; 8 | for(int i = 0; i < n; i+=1) { 9 | x_n_1 = pow(x_n, 2.0) - pow(y_n, 2.0) + c_x; 10 | y_n_1 = 2.0 * x_n * y_n + c_y; 11 | if(pow(x_n_1, 2.0) + pow(y_n_1, 2.0) > 4.0) { 12 | return n; 13 | } else { 14 | x_n = x_n_1; 15 | y_n = y_n_1; 16 | } 17 | } 18 | return 0; 19 | } 20 | 21 | int putchar(char); 22 | int puts(char *); 23 | 24 | int main() { 25 | double x_max = 1.0, x_min = 0.0-2.0, 26 | y_max = 1.0, y_min = 0.0-1.0, 27 | dx = 0.05, dy = 0.05; 28 | for(double y = y_max; y > y_min; y -= dy) { 29 | for(double x = x_min; x < x_max; x += dx) 30 | putchar(mandelbrot(x, y, 300) == 0 ? '*' : ' '); 31 | puts(""); 32 | } 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_hygiene)] 2 | #![feature(const_fn_fn_ptr_basics)] 3 | #![feature(type_ascription)] 4 | #![feature(const_mut_refs)] 5 | #![feature(stmt_expr_attributes)] 6 | #![feature(drain_filter)] 7 | #![feature(vec_remove_item)] 8 | #![recursion_limit = "256"] 9 | 10 | #[macro_use] 11 | pub mod macros; 12 | pub mod analysis; 13 | pub mod codegen; 14 | pub mod exec; 15 | pub mod ir; 16 | pub mod traits; 17 | pub mod util; 18 | 19 | pub use ir::*; 20 | 21 | extern crate defs; 22 | #[cfg(feature = "x86_64")] 23 | #[macro_use] 24 | extern crate dynasm; 25 | extern crate dyn_clone; 26 | extern crate dynasmrt; 27 | extern crate mmap; 28 | extern crate num; 29 | #[macro_use] 30 | extern crate enum_primitive; 31 | #[macro_use] 32 | extern crate target_lexicon; 33 | extern crate faerie; 34 | extern crate id_arena; 35 | #[macro_use] 36 | extern crate lazy_static; 37 | extern crate rustc_hash; 38 | 39 | pub use rustc_hash::{FxHashMap, FxHashSet}; 40 | -------------------------------------------------------------------------------- /src/codegen/aarch64/machine/spiller.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::{frame_object::FrameIndexInfo, machine::register::*}; 2 | pub use crate::codegen::common::machine::regalloc::*; 3 | use crate::codegen::common::machine::{function::MachineFunction, liveness::LiveRegMatrix}; 4 | 5 | pub struct Spiller<'a> { 6 | _func: &'a mut MachineFunction, 7 | _matrix: &'a mut LiveRegMatrix, 8 | } 9 | 10 | impl<'a> Spiller<'a> { 11 | pub fn new(_func: &'a mut MachineFunction, _matrix: &'a mut LiveRegMatrix) -> Self { 12 | Self { _func, _matrix } 13 | } 14 | 15 | pub fn insert_evict(&mut self, _reg_id: RegisterId, _slot: &FrameIndexInfo) -> Vec { 16 | unimplemented!() 17 | } 18 | 19 | pub fn insert_reload(&mut self, _reg_id: RegisterId, _slot: &FrameIndexInfo) -> Vec { 20 | unimplemented!() 21 | } 22 | 23 | pub fn spill(&mut self, _vreg: VirtReg) -> Vec { 24 | unimplemented!() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs-ja/frontend/2-変数.md: -------------------------------------------------------------------------------- 1 | # 変数 2 | 3 | ここでは,ローカル変数の基本的な使い方について説明します. 4 | 5 | **実はまだグローバル変数が使えません!** 6 | 7 | ## ローカル変数の定義 8 | 9 | ビルダーを使って符号付32bit整数のローカル変数を定義します. 10 | ``alloca``命令がスタック領域を確保します. 11 | 12 | ```rs 13 | let i = builder.build_alloca(Type::Int32); 14 | ``` 15 | 16 | マクロならこう書けます. 17 | 18 | ```rs 19 | i = alloca i32; 20 | ``` 21 | 22 | ## ストアとロード 23 | 24 | 先ほど定義した変数に``1``を代入して,読みだしてみます. 25 | 26 | ```rs 27 | builder.build_store(Value::new_imm_int32(42), i); 28 | let load_i = builder.build_load(i); 29 | ``` 30 | 31 | これで``load_i``には``42``という数値が入っているはずです.JITなどで確認してもいいでしょう. 32 | 33 | マクロだとこうです. 34 | 35 | ```rs 36 | store (i32 42), (%i); 37 | load_i = load (%i); 38 | ``` 39 | 40 | ## マクロでの型表現 41 | 42 | マクロを使うと ``i = alloca i32`` のように変数を確保できますが, 43 | 少し複雑な型名はこのままでは扱うことができません. 44 | 45 | 例えば,まだ配列の紹介をしていませんが,``i = alloca_ ([16; i32])`` のように ``alloca_``を使う必要があります. 46 | (これはproc-macroで新しくマクロを作り直せば解決できる問題です.) 47 | 48 | ## 次のページ 49 | 50 | 次のページでは [四則演算](./3-四則演算.md)について見ていきます. 51 | -------------------------------------------------------------------------------- /sericumcc/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate sericumcc; 2 | use sericumcc::compile; 3 | 4 | extern crate argopt; 5 | use argopt::cmd; 6 | 7 | use std::path::PathBuf; 8 | 9 | #[rustfmt::skip] 10 | #[cmd] 11 | fn main( 12 | /// Input files (*.c) 13 | files: Vec, 14 | ) { 15 | for file in files { 16 | compile(file, true) 17 | } 18 | () 19 | } 20 | 21 | #[test] 22 | fn run_examples() { 23 | use std::fs; 24 | let paths = match fs::read_dir("./examples") { 25 | Ok(paths) => paths, 26 | Err(e) => panic!("{:?}", e.kind()), 27 | }; 28 | for path in paths { 29 | eprintln!("{:?}", path); 30 | let name = path.as_ref().unwrap().path().to_str().unwrap().to_string(); 31 | if name.ends_with(".h") || name.contains("game_of_life") || name.contains("hello.c") { 32 | continue; 33 | } 34 | compile(path.as_ref().unwrap().path(), false); 35 | compile(path.unwrap().path(), true) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/traits/pass.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | pub trait ModulePassTrait { 4 | type M: Debug; 5 | fn name(&self) -> &'static str; 6 | fn run_on_module(&mut self, module: &mut Self::M); 7 | } 8 | 9 | pub struct ModulePassManager { 10 | pub list: Vec>>, 11 | } 12 | 13 | impl ModulePassManager { 14 | pub fn new() -> Self { 15 | Self { list: vec![] } 16 | } 17 | 18 | pub fn run_on_module(&mut self, module: &mut M) { 19 | for pass in &mut self.list { 20 | let now = ::std::time::Instant::now(); 21 | pass.run_on_module(module); 22 | debug!(println!( 23 | "after pass '{}': {:?}", 24 | pass.name(), 25 | ::std::time::Instant::now().duration_since(now) 26 | )); 27 | } 28 | } 29 | 30 | pub fn add_pass>(&mut self, pass: A) { 31 | self.list.push(Box::new(pass)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/codegen/riscv64/machine/spiller.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::{frame_object::FrameIndexInfo, machine::register::*}; 2 | pub use crate::codegen::common::machine::regalloc::*; 3 | use crate::codegen::common::machine::{function::MachineFunction, liveness::LiveRegMatrix}; 4 | use crate::ir::types::Types; 5 | 6 | pub struct Spiller<'a> { 7 | _func: &'a mut MachineFunction, 8 | _matrix: &'a mut LiveRegMatrix, 9 | } 10 | 11 | impl<'a> Spiller<'a> { 12 | pub fn new(_func: &'a mut MachineFunction, _matrix: &'a mut LiveRegMatrix) -> Self { 13 | Self { _func, _matrix } 14 | } 15 | 16 | pub fn insert_evict(&mut self, _reg_id: RegisterId, _slot: &FrameIndexInfo) -> Vec { 17 | unimplemented!() 18 | } 19 | 20 | pub fn insert_reload( 21 | &mut self, 22 | _tys: &Types, 23 | _reg_id: RegisterId, 24 | _slot: &FrameIndexInfo, 25 | ) -> Vec { 26 | unimplemented!() 27 | } 28 | 29 | pub fn spill(&mut self, _tys: &Types, _vreg: VirtReg) -> Vec { 30 | unimplemented!() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 uint256_t 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/traits/basic_block.rs: -------------------------------------------------------------------------------- 1 | use id_arena::{Arena, Id}; 2 | use rustc_hash::FxHashSet; 3 | 4 | pub trait BasicBlockTrait: Sized { 5 | fn get_preds(&self) -> &FxHashSet>; 6 | fn get_succs(&self) -> &FxHashSet>; 7 | } 8 | 9 | pub trait BasicBlocksTrait: Sized { 10 | type BB: BasicBlockTrait; 11 | fn get_arena(&self) -> &Arena; 12 | fn get_order(&self) -> &Vec>; 13 | } 14 | 15 | #[derive(Debug, Clone)] 16 | pub struct BasicBlocksIter<'a, T: BasicBlocksTrait> { 17 | basic_blocks: &'a T, 18 | nth: usize, 19 | } 20 | 21 | impl<'a, T: BasicBlocksTrait> BasicBlocksIter<'a, T> { 22 | pub fn new(basic_blocks: &'a T) -> Self { 23 | Self { 24 | basic_blocks, 25 | nth: 0, 26 | } 27 | } 28 | } 29 | 30 | impl<'a, T: BasicBlocksTrait> Iterator for BasicBlocksIter<'a, T> { 31 | type Item = (Id, &'a T::BB); 32 | 33 | fn next(&mut self) -> Option { 34 | self.nth += 1; 35 | let id = *self.basic_blocks.get_order().get(self.nth - 1)?; 36 | Some((id, &self.basic_blocks.get_arena()[id])) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/codegen/common/dag/module.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::common::dag::function::*; 2 | use crate::ir::{constant_pool::ConstantPool, global_val::GlobalVariables, types::Types}; 3 | use id_arena::*; 4 | use std::fmt; 5 | 6 | pub struct DAGModule { 7 | pub name: String, 8 | pub functions: Arena, 9 | pub types: Types, 10 | pub global_vars: GlobalVariables, 11 | pub const_pool: ConstantPool, 12 | } 13 | 14 | impl DAGModule { 15 | pub fn add_function(&mut self, f: DAGFunction) -> DAGFunctionId { 16 | self.functions.alloc(f) 17 | } 18 | 19 | pub fn function_ref(&self, id: DAGFunctionId) -> &DAGFunction { 20 | &self.functions[id] 21 | } 22 | 23 | pub fn function_ref_mut(&mut self, id: DAGFunctionId) -> &mut DAGFunction { 24 | &mut self.functions[id] 25 | } 26 | } 27 | 28 | impl fmt::Debug for DAGModule { 29 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 | writeln!(f, "DAGModule: {}", self.name)?; 31 | 32 | for (_, func) in &self.functions { 33 | func.debug(f)?; 34 | } 35 | 36 | fmt::Result::Ok(()) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/codegen/aarch64/machine/abi.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | codegen::{arch::machine::register::*, common::machine::calling_conv::CallingConv}, 3 | ir::types::StructType, 4 | }; 5 | 6 | #[derive(Clone)] 7 | pub struct AAPCS64 { 8 | gr32: Vec, 9 | gr64: Vec, 10 | } 11 | 12 | #[rustfmt::skip] 13 | impl AAPCS64 { 14 | pub fn new() -> Self { 15 | Self { 16 | gr32: to_phys![GR32::W0, GR32::W1, GR32::W2, GR32::W3, GR32::W4, GR32::W5, GR32::W6, GR32::W7], 17 | gr64: to_phys![GR64::X0, GR64::X1, GR64::X2, GR64::X3, GR64::X4, GR64::X5, GR64::X6, GR64::X7], 18 | } 19 | } 20 | } 21 | 22 | impl CallingConv for AAPCS64 { 23 | fn get_nth_arg_reg(&self, rc: RegisterClassKind, nth: usize) -> Option { 24 | match rc { 25 | RegisterClassKind::GR32 => self.gr32.get(nth), 26 | RegisterClassKind::GR64 => self.gr64.get(nth), 27 | RegisterClassKind::WSP => None, 28 | RegisterClassKind::SP => None, 29 | } 30 | .map_or(None, |r| Some(*r)) 31 | } 32 | 33 | fn reg_classes_used_for_passing_byval(_: &StructType) -> Vec { 34 | unimplemented!() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ir/remove_unreachable_block.rs: -------------------------------------------------------------------------------- 1 | use crate::ir::{function::Function, module::Module}; 2 | use rustc_hash::FxHashSet; 3 | 4 | pub struct RemoveUnreachableBlock {} 5 | 6 | pub struct RemoveUnreachableBlockOnFunction<'a> { 7 | func: &'a mut Function, 8 | } 9 | 10 | impl RemoveUnreachableBlock { 11 | pub fn new() -> Self { 12 | Self {} 13 | } 14 | 15 | pub fn run_on_module(&mut self, module: &mut Module) { 16 | for (_, func) in &mut module.functions { 17 | RemoveUnreachableBlockOnFunction::new(func).run() 18 | } 19 | } 20 | } 21 | 22 | impl<'a> RemoveUnreachableBlockOnFunction<'a> { 23 | pub fn new(func: &'a mut Function) -> Self { 24 | Self { func } 25 | } 26 | 27 | pub fn run(&mut self) { 28 | let mut blocks2remove = FxHashSet::default(); 29 | for (block_id, block) in &self.func.basic_blocks.arena { 30 | if block.pred.len() == 0 { 31 | blocks2remove.insert(block_id); 32 | } 33 | } 34 | // Do not remove entry block 35 | if let [entry, ..] = self.func.basic_blocks.order.as_slice() { 36 | blocks2remove.remove(entry); 37 | } 38 | self.func.basic_blocks.remove_blocks(&blocks2remove); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sericumcc/examples/stdarg.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __STDARG_H 3 | #define __STDARG_H 4 | 5 | typedef struct { 6 | unsigned int gp_offset; 7 | unsigned int fp_offset; 8 | void *overflow_arg_area; 9 | void *reg_save_area; 10 | } __va_elem; 11 | 12 | typedef __va_elem va_list[1]; 13 | 14 | static void *__va_arg_gp(__va_elem *ap) { 15 | void *r = (char *)ap->reg_save_area + ap->gp_offset; 16 | ap->gp_offset += 8; 17 | return r; 18 | } 19 | 20 | static void *__va_arg_fp(__va_elem *ap) { 21 | void *r = (char *)ap->reg_save_area + ap->fp_offset; 22 | ap->fp_offset += 16; 23 | return r; 24 | } 25 | 26 | static void *__va_arg_mem(__va_elem *ap) { 27 | 1 / 0; // unimplemented 28 | } 29 | 30 | #define va_start(ap, last) __builtin_va_start(ap) 31 | #define va_arg(ap, type) \ 32 | ({ \ 33 | int klass = __builtin_reg_class((type *)0); \ 34 | *(type *)(klass == 0 ? __va_arg_gp(ap) : \ 35 | klass == 1 ? __va_arg_fp(ap) : \ 36 | __va_arg_mem(ap)); \ 37 | }) 38 | 39 | #define va_end(ap) 1 40 | #define va_copy(dest, src) ((dest)[0] = (src)[0]) 41 | 42 | // Workaround to load stdio.h properly 43 | #define __GNUC_VA_LIST 1 44 | typedef va_list __gnuc_va_list; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/codegen/common/machine/calling_conv.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | codegen::{arch::machine::register::RegisterClassKind, common::machine::register::PhysReg}, 3 | ir::types::StructType, 4 | }; 5 | use rustc_hash::FxHashMap; 6 | 7 | pub trait CallingConv: Clone { 8 | fn get_nth_arg_reg(&self, rc: RegisterClassKind, nth: usize) -> Option; 9 | fn reg_classes_used_for_passing_byval(s: &StructType) -> Vec; 10 | } 11 | 12 | #[derive(Clone)] 13 | pub struct ArgumentRegisterOrder<'a, ABI: CallingConv> { 14 | abi: &'a ABI, 15 | nths: FxHashMap, 16 | } 17 | 18 | impl<'a, ABI: CallingConv> ArgumentRegisterOrder<'a, ABI> { 19 | pub fn new(abi: &'a ABI) -> Self { 20 | Self { 21 | abi, 22 | nths: FxHashMap::default(), 23 | } 24 | } 25 | 26 | pub fn next(&mut self, rc: RegisterClassKind) -> Option { 27 | let base = rc.register_file_base_class(); 28 | let nth = self.nths.entry(base).or_insert(0); 29 | *nth += 1; 30 | self.abi.get_nth_arg_reg(rc, *nth - 1) 31 | } 32 | 33 | pub fn regs_available_for(&self, rcs: &[RegisterClassKind]) -> bool { 34 | let mut arg_regs_order: ArgumentRegisterOrder<'a, ABI> = (*self).clone(); 35 | rcs.iter().all(|&rc| arg_regs_order.next(rc).is_some()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/codegen/aarch64/dag/node.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::common::dag::node::NodeId; 2 | pub use crate::codegen::common::dag::node::*; 3 | use std::fmt; 4 | 5 | #[derive(Debug, Clone, PartialEq)] 6 | pub enum MemNodeKind { 7 | // RegImm, 8 | RegFi, 9 | Reg, 10 | // FiReg, 11 | // ImmReg, 12 | Address, 13 | } 14 | 15 | #[derive(Clone, PartialEq)] 16 | pub enum MemKind { 17 | // RegImm, 18 | RegFi([NodeId; 2]), 19 | Reg(NodeId), 20 | // FiReg, 21 | // ImmReg, 22 | Address(NodeId), 23 | } 24 | 25 | impl MemKind { 26 | pub fn args(&self) -> &[NodeId] { 27 | match self { 28 | Self::RegFi(args) => args, 29 | Self::Reg(arg) | Self::Address(arg) => ::core::slice::from_ref(arg), 30 | } 31 | } 32 | 33 | pub fn args_mut(&mut self) -> &mut [NodeId] { 34 | match self { 35 | Self::RegFi(args) => args, 36 | Self::Reg(arg) | Self::Address(arg) => ::core::slice::from_mut(arg), 37 | } 38 | } 39 | } 40 | 41 | impl fmt::Debug for MemKind { 42 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 43 | write!( 44 | f, 45 | "{}", 46 | match self { 47 | Self::RegFi(_) => "RegFi", 48 | Self::Reg(_) => "Reg", 49 | Self::Address(_) => "Address", 50 | } 51 | ) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sericumcc/examples/sericumcc.h: -------------------------------------------------------------------------------- 1 | #define _LP64 1 2 | #define __SERICUMCC__ 1 3 | #define __ELF__ 1 4 | #define __LP64__ 1 5 | #define __SIZEOF_DOUBLE__ 8 6 | #define __SIZEOF_FLOAT__ 4 7 | #define __SIZEOF_INT__ 4 8 | #define __SIZEOF_LONG_DOUBLE__ 8 9 | #define __SIZEOF_LONG_LONG__ 8 10 | #define __SIZEOF_LONG__ 8 11 | #define __SIZEOF_POINTER__ 8 12 | #define __SIZEOF_PTRDIFF_T__ 8 13 | #define __SIZEOF_SHORT__ 2 14 | #define __SIZEOF_SIZE_T__ 8 15 | #define __STDC_HOSTED__ 1 16 | #define __STDC_ISO_10646__ 201103L 17 | #define __STDC_NO_ATOMICS__ 1 18 | #define __STDC_NO_COMPLEX__ 1 19 | #define __STDC_NO_THREADS__ 1 20 | #define __STDC_NO_VLA__ 1 21 | #define __STDC_UTF_16__ 1 22 | #define __STDC_UTF_32__ 1 23 | #define __STDC_VERSION__ 201112L 24 | #define __STDC__ 1 25 | #define __amd64 1 26 | #define __amd64__ 1 27 | #define __gnu_linux__ 1 28 | #define __linux 1 29 | #define __linux__ 1 30 | #define __unix 1 31 | #define __unix__ 1 32 | #define __x86_64 1 33 | #define __x86_64__ 1 34 | #define linux 1 35 | #define NO_ANSI_KEYWORDS 1 36 | 37 | #define __alignof__ alignof 38 | #define __const__ const 39 | #define __inline__ inline 40 | #define __restrict restrict 41 | #define __restrict__ restrict 42 | #define __signed__ signed 43 | #define __typeof__ typeof 44 | #define __volatile__ volatile 45 | 46 | typedef unsigned short char16_t; 47 | typedef unsigned int char32_t; 48 | 49 | typedef long size_t; 50 | 51 | -------------------------------------------------------------------------------- /src/codegen/aarch64/dag/convert.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::{ 2 | arch::machine::{abi::AAPCS64, register::*}, 3 | common::{ 4 | machine::calling_conv::{ArgumentRegisterOrder, CallingConv}, 5 | dag::{ 6 | convert::BlockConversionContext, 7 | node::{IRNode, IROpcode}, 8 | }, 9 | }, 10 | }; 11 | 12 | pub fn copy_reg_args<'a>(ctx: &mut BlockConversionContext<'a>) { 13 | let abi = AAPCS64::new(); 14 | let mut arg_regs_order = ArgumentRegisterOrder::new(&abi); 15 | 16 | for i in 0..ctx.func.get_params_len() { 17 | if let Some(ty) = ctx.func.get_param_type(i) { 18 | let arg_reg_class = match ty2rc(&ty) { 19 | Some(rc) => rc, 20 | None => continue, 21 | }; 22 | let arg_reg = match arg_regs_order.next(arg_reg_class) { 23 | Some(reg) => reg, 24 | None => continue, 25 | }; 26 | let arg_reg = ctx.node(ctx.regs.get_phys_reg(arg_reg).into()); 27 | let vreg = ctx.node(ctx.regs.new_virt_reg(arg_reg_class).into()); 28 | let copy = ctx.node( 29 | IRNode::new(IROpcode::CopyToReg) 30 | .args(vec![vreg, arg_reg]) 31 | .ty(ty) 32 | .into(), 33 | ); 34 | ctx.make_chain(copy); 35 | ctx.arg_regs.insert(i, vreg); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/codegen/riscv64/dag/convert.rs: -------------------------------------------------------------------------------- 1 | use super::node::*; 2 | use crate::codegen::arch::machine::register::*; 3 | use crate::codegen::common::dag::convert::ConvertToDAGNode; 4 | 5 | impl<'a> ConvertToDAGNode<'a> { 6 | pub fn copy_reg_args(&mut self) { 7 | for i in 0..self.func.get_params_len() { 8 | if let Some(ty) = self.func.get_param_type(i) { 9 | let arg_reg_class = match ty2rc(&ty) { 10 | Some(rc) => rc, 11 | None => continue, 12 | }; 13 | let arg_reg = match arg_reg_class.get_nth_arg_reg(i) { 14 | Some(reg) => reg, 15 | None => continue, 16 | }; 17 | let arg_reg = self.alloc_node(DAGNode::new_phys_reg(&self.regs_info, arg_reg)); 18 | let vreg = self.regs_info.new_virt_reg(arg_reg_class); 19 | let vreg = self.alloc_node(DAGNode::new( 20 | NodeKind::Operand(OperandNodeKind::Register(vreg)), 21 | vec![], 22 | ty, 23 | )); 24 | let copy = self.alloc_node(DAGNode::new( 25 | NodeKind::IR(IRNodeKind::CopyToReg), 26 | vec![vreg, arg_reg], 27 | ty, 28 | )); 29 | self.make_chain(copy); 30 | self.arg_regs.insert(i, vreg); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/demo.rs: -------------------------------------------------------------------------------- 1 | use sericum::{ 2 | codegen::arch::{asm::print::MachineAsmPrinter, standard_conversion_into_machine_module}, 3 | ir::{builder::IRBuilder, module::Module, opcode::ICmpKind, types::Type}, 4 | }; 5 | 6 | #[test] 7 | fn demo() { 8 | let mut module = Module::new("demo"); 9 | let fibo = module.create_function("fibo", Type::i32, vec![Type::i32]); 10 | let mut builder = module.ir_builder(fibo); 11 | 12 | let entry = builder.append_basic_block(); 13 | let block1 = builder.append_basic_block(); 14 | let block2 = builder.append_basic_block(); 15 | 16 | builder.set_insert_point(entry); 17 | let arg0 = builder.get_param(0).unwrap(); 18 | let eq = builder.build_icmp(ICmpKind::Le, arg0, 1); 19 | builder.build_cond_br(eq, block1, block2); 20 | 21 | builder.set_insert_point(block1); 22 | builder.build_ret(1); 23 | 24 | builder.set_insert_point(block2); 25 | let arg1 = builder.build_sub(arg0, 1); 26 | let ret0 = builder.build_call(builder.new_func_value(fibo).unwrap(), vec![arg1]); 27 | let arg2 = builder.build_sub(arg0, 2); 28 | let ret1 = builder.build_call(builder.new_func_value(fibo).unwrap(), vec![arg2]); 29 | let add = builder.build_add(ret0, ret1); 30 | builder.build_ret(add); 31 | 32 | println!("IR:\n{:?}", module); 33 | 34 | let machine_module = standard_conversion_into_machine_module(module); 35 | let mut printer = MachineAsmPrinter::new(); 36 | printer.run_on_module(&machine_module); 37 | 38 | println!("Assembly:\n{}", printer.output); 39 | } 40 | -------------------------------------------------------------------------------- /src/codegen/riscv64/machine/regalloc.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::{ 2 | frame_object::FrameIndexInfo, 3 | machine::inst::{MachineMemOperand, MachineOpcode}, 4 | machine::register::*, 5 | }; 6 | pub use crate::codegen::common::machine::regalloc::*; 7 | use crate::codegen::common::machine::{ 8 | basic_block::MachineBasicBlockId, 9 | function::MachineFunction, 10 | inst::{MachineInst, MachineInstId, MachineOperand}, 11 | }; 12 | 13 | impl RegisterAllocator { 14 | pub fn store_and_load_for_reg_preservation( 15 | &mut self, 16 | f: &mut MachineFunction, 17 | reg: RegisterId, 18 | frinfo: FrameIndexInfo, 19 | parent: MachineBasicBlockId, 20 | ) -> (MachineInstId, MachineInstId) { 21 | let src = MachineOperand::Register(reg); 22 | let s0 = f.regs_info.get_phys_reg(GPR::S0); 23 | let store_inst_id = f.alloc_inst(MachineInst::new( 24 | &f.regs_info, 25 | MachineOpcode::SD, 26 | vec![ 27 | src, 28 | MachineOperand::Mem(MachineMemOperand::FiReg(frinfo, s0)), 29 | ], 30 | None, 31 | parent, 32 | )); 33 | 34 | let load_inst_id = f.alloc_inst( 35 | MachineInst::new_simple( 36 | MachineOpcode::LD, 37 | vec![MachineOperand::Mem(MachineMemOperand::FiReg(frinfo, s0))], 38 | parent, 39 | ) 40 | .with_def(vec![reg]), 41 | ); 42 | 43 | (store_inst_id, load_inst_id) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/codegen/riscv64/machine/replace_copy.rs: -------------------------------------------------------------------------------- 1 | use super::super::dag::mc_convert::opcode_copy2reg; 2 | use super::inst::MachineOpcode; 3 | use crate::codegen::common::machine::{function::MachineFunction, module::MachineModule}; 4 | use crate::traits::pass::ModulePassTrait; 5 | 6 | pub struct ReplaceCopyWithProperMInst {} 7 | 8 | impl ModulePassTrait for ReplaceCopyWithProperMInst { 9 | type M = MachineModule; 10 | 11 | fn name(&self) -> &'static str { 12 | "ReplaceCopy" 13 | } 14 | 15 | fn run_on_module(&mut self, module: &mut Self::M) { 16 | self.run_on_module(module) 17 | } 18 | } 19 | 20 | impl ReplaceCopyWithProperMInst { 21 | pub fn new() -> Self { 22 | Self {} 23 | } 24 | 25 | pub fn run_on_module(&mut self, module: &mut MachineModule) { 26 | for (_, f) in &mut module.functions { 27 | if f.is_internal { 28 | continue; 29 | } 30 | self.run_on_function(/*&module.types,*/ f); 31 | } 32 | } 33 | 34 | pub fn run_on_function(&mut self, /*tys: &Types,*/ f: &mut MachineFunction) { 35 | for (_, bb) in f.body.basic_blocks.id_and_block() { 36 | for inst_id in &*bb.iseq_ref() { 37 | let inst = &mut f.body.inst_arena[*inst_id]; 38 | 39 | if inst.opcode != MachineOpcode::Copy { 40 | continue; 41 | } 42 | 43 | let opcode = opcode_copy2reg(&inst.operand[0]); 44 | inst.opcode = opcode; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/codegen/x64/machine/replace_copy.rs: -------------------------------------------------------------------------------- 1 | use super::super::dag::mc_convert::mov_rx; 2 | use super::inst::MachineOpcode; 3 | use crate::codegen::common::machine::{function::MachineFunction, module::MachineModule}; 4 | use crate::traits::pass::ModulePassTrait; 5 | 6 | pub struct ReplaceCopyWithProperMInst {} 7 | 8 | impl ModulePassTrait for ReplaceCopyWithProperMInst { 9 | type M = MachineModule; 10 | 11 | fn name(&self) -> &'static str { 12 | "ReplaceCopy" 13 | } 14 | 15 | fn run_on_module(&mut self, module: &mut Self::M) { 16 | self.run_on_module(module) 17 | } 18 | } 19 | 20 | impl ReplaceCopyWithProperMInst { 21 | pub fn new() -> Self { 22 | Self {} 23 | } 24 | 25 | pub fn run_on_module(&mut self, module: &mut MachineModule) { 26 | for (_, f) in &mut module.functions { 27 | self.run_on_function(f); 28 | } 29 | } 30 | 31 | pub fn run_on_function(&mut self, f: &mut MachineFunction) { 32 | if f.is_internal { 33 | return; 34 | } 35 | 36 | for (_, bb) in f.body.basic_blocks.id_and_block() { 37 | for inst_id in &*bb.iseq_ref() { 38 | let inst = &mut f.body.inst_arena[*inst_id]; 39 | 40 | if inst.opcode != MachineOpcode::Copy { 41 | continue; 42 | } 43 | 44 | let mov = 45 | mov_rx(inst.def[0].id.as_phys_reg().reg_class(), &inst.operand[0]).unwrap(); 46 | inst.opcode = mov; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sericumcc/examples/game_of_life.c: -------------------------------------------------------------------------------- 1 | #define W 30 2 | #define H 30 3 | #define RAND_MAX 2147483647 4 | 5 | int usleep(int); 6 | int putchar(char); 7 | int rand(); 8 | int srand(int); 9 | int puts(char *); 10 | int printf(char *); 11 | 12 | int show(int univ[H][W]) { 13 | printf("\033[H"); 14 | for (int y = 0; y < H; y += 1) { 15 | for (int x = 0; x < W; x += 1) { 16 | if (univ[y][x] == 1) 17 | printf("\033[07m \033[m"); 18 | else printf(" "); 19 | } 20 | printf("\033[E"); 21 | } 22 | return 0; 23 | } 24 | 25 | int evolve(int univ[H][W]) { 26 | int new[H][W]; 27 | 28 | for (int y = 0; y < H; y += 1) { 29 | for (int x = 0; x < W; x += 1) { 30 | int n = 0; 31 | for (int y1 = y - 1; y1 <= y + 1; y1+=1) 32 | for (int x1 = x - 1; x1 <= x + 1; x1+=1) 33 | if (univ[(y1 + H) % H][(x1 + W) % W] == 1) 34 | n += 1; 35 | if (univ[y][x] == 1) n -= 1; 36 | new[y][x] = 0; 37 | if (n == 3) { 38 | new[y][x] = 1; 39 | } else { 40 | if (n == 2) if (univ[y][x] == 1) new[y][x] = 1; 41 | } 42 | } 43 | } 44 | 45 | for (int y = 0; y < H; y += 1) 46 | for (int x = 0; x < W; x += 1) 47 | univ[y][x] = new[y][x]; 48 | 49 | return 0; 50 | } 51 | 52 | int game() { 53 | int univ[H][W]; 54 | for (int x = 0; x < W; x += 1) 55 | for (int y = 0; y < H; y += 1) 56 | univ[y][x] = rand() < RAND_MAX / 10 ? 1 : 0; 57 | for (;;) { 58 | show(univ); 59 | evolve(univ); 60 | usleep(100000); 61 | } 62 | return 0; 63 | } 64 | 65 | int main() { 66 | game(); 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /src/codegen/aarch64/machine/replace_copy.rs: -------------------------------------------------------------------------------- 1 | use super::super::dag::mc_convert::mov_rx; 2 | use super::inst::MachineOpcode; 3 | use crate::codegen::common::machine::{function::MachineFunction, module::MachineModule}; 4 | use crate::traits::pass::ModulePassTrait; 5 | 6 | pub struct ReplaceCopyWithProperMInst {} 7 | 8 | impl ModulePassTrait for ReplaceCopyWithProperMInst { 9 | type M = MachineModule; 10 | 11 | fn name(&self) -> &'static str { 12 | "ReplaceCopy" 13 | } 14 | 15 | fn run_on_module(&mut self, module: &mut Self::M) { 16 | self.run_on_module(module) 17 | } 18 | } 19 | 20 | impl ReplaceCopyWithProperMInst { 21 | pub fn new() -> Self { 22 | Self {} 23 | } 24 | 25 | pub fn run_on_module(&mut self, module: &mut MachineModule) { 26 | for (_, f) in &mut module.functions { 27 | if f.is_internal { 28 | continue; 29 | } 30 | self.run_on_function(/*&module.types,*/ f); 31 | } 32 | } 33 | 34 | pub fn run_on_function(&mut self, /*tys: &Types,*/ f: &mut MachineFunction) { 35 | for (_, bb) in f.body.basic_blocks.id_and_block() { 36 | for inst_id in &*bb.iseq_ref() { 37 | let inst = &mut f.body.inst_arena[*inst_id]; 38 | 39 | if inst.opcode != MachineOpcode::Copy { 40 | continue; 41 | } 42 | 43 | let opcode = 44 | mov_rx(inst.def[0].id.as_phys_reg().reg_class(), &inst.operand[0]).unwrap(); 45 | inst.opcode = opcode; 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/codegen/riscv64/machine/register.rs: -------------------------------------------------------------------------------- 1 | pub use crate::codegen::common::machine::register::*; 2 | use crate::ir::types::Type; 3 | use defs::registers; 4 | use id_arena::Arena; 5 | use rustc_hash::FxHashMap; 6 | use std::cell::RefCell; 7 | 8 | registers! { 9 | class GPR (64, i64, [i32, i64, Pointer!], [A0, A1]) { 10 | ZERO, RA, SP, GP, TP, T0, T1, T2, S0, S1, A0, 11 | A1, A2, A3, A4, A5, A6, A7, S2, S3, S4, S5, 12 | S6, S7, S8, S9, S10, S11, T3, T4, T5, T6 13 | } 14 | 15 | order arg GPR { A0, A1, A2, A3, A4, A5, A6, A7 } 16 | 17 | order gp GPR { T0, T1, T2, A0, A1, A2, A3, A4, 18 | A5, A6, A7, T3, T4, T5, T6, S2, 19 | S3, S4, S5, S6, S7, S8, S9, S10, S11 } // S1 is reserved 20 | } 21 | 22 | macro_rules! to_phys { 23 | ($($r:path),*) => { 24 | vec![$(($r.as_phys_reg())),*] 25 | }; 26 | } 27 | 28 | thread_local! { 29 | pub static CALLEE_SAVED_REGS: PhysRegSet = { 30 | let mut bits = PhysRegSet::new(); 31 | let regs = to_phys![ 32 | GPR::SP, 33 | GPR::S0, 34 | GPR::S1, 35 | GPR::S2, 36 | GPR::S3, 37 | GPR::S4, 38 | GPR::S5, 39 | GPR::S6, 40 | GPR::S7, 41 | GPR::S8, 42 | GPR::S9, 43 | GPR::S10, 44 | GPR::S11 45 | ]; 46 | for reg in regs { 47 | bits.set(reg) 48 | } 49 | bits 50 | }; 51 | 52 | pub static REG_FILE: RefCell> = { 53 | RefCell::new(FxHashMap::default()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/codegen/common/types.rs: -------------------------------------------------------------------------------- 1 | use crate::ir::types::Type; 2 | 3 | /// Machine Value Type 4 | #[allow(non_camel_case_types)] 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 6 | pub enum MVType { 7 | Invalid, 8 | Void, 9 | i1, 10 | i8, 11 | i16, 12 | i32, 13 | i64, 14 | f32, 15 | f64, 16 | } 17 | 18 | impl From for MVType { 19 | fn from(ty: Type) -> Self { 20 | match ty { 21 | Type::Void => MVType::Void, 22 | Type::i1 => MVType::i1, 23 | Type::i8 => MVType::i8, 24 | Type::i32 => MVType::i32, 25 | Type::i64 => MVType::i64, 26 | Type::f64 => MVType::f64, 27 | Type::Pointer(_) => MVType::i64, 28 | Type::Array(_) | Type::Struct(_) | Type::Function(_) => MVType::Invalid, 29 | } 30 | } 31 | } 32 | 33 | impl MVType { 34 | pub fn is_integer(&self) -> bool { 35 | matches!( 36 | self, 37 | Self::i1 | Self::i8 | Self::i16 | Self::i32 | Self::i64 38 | ) 39 | } 40 | 41 | pub fn size_in_bits(&self) -> i32 { 42 | match self { 43 | Self::Invalid => 0, 44 | Self::Void => 0, 45 | Self::i1 => 1, 46 | Self::i8 => 8, 47 | Self::i16 => 16, 48 | Self::i32 => 32, 49 | Self::i64 => 64, 50 | Self::f32 => 32, 51 | Self::f64 => 64, 52 | } 53 | } 54 | 55 | pub fn size_in_byte(&self) -> i32 { 56 | match self { 57 | Self::Invalid => 0, 58 | Self::Void => 0, 59 | Self::i1 => 1, 60 | Self::i8 => 1, 61 | Self::i16 => 2, 62 | Self::i32 => 4, 63 | Self::i64 => 8, 64 | Self::f32 => 4, 65 | Self::f64 => 8, 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sericum 2 | 3 | [![CircleCI](https://circleci.com/gh/maekawatoshiki/sericum.svg?style=shield)](https://circleci.com/gh/maekawatoshiki/sericum) 4 | [![](http://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) 5 | 6 | Compiler Infrastructure influenced by LLVM written in Rust 7 | 8 | Do not expect too much stuff! 9 | 10 | # To Do 11 | 12 | - [ ] Implement basic block parameters 13 | - [ ] Make it possible to generate code for multiple targets without rebuilding sericum itself 14 | - [ ] Verify IR 15 | - [ ] More optimizations for IR 16 | - [ ] Support returning struct as value 17 | - [ ] Write documents 18 | 19 | # Build 20 | 21 | **Requirement: Rust nightly** 22 | 23 | ```sh 24 | cargo test --features x86_64 # build for x86_64 25 | cargo test brainfuxk --features x86_64 --release -- --nocapture # this is fun. just try it. 26 | cargo test --features aarch64 # build for aarch64. a few features are implemented. 27 | cargo test --features riscv64 # currently doesn't work. need help. 28 | ``` 29 | 30 | # Example 31 | 32 | - [Generate a function which calculates a fibonacci number](./tests/demo.rs) 33 | 34 | ```sh 35 | cargo test demo --features $ARCH -- --nocapture # $ARCH is x86_64 or aarch64 36 | ``` 37 | 38 | - Useful macro is available to describe IR 39 | 40 | ```rust 41 | let fibo = sericum_ir!(m; define [i32] f [(i32)] { 42 | entry: 43 | cond = icmp le (%arg.0), (i32 2); 44 | br (%cond) l1, l2; 45 | l1: 46 | ret (i32 1); 47 | l2: 48 | a1 = sub (%arg.0), (i32 1); 49 | r1 = call f [(%a1)]; 50 | a2 = sub (%arg.0), (i32 2); 51 | r2 = call f [(%a2)]; 52 | r3 = add (%r1), (%r2); 53 | ret (%r3); 54 | }); 55 | ``` 56 | 57 | # Make your own language using sericum as backend 58 | 59 | ``./minilang`` and ``./sericumcc`` may help you. 60 | -------------------------------------------------------------------------------- /src/util/allocator.rs: -------------------------------------------------------------------------------- 1 | use rustc_hash::FxHashSet; 2 | use std::hash::{Hash, Hasher}; 3 | use std::ops::{Deref, DerefMut}; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Raw(*mut T); 7 | 8 | impl Copy for Raw {} 9 | 10 | impl PartialEq for Raw { 11 | fn eq(&self, other: &Self) -> bool { 12 | self.0 as u64 == other.0 as u64 13 | } 14 | } 15 | 16 | impl Hash for Raw { 17 | fn hash(&self, state: &mut H) { 18 | (self.0 as u64).hash(state) 19 | } 20 | } 21 | 22 | impl Eq for Raw {} 23 | 24 | pub struct RawAllocator { 25 | allocated: FxHashSet>, 26 | } 27 | 28 | impl RawAllocator { 29 | pub fn new() -> Self { 30 | RawAllocator { 31 | allocated: FxHashSet::default(), 32 | } 33 | } 34 | 35 | pub fn alloc(&mut self, val: T) -> Raw { 36 | let raw = Box::into_raw(Box::new(val)); 37 | self.allocated.insert(Raw(raw)); 38 | Raw(raw) 39 | } 40 | 41 | pub fn allocated_ref(&self) -> &FxHashSet> { 42 | &self.allocated 43 | } 44 | } 45 | 46 | impl Drop for RawAllocator { 47 | fn drop(&mut self) { 48 | for raw in &self.allocated { 49 | unsafe { 50 | Box::from_raw(raw.inner()); 51 | } 52 | } 53 | } 54 | } 55 | 56 | impl Raw { 57 | pub fn inner(&self) -> *mut T { 58 | self.0 59 | } 60 | 61 | pub fn inner_ref(&self) -> &T { 62 | unsafe { &*self.0 } 63 | } 64 | 65 | pub fn inner_ref_mut(&self) -> &mut T { 66 | unsafe { &mut *self.0 } 67 | } 68 | } 69 | 70 | impl Deref for Raw { 71 | type Target = T; 72 | 73 | fn deref(&self) -> &T { 74 | unsafe { &*self.inner() } 75 | } 76 | } 77 | 78 | impl DerefMut for Raw { 79 | fn deref_mut(&mut self) -> &mut T { 80 | unsafe { &mut *self.inner() } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/codegen/common/dag/basic_block.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::common::dag::node::{Node, NodeId}; 2 | use crate::ir::types::Types; 3 | use id_arena::*; 4 | use rustc_hash::{FxHashMap, FxHashSet}; 5 | use std::fmt; 6 | 7 | pub type DAGBasicBlockId = Id; 8 | 9 | #[derive(Clone)] 10 | pub struct DAGBasicBlock { 11 | /// Predecessors 12 | pub pred: FxHashSet, 13 | 14 | /// Successors 15 | pub succ: FxHashSet, 16 | 17 | /// Entry node 18 | pub entry: Option, 19 | 20 | /// Root node 21 | pub root: Option, 22 | } 23 | 24 | impl DAGBasicBlock { 25 | pub fn new() -> Self { 26 | Self { 27 | entry: None, 28 | root: None, 29 | pred: FxHashSet::default(), 30 | succ: FxHashSet::default(), 31 | } 32 | } 33 | 34 | pub fn set_entry(&mut self, entry: NodeId) { 35 | self.entry = Some(entry); 36 | } 37 | 38 | pub fn set_root(&mut self, root: NodeId) { 39 | self.root = Some(root); 40 | } 41 | 42 | pub fn debug( 43 | &self, 44 | f: &mut fmt::Formatter<'_>, 45 | arena: &Arena, 46 | tys: &Types, 47 | bb_idx: usize, 48 | ) -> fmt::Result { 49 | writeln!( 50 | f, 51 | "BB({}); pred: {{{}}}, succ: {{{}}});", 52 | bb_idx, 53 | self.pred 54 | .iter() 55 | .fold("".to_string(), |s, id| format!("{}{},", s, id.index())) 56 | .trim_matches(','), 57 | self.succ 58 | .iter() 59 | .fold("".to_string(), |s, id| format!("{}{},", s, id.index())) 60 | .trim_matches(','), 61 | )?; 62 | 63 | if let Some(entry) = self.entry { 64 | arena[entry].debug(f, arena, tys, &mut FxHashMap::default(), 0, 2)?; 65 | } 66 | 67 | fmt::Result::Ok(()) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/codegen/common/machine/module.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::common::machine::function::*; 2 | use crate::ir::{constant_pool::ConstantPool, global_val::GlobalVariables, types::*}; 3 | use id_arena::*; 4 | use std::fmt; 5 | 6 | pub struct MachineModule { 7 | pub name: String, 8 | pub functions: Arena, 9 | pub types: Types, 10 | pub global_vars: GlobalVariables, 11 | pub const_pool: ConstantPool, 12 | } 13 | 14 | impl MachineModule { 15 | pub fn new( 16 | name: String, 17 | functions: Arena, 18 | types: Types, 19 | global_vars: GlobalVariables, 20 | const_pool: ConstantPool, 21 | ) -> Self { 22 | Self { 23 | name, 24 | functions, 25 | types, 26 | global_vars, 27 | const_pool, 28 | } 29 | } 30 | 31 | pub fn add_function(&mut self, f: MachineFunction) -> MachineFunctionId { 32 | let id = self.functions.alloc(f); 33 | self.function_ref_mut(id).id = Some(id); 34 | id 35 | } 36 | 37 | pub fn function_ref(&self, id: MachineFunctionId) -> &MachineFunction { 38 | &self.functions[id] 39 | } 40 | 41 | pub fn function_ref_mut(&mut self, id: MachineFunctionId) -> &mut MachineFunction { 42 | &mut self.functions[id] 43 | } 44 | 45 | pub fn find_function_by_name(&self, name: &str) -> Option { 46 | for (id, func) in &self.functions { 47 | if func.name == name { 48 | return Some(id); 49 | } 50 | } 51 | None 52 | } 53 | } 54 | 55 | impl fmt::Debug for MachineModule { 56 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 57 | writeln!(f, "MachineModule (name: {})", self.name)?; 58 | 59 | for (_, func) in &self.functions { 60 | func.debug(f, &self.types)?; 61 | } 62 | 63 | fmt::Result::Ok(()) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/codegen/x64/machine/replace_data.rs: -------------------------------------------------------------------------------- 1 | use super::inst::*; 2 | use crate::codegen::common::machine::{function::*, module::*}; 3 | use crate::traits::pass::ModulePassTrait; 4 | 5 | pub struct ReplaceConstFPWithMemoryRef {} 6 | 7 | impl ModulePassTrait for ReplaceConstFPWithMemoryRef { 8 | type M = MachineModule; 9 | 10 | fn name(&self) -> &'static str { 11 | "ReplaceConstFPWithMemoryRef" 12 | } 13 | 14 | fn run_on_module(&mut self, module: &mut Self::M) { 15 | self.run_on_module(module); 16 | } 17 | } 18 | 19 | impl ReplaceConstFPWithMemoryRef { 20 | pub fn new() -> Self { 21 | Self {} 22 | } 23 | 24 | pub fn run_on_module(&mut self, module: &mut MachineModule) { 25 | for (_, func) in &mut module.functions { 26 | if func.is_internal { 27 | continue; 28 | } 29 | self.run_on_function(func); 30 | } 31 | } 32 | 33 | pub fn run_on_function(&mut self, cur_func: &mut MachineFunction) { 34 | for (_, bb) in cur_func.body.basic_blocks.id_and_block() { 35 | for inst_id in &*bb.iseq_ref() { 36 | let inst = &mut cur_func.body.inst_arena[*inst_id]; 37 | let replace = matches!(inst.opcode, MachineOpcode::MOVSDrm64); 38 | if !replace { 39 | continue; 40 | } 41 | for operand in &mut inst.operand { 42 | match operand { 43 | MachineOperand::Constant(MachineConstant::F64(f)) => { 44 | let id = cur_func.const_data.alloc(MachineConstant::F64(*f)); 45 | *operand = MachineOperand::Mem(MachineMemOperand::Address( 46 | AddressKind::Label(id), 47 | )); 48 | } 49 | _ => {} 50 | }; 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/codegen/aarch64/machine/replace_data.rs: -------------------------------------------------------------------------------- 1 | use super::const_data::ConstDataArena; 2 | use super::{function::*, inst::*, module::*}; 3 | use crate::traits::pass::ModulePassTrait; 4 | 5 | pub struct ReplaceConstFPWithMemoryRef {} 6 | 7 | impl ModulePassTrait for ReplaceConstFPWithMemoryRef { 8 | type M = MachineModule; 9 | 10 | fn name(&self) -> &'static str { 11 | "ReplaceConstFPWithMemoryRef" 12 | } 13 | 14 | fn run_on_module(&mut self, module: &mut Self::M) { 15 | self.run_on_module(module); 16 | } 17 | } 18 | 19 | impl ReplaceConstFPWithMemoryRef { 20 | pub fn new() -> Self { 21 | Self {} 22 | } 23 | 24 | pub fn run_on_module(&mut self, module: &mut MachineModule) { 25 | for (_, func) in &mut module.functions { 26 | if func.is_internal { 27 | continue; 28 | } 29 | self.run_on_function(&mut module.const_data, func); 30 | } 31 | } 32 | 33 | pub fn run_on_function(&mut self, data: &mut ConstDataArena, cur_func: &mut MachineFunction) { 34 | for (_, bb) in cur_func.body.basic_blocks.id_and_block() { 35 | for inst_id in &*bb.iseq_ref() { 36 | let inst = &mut cur_func.body.inst_arena[*inst_id]; 37 | let replace = matches!(inst.opcode, MachineOpcode::MOVSDrm64); 38 | if !replace { 39 | continue; 40 | } 41 | for operand in &mut inst.operand { 42 | match operand { 43 | MachineOperand::Constant(MachineConstant::F64(f)) => { 44 | let id = data.alloc(MachineConstant::F64(*f)); 45 | *operand = MachineOperand::Mem(MachineMemOperand::Address( 46 | AddressKind::Label(id), 47 | )); 48 | } 49 | _ => {} 50 | }; 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/codegen/riscv64/machine/replace_data.rs: -------------------------------------------------------------------------------- 1 | use super::const_data::ConstDataArena; 2 | use super::{function::*, inst::*, module::*}; 3 | use crate::traits::pass::ModulePassTrait; 4 | 5 | pub struct ReplaceConstFPWithMemoryRef {} 6 | 7 | impl ModulePassTrait for ReplaceConstFPWithMemoryRef { 8 | type M = MachineModule; 9 | 10 | fn name(&self) -> &'static str { 11 | "ReplaceConstFPWithMemoryRef" 12 | } 13 | 14 | fn run_on_module(&mut self, module: &mut Self::M) { 15 | self.run_on_module(module); 16 | } 17 | } 18 | 19 | impl ReplaceConstFPWithMemoryRef { 20 | pub fn new() -> Self { 21 | Self {} 22 | } 23 | 24 | pub fn run_on_module(&mut self, module: &mut MachineModule) { 25 | for (_, func) in &mut module.functions { 26 | if func.is_internal { 27 | continue; 28 | } 29 | self.run_on_function(&mut module.const_data, func); 30 | } 31 | } 32 | 33 | pub fn run_on_function(&mut self, data: &mut ConstDataArena, cur_func: &mut MachineFunction) { 34 | for (_, bb) in cur_func.body.basic_blocks.id_and_block() { 35 | for inst_id in &*bb.iseq_ref() { 36 | let inst = &mut cur_func.body.inst_arena[*inst_id]; 37 | let replace = matches!(inst.opcode, MachineOpcode::MOVSDrm64); 38 | if !replace { 39 | continue; 40 | } 41 | for operand in &mut inst.operand { 42 | match operand { 43 | MachineOperand::Constant(MachineConstant::F64(f)) => { 44 | let id = data.alloc(MachineConstant::F64(*f)); 45 | *operand = MachineOperand::Mem(MachineMemOperand::Address( 46 | AddressKind::Label(id), 47 | )); 48 | } 49 | _ => {} 50 | }; 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/codegen/common/dag/function.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::{frame_object::*, machine::register::*}; 2 | use crate::codegen::common::dag::{basic_block::*, node::*}; 3 | use crate::ir::{function::*, types::*}; 4 | use id_arena::*; 5 | use std::fmt; 6 | 7 | pub type DAGFunctionId = Id; 8 | 9 | pub struct DAGFunction { 10 | /// Function name 11 | pub name: String, 12 | 13 | /// Function type 14 | pub ty: Type, 15 | 16 | /// DAG Basic block arena 17 | pub dag_basic_block_arena: Arena, 18 | 19 | /// DAG Basic blocks list 20 | pub dag_basic_blocks: Vec, 21 | 22 | /// DAG node arena 23 | pub node_arena: Arena, 24 | 25 | pub local_vars: LocalVariables, 26 | 27 | pub regs: RegistersInfo, 28 | 29 | pub is_internal: bool, 30 | 31 | pub types: Types, 32 | } 33 | 34 | impl DAGFunction { 35 | pub fn new( 36 | func: &Function, 37 | node_arena: Arena, 38 | dag_basic_block_arena: Arena, 39 | dag_basic_blocks: Vec, 40 | local_vars: LocalVariables, 41 | regs: RegistersInfo, 42 | ) -> Self { 43 | Self { 44 | is_internal: func.is_internal, 45 | name: func.name.clone(), 46 | ty: func.ty.clone(), 47 | dag_basic_block_arena, 48 | dag_basic_blocks, 49 | node_arena, 50 | local_vars, 51 | regs, 52 | types: func.types.clone(), 53 | } 54 | } 55 | 56 | pub fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result { 57 | writeln!( 58 | f, 59 | "DAGFunc(name: {}, ty: {}):", 60 | self.name, 61 | self.types.to_string(self.ty) 62 | )?; 63 | 64 | for bb_id in &self.dag_basic_blocks { 65 | let bb = &self.dag_basic_block_arena[*bb_id]; 66 | bb.debug(f, &self.node_arena, &self.types, bb_id.index())?; 67 | } 68 | 69 | fmt::Result::Ok(()) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/codegen/x64/machine/calc_spill_weight.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | analysis::{ 3 | dom_tree::DominatorTreeConstructor, 4 | loops::{Loops, LoopsConstructor}, 5 | }, 6 | codegen::common::machine::{ 7 | basic_block::MachineBasicBlock, function::MachineFunction, liveness::LiveRegMatrix, 8 | }, 9 | traits::function::FunctionTrait, 10 | }; 11 | use rustc_hash::FxHashSet; 12 | 13 | struct SpillWeightCalculator<'a> { 14 | func: &'a MachineFunction, 15 | matrix: &'a mut LiveRegMatrix, 16 | loops: Loops, 17 | } 18 | 19 | pub fn calc_spill_weight(func: &MachineFunction, matrix: &mut LiveRegMatrix) { 20 | let dom_tree = DominatorTreeConstructor::new(func.get_basic_blocks()).construct(); 21 | let loops = LoopsConstructor::new(&dom_tree, &func.body.basic_blocks).analyze(); 22 | SpillWeightCalculator { 23 | func, 24 | matrix, 25 | loops, 26 | } 27 | .run() 28 | } 29 | 30 | impl<'a> SpillWeightCalculator<'a> { 31 | // VERY simple spill weight calculation 32 | pub fn run(&mut self) { 33 | for (vreg, li) in self.matrix.virt_reg_interval.inner_mut() { 34 | let inst_arena = &self.func.body.inst_arena; 35 | let reg_id = *self.matrix.virt_regs.get(vreg).unwrap(); 36 | let uses = &self.func.regs_info.arena_ref()[reg_id].uses; 37 | let bbs_used_in = uses 38 | .iter() 39 | .map(|id| inst_arena[*id].parent) 40 | .collect::>() 41 | .len(); 42 | let used_in_loop = { 43 | let loops = &self.loops; 44 | uses.iter() 45 | .any(|id| loops.get_loop_for(inst_arena[*id].parent).is_some()) 46 | }; 47 | let uses = uses.len(); 48 | li.spill_weight = uses as f32 * bbs_used_in as f32; 49 | if used_in_loop { 50 | li.spill_weight *= 3.0; 51 | } 52 | // println!("{:?} - {:?}", vreg, li.spill_weight); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/codegen/aarch64/machine/calc_spill_weight.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | analysis::{ 3 | dom_tree::DominatorTreeConstructor, 4 | loops::{Loops, LoopsConstructor}, 5 | }, 6 | codegen::common::machine::{ 7 | basic_block::MachineBasicBlock, function::MachineFunction, liveness::LiveRegMatrix, 8 | }, 9 | traits::function::FunctionTrait, 10 | }; 11 | use rustc_hash::FxHashSet; 12 | 13 | struct SpillWeightCalculator<'a> { 14 | func: &'a MachineFunction, 15 | matrix: &'a mut LiveRegMatrix, 16 | loops: Loops, 17 | } 18 | 19 | pub fn calc_spill_weight(func: &MachineFunction, matrix: &mut LiveRegMatrix) { 20 | let dom_tree = DominatorTreeConstructor::new(func.get_basic_blocks()).construct(); 21 | let loops = LoopsConstructor::new(&dom_tree, &func.body.basic_blocks).analyze(); 22 | SpillWeightCalculator { 23 | func, 24 | matrix, 25 | loops, 26 | } 27 | .run() 28 | } 29 | 30 | impl<'a> SpillWeightCalculator<'a> { 31 | // VERY simple spill weight calculation 32 | pub fn run(&mut self) { 33 | for (vreg, li) in self.matrix.virt_reg_interval.inner_mut() { 34 | let inst_arena = &self.func.body.inst_arena; 35 | let reg_id = *self.matrix.virt_regs.get(vreg).unwrap(); 36 | let uses = &self.func.regs_info.arena_ref()[reg_id].uses; 37 | let bbs_used_in = uses 38 | .iter() 39 | .map(|id| inst_arena[*id].parent) 40 | .collect::>() 41 | .len(); 42 | let used_in_loop = { 43 | let loops = &self.loops; 44 | uses.iter() 45 | .any(|id| loops.get_loop_for(inst_arena[*id].parent).is_some()) 46 | }; 47 | let uses = uses.len(); 48 | li.spill_weight = uses as f32 * bbs_used_in as f32; 49 | if used_in_loop { 50 | li.spill_weight *= 3.0; 51 | } 52 | // println!("{:?} - {:?}", vreg, li.spill_weight); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/codegen/riscv64/machine/calc_spill_weight.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | analysis::{ 3 | dom_tree::DominatorTreeConstructor, 4 | loops::{Loops, LoopsConstructor}, 5 | }, 6 | codegen::common::machine::{ 7 | basic_block::MachineBasicBlock, function::MachineFunction, liveness::LiveRegMatrix, 8 | }, 9 | traits::function::FunctionTrait, 10 | }; 11 | use rustc_hash::FxHashSet; 12 | 13 | struct SpillWeightCalculator<'a> { 14 | func: &'a MachineFunction, 15 | matrix: &'a mut LiveRegMatrix, 16 | loops: Loops, 17 | } 18 | 19 | pub fn calc_spill_weight(func: &MachineFunction, matrix: &mut LiveRegMatrix) { 20 | let dom_tree = DominatorTreeConstructor::new(func.get_basic_blocks()).construct(); 21 | let loops = LoopsConstructor::new(&dom_tree, &func.body.basic_blocks).analyze(); 22 | SpillWeightCalculator { 23 | func, 24 | matrix, 25 | loops, 26 | } 27 | .run() 28 | } 29 | 30 | impl<'a> SpillWeightCalculator<'a> { 31 | // VERY simple spill weight calculation 32 | pub fn run(&mut self) { 33 | for (vreg, li) in self.matrix.virt_reg_interval.inner_mut() { 34 | let inst_arena = &self.func.body.inst_arena; 35 | let reg_id = *self.matrix.virt_regs.get(vreg).unwrap(); 36 | let uses = &self.func.regs_info.arena_ref()[reg_id].uses; 37 | let bbs_used_in = uses 38 | .iter() 39 | .map(|id| inst_arena[*id].parent) 40 | .collect::>() 41 | .len(); 42 | let used_in_loop = { 43 | let loops = &self.loops; 44 | uses.iter() 45 | .any(|id| loops.get_loop_for(inst_arena[*id].parent).is_some()) 46 | }; 47 | let uses = uses.len(); 48 | li.spill_weight = uses as f32 * bbs_used_in as f32; 49 | if used_in_loop { 50 | li.spill_weight *= 3.0; 51 | } 52 | // println!("{:?} - {:?}", vreg, li.spill_weight); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/codegen/aarch64/machine/regalloc.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::{ 2 | frame_object::FrameIndexInfo, 3 | machine::inst::{MachineMemOperand, MachineOpcode}, 4 | machine::register::*, 5 | }; 6 | pub use crate::codegen::common::machine::regalloc::*; 7 | use crate::codegen::common::machine::{ 8 | basic_block::MachineBasicBlockId, 9 | function::MachineFunction, 10 | inst::{MachineInst, MachineInstId, MachineOperand, RegisterOperand}, 11 | }; 12 | 13 | impl RegisterAllocator { 14 | pub fn store_and_load_for_reg_preservation( 15 | &mut self, 16 | f: &mut MachineFunction, 17 | reg: RegisterId, 18 | frinfo: FrameIndexInfo, 19 | parent: MachineBasicBlockId, 20 | ) -> (MachineInstId, MachineInstId) { 21 | let src = MachineOperand::Register(RegisterOperand::new(reg)); 22 | let x29 = RegisterOperand::new(f.regs_info.get_phys_reg(GR64::X29)); 23 | let store_inst_id = f.alloc_inst(MachineInst::new( 24 | &f.regs_info, 25 | MachineOpcode::STR, 26 | vec![ 27 | src, 28 | MachineOperand::Mem(MachineMemOperand::RegFi(x29, frinfo)), 29 | ], 30 | None, 31 | parent, 32 | )); 33 | 34 | let load_inst_id = f.alloc_inst( 35 | MachineInst::new_simple( 36 | MachineOpcode::LDR32, 37 | vec![MachineOperand::Mem(MachineMemOperand::RegFi(x29, frinfo))], 38 | parent, 39 | ) 40 | .with_def(vec![RegisterOperand::new(reg)]), 41 | ); 42 | 43 | (store_inst_id, load_inst_id) 44 | } 45 | 46 | pub fn get_regs_used_to_preserve( 47 | &mut self, 48 | func: &mut MachineFunction, 49 | store_id: MachineInstId, 50 | load_id: MachineInstId, 51 | ) -> Vec<(RegisterId, RegisterId)> { 52 | let store = &func.body.inst_arena[store_id]; 53 | let r1 = store.operand[0].as_register().id; 54 | 55 | let load = &func.body.inst_arena[load_id]; 56 | let r2 = load.def[0].id; 57 | 58 | vec![(r1, r2)] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/codegen/common/machine/eliminate_fi.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::common::machine::{frame_object::*, function::*, module::*}; 2 | use crate::traits::pass::ModulePassTrait; 3 | use rustc_hash::FxHashSet; 4 | 5 | pub struct EliminateFrameIndex {} 6 | 7 | pub struct EliminateFrameIndexOnFunction<'a> { 8 | func: &'a mut MachineFunction, 9 | } 10 | 11 | impl ModulePassTrait for EliminateFrameIndex { 12 | type M = MachineModule; 13 | 14 | fn name(&self) -> &'static str { 15 | "EliminateFrameIndex" 16 | } 17 | 18 | fn run_on_module(&mut self, module: &mut Self::M) { 19 | self.run_on_module(module) 20 | } 21 | } 22 | 23 | impl EliminateFrameIndex { 24 | pub fn new() -> Self { 25 | Self {} 26 | } 27 | 28 | pub fn run_on_module(&mut self, module: &mut MachineModule) { 29 | for (_, func) in &mut module.functions { 30 | EliminateFrameIndexOnFunction { func }.run(); 31 | } 32 | } 33 | } 34 | 35 | impl<'a> EliminateFrameIndexOnFunction<'a> { 36 | pub fn run(&mut self) { 37 | if self.func.is_internal { 38 | return; 39 | } 40 | 41 | let mut mem_insts = vec![]; 42 | let mut m = FxHashSet::default(); 43 | 44 | for (_bb_id, _bb, iseq) in self.func.body.mbb_iter() { 45 | for (id, inst) in iseq { 46 | if let Some(&FrameIndexInfo { 47 | idx: FrameIndexKind::Local(i), 48 | .. 49 | }) = Self::get_frame_index(inst) 50 | { 51 | m.insert(i); 52 | } 53 | mem_insts.push(id); 54 | } 55 | } 56 | 57 | let frame_objects = FrameObjectsInfo::new(&self.func.types, self.func); 58 | 59 | for id in mem_insts { 60 | let inst = &mut self.func.body.inst_arena[id]; 61 | Self::replace_frame_index(&frame_objects, inst); 62 | } 63 | 64 | self.func.local_mgr.locals.retain(|local| match local.idx { 65 | FrameIndexKind::Arg(_) => true, 66 | FrameIndexKind::Local(i) if m.contains(&i) => true, 67 | FrameIndexKind::Local(_) => false, 68 | }); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/codegen/common/machine/phi_elimination.rs: -------------------------------------------------------------------------------- 1 | use super::inst::*; 2 | use crate::codegen::common::machine::{builder::*, function::*, module::*}; 3 | use crate::traits::pass::ModulePassTrait; 4 | 5 | pub struct PhiElimination {} 6 | 7 | impl ModulePassTrait for PhiElimination { 8 | type M = MachineModule; 9 | 10 | fn name(&self) -> &'static str { 11 | "PhiElimination" 12 | } 13 | 14 | fn run_on_module(&mut self, module: &mut Self::M) { 15 | self.run_on_module(module) 16 | } 17 | } 18 | 19 | impl PhiElimination { 20 | pub fn new() -> Self { 21 | Self {} 22 | } 23 | 24 | pub fn run_on_module(&mut self, module: &mut MachineModule) { 25 | for (_, f) in &mut module.functions { 26 | self.run_on_function(f); 27 | } 28 | } 29 | 30 | pub fn run_on_function(&mut self, f: &mut MachineFunction) { 31 | if f.is_internal { 32 | return; 33 | } 34 | 35 | let phi_list: Vec<_> = f 36 | .body 37 | .basic_blocks 38 | .id_and_block() 39 | .map(|(_, bb)| { 40 | bb.iseq_ref() 41 | .iter() 42 | .filter(|&&id| f.body.inst_arena[id].opcode == MachineOpcode::Phi) 43 | .map(|&id| (id, f.body.inst_arena[id].clone())) 44 | .collect::>() 45 | }) 46 | .flatten() 47 | .collect(); 48 | for (phi_id, phi) in phi_list { 49 | for i in (0..phi.operand.len()).step_by(2) { 50 | let val = &phi.operand[i + 0]; 51 | let incoming_bb_id = phi.operand[i + 1].as_basic_block(); 52 | 53 | let copy = 54 | MachineInst::new_simple(MachineOpcode::Copy, vec![val.clone()], incoming_bb_id) 55 | .with_def(phi.def.clone()); 56 | 57 | let mut builder = Builder::new(f); 58 | builder.set_insert_point_at_end(incoming_bb_id); 59 | builder.back_insert_point_while(|i| i.opcode.is_terminator()); 60 | builder.insert(copy); 61 | } 62 | f.remove_inst(phi_id); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/codegen/x64/machine/regalloc.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::{ 2 | dag::mc_convert::{mov_mx, mov_rx}, 3 | frame_object::FrameIndexInfo, 4 | machine::inst::MachineMemOperand, 5 | machine::register::*, 6 | }; 7 | pub use crate::codegen::common::machine::regalloc::*; 8 | use crate::codegen::common::machine::{ 9 | basic_block::MachineBasicBlockId, 10 | function::MachineFunction, 11 | inst::{MachineInst, MachineInstId, MachineOperand, RegisterOperand}, 12 | }; 13 | 14 | impl RegisterAllocator { 15 | pub fn store_and_load_for_reg_preservation( 16 | &mut self, 17 | f: &mut MachineFunction, 18 | reg: RegisterId, 19 | frinfo: FrameIndexInfo, 20 | parent: MachineBasicBlockId, 21 | ) -> (MachineInstId, MachineInstId) { 22 | let dst = MachineOperand::FrameIndex(frinfo.clone()); 23 | let src = MachineOperand::Register(RegisterOperand::new(reg)); 24 | let rbp = RegisterOperand::new(f.regs_info.get_phys_reg(GR64::RBP)); 25 | let store_inst_id = f.alloc_inst(MachineInst::new( 26 | &f.regs_info, 27 | mov_mx(&f.regs_info, &src).unwrap(), 28 | vec![ 29 | MachineOperand::Mem(MachineMemOperand::BaseFi(rbp, *dst.as_frame_index())), 30 | src, 31 | ], 32 | None, 33 | parent, 34 | )); 35 | 36 | let src = MachineOperand::Mem(MachineMemOperand::BaseFi(rbp, frinfo)); 37 | let opcode = mov_rx(f.regs_info.arena_ref()[reg].reg_class, &src).unwrap(); 38 | let load_inst_id = f.alloc_inst( 39 | MachineInst::new_simple(opcode, vec![src], parent) 40 | .with_def(vec![RegisterOperand::new(reg)]), 41 | ); 42 | 43 | (store_inst_id, load_inst_id) 44 | } 45 | 46 | pub fn get_regs_used_to_preserve( 47 | &mut self, 48 | func: &mut MachineFunction, 49 | store_id: MachineInstId, 50 | load_id: MachineInstId, 51 | ) -> Vec<(RegisterId, RegisterId)> { 52 | let store = &func.body.inst_arena[store_id]; 53 | let r1 = store.operand[1].as_register().id; 54 | 55 | let load = &func.body.inst_arena[load_id]; 56 | let r2 = load.def[0].id; 57 | 58 | vec![(r1, r2)] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/ir/alias_analysis.rs: -------------------------------------------------------------------------------- 1 | use crate::ir::{ 2 | function::Function, 3 | module::Module, 4 | opcode::Opcode, 5 | value::{InstructionValue, Value}, 6 | }; 7 | use rustc_hash::{FxHashMap, FxHashSet}; 8 | 9 | pub struct AliasAnalysis {} 10 | 11 | struct AliasAnalysisOnFunction<'a> { 12 | func: &'a Function, 13 | } 14 | 15 | impl AliasAnalysis { 16 | pub fn new() -> Self { 17 | Self {} 18 | } 19 | 20 | pub fn run_on_module(&mut self, module: &mut Module) { 21 | for (_, func) in &mut module.functions { 22 | AliasAnalysisOnFunction { func }.run() 23 | } 24 | } 25 | } 26 | 27 | impl<'a> AliasAnalysisOnFunction<'a> { 28 | pub fn run(&mut self) { 29 | let mut alloca = FxHashSet::default(); 30 | let mut points_to = FxHashMap::default(); 31 | 32 | for &block_id in &self.func.basic_blocks.order { 33 | let block = &self.func.basic_blocks.arena[block_id]; 34 | for &inst_id in &*block.iseq_ref() { 35 | let inst = &self.func.inst_table[inst_id]; 36 | 37 | match inst.opcode { 38 | Opcode::Alloca => { 39 | alloca.insert(inst_id); 40 | } 41 | Opcode::Load => { 42 | let mem = inst.operand.args()[0]; 43 | match mem { 44 | Value::Instruction(InstructionValue { id, .. }) 45 | if alloca.contains(&id) => 46 | { 47 | points_to.insert(inst_id, id); 48 | } 49 | Value::Instruction(InstructionValue { id, .. }) 50 | if points_to.contains_key(&id) => 51 | { 52 | points_to.insert(inst_id, id); 53 | } 54 | _ => { 55 | todo!("???") 56 | } 57 | } 58 | } 59 | _ => { 60 | panic!() 61 | } // Opcode 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ir/global_val.rs: -------------------------------------------------------------------------------- 1 | use super::types::{Type, Types}; 2 | use id_arena::{Arena, Id}; 3 | use std::fmt; 4 | 5 | pub type GlobalVariableId = Id; 6 | 7 | #[derive(Clone)] 8 | pub struct GlobalVariables { 9 | pub arena: Arena, 10 | types: Types, 11 | } 12 | 13 | #[derive(Debug, Clone)] 14 | pub struct GlobalVariable { 15 | pub ty: Type, 16 | pub linkage: Linkage, 17 | pub name: String, 18 | } 19 | 20 | #[derive(Clone, Copy, Eq, PartialEq, Hash)] 21 | pub enum Linkage { 22 | Common, 23 | External, 24 | // TODO ... 25 | } 26 | 27 | impl GlobalVariables { 28 | pub fn new(types: Types) -> Self { 29 | Self { 30 | arena: Arena::new(), 31 | types, 32 | } 33 | } 34 | 35 | pub fn new_global_var_with_name( 36 | &mut self, 37 | ty: Type, 38 | linkage: Linkage, 39 | name: &str, 40 | ) -> GlobalVariableId { 41 | let ptr_ty = self.types.new_pointer_ty(ty); 42 | let id = self.arena.alloc(GlobalVariable { 43 | ty, 44 | linkage, 45 | name: name.to_string(), 46 | }); 47 | self.types 48 | .base 49 | .borrow_mut() 50 | .gblvar_ptr_types 51 | .insert(id, ptr_ty); 52 | id 53 | } 54 | 55 | pub fn new_global_var(&mut self, ty: Type, linkage: Linkage) -> GlobalVariableId { 56 | self.arena.alloc(GlobalVariable { 57 | ty, 58 | linkage, 59 | name: "anony".to_string(), 60 | }) 61 | } 62 | } 63 | 64 | impl fmt::Debug for GlobalVariables { 65 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 66 | for (_, g) in &self.arena { 67 | writeln!( 68 | f, 69 | "@{} = {:?} global {}", 70 | g.name, 71 | g.linkage, 72 | self.types.to_string(g.ty) 73 | )?; 74 | } 75 | fmt::Result::Ok(()) 76 | } 77 | } 78 | 79 | impl fmt::Debug for Linkage { 80 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 81 | match self { 82 | Self::Common => write!(f, "common"), 83 | Self::External => write!(f, "external"), 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/codegen/aarch64/machine/eliminate_fi.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::machine::inst::MachineMemOperand; 2 | use crate::codegen::common::machine::eliminate_fi::EliminateFrameIndexOnFunction; 3 | use crate::codegen::common::machine::frame_object::{FrameIndexInfo, FrameObjectsInfo}; 4 | use crate::codegen::common::machine::inst::{MachineInst, MachineOperand}; 5 | 6 | impl<'a> EliminateFrameIndexOnFunction<'a> { 7 | pub fn get_frame_index(inst: &MachineInst) -> Option<&FrameIndexInfo> { 8 | // for op in &inst.operand { 9 | // match op { 10 | // MachineOperand::Mem(MachineMemOperand::BaseFi(_, fi)) 11 | // | MachineOperand::Mem(MachineMemOperand::BaseFiOff(_, fi, _)) 12 | // | MachineOperand::Mem(MachineMemOperand::BaseFiAlignOff(_, fi, _, _)) => { 13 | // return Some(fi) 14 | // } 15 | // _ => {} 16 | // } 17 | // } 18 | None 19 | } 20 | 21 | pub fn replace_frame_index(frame_objects: &FrameObjectsInfo, inst: &mut MachineInst) { 22 | // for op in &mut inst.operand { 23 | // match op { 24 | // MachineOperand::Mem(MachineMemOperand::BaseFi(base, fi)) => { 25 | // *op = MachineOperand::Mem(MachineMemOperand::BaseOff( 26 | // *base, 27 | // frame_objects.offset(fi.idx).unwrap(), 28 | // )) 29 | // } 30 | // MachineOperand::Mem(MachineMemOperand::BaseFiOff(base, fi, off)) => { 31 | // *op = MachineOperand::Mem(MachineMemOperand::BaseOff( 32 | // *base, 33 | // frame_objects.offset(fi.idx).unwrap() + *off, 34 | // )) 35 | // } 36 | // MachineOperand::Mem(MachineMemOperand::BaseFiAlignOff(base, fi, align, off)) => { 37 | // *op = MachineOperand::Mem(MachineMemOperand::BaseOffAlignOff( 38 | // *base, 39 | // frame_objects.offset(fi.idx).unwrap(), 40 | // *align, 41 | // *off, 42 | // )); 43 | // } 44 | // _ => {} 45 | // } 46 | // } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/codegen/riscv64/frame_object.rs: -------------------------------------------------------------------------------- 1 | use super::exec::roundup; 2 | pub use crate::codegen::common::machine::frame_object::*; 3 | use crate::codegen::common::machine::function::MachineFunction; 4 | use crate::ir::types::*; 5 | use rustc_hash::FxHashMap; 6 | 7 | impl FrameObjectsInfo { 8 | pub fn new(tys: &Types, f: &MachineFunction) -> Self { 9 | let mut offset_map = FxHashMap::default(); 10 | let callee_saved_regs_byte: usize = f 11 | .body 12 | .appeared_phys_regs() 13 | .containing_callee_saved_regs() 14 | .to_phys_set() 15 | .len() 16 | * 8 17 | + f.body.has_call() as usize * 8/*=ra*/; 18 | let mut total_size = 0i32; 19 | 20 | let padding = |off, align| -> i32 { (align - off % align) % align }; 21 | 22 | // TODO: Implement 23 | // for (i, param_ty) in tys 24 | // .base 25 | // .borrow() 26 | // .as_function_ty(f.ty) 27 | // .unwrap() 28 | // .params_ty 29 | // .iter() 30 | // .enumerate() 31 | // { 32 | // let rc = ty2rc(param_ty).unwrap(); 33 | // if rc.get_nth_arg_reg(i).is_none() { 34 | // offset += param_ty.size_in_byte(tys); 35 | // offset_map.insert(FrameIndexKind::Arg(i), offset); 36 | // } 37 | // } 38 | 39 | for FrameIndexInfo { idx, ty } in &f.local_mgr.locals { 40 | let size = ty.size_in_byte(tys) as i32; 41 | let align = ty.align_in_byte(tys) as i32; 42 | total_size += size + padding(total_size, align); 43 | offset_map.insert(*idx, -total_size); 44 | } 45 | 46 | let stack_down = Self::calc_max_adjust_stack_down(f) as i32; 47 | total_size = roundup(total_size + stack_down, ALIGN); 48 | 49 | Self { 50 | offset_map, 51 | total_size, 52 | callee_saved_regs_byte, 53 | } 54 | } 55 | 56 | pub fn offset(&self, kind: FrameIndexKind) -> Option { 57 | self.offset_map 58 | .get(&kind) 59 | .map(|x| *x as i32 - roundup(self.callee_saved_regs_byte as i32, ALIGN)) 60 | } 61 | 62 | pub fn total_size(&self) -> i32 { 63 | self.total_size as i32 + roundup(self.callee_saved_regs_byte as i32, ALIGN) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/codegen/x64/machine/abi.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | codegen::{arch::machine::register::*, common::machine::calling_conv::CallingConv}, 3 | ir::types::{StructType, Type}, 4 | }; 5 | 6 | #[derive(Clone)] 7 | pub struct SystemV { 8 | gr8: Vec, 9 | gr32: Vec, 10 | gr64: Vec, 11 | xmm: Vec, 12 | } 13 | 14 | #[rustfmt::skip] 15 | impl SystemV { 16 | pub fn new() -> Self { 17 | Self { 18 | gr8: to_phys![GR8::DIL, GR8::SIL, GR8::DL, GR8::CL, GR8::R8B, GR8::R9B ], 19 | gr32: to_phys![GR32::EDI, GR32::ESI, GR32::EDX, GR32::ECX, GR32::R8D, GR32::R9D], 20 | gr64: to_phys![GR64::RDI, GR64::RSI, GR64::RDX, GR64::RCX, GR64::R8, GR64::R9 ], 21 | xmm: to_phys![XMM::XMM0, XMM::XMM1, XMM::XMM2, XMM::XMM3, XMM::XMM4, XMM::XMM5, XMM::XMM6, XMM::XMM7], 22 | } 23 | } 24 | } 25 | 26 | impl CallingConv for SystemV { 27 | fn get_nth_arg_reg(&self, rc: RegisterClassKind, nth: usize) -> Option { 28 | match rc { 29 | RegisterClassKind::GR8 => self.gr8.get(nth), 30 | RegisterClassKind::GR32 => self.gr32.get(nth), 31 | RegisterClassKind::GR64 => self.gr64.get(nth), 32 | RegisterClassKind::XMM => self.xmm.get(nth), 33 | } 34 | .map_or(None, |r| Some(*r)) 35 | } 36 | 37 | fn reg_classes_used_for_passing_byval(struct_ty: &StructType) -> Vec { 38 | let sz = struct_ty.size(); 39 | let moves_by_8_bytes = sz / 8; 40 | let moves_by_4_bytes = (sz - 8 * moves_by_8_bytes) / 4; 41 | assert!((sz - 8 * moves_by_8_bytes) % 4 == 0); 42 | 43 | let mut regs = vec![]; 44 | 45 | if sz <= 16 { 46 | let mut off = 0; 47 | for &(count, size, rc) in &[ 48 | (moves_by_8_bytes, 8, RegisterClassKind::GR64), 49 | (moves_by_4_bytes, 4, RegisterClassKind::GR32), 50 | ] { 51 | for _ in 0..count { 52 | let float = struct_ty.get_type_at(off) == Some(&Type::f64); 53 | regs.push(if float { RegisterClassKind::XMM } else { rc }); 54 | off += size; 55 | } 56 | } 57 | return regs; 58 | } 59 | 60 | // the size of struct_ty is over 16, so put it onto stack 61 | vec![] 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/ir/dce.rs: -------------------------------------------------------------------------------- 1 | use crate::ir::{ 2 | function::Function, 3 | module::Module, 4 | opcode::{Instruction, InstructionId, Opcode}, 5 | value::{InstructionValue, Value}, 6 | }; 7 | 8 | pub struct DeadCodeElimination {} 9 | 10 | struct DeadCodeEliminationOnFunction<'a> { 11 | func: &'a mut Function, 12 | } 13 | 14 | impl DeadCodeElimination { 15 | pub fn new() -> Self { 16 | Self {} 17 | } 18 | 19 | pub fn run_on_module(&mut self, module: &mut Module) { 20 | for (_, func) in &mut module.functions { 21 | if func.is_internal || func.is_empty() { 22 | continue; 23 | } 24 | 25 | DeadCodeEliminationOnFunction { func }.run() 26 | } 27 | } 28 | } 29 | 30 | impl<'a> DeadCodeEliminationOnFunction<'a> { 31 | pub fn run(self) { 32 | let mut elimination_list = vec![]; 33 | let mut worklist = vec![]; 34 | 35 | for &block_id in &self.func.basic_blocks.order { 36 | let block = &self.func.basic_blocks.arena[block_id]; 37 | for &inst_id in &*block.iseq_ref() { 38 | let inst = &self.func.inst_table[inst_id]; 39 | Self::check_if_elimination_possible(inst, &mut elimination_list, &mut worklist); 40 | } 41 | } 42 | 43 | while let Some(inst_id) = worklist.pop() { 44 | let inst = &self.func.inst_table[inst_id]; 45 | Self::check_if_elimination_possible(inst, &mut elimination_list, &mut worklist); 46 | } 47 | 48 | for inst_id in elimination_list { 49 | self.func.remove_inst(inst_id) 50 | } 51 | } 52 | 53 | fn check_if_elimination_possible( 54 | inst: &Instruction, 55 | elimination_list: &mut Vec, 56 | worklist: &mut Vec, 57 | ) { 58 | let dont_eliminate = 59 | matches!(inst.opcode, Opcode::Store | Opcode::Call) || inst.opcode.is_terminator(); 60 | if dont_eliminate { 61 | return; 62 | } 63 | // no users then eliminate 64 | if inst.users.borrow().len() == 0 { 65 | elimination_list.push(inst.id.unwrap()); 66 | for op in inst.operand.args() { 67 | if let Value::Instruction(InstructionValue { id, .. }) = op { 68 | worklist.push(*id) 69 | } 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/util/count.rs: -------------------------------------------------------------------------------- 1 | use rustc_hash::FxHashMap; 2 | use std::collections::VecDeque; 3 | use std::hash::Hash; 4 | 5 | #[derive(Clone, Debug)] 6 | pub struct Count(FxHashMap); 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct CountIter<'a, T: Eq + Hash> { 10 | values: Vec<&'a T>, 11 | nth: usize, 12 | } 13 | 14 | #[derive(Debug, Clone)] 15 | pub struct CountIntoIter { 16 | values: VecDeque, 17 | } 18 | 19 | impl Count { 20 | pub fn new() -> Self { 21 | Self(FxHashMap::default()) 22 | } 23 | 24 | pub fn add(&mut self, t: T) { 25 | *self.0.entry(t).or_insert(0) += 1; 26 | } 27 | 28 | pub fn remove(&mut self, t: &T) { 29 | if !self.0.contains_key(&t) { 30 | return; 31 | } 32 | 33 | let c = self.0.get_mut(t).unwrap(); 34 | *c = c.saturating_sub(1); 35 | 36 | if *c == 0 { 37 | self.0.remove(t); 38 | } 39 | } 40 | 41 | pub fn len(&self) -> usize { 42 | self.0.len() 43 | } 44 | 45 | pub fn clear(&mut self) { 46 | self.0.clear(); 47 | } 48 | 49 | pub fn iter(&self) -> CountIter { 50 | CountIter { 51 | values: self.0.iter().map(|(k, _)| k).collect(), 52 | nth: 0, 53 | } 54 | } 55 | 56 | pub fn into_iter2(self) -> CountIntoIter { 57 | CountIntoIter { 58 | values: self.0.into_iter().map(|(k, _)| k).collect(), 59 | } 60 | } 61 | } 62 | 63 | impl<'a, T: Eq + Hash> Iterator for CountIter<'a, T> { 64 | type Item = &'a T; 65 | 66 | fn next(&mut self) -> Option { 67 | let v = *self.values.get(self.nth)?; 68 | self.nth += 1; 69 | Some(v) 70 | } 71 | } 72 | 73 | impl Iterator for CountIntoIter { 74 | type Item = T; 75 | 76 | fn next(&mut self) -> Option { 77 | self.values.pop_front() 78 | } 79 | } 80 | 81 | impl IntoIterator for Count { 82 | type Item = T; 83 | type IntoIter = CountIntoIter; 84 | 85 | fn into_iter(self) -> Self::IntoIter { 86 | self.into_iter2() 87 | } 88 | } 89 | 90 | impl<'a, T: Eq + Hash> IntoIterator for &'a Count { 91 | type Item = &'a T; 92 | type IntoIter = CountIter<'a, T>; 93 | 94 | fn into_iter(self) -> Self::IntoIter { 95 | self.iter() 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/ir/codegen_prepare.rs: -------------------------------------------------------------------------------- 1 | use crate::ir::{builder::*, function::*, module::*, opcode::*}; 2 | 3 | pub struct CodegenPrepare {} 4 | 5 | pub struct CodegenPrepareOnFunction<'a> { 6 | func: &'a mut Function, 7 | } 8 | 9 | impl CodegenPrepare { 10 | pub fn new() -> Self { 11 | Self {} 12 | } 13 | 14 | pub fn run_on_module(&mut self, module: &mut Module) { 15 | for (_, func) in &mut module.functions { 16 | if func.is_internal { 17 | continue; 18 | } 19 | 20 | CodegenPrepareOnFunction { func }.run() 21 | } 22 | } 23 | } 24 | 25 | impl<'a> CodegenPrepareOnFunction<'a> { 26 | pub fn run(&mut self) { 27 | let mut geps_to_be_sunk = vec![]; 28 | 29 | for (_, block) in &self.func.basic_blocks.arena { 30 | for &inst_id in &*block.iseq.borrow() { 31 | let inst = &self.func.inst_table[inst_id]; 32 | if inst.opcode == Opcode::GetElementPtr && self.able_to_be_sunk(inst) { 33 | geps_to_be_sunk.push(inst_id); 34 | } 35 | } 36 | } 37 | 38 | debug!(println!( 39 | "CodegenPrepare: {} GEPs to be sunk", 40 | geps_to_be_sunk.len() 41 | )); 42 | 43 | for gep_id in geps_to_be_sunk { 44 | let gep_val = self.func.remove_inst_from_block(gep_id); 45 | let gep = &self.func.inst_table[gep_id]; 46 | let user_id = *gep 47 | .users 48 | .borrow() 49 | .iter() 50 | .min_by(|&&x, &&y| { 51 | self.func 52 | .find_inst_pos(x) 53 | .unwrap() 54 | .1 55 | .cmp(&self.func.find_inst_pos(y).unwrap().1) 56 | }) 57 | .unwrap(); 58 | let user_parent = self.func.inst_table[user_id].parent; 59 | let gep = &mut self.func.inst_table[gep_id]; 60 | gep.parent = user_parent; 61 | let mut builder = self.func.ir_builder(); 62 | builder.set_insert_point_before_inst(user_id); 63 | builder.insert(gep_val); 64 | } 65 | } 66 | 67 | fn able_to_be_sunk(&self, inst: &Instruction) -> bool { 68 | inst.has_one_use() || { 69 | inst.users 70 | .borrow() 71 | .windows(2) 72 | .all(|us| self.func.inst_table[us[0]].parent == self.func.inst_table[us[1]].parent) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/codegen/aarch64/machine/register.rs: -------------------------------------------------------------------------------- 1 | pub use crate::codegen::common::machine::register::*; 2 | use crate::ir::types::Type; 3 | use defs::registers; 4 | use id_arena::Arena; 5 | use rustc_hash::FxHashMap; 6 | use std::cell::RefCell; 7 | 8 | registers! { 9 | class SP (64, i64, [], [SP]) { 10 | SP 11 | } 12 | 13 | class WSP (32, i32, [], [WSP]) < SP { 14 | WSP 15 | } 16 | 17 | class GR64 (64, i64, [i64, Pointer!], [X0]) { 18 | X0, X1, X2, X3, X4, 19 | X5, X6, X7, X8, X9, 20 | X10, X11, X12, X13, X14, 21 | X15, X16, X17, X18, X19, 22 | X20, X21, X22, X23, X24, 23 | X25, X26, X27, X28, X29, X30 24 | } 25 | 26 | class GR32 (32, i32, [i32], [W0]) < GR64 { 27 | W0, W1, W2, W3, W4, 28 | W5, W6, W7, W8, W9, 29 | W10, W11, W12, W13, W14, 30 | W15, W16, W17, W18, W19, 31 | W20, W21, W22, W23, W24, 32 | W25, W26, W27, W28, W29, W30 33 | } 34 | 35 | order arg GR32 { W0, W1, W2, W3, W4, W5, W6, W7 } 36 | order arg GR64 { X0, X1, X2, X3, X4, X5, X6, X7 } 37 | order arg SP { SP } order arg WSP { WSP } 38 | 39 | order gp GR32 { 40 | W0, W1, W2, W3, W4, W5, W6, W7, 41 | W9, W10, W11, W12, W13, W14, W15, 42 | W19, W20, W21, W22, W23, W24, W25, W26, W27, W28 43 | } 44 | order gp GR64 { 45 | X0, X1, X2, X3, X4, X5, X6, X7, 46 | X9, X10, X11, X12, X13, X14, X15, 47 | X19, X20, X21, X22, X23, X24, X25, X26, X27, X28 48 | } 49 | order gp SP { SP } order gp WSP { WSP } 50 | } 51 | 52 | thread_local! { 53 | pub static CALLEE_SAVED_REGS: PhysRegSet = { 54 | let mut bits = PhysRegSet::new(); 55 | let regs = to_phys![ 56 | GR32::W19, 57 | GR32::W20, 58 | GR32::W21, 59 | GR32::W22, 60 | GR32::W23, 61 | GR32::W24, 62 | GR32::W25, 63 | GR32::W26, 64 | GR32::W27, 65 | GR32::W28, 66 | GR32::W29, 67 | GR32::W30, 68 | GR64::X19, 69 | GR64::X20, 70 | GR64::X21, 71 | GR64::X22, 72 | GR64::X23, 73 | GR64::X24, 74 | GR64::X25, 75 | GR64::X26, 76 | GR64::X27, 77 | GR64::X28, 78 | GR64::X29, 79 | GR64::X30 80 | ]; 81 | for reg in regs { 82 | bits.set(reg) 83 | } 84 | bits 85 | }; 86 | 87 | pub static REG_FILE: RefCell> = { 88 | RefCell::new(FxHashMap::default()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/ir/constant_pool.rs: -------------------------------------------------------------------------------- 1 | use super::types::{Type, Types}; 2 | use super::value::ImmediateValue; 3 | use id_arena::{Arena, Id}; 4 | use std::fmt; 5 | 6 | pub type ConstantId = Id; 7 | 8 | #[derive(Clone)] 9 | pub struct ConstantPool { 10 | pub arena: Arena, 11 | types: Types, 12 | } 13 | 14 | #[derive(Debug, Clone)] 15 | pub struct Constant { 16 | pub ty: Type, 17 | pub kind: ConstantKind, 18 | } 19 | 20 | #[derive(Clone)] 21 | pub enum ConstantKind { 22 | String(String), 23 | Array(Vec), 24 | } 25 | 26 | #[derive(Clone)] 27 | pub enum ConstantArrayElement { 28 | String(ConstantId), 29 | Immediate(ImmediateValue), 30 | Array(Vec), 31 | } 32 | 33 | impl ConstantPool { 34 | pub fn new(types: Types) -> Self { 35 | Self { 36 | arena: Arena::new(), 37 | types, 38 | } 39 | } 40 | 41 | pub fn add(&mut self, c: Constant) -> ConstantId { 42 | let ptr_ty = self.types.new_pointer_ty(c.ty); 43 | let id = self.arena.alloc(c); 44 | self.types 45 | .base 46 | .borrow_mut() 47 | .const_ptr_types 48 | .insert(id, ptr_ty); 49 | id 50 | } 51 | } 52 | 53 | impl fmt::Debug for ConstantPool { 54 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 55 | for (id, c) in &self.arena { 56 | writeln!( 57 | f, 58 | "@const.{} = constant {} {:?}", 59 | id.index(), 60 | self.types.to_string(c.ty), 61 | c.kind 62 | )?; 63 | } 64 | fmt::Result::Ok(()) 65 | } 66 | } 67 | 68 | impl fmt::Debug for ConstantKind { 69 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 70 | match self { 71 | Self::String(s) => write!(f, "\"{}\"", s), 72 | Self::Array(es) => { 73 | write!(f, "{{")?; 74 | for e in es { 75 | write!(f, "{:?},", e)? 76 | } 77 | write!(f, "}}") 78 | } 79 | } 80 | } 81 | } 82 | 83 | impl fmt::Debug for ConstantArrayElement { 84 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 85 | match self { 86 | Self::String(id) => write!(f, "@const.{}", id.index()), 87 | Self::Array(es) => { 88 | write!(f, "{{")?; 89 | for e in es { 90 | write!(f, "{:?},", e)? 91 | } 92 | write!(f, "}}") 93 | } 94 | Self::Immediate(i) => write!(f, "{:?}", i), 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/codegen/aarch64/frame_object.rs: -------------------------------------------------------------------------------- 1 | use super::exec::roundup; 2 | pub use crate::codegen::common::machine::frame_object::*; 3 | use crate::codegen::common::machine::function::MachineFunction; 4 | use crate::ir::types::*; 5 | use rustc_hash::FxHashMap; 6 | 7 | impl FrameObjectsInfo { 8 | pub fn new(tys: &Types, f: &MachineFunction) -> Self { 9 | let mut offset_map = FxHashMap::default(); 10 | let callee_saved_regs_byte: usize = ::std::cmp::max( 11 | f.body 12 | .appeared_phys_regs() 13 | .containing_callee_saved_regs() 14 | .to_phys_set() 15 | .len() 16 | * 8, 17 | 16, 18 | ); 19 | // + f.body.has_call() as usize * 8/*=ra*/; 20 | let mut total_size = 0i32; 21 | 22 | let padding = |off, align| -> i32 { (align - off % align) % align }; 23 | 24 | // TODO: Implement 25 | // for (i, param_ty) in tys 26 | // .base 27 | // .borrow() 28 | // .as_function_ty(f.ty) 29 | // .unwrap() 30 | // .params_ty 31 | // .iter() 32 | // .enumerate() 33 | // { 34 | // let rc = ty2rc(param_ty).unwrap(); 35 | // if rc.get_nth_arg_reg(i).is_none() { 36 | // offset += param_ty.size_in_byte(tys); 37 | // offset_map.insert(FrameIndexKind::Arg(i), offset); 38 | // } 39 | // } 40 | 41 | for FrameIndexInfo { ty, .. } in &f.local_mgr.locals { 42 | let size = ty.size_in_byte(tys) as i32; 43 | let align = ty.align_in_byte(tys) as i32; 44 | total_size += size + padding(total_size, align); 45 | } 46 | 47 | total_size = roundup(total_size, ALIGN); 48 | let mut sz = 0; 49 | for FrameIndexInfo { idx, ty } in &f.local_mgr.locals { 50 | let size = ty.size_in_byte(tys) as i32; 51 | let align = ty.align_in_byte(tys) as i32; 52 | sz += size + padding(sz, align); 53 | offset_map.insert(*idx, total_size - sz); 54 | } 55 | 56 | let stack_down = Self::calc_max_adjust_stack_down(f) as i32; 57 | total_size = roundup(total_size + stack_down, ALIGN); 58 | 59 | Self { 60 | offset_map, 61 | total_size, 62 | callee_saved_regs_byte, 63 | } 64 | } 65 | 66 | pub fn offset(&self, kind: FrameIndexKind) -> Option { 67 | self.offset_map 68 | .get(&kind) 69 | .map(|x| *x as i32 + roundup(self.callee_saved_regs_byte as i32, ALIGN)) 70 | } 71 | 72 | pub fn total_size(&self) -> i32 { 73 | self.total_size as i32 + roundup(self.callee_saved_regs_byte as i32, ALIGN) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | build: 5 | docker: 6 | - image: ubuntu 7 | 8 | working_directory: /opt/sericum 9 | 10 | steps: 11 | - checkout 12 | 13 | - setup_remote_docker 14 | 15 | - run: 16 | command: apt-get update 17 | 18 | - run: 19 | name: Install Docker client 20 | command: | 21 | apt-get update 22 | apt-get install -y curl 23 | set -x 24 | VER="17.03.0-ce" 25 | curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz 26 | tar -xz -C /tmp -f /tmp/docker-$VER.tgz 27 | mv /tmp/docker/* /usr/bin 28 | 29 | - run: | 30 | docker build -t sericum -f Dockerfile_cov . 31 | docker run --security-opt seccomp=unconfined sericum bash run_kcov.sh $CODECOV_TOKEN 32 | 33 | # - run: 34 | # command: | 35 | # export DEBIAN_FRONTEND=noninteractive 36 | # ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime 37 | # apt-get install -y zlib1g-dev wget gcc libffi-dev g++ python curl 38 | # apt-get install -y libdw-dev cmake make git binutils-dev libiberty-dev libcurl4-openssl-dev libelf-dev libdw-dev 39 | # 40 | # - run: 41 | # command: | 42 | # wget "https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init" 43 | # chmod +x rustup-init 44 | # ./rustup-init -y --no-modify-path --default-toolchain nightly 45 | # RUSTUP_HOME=~/.cargo/bin/rustup 46 | # CARGO_HOME=~/.cargo/bin/cargo 47 | # chmod -R a+w $RUSTUP_HOME $CARGO_HOME; 48 | # rm rustup-init 49 | # source ~/.cargo/env 50 | # 51 | # - run: 52 | # name: Setting up kcov 53 | # command: | 54 | # git clone https://github.com/SimonKagstrom/kcov 55 | # cd kcov 56 | # git checkout 9db5fa58986c2eae39e82580f15ba6fadb2dc906 57 | # cmake . 58 | # make -j 59 | # make install 60 | # 61 | # - run: 62 | # name: Test and Coverage 63 | # command: | 64 | # export PATH=~/.cargo/bin:$PATH 65 | # RUSTFLAGS='-C link-dead-code -C link-args=-lffi' cargo test 66 | # 67 | # REPORT=$(find ./target/debug -maxdepth 2 -regex '.+/deps/sericum-.*' -a ! -name '*.d') 68 | # for file in $REPORT; do 69 | # echo $file 70 | # /usr/local/bin/kcov --include-pattern=sericum/src --exclude-pattern=/.cargo ./target/cov "$file" 71 | # done 72 | # bash <(curl -s https://codecov.io/bash) -s ./target/cov 73 | -------------------------------------------------------------------------------- /src/codegen/aarch64/dag/legalize.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::machine::register::{RegisterClassKind as RC, GR64}; 2 | use crate::codegen::arch::{dag::node::MemKind, machine::inst::MachineOpcode as MO}; 3 | use crate::codegen::common::{ 4 | dag::{ 5 | function::DAGFunction, 6 | module::DAGModule, 7 | node::{IRNode, IROpcode, ImmediateKind, MINode, Node, NodeId, OperandNode}, 8 | pat_match::{ 9 | add, any, any_block, any_cc, any_f64_imm, any_i32_imm, any_imm, any_imm32, 10 | any_imm32_power_of_2, any_imm_f64, any_reg, any_slot, fiaddr, gbladdr, inst_select, ir, 11 | mul, not, reg_, reg_class, reorder_patterns, store, CompoundPat, MatchContext, Pat, 12 | ReplacedNodeMap, 13 | }, 14 | }, 15 | types::MVType, 16 | }; 17 | use crate::ir::types::Type; 18 | 19 | pub fn run(module: &mut DAGModule) { 20 | for (_, func) in &mut module.functions { 21 | if func.is_internal { 22 | continue; 23 | } 24 | run_on_function(func); 25 | } 26 | } 27 | 28 | fn run_on_function(func: &mut DAGFunction) { 29 | let sext: Pat = ir(IROpcode::Sext) 30 | .named("sext") 31 | // .ty(Type::i64) 32 | .args(vec![any_reg().named("arg").into()]) 33 | .generate(|m, c| { 34 | let ty = c.arena[m["sext"]].as_ir().ty; 35 | c.arena.alloc( 36 | IRNode::new(IROpcode::RegClass) 37 | .args(vec![m["arg"]]) 38 | .ty(ty) 39 | .into(), 40 | ) 41 | }) 42 | .into(); 43 | 44 | let pats = vec![ 45 | sext, 46 | // sext, load4, load5, store, load, load2, load3, store2, brcc, fpbrcc, bitcast, load6, store3, 47 | // store4, 48 | ]; 49 | let pats = reorder_patterns(pats); 50 | 51 | let mut replaced = ReplacedNodeMap::default(); 52 | for &id in &func.dag_basic_blocks { 53 | let block = &func.dag_basic_block_arena[id]; 54 | let a = select_node( 55 | &mut MatchContext { 56 | arena: &mut func.node_arena, 57 | regs: &func.regs, 58 | }, 59 | &mut replaced, 60 | &pats, 61 | block.entry.unwrap(), 62 | ); 63 | assert_eq!(block.entry.unwrap(), a); 64 | } 65 | } 66 | 67 | fn select_node<'a>( 68 | ctx: &mut MatchContext<'a>, 69 | replaced: &mut ReplacedNodeMap, 70 | pats: &[Pat], 71 | id: NodeId, 72 | ) -> NodeId { 73 | let new = inst_select(replaced, ctx, id, pats); 74 | 75 | if let Some(next) = ctx.arena[id].next() { 76 | let next = select_node(ctx, replaced, pats, next); 77 | *ctx.arena[new].next_mut() = Some(next); 78 | } 79 | 80 | new 81 | } 82 | -------------------------------------------------------------------------------- /src/codegen/x64/dag/node.rs: -------------------------------------------------------------------------------- 1 | pub use crate::codegen::common::dag::node::*; 2 | use crate::codegen::common::dag::node::NodeId; 3 | use std::fmt; 4 | 5 | #[derive(Debug, Clone, PartialEq)] 6 | pub enum MemNodeKind { 7 | BaseFi, 8 | BaseFiOff, 9 | BaseFiAlignOff, 10 | BaseFiAlignOffOff, 11 | BaseAlignOff, 12 | BaseOff, 13 | Base, 14 | Address, 15 | AddressOff, 16 | AddressAlignOff, 17 | } 18 | 19 | #[derive(Clone, PartialEq)] 20 | pub enum MemKind { 21 | // BaseFi(NodeId, NodeId), 22 | BaseFi([NodeId; 2]), 23 | // BaseFiOff(NodeId, NodeId, NodeId), 24 | BaseFiOff([NodeId; 3]), 25 | // BaseFiAlignOff(NodeId, NodeId, NodeId, NodeId), 26 | BaseFiAlignOff([NodeId; 4]), 27 | BaseFiAlignOffOff([NodeId; 5]), 28 | BaseAlignOff([NodeId; 3]), 29 | // BaseOff(NodeId, NodeId), 30 | BaseOff([NodeId; 2]), 31 | Base(NodeId), 32 | Address(NodeId), 33 | AddressOff([NodeId; 2]), 34 | AddressAlignOff([NodeId; 3]), 35 | } 36 | 37 | impl MemKind { 38 | pub fn args(&self) -> &[NodeId] { 39 | match self { 40 | Self::BaseFiAlignOff(args) => args, 41 | Self::BaseFi(args) | Self::BaseOff(args) | Self::AddressOff(args) => args, 42 | Self::BaseFiAlignOffOff(args) => args, 43 | Self::AddressAlignOff(args) | Self::BaseFiOff(args) | Self::BaseAlignOff(args) => args, 44 | Self::Address(arg) | Self::Base(arg) => ::core::slice::from_ref(arg), 45 | } 46 | } 47 | 48 | pub fn args_mut(&mut self) -> &mut [NodeId] { 49 | match self { 50 | Self::BaseFiAlignOff(args) => args, 51 | Self::BaseFi(args) | Self::BaseOff(args) | Self::AddressOff(args) => args, 52 | Self::BaseFiAlignOffOff(args) => args, 53 | Self::AddressAlignOff(args) | Self::BaseFiOff(args) | Self::BaseAlignOff(args) => args, 54 | Self::Address(arg) | Self::Base(arg) => ::core::slice::from_mut(arg), 55 | } 56 | } 57 | } 58 | 59 | impl fmt::Debug for MemKind { 60 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 61 | write!( 62 | f, 63 | "{}", 64 | match self { 65 | Self::BaseFi(_) => "BaseFi", 66 | Self::BaseFiOff(_) => "BaseFiOff", 67 | Self::BaseFiAlignOff(_) => "BaseFiAlignOff", 68 | Self::BaseAlignOff(_) => "BaseAlignOff", 69 | Self::BaseFiAlignOffOff(_) => "BaseFiAlignOffOff", 70 | Self::BaseOff(_) => "BaseOff", 71 | Self::Base(_) => "Base", 72 | Self::Address(_) => "Address", 73 | Self::AddressOff(_) => "AddressOff", 74 | Self::AddressAlignOff(_) => "AddressAlignOff", 75 | } 76 | ) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/codegen/common/machine/frame_object.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::exec::roundup; 2 | use crate::codegen::arch::machine::inst::MachineOpcode; 3 | use crate::codegen::common::machine::function::MachineFunction; 4 | use crate::ir::types::*; 5 | use rustc_hash::FxHashMap; 6 | use std::cmp; 7 | use std::fmt; 8 | 9 | pub const ALIGN: i32 = 16; 10 | 11 | #[derive(Debug, Clone)] 12 | pub struct LocalVariables { 13 | pub locals: Vec, 14 | pub cur_idx: usize, 15 | } 16 | 17 | #[derive(Debug)] 18 | pub struct FrameObjectsInfo { 19 | pub offset_map: FxHashMap, // frame index -> offset 20 | pub total_size: i32, 21 | pub callee_saved_regs_byte: usize, // unaligned 22 | } 23 | 24 | #[derive(Clone, PartialEq, Copy)] 25 | pub struct FrameIndexInfo { 26 | pub ty: Type, 27 | pub idx: FrameIndexKind, 28 | } 29 | 30 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)] 31 | pub enum FrameIndexKind { 32 | Arg(usize), 33 | Local(usize), 34 | } 35 | 36 | impl LocalVariables { 37 | pub fn new() -> Self { 38 | Self { 39 | locals: vec![], 40 | cur_idx: 0, 41 | } 42 | } 43 | 44 | pub fn alloc(&mut self, ty: &Type) -> FrameIndexInfo { 45 | let info = FrameIndexInfo::new(*ty, FrameIndexKind::Local(self.cur_idx)); 46 | self.cur_idx += 1; 47 | self.locals.push(info.clone()); 48 | info 49 | } 50 | } 51 | 52 | impl FrameObjectsInfo { 53 | pub fn aligned_callee_saved_regs_byte(&self) -> i32 { 54 | roundup(self.callee_saved_regs_byte as i32, ALIGN) 55 | } 56 | 57 | pub fn calc_max_adjust_stack_down(f: &MachineFunction) -> i32 { 58 | let mut down = 0i32; 59 | 60 | for (_, _, iiter) in f.body.mbb_iter() { 61 | for (_, inst) in iiter { 62 | match inst.opcode { 63 | MachineOpcode::AdjStackDown => { 64 | let d = inst.operand[0].as_constant().as_i32(); 65 | down = cmp::max(d, down); 66 | } 67 | MachineOpcode::AdjStackUp => {} 68 | _ => continue, 69 | } 70 | } 71 | } 72 | 73 | down 74 | } 75 | } 76 | 77 | impl FrameIndexKind { 78 | pub fn new_arg(idx: usize) -> Self { 79 | FrameIndexKind::Arg(idx) 80 | } 81 | 82 | pub fn new_local(idx: usize) -> Self { 83 | FrameIndexKind::Local(idx) 84 | } 85 | } 86 | 87 | impl FrameIndexInfo { 88 | pub fn new(ty: Type, idx: FrameIndexKind) -> Self { 89 | Self { ty, idx } 90 | } 91 | } 92 | 93 | impl fmt::Debug for FrameIndexInfo { 94 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 95 | write!(f, "FI<{:?}, {:?}>", self.ty, self.idx) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/ir/merge_ret.rs: -------------------------------------------------------------------------------- 1 | use crate::ir::{ 2 | builder::IRBuilder, function::Function, module::Module, opcode::Opcode, types::Type, 3 | value::Value, 4 | }; 5 | 6 | pub struct MergeReturns {} 7 | 8 | struct MergeReturnsOnFunction<'a> { 9 | func: &'a mut Function, 10 | } 11 | 12 | impl MergeReturns { 13 | pub fn new() -> Self { 14 | Self {} 15 | } 16 | 17 | pub fn run_on_module(&mut self, module: &mut Module) { 18 | for (_, func) in &mut module.functions { 19 | if func.is_internal { 20 | continue; 21 | } 22 | 23 | MergeReturnsOnFunction { func }.run(); 24 | } 25 | } 26 | } 27 | 28 | impl<'a> MergeReturnsOnFunction<'a> { 29 | pub fn run(&mut self) { 30 | if self.func.basic_blocks.order.len() == 0 { 31 | return; 32 | } 33 | 34 | let ret_void = self.func.get_return_type() == Type::Void; 35 | let entry = self.func.basic_blocks.order[0]; 36 | let mut returns = vec![]; 37 | let mut return_at_last_block = false; 38 | for (i, &id) in self.func.basic_blocks.order.iter().enumerate() { 39 | let block = &self.func.basic_blocks.arena[id]; 40 | if id != entry && block.pred.len() == 0 { 41 | // unreachable block 42 | continue; 43 | } 44 | let inst_id = match block.iseq_ref().last() { 45 | Some(id) => *id, 46 | None => continue, 47 | }; 48 | let inst = &self.func.inst_table[inst_id]; 49 | if inst.opcode == Opcode::Ret { 50 | returns.push(inst_id); 51 | return_at_last_block = self.func.basic_blocks.order.len() - 1 == i; 52 | } 53 | } 54 | 55 | if returns.len() == 0 || (returns.len() == 1 && return_at_last_block) { 56 | return; 57 | } 58 | 59 | let mut builder = self.func.ir_builder(); 60 | let mut pairs = vec![]; 61 | let ret_bb = builder.append_basic_block(); 62 | 63 | for ret_id in returns { 64 | let ret = &builder.func_ref().inst_table[ret_id]; 65 | let parent = ret.parent; 66 | if !ret_void { 67 | pairs.push((ret.operand.args()[0], parent)); 68 | } 69 | builder.func_ref_mut().remove_inst(ret_id); 70 | builder.set_insert_point(parent); 71 | builder.build_br(ret_bb); 72 | } 73 | 74 | builder.set_insert_point(ret_bb); 75 | let val = if ret_void { 76 | Value::None 77 | } else { 78 | if pairs.len() == 1 { 79 | pairs[0].0 80 | } else { 81 | builder.build_phi(pairs) 82 | } 83 | }; 84 | builder.build_ret(val); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/codegen/x64/machine/eliminate_fi.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::machine::inst::MachineMemOperand; 2 | use crate::codegen::common::machine::eliminate_fi::EliminateFrameIndexOnFunction; 3 | use crate::codegen::common::machine::frame_object::{FrameIndexInfo, FrameObjectsInfo}; 4 | use crate::codegen::common::machine::inst::{MachineInst, MachineOperand}; 5 | 6 | impl<'a> EliminateFrameIndexOnFunction<'a> { 7 | pub fn get_frame_index(inst: &MachineInst) -> Option<&FrameIndexInfo> { 8 | for op in &inst.operand { 9 | match op { 10 | MachineOperand::Mem(MachineMemOperand::BaseFi(_, fi)) 11 | | MachineOperand::Mem(MachineMemOperand::BaseFiOff(_, fi, _)) 12 | | MachineOperand::Mem(MachineMemOperand::BaseFiAlignOff(_, fi, _, _)) 13 | | MachineOperand::Mem(MachineMemOperand::BaseFiAlignOffOff(_, fi, _, _, _)) => { 14 | return Some(fi) 15 | } 16 | _ => {} 17 | } 18 | } 19 | None 20 | } 21 | 22 | pub fn replace_frame_index(frame_objects: &FrameObjectsInfo, inst: &mut MachineInst) { 23 | for op in &mut inst.operand { 24 | match op { 25 | MachineOperand::Mem(MachineMemOperand::BaseFi(base, fi)) => { 26 | *op = MachineOperand::Mem(MachineMemOperand::BaseOff( 27 | *base, 28 | frame_objects.offset(fi.idx).unwrap(), 29 | )) 30 | } 31 | MachineOperand::Mem(MachineMemOperand::BaseFiOff(base, fi, off)) => { 32 | *op = MachineOperand::Mem(MachineMemOperand::BaseOff( 33 | *base, 34 | frame_objects.offset(fi.idx).unwrap() + *off, 35 | )) 36 | } 37 | MachineOperand::Mem(MachineMemOperand::BaseFiAlignOff(base, fi, align, off)) => { 38 | *op = MachineOperand::Mem(MachineMemOperand::BaseOffAlignOff( 39 | *base, 40 | frame_objects.offset(fi.idx).unwrap(), 41 | *align, 42 | *off, 43 | )); 44 | } 45 | MachineOperand::Mem(MachineMemOperand::BaseFiAlignOffOff( 46 | base, 47 | fi, 48 | align, 49 | off, 50 | off2, 51 | )) => { 52 | *op = MachineOperand::Mem(MachineMemOperand::BaseOffAlignOff( 53 | *base, 54 | frame_objects.offset(fi.idx).unwrap() + *off2, 55 | *align, 56 | *off, 57 | )); 58 | } 59 | _ => {} 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/codegen/common/machine/const_data.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::machine::inst::MachineConstant; 2 | use std::fmt; 3 | use std::ops::{Index, IndexMut}; 4 | use std::sync::atomic::{self, AtomicUsize}; 5 | 6 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 7 | pub struct DataId { 8 | arena_id: usize, 9 | id: usize, 10 | } 11 | 12 | pub struct ConstDataArena { 13 | id: usize, 14 | arena: Vec, 15 | } 16 | 17 | pub struct ConstDataArenaIter<'a> { 18 | id: usize, 19 | arena: &'a Vec, 20 | nth: usize, 21 | } 22 | 23 | impl ConstDataArena { 24 | pub fn new() -> Self { 25 | Self { 26 | id: Self::new_arena_id(), 27 | arena: vec![], 28 | } 29 | } 30 | 31 | pub fn alloc(&mut self, c: MachineConstant) -> DataId { 32 | let id = self.arena.len(); 33 | self.arena.push(c); 34 | DataId { 35 | arena_id: self.id, 36 | id, 37 | } 38 | } 39 | 40 | pub fn id_and_data<'a>(&'a self) -> ConstDataArenaIter<'a> { 41 | ConstDataArenaIter::new(self.id, &self.arena) 42 | } 43 | 44 | fn new_arena_id() -> usize { 45 | static ARENA_COUNTER: AtomicUsize = AtomicUsize::new(0); 46 | ARENA_COUNTER.fetch_add(1, atomic::Ordering::SeqCst) 47 | } 48 | } 49 | 50 | impl DataId { 51 | pub fn arena_id(&self) -> usize { 52 | self.arena_id 53 | } 54 | 55 | pub fn id(&self) -> usize { 56 | self.id 57 | } 58 | } 59 | 60 | impl<'a> ConstDataArenaIter<'a> { 61 | pub fn new(id: usize, arena: &'a Vec) -> Self { 62 | Self { id, arena, nth: 0 } 63 | } 64 | } 65 | 66 | impl<'a> Iterator for ConstDataArenaIter<'a> { 67 | type Item = (DataId, &'a MachineConstant); 68 | 69 | fn next(&mut self) -> Option { 70 | self.nth += 1; 71 | let id = self.nth - 1; 72 | self.arena.get(id).and_then(|item| { 73 | Some(( 74 | DataId { 75 | id, 76 | arena_id: self.id, 77 | }, 78 | item, 79 | )) 80 | }) 81 | } 82 | } 83 | 84 | impl Index for ConstDataArena { 85 | type Output = MachineConstant; 86 | 87 | fn index(&self, id: DataId) -> &Self::Output { 88 | assert_eq!(self.id, id.arena_id); 89 | &self.arena[id.id] 90 | } 91 | } 92 | 93 | impl IndexMut for ConstDataArena { 94 | fn index_mut(&mut self, id: DataId) -> &mut Self::Output { 95 | assert_eq!(self.id, id.arena_id); 96 | &mut self.arena[id.id] 97 | } 98 | } 99 | 100 | impl fmt::Display for DataId { 101 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 102 | write!(f, "data:{}", self.id) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/codegen/x64/machine/register.rs: -------------------------------------------------------------------------------- 1 | pub use crate::codegen::common::machine::register::*; 2 | use crate::ir::types::Type; 3 | use defs::registers; 4 | use id_arena::Arena; 5 | use rustc_hash::FxHashMap; 6 | use std::cell::RefCell; 7 | 8 | registers! { 9 | // register nubmering: https://corsix.github.io/dynasm-doc/instructions.html#registers 10 | class GR8 (8, i8, [i1, i8], [AL]) < GR32 { 11 | AL, CL, DL, BL, SPL, BPL, SIL, DIL, 12 | R8B, R9B, R10B, R11B, R12B, R13B, R14B, R15B 13 | } 14 | 15 | class GR32 (32, i32, [i32], [EAX]) < GR64 { 16 | EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI, 17 | R8D, R9D, R10D, R11D, R12D, R13D, R14D, R15D 18 | } 19 | 20 | class GR64 (64, i64, [i64, Pointer!], [RAX]) { 21 | RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, 22 | R8, R9, R10, R11, R12, R13, R14, R15 23 | } 24 | 25 | class XMM (128, f64, [f64], [XMM0]) { 26 | XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, 27 | XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15 28 | } 29 | 30 | // Normal order of registers used to pass arguments 31 | // TODO: This is System V AMD64 ABI. 32 | // https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI 33 | order arg GR8 { DIL, SIL, DL, CL, R8B, R9B } 34 | order arg GR32 { EDI, ESI, EDX, ECX, R8D, R9D } 35 | order arg GR64 { RDI, RSI, RDX, RCX, R8, R9 } 36 | order arg XMM { XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7 } 37 | 38 | // Normal order of general-purpose registers 39 | order gp GR8 { AL, CL, DL, R8B, R9B, R10B, R11B, BL, R12B, R13B, R14B, R15B } 40 | order gp GR32 { EAX, ECX, EDX, R8D, R9D, R10D, R11D, EBX,R12D, R13D, R14D, R15D } 41 | order gp GR64 { RAX, RCX, RDX, R8, R9, R10, R11, RBX,R12, R13, R14, R15 } 42 | order gp XMM { XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8, XMM15 } 43 | } 44 | 45 | thread_local! { 46 | pub static CALLEE_SAVED_REGS: PhysRegSet = { 47 | let mut bits = PhysRegSet::new(); 48 | let regs = to_phys![ 49 | GR8::BL, 50 | GR8::BPL, 51 | GR8::R12B, 52 | GR8::R13B, 53 | GR8::R14B, 54 | GR8::R15B, 55 | GR32::EBX, 56 | GR32::EBP, 57 | GR32::R12D, 58 | GR32::R13D, 59 | GR32::R14D, 60 | GR32::R15D, 61 | GR64::RBX, 62 | GR64::RBP, 63 | GR64::R12, 64 | GR64::R13, 65 | GR64::R14, 66 | GR64::R15, 67 | XMM::XMM6, 68 | XMM::XMM7, 69 | XMM::XMM8, 70 | XMM::XMM15 71 | ]; 72 | for reg in regs { 73 | bits.set(reg) 74 | } 75 | bits 76 | }; 77 | 78 | pub static REG_FILE: RefCell> = { 79 | RefCell::new(FxHashMap::default()) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/codegen/riscv64/machine/inst.rs: -------------------------------------------------------------------------------- 1 | use super::super::machine::register::RegisterId; 2 | use super::frame_object::*; 3 | pub use super::inst_def::TargetOpcode; 4 | pub use crate::codegen::common::machine::{basic_block::MachineBasicBlockId, inst::*}; 5 | use crate::ir::types::Type; 6 | 7 | #[derive(Debug, Clone)] 8 | pub enum MachineMemOperand { 9 | FiReg(FrameIndexInfo, RegisterId), 10 | ImmReg(i32, RegisterId), 11 | Address(AddressKind), 12 | } 13 | 14 | impl MachineOpcode { 15 | pub fn is_copy_like(&self) -> bool { 16 | matches!(self, MachineOpcode::MV | MachineOpcode::Copy) 17 | } 18 | 19 | pub fn is_terminator(&self) -> bool { 20 | matches!( 21 | self, 22 | MachineOpcode::Ret | MachineOpcode::JR | MachineOpcode::J | MachineOpcode::BEQ 23 | ) 24 | } 25 | 26 | pub fn is_unconditional_jmp(&self) -> bool { 27 | matches!(self, MachineOpcode::JR | MachineOpcode::J) 28 | } 29 | 30 | pub fn is_conditional_jmp(&self) -> bool { 31 | matches!( 32 | self, 33 | MachineOpcode::BEQ | MachineOpcode::BLT | MachineOpcode::BLE 34 | ) 35 | } 36 | 37 | pub fn flip_conditional_jmp(&self) -> Option { 38 | match self { 39 | Self::BEQ => Some(Self::BNE), 40 | Self::BNE => Some(Self::BEQ), 41 | Self::BGE => Some(Self::BLT), 42 | Self::BGT => Some(Self::BLE), 43 | Self::BLE => Some(Self::BGT), 44 | Self::BLT => Some(Self::BGE), 45 | _ => None, 46 | } 47 | } 48 | } 49 | 50 | impl MachineInst { 51 | pub fn get_jmp_dst(&self) -> Option { 52 | if !self.opcode.is_jmp() { 53 | return None; 54 | } 55 | 56 | match self.opcode { 57 | MachineOpcode::J => Some(self.operand[0].as_basic_block()), 58 | MachineOpcode::BEQ 59 | | MachineOpcode::BNE 60 | | MachineOpcode::BGE 61 | | MachineOpcode::BGT 62 | | MachineOpcode::BLE 63 | | MachineOpcode::BLT => Some(self.operand[2].as_basic_block()), 64 | _ => None, 65 | } 66 | } 67 | } 68 | 69 | impl MachineMemOperand { 70 | pub fn registers(&self) -> Vec<&RegisterId> { 71 | match self { 72 | MachineMemOperand::ImmReg(_, r) | MachineMemOperand::FiReg(_, r) => vec![r], 73 | MachineMemOperand::Address(_) => vec![], 74 | } 75 | } 76 | 77 | pub fn registers_mut(&mut self) -> Vec<&mut RegisterId> { 78 | match self { 79 | MachineMemOperand::ImmReg(_, r) | MachineMemOperand::FiReg(_, r) => vec![r], 80 | MachineMemOperand::Address(_) => vec![], 81 | } 82 | } 83 | 84 | pub fn get_type(&self) -> Option { 85 | match self { 86 | Self::FiReg(fi, _) => Some(fi.ty), 87 | Self::ImmReg(_, _) | Self::Address(_) => None, 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/codegen/x64/machine/two_addr.rs: -------------------------------------------------------------------------------- 1 | use super::inst::*; 2 | use crate::codegen::common::machine::{builder::*, function::*, module::*}; 3 | use crate::traits::pass::ModulePassTrait; 4 | use rustc_hash::FxHashMap; 5 | use std::mem; 6 | 7 | pub struct TwoAddressConverter {} 8 | 9 | impl ModulePassTrait for TwoAddressConverter { 10 | type M = MachineModule; 11 | 12 | fn name(&self) -> &'static str { 13 | "TwoAddressConverter" 14 | } 15 | 16 | fn run_on_module(&mut self, module: &mut Self::M) { 17 | self.run_on_module(module) 18 | } 19 | } 20 | 21 | impl TwoAddressConverter { 22 | pub fn new() -> Self { 23 | Self {} 24 | } 25 | 26 | pub fn run_on_module(&mut self, module: &mut MachineModule) { 27 | for (_, f) in &mut module.functions { 28 | self.run_on_function(f); 29 | } 30 | } 31 | 32 | pub fn run_on_function(&mut self, f: &mut MachineFunction) { 33 | if f.is_internal { 34 | return; 35 | } 36 | 37 | let mut tied = vec![]; 38 | 39 | for (_, bb) in f.body.basic_blocks.id_and_block() { 40 | for inst_id in &*bb.iseq_ref() { 41 | let inst = &mut f.body.inst_arena[*inst_id]; 42 | 43 | // No tied values then skip 44 | if inst.tie.len() == 0 { 45 | continue; 46 | } 47 | 48 | // Delete the map of tied registers by mem::replace. 49 | // Now that no registtters are tied. 50 | for (def, use_) in mem::replace(&mut inst.tie, FxHashMap::default()) { 51 | tied.push((*inst_id, def, use_)); 52 | } 53 | 54 | let opcode = inst.opcode; 55 | for (d, u) in &opcode.inst_def().unwrap().tie { 56 | // Replace a tied use virtual register with a tied def virtual register. 57 | // e.g. 58 | // before: v1 = add v2, 1 (tied: v1 and v2) 59 | // after: v1 = add v1, 1 60 | inst.replace_nth_operand_with( 61 | &f.regs_info, 62 | u.as_use(), 63 | MachineOperand::Register(inst.def[d.as_def()]), 64 | ); 65 | } 66 | } 67 | } 68 | 69 | for (inst_id, def, use_) in tied { 70 | let inst_bb = f.body.inst_arena[inst_id].parent; 71 | 72 | // before: v1 = add v1, 1 73 | // after: v1 = copy v2 74 | // v1 = add v1, 1 75 | 76 | let copy = MachineInst::new_simple( 77 | MachineOpcode::Copy, 78 | vec![MachineOperand::Register(use_)], 79 | inst_bb, 80 | ) 81 | .with_def(vec![def]) 82 | .copy_for_two_addr(Some(inst_id)); 83 | 84 | let mut builder = Builder::new(f); 85 | builder.set_insert_point_before_inst(inst_id).unwrap(); 86 | builder.insert(copy); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/codegen/common/machine/inst_def.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::arch::machine::inst::TargetOpcode; 2 | use crate::codegen::arch::machine::register::{PhysReg, RegisterClassKind}; 3 | use rustc_hash::FxHashMap; 4 | 5 | #[derive(Clone)] 6 | pub struct TargetInstDef { 7 | pub name: &'static str, 8 | pub opcode: TargetOpcode, 9 | pub uses: Vec, 10 | pub defs: Vec, 11 | pub tie: FxHashMap, // def -> use 12 | pub imp_use: Vec, 13 | pub imp_def: Vec, 14 | } 15 | 16 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] 17 | pub enum DefOrUseReg { 18 | Def(usize), // nth def register 19 | Use(usize), // nth use register 20 | } 21 | 22 | #[derive(Clone)] 23 | pub enum TargetOperand { 24 | Register(TargetRegister), 25 | Immediate(TargetImmediate), 26 | Addr, 27 | FrameIndex, 28 | Any, 29 | Mem, 30 | Block, 31 | } 32 | 33 | use std::{fmt::Debug, hash::Hash}; 34 | #[derive(Clone)] 35 | pub enum TargetRegister { 36 | RegClass(RegisterClassKind), 37 | Specific(PhysReg), 38 | Any, 39 | } 40 | 41 | #[derive(Clone, Copy, PartialEq)] 42 | pub enum TargetImmediate { 43 | I8, 44 | I16, 45 | I32, 46 | I64, 47 | F64, 48 | } 49 | 50 | impl TargetInstDef { 51 | pub fn new(name: &'static str, opcode: TargetOpcode) -> Self { 52 | Self { 53 | name, 54 | opcode, 55 | uses: vec![], 56 | defs: vec![], 57 | tie: FxHashMap::default(), 58 | imp_def: vec![], 59 | imp_use: vec![], 60 | } 61 | } 62 | 63 | pub fn set_uses(mut self, uses: Vec) -> Self { 64 | self.uses = uses; 65 | self 66 | } 67 | 68 | pub fn set_defs(mut self, defs: Vec) -> Self { 69 | self.defs = defs; 70 | self 71 | } 72 | 73 | pub fn add_tie(mut self, def: DefOrUseReg, use_: DefOrUseReg) -> Self { 74 | self.tie.insert(def, use_); 75 | self 76 | } 77 | 78 | pub fn set_imp_def(mut self, imp_def: Vec) -> Self { 79 | self.imp_def = imp_def; 80 | self 81 | } 82 | 83 | pub fn set_imp_use(mut self, imp_use: Vec) -> Self { 84 | self.imp_use = imp_use; 85 | self 86 | } 87 | 88 | pub fn def_reg_class(&self) -> RegisterClassKind { 89 | self.defs[0].as_reg_class() 90 | } 91 | } 92 | 93 | impl TargetRegister { 94 | pub fn as_reg_class(&self) -> RegisterClassKind { 95 | match self { 96 | Self::RegClass(rc) => *rc, 97 | _ => panic!(), 98 | } 99 | } 100 | } 101 | 102 | impl DefOrUseReg { 103 | pub fn as_def(&self) -> usize { 104 | match self { 105 | Self::Def(d) => *d, 106 | _ => panic!(), 107 | } 108 | } 109 | 110 | pub fn as_use(&self) -> usize { 111 | match self { 112 | Self::Use(u) => *u, 113 | _ => panic!(), 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/ir/inst_combine.rs: -------------------------------------------------------------------------------- 1 | use crate::ir::{ 2 | function::Function, 3 | module::Module, 4 | opcode::{Instruction, Opcode}, 5 | value::Value, 6 | }; 7 | use std::collections::VecDeque; 8 | 9 | pub struct InstructionCombine {} 10 | 11 | pub struct InstructionCombineOnFunction<'a> { 12 | func: &'a mut Function, 13 | } 14 | 15 | impl InstructionCombine { 16 | pub fn new() -> Self { 17 | Self {} 18 | } 19 | 20 | pub fn run_on_module(&mut self, module: &mut Module) { 21 | for (_, func) in &mut module.functions { 22 | if func.is_internal { 23 | continue; 24 | } 25 | 26 | InstructionCombineOnFunction { func }.run() 27 | } 28 | } 29 | } 30 | 31 | impl<'a> InstructionCombineOnFunction<'a> { 32 | pub fn run(&mut self) { 33 | let mut worklist = VecDeque::new(); 34 | 35 | for (_, block) in &self.func.basic_blocks.arena { 36 | for &inst_id in &*block.iseq_ref() { 37 | let inst = &self.func.inst_table[inst_id]; 38 | if self.is_combinable(inst) { 39 | worklist.push_back(inst_id); 40 | } 41 | } 42 | } 43 | 44 | while let Some(inst_id) = worklist.pop_front() { 45 | let inst1 = &self.func.inst_table[inst_id]; 46 | if !self.is_combinable(inst1) { 47 | continue; 48 | } 49 | let inst2_id = *inst1.users.borrow().iter().next().unwrap(); 50 | let op0 = inst1.operand.args()[0].clone(); 51 | let op1 = inst1.operand.args()[1].clone(); 52 | 53 | if inst1.has_one_use() { 54 | self.func.remove_inst(inst_id); 55 | } 56 | 57 | let inst2 = &mut self.func.inst_table[inst2_id]; 58 | 59 | inst2.operand.args_mut()[0] = op0; 60 | inst2.operand.args_mut()[1] = match inst2.opcode { 61 | Opcode::Add => op1.const_add(&inst2.operand.args()[1]).unwrap(), 62 | Opcode::Sub => op1.const_add(&inst2.operand.args()[1]).unwrap(), 63 | Opcode::Mul => op1.const_mul(&inst2.operand.args()[1]).unwrap(), 64 | Opcode::Div => op1.const_mul(&inst2.operand.args()[1]).unwrap(), 65 | Opcode::Rem => op1.const_rem(&inst2.operand.args()[1]).unwrap(), 66 | _ => unreachable!(), 67 | }; 68 | } 69 | } 70 | 71 | pub fn is_combinable(&self, inst: &Instruction) -> bool { 72 | Self::is_combinable_sub(inst) && { 73 | let user_id = *inst.users.borrow().iter().next().unwrap(); 74 | let user = &self.func.inst_table[user_id]; 75 | inst.opcode == user.opcode 76 | && inst.parent == user.parent 77 | && Self::is_combinable_sub(user) 78 | } 79 | } 80 | 81 | pub fn is_combinable_sub(inst: &Instruction) -> bool { 82 | matches!( 83 | inst.opcode, 84 | Opcode::Add | Opcode::Sub | Opcode::Mul | Opcode::Div | Opcode::Rem 85 | ) && matches!(inst.operand.args()[0], Value::Instruction(_)) 86 | && matches!(inst.operand.args()[1], Value::Immediate(_)) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /sericumcc/src/token.rs: -------------------------------------------------------------------------------- 1 | use id_arena::Id; 2 | use rustc_hash::FxHashSet; 3 | use std::path::PathBuf; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Token { 7 | pub kind: Kind, 8 | pub leading_space: bool, 9 | pub loc: SourceLoc, 10 | pub hideset: FxHashSet, 11 | } 12 | 13 | #[derive(Debug, Clone, PartialEq)] 14 | pub enum Kind { 15 | MacroParam { nth: usize }, 16 | Keyword(Keyword), 17 | Identifier(String), 18 | Int { n: i64, bits: u8 }, 19 | Float(f64), 20 | String(String), 21 | Char(char), 22 | Symbol(Symbol), 23 | Newline, 24 | } 25 | 26 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 27 | pub enum Keyword { 28 | Typedef, 29 | Extern, 30 | Static, 31 | Auto, 32 | Restrict, 33 | Register, 34 | Const, 35 | ConstExpr, 36 | Volatile, 37 | Void, 38 | Signed, 39 | Unsigned, 40 | Char, 41 | Int, 42 | Short, 43 | Long, 44 | Float, 45 | Double, 46 | Struct, 47 | Enum, 48 | Union, 49 | Noreturn, 50 | Inline, 51 | If, 52 | Else, 53 | For, 54 | Do, 55 | While, 56 | Switch, 57 | Case, 58 | Default, 59 | Goto, 60 | Break, 61 | Continue, 62 | Return, 63 | } 64 | 65 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 66 | pub enum Symbol { 67 | OpeningParen, 68 | ClosingParen, 69 | OpeningBrace, 70 | ClosingBrace, 71 | OpeningBoxBracket, 72 | ClosingBoxBracket, 73 | Comma, 74 | Semicolon, 75 | Colon, 76 | Point, 77 | Arrow, 78 | Inc, 79 | Dec, 80 | Add, 81 | Sub, 82 | Asterisk, 83 | Div, 84 | Mod, 85 | Not, 86 | BitwiseNot, 87 | Ampersand, 88 | Shl, 89 | Shr, 90 | Lt, 91 | Le, 92 | Gt, 93 | Ge, 94 | Eq, 95 | Ne, 96 | Xor, 97 | Or, 98 | LAnd, 99 | LOr, 100 | Question, 101 | Assign, 102 | AssignAdd, 103 | AssignSub, 104 | AssignMul, 105 | AssignDiv, 106 | AssignMod, 107 | AssignShl, 108 | AssignShr, 109 | AssignAnd, 110 | AssignXor, 111 | AssignOr, 112 | Hash, 113 | Vararg, 114 | Sizeof, 115 | } 116 | 117 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 118 | pub struct SourceLoc { 119 | pub file: Id, 120 | pub line: usize, 121 | pub pos: usize, 122 | } 123 | 124 | impl Token { 125 | pub fn new(kind: Kind, loc: SourceLoc) -> Self { 126 | Self { 127 | kind, 128 | leading_space: false, 129 | loc, 130 | hideset: FxHashSet::default(), 131 | } 132 | } 133 | 134 | pub fn leading_space(mut self, x: bool) -> Self { 135 | self.leading_space = x; 136 | self 137 | } 138 | } 139 | 140 | impl Kind { 141 | pub fn is_identifier(&self) -> bool { 142 | matches!(self, Kind::Identifier(_)) 143 | } 144 | 145 | pub fn is_keyword(&self) -> bool { 146 | matches!(self, Kind::Keyword(_)) 147 | } 148 | } 149 | 150 | impl SourceLoc { 151 | pub fn new(file: Id) -> Self { 152 | Self { 153 | file, 154 | line: 1, 155 | pos: 0, 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/codegen/aarch64/machine/inst.rs: -------------------------------------------------------------------------------- 1 | use super::frame_object::*; 2 | pub use super::inst_def::TargetOpcode; 3 | pub use crate::codegen::common::machine::{basic_block::MachineBasicBlockId, inst::*}; 4 | use crate::ir::types::Type; 5 | 6 | #[derive(Debug, Clone)] 7 | pub enum MachineMemOperand { 8 | RegFi(RegisterOperand, FrameIndexInfo), 9 | PreIndex(RegisterOperand, i32), 10 | PostIndex(RegisterOperand, i32), 11 | Reg(RegisterOperand), 12 | // ImmReg(i32, RegisterOperand), 13 | Address(AddressKind), 14 | } 15 | 16 | impl MachineOpcode { 17 | pub fn is_copy_like(&self) -> bool { 18 | matches!(self, MachineOpcode::Copy | MachineOpcode::MOVrr) 19 | } 20 | 21 | pub fn is_terminator(&self) -> bool { 22 | matches!( 23 | self, 24 | MachineOpcode::RET 25 | | MachineOpcode::Ret 26 | | MachineOpcode::B 27 | | MachineOpcode::B_EQ 28 | | MachineOpcode::B_NE 29 | | MachineOpcode::B_LE 30 | | MachineOpcode::B_LT 31 | | MachineOpcode::B_GE 32 | | MachineOpcode::B_GT 33 | ) 34 | } 35 | 36 | pub fn is_unconditional_jmp(&self) -> bool { 37 | matches!(self, MachineOpcode::B) 38 | } 39 | 40 | pub fn is_conditional_jmp(&self) -> bool { 41 | matches!( 42 | self, 43 | MachineOpcode::B_EQ 44 | | MachineOpcode::B_NE 45 | | MachineOpcode::B_LE 46 | | MachineOpcode::B_LT 47 | | MachineOpcode::B_GE 48 | | MachineOpcode::B_GT 49 | ) 50 | } 51 | 52 | pub fn flip_conditional_jmp(&self) -> Option { 53 | match self { 54 | Self::B_EQ => Some(Self::B_NE), 55 | Self::B_NE => Some(Self::B_EQ), 56 | Self::B_GE => Some(Self::B_LT), 57 | Self::B_GT => Some(Self::B_LE), 58 | Self::B_LE => Some(Self::B_GT), 59 | Self::B_LT => Some(Self::B_GE), 60 | _ => None, 61 | } 62 | } 63 | } 64 | 65 | impl MachineInst { 66 | pub fn get_jmp_dst(&self) -> Option { 67 | if !self.opcode.is_jmp() { 68 | return None; 69 | } 70 | 71 | match self.opcode { 72 | MachineOpcode::B 73 | | MachineOpcode::B_EQ 74 | | MachineOpcode::B_NE 75 | | MachineOpcode::B_GE 76 | | MachineOpcode::B_GT 77 | | MachineOpcode::B_LE 78 | | MachineOpcode::B_LT => Some(self.operand[0].as_basic_block()), 79 | _ => None, 80 | } 81 | } 82 | } 83 | 84 | impl MachineMemOperand { 85 | pub fn registers(&self) -> Vec<&RegisterOperand> { 86 | match self { 87 | Self::PostIndex(r, _) | Self::PreIndex(r, _) | Self::Reg(r) | Self::RegFi(r, _) => { 88 | vec![r] 89 | } 90 | Self::Address(_) => vec![], 91 | } 92 | } 93 | 94 | pub fn registers_mut(&mut self) -> Vec<&mut RegisterOperand> { 95 | match self { 96 | Self::PostIndex(r, _) | Self::PreIndex(r, _) | Self::Reg(r) | Self::RegFi(r, _) => { 97 | vec![r] 98 | } 99 | Self::Address(_) => vec![], 100 | } 101 | } 102 | 103 | pub fn get_type(&self) -> Option { 104 | match self { 105 | Self::RegFi(_, fi) => Some(fi.ty), 106 | Self::PostIndex(_, _) | Self::PreIndex(_, _) | Self::Address(_) | Self::Reg(_) => None, 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/codegen/x64/frame_object.rs: -------------------------------------------------------------------------------- 1 | use super::exec::roundup; 2 | use super::machine::register::{ty2rc, GR64}; 3 | use crate::codegen::arch::machine::abi::SystemV; 4 | use crate::codegen::common::machine::calling_conv::CallingConv; 5 | use crate::codegen::common::machine::function::MachineFunction; 6 | use crate::codegen::common::machine::register::TargetRegisterTrait; 7 | pub use crate::codegen::common::machine::{calling_conv::ArgumentRegisterOrder, frame_object::*}; 8 | use crate::ir::types::*; 9 | use rustc_hash::{FxHashMap, FxHashSet}; 10 | 11 | impl FrameObjectsInfo { 12 | pub fn new(tys: &Types, f: &MachineFunction) -> Self { 13 | let mut offset_map = FxHashMap::default(); 14 | let mut offset = 0i32; 15 | let has_call = f.body.has_call(); 16 | let mut saved_regs = f 17 | .body 18 | .appeared_phys_regs() 19 | .containing_callee_saved_regs() 20 | .to_phys_set() 21 | .into_iter() 22 | .map(|r| r.superest_reg()) 23 | .collect::>(); 24 | if has_call { 25 | saved_regs.insert(GR64::RBP.as_phys_reg()); 26 | } 27 | let saved_regs_byte = saved_regs.len() * 8; 28 | let padding = |off, align| -> i32 { (align - off % align) % align }; 29 | 30 | let base = &tys.base.borrow(); 31 | let f_ty = base.as_function_ty(f.ty).unwrap(); 32 | 33 | let abi = SystemV::new(); 34 | let mut arg_reg_order = ArgumentRegisterOrder::new(&abi); 35 | 36 | for (i, param_ty) in f_ty.params_ty.iter().enumerate() { 37 | // TODO: Correct? 38 | let byval = f_ty.params_attr.get(&i).map_or(false, |attr| attr.byval); 39 | if byval { 40 | let param_ty = base.get_element_ty(*param_ty, None).unwrap(); // param_ty = StructType 41 | let struct_ty = base.as_struct_ty(param_ty).unwrap(); 42 | let size = param_ty.size_in_byte(tys) as i32; 43 | let align = param_ty.align_in_byte(tys) as i32; 44 | offset += size + padding(offset, align); 45 | offset_map.insert(FrameIndexKind::Arg(i), -offset); 46 | for rc in SystemV::reg_classes_used_for_passing_byval(struct_ty) { 47 | arg_reg_order.next(rc); 48 | } 49 | continue; 50 | } 51 | 52 | let rc = ty2rc(param_ty).unwrap(); 53 | if arg_reg_order.next(rc).is_none() { 54 | let size = param_ty.size_in_byte(tys) as i32; 55 | let align = param_ty.align_in_byte(tys) as i32; 56 | offset += size + padding(offset, align); 57 | offset_map.insert(FrameIndexKind::Arg(i), -offset); 58 | } 59 | } 60 | 61 | for FrameIndexInfo { idx, ty } in &f.local_mgr.locals { 62 | let size = ty.size_in_byte(tys) as i32; 63 | let align = ty.align_in_byte(tys) as i32; 64 | offset += size + padding(offset, align); 65 | offset_map.insert(*idx, -offset); 66 | } 67 | 68 | let stack_down = Self::calc_max_adjust_stack_down(f); 69 | offset = roundup(offset + stack_down as i32, 16) 70 | + if saved_regs_byte == 0 && !has_call { 71 | 0 72 | } else if saved_regs_byte % 16 == 0 { 73 | 8 74 | } else { 75 | 0 76 | }; 77 | 78 | Self { 79 | offset_map, 80 | total_size: offset, 81 | callee_saved_regs_byte: saved_regs_byte, 82 | } 83 | } 84 | 85 | pub fn offset(&self, kind: FrameIndexKind) -> Option { 86 | self.offset_map.get(&kind).map(|x| *x as i32) 87 | } 88 | 89 | pub fn total_size(&self) -> i32 { 90 | self.total_size 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /sericumcc/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate id_arena; 2 | extern crate rustc_hash; 3 | extern crate sericum; 4 | 5 | use std::path::PathBuf; 6 | use std::{ 7 | fs, 8 | io::{BufWriter, Write}, 9 | process, 10 | }; 11 | use {rand, rand::Rng}; 12 | 13 | pub mod ast; 14 | pub mod codegen; 15 | pub mod lexer; 16 | pub mod parser; 17 | pub mod token; 18 | pub mod types; 19 | 20 | // TODO: Refine code 21 | 22 | pub fn compile(path: PathBuf, optimization: bool) { 23 | let mut lexer = lexer::Lexer::new(path); 24 | let mut parser = parser::Parser::new(&mut lexer); 25 | let nodes = match parser.parse() { 26 | Ok(ok) => ok, 27 | Err(lexer::Error::EOF) => panic!("unexpected EOF"), 28 | Err(lexer::Error::Message(loc, msg)) => { 29 | println!( 30 | "{}:{}: {}", 31 | lexer.path_arena().borrow()[loc.file] 32 | .as_path() 33 | .display() 34 | .to_string(), 35 | loc.line, 36 | msg 37 | ); 38 | println!("{}", lexer.get_surrounding_line(loc)); 39 | panic!(); 40 | } 41 | }; 42 | 43 | println!("{:#?}", nodes); 44 | 45 | let mut codegen = codegen::Codegenerator::new(&mut parser.compound_types); 46 | for node in nodes { 47 | if let Err(codegen::Error::Message(loc, msg)) = codegen.generate(&node) { 48 | println!( 49 | "{}:{}: {}", 50 | parser.lexer.path_arena().borrow()[loc.file] 51 | .as_path() 52 | .display() 53 | .to_string(), 54 | loc.line, 55 | msg 56 | ); 57 | println!("{}", parser.lexer.get_surrounding_line(loc)); 58 | panic!(); 59 | } 60 | } 61 | 62 | if optimization { 63 | sericum::ir::mem2reg::Mem2Reg::new().run_on_module(&mut codegen.module); 64 | sericum::ir::cse::CommonSubexprElimination::new().run_on_module(&mut codegen.module); 65 | sericum::ir::licm::LoopInvariantCodeMotion::new().run_on_module(&mut codegen.module); 66 | } 67 | println!("{:?}", codegen.module); 68 | 69 | let machine_module = 70 | sericum::codegen::x64::standard_conversion_into_machine_module(codegen.module); 71 | let mut printer = sericum::codegen::x64::asm::print::MachineAsmPrinter::new(); 72 | printer.run_on_module(&machine_module); 73 | println!("{}", printer.output); 74 | 75 | assemble_and_run(&printer.output); 76 | } 77 | 78 | fn unique_file_name(extension: &str) -> String { 79 | const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ 80 | abcdefghijklmnopqrstuvwxyz\ 81 | 0123456789"; 82 | const LEN: usize = 16; 83 | let mut rng = rand::thread_rng(); 84 | let name: String = (0..LEN) 85 | .map(|_| { 86 | let idx = rng.gen_range(0, CHARSET.len()); 87 | CHARSET[idx] as char 88 | }) 89 | .collect(); 90 | format!("/tmp/{}.{}", name, extension) 91 | } 92 | 93 | fn assemble_and_run(s_target: &str) { 94 | let target_name = unique_file_name("s"); 95 | { 96 | let mut target = BufWriter::new(fs::File::create(target_name.as_str()).unwrap()); 97 | target.write_all(s_target.as_bytes()).unwrap(); 98 | } 99 | 100 | let output_name = unique_file_name("out"); 101 | let compilation = process::Command::new("clang") 102 | .args(&[ 103 | target_name.as_str(), 104 | "-o", 105 | output_name.as_str(), 106 | "-lm", 107 | "-no-pie", 108 | ]) 109 | .status() 110 | .unwrap(); 111 | assert!(compilation.success()); 112 | 113 | let execution = process::Command::new(output_name.as_str()) 114 | .status() 115 | .unwrap(); 116 | if let Some(code) = execution.code() { 117 | println!("Exit code: {:?}", code); 118 | assert!(code == 0); 119 | } else { 120 | assert!(execution.success()); 121 | } 122 | 123 | fs::remove_file(output_name).unwrap(); 124 | fs::remove_file(target_name).unwrap(); 125 | } 126 | -------------------------------------------------------------------------------- /src/codegen/aarch64/machine/validate_frame_index.rs: -------------------------------------------------------------------------------- 1 | use super::super::{frame_object::*, machine::register::*}; 2 | use super::inst::*; 3 | use crate::codegen::common::machine::{builder::*, function::*, module::MachineModule}; 4 | use crate::{ir::types::*, traits::pass::ModulePassTrait}; 5 | 6 | pub struct ValidateFrameIndex {} 7 | 8 | impl ModulePassTrait for ValidateFrameIndex { 9 | type M = MachineModule; 10 | 11 | fn name(&self) -> &'static str { 12 | "ValidateFrameIndex" 13 | } 14 | 15 | fn run_on_module(&mut self, module: &mut Self::M) { 16 | self.run_on_module(module) 17 | } 18 | } 19 | 20 | impl ValidateFrameIndex { 21 | pub fn new() -> Self { 22 | Self {} 23 | } 24 | 25 | pub fn run_on_module(&mut self, module: &mut MachineModule) { 26 | for (_, func) in &mut module.functions { 27 | if func.is_internal { 28 | continue; 29 | } 30 | self.run_on_function(&module.types, func); 31 | } 32 | } 33 | 34 | // TODO: Refine 35 | pub fn run_on_function(&mut self, tys: &Types, f: &mut MachineFunction) { 36 | let mut frame_objects = FrameObjectsInfo::new(tys, f); 37 | frame_objects.callee_saved_regs_byte += 8; /*s1*/ 38 | 39 | let s0 = f.regs_info.get_phys_reg(GPR::S0); 40 | let s1 = f.regs_info.get_phys_reg(GPR::S1); 41 | let mut mem = vec![]; 42 | let mut add = vec![]; 43 | 44 | for (_, block) in f.body.basic_blocks.id_and_block() { 45 | for &inst_id in &*block.iseq_ref() { 46 | let inst = &mut f.body.inst_arena[inst_id]; 47 | for operand in &mut inst.operand { 48 | match operand { 49 | MachineOperand::Mem(MachineMemOperand::FiReg(fi, _)) 50 | if !bits_within(frame_objects.offset(fi.idx).unwrap(), 12) => 51 | { 52 | mem.push((inst_id, *fi)); 53 | block.liveness_ref_mut().add_phys_def(s1.as_phys_reg()); 54 | *operand = MachineOperand::Mem(MachineMemOperand::ImmReg(0, s1)); 55 | } 56 | MachineOperand::FrameIndex(fi) 57 | if !bits_within(frame_objects.offset(fi.idx).unwrap(), 12) => 58 | { 59 | add.push((inst_id, *fi)); 60 | block.liveness_ref_mut().add_phys_def(s1.as_phys_reg()); 61 | *operand = MachineOperand::Register(s1); 62 | assert!(inst.opcode == MachineOpcode::ADDI); 63 | inst.opcode = MachineOpcode::ADD; 64 | } 65 | _ => {} 66 | } 67 | } 68 | } 69 | } 70 | 71 | for (inst_id, fi) in add { 72 | let mut builder = Builder::new(f); 73 | builder.set_insert_point_before_inst(inst_id); 74 | let li = MachineInst::new_simple( 75 | MachineOpcode::LI, 76 | vec![MachineOperand::FrameIndex(fi)], 77 | builder.get_cur_bb().unwrap(), 78 | ) 79 | .with_def(vec![s1]); 80 | builder.insert(li); 81 | } 82 | 83 | for (inst_id, fi) in mem { 84 | let mut builder = Builder::new(f); 85 | builder.set_insert_point_before_inst(inst_id); 86 | let li = MachineInst::new_simple( 87 | MachineOpcode::LI, 88 | vec![MachineOperand::FrameIndex(fi)], 89 | builder.get_cur_bb().unwrap(), 90 | ) 91 | .with_def(vec![s1]); 92 | let add = MachineInst::new_simple( 93 | MachineOpcode::ADD, 94 | vec![MachineOperand::Register(s0), MachineOperand::Register(s1)], 95 | builder.get_cur_bb().unwrap(), 96 | ) 97 | .with_def(vec![s1]); 98 | builder.insert(li); 99 | builder.insert(add); 100 | } 101 | } 102 | } 103 | 104 | fn bits_within(x: i32, y: u32) -> bool { 105 | (x << (32 - y)) >> (32 - y) == x 106 | } 107 | -------------------------------------------------------------------------------- /src/codegen/riscv64/machine/validate_frame_index.rs: -------------------------------------------------------------------------------- 1 | use super::super::{frame_object::*, machine::register::*}; 2 | use super::inst::*; 3 | use crate::codegen::common::machine::{builder::*, function::*, module::MachineModule}; 4 | use crate::{ir::types::*, traits::pass::ModulePassTrait}; 5 | 6 | pub struct ValidateFrameIndex {} 7 | 8 | impl ModulePassTrait for ValidateFrameIndex { 9 | type M = MachineModule; 10 | 11 | fn name(&self) -> &'static str { 12 | "ValidateFrameIndex" 13 | } 14 | 15 | fn run_on_module(&mut self, module: &mut Self::M) { 16 | self.run_on_module(module) 17 | } 18 | } 19 | 20 | impl ValidateFrameIndex { 21 | pub fn new() -> Self { 22 | Self {} 23 | } 24 | 25 | pub fn run_on_module(&mut self, module: &mut MachineModule) { 26 | for (_, func) in &mut module.functions { 27 | if func.is_internal { 28 | continue; 29 | } 30 | self.run_on_function(&module.types, func); 31 | } 32 | } 33 | 34 | // TODO: Refine 35 | pub fn run_on_function(&mut self, tys: &Types, f: &mut MachineFunction) { 36 | let mut frame_objects = FrameObjectsInfo::new(tys, f); 37 | frame_objects.callee_saved_regs_byte += 8; /*s1*/ 38 | 39 | let s0 = f.regs_info.get_phys_reg(GPR::S0); 40 | let s1 = f.regs_info.get_phys_reg(GPR::S1); 41 | let mut mem = vec![]; 42 | let mut add = vec![]; 43 | 44 | for (_, block) in f.body.basic_blocks.id_and_block() { 45 | for &inst_id in &*block.iseq_ref() { 46 | let inst = &mut f.body.inst_arena[inst_id]; 47 | for operand in &mut inst.operand { 48 | match operand { 49 | MachineOperand::Mem(MachineMemOperand::FiReg(fi, _)) 50 | if !bits_within(frame_objects.offset(fi.idx).unwrap(), 12) => 51 | { 52 | mem.push((inst_id, *fi)); 53 | block.liveness_ref_mut().add_phys_def(s1.as_phys_reg()); 54 | *operand = MachineOperand::Mem(MachineMemOperand::ImmReg(0, s1)); 55 | } 56 | MachineOperand::FrameIndex(fi) 57 | if !bits_within(frame_objects.offset(fi.idx).unwrap(), 12) => 58 | { 59 | add.push((inst_id, *fi)); 60 | block.liveness_ref_mut().add_phys_def(s1.as_phys_reg()); 61 | *operand = MachineOperand::Register(s1); 62 | assert!(inst.opcode == MachineOpcode::ADDI); 63 | inst.opcode = MachineOpcode::ADD; 64 | } 65 | _ => {} 66 | } 67 | } 68 | } 69 | } 70 | 71 | for (inst_id, fi) in add { 72 | let mut builder = Builder::new(f); 73 | builder.set_insert_point_before_inst(inst_id); 74 | let li = MachineInst::new_simple( 75 | MachineOpcode::LI, 76 | vec![MachineOperand::FrameIndex(fi)], 77 | builder.get_cur_bb().unwrap(), 78 | ) 79 | .with_def(vec![s1]); 80 | builder.insert(li); 81 | } 82 | 83 | for (inst_id, fi) in mem { 84 | let mut builder = Builder::new(f); 85 | builder.set_insert_point_before_inst(inst_id); 86 | let li = MachineInst::new_simple( 87 | MachineOpcode::LI, 88 | vec![MachineOperand::FrameIndex(fi)], 89 | builder.get_cur_bb().unwrap(), 90 | ) 91 | .with_def(vec![s1]); 92 | let add = MachineInst::new_simple( 93 | MachineOpcode::ADD, 94 | vec![MachineOperand::Register(s0), MachineOperand::Register(s1)], 95 | builder.get_cur_bb().unwrap(), 96 | ) 97 | .with_def(vec![s1]); 98 | builder.insert(li); 99 | builder.insert(add); 100 | } 101 | } 102 | } 103 | 104 | fn bits_within(x: i32, y: u32) -> bool { 105 | (x << (32 - y)) >> (32 - y) == x 106 | } 107 | -------------------------------------------------------------------------------- /src/codegen/riscv64/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod asm; 2 | pub mod dag; 3 | pub mod exec; 4 | pub mod frame_object; 5 | pub mod machine; 6 | 7 | use crate::{ 8 | codegen::common::{ 9 | dag::{combine, convert}, 10 | machine::{branch_folding, module::MachineModule, phi_elimination}, 11 | }, 12 | ir, 13 | ir::module::Module, 14 | ir::types::*, 15 | traits::pass::ModulePassManager, 16 | }; 17 | 18 | impl TypeSize for Type { 19 | fn size_in_byte(&self, tys: &Types) -> usize { 20 | match self { 21 | Type::i1 => 1, 22 | Type::i8 => 1, 23 | Type::i32 => 4, 24 | Type::i64 => 8, 25 | Type::f64 => 8, 26 | Type::Array(id) => tys.base.borrow().non_primitive_types[*id] 27 | .as_array() 28 | .size_in_byte(tys), 29 | Type::Struct(id) => tys.base.borrow().non_primitive_types[*id] 30 | .as_struct() 31 | .size_in_byte(tys), 32 | Type::Pointer(_) => 8, 33 | Type::Function(_) => unimplemented!(), 34 | Type::Void => 0, 35 | } 36 | } 37 | 38 | fn size_in_bits(&self, tys: &Types) -> usize { 39 | self.size_in_byte(tys) * 8 40 | } 41 | 42 | fn align_in_byte(&self, tys: &Types) -> usize { 43 | match self { 44 | Type::i1 => 1, 45 | Type::i8 => 1, 46 | Type::i32 => 4, 47 | Type::i64 => 8, 48 | Type::f64 => 8, 49 | Type::Array(id) => tys.base.borrow().non_primitive_types[*id] 50 | .as_array() 51 | .align_in_byte(tys), 52 | Type::Struct(id) => tys.base.borrow().non_primitive_types[*id] 53 | .as_struct() 54 | .align_in_byte(tys), 55 | Type::Pointer(_) => 8, 56 | Type::Function(_) => unimplemented!(), 57 | Type::Void => 0, 58 | } 59 | } 60 | } 61 | 62 | const MAX_ALIGN: usize = 16; 63 | 64 | impl TypeSize for ArrayType { 65 | fn size_in_byte(&self, tys: &Types) -> usize { 66 | self.elem_ty.size_in_byte(tys) * self.len 67 | } 68 | 69 | fn size_in_bits(&self, tys: &Types) -> usize { 70 | self.size_in_byte(tys) * 8 71 | } 72 | 73 | fn align_in_byte(&self, tys: &Types) -> usize { 74 | let size = self.size_in_byte(tys); 75 | let align = self.elem_ty.align_in_byte(tys); 76 | if size > MAX_ALIGN { 77 | MAX_ALIGN 78 | } else { 79 | align 80 | } 81 | } 82 | } 83 | 84 | impl TypeSize for StructType { 85 | fn size_in_byte(&self, _tys: &Types) -> usize { 86 | self.size() 87 | } 88 | 89 | fn size_in_bits(&self, tys: &Types) -> usize { 90 | self.size_in_byte(tys) * 8 91 | } 92 | 93 | fn align_in_byte(&self, _tys: &Types) -> usize { 94 | self.align() 95 | } 96 | } 97 | 98 | pub fn standard_conversion_into_machine_module(module: &mut Module) -> MachineModule { 99 | ir::merge_ret::MergeReturns::new().run_on_module(module); 100 | 101 | let mut dag_module = convert::ConvertToDAGModule::new(module).run(); 102 | 103 | let mut pass_mgr = ModulePassManager::new(); 104 | pass_mgr.add_pass(combine::Combine::new()); 105 | pass_mgr.add_pass(dag::legalize::Legalize::new()); 106 | pass_mgr.add_pass(dag::isel::MISelector::new()); 107 | pass_mgr.run_on_module(&mut dag_module); 108 | // println!("{:?}", dag_module); 109 | 110 | let mut machine_module = dag::mc_convert::convert_module(dag_module); 111 | 112 | let mut pass_mgr = ModulePassManager::new(); 113 | pass_mgr.add_pass(phi_elimination::PhiElimination::new()); 114 | // pass_mgr.add_pass(machine::two_addr::TwoAddressConverter::new()); 115 | pass_mgr.add_pass(machine::regalloc::RegisterAllocator::new()); 116 | pass_mgr.add_pass(branch_folding::BranchFolding::new()); 117 | pass_mgr.add_pass(machine::validate_frame_index::ValidateFrameIndex::new()); 118 | pass_mgr.add_pass(machine::pro_epi_inserter::PrologueEpilogueInserter::new()); 119 | pass_mgr.add_pass(machine::replace_copy::ReplaceCopyWithProperMInst::new()); 120 | // pass_mgr.add_pass(machine::replace_data::ReplaceConstFPWithMemoryRef::new()); 121 | pass_mgr.run_on_module(&mut machine_module); 122 | 123 | machine_module 124 | } 125 | -------------------------------------------------------------------------------- /src/ir/liveness.rs: -------------------------------------------------------------------------------- 1 | use crate::ir::{basic_block::*, function::*, module::*, opcode::*, types::*}; 2 | use rustc_hash::FxHashMap; 3 | 4 | pub struct IRLivenessAnalyzer<'a> { 5 | module: &'a mut Module, 6 | } 7 | 8 | pub struct LivenessAnalyzerOnFunction<'a> { 9 | func: &'a Function, 10 | liveness: FxHashMap, 11 | } 12 | 13 | impl<'a> IRLivenessAnalyzer<'a> { 14 | pub fn new(module: &'a mut Module) -> Self { 15 | Self { module } 16 | } 17 | 18 | pub fn analyze(&mut self) { 19 | for (_, f) in &mut self.module.functions { 20 | if f.is_internal { 21 | continue; 22 | } 23 | 24 | let liveness = LivenessAnalyzerOnFunction::new(f).analyze(); 25 | f.basic_blocks.liveness = liveness; 26 | } 27 | } 28 | } 29 | 30 | impl<'a> LivenessAnalyzerOnFunction<'a> { 31 | pub fn new(func: &'a Function) -> Self { 32 | Self { 33 | func, 34 | liveness: FxHashMap::default(), 35 | } 36 | } 37 | 38 | pub fn analyze(mut self) -> FxHashMap { 39 | self.set_def(); 40 | self.visit(); 41 | self.liveness 42 | } 43 | 44 | pub fn set_def(&mut self) { 45 | for (id, block) in &self.func.basic_blocks.arena { 46 | let mut liveness = LivenessInfo::new(); 47 | 48 | for &inst_id in &*block.iseq_ref() { 49 | let inst = &self.func.inst_table[inst_id]; 50 | 51 | if inst.opcode == Opcode::Call && self.func.get_return_type() != Type::Void { 52 | liveness.def.insert(inst_id); 53 | continue; 54 | } 55 | 56 | if inst.opcode.returns_value() { 57 | liveness.def.insert(inst_id); 58 | } 59 | } 60 | 61 | self.liveness.insert(id, liveness); 62 | } 63 | } 64 | 65 | pub fn visit(&mut self) { 66 | for (bb_id, bb) in &self.func.basic_blocks.arena { 67 | for &inst_id in &*bb.iseq_ref() { 68 | let inst = &self.func.inst_table[inst_id]; 69 | self.visit_operands(bb_id, &inst.operand, inst.opcode == Opcode::Phi); 70 | } 71 | } 72 | } 73 | 74 | pub fn visit_operands(&mut self, cur_bb: BasicBlockId, operand: &InstOperand, is_phi: bool) { 75 | for (i, v) in operand.args().iter().enumerate() { 76 | some_then!( 77 | id, 78 | v.get_inst_id(), 79 | self.propagate( 80 | cur_bb, 81 | id, 82 | if is_phi { 83 | Some(operand.blocks()[i]) 84 | } else { 85 | None 86 | }, 87 | is_phi 88 | ) 89 | ); 90 | } 91 | } 92 | 93 | pub fn propagate( 94 | &mut self, 95 | bb_id: BasicBlockId, 96 | inst_id: InstructionId, 97 | phi_incoming: Option, 98 | phi: bool, 99 | ) { 100 | let bb = &self.func.basic_blocks.arena[bb_id]; 101 | 102 | { 103 | let liveness = self.liveness.get_mut(&bb_id).unwrap(); 104 | 105 | if liveness.def.contains(&inst_id) { 106 | return; 107 | } 108 | 109 | if !liveness.live_in.insert(inst_id) && !phi { 110 | // live_in already had the value inst_id 111 | return; 112 | } 113 | } 114 | 115 | if let Some(phi_incoming) = phi_incoming { 116 | self.propagate_if_necessary(phi_incoming, inst_id, phi); 117 | } else { 118 | for pred_id in &bb.pred { 119 | self.propagate_if_necessary(*pred_id, inst_id, phi); 120 | } 121 | } 122 | } 123 | 124 | fn propagate_if_necessary(&mut self, pred_id: BasicBlockId, inst_id: InstructionId, phi: bool) { 125 | if self 126 | .liveness 127 | .get_mut(&pred_id) 128 | .unwrap() 129 | .live_out 130 | .insert(inst_id) 131 | { 132 | // live_out didn't have the value inst_id 133 | self.propagate(pred_id, inst_id, None, phi); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/codegen/aarch64/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod asm; 2 | pub mod dag; 3 | pub mod exec; 4 | pub mod frame_object; 5 | pub mod machine; 6 | 7 | use crate::{ 8 | codegen::common::{ 9 | dag::{combine, convert}, 10 | machine::{branch_folding, module::MachineModule, phi_elimination}, 11 | }, 12 | ir, 13 | ir::module::Module, 14 | ir::types::*, 15 | traits::pass::ModulePassManager, 16 | }; 17 | 18 | impl TypeSize for Type { 19 | fn size_in_byte(&self, tys: &Types) -> usize { 20 | match self { 21 | Type::i1 => 1, 22 | Type::i8 => 1, 23 | Type::i32 => 4, 24 | Type::i64 => 8, 25 | Type::f64 => 8, 26 | Type::Array(id) => tys.compound_ty(*id).as_array().size_in_byte(tys), 27 | Type::Struct(id) => tys.compound_ty(*id).as_struct().size_in_byte(tys), 28 | Type::Pointer(_) => 8, 29 | Type::Function(_) => unimplemented!(), 30 | Type::Void => 0, 31 | } 32 | } 33 | 34 | fn size_in_bits(&self, tys: &Types) -> usize { 35 | self.size_in_byte(tys) * 8 36 | } 37 | 38 | fn align_in_byte(&self, tys: &Types) -> usize { 39 | match self { 40 | Type::i1 => 1, 41 | Type::i8 => 1, 42 | Type::i32 => 4, 43 | Type::i64 => 8, 44 | Type::f64 => 8, 45 | Type::Array(id) => tys.compound_ty(*id).as_array().align_in_byte(tys), 46 | Type::Struct(id) => tys.compound_ty(*id).as_struct().align_in_byte(tys), 47 | Type::Pointer(_) => 8, 48 | Type::Function(_) => unimplemented!(), 49 | Type::Void => 0, 50 | } 51 | } 52 | } 53 | 54 | const MAX_ALIGN: usize = 16; 55 | 56 | impl TypeSize for ArrayType { 57 | fn size_in_byte(&self, tys: &Types) -> usize { 58 | self.elem_ty.size_in_byte(tys) * self.len 59 | } 60 | 61 | fn size_in_bits(&self, tys: &Types) -> usize { 62 | self.size_in_byte(tys) * 8 63 | } 64 | 65 | fn align_in_byte(&self, tys: &Types) -> usize { 66 | let size = self.size_in_byte(tys); 67 | let align = self.elem_ty.align_in_byte(tys); 68 | if size > MAX_ALIGN { 69 | MAX_ALIGN 70 | } else { 71 | align 72 | } 73 | } 74 | } 75 | 76 | impl TypeSize for StructType { 77 | fn size_in_byte(&self, _tys: &Types) -> usize { 78 | self.size() 79 | } 80 | 81 | fn size_in_bits(&self, tys: &Types) -> usize { 82 | self.size_in_byte(tys) * 8 83 | } 84 | 85 | fn align_in_byte(&self, _tys: &Types) -> usize { 86 | self.align() 87 | } 88 | } 89 | 90 | pub fn standard_conversion_into_machine_module(mut module: Module) -> MachineModule { 91 | ir::dce::DeadCodeElimination::new().run_on_module(&mut module); 92 | ir::merge_ret::MergeReturns::new().run_on_module(&mut module); 93 | // ir::const_folding::ConstantFolding::new().run_on_module(&mut module); 94 | // ir::inst_combine::InstructionCombine::new().run_on_module(&mut module); 95 | ir::codegen_prepare::CodegenPrepare::new().run_on_module(&mut module); 96 | 97 | let mut module = crate::codegen::common::dag::convert::convert_module_to_dag_module(module); 98 | crate::codegen::common::dag::combine::run(&mut module); 99 | // println!("Initial DAG:\n{:?}", module); 100 | crate::codegen::arch::dag::legalize::run(&mut module); 101 | crate::codegen::arch::dag::isel::run(&mut module); 102 | // println!("Selected DAG:\n{:?}", module); 103 | 104 | let mut machine_module = crate::codegen::common::dag::mc_convert::convert_module(module); 105 | 106 | let mut pass_mgr = ModulePassManager::new(); 107 | pass_mgr.add_pass(phi_elimination::PhiElimination::new()); 108 | // pass_mgr.add_pass(machine::two_addr::TwoAddressConverter::new()); 109 | pass_mgr.add_pass(machine::regalloc::RegisterAllocator::new()); 110 | pass_mgr.add_pass(branch_folding::BranchFolding::new()); 111 | // pass_mgr.add_pass(machine::validate_frame_index::ValidateFrameIndex::new()); 112 | pass_mgr.add_pass(machine::pro_epi_inserter::PrologueEpilogueInserter::new()); 113 | pass_mgr.add_pass(machine::replace_copy::ReplaceCopyWithProperMInst::new()); 114 | // pass_mgr.add_pass(machine::replace_data::ReplaceConstFPWithMemoryRef::new()); 115 | pass_mgr.run_on_module(&mut machine_module); 116 | 117 | machine_module 118 | } 119 | -------------------------------------------------------------------------------- /src/codegen/x64/dag/convert.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::{ 2 | arch::machine::{abi::SystemV, register::*}, 3 | common::{ 4 | machine::calling_conv::{ArgumentRegisterOrder, CallingConv}, 5 | dag::{ 6 | convert::BlockConversionContext, 7 | node::{IRNode, IROpcode}, 8 | }, 9 | }, 10 | }; 11 | 12 | pub fn copy_reg_args<'a>(ctx: &mut BlockConversionContext<'a>) { 13 | let abi = SystemV::new(); 14 | let mut arg_regs_order = ArgumentRegisterOrder::new(&abi); 15 | 16 | for i in 0..ctx.func.get_params_len() { 17 | let byval = ctx.func.get_param_attr(i).map_or(false, |attr| attr.byval); 18 | if let Some(ty) = ctx.func.get_param_type(i) { 19 | if byval { 20 | let base = &ctx.func.types.base.borrow(); 21 | let struct_ty = base 22 | .as_struct_ty(base.get_element_ty(ty, None).unwrap()) 23 | .unwrap(); 24 | let sz = struct_ty.size(); 25 | let regs_classes = SystemV::reg_classes_used_for_passing_byval(struct_ty); 26 | if sz <= 16 && arg_regs_order.regs_available_for(®s_classes) { 27 | for rc in regs_classes { 28 | assert!(arg_regs_order.next(rc).is_some()) 29 | } 30 | } 31 | continue; 32 | } 33 | 34 | let arg_reg_class = match ty2rc(&ty) { 35 | Some(rc) => rc, 36 | None => continue, 37 | }; 38 | let arg_reg = match arg_regs_order.next(arg_reg_class) { 39 | Some(reg) => reg, 40 | None => continue, 41 | }; 42 | let arg_reg = ctx.node(ctx.regs.get_phys_reg(arg_reg).into()); 43 | let vreg = ctx.node(ctx.regs.new_virt_reg(arg_reg_class).into()); 44 | let copy = ctx.node( 45 | IRNode::new(IROpcode::CopyToReg) 46 | .args(vec![vreg, arg_reg]) 47 | .ty(ty) 48 | .into(), 49 | ); 50 | ctx.make_chain(copy); 51 | ctx.arg_regs.insert(i, vreg); 52 | } 53 | } 54 | } 55 | 56 | // impl<'a> ConvertToDAGNode<'a> { 57 | // // TODO: Refine 58 | // pub fn copy_reg_args(&mut self) { 59 | // let abi = SystemV::new(); 60 | // let mut arg_regs_order = ArgumentRegisterOrder::new(&abi); 61 | // 62 | // for i in 0..self.func.get_params_len() { 63 | // let byval = self.func.get_param_attr(i).map_or(false, |attr| attr.byval); 64 | // if let Some(ty) = self.func.get_param_type(i) { 65 | // if byval { 66 | // let base = &self.func.types.base.borrow(); 67 | // let struct_ty = base 68 | // .as_struct_ty(base.get_element_ty(ty, None).unwrap()) 69 | // .unwrap(); 70 | // let sz = struct_ty.size(); 71 | // let regs_classes = SystemV::reg_classes_used_for_passing_byval(struct_ty); 72 | // if sz <= 16 && arg_regs_order.regs_available_for(®s_classes) { 73 | // for rc in regs_classes { 74 | // assert!(arg_regs_order.next(rc).is_some()) 75 | // } 76 | // } 77 | // continue; 78 | // } 79 | // 80 | // let arg_reg_class = match ty2rc(&ty) { 81 | // Some(rc) => rc, 82 | // None => continue, 83 | // }; 84 | // let arg_reg = match arg_regs_order.next(arg_reg_class) { 85 | // Some(reg) => reg, 86 | // None => continue, 87 | // }; 88 | // let arg_reg = self.alloc_node(DAGNode::new_phys_reg(&self.regs_info, arg_reg)); 89 | // let vreg = self.regs_info.new_virt_reg(arg_reg_class); 90 | // let vreg = self.alloc_node( 91 | // DAGNode::new(NodeKind::Operand(OperandNodeKind::Register(vreg))).with_ty(ty), 92 | // ); 93 | // let copy = self.alloc_node( 94 | // DAGNode::new(NodeKind::IR(IRNodeKind::CopyToReg)) 95 | // .with_operand(vec![vreg, arg_reg]) 96 | // .with_ty(ty), 97 | // ); 98 | // self.make_chain(copy); 99 | // self.arg_regs.insert(i, vreg); 100 | // } 101 | // } 102 | // } 103 | // } 104 | -------------------------------------------------------------------------------- /sericumcc/src/ast.rs: -------------------------------------------------------------------------------- 1 | use super::token::SourceLoc; 2 | use super::types::{StorageClass, Type}; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct AST { 6 | pub kind: Kind, 7 | pub loc: SourceLoc, 8 | } 9 | 10 | #[derive(Debug, Clone)] 11 | pub enum Kind { 12 | Block(Vec), 13 | FuncDef { 14 | ty: Type, 15 | param_names: Vec, 16 | name: String, 17 | body: Box, 18 | }, 19 | Int { 20 | n: i64, 21 | bits: u8, 22 | }, 23 | Float(f64), 24 | Char(char), 25 | String(String), 26 | ConstArray(Type, Vec), 27 | Typedef(Type, String), 28 | UnaryOp(UnaryOp, Box), 29 | BinaryOp(BinaryOp, Box, Box), 30 | TernaryOp(Box, Box, Box), 31 | Assign { 32 | dst: Box, 33 | src: Box, 34 | }, 35 | Load(Box), 36 | TypeCast(Box, Type), 37 | FieldRef(Box, String), 38 | Variable(Type, String), 39 | VariableDecl(Type, String, StorageClass, Option>), 40 | FuncCall(Box, Vec), 41 | If { 42 | cond: Box, 43 | then_: Box, 44 | else_: Box, 45 | }, 46 | While { 47 | cond: Box, 48 | body: Box, 49 | }, 50 | For { 51 | init: Box, 52 | cond: Option>, 53 | step: Box, 54 | body: Box, 55 | }, 56 | Return(Option>), 57 | } 58 | 59 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 60 | pub enum UnaryOp { 61 | LogicalNot, 62 | BitwiseNot, 63 | Minus, 64 | // PreInc, 65 | // PreDec, 66 | PostInc, 67 | PostDec, 68 | Deref, 69 | Addr, 70 | Sizeof, 71 | } 72 | 73 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 74 | pub enum BinaryOp { 75 | Add, 76 | Sub, 77 | Mul, 78 | Div, 79 | Rem, 80 | And, 81 | Or, 82 | Xor, 83 | LogicalAnd, 84 | LogicalOr, 85 | Eq, 86 | Ne, 87 | Lt, 88 | Gt, 89 | Le, 90 | Ge, 91 | Shl, 92 | Shr, 93 | Comma, 94 | // Assign, 95 | } 96 | 97 | impl AST { 98 | pub fn new(kind: Kind, loc: SourceLoc) -> Self { 99 | Self { kind, loc } 100 | } 101 | 102 | pub fn eval(&self) -> Option { 103 | Some(match self.kind { 104 | Kind::Int { n, .. } => n, 105 | Kind::UnaryOp(UnaryOp::LogicalNot, ref e) => (e.eval()? == 0) as i64, 106 | Kind::UnaryOp(UnaryOp::BitwiseNot, ref e) => !e.eval()?, 107 | Kind::UnaryOp(UnaryOp::Minus, ref e) => -e.eval()?, 108 | Kind::UnaryOp(UnaryOp::PostInc, ref e) => e.eval()? + 1, 109 | Kind::UnaryOp(UnaryOp::PostDec, ref e) => e.eval()? - 1, 110 | Kind::UnaryOp(UnaryOp::Deref, ref e) => e.eval()?, 111 | Kind::UnaryOp(UnaryOp::Addr, ref e) => e.eval()?, 112 | Kind::BinaryOp(BinaryOp::Add, ref l, ref r) => l.eval()? + r.eval()?, 113 | Kind::BinaryOp(BinaryOp::Sub, ref l, ref r) => l.eval()? - r.eval()?, 114 | Kind::BinaryOp(BinaryOp::Mul, ref l, ref r) => l.eval()? * r.eval()?, 115 | Kind::BinaryOp(BinaryOp::Div, ref l, ref r) => l.eval()? / r.eval()?, 116 | Kind::BinaryOp(BinaryOp::Rem, ref l, ref r) => l.eval()? % r.eval()?, 117 | Kind::BinaryOp(BinaryOp::And, ref l, ref r) => l.eval()? & r.eval()?, 118 | Kind::BinaryOp(BinaryOp::Or, ref l, ref r) => l.eval()? | r.eval()?, 119 | Kind::BinaryOp(BinaryOp::Xor, ref l, ref r) => l.eval()? ^ r.eval()?, 120 | Kind::BinaryOp(BinaryOp::LogicalAnd, ref l, ref r) => l.eval()? & r.eval()?, 121 | Kind::BinaryOp(BinaryOp::LogicalOr, ref l, ref r) => l.eval()? | r.eval()?, 122 | Kind::BinaryOp(BinaryOp::Eq, ref l, ref r) => (l.eval()? == r.eval()?) as i64, 123 | Kind::BinaryOp(BinaryOp::Ne, ref l, ref r) => (l.eval()? != r.eval()?) as i64, 124 | Kind::BinaryOp(BinaryOp::Lt, ref l, ref r) => (l.eval()? < r.eval()?) as i64, 125 | Kind::BinaryOp(BinaryOp::Gt, ref l, ref r) => (l.eval()? > r.eval()?) as i64, 126 | Kind::BinaryOp(BinaryOp::Le, ref l, ref r) => (l.eval()? <= r.eval()?) as i64, 127 | Kind::BinaryOp(BinaryOp::Ge, ref l, ref r) => (l.eval()? >= r.eval()?) as i64, 128 | Kind::BinaryOp(BinaryOp::Shl, ref l, ref r) => l.eval()? << r.eval()?, 129 | Kind::BinaryOp(BinaryOp::Shr, ref l, ref r) => l.eval()? >> r.eval()?, 130 | Kind::BinaryOp(BinaryOp::Comma, ref _l, ref r) => r.eval()?, 131 | Kind::TernaryOp(ref c, ref l, ref r) => { 132 | if c.eval()? != 0 { 133 | l.eval()? 134 | } else { 135 | r.eval()? 136 | } 137 | } 138 | _ => return None, 139 | }) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/codegen/common/dag/combine.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::common::dag::{ 2 | function::DAGFunction, 3 | module::DAGModule, 4 | node::{IRNode, IROpcode, NodeId}, 5 | pat_match::{ 6 | any, any_block, any_cc, any_i32_imm, any_imm, any_reg, i32_imm, inst_select, ir, not, 7 | null_imm, MatchContext, Pat, ReplacedNodeMap, 8 | }, 9 | }; 10 | use defs::node_gen; 11 | 12 | pub fn run(module: &mut DAGModule) { 13 | for (_, func) in &mut module.functions { 14 | if func.is_internal { 15 | continue; 16 | } 17 | run_on_function(func); 18 | } 19 | } 20 | 21 | fn run_on_function(func: &mut DAGFunction) { 22 | let args = vec![ 23 | any_cc().named("cc").into(), 24 | (any_imm() | any_reg()).named("lhs").into(), 25 | (any_imm() | any_reg()).named("rhs").into(), 26 | ]; 27 | let brcond: Pat = ir(IROpcode::BrCond) 28 | .args(vec![ 29 | (ir(IROpcode::Setcc).args(args.clone()) | ir(IROpcode::FCmp).args(args.clone())) 30 | .named("setcc") 31 | .into(), 32 | any_block().named("dst").into(), 33 | ]) 34 | .generate(|m, c| { 35 | c.arena.alloc( 36 | IRNode::new(match c.arena[m["setcc"]].as_ir().opcode { 37 | IROpcode::Setcc => IROpcode::Brcc, 38 | IROpcode::FCmp => IROpcode::FPBrcc, 39 | _ => unreachable!(), 40 | }) 41 | .args(vec![m["cc"], m["lhs"], m["rhs"], m["dst"]]) 42 | .into(), 43 | ) 44 | }) 45 | .into(); 46 | let setcc: Pat = ir(IROpcode::Setcc) 47 | .named("setcc") 48 | .args(vec![ 49 | any_cc().named("cc").into(), 50 | any_imm().into(), 51 | any_reg().into(), 52 | ]) 53 | .generate(|m, c| { 54 | let cc = c.arena[m["cc"]].as_operand_mut().as_cc_mut(); 55 | *cc = cc.flip(); 56 | c.arena[m["setcc"]].as_ir_mut().args.swap(1, 2); 57 | m["setcc"] 58 | }) 59 | .into(); 60 | 61 | let add: Pat = ( 62 | // (IMM + any) -> (any + IMM) 63 | ir(IROpcode::Add) 64 | .named("n") 65 | .args(vec![any_imm().into(), any()]) 66 | .generate(|m, c| { 67 | c.arena[m["n"]].as_ir_mut().args.swap(0, 1); 68 | m["n"] 69 | }) 70 | // (A + 0) -> A 71 | | ir(IROpcode::Add) 72 | .args(vec![any().named("l"), null_imm().into()]) 73 | .generate(|m, _| m["l"]) 74 | // ((node + C1) + C2) -> (node + C3) 75 | | ir(IROpcode::Add).named("add").args(vec![ 76 | ir(IROpcode::Add).args(vec![ 77 | not().any_i32_imm().named("n").into(), 78 | any_i32_imm().named("c1").into(), 79 | ]).into(), 80 | any_i32_imm().named("c2").into() 81 | ]).generate(|m, c|{ 82 | let c1 = c.arena[m["c1"]].as_operand().as_imm().as_i32(); 83 | let c2 = c.arena[m["c2"]].as_operand().as_imm().as_i32(); 84 | let c3 = c.arena.alloc((c1 + c2).into()); 85 | let ty = c.arena[m["add"]].as_ir().ty; 86 | node_gen!((IR.Add.(ty) m["n"], c3)) 87 | }).into() 88 | ) 89 | .into(); 90 | 91 | let mul: Pat = (ir(IROpcode::Mul) 92 | .named("n") 93 | .args(vec![any_imm().into(), any()]) 94 | .generate(|m, c| { 95 | c.arena[m["n"]].as_ir_mut().args.swap(0, 1); 96 | m["n"] 97 | }) 98 | | ir(IROpcode::Mul) 99 | .args(vec![any(), i32_imm(0).named("0").into()]) 100 | .generate(|m, _c| m["0"]) 101 | | ir(IROpcode::Mul) 102 | .args(vec![any().named("n"), i32_imm(1).into()]) 103 | .generate(|m, _c| m["n"]) 104 | .into()) 105 | .into(); 106 | 107 | let pats = vec![brcond, setcc, add, mul]; 108 | 109 | let mut replaced = ReplacedNodeMap::default(); 110 | for &id in &func.dag_basic_blocks { 111 | let block = &func.dag_basic_block_arena[id]; 112 | let a = select_node( 113 | &mut MatchContext { 114 | arena: &mut func.node_arena, 115 | regs: &func.regs, 116 | }, 117 | &mut replaced, 118 | &pats, 119 | block.entry.unwrap(), 120 | ); 121 | assert_eq!(block.entry.unwrap(), a); 122 | } 123 | } 124 | 125 | fn select_node<'a>( 126 | ctx: &mut MatchContext<'a>, 127 | replaced: &mut ReplacedNodeMap, 128 | pats: &[Pat], 129 | id: NodeId, 130 | ) -> NodeId { 131 | let new = inst_select(replaced, ctx, id, pats); 132 | 133 | if let Some(next) = ctx.arena[id].next() { 134 | let next = select_node(ctx, replaced, pats, next); 135 | *ctx.arena[new].next_mut() = Some(next); 136 | } 137 | 138 | new 139 | } 140 | -------------------------------------------------------------------------------- /src/codegen/x64/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod asm; 2 | pub mod dag; 3 | pub mod exec; 4 | pub mod frame_object; 5 | pub mod machine; 6 | 7 | use crate::{ 8 | codegen::common::machine::{ 9 | branch_folding, eliminate_fi, module::MachineModule, phi_elimination, 10 | }, 11 | ir, 12 | ir::module::Module, 13 | ir::types::*, 14 | traits::pass::ModulePassManager, 15 | }; 16 | 17 | impl TypeSize for Type { 18 | fn size_in_byte(&self, tys: &Types) -> usize { 19 | match self { 20 | Type::i1 => 1, 21 | Type::i8 => 1, 22 | Type::i32 => 4, 23 | Type::i64 => 8, 24 | Type::f64 => 8, 25 | Type::Array(id) => tys.compound_ty(*id).as_array().size_in_byte(tys), 26 | Type::Struct(id) => tys.compound_ty(*id).as_struct().size_in_byte(tys), 27 | Type::Pointer(_) => 8, 28 | Type::Function(_) => unimplemented!(), 29 | Type::Void => 0, 30 | } 31 | } 32 | 33 | fn size_in_bits(&self, tys: &Types) -> usize { 34 | self.size_in_byte(tys) * 8 35 | } 36 | 37 | fn align_in_byte(&self, tys: &Types) -> usize { 38 | match self { 39 | Type::i1 => 1, 40 | Type::i8 => 1, 41 | Type::i32 => 4, 42 | Type::i64 => 8, 43 | Type::f64 => 8, 44 | Type::Array(id) => tys.compound_ty(*id).as_array().align_in_byte(tys), 45 | Type::Struct(id) => tys.compound_ty(*id).as_struct().align_in_byte(tys), 46 | Type::Pointer(_) => 8, 47 | Type::Function(_) => unimplemented!(), 48 | Type::Void => 0, 49 | } 50 | } 51 | } 52 | 53 | const MAX_ALIGN: usize = 16; 54 | 55 | impl TypeSize for ArrayType { 56 | fn size_in_byte(&self, tys: &Types) -> usize { 57 | self.elem_ty.size_in_byte(tys) * self.len 58 | } 59 | 60 | fn size_in_bits(&self, tys: &Types) -> usize { 61 | self.size_in_byte(tys) * 8 62 | } 63 | 64 | fn align_in_byte(&self, tys: &Types) -> usize { 65 | let size = self.size_in_byte(tys); 66 | let align = self.elem_ty.align_in_byte(tys); 67 | if size > MAX_ALIGN { 68 | MAX_ALIGN 69 | } else { 70 | align 71 | } 72 | } 73 | } 74 | 75 | impl TypeSize for StructType { 76 | fn size_in_byte(&self, _tys: &Types) -> usize { 77 | self.size() 78 | } 79 | 80 | fn size_in_bits(&self, tys: &Types) -> usize { 81 | self.size_in_byte(tys) * 8 82 | } 83 | 84 | fn align_in_byte(&self, _tys: &Types) -> usize { 85 | self.align() 86 | } 87 | } 88 | 89 | pub fn standard_conversion_into_machine_module(mut module: Module) -> MachineModule { 90 | ir::dce::DeadCodeElimination::new().run_on_module(&mut module); 91 | ir::merge_ret::MergeReturns::new().run_on_module(&mut module); 92 | ir::const_folding::ConstantFolding::new().run_on_module(&mut module); 93 | ir::inst_combine::InstructionCombine::new().run_on_module(&mut module); 94 | ir::codegen_prepare::CodegenPrepare::new().run_on_module(&mut module); 95 | 96 | ir::verify::verify_module(&module).unwrap(); 97 | 98 | // println!("{:?}", module); 99 | 100 | // let now = ::std::time::Instant::now(); 101 | let mut module = crate::codegen::common::dag::convert::convert_module_to_dag_module(module); 102 | crate::codegen::common::dag::combine::run(&mut module); 103 | // println!("Initial DAG:\n{:?}", module); 104 | crate::codegen::arch::dag::legalize::run(&mut module); 105 | crate::codegen::arch::dag::isel::run(&mut module); 106 | // debug!(println!( 107 | // "after pass {:?}", 108 | // ::std::time::Instant::now().duration_since(now) 109 | // )); 110 | // println!("Selected DAG:\n{:?}", module); 111 | 112 | let mut module = crate::codegen::common::dag::mc_convert::convert_module(module); 113 | 114 | // println!("{:?}", module); 115 | 116 | // let mut dag_module = convert::convert_to_dag_module(module); 117 | // 118 | // let mut pass_mgr = ModulePassManager::new(); 119 | // pass_mgr.add_pass(combine::Combine::new()); 120 | // pass_mgr.add_pass(dag::legalize::Legalize::new()); 121 | // pass_mgr.add_pass(dag::isel::MISelector::new()); 122 | // pass_mgr.run_on_module(&mut dag_module); 123 | // 124 | // let mut machine_module = dag::mc_convert::convert_module(dag_module); 125 | 126 | let mut pass_mgr = ModulePassManager::new(); 127 | pass_mgr.add_pass(branch_folding::BranchFolding::new().only_removing_unreachable_block()); 128 | pass_mgr.add_pass(phi_elimination::PhiElimination::new()); 129 | pass_mgr.add_pass(machine::two_addr::TwoAddressConverter::new()); 130 | pass_mgr.add_pass(machine::regalloc::RegisterAllocator::new()); 131 | pass_mgr.add_pass(branch_folding::BranchFolding::new()); // apply after regalloc 132 | pass_mgr.add_pass(machine::pro_epi_inserter::PrologueEpilogueInserter::new()); 133 | pass_mgr.add_pass(machine::replace_copy::ReplaceCopyWithProperMInst::new()); 134 | pass_mgr.add_pass(machine::replace_data::ReplaceConstFPWithMemoryRef::new()); 135 | pass_mgr.add_pass(eliminate_fi::EliminateFrameIndex::new()); 136 | pass_mgr.run_on_module(&mut module); 137 | 138 | module 139 | } 140 | -------------------------------------------------------------------------------- /src/codegen/common/dag/mc_convert.rs: -------------------------------------------------------------------------------- 1 | use super::node::*; 2 | use crate::codegen::arch::machine::register::ty2rc; 3 | use crate::codegen::common::machine::inst::*; 4 | use crate::codegen::common::{ 5 | dag::{basic_block::*, function::*, module::*}, 6 | machine::{basic_block::*, const_data::ConstDataArena, function::*, module::*}, 7 | }; 8 | use crate::ir::types::*; 9 | use id_arena::*; 10 | use rustc_hash::FxHashMap; 11 | use std::cell::RefCell; 12 | 13 | pub type FuncTyMap = FxHashMap; 14 | 15 | pub struct ScheduleContext<'a> { 16 | pub func_map: &'a FuncTyMap, 17 | pub func: &'a DAGFunction, 18 | pub inst_arena: &'a mut InstructionArena, 19 | pub node2reg: FxHashMap>, 20 | pub block_id: MachineBasicBlockId, 21 | pub iseq: &'a mut Vec, 22 | pub bb_map: &'a FxHashMap, 23 | pub node2inst: &'a mut FxHashMap, 24 | } 25 | 26 | pub fn convert_module(module: DAGModule) -> MachineModule { 27 | let mut functions = Arena::new(); 28 | let mut func_map = FuncTyMap::default(); 29 | for (_, func) in &module.functions { 30 | func_map.insert(func.name.to_string(), func.ty); 31 | } 32 | for (_, func) in module.functions { 33 | // TODO: refine code 34 | let id = functions.alloc(convert_function(&func_map, func)); 35 | functions[id].id = Some(id); 36 | } 37 | MachineModule::new( 38 | module.name, 39 | functions, 40 | module.types, 41 | module.global_vars, 42 | module.const_pool, 43 | ) 44 | } 45 | 46 | pub fn convert_function(func_map: &FuncTyMap, func: DAGFunction) -> MachineFunction { 47 | let mut bb_map = FxHashMap::default(); 48 | let mut basic_blocks = MachineBasicBlocks::new(); 49 | 50 | for dag_bb_id in &func.dag_basic_blocks { 51 | let mbb_id = basic_blocks.arena.alloc(MachineBasicBlock::new()); 52 | bb_map.insert(*dag_bb_id, mbb_id); 53 | basic_blocks.order.push(mbb_id); 54 | } 55 | 56 | for (dag, machine) in &bb_map { 57 | let dbb = &func.dag_basic_block_arena[*dag]; 58 | let mbb = &mut basic_blocks.arena[*machine]; 59 | mbb.pred = dbb.pred.iter().map(|bb| *bb_map.get(bb).unwrap()).collect(); 60 | mbb.succ = dbb.succ.iter().map(|bb| *bb_map.get(bb).unwrap()).collect(); 61 | } 62 | 63 | let mut inst_arena = InstructionArena::new(); 64 | let mut node2inst = FxHashMap::default(); 65 | 66 | for dag_bb_id in &func.dag_basic_blocks { 67 | let node = &func.dag_basic_block_arena[*dag_bb_id]; 68 | let bb_id = *bb_map.get(dag_bb_id).unwrap(); 69 | let mut iseq = vec![]; 70 | 71 | let entry = match node.entry { 72 | Some(entry) => entry, 73 | None => continue, 74 | }; 75 | 76 | ScheduleContext { 77 | func_map, 78 | func: &func, 79 | inst_arena: &mut inst_arena, 80 | node2reg: FxHashMap::default(), 81 | block_id: bb_id, 82 | iseq: &mut iseq, 83 | bb_map: &bb_map, 84 | node2inst: &mut node2inst, 85 | } 86 | .convert(entry); 87 | 88 | basic_blocks.arena[bb_id].iseq = RefCell::new(iseq); 89 | } 90 | 91 | MachineFunction { 92 | id: None, 93 | is_internal: func.is_internal, 94 | name: func.name, 95 | ty: func.ty, 96 | body: MachineFunctionBody { 97 | inst_arena, 98 | basic_blocks, 99 | }, 100 | local_mgr: func.local_vars, 101 | regs_info: func.regs, 102 | frame_objects: None, 103 | const_data: ConstDataArena::new(), 104 | types: func.types.clone(), 105 | } 106 | } 107 | 108 | impl<'a> ScheduleContext<'a> { 109 | pub fn convert(&mut self, node: NodeId) -> Option { 110 | if let Some(reg) = self.node2reg.get(&node) { 111 | return *reg; 112 | } 113 | 114 | let reg = match &self.func.node_arena[node] { 115 | Node::IR(IRNode { 116 | opcode: IROpcode::Entry, 117 | .. 118 | }) 119 | | Node::IR(IRNode { 120 | opcode: IROpcode::Root, 121 | .. 122 | }) => None, 123 | Node::IR(IRNode { 124 | opcode: IROpcode::RegClass, 125 | args, 126 | ty, 127 | .. 128 | }) => { 129 | let val = self.normal_arg(args[0]); 130 | Some(val.as_register().sub_super(ty2rc(&ty))) 131 | } 132 | _ => { 133 | let inst_id = self.convert_node(node); 134 | self.inst_arena[inst_id].get_def_reg() 135 | } 136 | }; 137 | self.node2reg.insert(node, reg); 138 | 139 | if let Some(next) = self.func.node_arena[node].next() { 140 | self.convert(next); 141 | } 142 | 143 | reg 144 | } 145 | 146 | pub fn append_inst(&mut self, inst: MachineInst) -> MachineInstId { 147 | let inst_id = self.inst_arena.alloc(&self.func.regs, inst); 148 | self.iseq.push(inst_id); 149 | inst_id 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/codegen/riscv64/asm/print.rs: -------------------------------------------------------------------------------- 1 | use super::super::frame_object::FrameObjectsInfo; 2 | use super::super::machine::inst::*; 3 | use crate::codegen::common::machine::{ 4 | basic_block::MachineBasicBlockId, 5 | function::{InstIter, MachineFunction}, 6 | module::MachineModule, 7 | }; 8 | use crate::ir::{global_val::GlobalVariableId, types::TypeSize}; 9 | use rustc_hash::FxHashMap; 10 | 11 | pub struct MachineAsmPrinter<'a> { 12 | pub output: String, 13 | cur_bb_id_base: usize, 14 | global_var_name: FxHashMap, 15 | } 16 | 17 | impl<'s> MachineAsmPrinter<'s> { 18 | pub fn new() -> Self { 19 | Self { 20 | output: "".to_string(), 21 | cur_bb_id_base: 0, 22 | global_var_name: FxHashMap::default(), 23 | } 24 | } 25 | 26 | pub fn run_on_module(&mut self, m: &'s MachineModule) { 27 | self.output.push_str(" .text\n"); 28 | 29 | for (id, g) in &m.global_vars.arena { 30 | let size = g.ty.size_in_byte(&m.types); 31 | let align = g.ty.align_in_byte(&m.types); 32 | self.output 33 | .push_str(format!(" .comm {},{},{}\n", g.name, size, align).as_str()); 34 | self.global_var_name.insert(id, g.name.as_str()); 35 | } 36 | 37 | for (_, func) in &m.functions { 38 | self.run_on_function(&func) 39 | } 40 | } 41 | 42 | fn run_on_function(&mut self, f: &MachineFunction) { 43 | if f.is_internal { 44 | return; 45 | } 46 | 47 | self.output 48 | .push_str(format!(" .globl {}\n", f.name).as_str()); // TODO 49 | 50 | self.output.push_str(format!("{}:\n", f.name).as_str()); 51 | 52 | self.run_on_basic_blocks(f); 53 | } 54 | 55 | fn run_on_basic_blocks( 56 | &mut self, 57 | // tys: &Types, 58 | f: &MachineFunction, 59 | ) { 60 | for (id, _, inst_iter) in f.body.mbb_iter() { 61 | self.output 62 | .push_str(format!("{}:\n", self.bb_id_to_label_id(&id)).as_str()); 63 | self.run_on_basic_block(inst_iter, &f.frame_objects.as_ref().unwrap()); 64 | } 65 | self.cur_bb_id_base += f.body.basic_blocks.arena.len(); 66 | } 67 | 68 | fn run_on_basic_block<'a>( 69 | &mut self, 70 | // tys: &Types, 71 | // regs_info: &RegistersInfo, 72 | inst_iter: InstIter<'a>, 73 | fo: &FrameObjectsInfo, 74 | ) { 75 | for (_, inst) in inst_iter { 76 | self.run_on_inst(inst, fo); 77 | } 78 | } 79 | 80 | fn run_on_inst( 81 | &mut self, 82 | // tys: &Types, 83 | // regs_info: &RegistersInfo, 84 | inst: &MachineInst, 85 | fo: &FrameObjectsInfo, 86 | ) { 87 | self.output.push_str(" "); 88 | 89 | self.output.push_str(inst.opcode.inst_def().unwrap().name); 90 | self.output.push(' '); 91 | 92 | for (i, r) in inst.def.iter().enumerate() { 93 | self.output.push_str(r.as_phys_reg().name()); 94 | if i != inst.def.len() - 1 { 95 | self.output.push_str(", "); 96 | } 97 | } 98 | 99 | if inst.def.len() > 0 && inst.operand.len() > 0 { 100 | self.output.push_str(", "); 101 | } 102 | 103 | for (i, o) in inst.operand.iter().enumerate() { 104 | self.operand2asm(fo, o); 105 | if i != inst.operand.len() - 1 { 106 | self.output.push_str(", "); 107 | } 108 | } 109 | 110 | self.output.push('\n'); 111 | } 112 | 113 | fn bb_id_to_label_id(&self, bb_id: &MachineBasicBlockId) -> String { 114 | format!(".L{}", bb_id.index() + self.cur_bb_id_base) 115 | } 116 | 117 | fn operand2asm(&mut self, fo: &FrameObjectsInfo, operand: &MachineOperand) { 118 | match operand { 119 | MachineOperand::Branch(id) => self.output.push_str(self.bb_id_to_label_id(id).as_str()), 120 | MachineOperand::Constant(MachineConstant::Int32(i)) => { 121 | self.output.push_str(format!("{}", i).as_str()) 122 | } 123 | MachineOperand::Constant(MachineConstant::Int8(i)) => { 124 | self.output.push_str(format!("{}", i).as_str()) 125 | } 126 | MachineOperand::Register(r) => self.output.push_str(r.as_phys_reg().name()), 127 | MachineOperand::FrameIndex(i) => self 128 | .output 129 | .push_str(format!("{}", fo.offset(i.idx).unwrap()).as_str()), 130 | MachineOperand::Mem(MachineMemOperand::FiReg(fi, r)) => self.output.push_str( 131 | format!("{}({})", fo.offset(fi.idx).unwrap(), r.as_phys_reg().name()).as_str(), 132 | ), 133 | MachineOperand::Mem(MachineMemOperand::ImmReg(imm, r)) => self 134 | .output 135 | .push_str(format!("{}({})", imm, r.as_phys_reg().name()).as_str()), 136 | MachineOperand::Mem(MachineMemOperand::Address(AddressKind::FunctionName(name))) => { 137 | self.output.push_str(name.replace('.', "_").as_str()) 138 | } 139 | MachineOperand::Mem(MachineMemOperand::Address(AddressKind::Global(id))) => { 140 | self.output.push_str(self.global_var_name[id]) 141 | } 142 | _ => unimplemented!(), 143 | }; 144 | } 145 | } 146 | --------------------------------------------------------------------------------