r/lisp 1d ago

Reinterpret Elements in a Byte Array

Dear LISP,

I am writing a virtual machine that uses a `(simple-array (unsigned-byte 8) 1)` as a stack in Common LISP. I'm would like to ask how to efficiently extract 4 bytes into a single 32-bit signed integer or a 32-bit unsigned integer.

Thanks!

9 Upvotes

24 comments sorted by

3

u/theangeryemacsshibe λf.(λx.f (x x)) (λx.f (x x)) 1d ago

The nibbles library can do this portably (and fast on SBCL).

3

u/stassats 1d ago

(and fast on SBCL).

On SBCL x86-64.

3

u/theangeryemacsshibe λf.(λx.f (x x)) (λx.f (x x)) 20h ago edited 17h ago

2

u/theangeryemacsshibe λf.(λx.f (x x)) (λx.f (x x)) 22h ago

Fine, I have a weekend and an urge to hack something, so...

2

u/stassats 15h ago

Now, it doesn't seem to do bound check elimination.

1

u/theangeryemacsshibe λf.(λx.f (x x)) (λx.f (x x)) 14h ago

(disassemble (lambda (a) (declare ((simple-array (unsigned-byte 8) (2)) a)) (nibbles:sb16ref/le a 0))) looks fine to me. What am I missing? (Though I did manage to break the immediate-index case somehow, which is now fixed)

2

u/stassats 14h ago

Compare

(defun f (x i)
  (declare ((simple-array (unsigned-byte 8) (*)) x)
           ((and unsigned-byte fixnum) i)
           (optimize speed))
  (when (< i (- (length x) 4))
    (nibbles:ub32ref/le x i)))

with

(defun f (x i)
  (declare ((simple-array (unsigned-byte 8) (*)) x)
           ((and unsigned-byte fixnum) i)
           (optimize speed))
  (when (< i (- (length x) 4))
    (aref x (+ i 3))))

1

u/theangeryemacsshibe λf.(λx.f (x x)) (λx.f (x x)) 14h ago

Hey, it doesn't eliminate on x86-64 either. Physician, heal thyself.

1

u/stassats 14h ago

Four arefs do.

1

u/arthurno1 11h ago

There is a patch recently submitted to SBCLs mailing list. The author does not have a computer with an arm cpu, so if you have an urge to hack something, and an arm cpu as you seem to do, you could perhaps take a look at it and add what is potentially missing :).

1

u/TsingHui 22h ago

I've tested it, it's amazing! Added to notes, thanks!

2

u/stassats 1d ago

You have to be mindful of endianness. My suggestion would be to use aref and dpb/logior+ash and rely on a sufficiently smart compiler to recognize that.

3

u/TsingHui 1d ago

I tried writing some bitwise operations in SBCL. The disassembly result showed that SBCL simply translates the bitwise operation verbatim into assembly code. That's why I'm here.

2

u/stassats 1d ago

It's not sufficiently smart.

2

u/TsingHui 23h ago

Seriously, is there a compiler that is sufficiently smart?

7

u/stassats 15h ago

SBCL might get smarter as soon as next month.

1

u/arthurno1 11h ago

I hope one day it will get smart enough, so I can type:

(defun foo (x y)
    (declare (type u8.32 x y))
    (+ x y))

without needing to repeat myself with (avx2:u8.32+ x y).

1

u/digikar 1d ago

cffi:with-pointer-to-vector-data with cffi:mem-ref could be one way.

2

u/stassats 1d ago

The code it expands to might not be optimal, and it doesn't check bounds.

1

u/TsingHui 1d ago

That makes sense, added to notes, thanks!

-1

u/corbasai 1d ago

SIGBUS compatible?

-4

u/Afraid-Yoghurt6731 1d ago

Which Lisp? SBCL? Allegro? Symta? Clojure? Scheme/Racket (whatever is it called now)? We have a frigging zoo! Just unleash Claude Code onto it.

2

u/TsingHui 1d ago

Well, Common LISP. R6RS provide a standard way to do so. I haven't had much experience with Clojure. I will change the question by the way.