close Warning: Can't synchronize with repository "(default)" (/project/movitz/svn does not appear to be a Subversion repository.). Look in the Trac log for more information.

Inline Assembly

GC safety

From the better-than-nothing department, here's a log from an IRC session:

<sabetts> ya i tried to write a screen blitter in asm
<sabetts> but..i couldn't really get it doing anything useful
<sabetts> i'm actually sorta curious how one would do it..
<frodef> ok..?
<sabetts> here's what I had:
<sabetts> (defun blit-double-buffer-faster (&optional (dbuffer *g-double-buffer*))
<sabetts>   (let ((array (dbuffer-32-bit dbuffer)))
<sabetts>     (with-inline-assembly (:returns :nothing)
<sabetts>       (:compile-form (:result-mode :eax) array)
<sabetts>       (:movl #xA0000 :ebx)
<sabetts>       (:movl 16000 :ecx)
<sabetts>       loop
<sabetts>       (:movl (:eax) :edx)
<sabetts>       (:movl :edx (:ebx))
<sabetts>       (:addl 4 :eax)
<sabetts>       (:addl 4 :ebx)
<sabetts>       (:decl :ecx)
<sabetts>       (:jnz 'loop))
<sabetts>     (values)))
<sabetts> where dbuffer-32-bit is: (make-array (* width height) :element-type '(unsigned-byte
          32) :initial-element 0)
<frodef> firstly, the data in an array starts at an offset..
<sabetts> right, i didn't know how to get that
<frodef>'s 2, but should be available in asm as (:offset movitz-basic-vector data)
<frodef> this is defined in storage-types.lisp
<frodef> anyways, you want to do this GC-safe, which is more tricky still.
<sabetts> ya i guess i dont want it moving the array on me :)
<sabetts> is there a without-gc macro or something?
<frodef> right :)
<frodef> there is, but it's rather expensive.
<frodef> the better option would be to simply make the code gc-safe.
<sabetts> oh okay
<frodef> i.e keep the array in a register (like eax) all the time
<frodef> and use another register (like ecx) as an index
<sabetts> right okay
<sabetts> nice'n'easy
<frodef> ..then refer to elements like (:eax :ecx (:offset ...))
<frodef> so now, if gc moves the vector, it doesn't matter because eax will get updated.
<sabetts> wow, how does that work?
<sabetts> gc black magic :)
<frodef> not really.. :)
<sabetts> good :)
<frodef> gc can only happen here if the loop is interrupted, since there is no consing.
<sabetts> which could happen with my scheduler patch
<frodef> at an interrupt, all registers etc. are saved on stack.
<sabetts> right
<frodef> ok, so when the gc happens, the registers on the stack are treated like any other
         pointers, so they get updated too.
<sabetts> wow, neat!
<sabetts> so the gc parses the stack, which totally makes sense :)
<frodef> right.. so this happens for all registers except ECX
<sabetts> nutty, why?
<frodef> which means that you can never use ecx as a pointer
<sabetts> thats good to know
<frodef> but the upside is that you can use it for anything..
<frodef> ..all the other registers must be "safe" lisp-values, otherwise gc might interpret
         them as (stray) pointers
<sabetts> does the gc know registers pushed onto the stack are pointers and not just
<sabetts> ahhh
<frodef> so that's another reason why you can't use eax like you did first.
<sabetts> right, okay
<sabetts> but this is okay?       (:movl #xA0000 :ebx)
<sabetts> or would i wanna do a similar offset thing with cx?
<frodef> so long as the two lower bits are zero, it will never be interpreted as a pointer..
<sabetts> ah okay
<frodef> ..because that's the fixnum type.
<sabetts> i guess the other thing is i need to decode the numbers in the array?
<frodef> if the element-type is (unsigned-byte 32), which you should probably check-type
         first, then it's simply 32-bit integers there.
<sabetts> ah okay, nice
<sabetts> thanks for that :)

The resulting function was:

(defun blit-double-buffer-faster (&optional (dbuffer *g-double-buffer*))
  (let ((array (dbuffer-pixels dbuffer)))
    (with-inline-assembly (:returns :nothing)
      (:compile-form (:result-mode :eax) array)
      (:movl #xA0000 :ebx)
      ;; Ordinarily ecx is the counting register but it's the only
      ;; safe one so use it for the pixel value and use :edx to count
      ;; since the number counts by 4s, the bottom 2 bits will be 0,
      ;; which makes it a valid fixnum.
      (:movl 0 :edx)
      (:movl (:eax :edx (:offset movitz-basic-vector data)) :ecx)
      (#.movitz:*compiler-physical-segment-prefix* :movl :ecx (:ebx :edx))
      (:addl 4 :edx)
      (:cmpl 64000 :edx)
      (:jnz 'loop))
Last modified 12 years ago Last modified on 04/05/07 20:09:25