├── cmp.txt ├── .gitignore ├── Cargo.toml ├── fibo.rb ├── fibo.c ├── fibo.js ├── monoasm_macro ├── Cargo.toml └── src │ ├── lib.rs │ ├── parse.rs │ ├── inst.rs │ └── asm.rs ├── monoasm ├── Cargo.toml ├── tests │ ├── roundpd.rs │ ├── cvttsd2si.rs │ ├── andpd.rs │ ├── xorpd.rs │ ├── minmaxsd.rs │ ├── divl.rs │ ├── cdq.rs │ ├── idivl.rs │ ├── div.rs │ ├── counts.rs │ └── shift_dword.rs └── src │ ├── test.rs │ ├── lib.rs │ └── jit_memory.rs ├── .github └── workflows │ └── rust.yml ├── gen.rb ├── README.md └── Cargo.lock /cmp.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.* 3 | **/*.rs.bk 4 | a.out 5 | *.s 6 | *.bin 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "monoasm", 5 | "monoasm_macro", 6 | ] -------------------------------------------------------------------------------- /fibo.rb: -------------------------------------------------------------------------------- 1 | def fibo(x) 2 | if x == 0 then return 0 end 3 | if x <= 2 then return 1 end 4 | return fibo(x-1) + fibo(x-2) 5 | end 6 | now = Time.now 7 | answer = fibo(30) 8 | puts "#{answer} #{(Time.now - now) * 1000} ms" -------------------------------------------------------------------------------- /fibo.c: -------------------------------------------------------------------------------- 1 | #include 2 | int fibo(int x); 3 | 4 | int main() 5 | { 6 | printf("fib( 30 ) = %d\n", fibo(30)); 7 | } 8 | 9 | int fibo(int x) 10 | { 11 | if (x == 0) 12 | return 0; 13 | if (x <= 2) 14 | return 1; 15 | return fibo(x - 1) + fibo(x - 2); 16 | } -------------------------------------------------------------------------------- /fibo.js: -------------------------------------------------------------------------------- 1 | const now = performance.now(); 2 | const answer = fibo(30) 3 | const time = (performance.now() - now).toFixed(5); 4 | console.log(`${answer} ${time} ms`) 5 | 6 | 7 | function fibo(x) { 8 | if (x == 0) return 0 9 | if (x <= 2) return 1 10 | return fibo(x-1) + fibo(x-2) 11 | } -------------------------------------------------------------------------------- /monoasm_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monoasm_macro" 3 | version = "0.1.0" 4 | authors = ["monochrome "] 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [lib] 10 | proc-macro = true 11 | 12 | [dependencies] 13 | proc-macro2 = "1.0.24" 14 | quote = "1.0.8" 15 | 16 | [dependencies.syn] 17 | version = "2.0.75" 18 | features = ["full"] 19 | -------------------------------------------------------------------------------- /monoasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monoasm" 3 | version = "0.1.0" 4 | authors = ["monochrome "] 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | libc = "0.2.82" 11 | region = "3.0.0" 12 | monoasm_macro = { path = "../monoasm_macro" } 13 | proc-macro2 = "1.0.24" 14 | syn = "2.0.28" 15 | chrono = "0.4.26" 16 | tempfile = "3.13.0" 17 | -------------------------------------------------------------------------------- /monoasm/tests/roundpd.rs: -------------------------------------------------------------------------------- 1 | extern crate monoasm; 2 | extern crate monoasm_macro; 3 | 4 | use monoasm::*; 5 | use monoasm_macro::monoasm; 6 | 7 | #[test] 8 | fn roundpd() { 9 | let mut jit: JitMemory = JitMemory::new(); 10 | let begin = jit.label(); 11 | let pi = 3.141592653589793f64; 12 | monoasm!(&mut jit, 13 | begin: 14 | roundpd xmm0, xmm0, (0x02); // 0x02 -> ceil 15 | ret; 16 | ); 17 | jit.finalize(); 18 | 19 | let f = jit.get_label_addr::(begin); 20 | let ret = f(pi); 21 | assert_eq!(ret, pi.ceil()); 22 | } -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Install latest nightly 20 | uses: actions-rs/toolchain@v1 21 | with: 22 | toolchain: nightly 23 | override: true 24 | components: rustfmt, clippy 25 | - name: Build 26 | run: cargo build --verbose 27 | - name: Run tests 28 | run: cargo test --verbose 29 | -------------------------------------------------------------------------------- /monoasm/tests/cvttsd2si.rs: -------------------------------------------------------------------------------- 1 | extern crate monoasm; 2 | extern crate monoasm_macro; 3 | 4 | use monoasm::*; 5 | use monoasm_macro::monoasm; 6 | pub type ReturnFunc = extern "C" fn() -> u64; 7 | 8 | #[test] 9 | fn cvttsd2si() { 10 | let mut jit: JitMemory = JitMemory::new(); 11 | let begin = jit.label(); 12 | let pi = 3.141592653589793f64; 13 | monoasm!(&mut jit, 14 | begin: 15 | cvttsd2siq rax, xmm0; 16 | ret; 17 | ); 18 | jit.finalize(); 19 | eprintln!("{}", jit.dump_code().unwrap()); 20 | 21 | let f = jit.get_label_addr::(begin); 22 | let ret = f(pi); 23 | assert_eq!(ret, 3); 24 | } 25 | -------------------------------------------------------------------------------- /monoasm/tests/andpd.rs: -------------------------------------------------------------------------------- 1 | extern crate monoasm; 2 | extern crate monoasm_macro; 3 | 4 | use monoasm::*; 5 | use monoasm_macro::monoasm; 6 | 7 | #[test] 8 | fn andpd() { 9 | let mut jit: JitMemory = JitMemory::new(); 10 | let begin = jit.label(); 11 | let pi =-3.141592653589793; 12 | let mask = 0x7fffffffffffffffu64; // mask for the sign bit 13 | monoasm!(&mut jit, 14 | begin: 15 | movq rax, (mask); 16 | movq xmm1, rax; 17 | 18 | andpd xmm0, xmm1; 19 | ret; 20 | ); 21 | jit.finalize(); 22 | 23 | let f = jit.get_label_addr::(begin); 24 | let ret = f(pi); 25 | assert_eq!(ret, pi.abs()); 26 | } -------------------------------------------------------------------------------- /monoasm/tests/xorpd.rs: -------------------------------------------------------------------------------- 1 | extern crate monoasm; 2 | extern crate monoasm_macro; 3 | 4 | use monoasm::*; 5 | use monoasm_macro::monoasm; 6 | use std::ops::Neg; 7 | 8 | #[test] 9 | fn xorpd() { 10 | let mut jit: JitMemory = JitMemory::new(); 11 | let begin = jit.label(); 12 | let pi = 3.141592653589793f64; 13 | let mask = 0x8000000000000000u64; // mask for the negation 14 | monoasm!(&mut jit, 15 | begin: 16 | movq rax, (mask); 17 | movq xmm1, rax; 18 | 19 | xorpd xmm0, xmm1; 20 | ret; 21 | movq rax, (foo); 22 | ); 23 | jit.finalize(); 24 | 25 | let f = jit.get_label_addr::(begin); 26 | let ret = f(pi); 27 | assert_eq!(ret, pi.neg()); 28 | } 29 | 30 | extern "C" fn foo() {} 31 | -------------------------------------------------------------------------------- /monoasm_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_hygiene)] 2 | #![allow(dead_code)] 3 | #![allow(unreachable_patterns)] 4 | 5 | extern crate proc_macro; 6 | extern crate quote; 7 | extern crate syn; 8 | use proc_macro::TokenStream; 9 | use quote::quote; 10 | use syn::parse_macro_input; 11 | mod asm; 12 | mod inst; 13 | mod parse; 14 | use asm::*; 15 | 16 | //---------------------------------------------------------------------- 17 | // 18 | // Entry point for monoasm proc-macro. 19 | // 20 | //---------------------------------------------------------------------- 21 | 22 | #[proc_macro] 23 | pub fn monoasm(tokens: TokenStream) -> TokenStream { 24 | let stmts = parse_macro_input!(tokens as inst::Stmts); 25 | let base = stmts.base; 26 | let mut ts = quote!(let mut jit = #base;); 27 | ts.extend(stmts.contents.into_iter().map(compile)); 28 | quote!({ #ts }).into() 29 | } 30 | -------------------------------------------------------------------------------- /monoasm/tests/minmaxsd.rs: -------------------------------------------------------------------------------- 1 | use monoasm::*; 2 | use monoasm_macro::monoasm; 3 | 4 | #[test] 5 | fn minmaxsd() { 6 | let mut jit = JitMemory::new(); 7 | let label0 = jit.label(); 8 | let label1 = jit.label(); 9 | let data = jit.data(64); 10 | monoasm! { &mut jit, 11 | label0: 12 | lea rsi, [rip + data]; 13 | movq rcx, 8; 14 | movq [rsi + rcx * 2 + 4], xmm1; 15 | minsd xmm0, [rsi + rcx * 2 + 4]; 16 | ret; 17 | label1: 18 | lea rsi, [rip + data]; 19 | movq rcx, 8; 20 | movq [rsi + rcx * 2 + 4], xmm1; 21 | maxsd xmm0, [rsi + rcx * 2 + 4]; 22 | ret; 23 | } 24 | jit.finalize(); 25 | eprintln!("{}", jit.dump_code().unwrap()); 26 | 27 | let f1 = jit.get_label_addr2::(label0); 28 | let f2 = jit.get_label_addr2::(label1); 29 | assert_eq!(-0.1, dbg!(f1(0.02, -0.1))); 30 | assert_eq!(0.02, dbg!(f2(0.02, -0.1))); 31 | } 32 | -------------------------------------------------------------------------------- /monoasm/tests/divl.rs: -------------------------------------------------------------------------------- 1 | extern crate monoasm; 2 | extern crate monoasm_macro; 3 | 4 | use monoasm::*; 5 | use monoasm_macro::monoasm; 6 | 7 | #[repr(C)] 8 | #[derive(Debug)] 9 | struct UnsignedDiv { 10 | quotient: u64, // eax 11 | remainder: u64, // edx 12 | } 13 | 14 | #[test] 15 | fn divl() { 16 | let mut jit: JitMemory = JitMemory::new(); 17 | let begin = jit.label(); 18 | monoasm!(&mut jit, 19 | begin: 20 | movq rax, (0xffff_ffff_ffff_ffffu64); 21 | movq rdx, rax; 22 | movl rax, rdi; 23 | cdq; 24 | divl rsi; 25 | ret; 26 | ); 27 | jit.finalize(); 28 | eprintln!("{}", jit.dump_code().unwrap()); 29 | 30 | let dividend = 7777777; 31 | let divisor = 111112; 32 | let f = jit.get_label_addr2::(begin); 33 | let ret = dbg!(f(dividend, divisor)); // rax contains (7) 34 | assert_eq!(ret.quotient as u32, dividend / divisor); 35 | assert_eq!(ret.remainder as u32, dividend % divisor); 36 | } 37 | -------------------------------------------------------------------------------- /monoasm/tests/cdq.rs: -------------------------------------------------------------------------------- 1 | extern crate monoasm; 2 | extern crate monoasm_macro; 3 | 4 | use monoasm::*; 5 | use monoasm_macro::monoasm; 6 | pub type ReturnFunc = extern "C" fn() -> u64; 7 | 8 | #[test] 9 | fn cdq() { 10 | let mut jit: JitMemory = JitMemory::new(); 11 | let begin = jit.label(); 12 | monoasm!(&mut jit, 13 | begin: 14 | movq rax, rdi; 15 | cdq; 16 | movl rax, rdx; 17 | ret; 18 | ); 19 | jit.finalize(); 20 | 21 | let f = jit.get_label_addr::(begin); 22 | let ret = f(0x80000000); // rax contains 0x00000000FFFFFFFF 23 | assert_eq!(ret, 0x00000000FFFFFFFF); 24 | } 25 | 26 | #[test] 27 | fn notq() { 28 | let mut jit: JitMemory = JitMemory::new(); 29 | let begin = jit.label(); 30 | monoasm!(&mut jit, 31 | begin: 32 | notq rdi; 33 | movq rax, rdi; 34 | ret; 35 | ); 36 | jit.finalize(); 37 | 38 | let f = jit.get_label_addr::(begin); 39 | assert_eq!(f(354), !354); 40 | assert_eq!(f(-354), !(-354)); 41 | } 42 | -------------------------------------------------------------------------------- /monoasm/tests/idivl.rs: -------------------------------------------------------------------------------- 1 | extern crate monoasm; 2 | extern crate monoasm_macro; 3 | 4 | use monoasm::*; 5 | use monoasm_macro::monoasm; 6 | pub type ReturnFunc = extern "C" fn() -> u64; 7 | 8 | #[test] 9 | fn idivl() { 10 | let mut jit: JitMemory = JitMemory::new(); 11 | let begin = jit.label(); 12 | monoasm!(&mut jit, 13 | begin: 14 | xorl rdx, rdx; 15 | xorl rax, rax; 16 | movq rax, rdi; 17 | cdq; 18 | idivl rsi; // eax = -7, edx = 0 19 | ret; 20 | ); 21 | jit.finalize(); 22 | eprintln!("{}", jit.dump_code().unwrap()); 23 | 24 | let f = jit.get_label_addr2::(begin); 25 | let ret = f(-7777777, 1111111); // rax contains (-7) 26 | assert_eq!(ret, -7); 27 | } 28 | 29 | #[test] 30 | fn idivl_rem() { 31 | let mut jit: JitMemory = JitMemory::new(); 32 | let begin = jit.label(); 33 | monoasm!(&mut jit, 34 | begin: 35 | xorl rdx, rdx; 36 | xorl rax, rax; 37 | movq rax, rdi; 38 | cdq; 39 | idivl rsi; // eax = -5, edx = -3 40 | movl rax, rdx; 41 | ret; 42 | ); 43 | jit.finalize(); 44 | eprintln!("{}", jit.dump_code().unwrap()); 45 | 46 | let f = jit.get_label_addr2::(begin); 47 | let ret = f(-23, 4); // rax contains (-3) 48 | assert_eq!(ret, -3); 49 | } 50 | -------------------------------------------------------------------------------- /monoasm/src/test.rs: -------------------------------------------------------------------------------- 1 | use std::arch::asm; 2 | 3 | // Utility functions 4 | pub const PUTC: *const fn() = putc as *const fn(); 5 | pub const PUTINT: *const fn() = putint as *const fn(); 6 | pub const PANIC: *const fn() = panic as *const fn(); 7 | pub const DUMP: *const fn() = dump as *const fn(); 8 | 9 | extern "C" fn putc(ch: u8) { 10 | eprint!("{}", ch as char); 11 | } 12 | 13 | extern "C" fn putint(i: u64) { 14 | eprintln!("{:?}", i); 15 | } 16 | 17 | extern "C" fn panic() { 18 | panic!("panic() is called.") 19 | } 20 | 21 | extern "C" fn dump() { 22 | #[allow(unused_assignments)] 23 | let (mut rax, mut rdi, mut rsi, mut rdx, mut rcx, mut r8) = 24 | (0u64, 0u64, 0u64, 0u64, 0u64, 0u64); 25 | macro_rules! reg_save { 26 | ($reg:ident) => { 27 | unsafe { 28 | asm!( 29 | concat!("mov {}, ", stringify!($reg)), 30 | out(reg) $reg, 31 | ); 32 | } 33 | }; 34 | } 35 | macro_rules! reg_restore { 36 | ($reg:ident) => { 37 | unsafe { 38 | asm!( 39 | concat!("mov ", stringify!($reg),", {}"), 40 | in(reg) $reg, 41 | ); 42 | } 43 | }; 44 | } 45 | macro_rules! reg_print { 46 | ($reg:ident) => { 47 | eprint!(" {:3}: {:016x}", stringify!($reg), $reg); 48 | }; 49 | } 50 | reg_save!(rax); 51 | reg_save!(rdi); 52 | reg_save!(rsi); 53 | reg_save!(rdx); 54 | reg_save!(rcx); 55 | reg_save!(r8); 56 | 57 | reg_print!(rax); 58 | reg_print!(rdi); 59 | reg_print!(rsi); 60 | reg_print!(rdx); 61 | reg_print!(rcx); 62 | eprintln!(); 63 | reg_print!(r8); 64 | eprintln!(); 65 | 66 | reg_restore!(rax); 67 | reg_restore!(rdi); 68 | reg_restore!(rsi); 69 | reg_restore!(rdx); 70 | reg_restore!(rcx); 71 | reg_restore!(r8); 72 | } 73 | -------------------------------------------------------------------------------- /monoasm/tests/div.rs: -------------------------------------------------------------------------------- 1 | use monoasm::*; 2 | use monoasm_macro::monoasm; 3 | 4 | #[repr(C)] 5 | #[derive(Debug)] 6 | struct UnsignedDiv { 7 | quotient: u64, 8 | remainder: u64, 9 | } 10 | 11 | #[test] 12 | fn udiv() { 13 | let mut jit = JitMemory::new(); 14 | let label = jit.label(); 15 | monoasm! { &mut jit, 16 | label: 17 | movq rcx, rdx; 18 | movq rax, rdi; 19 | movq rdx, rsi; 20 | div rcx; 21 | ret; 22 | } 23 | jit.finalize(); 24 | eprintln!("{}", jit.dump_code().unwrap()); 25 | 26 | let f = jit.get_label_addr2::(label); 27 | let dividend = 0x0123456789abcdef; 28 | let divisor = 0x56789a; 29 | let res = dbg!(f(dividend, divisor)); 30 | assert_eq!( 31 | dividend, 32 | res.quotient as u128 * divisor as u128 + res.remainder as u128 33 | ); 34 | } 35 | 36 | #[repr(C)] 37 | #[derive(Debug)] 38 | struct SignedDiv { 39 | quotient: i64, 40 | remainder: i64, 41 | } 42 | 43 | #[test] 44 | fn idiv() { 45 | let mut jit = JitMemory::new(); 46 | let label = jit.label(); 47 | monoasm! { &mut jit, 48 | label: 49 | movq rcx, rdx; 50 | movq rax, rdi; 51 | movq rdx, rsi; 52 | idiv rcx; 53 | ret; 54 | } 55 | jit.finalize(); 56 | eprintln!("{}", jit.dump_code().unwrap()); 57 | 58 | let f = jit.get_label_addr2::(label); 59 | let dividend = 0x0123456789abcdef; 60 | let divisor = 0x56789a; 61 | let res = dbg!(f(dividend, divisor)); 62 | assert_eq!( 63 | dividend, 64 | res.quotient as i128 * divisor as i128 + res.remainder as i128 65 | ); 66 | let res = dbg!(f(-dividend, divisor)); 67 | assert_eq!( 68 | -dividend, 69 | res.quotient as i128 * divisor as i128 + res.remainder as i128 70 | ); 71 | let res = dbg!(f(dividend, -divisor)); 72 | assert_eq!( 73 | dividend, 74 | res.quotient as i128 * (-divisor) as i128 + res.remainder as i128 75 | ); 76 | let res = dbg!(f(-dividend, -divisor)); 77 | assert_eq!( 78 | -dividend, 79 | res.quotient as i128 * (-divisor) as i128 + res.remainder as i128 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /monoasm/tests/counts.rs: -------------------------------------------------------------------------------- 1 | extern crate monoasm; 2 | extern crate monoasm_macro; 3 | 4 | use monoasm::*; 5 | use monoasm_macro::monoasm; 6 | 7 | #[test] 8 | fn popcntq() { 9 | let mut jit = JitMemory::new(); 10 | let label = jit.label(); 11 | let data = jit.data(64); 12 | let magic = 0x1234_5678_9abc_def0u64; 13 | monoasm! { &mut jit, 14 | label: 15 | lea rsi, [rip + data]; 16 | movq rcx, 8; 17 | movq [rsi + rcx * 2 + 4], rdi; 18 | popcntq rax, [rsi + rcx * 2 + 4]; 19 | ret; 20 | } 21 | jit.finalize(); 22 | eprintln!("{}", jit.dump_code().unwrap()); 23 | 24 | let f = jit.get_label_addr::(label); 25 | assert_eq!(magic.count_ones() as u64, dbg!(f(magic))); 26 | } 27 | 28 | #[test] 29 | fn lzcntq() { 30 | let mut jit = JitMemory::new(); 31 | let label = jit.label(); 32 | let data = jit.data(64); 33 | let magic = 0x0034_5678_9abc_def0u64; 34 | monoasm! { &mut jit, 35 | label: 36 | lea rsi, [rip + data]; 37 | movq rcx, 8; 38 | movq [rsi + rcx * 2 + 4], rdi; 39 | lzcntq rax, [rsi + rcx * 2 + 4]; 40 | ret; 41 | } 42 | jit.finalize(); 43 | eprintln!("{}", jit.dump_code().unwrap()); 44 | 45 | let f = jit.get_label_addr::(label); 46 | assert_eq!(magic.leading_zeros() as u64, dbg!(f(magic))); 47 | } 48 | 49 | #[test] 50 | fn tzcntq() { 51 | let mut jit = JitMemory::new(); 52 | let label = jit.label(); 53 | let data = jit.data(64); 54 | let magic = 0x0012_3456_7800_0000u64; 55 | monoasm! { &mut jit, 56 | label: 57 | lea rsi, [rip + data]; 58 | movq rcx, 8; 59 | movq [rsi + rcx * 2 + 4], rdi; 60 | tzcntq rax, [rsi + rcx * 2 + 4]; 61 | ret; 62 | } 63 | jit.finalize(); 64 | eprintln!("{}", jit.dump_code().unwrap()); 65 | 66 | let f = jit.get_label_addr::(label); 67 | assert_eq!(magic.trailing_zeros() as u64, dbg!(f(magic))); 68 | } 69 | 70 | #[test] 71 | fn lzcntl() { 72 | let mut jit: JitMemory = JitMemory::new(); 73 | let begin = jit.label(); 74 | let val = 0x8000_0000_000F_F0F0; 75 | monoasm!(&mut jit, 76 | begin: 77 | movq rax, rdi; 78 | lzcntl rax, rax; 79 | ret; 80 | ); 81 | jit.finalize(); 82 | eprintln!("{}", jit.dump_code().unwrap()); 83 | 84 | let f = jit.get_label_addr::(begin); 85 | let ret = dbg!(f(val)); 86 | assert_eq!(ret, (val as u32).leading_zeros() as _); 87 | } 88 | 89 | #[test] 90 | fn tzcntl() { 91 | let mut jit: JitMemory = JitMemory::new(); 92 | let begin = jit.label(); 93 | let val = 0x8000_0000_000F_0F40; // 0000 0000 0000 1111 0000 1111 0100 0000 94 | monoasm!(&mut jit, 95 | begin: 96 | movq rax, rdi; 97 | tzcntl rax, rax; 98 | ret; 99 | ); 100 | jit.finalize(); 101 | eprintln!("{}", jit.dump_code().unwrap()); 102 | 103 | let f = jit.get_label_addr::(begin); 104 | let ret = dbg!(f(val)); 105 | assert_eq!(ret, (val as u32).trailing_zeros() as _); 106 | } 107 | 108 | #[test] 109 | fn popcntl() { 110 | let mut jit: JitMemory = JitMemory::new(); 111 | let begin = jit.label(); 112 | let val = 0xCAFE_BABE_DEAD_BEEFu64; // 1101 1110 1010 1101 1011 1110 1110 1111 113 | monoasm!(&mut jit, 114 | begin: 115 | movq rax, rdi; 116 | popcntl rax, rax; 117 | ret; 118 | ); 119 | jit.finalize(); 120 | eprintln!("{}", jit.dump_code().unwrap()); 121 | 122 | let f = jit.get_label_addr::(begin); 123 | let ret = dbg!(f(val)); 124 | assert_eq!(ret, (val as u32).count_ones() as _); 125 | } 126 | -------------------------------------------------------------------------------- /monoasm/tests/shift_dword.rs: -------------------------------------------------------------------------------- 1 | extern crate monoasm; 2 | extern crate monoasm_macro; 3 | 4 | use monoasm::*; 5 | use monoasm_macro::monoasm; 6 | 7 | #[test] 8 | fn shll() { 9 | let mut jit: JitMemory = JitMemory::new(); 10 | let begin = jit.label(); 11 | let val = 0x0000000000000040u64; // 0000 ... 0000 0100 0000 12 | monoasm!(&mut jit, 13 | begin: 14 | movq rax, rdi; 15 | movb cl, (0x1F); // 31, oops, its all gone since it is shll 16 | shll rax, cl; 17 | ret; 18 | ); 19 | jit.finalize(); 20 | eprintln!("{}", jit.dump_code().unwrap()); 21 | 22 | let f = jit.get_label_addr::(begin); 23 | let ret = f(val); 24 | assert_eq!(ret, 0x0000000000000000u64); 25 | } 26 | 27 | #[test] 28 | fn shrl() { 29 | let mut jit: JitMemory = JitMemory::new(); 30 | let begin = jit.label(); 31 | let val = 0x0000000000000040u64; // 0000 ... 0000 0100 0000 32 | monoasm!(&mut jit, 33 | begin: 34 | movq rax, rdi; 35 | movb cl, (0x01); 36 | shrl rax, cl; 37 | ret; 38 | ); 39 | jit.finalize(); 40 | eprintln!("{}", jit.dump_code().unwrap()); 41 | 42 | let f = jit.get_label_addr::(begin); 43 | let ret = f(val); 44 | assert_eq!(ret, 0x0000000000000020u64); 45 | } 46 | 47 | #[test] 48 | fn sall() { 49 | let mut jit: JitMemory = JitMemory::new(); 50 | let begin = jit.label(); 51 | let val = 0x0000000000000001u64; // 0000 ... 0000 0001 52 | monoasm!(&mut jit, 53 | begin: 54 | movq rax, rdi; 55 | movb cl, (0x1F); 56 | sall rax, cl; 57 | ret; 58 | ); 59 | jit.finalize(); 60 | eprintln!("{}", jit.dump_code().unwrap()); 61 | 62 | let f = jit.get_label_addr::(begin); 63 | let ret = f(val); 64 | assert_eq!(ret, 0x0000000080000000u64); 65 | } 66 | 67 | #[test] 68 | fn sarl() { 69 | let mut jit: JitMemory = JitMemory::new(); 70 | let begin = jit.label(); 71 | let val = 0x0000000080000000u64; // 0000 ... 0000 0001 72 | monoasm!(&mut jit, 73 | begin: 74 | movq rax, rdi; 75 | movb cl, (0x1F); 76 | sarl rax, cl; // dword operation, so it should carry the sign bit 77 | ret; 78 | ); 79 | jit.finalize(); 80 | eprintln!("{}", jit.dump_code().unwrap()); 81 | 82 | let f = jit.get_label_addr::(begin); 83 | let ret = f(val); 84 | assert_eq!(ret, 0x00000000FFFFFFFFu64); 85 | } 86 | 87 | #[test] 88 | fn roll() { 89 | let mut jit: JitMemory = JitMemory::new(); 90 | let begin = jit.label(); 91 | let val = 0x0000000000000001u64; // 0000 ... 0000 0001 92 | monoasm!(&mut jit, 93 | begin: 94 | movq rax, rdi; 95 | movb cl, (0x04); 96 | roll rax, cl; 97 | ret; 98 | ); 99 | jit.finalize(); 100 | eprintln!("{}", jit.dump_code().unwrap()); 101 | 102 | let f = jit.get_label_addr::(begin); 103 | let ret = f(val); 104 | assert_eq!(ret, 0x0000000000000010u64); 105 | } 106 | 107 | #[test] 108 | fn rorl() { 109 | let mut jit: JitMemory = JitMemory::new(); 110 | let begin = jit.label(); 111 | let val = 0x0000000000000010u64; // 0000 ... 0000 0001 112 | monoasm!(&mut jit, 113 | begin: 114 | movq rax, rdi; 115 | movb cl, (0x04); 116 | rorl rax, cl; 117 | ret; 118 | ); 119 | jit.finalize(); 120 | eprintln!("{}", jit.dump_code().unwrap()); 121 | 122 | let f = jit.get_label_addr::(begin); 123 | let ret = f(val); 124 | assert_eq!(ret, 0x0000000000000001u64); 125 | } 126 | -------------------------------------------------------------------------------- /monoasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | use std::mem; 3 | use std::ops::{Add, Deref, DerefMut, Index, IndexMut, Sub}; 4 | mod jit_memory; 5 | pub mod test; 6 | pub use jit_memory::*; 7 | 8 | const PAGE_SIZE: usize = 1024 * 1024 * 256; 9 | 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 11 | #[repr(transparent)] 12 | pub struct CodePtr(std::num::NonZero); 13 | 14 | impl std::ops::Sub for CodePtr { 15 | type Output = i64; 16 | fn sub(self, rhs: CodePtr) -> Self::Output { 17 | (self.0.get() as i64) - (rhs.0.get() as i64) 18 | } 19 | } 20 | 21 | impl std::ops::Add for CodePtr { 22 | type Output = CodePtr; 23 | fn add(self, rhs: usize) -> Self::Output { 24 | CodePtr::from(unsafe { self.as_ptr().add(rhs) }) 25 | } 26 | } 27 | 28 | impl std::cmp::PartialOrd for CodePtr { 29 | fn partial_cmp(&self, other: &Self) -> Option { 30 | self.as_ptr().partial_cmp(&other.as_ptr()) 31 | } 32 | } 33 | 34 | impl CodePtr { 35 | pub fn from(ptr: *mut u8) -> Self { 36 | Self(std::num::NonZero::new(ptr as usize).unwrap()) 37 | } 38 | 39 | pub fn as_ptr(&self) -> *mut u8 { 40 | self.0.get() as *mut u8 41 | } 42 | } 43 | 44 | /// Register. 45 | #[derive(Copy, Clone, PartialEq, Debug)] 46 | pub struct Reg(u8); 47 | 48 | impl Reg { 49 | pub fn from(num: u64) -> Self { 50 | Reg(num as u8) 51 | } 52 | 53 | pub fn is_rip(&self) -> bool { 54 | self == &Self::rip() 55 | } 56 | 57 | pub fn is_cl(&self) -> bool { 58 | self == &Self::rcx() 59 | } 60 | 61 | pub fn is_rax(&self) -> bool { 62 | self == &Self::rax() 63 | } 64 | } 65 | 66 | impl Reg { 67 | fn rax() -> Self { 68 | Self::from(0) 69 | } 70 | fn rcx() -> Self { 71 | Self::from(1) 72 | } 73 | 74 | fn rbp() -> Self { 75 | Self::from(5) 76 | } 77 | 78 | fn rip() -> Self { 79 | Self::from(16) 80 | } 81 | } 82 | 83 | impl std::fmt::Display for Reg { 84 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 85 | write!(f, "R({})", self.0) 86 | } 87 | } 88 | 89 | /// Displacement for indirect addressing. 90 | #[derive(Copy, Clone, PartialEq, Debug)] 91 | pub enum Disp { 92 | None, 93 | D8(i8), 94 | D32(i32), 95 | Label(DestLabel), 96 | } 97 | 98 | impl Disp { 99 | pub fn from_disp(disp: i32) -> Self { 100 | match disp { 101 | 0 => Disp::None, 102 | disp => { 103 | if let Ok(disp) = i8::try_from(disp) { 104 | Disp::D8(disp) 105 | } else { 106 | Disp::D32(disp) 107 | } 108 | } 109 | } 110 | } 111 | 112 | pub fn from_label(label: DestLabel) -> Self { 113 | Disp::Label(label) 114 | } 115 | } 116 | 117 | /// Scale index for indirect addressing. 118 | #[derive(Copy, Clone, PartialEq, Debug)] 119 | pub enum Scale { 120 | None, 121 | S1(u8, Reg), 122 | } 123 | 124 | impl Scale { 125 | fn index(&self) -> Reg { 126 | match self { 127 | Self::None => Reg(0), 128 | Self::S1(_, r) => *r, 129 | } 130 | } 131 | } 132 | 133 | pub enum Imm { 134 | None, 135 | B(i8), 136 | W(i16), 137 | L(i32), 138 | Q(i64), 139 | } 140 | 141 | impl Imm { 142 | pub fn offset(&self) -> u8 { 143 | match self { 144 | Self::None => 0, 145 | Self::B(_) => 1, 146 | Self::W(_) => 2, 147 | Self::L(_) => 4, 148 | Self::Q(_) => 8, 149 | } 150 | } 151 | } 152 | 153 | /// Destination for jump and call instructions. 154 | #[derive(Copy, Clone, PartialEq, Debug)] 155 | pub enum Dest { 156 | /// Register 157 | Reg(Reg), 158 | /// Relative 159 | Rel(usize), 160 | } 161 | 162 | /// Adressing modes. 163 | #[derive(Copy, Clone, PartialEq, Debug)] 164 | pub enum Mode { 165 | Reg, 166 | Ind(Scale, Disp), // [reg + disp] 167 | } 168 | 169 | impl Mode { 170 | fn encode(&self) -> u8 { 171 | match self { 172 | Mode::Reg => 3, 173 | Mode::Ind(_, Disp::None) => 0, 174 | Mode::Ind(_, Disp::D8(_)) => 1, 175 | Mode::Ind(_, Disp::D32(_)) => 2, 176 | Mode::Ind(_, Disp::Label(_)) => 2, 177 | } 178 | } 179 | 180 | fn scale(&self) -> Scale { 181 | match self { 182 | Mode::Reg => Scale::None, 183 | Mode::Ind(scale, _) => *scale, 184 | } 185 | } 186 | 187 | fn disp(&self) -> Disp { 188 | match self { 189 | Mode::Reg => Disp::None, 190 | Mode::Ind(_, disp) => *disp, 191 | } 192 | } 193 | 194 | fn is_indirect_no_disp(&self) -> bool { 195 | matches!(self, Mode::Ind(_, Disp::None)) 196 | } 197 | } 198 | 199 | /// Register / Memory reference Operands. 200 | #[derive(Copy, Clone, PartialEq, Debug)] 201 | pub struct Rm { 202 | base: Reg, 203 | mode: Mode, 204 | } 205 | 206 | impl std::convert::From for Rm { 207 | fn from(value: Reg) -> Self { 208 | Rm::reg(value) 209 | } 210 | } 211 | 212 | impl Rm { 213 | pub fn reg(base: Reg) -> Self { 214 | Self { 215 | base, 216 | mode: Mode::Reg, 217 | } 218 | } 219 | 220 | pub fn is_reg(&self) -> bool { 221 | self.mode == Mode::Reg 222 | } 223 | 224 | pub fn is_rax(&self) -> bool { 225 | self.mode == Mode::Reg && self.base.is_rax() 226 | } 227 | 228 | pub fn ind(base: Reg, disp: Disp, scale: Scale) -> Self { 229 | let mode = Mode::Ind(scale, disp); 230 | Self { base, mode } 231 | } 232 | 233 | pub fn rip_ind_from(rm: Rm) -> Self { 234 | let disp = match rm.mode { 235 | Mode::Reg => unimplemented!("register direct addressing is not allowed for RIP."), 236 | Mode::Ind(Scale::None, Disp::D8(d)) => d as i32, 237 | Mode::Ind(Scale::None, Disp::D32(d)) => d, 238 | Mode::Ind(Scale::None, Disp::None) => 0, 239 | Mode::Ind(Scale::None, Disp::Label(label)) => { 240 | return Self { 241 | base: Reg::rbp(), 242 | mode: Mode::Ind(Scale::None, Disp::Label(label)), 243 | } 244 | } 245 | _ => unimplemented!("scale index is not allowed for RIP."), 246 | }; 247 | Self { 248 | base: Reg::rbp(), 249 | mode: Mode::Ind(Scale::None, Disp::D32(disp)), 250 | } 251 | } 252 | } 253 | 254 | /// Position in JitMemory. 255 | #[derive(Copy, Clone, PartialEq, Debug)] 256 | pub struct Pos(usize); 257 | 258 | impl Pos { 259 | pub fn from(pos: usize) -> Self { 260 | Pos(pos) 261 | } 262 | } 263 | 264 | impl Add for Pos { 265 | type Output = Pos; 266 | 267 | fn add(self, other: i32) -> Self { 268 | Pos((self.0 as i64 + other as i64) as usize) 269 | } 270 | } 271 | 272 | impl Sub for Pos { 273 | type Output = usize; 274 | 275 | fn sub(self, other: Pos) -> Self::Output { 276 | self.0 - other.0 277 | } 278 | } 279 | 280 | /// Id for destination label. 281 | #[derive(Copy, Clone, PartialEq, Default, Debug)] 282 | #[repr(transparent)] 283 | pub struct DestLabel(usize); 284 | 285 | impl DestLabel { 286 | pub fn to_usize(&self) -> usize { 287 | self.0 288 | } 289 | } 290 | 291 | /// 292 | /// Relocation 293 | /// 294 | /// This holds a pair of a location in JitMemory (whether determined or not) 295 | /// and (possibly multiple) target positions for each *DestLabel*. 296 | /// 297 | #[derive(Clone, PartialEq, Debug)] 298 | enum LabelInfo { 299 | /// A location of each *DestLabel* in JitMemory. 300 | /// None for not yet determined. 301 | Resolved((Page, Pos)), 302 | /// Target informations. 303 | NotResolved(Vec), 304 | } 305 | 306 | impl LabelInfo { 307 | fn new() -> LabelInfo { 308 | LabelInfo::NotResolved(vec![]) 309 | } 310 | 311 | fn loc(&self) -> (Page, Pos) { 312 | match self { 313 | LabelInfo::Resolved(loc) => *loc, 314 | _ => panic!("The DestLabel has not been resolved"), 315 | } 316 | } 317 | } 318 | 319 | #[derive(Clone, PartialEq, Debug)] 320 | enum TargetType { 321 | Rel { page: Page, offset: u8, pos: Pos }, 322 | Abs { page: Page, pos: Pos }, 323 | } 324 | 325 | /// Relocation tabla. 326 | #[derive(Debug, Default)] 327 | struct Labels(Vec); 328 | 329 | impl Labels { 330 | fn new() -> Self { 331 | Labels(vec![]) 332 | } 333 | 334 | fn new_label(&mut self) -> DestLabel { 335 | let label = DestLabel(self.0.len()); 336 | self.0.push(LabelInfo::new()); 337 | label 338 | } 339 | } 340 | 341 | impl Index for Labels { 342 | type Output = LabelInfo; 343 | 344 | fn index(&self, dest: DestLabel) -> &Self::Output { 345 | &self.0[dest.0] 346 | } 347 | } 348 | 349 | impl IndexMut for Labels { 350 | fn index_mut(&mut self, dest: DestLabel) -> &mut Self::Output { 351 | &mut self.0[dest.0] 352 | } 353 | } 354 | 355 | impl Deref for Labels { 356 | type Target = Vec; 357 | fn deref(&self) -> &Self::Target { 358 | &self.0 359 | } 360 | } 361 | 362 | impl DerefMut for Labels { 363 | fn deref_mut(&mut self) -> &mut Self::Target { 364 | &mut self.0 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /gen.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Automatic verification tool for monoasm 3 | # 4 | 5 | def reg_template(size) 6 | if size ==8 7 | ["rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"] 8 | elsif size == 4 9 | ["eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d"] 10 | elsif size == 2 11 | ["ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w"] 12 | elsif size == 1 13 | ["al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b"] 14 | else 15 | raise "invalid size descriptor #{size}" 16 | end 17 | end 18 | 19 | INDEX_TEMPLATE = [ 20 | "rax", 21 | "r15", 22 | ] 23 | 24 | CONDITIONS = [ 25 | "o", 26 | "no", 27 | "b", 28 | "c", 29 | "nae", 30 | "ae", 31 | "nc", 32 | "eq", 33 | "e", 34 | "z", 35 | "ne", 36 | "nz", 37 | "be", 38 | "na", 39 | "a", 40 | "nbe", 41 | "s", 42 | "ns", 43 | "p", 44 | "pe", 45 | "np", 46 | "po", 47 | "lt", 48 | "nge", 49 | "ge", 50 | "nl", 51 | "le", 52 | "ng", 53 | "gt", 54 | "nle" 55 | ] 56 | 57 | def indirect_template(size) 58 | (reg_template(8) + ["rip"]).map do |r| 59 | [ 60 | "[#{r}]", 61 | "[#{r} + 16]", 62 | "[#{r} + 512]" 63 | ] 64 | end.flatten + 65 | reg_template(8).map do |r| 66 | INDEX_TEMPLATE.map do |i| 67 | ["1", "8"].map do |s| 68 | [ 69 | "[#{r} + #{i} * #{s}]", 70 | "[#{r} + #{i} * #{s} + 20]" 71 | ] 72 | end 73 | end 74 | end.flatten 75 | end 76 | 77 | def asm_indirect_template(size) 78 | prefix = if size == 8 79 | "QWORD PTR" 80 | elsif size == 4 81 | "DWORD PTR" 82 | elsif size == 2 83 | "WORD PTR" 84 | elsif size == 1 85 | "BYTE PTR" 86 | else 87 | raise "invalid size descriptor #{size}" 88 | end 89 | (reg_template(8) + ["rip"]).map do |r| 90 | [ 91 | "#{prefix} [#{r}]", 92 | "#{prefix} [#{r} + 16]", 93 | "#{prefix} [#{r} + 512]" 94 | ] 95 | end.flatten + 96 | reg_template(8).map do |r| 97 | INDEX_TEMPLATE.map do |i| 98 | ["1", "8"].map do |s| 99 | [ 100 | "#{prefix} [#{r} + #{i} * #{s}]", 101 | "#{prefix} [#{r} + #{i} * #{s} + 20]", 102 | ] 103 | end 104 | end 105 | end.flatten 106 | end 107 | 108 | IMM_TEMPLATE = ["1", "18"] 109 | 110 | ASM_HEADER = < monoasm/tests/gen/#{inst}.txt` 220 | `objdump -D -Mintel,x86-64 -b binary -m i386 monoasm/tests/gen/#{inst}_monoasm.bin > monoasm/tests/gen/#{inst}_monoasm.txt` 221 | end 222 | end 223 | 224 | private 225 | 226 | def self.header 227 | < extern "C" fn(u64) -> u64 { 210 | let mut vm = VM::new(); 211 | // Jump labels must be declared as local variable in advance. 212 | let fibo = vm.jit.label(); 213 | let label0 = vm.jit.label(); 214 | let label1 = vm.jit.label(); 215 | let end = vm.jit.label(); 216 | // Entry point 217 | vm.jit.bind_label(fibo); 218 | // Negative offset to rbp for local variable 'x'. 219 | let x_ofs = 8; 220 | // Working registers: %0:r12 %1:r13 %2:r14 221 | vm.prologue(1); 222 | monoasm! {&mut vm.jit, 223 | pushq r12; pushq r13; pushq r14; pushq r15; 224 | // Load argument to x. 225 | // You can use Rust expression in displacement, register designation.. etc. 226 | // Rust expression must be wrapped with (). 227 | movq [rbp-(x_ofs)], rdi; 228 | } 229 | // %0 <- x 230 | vm.get_local(0, x_ofs); 231 | // %1 <- 0 232 | vm.set_int(1, 0); 233 | // cmp %0, %1 234 | vm.cmp(0, 1); 235 | vm.jne(label0); 236 | monoasm! {&mut vm.jit, 237 | movq rax, 0; 238 | jmp end; 239 | label0: 240 | } 241 | // %1 <- 1 242 | vm.set_int(1, 1); 243 | // cmp %0, %1 244 | vm.cmp(0, 1); 245 | vm.jne(label1); 246 | monoasm! {&mut vm.jit, 247 | movq rax, 1; 248 | jmp end; 249 | label1: 250 | } 251 | // %0 <- x 252 | vm.get_local(0, x_ofs); 253 | // %1 <- 1 254 | vm.set_int(1, 1); 255 | // %0 <- %0 - %1 256 | vm.sub(0, 1); 257 | // %0 <- fibo(%0) 258 | vm.call_arg1(fibo, 0, 0); 259 | // %1 <- x 260 | vm.get_local(1, x_ofs); 261 | // %2 <- 2 262 | vm.set_int(2, 2); 263 | // %1 <- %1 - %2 264 | vm.sub(1, 2); 265 | // %1 <- fibo(%1) 266 | vm.call_arg1(fibo, 1, 1); 267 | // %0 <- %0 + %1 268 | vm.add(0, 1); 269 | monoasm! {&mut vm.jit, 270 | movq rax, R(12); 271 | } 272 | 273 | monoasm! {&mut vm.jit, 274 | end: 275 | popq r15; popq r14; popq r13; popq r12; 276 | } 277 | vm.epilogue(); 278 | vm.jit.finalize(); 279 | vm.jit.get_label_addr(fibo) as _ 280 | } 281 | 282 | struct VM { 283 | jit: JitMemory, 284 | } 285 | 286 | // Virtual machine codes 287 | #[allow(dead_code)] 288 | impl VM { 289 | fn new() -> Self { 290 | Self { 291 | jit: JitMemory::new(), 292 | } 293 | } 294 | 295 | fn prologue(&mut self, locals: usize) { 296 | monoasm! {&mut self.jit, 297 | pushq rbp; 298 | movq rbp, rsp; 299 | subq rsp, ((locals + locals % 2)*8); 300 | } 301 | } 302 | 303 | fn epilogue(&mut self) { 304 | monoasm! {&mut self.jit, 305 | movq rsp, rbp; 306 | popq rbp; 307 | ret; 308 | } 309 | } 310 | 311 | fn set_int(&mut self, reg: u64, val: i64) { 312 | // You can use Rust expression for register designation. 313 | // ex. R(reg + 12) 314 | monoasm! {&mut self.jit, 315 | movq R(reg + 12), (val as u64); 316 | } 317 | } 318 | 319 | fn set_local(&mut self, reg: u64, offset: i64) { 320 | monoasm! {&mut self.jit, 321 | movq [rbp-(offset)], R(reg + 12); 322 | } 323 | } 324 | 325 | fn get_local(&mut self, reg: u64, offset: i64) { 326 | monoasm! {&mut self.jit, 327 | movq R(reg + 12), [rbp-(offset)]; 328 | } 329 | } 330 | 331 | fn sub(&mut self, dest_reg: u64, src_reg: u64) { 332 | monoasm! {&mut self.jit, 333 | subq R(dest_reg + 12), R(src_reg + 12); 334 | } 335 | } 336 | 337 | fn add(&mut self, dest_reg: u64, src_reg: u64) { 338 | monoasm! {&mut self.jit, 339 | addq R(dest_reg + 12), R(src_reg + 12); 340 | } 341 | } 342 | 343 | fn cmp(&mut self, dest_reg: u64, src_reg: u64) { 344 | monoasm! {&mut self.jit, 345 | cmpq R(dest_reg + 12), R(src_reg + 12); 346 | } 347 | } 348 | 349 | fn jne(&mut self, dest: DestLabel) { 350 | monoasm!(&mut self.jit, jne dest;); 351 | } 352 | 353 | fn call_arg1(&mut self, dest: DestLabel, arg0_reg: u64, ret_reg: u64) { 354 | monoasm! {&mut self.jit, 355 | movq rdi, R(arg0_reg + 12); 356 | call dest; 357 | movq R(ret_reg + 12), rax; 358 | } 359 | } 360 | 361 | fn puts(&mut self, reg: u64) { 362 | monoasm! {&mut self.jit, 363 | movq rdi, R(reg + 12); 364 | movq rax, (monoasm::test::PUTINT as u64); 365 | call rax; 366 | } 367 | } 368 | } 369 | 370 | #[test] 371 | fn vm_fibo() { 372 | let func = jit(); 373 | let x = 35; 374 | let ret = func(x); 375 | eprintln!("fib( {} ) = {}", x, ret); 376 | assert_eq!(9227465, ret) 377 | } 378 | ``` 379 | 380 | Most assemblies written using `monoasm!()` macros are compiled into x86 machine code at compile time, but some codes such as immediate values or register designation using Rust variables and functions, are resolved at run time. 381 | Here is a Rust code generated by `monoasm!()` macros. 382 | 383 | ```Rust 384 | jit . emitb(65u8) ; jit . emitb(84u8) ; 385 | jit . emitb(65u8) ; jit . emitb(85u8) ; 386 | jit . emitb(65u8) ; jit . emitb(86u8) ; 387 | jit . emitb(65u8) ; jit . emitb(87u8) ; 388 | jit . emitb(72u8) ; jit . emitb(137u8) ; jit . emitb(189u8) ; 389 | jit . emitl((- (x_ofs)) as u32) ; 390 | let imm = (0) as u64 ; if imm <= 0xffff_ffff { 391 | jit . emitb(72u8) ; jit . emitb(199u8) ; jit . emitb(192u8) ; 392 | jit . emitl(imm as u32) ; 393 | } else { 394 | jit . emitb(72u8) ; jit . emitb(184u8) ; jit . emitq(imm) ; 395 | } 396 | jit . emitb(0xe9) ; jit . save_reloc(end, 4) ; jit . emitl(0) ; 397 | jit . bind_label(label0) ; 398 | let imm = (1) as u64 ; if imm <= 0xffff_ffff { 399 | jit . emitb(72u8) ; jit . emitb(199u8) ; jit . emitb(192u8) ; 400 | jit . emitl(imm as u32) ; 401 | } else { 402 | jit . emitb(72u8) ; jit . emitb(184u8) ; jit . emitq(imm) ; 403 | } 404 | jit . emitb(0xe9) ; jit . save_reloc(end, 4) ; jit . emitl(0) ; 405 | jit . bind_label(label1) ; 406 | jit . emitb(76u8) ; jit . emitb(137u8) ; jit . emitb(224u8) ; 407 | jit . bind_label(end) ; 408 | jit . emitb(65u8) ; jit . emitb(95u8) ; 409 | jit . emitb(65u8) ; jit . emitb(94u8) ; 410 | jit . emitb(65u8) ; jit . emitb(93u8) ; 411 | jit . emitb(65u8) ; jit . emitb(92u8) ; 412 | jit . emitb(85u8) ; 413 | jit . emitb(72u8) ; jit . emitb(137u8) ; jit . emitb(229u8) ; 414 | let imm = ((locals + locals % 2) * 8) as u64 ; 415 | if imm > 0xffff_ffff { 416 | panic ! ("'{} {}, imm64' does not exists.", "SUB", "Rsp") ; 417 | } 418 | jit . emitb(72u8) ; jit . emitb(129u8) ; jit . emitb(236u8) ; 419 | jit . emitl(imm as u32) ; 420 | jit . emitb(72u8) ; jit . emitb(137u8) ; jit . emitb(236u8) ; 421 | jit . emitb(93u8) ; 422 | jit . emitb(0xc3) ; 423 | let imm = (val as u64) as u64 ; 424 | let r = Reg :: from(reg + 12 as u64) ; 425 | let rm_op = Or :: Reg(r) ; 426 | if imm <= 0xffff_ffff { 427 | jit . enc_mi(0xc7, rm_op) ; jit . emitl(imm as u32) ; 428 | } else { 429 | jit . enc_o(0xb8, r) ; jit . emitq(imm) ; 430 | } ; 431 | let r1 = Reg :: from((reg + 12) as u64) ; 432 | jit . enc_mr(137u8, r1, Or :: IndD32(Reg :: from(5u64), (- (offset)) as i32)) ; 433 | let r1 = Reg :: from((reg + 12) as u64) ; 434 | jit . enc_mr(139u8, r1, Or :: IndD32(Reg :: from(5u64), (- (offset)) as i32)) ; 435 | let r2 = Reg :: from((src_reg + 12) as u64) ; 436 | jit . enc_mr(41u8, r2, Or :: Reg(Reg :: from((dest_reg + 12) as u64))) ; 437 | let r2 = Reg :: from((src_reg + 12) as u64) ; 438 | jit . enc_mr(1u8, r2, Or :: Reg(Reg :: from((dest_reg + 12) as u64))) ; 439 | let r2 = Reg :: from((src_reg + 12) as u64) ; 440 | jit . enc_mr(57u8, r2, Or :: Reg(Reg :: from((dest_reg + 12) as u64))) ; 441 | jit . emitb(0x0f) ; jit . emitb(0x85) ; jit . save_reloc(dest, 4) ; 442 | jit . emitl(0) ; 443 | let r1 = Reg :: from((arg0_reg + 12) as u64) ; 444 | jit . enc_mr(137u8, r1, Or :: Reg(Reg :: from(7u64))) ; 445 | jit . emitb(0xe8) ; jit . save_reloc(dest, 4) ; jit . emitl(0) ; 446 | let r1 = Reg :: from((ret_reg + 12) as u64) ; 447 | jit . enc_mr(139u8, r1, Or :: Reg(Reg :: from(0u64))) ; 448 | let r1 = Reg :: from((reg + 12) as u64) ; 449 | jit . enc_mr(137u8, r1, Or :: Reg(Reg :: from(7u64))) ; 450 | let imm = (monoasm :: test :: PUTINT as u64) as u64 ; 451 | if imm <= 0xffff_ffff { 452 | jit . emitb(72u8) ; jit . emitb(199u8) ; jit . emitb(192u8) ; 453 | jit . emitl(imm as u32) ; 454 | } else { 455 | jit . emitb(72u8) ; jit . emitb(184u8) ; jit . emitq(imm) ; 456 | } 457 | jit . emitb(0xff) ; jit . emitb(208u8) ; 458 | ``` 459 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "android-tzdata" 7 | version = "0.1.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 10 | 11 | [[package]] 12 | name = "android_system_properties" 13 | version = "0.1.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 16 | dependencies = [ 17 | "libc", 18 | ] 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.4.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 25 | 26 | [[package]] 27 | name = "bitflags" 28 | version = "1.3.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 31 | 32 | [[package]] 33 | name = "bitflags" 34 | version = "2.6.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 37 | 38 | [[package]] 39 | name = "bumpalo" 40 | version = "3.16.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 43 | 44 | [[package]] 45 | name = "cc" 46 | version = "1.2.3" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" 49 | dependencies = [ 50 | "shlex", 51 | ] 52 | 53 | [[package]] 54 | name = "cfg-if" 55 | version = "1.0.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 58 | 59 | [[package]] 60 | name = "chrono" 61 | version = "0.4.39" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" 64 | dependencies = [ 65 | "android-tzdata", 66 | "iana-time-zone", 67 | "js-sys", 68 | "num-traits", 69 | "wasm-bindgen", 70 | "windows-targets", 71 | ] 72 | 73 | [[package]] 74 | name = "core-foundation-sys" 75 | version = "0.8.7" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 78 | 79 | [[package]] 80 | name = "errno" 81 | version = "0.3.10" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 84 | dependencies = [ 85 | "libc", 86 | "windows-sys 0.59.0", 87 | ] 88 | 89 | [[package]] 90 | name = "fastrand" 91 | version = "2.3.0" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 94 | 95 | [[package]] 96 | name = "iana-time-zone" 97 | version = "0.1.61" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 100 | dependencies = [ 101 | "android_system_properties", 102 | "core-foundation-sys", 103 | "iana-time-zone-haiku", 104 | "js-sys", 105 | "wasm-bindgen", 106 | "windows-core", 107 | ] 108 | 109 | [[package]] 110 | name = "iana-time-zone-haiku" 111 | version = "0.1.2" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 114 | dependencies = [ 115 | "cc", 116 | ] 117 | 118 | [[package]] 119 | name = "js-sys" 120 | version = "0.3.76" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" 123 | dependencies = [ 124 | "once_cell", 125 | "wasm-bindgen", 126 | ] 127 | 128 | [[package]] 129 | name = "libc" 130 | version = "0.2.168" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" 133 | 134 | [[package]] 135 | name = "linux-raw-sys" 136 | version = "0.4.14" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 139 | 140 | [[package]] 141 | name = "log" 142 | version = "0.4.22" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 145 | 146 | [[package]] 147 | name = "mach2" 148 | version = "0.4.2" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" 151 | dependencies = [ 152 | "libc", 153 | ] 154 | 155 | [[package]] 156 | name = "monoasm" 157 | version = "0.1.0" 158 | dependencies = [ 159 | "chrono", 160 | "libc", 161 | "monoasm_macro", 162 | "proc-macro2", 163 | "region", 164 | "syn", 165 | "tempfile", 166 | ] 167 | 168 | [[package]] 169 | name = "monoasm_macro" 170 | version = "0.1.0" 171 | dependencies = [ 172 | "proc-macro2", 173 | "quote", 174 | "syn", 175 | ] 176 | 177 | [[package]] 178 | name = "num-traits" 179 | version = "0.2.19" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 182 | dependencies = [ 183 | "autocfg", 184 | ] 185 | 186 | [[package]] 187 | name = "once_cell" 188 | version = "1.20.2" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 191 | 192 | [[package]] 193 | name = "proc-macro2" 194 | version = "1.0.92" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 197 | dependencies = [ 198 | "unicode-ident", 199 | ] 200 | 201 | [[package]] 202 | name = "quote" 203 | version = "1.0.37" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 206 | dependencies = [ 207 | "proc-macro2", 208 | ] 209 | 210 | [[package]] 211 | name = "region" 212 | version = "3.0.2" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" 215 | dependencies = [ 216 | "bitflags 1.3.2", 217 | "libc", 218 | "mach2", 219 | "windows-sys 0.52.0", 220 | ] 221 | 222 | [[package]] 223 | name = "rustix" 224 | version = "0.38.42" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" 227 | dependencies = [ 228 | "bitflags 2.6.0", 229 | "errno", 230 | "libc", 231 | "linux-raw-sys", 232 | "windows-sys 0.59.0", 233 | ] 234 | 235 | [[package]] 236 | name = "shlex" 237 | version = "1.3.0" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 240 | 241 | [[package]] 242 | name = "syn" 243 | version = "2.0.90" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" 246 | dependencies = [ 247 | "proc-macro2", 248 | "quote", 249 | "unicode-ident", 250 | ] 251 | 252 | [[package]] 253 | name = "tempfile" 254 | version = "3.14.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" 257 | dependencies = [ 258 | "cfg-if", 259 | "fastrand", 260 | "once_cell", 261 | "rustix", 262 | "windows-sys 0.59.0", 263 | ] 264 | 265 | [[package]] 266 | name = "unicode-ident" 267 | version = "1.0.14" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 270 | 271 | [[package]] 272 | name = "wasm-bindgen" 273 | version = "0.2.99" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" 276 | dependencies = [ 277 | "cfg-if", 278 | "once_cell", 279 | "wasm-bindgen-macro", 280 | ] 281 | 282 | [[package]] 283 | name = "wasm-bindgen-backend" 284 | version = "0.2.99" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" 287 | dependencies = [ 288 | "bumpalo", 289 | "log", 290 | "proc-macro2", 291 | "quote", 292 | "syn", 293 | "wasm-bindgen-shared", 294 | ] 295 | 296 | [[package]] 297 | name = "wasm-bindgen-macro" 298 | version = "0.2.99" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" 301 | dependencies = [ 302 | "quote", 303 | "wasm-bindgen-macro-support", 304 | ] 305 | 306 | [[package]] 307 | name = "wasm-bindgen-macro-support" 308 | version = "0.2.99" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" 311 | dependencies = [ 312 | "proc-macro2", 313 | "quote", 314 | "syn", 315 | "wasm-bindgen-backend", 316 | "wasm-bindgen-shared", 317 | ] 318 | 319 | [[package]] 320 | name = "wasm-bindgen-shared" 321 | version = "0.2.99" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" 324 | 325 | [[package]] 326 | name = "windows-core" 327 | version = "0.52.0" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 330 | dependencies = [ 331 | "windows-targets", 332 | ] 333 | 334 | [[package]] 335 | name = "windows-sys" 336 | version = "0.52.0" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 339 | dependencies = [ 340 | "windows-targets", 341 | ] 342 | 343 | [[package]] 344 | name = "windows-sys" 345 | version = "0.59.0" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 348 | dependencies = [ 349 | "windows-targets", 350 | ] 351 | 352 | [[package]] 353 | name = "windows-targets" 354 | version = "0.52.6" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 357 | dependencies = [ 358 | "windows_aarch64_gnullvm", 359 | "windows_aarch64_msvc", 360 | "windows_i686_gnu", 361 | "windows_i686_gnullvm", 362 | "windows_i686_msvc", 363 | "windows_x86_64_gnu", 364 | "windows_x86_64_gnullvm", 365 | "windows_x86_64_msvc", 366 | ] 367 | 368 | [[package]] 369 | name = "windows_aarch64_gnullvm" 370 | version = "0.52.6" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 373 | 374 | [[package]] 375 | name = "windows_aarch64_msvc" 376 | version = "0.52.6" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 379 | 380 | [[package]] 381 | name = "windows_i686_gnu" 382 | version = "0.52.6" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 385 | 386 | [[package]] 387 | name = "windows_i686_gnullvm" 388 | version = "0.52.6" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 391 | 392 | [[package]] 393 | name = "windows_i686_msvc" 394 | version = "0.52.6" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 397 | 398 | [[package]] 399 | name = "windows_x86_64_gnu" 400 | version = "0.52.6" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 403 | 404 | [[package]] 405 | name = "windows_x86_64_gnullvm" 406 | version = "0.52.6" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 409 | 410 | [[package]] 411 | name = "windows_x86_64_msvc" 412 | version = "0.52.6" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 415 | -------------------------------------------------------------------------------- /monoasm_macro/src/parse.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Group; 2 | use proc_macro2::Punct; 3 | use proc_macro2::TokenStream; 4 | use quote::{quote, ToTokens}; 5 | use syn::parse::ParseBuffer; 6 | use syn::{ 7 | parse::{Parse, ParseStream}, 8 | Expr, 9 | }; 10 | use syn::{token, Error, Ident, LitInt, Token}; 11 | 12 | ///---------------------------------------------------------------------- 13 | /// 14 | /// General Registers. 15 | /// 16 | ///---------------------------------------------------------------------- 17 | #[derive(Copy, Clone, PartialEq, Debug)] 18 | pub enum Reg { 19 | Rax = 0, 20 | Rcx = 1, 21 | Rdx = 2, 22 | Rbx = 3, 23 | Rsp = 4, 24 | Rbp = 5, 25 | Rsi = 6, 26 | Rdi = 7, 27 | R8 = 8, 28 | R9 = 9, 29 | R10 = 10, 30 | R11 = 11, 31 | R12 = 12, 32 | R13 = 13, 33 | R14 = 14, 34 | R15 = 15, 35 | RIP = 16, 36 | } 37 | 38 | impl Reg { 39 | fn is_rip(&self) -> bool { 40 | self == &Reg::RIP 41 | } 42 | 43 | pub fn from(num: u64) -> Self { 44 | match num { 45 | 0 => Reg::Rax, 46 | 1 => Reg::Rcx, 47 | 2 => Reg::Rdx, 48 | 3 => Reg::Rbx, 49 | 4 => Reg::Rsp, 50 | 5 => Reg::Rbp, 51 | 6 => Reg::Rsi, 52 | 7 => Reg::Rdi, 53 | 8 => Reg::R8, 54 | 9 => Reg::R9, 55 | 10 => Reg::R10, 56 | 11 => Reg::R11, 57 | 12 => Reg::R12, 58 | 13 => Reg::R13, 59 | 14 => Reg::R14, 60 | 15 => Reg::R15, 61 | 16 => Reg::RIP, 62 | _ => unreachable!("Illegal register number."), 63 | } 64 | } 65 | 66 | pub fn from_str(string: impl Into) -> Option { 67 | let mut string = string.into(); 68 | string.make_ascii_lowercase(); 69 | let reg = match string.as_str() { 70 | "rax" => Reg::Rax, 71 | "rcx" => Reg::Rcx, 72 | "cl" => Reg::Rcx, 73 | "rdx" => Reg::Rdx, 74 | "rbx" => Reg::Rbx, 75 | "rsp" => Reg::Rsp, 76 | "rbp" => Reg::Rbp, 77 | "rsi" => Reg::Rsi, 78 | "rdi" => Reg::Rdi, 79 | "r8" => Reg::R8, 80 | "r9" => Reg::R9, 81 | "r10" => Reg::R10, 82 | "r11" => Reg::R11, 83 | "r12" => Reg::R12, 84 | "r13" => Reg::R13, 85 | "r14" => Reg::R14, 86 | "r15" => Reg::R15, 87 | "rip" => Reg::RIP, 88 | _ => return None, 89 | }; 90 | Some(reg) 91 | } 92 | } 93 | 94 | impl ToTokens for Reg { 95 | fn to_tokens(&self, tokens: &mut TokenStream) { 96 | let r = *self as u64; 97 | let ts = quote!(Reg::from(#r)); 98 | tokens.extend(ts); 99 | } 100 | } 101 | 102 | ///---------------------------------------------------------------------- 103 | /// 104 | /// Addressing modes. 105 | /// 106 | ///---------------------------------------------------------------------- 107 | #[derive(Copy, Clone, PartialEq)] 108 | pub enum Mode { 109 | Ind = 0, // [reg] 110 | InD8 = 1, // [reg + disp8] 111 | InD32 = 2, // [reg + disp32] 112 | Reg = 3, // reg 113 | } 114 | 115 | ///---------------------------------------------------------------------- 116 | /// 117 | /// Designation for general registers. 118 | /// 119 | ///---------------------------------------------------------------------- 120 | #[derive(Clone, Debug)] 121 | pub enum Register { 122 | Reg(u8), 123 | Expr(TokenStream), 124 | } 125 | 126 | impl Parse for Register { 127 | fn parse(input: ParseStream) -> Result { 128 | Self::parse_register(input, input.parse::()?.to_string()) 129 | } 130 | } 131 | 132 | impl ToTokens for Register { 133 | fn to_tokens(&self, tokens: &mut TokenStream) { 134 | let ts = match self { 135 | Self::Reg(n) => quote! ( Reg::from(#n as u64) ), 136 | Self::Expr(ts) => quote! ( Reg::from(#ts) ), 137 | }; 138 | tokens.extend(ts); 139 | } 140 | } 141 | 142 | impl std::fmt::Display for Register { 143 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 144 | match self { 145 | Self::Reg(n) => write!(f, "R({})", n), 146 | Self::Expr(ts) => write!(f, "R({})", ts), 147 | } 148 | } 149 | } 150 | 151 | impl Register { 152 | fn check_register(input: &ParseBuffer, ident: &Ident) -> Result, Error> { 153 | if ident == "R" { 154 | // e.g. "R(13)" 155 | let content; 156 | syn::parenthesized!(content in input); 157 | let s = content.parse::()?; 158 | Ok(Some(Register::Expr(quote!(#s)))) 159 | } else { 160 | // e.g. "rax" 161 | match Reg::from_str(ident.to_string()) { 162 | Some(reg) => Ok(Some(Register::Reg(reg as u64 as u8))), 163 | None => Ok(None), 164 | } 165 | } 166 | } 167 | 168 | pub fn parse_register(input: ParseStream, ident: String) -> Result { 169 | if ident == "R" { 170 | // e.g. "R(13)" 171 | let content; 172 | syn::parenthesized!(content in input); 173 | let s = content.parse::()?; 174 | Ok(Register::Expr(quote!(#s))) 175 | } else { 176 | // e.g. "rax" 177 | let reg = Reg::from_str(ident).ok_or(input.error("Expected register name."))? as u8; 178 | Ok(Register::Reg(reg)) 179 | } 180 | } 181 | } 182 | 183 | ///---------------------------------------------------------------------- 184 | /// 185 | /// Floating pointer register(xmm). 186 | /// 187 | ///---------------------------------------------------------------------- 188 | #[derive(Clone, Debug)] 189 | pub struct Xmm(pub TokenStream); 190 | 191 | fn parse_xmm(input: ParseStream, ident: &String) -> Result { 192 | assert!(ident.starts_with("xmm")); 193 | if ident.len() == 3 { 194 | if input.peek(token::Paren) { 195 | let gr = input.parse::()?; 196 | Ok(gr.stream()) 197 | } else { 198 | Err(input.error(format!( 199 | "Expected xmm register number. e.g. xmm0 or xmm(0) actual:{}", 200 | ident, 201 | ))) 202 | } 203 | } else if let Ok(no) = ident[3..].parse::() { 204 | if no > 15 { 205 | Err(input.error(format!("Invalid xmm register name. {}", ident))) 206 | } else { 207 | Ok(quote!(#no as u64)) 208 | } 209 | } else { 210 | Err(input.error(format!("Invalid xmm register name. {}", ident))) 211 | } 212 | } 213 | 214 | impl Parse for Xmm { 215 | fn parse(input: ParseStream) -> Result { 216 | if input.peek(Ident) { 217 | let reg = input.parse::()?.to_string(); 218 | if reg.starts_with("xmm") { 219 | Ok(Xmm(parse_xmm(input, ®)?)) 220 | } else { 221 | Err(input.error("Expected xmm register name.")) 222 | } 223 | } else { 224 | Err(input.error("Expected xmm register name.")) 225 | } 226 | } 227 | } 228 | 229 | impl std::fmt::Display for Xmm { 230 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 231 | write!(f, "xmm({})", self.0) 232 | } 233 | } 234 | 235 | impl ToTokens for Xmm { 236 | fn to_tokens(&self, tokens: &mut TokenStream) { 237 | let r = &self.0; 238 | let ts = quote!(Rm::reg(Reg::from(#r))); 239 | tokens.extend(ts); 240 | } 241 | } 242 | 243 | ///---------------------------------------------------------------------- 244 | /// 245 | /// Indirect addressing modes. 246 | /// 247 | ///---------------------------------------------------------------------- 248 | #[derive(Clone, Debug)] 249 | pub struct IndAddr { 250 | pub base: Register, 251 | pub scale: Scale, 252 | pub disp: Disp, 253 | } 254 | 255 | impl IndAddr { 256 | pub fn new(base: Register, scale: Scale, disp: Disp) -> Self { 257 | Self { base, scale, disp } 258 | } 259 | } 260 | 261 | impl Parse for IndAddr { 262 | fn parse(input: ParseStream) -> Result { 263 | let content; 264 | syn::bracketed!(content in input); 265 | let base = content.parse::()?; 266 | if content.peek(Token![-]) || content.peek(Token![+]) { 267 | let negate = match content.parse::()?.as_char() { 268 | '-' => true, 269 | '+' => false, 270 | _ => unreachable!(), 271 | }; 272 | 273 | if negate { 274 | let disp = content.parse::()?; 275 | Ok(IndAddr { 276 | base, 277 | scale: Scale::None, 278 | disp: disp.neg(), 279 | }) 280 | } else if content.peek(Ident) { 281 | let ident = content.parse::().unwrap(); 282 | match Register::check_register(&content, &ident)? { 283 | Some(reg) => { 284 | let scale = if !content.peek(Token![*]) { 285 | 0 286 | } else { 287 | content.parse::()?; 288 | match content.parse::()?.base10_parse::()? { 289 | 1 => 0, 290 | 2 => 1, 291 | 4 => 2, 292 | 8 => 3, 293 | _ => unreachable!("invalid scale number."), 294 | } 295 | }; 296 | let disp = if content.peek(Token![-]) || content.peek(Token![+]) { 297 | let negate = match content.parse::()?.as_char() { 298 | '-' => true, 299 | '+' => false, 300 | _ => unreachable!(), 301 | }; 302 | let disp = content.parse::()?; 303 | if negate { 304 | disp.neg() 305 | } else { 306 | disp 307 | } 308 | } else { 309 | Disp::Imm(0) 310 | }; 311 | Ok(IndAddr { 312 | base, 313 | scale: Scale::S1(scale, reg), 314 | disp, 315 | }) 316 | } 317 | None => Ok(IndAddr { 318 | base, 319 | scale: Scale::None, 320 | disp: Disp::Label(ident), 321 | }), 322 | } 323 | } else { 324 | Ok(IndAddr { 325 | base, 326 | scale: Scale::None, 327 | disp: content.parse::()?, 328 | }) 329 | } 330 | } else { 331 | Ok(IndAddr { 332 | base, 333 | scale: Scale::None, 334 | disp: Disp::Imm(0), 335 | }) 336 | } 337 | } 338 | } 339 | 340 | impl std::fmt::Display for IndAddr { 341 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 342 | write!(f, "[{} + ({})]", self.base, self.disp) 343 | } 344 | } 345 | 346 | impl ToTokens for IndAddr { 347 | fn to_tokens(&self, tokens: &mut TokenStream) { 348 | let Self { base, disp, scale } = self; 349 | tokens.extend(quote!( Rm::ind(#base, #disp, #scale) )); 350 | } 351 | } 352 | 353 | #[derive(Clone, Debug)] 354 | pub enum Scale { 355 | None, 356 | S1(u8, Register), 357 | } 358 | 359 | impl ToTokens for Scale { 360 | fn to_tokens(&self, tokens: &mut TokenStream) { 361 | tokens.extend(match self { 362 | Self::None => quote!(Scale::None), 363 | Self::S1(scale, reg) => quote!( Scale::S1(#scale, #reg) ), 364 | }); 365 | } 366 | } 367 | 368 | ///---------------------------------------------------------------------- 369 | /// 370 | /// Destination operand for jump and call instructions. 371 | /// 372 | ///---------------------------------------------------------------------- 373 | #[derive(Clone, Debug)] 374 | pub enum Dest { 375 | Reg(Reg), 376 | Rel(Ident), 377 | Disp(TokenStream), 378 | Ind(IndAddr), 379 | } 380 | 381 | impl Parse for Dest { 382 | fn parse(input: ParseStream) -> Result { 383 | let lookahead = input.lookahead1(); 384 | if lookahead.peek(Ident) && is_single(input) { 385 | let dest: Ident = input.parse()?; 386 | match Reg::from_str(dest.to_string()) { 387 | Some(reg) => Ok(Dest::Reg(reg)), 388 | None => Ok(Dest::Rel(dest)), 389 | } 390 | } else if input.peek(token::Paren) { 391 | // e.g. "(42)" 392 | let gr = input.parse::()?; 393 | Ok(Dest::Disp(gr.stream())) 394 | } else if input.peek(token::Bracket) { 395 | let ind = input.parse::()?; 396 | Ok(Dest::Ind(ind)) 397 | } else { 398 | Err(lookahead.error()) 399 | } 400 | } 401 | } 402 | 403 | ///---------------------------------------------------------------------- 404 | /// 405 | /// Immediate. 406 | /// 407 | ///---------------------------------------------------------------------- 408 | #[derive(Clone, Debug)] 409 | pub struct Immediate(pub TokenStream); 410 | 411 | impl Parse for Immediate { 412 | fn parse(input: ParseStream) -> Result { 413 | if input.peek(LitInt) && is_single(input) { 414 | // e.g. "42" 415 | let imm = input.parse::()?; 416 | Ok(Self(quote! { #imm })) 417 | } else if input.peek(token::Paren) { 418 | // e.g. "(42)" 419 | let gr = input.parse::()?; 420 | Ok(Self(gr.stream())) 421 | } else { 422 | Err(input.error("Illegal immediate.".to_string())) 423 | } 424 | } 425 | } 426 | 427 | pub fn is_single(input: ParseStream) -> bool { 428 | input.peek2(Token![,]) || input.peek2(Token![;]) 429 | } 430 | 431 | ///---------------------------------------------------------------------- 432 | /// 433 | /// Displacement for indirect addressing. 434 | /// 435 | ///---------------------------------------------------------------------- 436 | #[derive(Clone, Debug)] 437 | pub enum Disp { 438 | Imm(i32), 439 | Expr(TokenStream), 440 | Label(Ident), 441 | } 442 | 443 | impl Disp { 444 | fn neg(self) -> Self { 445 | match self { 446 | Disp::Imm(disp) => Disp::Imm(-disp), 447 | Disp::Expr(ts) => Disp::Expr(quote!(-(#ts))), 448 | disp => disp, 449 | } 450 | } 451 | } 452 | 453 | impl ToTokens for Disp { 454 | fn to_tokens(&self, tokens: &mut TokenStream) { 455 | let ts = match self { 456 | Disp::Imm(disp) => quote!( 457 | Disp::from_disp(#disp) 458 | ), 459 | Disp::Expr(ts) => quote!( 460 | Disp::from_disp(#ts) 461 | ), 462 | Disp::Label(label) => quote!( 463 | Disp::from_label(#label) 464 | ), 465 | }; 466 | tokens.extend(ts); 467 | } 468 | } 469 | 470 | impl std::fmt::Display for Disp { 471 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 472 | match self { 473 | Self::Imm(disp) => write!(f, "{}", disp), 474 | Self::Expr(ts) => write!(f, "{}", ts), 475 | Self::Label(label) => write!(f, "{}", label), 476 | } 477 | } 478 | } 479 | 480 | impl Parse for Disp { 481 | fn parse(input: ParseStream) -> Result { 482 | if input.peek(token::Paren) { 483 | // e.g. "[rax - (4)]" 484 | let content; 485 | syn::parenthesized!(content in input); 486 | let expr = content.parse::()?; 487 | let expr = quote!(#expr as i32); 488 | Ok(Disp::Expr(expr)) 489 | } else if input.peek(LitInt) { 490 | // e.g. "[rax + 4]" 491 | let disp: i32 = input.parse::()?.base10_parse()?; 492 | Ok(Disp::Imm(disp)) 493 | } else if input.peek(Ident) { 494 | let label = input.parse::().unwrap(); 495 | Ok(Disp::Label(label)) 496 | } else { 497 | Err(input.error("invalid displacement expression.")) 498 | } 499 | } 500 | } 501 | -------------------------------------------------------------------------------- /monoasm_macro/src/inst.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro2; 2 | extern crate quote; 3 | extern crate syn; 4 | pub use super::parse::*; 5 | use proc_macro2::Group; 6 | use proc_macro2::TokenStream; 7 | use quote::{quote, ToTokens}; 8 | use syn::parse::{Parse, ParseStream}; 9 | use syn::Expr; 10 | use syn::LitFloat; 11 | use syn::{token, Error, Ident, LitInt, Token}; 12 | 13 | ///---------------------------------------------------------------------- 14 | /// 15 | /// Assembly statements. 16 | /// 17 | ///---------------------------------------------------------------------- 18 | #[derive(Clone)] 19 | pub struct Stmts { 20 | pub base: syn::Expr, 21 | pub contents: Vec, 22 | } 23 | 24 | impl Parse for Stmts { 25 | fn parse(input: ParseStream) -> Result { 26 | let base: Expr = input.parse()?; 27 | input.parse::()?; 28 | let mut stmts = Stmts { 29 | base, 30 | contents: vec![], 31 | }; 32 | loop { 33 | if input.is_empty() { 34 | break; 35 | } 36 | let inst = input.parse::()?; 37 | //println!("{:?}", &inst); 38 | stmts.contents.push(inst); 39 | } 40 | Ok(stmts) 41 | } 42 | } 43 | 44 | ///---------------------------------------------------------------------- 45 | /// 46 | /// Assembly instructions. 47 | /// 48 | ///---------------------------------------------------------------------- 49 | #[derive(Clone, Debug)] 50 | pub enum Inst { 51 | Label(Ident), 52 | 53 | F64(f64), 54 | I64(i64), 55 | 56 | Movq(MovOperand, MovOperand), 57 | Movl(RmOperand, RmiOperand), 58 | Movw(RmOperand, RmiOperand), 59 | Movb(RmOperand, RmiOperand), 60 | Movsxl(Register, RmOperand), 61 | Movzxw(Register, RmOperand), 62 | Movsxw(Register, RmOperand), 63 | Movzxb(Register, RmOperand), 64 | Movsxb(Register, RmOperand), 65 | Add(OperandSize, RmOperand, RmiOperand), 66 | Sub(OperandSize, RmOperand, RmiOperand), 67 | Adc(OperandSize, RmOperand, RmiOperand), 68 | Sbb(OperandSize, RmOperand, RmiOperand), 69 | And(OperandSize, RmOperand, RmiOperand), 70 | Or(OperandSize, RmOperand, RmiOperand), 71 | Xor(OperandSize, RmOperand, RmiOperand), 72 | Cmp(OperandSize, RmOperand, RmiOperand), 73 | Xchg(OperandSize, RmOperand, RmOperand), 74 | Negq(RmOperand), 75 | Notq(RmOperand), 76 | 77 | Shl(OperandSize, RmOperand, RiOperand), 78 | Shr(OperandSize, RmOperand, RiOperand), 79 | Sal(OperandSize, RmOperand, RiOperand), 80 | Sar(OperandSize, RmOperand, RiOperand), 81 | Rol(OperandSize, RmOperand, RiOperand), 82 | Ror(OperandSize, RmOperand, RiOperand), 83 | 84 | Imul(RmiOperand, RmiOperand), 85 | Idiv(RmOperand), 86 | Idivl(RmOperand), 87 | Div(RmOperand), 88 | Divl(RmOperand), 89 | 90 | Lea(RmOperand, RmOperand), 91 | 92 | Testq(RmOperand, RiOperand), 93 | Testb(RmOperand, RiOperand), 94 | 95 | Setcc(Cond, RmOperand), 96 | Cmovcc(OperandSize, Cond, Register, RmOperand), 97 | Cqo, 98 | Cdq, 99 | 100 | Movsd(XmOperand, XmOperand), 101 | Addsd(Xmm, XmOperand), 102 | Subsd(Xmm, XmOperand), 103 | Mulsd(Xmm, XmOperand), 104 | Divsd(Xmm, XmOperand), 105 | Xorps(Xmm, XmOperand), 106 | UComIsd(Xmm, XmOperand), 107 | Minsd(Xmm, XmOperand), 108 | Maxsd(Xmm, XmOperand), 109 | 110 | Andpd(Xmm, XmOperand), 111 | Xorpd(Xmm, XmOperand), 112 | Roundpd(Xmm, XmOperand, ImmOperand), 113 | Cvtsi2sdq(Xmm, RmOperand), 114 | Cvttsd2siq(Register, XmOperand), 115 | Sqrtpd(Xmm, XmOperand), 116 | Sqrtsd(Xmm, XmOperand), 117 | 118 | Pushq(RmOperand), 119 | Popq(RmOperand), 120 | 121 | Jmp(Dest), 122 | Jcc(Cond, Ident), 123 | 124 | Call(Dest), 125 | Leave, 126 | Ret, 127 | Syscall, 128 | 129 | Lzcnt(OperandSize, Register, RmOperand), 130 | Tzcnt(OperandSize, Register, RmOperand), 131 | Popcnt(OperandSize, Register, RmOperand), 132 | 133 | Int3, 134 | } 135 | 136 | ///---------------------------------------------------------------------- 137 | /// 138 | /// Operand size. 139 | /// 140 | ///---------------------------------------------------------------------- 141 | #[derive(Clone, Debug, PartialEq)] 142 | pub enum OperandSize { 143 | QWORD = 8, 144 | DWORD = 4, 145 | WORD = 2, 146 | BYTE = 1, 147 | } 148 | 149 | ///---------------------------------------------------------------------- 150 | /// 151 | /// Comparison kinds. 152 | /// 153 | ///---------------------------------------------------------------------- 154 | #[derive(Debug, Clone, Copy)] 155 | pub enum Cond { 156 | O = 0, 157 | No = 1, 158 | B = 2, 159 | Ae = 3, 160 | Eq = 4, 161 | Ne = 5, 162 | Be = 6, 163 | A = 7, 164 | S = 8, 165 | Ns = 9, 166 | P = 10, 167 | Np = 11, 168 | Lt = 12, 169 | Ge = 13, 170 | Le = 14, 171 | Gt = 15, 172 | } 173 | 174 | impl Cond { 175 | pub fn from(s: &str) -> Option { 176 | let cond = match s { 177 | "o" => Cond::O, 178 | "no" => Cond::No, 179 | "b" => Cond::B, 180 | "c" => Cond::B, 181 | "nae" => Cond::B, 182 | "ae" => Cond::Ae, 183 | "nc" => Cond::Ae, 184 | "eq" => Cond::Eq, 185 | "e" => Cond::Eq, 186 | "z" => Cond::Eq, 187 | "ne" => Cond::Ne, 188 | "nz" => Cond::Ne, 189 | "be" => Cond::Be, 190 | "na" => Cond::Be, 191 | "a" => Cond::A, 192 | "nbe" => Cond::A, 193 | "s" => Cond::S, 194 | "ns" => Cond::Ns, 195 | "p" => Cond::P, 196 | "pe" => Cond::P, 197 | "np" => Cond::Np, 198 | "po" => Cond::Np, 199 | "lt" => Cond::Lt, 200 | "l" => Cond::Lt, 201 | "nge" => Cond::Lt, 202 | "ge" => Cond::Ge, 203 | "nl" => Cond::Ge, 204 | "le" => Cond::Le, 205 | "ng" => Cond::Le, 206 | "gt" => Cond::Gt, 207 | "g" => Cond::Gt, 208 | "nle" => Cond::Gt, 209 | _ => return None, 210 | }; 211 | Some(cond) 212 | } 213 | } 214 | 215 | impl Parse for Inst { 216 | fn parse(input: ParseStream) -> Result { 217 | macro_rules! parse_2op_sized { 218 | ($inst: ident, $size: ident) => ( 219 | { 220 | let op1 = input.parse()?; 221 | input.parse::()?; 222 | let op2 = input.parse()?; 223 | input.parse::()?; 224 | Ok(Inst::$inst(OperandSize::$size, op1, op2)) 225 | } 226 | ) 227 | } 228 | 229 | macro_rules! parse_3op { 230 | ($inst: ident) => ( 231 | { 232 | let op1 = input.parse()?; 233 | input.parse::()?; 234 | let op2 = input.parse()?; 235 | input.parse::()?; 236 | let op3 = input.parse()?; 237 | input.parse::()?; 238 | Ok(Inst::$inst(op1, op2, op3)) 239 | } 240 | ) 241 | } 242 | 243 | macro_rules! parse_2op { 244 | ($inst: ident) => ( 245 | { 246 | let op1 = input.parse()?; 247 | input.parse::()?; 248 | let op2 = input.parse()?; 249 | input.parse::()?; 250 | Ok(Inst::$inst(op1, op2)) 251 | } 252 | ) 253 | } 254 | 255 | macro_rules! parse_1op { 256 | ($inst: ident) => ( 257 | { 258 | let op = input.parse()?; 259 | input.parse::()?; 260 | Ok(Inst::$inst(op)) 261 | } 262 | ) 263 | } 264 | 265 | macro_rules! parse_0op { 266 | ($inst: ident) => ( 267 | { 268 | input.parse::()?; 269 | Ok(Inst::$inst) 270 | } 271 | ) 272 | } 273 | 274 | macro_rules! parse_jcc { 275 | ($inst: ident) => ( 276 | { 277 | let op = input.parse::()?; 278 | input.parse::()?; 279 | Ok(Inst::Jcc(Cond::$inst, op)) 280 | } 281 | ) 282 | } 283 | 284 | macro_rules! parse_set { 285 | ($flag: ident) => ( 286 | { 287 | let op = input.parse()?; 288 | input.parse::()?; 289 | Ok(Inst::Setcc($flag, op)) 290 | } 291 | ) 292 | } 293 | 294 | macro_rules! parse_cmov { 295 | ($size: ident, $flag: ident) => ( 296 | { 297 | let op1 = input.parse()?; 298 | input.parse::()?; 299 | let op2 = input.parse()?; 300 | input.parse::()?; 301 | Ok(Inst::Cmovcc($size, $flag, op1, op2)) 302 | } 303 | ) 304 | } 305 | 306 | let ident = input.parse::()?; 307 | if input.peek(Token![:]) { 308 | input.parse::()?; 309 | Ok(Inst::Label(ident)) 310 | } else { 311 | match ident.to_string().as_str() { 312 | "movq" => parse_2op!(Movq), 313 | "movl" => parse_2op!(Movl), 314 | "movw" => parse_2op!(Movw), 315 | "movb" => parse_2op!(Movb), 316 | "movsxl" => parse_2op!(Movsxl), 317 | "movzxw" => parse_2op!(Movzxw), 318 | "movsxw" => parse_2op!(Movsxw), 319 | "movzxb" => parse_2op!(Movzxb), 320 | "movsxb" => parse_2op!(Movsxb), 321 | 322 | "addq" => parse_2op_sized!(Add, QWORD), 323 | "addl" => parse_2op_sized!(Add, DWORD), 324 | "addw" => parse_2op_sized!(Add, WORD), 325 | "addb" => parse_2op_sized!(Add, BYTE), 326 | "orq" => parse_2op_sized!(Or, QWORD), 327 | "orl" => parse_2op_sized!(Or, DWORD), 328 | "orw" => parse_2op_sized!(Or, WORD), 329 | "orb" => parse_2op_sized!(Or, BYTE), 330 | "adcq" => parse_2op_sized!(Adc, QWORD), 331 | "adcl" => parse_2op_sized!(Adc, DWORD), 332 | "adcw" => parse_2op_sized!(Adc, WORD), 333 | "adcb" => parse_2op_sized!(Adc, BYTE), 334 | "sbbq" => parse_2op_sized!(Sbb, QWORD), 335 | "sbbl" => parse_2op_sized!(Sbb, DWORD), 336 | "sbbw" => parse_2op_sized!(Sbb, WORD), 337 | "sbbb" => parse_2op_sized!(Sbb, BYTE), 338 | "andq" => parse_2op_sized!(And, QWORD), 339 | "andl" => parse_2op_sized!(And, DWORD), 340 | "andw" => parse_2op_sized!(And, WORD), 341 | "andb" => parse_2op_sized!(And, BYTE), 342 | "subq" => parse_2op_sized!(Sub, QWORD), 343 | "subl" => parse_2op_sized!(Sub, DWORD), 344 | "subw" => parse_2op_sized!(Sub, WORD), 345 | "subb" => parse_2op_sized!(Sub, BYTE), 346 | "xorq" => parse_2op_sized!(Xor, QWORD), 347 | "xorl" => parse_2op_sized!(Xor, DWORD), 348 | "xorw" => parse_2op_sized!(Xor, WORD), 349 | "xorb" => parse_2op_sized!(Xor, BYTE), 350 | "cmpq" => parse_2op_sized!(Cmp, QWORD), 351 | "cmpl" => parse_2op_sized!(Cmp, DWORD), 352 | "cmpw" => parse_2op_sized!(Cmp, WORD), 353 | "cmpb" => parse_2op_sized!(Cmp, BYTE), 354 | "xchgq" => parse_2op_sized!(Xchg, QWORD), 355 | "xchgl" => parse_2op_sized!(Xchg, DWORD), 356 | "xchgw" => parse_2op_sized!(Xchg, WORD), 357 | "xchgb" => parse_2op_sized!(Xchg, BYTE), 358 | 359 | "negq" => parse_1op!(Negq), 360 | "notq" => parse_1op!(Notq), 361 | "imul" => parse_2op!(Imul), 362 | "idiv" => parse_1op!(Idiv), 363 | "idivq" => parse_1op!(Idiv), 364 | "idivl" => parse_1op!(Idivl), 365 | "div" => parse_1op!(Div), 366 | "divq" => parse_1op!(Div), 367 | "divl" => parse_1op!(Divl), 368 | 369 | "testq" => parse_2op!(Testq), 370 | "testb" => parse_2op!(Testb), 371 | "lea" => parse_2op!(Lea), 372 | 373 | "cqo" => parse_0op!(Cqo), 374 | "cdq" => parse_0op!(Cdq), 375 | "int3" => parse_0op!(Int3), 376 | 377 | "shlq" => parse_2op_sized!(Shl, QWORD), 378 | "shrq" => parse_2op_sized!(Shr, QWORD), 379 | "salq" => parse_2op_sized!(Sal, QWORD), 380 | "sarq" => parse_2op_sized!(Sar, QWORD), 381 | "rolq" => parse_2op_sized!(Rol, QWORD), 382 | "rorq" => parse_2op_sized!(Ror, QWORD), 383 | 384 | "shll" => parse_2op_sized!(Sal, DWORD), 385 | "shrl" => parse_2op_sized!(Shr, DWORD), 386 | "sall" => parse_2op_sized!(Sal, DWORD), 387 | "sarl" => parse_2op_sized!(Sar, DWORD), 388 | "roll" => parse_2op_sized!(Rol, DWORD), 389 | "rorl" => parse_2op_sized!(Ror, DWORD), 390 | 391 | "movsd" => parse_2op!(Movsd), 392 | "addsd" => parse_2op!(Addsd), 393 | "subsd" => parse_2op!(Subsd), 394 | "mulsd" => parse_2op!(Mulsd), 395 | "divsd" => parse_2op!(Divsd), 396 | "minsd" => parse_2op!(Minsd), 397 | "maxsd" => parse_2op!(Maxsd), 398 | "xorps" => parse_2op!(Xorps), 399 | "ucomisd" => parse_2op!(UComIsd), 400 | 401 | "andpd" => parse_2op!(Andpd), 402 | "xorpd" => parse_2op!(Xorpd), 403 | "roundpd" => parse_3op!(Roundpd), 404 | "cvtsi2sdq" => parse_2op!(Cvtsi2sdq), 405 | "cvttsd2siq" => parse_2op!(Cvttsd2siq), 406 | "sqrtpd" => parse_2op!(Sqrtpd), 407 | "sqrtsd" => parse_2op!(Sqrtsd), 408 | 409 | "pushq" => parse_1op!(Pushq), 410 | "popq" => parse_1op!(Popq), 411 | "call" => parse_1op!(Call), 412 | "ret" => parse_0op!(Ret), 413 | "jmp" => parse_1op!(Jmp), 414 | 415 | "jo" => parse_jcc!(O), 416 | "jno" => parse_jcc!(No), 417 | "jb" => parse_jcc!(B), 418 | "jc" => parse_jcc!(B), 419 | "jnae" => parse_jcc!(B), 420 | "jae" => parse_jcc!(Ae), 421 | "jnc" => parse_jcc!(Ae), 422 | "jeq" => parse_jcc!(Eq), 423 | "je" => parse_jcc!(Eq), 424 | "jz" => parse_jcc!(Eq), 425 | "jne" => parse_jcc!(Ne), 426 | "jnz" => parse_jcc!(Ne), 427 | "jbe" => parse_jcc!(Be), 428 | "jna" => parse_jcc!(Be), 429 | "ja" => parse_jcc!(A), 430 | "jnbe" => parse_jcc!(A), 431 | "js" => parse_jcc!(S), 432 | "jns" => parse_jcc!(Ns), 433 | "jp" => parse_jcc!(P), 434 | "jpe" => parse_jcc!(P), 435 | "jnp" => parse_jcc!(Np), 436 | "jpo" => parse_jcc!(Np), 437 | "jlt" => parse_jcc!(Lt), 438 | "jnge" => parse_jcc!(Lt), 439 | "jge" => parse_jcc!(Ge), 440 | "jnl" => parse_jcc!(Ge), 441 | "jle" => parse_jcc!(Le), 442 | "jng" => parse_jcc!(Le), 443 | "jgt" => parse_jcc!(Gt), 444 | "jnle" => parse_jcc!(Gt), 445 | 446 | "syscall" => parse_0op!(Syscall), 447 | "leave" => parse_0op!(Leave), 448 | 449 | "lzcntq" => parse_2op_sized!(Lzcnt, QWORD), 450 | "tzcntq" => parse_2op_sized!(Tzcnt, QWORD), 451 | "popcntq" => parse_2op_sized!(Popcnt, QWORD), 452 | "lzcntl" => parse_2op_sized!(Lzcnt, DWORD), 453 | "tzcntl" => parse_2op_sized!(Tzcnt, DWORD), 454 | "popcntl" => parse_2op_sized!(Popcnt, DWORD), 455 | 456 | "dq" => { 457 | if input.peek(LitFloat) { 458 | let f = input.parse::()?.base10_parse()?; 459 | input.parse::()?; 460 | Ok(Inst::F64(f)) 461 | } else if input.peek(LitInt) { 462 | let i = input.parse::()?.base10_parse()?; 463 | input.parse::()?; 464 | Ok(Inst::I64(i)) 465 | } else { 466 | Err(input.error("unimplemented literal.")) 467 | } 468 | } 469 | 470 | s if s.starts_with("cmov") => { 471 | let width = match s.as_bytes()[s.len() - 1] { 472 | b'q' => OperandSize::QWORD, 473 | b'l' => OperandSize::DWORD, 474 | b'w' => OperandSize::WORD, 475 | _ => { 476 | return Err(Error::new( 477 | ident.span(), 478 | "illegal cmov operand size suffix.", 479 | )) 480 | } 481 | }; 482 | if 5 >= s.len() { 483 | return Err(Error::new(ident.span(), "illegal cmov condition.")); 484 | } 485 | let cond = match Cond::from(&s[4..s.len() - 1]) { 486 | Some(cond) => cond, 487 | None => return Err(Error::new(ident.span(), "illegal cmov condition.")), 488 | }; 489 | parse_cmov!(width, cond) 490 | } 491 | 492 | s if s.starts_with("set") => { 493 | if 3 >= s.len() { 494 | return Err(Error::new(ident.span(), "illegal setcc condition.")); 495 | } 496 | let cond = match Cond::from(&s[3..]) { 497 | Some(cond) => cond, 498 | None => return Err(Error::new(ident.span(), "illegal setcc condition.")), 499 | }; 500 | parse_set!(cond) 501 | } 502 | 503 | _ => Err(Error::new(ident.span(), "unimplemented instruction.")), 504 | } 505 | } 506 | } 507 | } 508 | 509 | ///---------------------------------------------------------------------- 510 | /// 511 | /// General register / memory reference. 512 | /// 513 | ///---------------------------------------------------------------------- 514 | #[derive(Clone, Debug)] 515 | pub enum RmOperand { 516 | Reg(Register), 517 | Ind(IndAddr), 518 | } 519 | 520 | impl Parse for RmOperand { 521 | fn parse(input: ParseStream) -> Result { 522 | if input.peek(Ident) { 523 | // e.g. "rax" 524 | let reg = input.parse::()?; 525 | Ok(Self::reg(reg)) 526 | } else if input.peek(token::Bracket) { 527 | // e.g. "[rax + 4]", "[rax - (4)]" 528 | Ok(Self::Ind(input.parse::()?)) 529 | } else { 530 | Err(input.error("Expected register name or memory reference.")) 531 | } 532 | } 533 | } 534 | 535 | impl std::fmt::Display for RmOperand { 536 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 537 | match self { 538 | Self::Reg(reg) => write!(f, "{}", reg), 539 | Self::Ind(ind) => write!(f, "{}", ind), 540 | } 541 | } 542 | } 543 | 544 | impl ToTokens for RmOperand { 545 | fn to_tokens(&self, tokens: &mut TokenStream) { 546 | let ts = match self { 547 | Self::Reg(ts) => quote!(Rm::reg(#ts)), 548 | Self::Ind(ind) => quote!(#ind), 549 | }; 550 | tokens.extend(ts); 551 | } 552 | } 553 | 554 | impl RmOperand { 555 | pub fn reg(reg: Register) -> Self { 556 | Self::Reg(reg) 557 | } 558 | } 559 | 560 | ///---------------------------------------------------------------------- 561 | /// 562 | /// General register / memory reference / immediate Operands. 563 | /// 564 | ///---------------------------------------------------------------------- 565 | #[derive(Clone, Debug)] 566 | pub enum RmiOperand { 567 | Imm(TokenStream), 568 | Reg(Register), 569 | Ind(IndAddr), 570 | } 571 | 572 | impl Parse for RmiOperand { 573 | fn parse(input: ParseStream) -> Result { 574 | if input.peek(Ident) { 575 | let ident = input.parse::()?; 576 | // e.g. "rax" 577 | let reg = Register::parse_register(input, ident.to_string())?; 578 | Ok(RmiOperand::reg(reg)) 579 | } else if input.peek(LitInt) && is_single(input) { 580 | // e.g. "42" 581 | let imm = input.parse::()?; 582 | Ok(RmiOperand::Imm(imm.to_token_stream())) 583 | } else if input.peek(token::Bracket) { 584 | // e.g. "[rax + 4]", "[rax - (4)]" 585 | Ok(RmiOperand::Ind(input.parse::()?)) 586 | } else if input.peek(token::Paren) { 587 | // e.g. "(42)" 588 | let gr = input.parse::()?; 589 | Ok(RmiOperand::Imm(gr.stream())) 590 | } else { 591 | Err(input.error("Expected register name, integer literal, memory reference, or Rust expression with parenthesis.")) 592 | } 593 | } 594 | } 595 | 596 | impl std::fmt::Display for RmiOperand { 597 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 598 | match self { 599 | RmiOperand::Imm(i) => write!(f, "{}", i), 600 | RmiOperand::Reg(reg) => write!(f, "{}", reg), 601 | RmiOperand::Ind(ind) => write!(f, "{}", ind), 602 | } 603 | } 604 | } 605 | 606 | impl ToTokens for RmiOperand { 607 | fn to_tokens(&self, tokens: &mut TokenStream) { 608 | let ts = match self { 609 | RmiOperand::Imm(..) => unreachable!("immediate"), 610 | RmiOperand::Reg(ts) => quote!(Rm::reg(#ts)), 611 | RmiOperand::Ind(ind) => quote!(#ind), 612 | }; 613 | tokens.extend(ts); 614 | } 615 | } 616 | 617 | impl RmiOperand { 618 | pub fn reg(reg: Register) -> Self { 619 | Self::Reg(reg) 620 | } 621 | } 622 | 623 | ///---------------------------------------------------------------------- 624 | /// 625 | /// General register / immediate Operands. 626 | /// 627 | ///---------------------------------------------------------------------- 628 | #[derive(Clone, Debug)] 629 | pub enum RiOperand { 630 | Imm(TokenStream), 631 | Reg(Register), 632 | } 633 | 634 | impl Parse for RiOperand { 635 | fn parse(input: ParseStream) -> Result { 636 | if input.peek(Ident) { 637 | // e.g. "rax" 638 | let reg = input.parse::()?; 639 | Ok(RiOperand::reg(reg)) 640 | } else if input.peek(LitInt) && is_single(input) { 641 | // e.g. "42" 642 | let imm = input.parse::()?; 643 | Ok(RiOperand::imm(imm)) 644 | } else if input.peek(token::Paren) { 645 | // e.g. "(42)" 646 | let gr = input.parse::()?; 647 | Ok(RiOperand::Imm(gr.stream())) 648 | } else { 649 | Err(input.error("Expected register name, integer literal, memory reference, or Rust expression with parenthesis.")) 650 | } 651 | } 652 | } 653 | 654 | impl std::fmt::Display for RiOperand { 655 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 656 | match self { 657 | RiOperand::Imm(i) => write!(f, "{}", i), 658 | RiOperand::Reg(reg) => write!(f, "{}", reg), 659 | } 660 | } 661 | } 662 | 663 | impl ToTokens for RiOperand { 664 | fn to_tokens(&self, tokens: &mut TokenStream) { 665 | let ts = match self { 666 | RiOperand::Imm(_) => unreachable!("immediate"), 667 | RiOperand::Reg(ts) => quote!(Rm::reg(#ts)), 668 | }; 669 | tokens.extend(ts); 670 | } 671 | } 672 | 673 | impl RiOperand { 674 | pub fn reg(reg: Register) -> Self { 675 | Self::Reg(reg) 676 | } 677 | 678 | pub fn imm(imm: impl ToTokens) -> Self { 679 | Self::Imm(quote! { #imm }) 680 | } 681 | } 682 | 683 | ///---------------------------------------------------------------------- 684 | /// 685 | /// Immediate Operands. 686 | /// 687 | ///---------------------------------------------------------------------- 688 | #[derive(Clone, Debug)] 689 | pub struct ImmOperand(pub TokenStream); 690 | 691 | impl Parse for ImmOperand { 692 | fn parse(input: ParseStream) -> Result { 693 | if input.peek(LitInt) && is_single(input) { 694 | // e.g. "42" 695 | let imm = input.parse::()?; 696 | Ok(ImmOperand::new(imm)) 697 | } else if input.peek(token::Paren) { 698 | // e.g. "(42)" 699 | let gr = input.parse::()?; 700 | Ok(ImmOperand(gr.stream())) 701 | } else { 702 | Err(input.error("Expected integer literal or Rust expression with parenthesis.")) 703 | } 704 | } 705 | } 706 | 707 | impl std::fmt::Display for ImmOperand { 708 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 709 | match self { 710 | ImmOperand(i) => write!(f, "{}", i), 711 | } 712 | } 713 | } 714 | 715 | impl ImmOperand { 716 | pub fn new(imm: impl ToTokens) -> Self { 717 | Self(quote! { #imm }) 718 | } 719 | } 720 | 721 | ///---------------------------------------------------------------------- 722 | /// 723 | /// Floating pointer register(xmm) / memory reference Operands. 724 | /// 725 | ///---------------------------------------------------------------------- 726 | #[derive(Clone, Debug)] 727 | pub enum XmOperand { 728 | Xmm(TokenStream), 729 | Ind(IndAddr), 730 | } 731 | 732 | fn parse_xmm(input: ParseStream, ident: &String) -> Result { 733 | assert!(ident.starts_with("xmm")); 734 | if ident.len() == 3 { 735 | if input.peek(token::Paren) { 736 | let gr = input.parse::()?; 737 | Ok(gr.stream()) 738 | } else { 739 | Err(input.error(format!( 740 | "Expected xmm register number. e.g. xmm0 or xmm(0) actual:{}", 741 | ident, 742 | ))) 743 | } 744 | } else if let Ok(no) = ident[3..].parse::() { 745 | if no > 15 { 746 | Err(input.error(format!("Invalid xmm register name. {}", ident))) 747 | } else { 748 | Ok(quote!(#no as u64)) 749 | } 750 | } else { 751 | Err(input.error(format!("Invalid xmm register name. {}", ident))) 752 | } 753 | } 754 | 755 | impl Parse for XmOperand { 756 | fn parse(input: ParseStream) -> Result { 757 | if input.peek(Ident) { 758 | let reg = input.parse::()?.to_string(); 759 | if reg.starts_with("xmm") { 760 | Ok(Self::Xmm(parse_xmm(input, ®)?)) 761 | } else { 762 | Err(input.error("Expected xmm register name or memory reference.")) 763 | } 764 | } else if input.peek(token::Bracket) { 765 | Ok(Self::Ind(input.parse::()?)) 766 | } else { 767 | Err(input.error("Expected xmm register name or memory reference.")) 768 | } 769 | } 770 | } 771 | 772 | impl std::fmt::Display for XmOperand { 773 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 774 | match self { 775 | Self::Xmm(reg) => write!(f, "xmm({})", reg), 776 | Self::Ind(ind) => write!(f, "{}", ind), 777 | } 778 | } 779 | } 780 | 781 | impl ToTokens for XmOperand { 782 | fn to_tokens(&self, tokens: &mut TokenStream) { 783 | let ts = match self { 784 | Self::Xmm(ts) => quote!(Rm::reg(Reg::from(#ts))), 785 | Self::Ind(ind) => quote!(#ind), 786 | }; 787 | tokens.extend(ts); 788 | } 789 | } 790 | 791 | ///---------------------------------------------------------------------- 792 | /// 793 | /// General register / Xmm register / memory reference / immediate Operands. 794 | /// 795 | ///---------------------------------------------------------------------- 796 | #[derive(Clone, Debug)] 797 | pub enum MovOperand { 798 | Imm(TokenStream, Option), 799 | Reg(Register), 800 | Xmm(TokenStream), 801 | Ind(IndAddr), 802 | } 803 | 804 | impl Parse for MovOperand { 805 | fn parse(input: ParseStream) -> Result { 806 | if input.peek(Ident) { 807 | let ident = input.parse::()?.to_string(); 808 | if ident == "qword" { 809 | let imm = input.parse::()?; 810 | Ok(Self::Imm(imm.0, Some(OperandSize::QWORD))) 811 | } else if ident.starts_with("xmm") { 812 | Ok(Self::Xmm(parse_xmm(input, &ident)?)) 813 | } else { 814 | Ok(Self::reg(Register::parse_register(input, ident)?)) 815 | } 816 | } else if input.peek(LitInt) && is_single(input) { 817 | // e.g. "42" 818 | let imm = input.parse::()?; 819 | Ok(Self::Imm(imm.to_token_stream(), None)) 820 | } else if input.peek(token::Bracket) { 821 | // e.g. "[rax + 4]", "[rax - (4)]" 822 | Ok(Self::Ind(input.parse::()?)) 823 | } else if input.peek(token::Paren) { 824 | // e.g. "(42)" 825 | let gr = input.parse::()?; 826 | Ok(Self::Imm(gr.stream(), None)) 827 | } else { 828 | Err(input.error("Expected register name, integer literal, memory reference, or Rust expression with parenthesis.")) 829 | } 830 | } 831 | } 832 | 833 | impl std::fmt::Display for MovOperand { 834 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 835 | match self { 836 | Self::Imm(i, size) => match size { 837 | Some(size) => write!(f, "{}:{:?}", i, size), 838 | None => write!(f, "{}", i), 839 | }, 840 | Self::Reg(reg) => write!(f, "{}", reg), 841 | Self::Xmm(reg) => write!(f, "xmm({})", reg), 842 | Self::Ind(ind) => write!(f, "{}", ind), 843 | } 844 | } 845 | } 846 | 847 | impl ToTokens for MovOperand { 848 | fn to_tokens(&self, tokens: &mut TokenStream) { 849 | let ts = match self { 850 | Self::Imm(..) => unreachable!("immediate"), 851 | Self::Reg(r) => quote!(Rm::reg(#r)), 852 | Self::Xmm(ts) => quote!(Rm::reg(Reg::from(#ts))), 853 | Self::Ind(ind) => quote!(#ind), 854 | }; 855 | tokens.extend(ts); 856 | } 857 | } 858 | 859 | impl MovOperand { 860 | pub fn reg(reg: Register) -> Self { 861 | Self::Reg(reg) 862 | } 863 | } 864 | -------------------------------------------------------------------------------- /monoasm/src/jit_memory.rs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // JIT module runtime 4 | // 5 | //----------------------------------------------------- 6 | 7 | use crate::*; 8 | //use monoasm_inst::Reg; 9 | use region::{protect, Protection}; 10 | use std::{ 11 | alloc::{alloc, Layout}, 12 | io::Write, 13 | }; 14 | 15 | /// Memory manager. 16 | #[derive(Debug)] 17 | pub struct JitMemory { 18 | /// Current memory page. 19 | page: Page, 20 | /// Information of momory pages. 21 | pages: [MemPage; 3], 22 | /// Relocation information. 23 | labels: Labels, 24 | } 25 | 26 | #[derive(Debug, Clone, Copy, PartialEq)] 27 | pub struct Page(pub usize); 28 | 29 | const DATA_PAGE: Page = Page(2); 30 | 31 | /// 32 | /// Memory manager. 33 | /// 34 | #[derive(Debug)] 35 | pub struct MemPage { 36 | /// Pointer to the heap. 37 | contents: usize, 38 | /// Current position 39 | counter: Pos, 40 | /// Constants section. 41 | constants: Vec<(DataType, DestLabel)>, 42 | /// Data section. 43 | data: Vec<(DataType, DestLabel)>, 44 | /// Machine code length 45 | code_len: usize, 46 | /// The top pos of the current code block. 47 | code_block_top: Pos, 48 | /// Code blocks. (start_pos, code_end, end_pos) 49 | pub code_block: Vec<(Pos, Pos, Pos)>, 50 | } 51 | 52 | impl Index for MemPage { 53 | type Output = u8; 54 | 55 | fn index(&self, index: Pos) -> &u8 { 56 | if index.0 >= PAGE_SIZE { 57 | panic!("Page size overflow") 58 | } 59 | unsafe { &*self.contents().add(index.0) } 60 | } 61 | } 62 | 63 | impl IndexMut for MemPage { 64 | fn index_mut(&mut self, index: Pos) -> &mut u8 { 65 | if index.0 >= PAGE_SIZE { 66 | panic!("Page size overflow") 67 | } 68 | unsafe { &mut *self.contents().add(index.0) } 69 | } 70 | } 71 | 72 | impl MemPage { 73 | fn new(contents: *mut u8) -> Self { 74 | MemPage { 75 | contents: contents as usize, 76 | counter: Pos(0), 77 | constants: vec![], 78 | data: vec![], 79 | code_len: 0usize, 80 | code_block_top: Pos(0), 81 | code_block: vec![], 82 | } 83 | } 84 | 85 | fn contents(&self) -> *mut u8 { 86 | self.contents as *mut u8 87 | } 88 | 89 | /// Adjust cursor with 4KB alignment. 90 | pub fn align_page(&mut self) { 91 | self.counter = Pos((self.counter.0 + 4095) & !0b1111_1111_1111); 92 | } 93 | 94 | /// Adjust cursor with 16 byte alignment. 95 | pub fn align16(&mut self) { 96 | self.counter = Pos((self.counter.0 + 15) & !0b1111); 97 | } 98 | 99 | /// Adjust cursor with 8 byte alignment. 100 | pub fn align8(&mut self) { 101 | self.counter = Pos((self.counter.0 + 7) & !0b111); 102 | } 103 | 104 | /// Adjust cursor with 4 byte alignment. 105 | pub fn align4(&mut self) { 106 | self.counter = Pos((self.counter.0 + 3) & !0b11); 107 | } 108 | 109 | /// Emit a byte. 110 | pub fn emitb(&mut self, val: u8) { 111 | let c = self.counter; 112 | self[c] = val; 113 | self.counter = c + 1; 114 | } 115 | 116 | /// Emit a word. 117 | pub fn emitw(&mut self, val: u16) { 118 | let c = self.counter; 119 | self[c] = val as u8; 120 | self[c + 1] = (val >> 8) as u8; 121 | self.counter = c + 2; 122 | } 123 | 124 | /// Emit a long word. 125 | pub fn emitl(&mut self, val: u32) { 126 | let c = self.counter; 127 | self[c] = val as u8; 128 | self[c + 1] = (val >> 8) as u8; 129 | self[c + 2] = (val >> 16) as u8; 130 | self[c + 3] = (val >> 24) as u8; 131 | self.counter = c + 4; 132 | } 133 | 134 | /// Emit a quad word. 135 | pub fn emitq(&mut self, val: u64) { 136 | self.emitl(val as u32); 137 | self.emitl((val >> 32) as u32); 138 | } 139 | 140 | /// Write 32bit data `val` on `loc`. 141 | fn write32(&mut self, loc: Pos, val: i32) { 142 | let val = val as u32; 143 | self[loc] = val as u8; 144 | self[loc + 1] = (val >> 8) as u8; 145 | self[loc + 2] = (val >> 16) as u8; 146 | self[loc + 3] = (val >> 24) as u8; 147 | } 148 | 149 | /// Write 64bit data `val` on `loc`. 150 | fn write64(&mut self, loc: Pos, val: u64) { 151 | self[loc] = val as u8; 152 | self[loc + 1] = (val >> 8) as u8; 153 | self[loc + 2] = (val >> 16) as u8; 154 | self[loc + 3] = (val >> 24) as u8; 155 | self[loc + 4] = (val >> 32) as u8; 156 | self[loc + 5] = (val >> 40) as u8; 157 | self[loc + 6] = (val >> 48) as u8; 158 | self[loc + 7] = (val >> 56) as u8; 159 | } 160 | } 161 | 162 | enum ModRM { 163 | Reg(Reg), 164 | Digit(u8), 165 | } 166 | 167 | enum Rex { 168 | REXW, 169 | None, 170 | Byte, 171 | } 172 | 173 | #[derive(Debug, Clone, Copy)] 174 | enum DataType { 175 | U64(u64), 176 | U32(u32), 177 | Bytes(usize), 178 | AbsAddress(DestLabel), 179 | Align8, 180 | } 181 | 182 | impl std::ops::Deref for JitMemory { 183 | type Target = MemPage; 184 | fn deref(&self) -> &Self::Target { 185 | &self.pages[self.page.0] 186 | } 187 | } 188 | 189 | impl std::ops::DerefMut for JitMemory { 190 | fn deref_mut(&mut self) -> &mut Self::Target { 191 | &mut self.pages[self.page.0] 192 | } 193 | } 194 | 195 | impl Index for JitMemory { 196 | type Output = MemPage; 197 | 198 | fn index(&self, index: Page) -> &MemPage { 199 | &self.pages[index.0] 200 | } 201 | } 202 | 203 | impl IndexMut for JitMemory { 204 | fn index_mut(&mut self, index: Page) -> &mut MemPage { 205 | &mut self.pages[index.0] 206 | } 207 | } 208 | 209 | impl Index for JitMemory { 210 | type Output = u8; 211 | 212 | fn index(&self, index: Pos) -> &u8 { 213 | if index.0 >= PAGE_SIZE { 214 | panic!("Page size overflow") 215 | } 216 | unsafe { &*self.contents().add(index.0) } 217 | } 218 | } 219 | 220 | impl IndexMut for JitMemory { 221 | fn index_mut(&mut self, index: Pos) -> &mut u8 { 222 | if index.0 >= PAGE_SIZE { 223 | panic!("Page size overflow") 224 | } 225 | unsafe { &mut *self.contents().add(index.0) } 226 | } 227 | } 228 | 229 | impl std::default::Default for JitMemory { 230 | fn default() -> Self { 231 | Self::new() 232 | } 233 | } 234 | 235 | impl JitMemory { 236 | /// Create new JitMemory. 237 | /// 238 | /// This function try to allocate heap memory of 64KB for JIT assemble. 239 | /// 240 | /// ### panic 241 | /// Panic if Layout::from_size_align() or region::protect() returned Err. 242 | pub fn new() -> JitMemory { 243 | let layout = Layout::from_size_align(PAGE_SIZE * 3, PAGE_SIZE).expect("Bad Layout."); 244 | let contents = unsafe { alloc(layout) }; 245 | unsafe { 246 | protect(contents, PAGE_SIZE * 2, Protection::READ_WRITE_EXECUTE) 247 | .expect("Mprotect failed."); 248 | protect( 249 | contents.add(PAGE_SIZE * 2), 250 | PAGE_SIZE, 251 | Protection::READ_WRITE, 252 | ) 253 | .expect("Mprotect failed."); 254 | } 255 | let initial_page = MemPage::new(contents); 256 | let second_page = MemPage::new(unsafe { contents.add(PAGE_SIZE) }); 257 | let data_page = MemPage::new(unsafe { contents.add(PAGE_SIZE * 2) }); 258 | JitMemory { 259 | page: Page(0), 260 | pages: [initial_page, second_page, data_page], 261 | labels: Labels::new(), 262 | } 263 | } 264 | 265 | pub fn select_page(&mut self, page: usize) { 266 | assert!(page < 2); 267 | self.page = Page(page); 268 | } 269 | 270 | pub fn get_page(&self) -> usize { 271 | self.page.0 272 | } 273 | 274 | pub fn include(&self, ptr: *mut u8) -> bool { 275 | self.contents() <= ptr && ptr < unsafe { self.contents().add(PAGE_SIZE * 2) } 276 | } 277 | 278 | /// Resolve all relocations and return the top addresss of generated machine code as a function pointer. 279 | pub fn finalize(&mut self) { 280 | for page in &mut self.pages { 281 | let start_pos = page.code_block_top; 282 | let code_end = page.counter; 283 | page.code_len = page.counter.0; 284 | page.code_block.push((start_pos, code_end, Pos(0))); 285 | } 286 | self.resolve_constants(); 287 | self.resolve_data(); 288 | for page in &mut self.pages { 289 | page.code_block.last_mut().unwrap().2 = page.counter; 290 | } 291 | self.fill_relocs(); 292 | for page in &mut self.pages { 293 | page.code_block_top = page.counter; 294 | } 295 | } 296 | 297 | pub fn as_slice(&self) -> &[u8] { 298 | unsafe { std::slice::from_raw_parts(self.contents(), self.code_len) } 299 | } 300 | 301 | pub fn get_current(&self) -> usize { 302 | self.counter.0 303 | } 304 | 305 | fn _p(&self) { 306 | for i in 0..self.counter.0 { 307 | print!("{:>02x} ", self[Pos(i)]); 308 | } 309 | println!(); 310 | } 311 | 312 | /// Create a new label and returns `DestLabel`. 313 | pub fn label(&mut self) -> DestLabel { 314 | self.labels.new_label() 315 | } 316 | 317 | pub fn const_f64(&mut self, val: f64) -> DestLabel { 318 | let label = self.label(); 319 | let val = u64::from_ne_bytes(val.to_ne_bytes()); 320 | self.constants.push((DataType::U64(val), label)); 321 | label 322 | } 323 | 324 | pub fn const_i64(&mut self, val: i64) -> DestLabel { 325 | let label = self.label(); 326 | let val = val as u64; 327 | self.constants.push((DataType::U64(val), label)); 328 | label 329 | } 330 | 331 | pub fn data_i64(&mut self, val: i64) -> DestLabel { 332 | let label = self.label(); 333 | let val = val as u64; 334 | self.data.push((DataType::U64(val), label)); 335 | label 336 | } 337 | 338 | pub fn const_i32(&mut self, val: i32) -> DestLabel { 339 | let label = self.label(); 340 | let val = val as u32; 341 | self.constants.push((DataType::U32(val), label)); 342 | label 343 | } 344 | 345 | pub fn data_i32(&mut self, val: i32) -> DestLabel { 346 | let label = self.label(); 347 | let val = val as u32; 348 | self.data.push((DataType::U32(val), label)); 349 | label 350 | } 351 | 352 | pub fn constant(&mut self, size: usize) -> DestLabel { 353 | let label = self.label(); 354 | self.constants.push((DataType::Bytes(size), label)); 355 | label 356 | } 357 | 358 | pub fn data(&mut self, size: usize) -> DestLabel { 359 | let label = self.label(); 360 | self.data.push((DataType::Bytes(size), label)); 361 | label 362 | } 363 | 364 | pub fn abs_address(&mut self, addr_label: DestLabel) -> DestLabel { 365 | let label = self.label(); 366 | self.constants 367 | .push((DataType::AbsAddress(addr_label), label)); 368 | label 369 | } 370 | 371 | pub fn const_align8(&mut self) -> DestLabel { 372 | let label = self.label(); 373 | self.constants.push((DataType::Align8, label)); 374 | label 375 | } 376 | 377 | /// Bind the current location to `label`. 378 | pub fn bind_label(&mut self, label: DestLabel) { 379 | let src_page = self.page; 380 | self.bind_label_with_page(src_page, label); 381 | } 382 | 383 | pub fn bind_label_with_page(&mut self, src_page: Page, label: DestLabel) { 384 | let src_pos = self[src_page].counter; 385 | match &mut self.labels[label] { 386 | LabelInfo::Resolved(_) => {} //panic!("The DestLabel has already been resolved."), 387 | LabelInfo::NotResolved(targets) => { 388 | for target in std::mem::take(targets) { 389 | self.write_reloc(src_page, src_pos, target); 390 | } 391 | } 392 | } 393 | self.labels[label] = LabelInfo::Resolved((src_page, src_pos)); 394 | } 395 | 396 | /// Bind the current location to `label`. 397 | fn get_label_pos(&self, label: DestLabel) -> (Page, Pos) { 398 | self.labels[label].loc() 399 | } 400 | 401 | pub fn get_current_address(&self) -> CodePtr { 402 | let ptr = unsafe { self.contents().add(self.counter.0) }; 403 | CodePtr::from(ptr) 404 | } 405 | 406 | pub fn get_label_address(&self, label: DestLabel) -> CodePtr { 407 | let (page, pos) = self.get_label_pos(label); 408 | let ptr = unsafe { self[page].contents().add(pos.0) }; 409 | CodePtr::from(ptr) 410 | } 411 | 412 | /// Save relocaton slot for `DestLabel`. 413 | pub fn emit_reloc(&mut self, dest: DestLabel, offset: u8) { 414 | let page = self.page; 415 | let pos = self.counter; 416 | let target = TargetType::Rel { page, offset, pos }; 417 | self.emitl(0); 418 | match self.labels[dest] { 419 | LabelInfo::Resolved((src_page, src_pos)) => { 420 | self.write_reloc(src_page, src_pos, target); 421 | } 422 | LabelInfo::NotResolved(ref mut targets) => { 423 | targets.push(target); 424 | } 425 | } 426 | } 427 | 428 | /// Save relocaton slot for `DestLabel`. 429 | fn emit_absolute_reloc(&mut self, page: Page, dest: DestLabel) { 430 | let pos = self[page].counter; 431 | let target = TargetType::Abs { page, pos }; 432 | self[page].emitq(0); 433 | match self.labels[dest] { 434 | LabelInfo::Resolved((src_page, src_pos)) => { 435 | self.write_reloc(src_page, src_pos, target); 436 | } 437 | LabelInfo::NotResolved(ref mut targets) => { 438 | targets.push(target); 439 | } 440 | } 441 | } 442 | 443 | fn write_reloc(&mut self, src_page: Page, src_pos: Pos, target: TargetType) { 444 | let src_ptr = self[src_page].contents + src_pos.0; 445 | match target { 446 | TargetType::Rel { page, offset, pos } => { 447 | let target_ptr = self[page].contents + pos.0 + (offset as usize); 448 | let disp = (src_ptr as i128) - (target_ptr as i128); 449 | match i32::try_from(disp) { 450 | Ok(disp) => self[page].write32(pos, disp), 451 | Err(_) => panic!( 452 | "Relocation overflow. src:{:016x} dest:{:016x}", 453 | src_ptr, target_ptr 454 | ), 455 | } 456 | } 457 | TargetType::Abs { page, pos } => { 458 | self[page].write64(pos, src_ptr as _); 459 | } 460 | } 461 | } 462 | 463 | /// Resolve and fill all relocations. 464 | fn fill_relocs(&mut self) { 465 | //let mut reloc = std::mem::take(&mut self.labels); 466 | //for rel in reloc.iter_mut() { 467 | // if let LabelInfo::Resolved((src_page, src_pos)) = rel { 468 | // for target in std::mem::take(&mut rel.target) { 469 | // self.write_reloc(src_page, src_pos, target); 470 | // } 471 | // } 472 | //} 473 | //self.labels = reloc; 474 | } 475 | 476 | /// Resolve labels for constant data, and emit them to `contents`. 477 | fn resolve_constants(&mut self) { 478 | for id in 0..2 { 479 | let constants = std::mem::take(&mut self[Page(id)].constants); 480 | for (c, const_label) in constants { 481 | match c { 482 | DataType::U64(val) => { 483 | self[Page(id)].align16(); 484 | self.bind_label_with_page(Page(id), const_label); 485 | self[Page(id)].emitq(val); 486 | } 487 | DataType::U32(val) => { 488 | self[Page(id)].align4(); 489 | self.bind_label_with_page(Page(id), const_label); 490 | self[Page(id)].emitl(val); 491 | } 492 | DataType::Bytes(size) => { 493 | self[Page(id)].align16(); 494 | self.bind_label_with_page(Page(id), const_label); 495 | for _ in 0..size { 496 | self[Page(id)].emitb(0); 497 | } 498 | } 499 | DataType::AbsAddress(label) => { 500 | self[Page(id)].align8(); 501 | self.bind_label_with_page(Page(id), const_label); 502 | self.emit_absolute_reloc(Page(id), label); 503 | } 504 | DataType::Align8 => { 505 | self[Page(id)].align8(); 506 | self.bind_label_with_page(Page(id), const_label); 507 | } 508 | } 509 | } 510 | } 511 | } 512 | 513 | /// Resolve labels for data, and emit them to the data page. 514 | fn resolve_data(&mut self) { 515 | for id in 0..2 { 516 | let data = std::mem::take(&mut self[Page(id)].data); 517 | for (c, data_label) in data { 518 | match c { 519 | DataType::U64(val) => { 520 | self[DATA_PAGE].align16(); 521 | self.bind_label_with_page(DATA_PAGE, data_label); 522 | self[DATA_PAGE].emitq(val); 523 | } 524 | DataType::U32(val) => { 525 | self[DATA_PAGE].align4(); 526 | self.bind_label_with_page(DATA_PAGE, data_label); 527 | self[DATA_PAGE].emitl(val); 528 | } 529 | DataType::Bytes(size) => { 530 | self[DATA_PAGE].align16(); 531 | self.bind_label_with_page(DATA_PAGE, data_label); 532 | for _ in 0..size { 533 | self[DATA_PAGE].emitb(0); 534 | } 535 | } 536 | DataType::AbsAddress(label) => { 537 | self[DATA_PAGE].align8(); 538 | self.bind_label_with_page(DATA_PAGE, data_label); 539 | self.emit_absolute_reloc(DATA_PAGE, label); 540 | } 541 | DataType::Align8 => { 542 | self[DATA_PAGE].align8(); 543 | self.bind_label_with_page(DATA_PAGE, data_label); 544 | } 545 | } 546 | } 547 | } 548 | } 549 | 550 | pub fn get_label_addr(&mut self, label: DestLabel) -> extern "C" fn(T) -> U { 551 | let (page, counter) = self.labels[label].loc(); 552 | let adr = self[page].contents(); 553 | unsafe { mem::transmute(adr.add(counter.0)) } 554 | } 555 | 556 | pub fn get_label_addr2(&mut self, label: DestLabel) -> extern "C" fn(S, T) -> U { 557 | let (page, counter) = self.labels[label].loc(); 558 | let adr = self[page].contents(); 559 | unsafe { mem::transmute(adr.add(counter.0)) } 560 | } 561 | 562 | pub fn get_label_u64(&mut self, label: DestLabel) -> u64 { 563 | let (page, counter) = self.labels[label].loc(); 564 | let adr = self[page].contents(); 565 | unsafe { adr.add(counter.0) as u64 } 566 | } 567 | 568 | /// Emit bytes. 569 | pub fn emit(&mut self, slice: &[u8]) { 570 | slice.iter().for_each(|b| self.emitb(*b)); 571 | } 572 | 573 | /// 574 | /// Apply patch for the displacement of the jmp instruction in *patch_point*. 575 | /// 576 | pub fn apply_jmp_patch(&mut self, patch_point: DestLabel, jmp_dest: DestLabel) { 577 | let patch_point = self.get_label_address(patch_point); 578 | let jmp_dest = self.get_label_address(jmp_dest); 579 | let offset = jmp_dest - patch_point - 5; 580 | unsafe { *(patch_point.as_ptr().add(1) as *mut [u8; 4]) = (offset as i32).to_ne_bytes() }; 581 | } 582 | 583 | /// 584 | /// Apply patch for the displacement of the jmp instruction in *patch_point*. 585 | /// 586 | pub fn apply_jmp_patch_address(&mut self, patch_point: CodePtr, jmp_dest: DestLabel) { 587 | let jmp_dest = self.get_label_address(jmp_dest); 588 | let offset = jmp_dest - patch_point - 5; 589 | unsafe { *(patch_point.as_ptr().add(1) as *mut [u8; 4]) = (offset as i32).to_ne_bytes() }; 590 | } 591 | } 592 | 593 | #[allow(dead_code)] 594 | impl JitMemory { 595 | /// Encoding: Opcode +rd 596 | /// Op+ rd 597 | pub fn enc_o(&mut self, op: u8, reg: Reg) { 598 | assert!(!reg.is_rip()); 599 | self.rex_none(Reg(0), reg, Reg(0), Mode::Reg); 600 | self.op_with_rd(op, reg); 601 | } 602 | 603 | /// Encoding: Opcode +rd 604 | /// Op+ rd 605 | pub fn enc_oi(&mut self, op: u8, reg: Reg) { 606 | assert!(!reg.is_rip()); 607 | self.rex_none(Reg(0), reg, Reg(0), Mode::Reg); 608 | self.op_with_rd(op, reg); 609 | } 610 | 611 | pub fn enc_oi_byte(&mut self, op: u8, reg: Reg) { 612 | assert!(!reg.is_rip()); 613 | self.rex_none_byte(Reg(0), reg, Reg(0), Mode::Reg); 614 | self.op_with_rd(op, reg); 615 | } 616 | 617 | /// Encoding: Opcode +rd 618 | /// REX.W Op+ rd 619 | pub fn enc_rexw_o(&mut self, op: u8, reg: Reg) { 620 | assert!(!reg.is_rip()); 621 | self.rexw(Reg(0), reg, Reg(0), Mode::Reg); 622 | self.op_with_rd(op, reg); 623 | } 624 | 625 | /// Encoding: MI 626 | /// Op ModRM:r/m 627 | pub fn enc_mi(&mut self, op: u8, rm_op: Rm, imm: Imm) { 628 | self.encode(&[op], Rex::None, ModRM::Reg(Reg(0)), rm_op, imm); 629 | } 630 | 631 | /// Encoding: MI 632 | /// REX.W Op ModRM:r/m 633 | pub fn enc_rexw_mi(&mut self, op: u8, rm_op: Rm, imm: Imm) { 634 | self.encode(&[op], Rex::REXW, ModRM::Reg(Reg(0)), rm_op, imm); 635 | } 636 | 637 | pub fn enc_mi_byte(&mut self, op: u8, rm_op: Rm, imm: Imm) { 638 | self.encode(&[op], Rex::Byte, ModRM::Reg(Reg(0)), rm_op, imm); 639 | } 640 | 641 | /// REX Op ModRM 642 | /// MR-> ModRM:r/m(w) ModRM:reg(r) 643 | /// RM-> ModRM:reg(r) ModRM:r/m(w) 644 | pub fn enc_mr(&mut self, op: &[u8], reg: Reg, rm_op: Rm) { 645 | self.encode(op, Rex::None, ModRM::Reg(reg), rm_op, Imm::None); 646 | } 647 | 648 | pub fn enc_mr_byte(&mut self, op: &[u8], reg: Reg, rm_op: Rm) { 649 | self.encode(op, Rex::Byte, ModRM::Reg(reg), rm_op, Imm::None); 650 | } 651 | 652 | /// REX.W Op ModRM 653 | /// MR-> ModRM:r/m(w) ModRM:reg(r) 654 | /// RM-> ModRM:reg(r) ModRM:r/m(w) 655 | pub fn enc_rexw_mr(&mut self, op: &[u8], reg: Reg, rm_op: Rm) { 656 | self.encode(op, Rex::REXW, ModRM::Reg(reg), rm_op, Imm::None); 657 | } 658 | 659 | /// This is used in "setcc r/m8". 660 | pub fn enc_m_byte(&mut self, op: &[u8], rm: Rm) { 661 | self.encode(op, Rex::Byte, ModRM::Reg(Reg(0)), rm, Imm::None); 662 | } 663 | 664 | /// Encoding: D 665 | /// Op cd 666 | pub fn enc_d(&mut self, op: &[u8], dest: DestLabel) { 667 | self.emit(op); 668 | self.emit_reloc(dest, 4); 669 | } 670 | 671 | /// Encoding: /n 672 | /// Op /n 673 | pub fn enc_digit(&mut self, op: &[u8], rm: impl Into, digit: u8) { 674 | let rm = rm.into(); 675 | self.encode(op, Rex::None, ModRM::Digit(digit), rm, Imm::None); 676 | } 677 | 678 | pub fn enc_digit_imm(&mut self, op: &[u8], rm: Rm, digit: u8, imm: Imm) { 679 | self.encode(op, Rex::None, ModRM::Digit(digit), rm, imm); 680 | } 681 | 682 | pub fn enc_digit_imm_byte(&mut self, op: &[u8], op_al: u8, rm: Rm, digit: u8, imm: Imm) { 683 | if rm.is_rax() { 684 | self.emitb(op_al); 685 | self.emit_disp_imm(Disp::None, imm); 686 | } else if rm.is_reg() { 687 | self.encode(op, Rex::Byte, ModRM::Digit(digit), rm, imm); 688 | } else { 689 | self.encode(op, Rex::None, ModRM::Digit(digit), rm, imm); 690 | } 691 | } 692 | 693 | /*pub fn enc_rex_digit(&mut self, op: &[u8], rm: Rm, digit: u8, imm: Imm) { 694 | self.encode(op, Rex::REX, ModRM::Digit(digit), rm, imm); 695 | }*/ 696 | 697 | /// Encoding: /n 698 | /// REX.W Op /n 699 | pub fn enc_rexw_digit(&mut self, op: &[u8], rm: Rm, digit: u8, imm: Imm) { 700 | self.encode(op, Rex::REXW, ModRM::Digit(digit), rm, imm); 701 | } 702 | 703 | fn encode(&mut self, op: &[u8], rex: Rex, modrm_mode: ModRM, rm: Rm, imm: Imm) { 704 | let reg = match modrm_mode { 705 | ModRM::Digit(_) => Reg(0), 706 | ModRM::Reg(r) => r, 707 | }; 708 | let rex_fn = match rex { 709 | Rex::REXW => JitMemory::rexw, 710 | Rex::None => JitMemory::rex_none, 711 | Rex::Byte => JitMemory::rex_none_byte, 712 | }; 713 | assert!(!reg.is_rip()); 714 | if rm.base.is_rip() { 715 | // For rip, only indirect addressing with disp32 ([rip + disp32]) is allowed. 716 | // [rip] and [rip + disp8] are to be converted to [rip + disp32]. 717 | let rm = Rm::rip_ind_from(rm); 718 | rex_fn(self, reg, rm.base, Reg(0), rm.mode); 719 | self.emit(op); 720 | self.modrm(modrm_mode, Mode::Ind(Scale::None, Disp::None), rm.base); 721 | self.emit_disp_imm(rm.mode.disp(), imm); 722 | } else if rm.mode != Mode::Reg && (rm.base.0 & 0b111) == 4 { 723 | // If mode != Reg and r/m == 4/12 (rsp/r12), use SIB. 724 | match rm.mode { 725 | Mode::Ind(scale, disp) => { 726 | let (scale, index) = match scale { 727 | Scale::None => (0, Reg(4)), // magic number 728 | Scale::S1(scale, index) => (scale, index), 729 | }; 730 | let base = rm.base; 731 | rex_fn(self, reg, base, index, rm.mode); 732 | self.emit(op); 733 | self.modrm(modrm_mode, rm.mode, base); 734 | self.sib(scale, index, base); 735 | self.emit_disp_imm(disp, imm); 736 | } 737 | _ => unreachable!(), 738 | } 739 | } else if rm.mode.is_indirect_no_disp() && (rm.base.0 & 0b111) == 5 { 740 | // If mode == Ind and r/m == 5/13 (rbp/r13), use [rbp/r13 + 0(disp8)]. 741 | let scale = rm.mode.scale(); 742 | rex_fn(self, reg, rm.base, scale.index(), rm.mode); 743 | let mode = Mode::Ind(scale, Disp::D8(0)); 744 | self.emit(op); 745 | self.modrm(modrm_mode, mode, rm.base); 746 | match scale { 747 | Scale::None => {} 748 | Scale::S1(scale, index) => self.sib(scale, index, rm.base), 749 | } 750 | self.emit_disp_imm(mode.disp(), imm); 751 | } else { 752 | rex_fn( 753 | self, 754 | reg, 755 | rm.base, 756 | match rm.mode { 757 | Mode::Reg => Reg(0), 758 | Mode::Ind(scale, _) => match scale { 759 | Scale::None => Reg(0), 760 | Scale::S1(_, index) => index, 761 | }, 762 | }, 763 | rm.mode, 764 | ); 765 | // index != Reg::RIP 766 | self.emit(op); 767 | self.modrm(modrm_mode, rm.mode, rm.base); 768 | match rm.mode { 769 | Mode::Reg => {} 770 | Mode::Ind(scale, _) => match scale { 771 | Scale::None => {} 772 | Scale::S1(scale, index) => self.sib(scale, index, rm.base), 773 | }, 774 | }; 775 | self.emit_disp_imm(rm.mode.disp(), imm); 776 | } 777 | } 778 | } 779 | 780 | impl JitMemory { 781 | /// ModRM 782 | /// 783 | /// ~~~~text 784 | /// +-------+---+---+---+---+---+---+---+---+ 785 | /// | bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 786 | /// +-------+---+---+---+---+---+---+---+---+ 787 | /// | field | mod | reg | r/m | 788 | /// +-------+-------+-----------+-----------+ 789 | /// | rex | | r | b | 790 | /// +-------+-------+-----------+-----------+ 791 | /// ~~~~ 792 | /// 793 | fn modrm(&mut self, modrm_mode: ModRM, mode: Mode, base: Reg) { 794 | let base = match mode { 795 | Mode::Reg | Mode::Ind(Scale::None, _) => base, 796 | Mode::Ind(_, _) => Reg(4), 797 | }; 798 | let mode = mode.encode(); 799 | let modrm = mode << 6 800 | | (match modrm_mode { 801 | ModRM::Digit(d) => d, 802 | ModRM::Reg(r) => r.0, 803 | } & 0b111) 804 | << 3 805 | | (base.0 & 0b111); 806 | self.emitb(modrm); 807 | } 808 | 809 | /// REX.W 810 | /// 811 | /// ~~~~text 812 | /// bit 813 | /// +---+---+------------------------------------------------+ 814 | /// | W | 3 | 1 = 64 bit operand size | 815 | /// +---+---+------------------------------------------------+ 816 | /// | R | 2 | rex_r = ext of reg field of ModRM | 817 | /// +---+---+------------------------------------------------+ 818 | /// | X | 1 | rex_i = ext of index field of SIB | 819 | /// +---+---+------------------------------------------------+ 820 | /// | B | 0 | rex_b = ext of r/m(ModRM) or base(SIB) | 821 | /// | | | or reg field of Op. | 822 | /// +---+---+------------------------------------------------+ 823 | /// ~~~~ 824 | /// 825 | fn rexw(&mut self, reg: Reg, base: Reg, index: Reg, _mode: Mode) { 826 | let rexw = 0x48 | (reg.0 & 0b1000) >> 1 | (index.0 & 0b1000) >> 2 | (base.0 & 0b1000) >> 3; 827 | self.emitb(rexw); 828 | } 829 | 830 | fn rex_none(&mut self, reg: Reg, base: Reg, index: Reg, mode: Mode) { 831 | if reg.0 > 7 || base.0 > 7 || index.0 > 7 { 832 | self.rex(reg, base, index, mode); 833 | }; 834 | } 835 | 836 | fn rex_none_byte(&mut self, reg: Reg, base: Reg, index: Reg, mode: Mode) { 837 | if reg.0 > 7 || base.0 > 7 || index.0 > 7 { 838 | self.rex(reg, base, index, mode); 839 | } else if reg.0 > 3 || (base.0 > 3 && mode == Mode::Reg) { 840 | self.rex(Reg(0), Reg(0), Reg(0), mode); 841 | }; 842 | } 843 | 844 | fn rex(&mut self, reg: Reg, base: Reg, index: Reg, _mode: Mode) { 845 | let rex_prefix = 846 | 0x40 | (reg.0 & 0b1000) >> 1 | (index.0 & 0b1000) >> 2 | (base.0 & 0b1000) >> 3; 847 | self.emitb(rex_prefix); 848 | } 849 | 850 | fn op_with_rd(&mut self, op: u8, reg: Reg) { 851 | let op = op | (reg.0 & 0b0111); 852 | self.emitb(op); 853 | } 854 | 855 | /// SIB 856 | /// 857 | /// ~~~~text 858 | /// +-------+---+---+---+---+---+---+---+---+ 859 | /// | bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 860 | /// +-------+---+---+---+---+---+---+---+---+ 861 | /// | field | scale | index | base | 862 | /// +-------+-------+-----------+-----------+ 863 | /// | rex | | x | b | 864 | /// +-------+-------+-----------+-----------+ 865 | /// 866 | /// scale: 00|01|10|11 867 | /// mul : na| 2| 4| 8 868 | /// 869 | /// index: register number (with rex.x) 870 | /// 871 | /// base: register number (with rex.b) 872 | /// rex.b:0 base:101 => use RBP mode:00/disp32 01/RBP+disp8 10/RBP+disp32 873 | /// rex.b:1 base:101 => use R13 mode:00/disp32 01/R13+disp8 10/R13+disp32 874 | /// ~~~~ 875 | /// 876 | fn sib(&mut self, scale: u8, index: Reg, base: Reg) { 877 | assert!(scale < 4); 878 | assert!(index.0 < 16); 879 | let sib = (scale << 6) | ((index.0 & 0b111) << 3) | (base.0 & 0b111); 880 | self.emitb(sib); 881 | } 882 | 883 | fn emit_disp_imm(&mut self, disp: Disp, imm: Imm) { 884 | match disp { 885 | Disp::D8(d) => self.emitb(d as u8), 886 | Disp::D32(d) => self.emitl(d as u32), 887 | Disp::Label(label) => { 888 | self.emit_reloc(label, 4 + imm.offset()); 889 | } 890 | Disp::None => {} 891 | } 892 | match imm { 893 | Imm::None => {} 894 | Imm::B(b) => self.emitb(b as u8), 895 | Imm::W(w) => self.emitw(w as u16), 896 | Imm::L(l) => self.emitl(l as u32), 897 | Imm::Q(q) => self.emitq(q as u64), 898 | } 899 | } 900 | } 901 | impl JitMemory { 902 | /// Dump generated code. 903 | pub fn dump_code(&self) -> Result { 904 | use std::process::Command; 905 | let asm = self.as_slice(); 906 | let mut file = tempfile::NamedTempFile::new()?; 907 | let (start_pos, code_end, _end_pos) = self.code_block.last().unwrap(); 908 | file.write_all(&asm[start_pos.0..code_end.0]).unwrap(); 909 | 910 | Command::new("objdump") 911 | .args([ 912 | "-D", 913 | "-Mintel,x86-64", 914 | "-b", 915 | "binary", 916 | "-m", 917 | "i386", 918 | file.path().to_str().unwrap(), 919 | ]) 920 | .output() 921 | .map(|o| { 922 | std::str::from_utf8(&o.stdout) 923 | .unwrap() 924 | .to_string() 925 | .split_inclusive('\n') 926 | .filter(|s| { 927 | s.len() > 1 928 | && !s.contains("file format binary") 929 | && !s.contains("Disassembly of section") 930 | && !s.contains("<.data>") 931 | }) 932 | .collect() 933 | }) 934 | } 935 | } 936 | -------------------------------------------------------------------------------- /monoasm_macro/src/asm.rs: -------------------------------------------------------------------------------- 1 | use super::inst::*; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | 5 | pub fn compile(inst: Inst) -> TokenStream { 6 | match inst { 7 | Inst::Label(ident) => quote!( jit.bind_label(#ident); ), 8 | Inst::F64(f) => { 9 | quote!( jit.emit(&#f.to_ne_bytes()); ) 10 | } 11 | Inst::I64(i) => { 12 | quote!( jit.emitq(#i as u64); ) 13 | } 14 | 15 | Inst::Movq(op1, op2) => movq(op1, op2), 16 | Inst::Movl(op1, op2) => movl(op1, op2), 17 | Inst::Movw(op1, op2) => movw(op1, op2), 18 | Inst::Movb(op1, op2) => movb(op1, op2), 19 | Inst::Movsxl(op1, op2) => quote!( jit.enc_rexw_mr(&[0x63], #op1, #op2); ), 20 | Inst::Movzxw(op1, op2) => quote!( jit.enc_rexw_mr(&[0x0f, 0xb7], #op1, #op2); ), 21 | Inst::Movsxw(op1, op2) => quote!( jit.enc_rexw_mr(&[0x0f, 0xbf], #op1, #op2); ), 22 | Inst::Movzxb(op1, op2) => quote!( jit.enc_rexw_mr(&[0x0f, 0xb6], #op1, #op2); ), 23 | Inst::Movsxb(op1, op2) => quote!( jit.enc_rexw_mr(&[0x0f, 0xbe], #op1, #op2); ), 24 | Inst::Add(size, op1, op2) => binary_op(size, "ADD", 0x01, 0, op1, op2), 25 | Inst::Or(size, op1, op2) => binary_op(size, "OR", 0x09, 1, op1, op2), 26 | Inst::Adc(size, op1, op2) => binary_op(size, "ADC", 0x11, 2, op1, op2), 27 | Inst::Sbb(size, op1, op2) => binary_op(size, "SBB", 0x19, 3, op1, op2), 28 | Inst::And(size, op1, op2) => binary_op(size, "AND", 0x21, 4, op1, op2), 29 | Inst::Sub(size, op1, op2) => binary_op(size, "SUB", 0x29, 5, op1, op2), 30 | Inst::Xor(size, op1, op2) => binary_op(size, "XOR", 0x31, 6, op1, op2), 31 | Inst::Cmp(size, op1, op2) => binary_op(size, "CMP", 0x39, 7, op1, op2), 32 | 33 | Inst::Xchg(size, op1, op2) => match size { 34 | OperandSize::QWORD => match (&op1, &op2) { 35 | (RmOperand::Reg(r1), RmOperand::Reg(r2)) => quote! { 36 | if #r1 == Reg::from(0) { 37 | if #r2 == Reg::from(0) { 38 | jit.emitb(0x90); 39 | } else { 40 | jit.enc_rexw_o(0x90, #r2); 41 | } 42 | } else if #r2 == Reg::from(0) { 43 | jit.enc_rexw_o(0x90, #r1); 44 | } else { 45 | jit.enc_rexw_mr(&[0x87], #r2, #op1); 46 | } 47 | }, 48 | (op1, RmOperand::Reg(op2)) => quote! { 49 | jit.enc_rexw_mr(&[0x87], #op2, #op1); 50 | }, 51 | (RmOperand::Reg(op1), RmOperand::Ind(op2)) => quote! { 52 | jit.enc_rexw_mr(&[0x87], #op1, #op2); 53 | }, 54 | (op1, op2) => { 55 | panic!("'Xchg {op1}, {op2}' does not exists.") 56 | } 57 | }, 58 | _ => panic!("'Xchg {:?} {op1}, {op2}' does not exists.", size), 59 | }, 60 | 61 | Inst::Testq(op1, op2) => match op2 { 62 | RiOperand::Imm(i) => { 63 | let op1_str = format!("{}", op1); 64 | match &op1 { 65 | RmOperand::Reg(reg) => { 66 | quote! { 67 | let imm = (#i) as i64; 68 | if let Ok(imm) = i32::try_from(imm) { 69 | if #reg.is_rax() { 70 | jit.emit(&[0x48, 0xa9]); 71 | jit.emitl(imm as u32); 72 | } else { 73 | jit.enc_rexw_mi(0xf7, #op1, Imm::L(imm)); 74 | } 75 | } else { 76 | panic!("'TEST {}, imm64' does not exists.", #op1_str); 77 | } 78 | } 79 | } 80 | _ => { 81 | quote! { 82 | let imm = (#i) as i64; 83 | if let Ok(imm) = i32::try_from(imm) { 84 | jit.enc_rexw_mi(0xf7, #op1, Imm::L(imm)); 85 | } else { 86 | panic!("'TEST {}, imm64' does not exists.", #op1_str); 87 | } 88 | } 89 | } 90 | } 91 | } 92 | RiOperand::Reg(expr) => quote!( jit.enc_rexw_mr(&[0x85], #expr, #op1); ), 93 | }, 94 | Inst::Testb(op1, op2) => match op2 { 95 | RiOperand::Imm(i) => { 96 | let op1_str = format!("{}", op1); 97 | match &op1 { 98 | RmOperand::Reg(reg) => { 99 | quote! { 100 | let imm = (#i) as i64; 101 | if let Ok(imm) = i8::try_from(imm) { 102 | if #reg.is_rax() { 103 | jit.emit(&[0xa8]); 104 | jit.emitb(imm as u8); 105 | } else { 106 | jit.enc_mi_byte(0xf6, #op1, Imm::B(imm)); 107 | } 108 | } else { 109 | panic!("'TEST {}, imm64' does not exists.", #op1_str); 110 | } 111 | } 112 | } 113 | _ => { 114 | quote! { 115 | let imm = (#i) as i64; 116 | if let Ok(imm) = i8::try_from(imm) { 117 | jit.enc_mi_byte(0xf6, #op1, Imm::B(imm)); 118 | } else { 119 | panic!("'TEST {}, imm64' does not exists.", #op1_str); 120 | } 121 | } 122 | } 123 | } 124 | } 125 | RiOperand::Reg(expr) => quote!( jit.enc_mr_byte(&[0x84], #expr, #op1); ), 126 | }, 127 | 128 | Inst::Rol(width, op1, op2) => shift_op(width, 0, op1, op2, "ROL"), 129 | Inst::Ror(width, op1, op2) => shift_op(width, 1, op1, op2, "ROR"), 130 | Inst::Shl(width, op1, op2) => shift_op(width, 4, op1, op2, "SHL"), 131 | Inst::Shr(width, op1, op2) => shift_op(width, 5, op1, op2, "SHR"), 132 | Inst::Sal(width, op1, op2) => shift_op(width, 4, op1, op2, "SAL"), 133 | Inst::Sar(width, op1, op2) => shift_op(width, 7, op1, op2, "SAR"), 134 | 135 | Inst::Setcc(cond, op) => { 136 | let cond: u8 = 0x90 + cond as u8; 137 | quote!( jit.enc_m_byte(&[0x0f, #cond], #op); ) 138 | } 139 | Inst::Cmovcc(size, cond, op1, op2) => { 140 | let cond: u8 = 0x40 + cond as u8; 141 | match size { 142 | OperandSize::QWORD => quote!( jit.enc_rexw_mr(&[0x0f, #cond], #op1, #op2); ), 143 | OperandSize::DWORD => quote!( jit.enc_mr(&[0x0f, #cond], #op1, #op2); ), 144 | OperandSize::WORD => quote!( 145 | jit.emitb(0x66); 146 | jit.enc_mr(&[0x0f, #cond], #op1, #op2); 147 | ), 148 | _ => unimplemented!(), 149 | } 150 | } 151 | 152 | Inst::Cqo => quote! ( jit.emit(&[0x48, 0x99]); ), 153 | Inst::Cdq => quote! ( jit.emit(&[0x99]); ), 154 | Inst::Int3 => quote! ( jit.emit(&[0xcc]); ), 155 | 156 | Inst::Negq(op) => quote! ( jit.enc_rexw_digit(&[0xf7], #op, 3, Imm::None); ), 157 | Inst::Notq(op) => quote! ( jit.enc_rexw_digit(&[0xf7], #op, 2, Imm::None); ), 158 | 159 | Inst::Imul(op1, op2) => { 160 | // IMUL r64, r/m64: r64 <- r64 * r/m64 161 | match (op1, op2) { 162 | // IMUL r64, r/m64 163 | // REX.W 0F AF /r 164 | // RM 165 | (RmiOperand::Reg(expr), op2) => quote! ( 166 | jit.enc_rexw_mr(&[0x0f,0xaf], #expr, #op2); 167 | ), 168 | 169 | _ => unimplemented!(), 170 | } 171 | } 172 | 173 | Inst::Idiv(op) => divide_op(OperandSize::QWORD, true, op), 174 | Inst::Idivl(op) => divide_op(OperandSize::DWORD, true, op), 175 | Inst::Div(op) => divide_op(OperandSize::QWORD, false, op), 176 | Inst::Divl(op) => divide_op(OperandSize::DWORD, false, op), 177 | 178 | Inst::Movsd(op1, op2) => match (op1, op2) { 179 | (XmOperand::Xmm(op1), op2) => quote! { 180 | jit.emitb(0xf2); 181 | jit.enc_mr(&[0x0f, 0x10], Reg::from(#op1), #op2); 182 | }, 183 | (op1, XmOperand::Xmm(op2)) => quote! { 184 | jit.emitb(0xf2); 185 | jit.enc_mr(&[0x0f, 0x11], Reg::from(#op2), #op1); 186 | }, 187 | _ => { 188 | panic!("'MOVSD m64, m64' does not exists.") 189 | } 190 | }, 191 | Inst::Addsd(op1, op2) => binary_sd_op(0x58, op1, op2), 192 | Inst::Subsd(op1, op2) => binary_sd_op(0x5c, op1, op2), 193 | Inst::Mulsd(op1, op2) => binary_sd_op(0x59, op1, op2), 194 | Inst::Divsd(op1, op2) => binary_sd_op(0x5e, op1, op2), 195 | Inst::Minsd(op1, op2) => binary_sd_op(0x5d, op1, op2), 196 | Inst::Maxsd(op1, op2) => binary_sd_op(0x5f, op1, op2), 197 | Inst::Xorps(Xmm(op1), op2) => { 198 | quote! { 199 | jit.enc_mr(&[0x0f, 0x57], Reg::from(#op1), #op2); 200 | } 201 | } 202 | 203 | Inst::Lea(op1, op2) => match (op1, op2) { 204 | (RmOperand::Reg(op1), RmOperand::Ind(op2)) => quote! { 205 | jit.enc_rexw_mr(&[0x8d], #op1, #op2); 206 | }, 207 | (op1, op2) => { 208 | panic!("'Lea {}, {}' does not exists.", op1, op2) 209 | } 210 | }, 211 | 212 | Inst::Cvtsi2sdq(Xmm(op1), op2) => { 213 | quote! { 214 | jit.emitb(0xf2); 215 | jit.enc_rexw_mr(&[0x0f, 0x2a], Reg::from(#op1), #op2); 216 | } 217 | } 218 | 219 | Inst::Cvttsd2siq(op1, op2) => { 220 | quote! { 221 | jit.emitb(0xf2); 222 | jit.enc_mr(&[0x0f, 0x2c], #op1, #op2); 223 | } 224 | } 225 | 226 | Inst::Andpd(Xmm(op1), op2) => { 227 | quote! { 228 | jit.emitb(0x66); 229 | jit.enc_mr(&[0x0f, 0x54], Reg::from(#op1), #op2); 230 | } 231 | } 232 | 233 | Inst::Xorpd(Xmm(op1), op2) => { 234 | quote! { 235 | jit.emitb(0x66); 236 | jit.enc_mr(&[0x0f, 0x57], Reg::from(#op1), #op2); 237 | } 238 | } 239 | 240 | Inst::Roundpd(Xmm(op1), op2, ImmOperand(op3)) => { 241 | quote! { 242 | jit.emitb(0x66); 243 | jit.enc_mr(&[0x0f, 0x3a, 0x09], Reg::from(#op1), #op2); 244 | jit.emitb(#op3); 245 | } 246 | } 247 | 248 | Inst::Sqrtpd(Xmm(op1), op2) => { 249 | quote! { 250 | jit.emitb(0x66); 251 | jit.enc_mr(&[0x0f, 0x51], Reg::from(#op1), #op2); 252 | } 253 | } 254 | Inst::Sqrtsd(Xmm(op1), op2) => { 255 | quote! { 256 | jit.emitb(0xf2); 257 | jit.enc_mr(&[0x0f, 0x51], Reg::from(#op1), #op2); 258 | } 259 | } 260 | 261 | Inst::Pushq(op) => push_pop(0x50, (0xff, 6), op), 262 | Inst::Popq(op) => push_pop(0x58, (0x8f, 0), op), 263 | 264 | Inst::Call(dest) => match dest { 265 | Dest::Reg(r) => { 266 | // CALL r/m64 267 | // FF /2 268 | quote! { 269 | jit.enc_digit(&[0xff], #r, 2); 270 | } 271 | } 272 | Dest::Ind(ind) => { 273 | // CALL r/m64 274 | // FF /2 275 | quote! { 276 | jit.enc_digit(&[0xff], #ind, 2); 277 | } 278 | } 279 | Dest::Rel(dest) => { 280 | // CALL rel32 281 | // E8 cd 282 | quote! { 283 | jit.emitb(0xe8); 284 | jit.emit_reloc(#dest, 4); 285 | } 286 | } 287 | Dest::Disp(imm) => { 288 | quote! { 289 | if let Ok(imm) = i32::try_from(#imm as i64) { 290 | jit.emitb(0xe8); 291 | jit.emitl(imm as u32); 292 | } else { 293 | panic!("'CALL ({})' out of range.", #imm); 294 | } 295 | } 296 | } 297 | dest => unimplemented!("CALL {:?}", dest), 298 | }, 299 | Inst::Ret => quote!( jit.emitb(0xc3); ), 300 | Inst::Jmp(dest) => match dest { 301 | Dest::Reg(r) => { 302 | // JMP r/m64 303 | // FF /4 304 | // M 305 | quote! ( 306 | jit.enc_digit(&[0xff], #r, 4); 307 | ) 308 | } 309 | Dest::Ind(ind) => { 310 | // JMP r/m64 311 | // FF /4 312 | // M 313 | quote! ( 314 | jit.enc_digit(&[0xff], #ind, 4); 315 | ) 316 | } 317 | // JMP rel32 318 | // E9 cd 319 | // D 320 | Dest::Rel(dest) => quote! ( jit.enc_d(&[0xe9], #dest); ), 321 | dest => unimplemented!("JMP {:?}", dest), 322 | }, 323 | Inst::Jcc(cond, dest) => { 324 | let cond = 0x80 + cond as u8; 325 | quote!( jit.enc_d(&[0x0f, #cond], #dest); ) 326 | } 327 | 328 | Inst::UComIsd(op1, op2) => { 329 | let op1 = op1.0; 330 | quote!( 331 | jit.emitb(0x66); 332 | jit.enc_mr(&[0x0f, 0x2e], Reg::from(#op1), #op2); 333 | ) 334 | } 335 | 336 | Inst::Syscall => quote!( 337 | jit.emit(&[0x0f, 0x05]); 338 | ), 339 | Inst::Leave => quote!( 340 | jit.emitb(0xc9); 341 | ), 342 | Inst::Tzcnt(width, op1, op2) => count_op(width, 0xbc, op1, op2), 343 | Inst::Lzcnt(width, op1, op2) => count_op(width, 0xbd, op1, op2), 344 | Inst::Popcnt(width, op1, op2) => count_op(width, 0xb8, op1, op2), 345 | } 346 | } 347 | 348 | fn movq(op1: MovOperand, op2: MovOperand) -> TokenStream { 349 | match (op1, op2) { 350 | (MovOperand::Xmm(op1), op2) => match &op2 { 351 | MovOperand::Imm(..) => panic!("'MOV xmm, Imm' does not exists."), 352 | MovOperand::Reg(op2) => quote! { 353 | let op2 = Rm::reg(#op2); 354 | jit.emitb(0x66); 355 | jit.enc_rexw_mr(&[0x0f, 0x6e], Reg::from(#op1), op2); 356 | }, 357 | op2 => quote! { 358 | jit.emitb(0xf3); 359 | jit.enc_mr(&[0x0f, 0x7e], Reg::from(#op1), #op2); 360 | }, 361 | }, 362 | (MovOperand::Imm(..), op2) => panic!("'MOV Imm, {}' does not exists.", op2), 363 | // MOV r/m64, imm32 364 | // REX.W + C7 /0 id 365 | // MI 366 | // 367 | // MOV r64, imm64 368 | // REX.W + B8+ rd io 369 | // OI 370 | (MovOperand::Reg(expr), MovOperand::Imm(i, size)) => { 371 | if let Some(size) = size { 372 | assert_eq!(OperandSize::QWORD, size); 373 | quote!( 374 | let imm = (#i) as i64; 375 | let rm_op = Rm::reg(#expr); 376 | // MOV r64, imm64 377 | jit.enc_rexw_o(0xb8, #expr); 378 | jit.emitq(imm as u64); 379 | ) 380 | } else { 381 | quote!( 382 | let imm = (#i) as i64; 383 | let rm_op = Rm::reg(#expr); 384 | //if let Ok(imm) = i32::try_from(imm) { 385 | // // MOV r/m64, imm32 386 | // jit.enc_rexw_mi(0xc7, rm_op, Imm::L(imm)); 387 | //} else { 388 | // // MOV r64, imm64 389 | jit.enc_rexw_o(0xb8, #expr); 390 | jit.emitq(imm as u64); 391 | //}; 392 | ) 393 | } 394 | } 395 | // MOV r/m64, imm32 396 | // REX.W + C7 /0 id 397 | // MI 398 | (op1, MovOperand::Imm(i, None)) => { 399 | let op1_str = format!("{}", op1); 400 | quote! { 401 | let imm = (#i) as i64; 402 | if let Ok(imm) = i32::try_from(imm) { 403 | jit.enc_rexw_mi(0xc7, #op1, Imm::L(imm)); 404 | } else { 405 | panic!("'MOV {}, imm64' does not exists.", #op1_str); 406 | } 407 | } 408 | } 409 | // MOV r/m64,r64 410 | // REX.W + 89 /r 411 | // MR 412 | (op1, MovOperand::Reg(expr)) => quote!( jit.enc_rexw_mr(&[0x89], #expr, #op1); ), 413 | (MovOperand::Reg(op1), MovOperand::Xmm(op2)) => quote! { 414 | let op1 = Rm::reg(#op1); 415 | jit.emitb(0x66); 416 | jit.enc_rexw_mr(&[0x0f, 0x7e], Reg::from(#op2), op1); 417 | }, 418 | (op1, MovOperand::Xmm(op2)) => quote! { 419 | jit.emitb(0x66); 420 | jit.enc_mr(&[0x0f, 0xd6], Reg::from(#op2), #op1); 421 | }, 422 | // MOV r64,m64 423 | // REX.W + 8B /r 424 | // RM 425 | (MovOperand::Reg(op1), op2) => quote!( jit.enc_rexw_mr(&[0x8b], #op1, #op2); ), 426 | (op1, op2) => unimplemented!("MOV {}, {}", op1, op2), 427 | } 428 | } 429 | 430 | fn movl(op1: RmOperand, op2: RmiOperand) -> TokenStream { 431 | match (op1, op2) { 432 | // MOV r32, imm32 433 | // B8+ rd id 434 | // OI 435 | (RmOperand::Reg(expr), RmiOperand::Imm(i)) => { 436 | quote!( 437 | let imm = (#i) as i64; 438 | if let Ok(imm) = i32::try_from(imm) { 439 | // MOV r32, imm32 440 | jit.enc_oi(0xb8, #expr); 441 | jit.emitl(imm as u32); 442 | } else { 443 | panic!("'MOVL {}, {}' does not exists.", #expr, imm); 444 | }; 445 | ) 446 | } 447 | // MOV r/m32, imm32 448 | // C7 /0 id 449 | // MI 450 | (op1, RmiOperand::Imm(i)) => { 451 | let op1_str = format!("{}", op1); 452 | quote! { 453 | let imm = (#i) as i64; 454 | if let Ok(imm) = i32::try_from(imm) { 455 | jit.enc_mi(0xc7, #op1, Imm::L(imm)); 456 | } else { 457 | panic!("'MOVL {}, imm64' does not exists.", #op1_str); 458 | } 459 | } 460 | } 461 | // MOV r/m32, r32 462 | // 89 /r 463 | // MR 464 | (op1, RmiOperand::Reg(expr)) => quote!( jit.enc_mr(&[0x89], #expr, #op1); ), 465 | // MOV r32, m32 466 | // 8B /r 467 | // RM 468 | (RmOperand::Reg(op1), op2) => quote!( jit.enc_mr(&[0x8b], #op1, #op2); ), 469 | (op1, op2) => unimplemented!("MOV {}, {}", op1, op2), 470 | } 471 | } 472 | 473 | fn movw(op1: RmOperand, op2: RmiOperand) -> TokenStream { 474 | match (op1, op2) { 475 | // MOV r16, imm16 476 | // B8+ rw iw 477 | (RmOperand::Reg(expr), RmiOperand::Imm(i)) => { 478 | quote!( 479 | let imm = (#i) as i64; 480 | if let Ok(imm) = i16::try_from(imm) { 481 | // MOV r16, imm16 482 | jit.emitb(0x66); 483 | jit.enc_oi(0xb8, #expr); 484 | jit.emitw(imm as u16); 485 | } else { 486 | panic!("'MOVW {}, {}' does not exists.", #expr, imm); 487 | }; 488 | ) 489 | } 490 | // MOV r/m16, imm16 491 | // C7 /0 iw 492 | (op1, RmiOperand::Imm(i)) => { 493 | let op1_str = format!("{}", op1); 494 | quote! { 495 | let imm = (#i) as i64; 496 | if let Ok(imm) = i16::try_from(imm) { 497 | jit.emitb(0x66); 498 | jit.enc_mi(0xc7, #op1, Imm::W(imm)); 499 | } else { 500 | panic!("'MOVW {}, {}' does not exists.", #op1_str, imm); 501 | } 502 | } 503 | } 504 | // MOV r/m16, r16 505 | // 89 /r 506 | (op1, RmiOperand::Reg(expr)) => quote!( 507 | jit.emitb(0x66); 508 | jit.enc_mr(&[0x89], #expr, #op1); 509 | ), 510 | // MOV r16, m16 511 | // 8B /r 512 | (RmOperand::Reg(op1), op2) => quote!( 513 | jit.emitb(0x66); 514 | jit.enc_mr(&[0x8b], #op1, #op2); 515 | ), 516 | (op1, op2) => unimplemented!("MOV {}, {}", op1, op2), 517 | } 518 | } 519 | 520 | fn movb(op1: RmOperand, op2: RmiOperand) -> TokenStream { 521 | match (op1, op2) { 522 | // MOV r8, imm8 523 | // B0+ rb ib 524 | (RmOperand::Reg(expr), RmiOperand::Imm(i)) => { 525 | quote!( 526 | let imm = (#i) as i64; 527 | if let Ok(imm) = i8::try_from(imm) { 528 | // MOV r8, imm8 529 | //jit.emitb(0x66); 530 | jit.enc_oi_byte(0xb0, #expr); 531 | jit.emitb(imm as u8); 532 | } else { 533 | panic!("'MOVW {}, {}' does not exists.", #expr, imm); 534 | }; 535 | ) 536 | } 537 | // MOV r/m8, imm8 538 | // C6 /0 ib 539 | (op1, RmiOperand::Imm(i)) => { 540 | let op1_str = format!("{}", op1); 541 | quote! { 542 | let imm = (#i) as i64; 543 | if let Ok(imm) = i8::try_from(imm) { 544 | jit.enc_mi_byte(0xc6, #op1, Imm::B(imm)); 545 | } else { 546 | panic!("'MOVW {}, {}' does not exists.", #op1_str, imm); 547 | } 548 | } 549 | } 550 | // MOV r/m8, r8 551 | // 88 /r 552 | (op1, RmiOperand::Reg(expr)) => quote!( 553 | //jit.emitb(0x66); 554 | jit.enc_mr_byte(&[0x88], #expr, #op1); 555 | ), 556 | // MOV r8, m8 557 | // 8A /r 558 | (RmOperand::Reg(op1), op2) => quote!( 559 | //jit.emitb(0x66); 560 | jit.enc_mr_byte(&[0x8a], #op1, #op2); 561 | ), 562 | (op1, op2) => unimplemented!("MOV {}, {}", op1, op2), 563 | } 564 | } 565 | 566 | fn binary_op( 567 | size: OperandSize, 568 | op_name: &str, 569 | op_mr: u8, 570 | digit: u8, 571 | op1: RmOperand, 572 | op2: RmiOperand, 573 | ) -> TokenStream { 574 | match size { 575 | OperandSize::QWORD => binary_opq(op_name, op_mr, digit, op1, op2), 576 | OperandSize::DWORD => binary_opl(op_name, op_mr, digit, op1, op2), 577 | OperandSize::WORD => binary_opw(op_name, op_mr, digit, op1, op2), 578 | OperandSize::BYTE => binary_opb(op_name, op_mr - 1, digit, op1, op2), 579 | } 580 | } 581 | 582 | fn binary_opq(op_name: &str, op_mr: u8, digit: u8, op1: RmOperand, op2: RmiOperand) -> TokenStream { 583 | let op_rm = op_mr + 2; 584 | match (op1, op2) { 585 | // OP r/m64, imm32 586 | // OP=ADD REX.W 81 /0 id 587 | // OP=OR REX.W 81 /1 id 588 | // OP=ADC REX.W 81 /2 id 589 | // OP=SBB REX.W 81 /3 id 590 | // OP=AND REX.W 81 /4 id 591 | // OP=SUB REX.W 81 /5 id 592 | // OP=XOR REX.W 81 /6 id 593 | // OP=CMP REX.W 81 /7 id 594 | // 595 | // OP r/m64, imm8 596 | // OP=ADD REX.W 83 /0 id 597 | // OP=OR REX.W 83 /1 id 598 | // OP=ADC REX.W 83 /2 id 599 | // OP=SBB REX.W 83 /3 id 600 | // OP=AND REX.W 83 /4 id 601 | // OP=SUB REX.W 83 /5 id 602 | // OP=XOR REX.W 83 /6 id 603 | // OP=CMP REX.W 83 /7 id 604 | (op1, RmiOperand::Imm(i)) => { 605 | let op1_str = format!("{}", op1); 606 | quote! { 607 | let imm = (#i) as i64; 608 | if let Ok(imm) = i8::try_from(imm) { 609 | jit.enc_rexw_digit(&[0x83], #op1, #digit, Imm::B(imm)); 610 | } else if let Ok(imm) = i32::try_from(imm) { 611 | jit.enc_rexw_digit(&[0x81], #op1, #digit, Imm::L(imm)); 612 | } else { 613 | panic!("'{} {}, imm64' does not exists.", #op_name, #op1_str); 614 | } 615 | } 616 | } 617 | // OP r/m64, r64 618 | // OP=ADD REX.W 01 /r 619 | // OP=OR REX.W 09 /r 620 | // OP=ADC REX.W 11 /r 621 | // OP=SBB REX.W 19 /r 622 | // OP=AND REX.W 21 /r 623 | // OP=SUB REX.W 29 /r 624 | // OP=XOR REX.W 31 /r 625 | // OP=CMP REX.W 39 /r 626 | (op1, RmiOperand::Reg(expr)) => quote! ( jit.enc_rexw_mr(&[#op_mr], #expr, #op1); ), 627 | // OP r64, m64 628 | // OP=ADD REX.W 03 /r 629 | // OP=OR REX.W 0b /r 630 | // OP=ADC REX.W 13 /r 631 | // OP=SBB REX.W 1b /r 632 | // OP=AND REX.W 23 /r 633 | // OP=SUB REX.W 2b /r 634 | // OP=XOR REX.W 33 /r 635 | // OP=CMP REX.W 3b /r 636 | (RmOperand::Reg(expr), op2) => quote! ( jit.enc_rexw_mr(&[#op_rm], #expr, #op2); ), 637 | (op1, op2) => unimplemented!("{} {}, {}", op_name, op1, op2), 638 | } 639 | } 640 | 641 | fn binary_opl(op_name: &str, op_mr: u8, digit: u8, op1: RmOperand, op2: RmiOperand) -> TokenStream { 642 | let op_rm = op_mr + 2; 643 | match (op1, op2) { 644 | // OP r/m32, imm32 645 | // OP=ADD 81 /0 id 646 | // OP=OR 81 /1 id 647 | // OP=ADC 81 /2 id 648 | // OP=SBB 81 /3 id 649 | // OP=AND 81 /4 id 650 | // OP=SUB 81 /5 id 651 | // OP=XOR 81 /6 id 652 | // OP=CMP 81 /7 id 653 | // 654 | // OP r/m32, imm8 655 | // OP=ADD 83 /0 id 656 | // OP=OR 83 /1 id 657 | // OP=ADC 83 /2 id 658 | // OP=SBB 83 /3 id 659 | // OP=AND 83 /4 id 660 | // OP=SUB 83 /5 id 661 | // OP=XOR 83 /6 id 662 | // OP=CMP 83 /7 id 663 | (op1, RmiOperand::Imm(i)) => { 664 | let op1_str = format!("{}", op1); 665 | quote! { 666 | let imm = (#i) as i64; 667 | if let Ok(imm) = i8::try_from(imm) { 668 | jit.enc_digit_imm(&[0x83], #op1, #digit, Imm::B(imm)); 669 | } else if let Ok(imm) = i32::try_from(imm) { 670 | jit.enc_digit_imm(&[0x81], #op1, #digit, Imm::L(imm)); 671 | } else { 672 | panic!("'{} {}, imm64' does not exists.", #op_name, #op1_str); 673 | } 674 | } 675 | } 676 | // OP r/m32, r32 677 | // OP=ADD 01 /r 678 | // OP=OR 09 /r 679 | // OP=ADC 11 /r 680 | // OP=SBB 19 /r 681 | // OP=AND 21 /r 682 | // OP=SUB 29 /r 683 | // OP=XOR 31 /r 684 | // OP=CMP 39 /r 685 | (op1, RmiOperand::Reg(expr)) => quote! ( jit.enc_mr(&[#op_mr], #expr, #op1); ), 686 | // OP r32, m32 687 | // OP=ADD 03 /r 688 | // OP=OR 0b /r 689 | // OP=ADC 13 /r 690 | // OP=SBB 1b /r 691 | // OP=AND 23 /r 692 | // OP=SUB 2b /r 693 | // OP=XOR 33 /r 694 | // OP=CMP 3b /r 695 | (RmOperand::Reg(expr), op2) => quote! ( jit.enc_mr(&[#op_rm], #expr, #op2); ), 696 | (op1, op2) => unimplemented!("{} {}, {} does not exists.", op_name, op1, op2), 697 | } 698 | } 699 | 700 | fn binary_opw(op_name: &str, op_mr: u8, digit: u8, op1: RmOperand, op2: RmiOperand) -> TokenStream { 701 | let op_rm = op_mr + 2; 702 | match (op1, op2) { 703 | // OP r/m16, imm16 704 | // OP=ADD 81 /0 iw 705 | // OP=OR 81 /1 iw 706 | // OP=ADC 81 /2 iw 707 | // OP=SBB 81 /3 iw 708 | // OP=AND 81 /4 iw 709 | // OP=SUB 81 /5 iw 710 | // OP=XOR 81 /6 iw 711 | // OP=CMP 81 /7 iw 712 | // 713 | // OP r/m16, imm8 714 | // OP=ADD 83 /0 ib 715 | // OP=OR 83 /1 ib 716 | // OP=ADC 83 /2 ib 717 | // OP=SBB 83 /3 ib 718 | // OP=AND 83 /4 ib 719 | // OP=SUB 83 /5 ib 720 | // OP=XOR 83 /6 ib 721 | // OP=CMP 83 /7 ib 722 | (op1, RmiOperand::Imm(i)) => { 723 | let op1_str = format!("{}", op1); 724 | quote! { 725 | let imm = (#i) as i64; 726 | jit.emitb(0x66); 727 | if let Ok(imm) = i8::try_from(imm) { 728 | jit.enc_digit_imm(&[0x83], #op1, #digit, Imm::B(imm)); 729 | } else if let Ok(imm) = i16::try_from(imm) { 730 | jit.enc_digit_imm(&[0x81], #op1, #digit, Imm::W(imm)); 731 | } else { 732 | panic!("'{} {}, imm64' does not exists.", #op_name, #op1_str); 733 | } 734 | } 735 | } 736 | // OP r/m16, r16 737 | // OP=ADD 01 /r 738 | // OP=OR 09 /r 739 | // OP=ADC 11 /r 740 | // OP=SBB 19 /r 741 | // OP=AND 21 /r 742 | // OP=SUB 29 /r 743 | // OP=XOR 31 /r 744 | // OP=CMP 39 /r 745 | (op1, RmiOperand::Reg(expr)) => quote! { 746 | jit.emitb(0x66); 747 | jit.enc_mr(&[#op_mr], #expr, #op1); 748 | }, 749 | // OP r16, m16 750 | // OP=ADD 03 /r 751 | // OP=OR 0b /r 752 | // OP=ADC 13 /r 753 | // OP=SBB 1b /r 754 | // OP=AND 23 /r 755 | // OP=SUB 2b /r 756 | // OP=XOR 33 /r 757 | // OP=CMP 3b /r 758 | (RmOperand::Reg(expr), op2) => quote! { 759 | jit.emitb(0x66); 760 | jit.enc_mr(&[#op_rm], #expr, #op2); 761 | }, 762 | (op1, op2) => unimplemented!("{} {}, {} does not exists.", op_name, op1, op2), 763 | } 764 | } 765 | 766 | fn binary_opb(op_name: &str, op_mr: u8, digit: u8, op1: RmOperand, op2: RmiOperand) -> TokenStream { 767 | let op_rm = op_mr + 2; 768 | match (op1, op2) { 769 | // OP r/m8, imm8 770 | // OP=ADD REX 80 /0 ib 771 | // OP=OR REX 80 /1 ib 772 | // OP=ADC REX 80 /2 ib 773 | // OP=SBB REX 80 /3 ib 774 | // OP=AND REX 80 /4 ib 775 | // OP=SUB REX 80 /5 ib 776 | // OP=XOR REX 80 /6 ib 777 | // OP=CMP REX 80 /7 ib 778 | 779 | // OP al, imm8 780 | // OP=ADD 04 ib 781 | // OP=OR 0c ib 782 | // OP=ADC 14 ib 783 | // OP=SBB 1c ib 784 | // OP=AND 24 ib 785 | // OP=SUB 2c ib 786 | // OP=XOR 34 ib 787 | // OP=CMP 3c ib 788 | (op1, RmiOperand::Imm(i)) => { 789 | let op_al = op_mr + 4; 790 | quote! { 791 | let imm = (#i) as i64; 792 | if let Ok(imm) = i8::try_from(imm) { 793 | jit.enc_digit_imm_byte(&[0x80], #op_al, #op1, #digit, Imm::B(imm)); 794 | } else { 795 | panic!("{} is out of 8bit.", #i); 796 | } 797 | } 798 | } 799 | // cmp r/m8, r8 800 | // OP=ADD REX 00 /r 801 | // OP=OR REX 08 /r 802 | // OP=ADC REX 10 /r 803 | // OP=SBB REX 18 /r 804 | // OP=AND REX 20 /r 805 | // OP=SUB REX 28 /r 806 | // OP=XOR REX 30 /r 807 | // OP=CMP REX 38 /r 808 | (op1, RmiOperand::Reg(expr)) => quote! ( jit.enc_mr_byte(&[#op_mr], #expr, #op1); ), 809 | // OP r8, m8 810 | // OP=ADD REX 02 /r 811 | // OP=OR REX 0a /r 812 | // OP=ADC REX 12 /r 813 | // OP=SBB REX 1a /r 814 | // OP=AND REX 22 /r 815 | // OP=SUB REX 2a /r 816 | // OP=XOR REX 32 /r 817 | // OP=CMP REX 3a /r 818 | (RmOperand::Reg(expr), op2) => quote! ( jit.enc_mr_byte(&[#op_rm], #expr, #op2); ), 819 | (op1, op2) => unimplemented!("{} {}, {} does not exists.", op_name, op1, op2), 820 | } 821 | } 822 | 823 | fn divide_op(width: OperandSize, signed: bool, op: RmOperand) -> TokenStream { 824 | // IDIV r/m64: RAX:quo RDX:rem <- RDX:RAX / r/m64 825 | // REX.W F7 /7 826 | // IDIV r/m32: EAX:quo EDX:rem <- EDX:EAX / r/m32 827 | // REX F7 /7 828 | // DIV r/m64: RAX:quo RDX:rem <- RDX:RAX / r/m64 829 | // REX.W F7 /6 830 | // DIV r/m32: EAX:quo EDX:rem <- EDX:EAX / r/m32 831 | // REX F7 /6 832 | let signed = if signed { 7u8 } else { 6u8 }; 833 | match width { 834 | OperandSize::QWORD => { 835 | quote! { jit.enc_rexw_digit(&[0xf7], #op, #signed, Imm::None); } 836 | } 837 | OperandSize::DWORD => { 838 | quote! { jit.enc_digit(&[0xf7], #op, #signed); } 839 | } 840 | _ => unimplemented!("DIV/IDIV for {:?} is not yet supported.", width), 841 | } 842 | } 843 | 844 | /// 845 | /// Shift and rotate operation. 846 | /// 847 | /// #### Addressiong modes 848 | /// 849 | /// ##### XXX r/m64, imm8 850 | /// - REX.W C1 /Y ib 851 | /// 852 | /// ##### XXX r/m64, 1 853 | /// - REX.W D1 /Y 854 | /// 855 | /// ##### XXX r/m64, Cl 856 | /// - REX.W D3 /Y 857 | /// 858 | /// #### Instructions 859 | /// - XXX=Rol Y=0 860 | /// - XXX=Ror Y=1 861 | /// - XXX=Shl Y=4 862 | /// - XXX=Sal Y=4 863 | /// - XXX=Shr Y=5 864 | /// - XXX=Sar Y=7 865 | /// 866 | fn shift_op( 867 | width: OperandSize, 868 | digit: u8, 869 | op1: RmOperand, 870 | op2: RiOperand, 871 | inst_str: &str, 872 | ) -> TokenStream { 873 | match width { 874 | OperandSize::QWORD => match (op1, op2) { 875 | (op1, RiOperand::Imm(i)) => { 876 | let op1_str = format!("{}", op1); 877 | // Shl r/m64, imm8 878 | // REX.W C1 /4 ib 879 | quote! { 880 | let imm = (#i) as i64; 881 | if imm == 1 { 882 | jit.enc_rexw_digit(&[0xd1], #op1, #digit, Imm::None); 883 | } else if let Ok(imm) = i8::try_from(imm) { 884 | jit.enc_rexw_digit(&[0xc1], #op1, #digit, Imm::B(imm)); 885 | } else { 886 | panic!("'{} {}, imm' imm should be 8 bit.", #inst_str, #op1_str); 887 | } 888 | } 889 | } 890 | // Shl r/m64, Cl 891 | // REX.W D3 /4 892 | (op1, RiOperand::Reg(reg)) => { 893 | let op1_str = format!("{}", op1); 894 | quote! { 895 | if !#reg.is_cl() { 896 | panic!("'{} {}, reg' reg should be CL.", #inst_str, #op1_str); 897 | }; 898 | jit.enc_rexw_digit(&[0xd3], #op1, #digit, Imm::None); 899 | } 900 | } 901 | }, 902 | OperandSize::DWORD => match (op1, op2) { 903 | (op1, RiOperand::Imm(i)) => { 904 | let op1_str = format!("{}", op1); 905 | // Shl r/m32, imm8 906 | // REX C1 /4 ib 907 | quote! { 908 | let imm = (#i) as i64; 909 | if imm == 1 { 910 | jit.enc_digit(&[0xd1], #op1, #digit); 911 | } else if let Ok(imm) = i8::try_from(imm) { 912 | jit.enc_digit_imm(&[0xc1], #op1, #digit, Imm::B(imm)); 913 | } else { 914 | panic!("'{} {}, imm' imm should be 8 bit.", #inst_str, #op1_str); 915 | } 916 | } 917 | } 918 | // Shl r/m32, Cl 919 | // REX D3 /4 920 | (op1, RiOperand::Reg(reg)) => { 921 | let op1_str = format!("{}", op1); 922 | quote! { 923 | if !#reg.is_cl() { 924 | panic!("'{} {}, reg' reg should be CL.", #inst_str, #op1_str); 925 | }; 926 | jit.enc_digit(&[0xd3], #op1, #digit); 927 | } 928 | } 929 | }, 930 | OperandSize::WORD | OperandSize::BYTE => todo!("not implemented yet."), 931 | } 932 | } 933 | 934 | fn binary_sd_op(operand: u8, op1: Xmm, op2: XmOperand) -> TokenStream { 935 | let op1 = op1.0; 936 | quote! { 937 | jit.emitb(0xf2); 938 | jit.enc_mr(&[0x0f, #operand], Reg::from(#op1), #op2); 939 | } 940 | } 941 | 942 | fn push_pop(opcode_r: u8, opcode_m: (u8, u8), op: RmOperand) -> TokenStream { 943 | match op { 944 | // PUSH r64 POP r64 945 | // 50 +rd 58 +rd 946 | // O O 947 | RmOperand::Reg(reg) => quote! ( jit.enc_o(#opcode_r, #reg); ), 948 | // PUSH r/m64 POP r/m64 949 | // ff /6 8f /0 950 | // M M 951 | op => { 952 | let (opcode, digit) = opcode_m; 953 | quote! ( jit.enc_digit(&[#opcode], #op, #digit); ) 954 | } 955 | } 956 | } 957 | 958 | fn count_op(width: OperandSize, opcode: u8, op1: Register, op2: RmOperand) -> TokenStream { 959 | match width { 960 | OperandSize::QWORD => { 961 | quote!( 962 | jit.emitb(0xf3); 963 | jit.enc_rexw_mr(&[0x0f, #opcode], #op1, #op2); 964 | ) 965 | } 966 | OperandSize::DWORD => { 967 | quote!( 968 | jit.emitb(0xf3); 969 | jit.enc_mr(&[0x0f, #opcode], #op1, #op2); 970 | ) 971 | } 972 | _ => unimplemented!("Count operation for {:?} is not yet supported.", width), 973 | } 974 | } 975 | --------------------------------------------------------------------------------