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> ..it'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> so..how does the gc know registers pushed onto the stack are pointers and not just numbers? <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) loop (: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)) (values)))
Last modified 18 years ago
Last modified on 04/05/07 20:09:25