Skip to content

Commit

Permalink
stdlib: implement Int64#reverseBits and Int32#reverseBits
Browse files Browse the repository at this point in the history
fixes #83
  • Loading branch information
soc committed Sep 6, 2023
1 parent 61288a8 commit 1bcfa54
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 4 deletions.
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions dora-asm/src/x64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
20 changes: 20 additions & 0 deletions dora/src/cannon/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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];
Expand Down
4 changes: 4 additions & 0 deletions dora/src/compiler/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
6 changes: 6 additions & 0 deletions dora/src/language/sem_analysis/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ pub enum Intrinsic {
Int32RotateLeft,
Int32RotateRight,

Int32ReverseBits,
Int32ReverseBytes,

Int32Not,
Expand Down Expand Up @@ -385,6 +386,7 @@ pub enum Intrinsic {
Int64RotateLeft,
Int64RotateRight,

Int64ReverseBits,
Int64ReverseBytes,

Int64Not,
Expand Down Expand Up @@ -550,7 +552,9 @@ impl Intrinsic {
| Intrinsic::Int32RotateRight
| Intrinsic::Int64RotateLeft
| Intrinsic::Int64RotateRight
| Intrinsic::Int32ReverseBits
| Intrinsic::Int32ReverseBytes
| Intrinsic::Int64ReverseBits
| Intrinsic::Int64ReverseBytes
| Intrinsic::ThreadCurrent => true,
_ => false,
Expand All @@ -575,6 +579,7 @@ impl Intrinsic {
| Intrinsic::Int32Sar
| Intrinsic::Int32RotateLeft
| Intrinsic::Int32RotateRight
| Intrinsic::Int32ReverseBits
| Intrinsic::Int32ReverseBytes
| Intrinsic::Int32Not
| Intrinsic::Int32Plus
Expand Down Expand Up @@ -623,6 +628,7 @@ impl Intrinsic {
| Intrinsic::Int64Sar
| Intrinsic::Int64RotateLeft
| Intrinsic::Int64RotateRight
| Intrinsic::Int64ReverseBits
| Intrinsic::Int64ReverseBytes
| Intrinsic::Int64Not
| Intrinsic::Int64Plus
Expand Down
14 changes: 14 additions & 0 deletions dora/src/language/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 8 additions & 0 deletions dora/src/masm/arm64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
Expand Down
89 changes: 89 additions & 0 deletions dora/src/masm/x64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
2 changes: 2 additions & 0 deletions dora/stdlib/primitives.dora
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions tests/int/int32-reverseBits.dora
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fun main(): Unit {
assert(0b00000001001000110100010101100111i32.reverseBits() == 0b11100110101000101100010010000000i32);
assert(0b11100110101000101100010010000000i32.reverseBits() == 0b00000001001000110100010101100111i32);
}
4 changes: 4 additions & 0 deletions tests/int/int64-reverseBits.dora
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fun main(): Unit {
assert(0b0000000100100011010001010110011110001001101010111100110111101111.reverseBits() == 0b1111011110110011110101011001000111100110101000101100010010000000);
assert(0b1111011110110011110101011001000111100110101000101100010010000000.reverseBits() == 0b0000000100100011010001010110011110001001101010111100110111101111);
}

0 comments on commit 1bcfa54

Please sign in to comment.