From 1bcfa544962e09fc8ae08d089613189dfe211345 Mon Sep 17 00:00:00 2001 From: Simon Ochsenreither Date: Wed, 6 Sep 2023 02:50:06 +0200 Subject: [PATCH] stdlib: implement `Int64#reverseBits` and `Int32#reverseBits` fixes #83 --- README.md | 4 - dora-asm/src/x64.rs | 4 + dora/src/cannon/codegen.rs | 20 +++++ dora/src/compiler/asm.rs | 4 + dora/src/language/sem_analysis/functions.rs | 6 ++ dora/src/language/stdlib.rs | 14 ++++ dora/src/masm/arm64.rs | 8 ++ dora/src/masm/x64.rs | 89 +++++++++++++++++++++ dora/stdlib/primitives.dora | 2 + tests/int/int32-reverseBits.dora | 4 + tests/int/int64-reverseBits.dora | 4 + 11 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 tests/int/int32-reverseBits.dora create mode 100644 tests/int/int64-reverseBits.dora diff --git a/README.md b/README.md index f03a482a..50cb6dca 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,6 @@ Works on Linux, Windows and macOS (x86\_64 and aarch64). The source repository of the Core Programming Language [website](https://core-lang.dev) can be found [here](https://github.com/core-lang/core-website). -## Setup -Install Rust nightly through [rustup.rs](http://rustup.rs). Use the specific nightly version listed in the [rust-toolchain](https://github.com/dinfuehr/dora/blob/master/rust-toolchain) file. Core simply uses `cargo` for building: - - ## Compilation ``` # build in debug and release mode diff --git a/dora-asm/src/x64.rs b/dora-asm/src/x64.rs index bb08988a..1bff732d 100644 --- a/dora-asm/src/x64.rs +++ b/dora-asm/src/x64.rs @@ -496,6 +496,10 @@ impl AssemblerX64 { self.emit_modrm_registers(src, dest); } + pub fn andl_ri(&mut self, dest: Register, imm: Immediate) { + self.emit_alu32_imm(dest, imm, 0b100, 0x25); + } + pub fn andq_rr(&mut self, dest: Register, src: Register) { self.emit_rex64_modrm(src, dest); self.emit_u8(0x21); diff --git a/dora/src/cannon/codegen.rs b/dora/src/cannon/codegen.rs index 8f4ddcbd..df9cc238 100644 --- a/dora/src/cannon/codegen.rs +++ b/dora/src/cannon/codegen.rs @@ -1029,6 +1029,21 @@ impl<'a> CannonCodeGen<'a> { self.emit_store_register(REG_RESULT.into(), dest); } + fn emit_reverse_bits(&mut self, dest: Register, src: Register) { + assert_eq!( + self.bytecode.register_type(src), + self.bytecode.register_type(dest) + ); + + self.emit_load_register(src, REG_RESULT.into()); + + let bytecode_type = self.bytecode.register_type(dest); + self.asm + .int_reverse_bits(mode(self.vm, bytecode_type), REG_RESULT, REG_RESULT); + + self.emit_store_register(REG_RESULT.into(), dest); + } + fn emit_reverse_bytes(&mut self, dest: Register, src: Register) { assert_eq!( self.bytecode.register_type(src), @@ -3898,6 +3913,11 @@ impl<'a> CannonCodeGen<'a> { self.emit_ror_int(dest, lhs_reg, rhs_reg); } + Intrinsic::Int32ReverseBits | Intrinsic::Int64ReverseBits => { + assert_eq!(arguments.len(), 1); + let src_reg = arguments[0]; + self.emit_reverse_bits(dest, src_reg); + } Intrinsic::Int32ReverseBytes | Intrinsic::Int64ReverseBytes => { assert_eq!(arguments.len(), 1); let src_reg = arguments[0]; diff --git a/dora/src/compiler/asm.rs b/dora/src/compiler/asm.rs index 7326fe9c..60a1f3da 100644 --- a/dora/src/compiler/asm.rs +++ b/dora/src/compiler/asm.rs @@ -373,6 +373,10 @@ impl<'a> BaselineAssembler<'a> { self.masm.int_ror(mode, dest, lhs, rhs); } + pub fn int_reverse_bits(&mut self, mode: MachineMode, dest: Reg, src: Reg) { + self.masm.int_reverse_bits(mode, dest, src); + } + pub fn int_reverse_bytes(&mut self, mode: MachineMode, dest: Reg, src: Reg) { self.masm.int_reverse_bytes(mode, dest, src); } diff --git a/dora/src/language/sem_analysis/functions.rs b/dora/src/language/sem_analysis/functions.rs index 50488b0e..f1120ea4 100644 --- a/dora/src/language/sem_analysis/functions.rs +++ b/dora/src/language/sem_analysis/functions.rs @@ -342,6 +342,7 @@ pub enum Intrinsic { Int32RotateLeft, Int32RotateRight, + Int32ReverseBits, Int32ReverseBytes, Int32Not, @@ -385,6 +386,7 @@ pub enum Intrinsic { Int64RotateLeft, Int64RotateRight, + Int64ReverseBits, Int64ReverseBytes, Int64Not, @@ -550,7 +552,9 @@ impl Intrinsic { | Intrinsic::Int32RotateRight | Intrinsic::Int64RotateLeft | Intrinsic::Int64RotateRight + | Intrinsic::Int32ReverseBits | Intrinsic::Int32ReverseBytes + | Intrinsic::Int64ReverseBits | Intrinsic::Int64ReverseBytes | Intrinsic::ThreadCurrent => true, _ => false, @@ -575,6 +579,7 @@ impl Intrinsic { | Intrinsic::Int32Sar | Intrinsic::Int32RotateLeft | Intrinsic::Int32RotateRight + | Intrinsic::Int32ReverseBits | Intrinsic::Int32ReverseBytes | Intrinsic::Int32Not | Intrinsic::Int32Plus @@ -623,6 +628,7 @@ impl Intrinsic { | Intrinsic::Int64Sar | Intrinsic::Int64RotateLeft | Intrinsic::Int64RotateRight + | Intrinsic::Int64ReverseBits | Intrinsic::Int64ReverseBytes | Intrinsic::Int64Not | Intrinsic::Int64Plus diff --git a/dora/src/language/stdlib.rs b/dora/src/language/stdlib.rs index 9b75bb90..d47828fb 100644 --- a/dora/src/language/stdlib.rs +++ b/dora/src/language/stdlib.rs @@ -670,6 +670,13 @@ pub fn resolve_internal_functions(sa: &mut SemAnalysis) { Intrinsic::Int32RotateRight, ); + intrinsic_method( + sa, + stdlib_id, + "primitives::Int32", + "reverseBits", + Intrinsic::Int32ReverseBits, + ); intrinsic_method( sa, stdlib_id, @@ -928,6 +935,13 @@ pub fn resolve_internal_functions(sa: &mut SemAnalysis) { Intrinsic::Int64RotateRight, ); + intrinsic_method( + sa, + stdlib_id, + "primitives::Int64", + "reverseBits", + Intrinsic::Int64ReverseBits, + ); intrinsic_method( sa, stdlib_id, diff --git a/dora/src/masm/arm64.rs b/dora/src/masm/arm64.rs index 191159a2..fad74e6b 100644 --- a/dora/src/masm/arm64.rs +++ b/dora/src/masm/arm64.rs @@ -541,6 +541,14 @@ impl MacroAssembler { } } + pub fn int_reverse_bits(&mut self, mode: MachineMode, dest: Reg, src: Reg) { + match mode { + MachineMode::Int32 => self.asm.rbit_w(dest.into(), src.into()), + MachineMode::Int64 => self.asm.rbit(dest.into(), src.into()), + _ => panic!("unimplemented mode {:?}", mode), + } + } + pub fn int_reverse_bytes(&mut self, mode: MachineMode, dest: Reg, src: Reg) { match mode { MachineMode::Int32 => self.asm.rev_w(dest.into(), src.into()), diff --git a/dora/src/masm/x64.rs b/dora/src/masm/x64.rs index 6d980c02..b92111fb 100644 --- a/dora/src/masm/x64.rs +++ b/dora/src/masm/x64.rs @@ -740,6 +740,95 @@ impl MacroAssembler { } } + pub fn int_reverse_bits(&mut self, mode: MachineMode, dest: Reg, src: Reg) { + match mode { + MachineMode::Int32 => { + let scratch1 = self.get_scratch(); + self.asm.movl_rr((*scratch1).into(), src.into()); + self.asm.andl_ri((*scratch1).into(), Immediate(0x55555555)); + self.asm.shll_ri((*scratch1).into(), Immediate(1)); + + let scratch2 = self.get_scratch(); + self.asm.movl_rr((*scratch2).into(), src.into()); + self.asm + .andl_ri((*scratch2).into(), Immediate(0xAAAAAAAAu32 as i32 as i64)); + self.asm.shrl_ri((*scratch2).into(), Immediate(1)); + + self.asm.orl_rr((*scratch1).into(), (*scratch2).into()); + + let scratch3 = self.get_scratch(); + self.asm.movl_ri((*scratch3).into(), Immediate(0x33333333)); + self.asm.andl_rr((*scratch3).into(), (*scratch1).into()); + self.asm.shll_ri((*scratch3).into(), Immediate(2)); + + let scratch4 = self.get_scratch(); + self.asm + .movl_ri((*scratch4).into(), Immediate(0xCCCCCCCCu32 as i32 as i64)); + self.asm.andl_rr((*scratch4).into(), (*scratch1).into()); + self.asm.shrl_ri((*scratch4).into(), Immediate(2)); + + self.asm.orl_rr((*scratch3).into(), (*scratch4).into()); + + // re-use scratch register + self.asm.movl_ri((*scratch1).into(), Immediate(0x0F0F0F0F)); + self.asm.andl_rr((*scratch1).into(), (*scratch3).into()); + self.asm.shll_ri((*scratch1).into(), Immediate(4)); + + self.asm + .movl_ri(dest.into(), Immediate(0xF0F0F0F0u32 as i32 as i64)); + self.asm.andl_rr(dest.into(), (*scratch3).into()); + self.asm.shrl_ri(dest.into(), Immediate(4)); + + self.asm.orl_rr(dest.into(), (*scratch1).into()); + self.asm.bswapl_r(dest.into()) + } + MachineMode::Int64 => { + let scratch1 = self.get_scratch(); + self.asm + .movq_ri((*scratch1).into(), Immediate(0x5555555555555555)); + self.asm.andq_rr((*scratch1).into(), src.into()); + self.asm.shlq_ri((*scratch1).into(), Immediate(1)); + + let scratch2 = self.get_scratch(); + self.asm + .movq_ri((*scratch2).into(), Immediate(0xAAAAAAAAAAAAAAAAu64 as i64)); + self.asm.andq_rr((*scratch2).into(), src.into()); + self.asm.shrq_ri((*scratch2).into(), Immediate(1)); + + self.asm.orq_rr((*scratch1).into(), (*scratch2).into()); + + let scratch3 = self.get_scratch(); + self.asm + .movq_ri((*scratch3).into(), Immediate(0x3333333333333333)); + self.asm.andq_rr((*scratch3).into(), (*scratch1).into()); + self.asm.shlq_ri((*scratch3).into(), Immediate(2)); + + let scratch4 = self.get_scratch(); + self.asm + .movq_ri((*scratch4).into(), Immediate(0xCCCCCCCCCCCCCCCCu64 as i64)); + self.asm.andq_rr((*scratch4).into(), (*scratch1).into()); + self.asm.shrq_ri((*scratch4).into(), Immediate(2)); + + self.asm.orq_rr((*scratch3).into(), (*scratch4).into()); + + // re-use scratch register + self.asm + .movq_ri((*scratch1).into(), Immediate(0x0F0F0F0F0F0F0F0F)); + self.asm.andq_rr((*scratch1).into(), (*scratch3).into()); + self.asm.shlq_ri((*scratch1).into(), Immediate(4)); + + self.asm + .movq_ri(dest.into(), Immediate(0xF0F0F0F0F0F0F0F0u64 as i64)); + self.asm.andq_rr(dest.into(), (*scratch3).into()); + self.asm.shrq_ri(dest.into(), Immediate(4)); + + self.asm.orq_rr(dest.into(), (*scratch1).into()); + self.asm.bswapq_r(dest.into()) + } + _ => panic!("unimplemented mode {:?}", mode), + } + } + pub fn int_reverse_bytes(&mut self, mode: MachineMode, dest: Reg, src: Reg) { if mode.is64() { self.asm.bswapq_r(src.into()); diff --git a/dora/stdlib/primitives.dora b/dora/stdlib/primitives.dora index 623238a8..f20bcf8b 100644 --- a/dora/stdlib/primitives.dora +++ b/dora/stdlib/primitives.dora @@ -67,6 +67,7 @@ impl Int32 { @pub @internal fun rotateLeft(by: Int32): Int32; @pub @internal fun rotateRight(by: Int32): Int32; + @pub @internal fun reverseBits(): Int32; @pub @internal fun reverseBytes(): Int32; @pub @internal fun unaryPlus(): Int32; @@ -245,6 +246,7 @@ impl Int64 { @pub @internal fun rotateLeft(by: Int32): Int64; @pub @internal fun rotateRight(by: Int32): Int64; + @pub @internal fun reverseBits(): Int64; @pub @internal fun reverseBytes(): Int64; @pub @internal fun unaryPlus(): Int64; diff --git a/tests/int/int32-reverseBits.dora b/tests/int/int32-reverseBits.dora new file mode 100644 index 00000000..d3162a70 --- /dev/null +++ b/tests/int/int32-reverseBits.dora @@ -0,0 +1,4 @@ +fun main(): Unit { + assert(0b00000001001000110100010101100111i32.reverseBits() == 0b11100110101000101100010010000000i32); + assert(0b11100110101000101100010010000000i32.reverseBits() == 0b00000001001000110100010101100111i32); +} diff --git a/tests/int/int64-reverseBits.dora b/tests/int/int64-reverseBits.dora new file mode 100644 index 00000000..b49ad3be --- /dev/null +++ b/tests/int/int64-reverseBits.dora @@ -0,0 +1,4 @@ +fun main(): Unit { + assert(0b0000000100100011010001010110011110001001101010111100110111101111.reverseBits() == 0b1111011110110011110101011001000111100110101000101100010010000000); + assert(0b1111011110110011110101011001000111100110101000101100010010000000.reverseBits() == 0b0000000100100011010001010110011110001001101010111100110111101111); +}