Greetings, and thanks! BTW, the Debian axiom package automatically polls
http://axiom.axiom-developer.org/axiom-website/download.html for new releases (in case I miss an email list post), as http://axiom.axiom-developer.org/axiom-website/downloads/ is not searchable. So if you remember to update the former when making a release, I'll be sure to see it and package updates can proceed more smoothly. u1204 <[email protected]> writes: > Yes, the src tar is at: > http://axiom-developer.org/axiom-website/downloads/axiom-may2012-src.tgz > > I'd like to know the details for the blas/lapack/atlas FFI > and also the details of FFI. > You're probably already familiar with the legacy FFI using defentry, as I think axiom uses this already. This is a bit slow, but functional. I've designed a new one with the goal of accessing shared libraries from within GCL given its unique (it appears) ability to unexec a running image, and reexec later, possibly on a different machine. A shared library link therefore needs to be remembered in the image, together with all code pointer locations which must be reset on image startup. The following is implemented in the 2.7.0 tree, but can be backported rather easily if you need it. There is a :lib package: (do-external-symbols (s :lib) (print s)) LIB:|libm| LIB:|libc| each external symbol of which tracks a live shared library binding, and which also names another package with the dynamically linked symbols in use: COMPILER>(do-external-symbols (s 'lib:|libm|) (print s)) |libm|:|atan| |libm|:|ctan| |libm|:|csqrt| |libm|:|clogf| |libm|:|acosh| |libm|:|ccosh| |libm|:|expf| |libm|:|atanhf| |libm|:|casin| |libm|:|cexpf| |libm|:|acosf| |libm|:|sqrtf| |libm|:|exp| |libm|:|atanh| |libm|:|ccosf| |libm|:|ctanh| |libm|:|cosh| |libm|:|ccoshf| |libm|:|cosf| |libm|:|atanf| |libm|:|cos| |libm|:|cacos| |libm|:|tanh| |libm|:|ctanf| |libm|:|csinhf| |libm|:|tanf| |libm|:|tan| |libm|:|asin| |libm|:|sinh| |libm|:|csin| |libm|:|sinf| |libm|:|cabs| |libm|:|sin| |libm|:|catanhf| |libm|:|coshf| |libm|:|catanh| |libm|:|fabs| |libm|:|catanf| |libm|:|tanhf| |libm|:|acoshf| |libm|:|asinh| |libm|:|csinh| |libm|:|asinhf| |libm|:|atan2f| |libm|:|asinf| |libm|:|sinhf| |libm|:|atan2| |libm|:|csinf| |libm|:|cabsf| |libm|:|fabsf| |libm|:|casinhf| |libm|:|logf| |libm|:|casinh| |libm|:|clog| |libm|:|casinf| |libm|:|ctanhf| |libm|:|csqrtf| |libm|:|log| |libm|:|cacoshf| |libm|:|cacosh| |libm|:|catan| |libm|:|cacosf| |libm|:|cexp| |libm|:|acos| |libm|:|sqrt| |libm|:|ccos| Each of these symbols name genuine lisp functions which wrap calls to the external routine in question: (function-lambda-expression #'|libm|:|atan| ) (LAMBDA (#:G550) (DECLARE (OPTIMIZE (SAFETY 2))) (CHECK-TYPE #:G550 (LONG-FLOAT * *)) (BLOCK |libm|:|atan| (CADD-DLADDRESS "dlatan" |libm|:|atan|) (LIT :DOUBLE "(" "(double(*)(double))" "(" "dlatan" "))(" (:DOUBLE #:G550) "" ")"))) NIL |libm|:|atan| These functions check argument types, but when inlined at (default) safety 0, simply compile to calls to the routine through a C pointer. COMPILER>(disassemble '|libm|:|atan| nil) ;; Compiling /tmp/gazonk_21018_0.lsp. ;; End of Pass 1. ;; End of Pass 2. ;; OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3, (Debug quality ignored) ;; Finished compiling /tmp/gazonk_21018_0.o. #include "gazonk_21018_0.h" void init_code(){do_init((void *)VV);} /* local entry for function libm::atan */ static object LI1__atan___gazonk_21018_0(object V2) { VMB1 VMS1 VMV1 if(!(type_of((V2))==t_longfloat)){ goto T4;} goto T3; goto T4; T4:; V2= (VFUN_NARGS=4,/* SYSTEM::CHECK-TYPE-SYMBOL */(*LnkLI2)(((object)VV[1]),(V2),((object)VV[2]),Cnil)); goto T3; T3:; {object V3 = make_longfloat(((double(*)(double))(dlatan))(lf(V2))); VMR1(V3);} } COMPILER>(declaim (inline |libm|:|atan|)) NIL COMPILER>(disassemble '(lambda (x) (declare (long-float x)) (|libm|:|atan| x)) nil) ;; Compiling /tmp/gazonk_21018_0.lsp. ;; End of Pass 1. ;; End of Pass 2. ;; OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3, (Debug quality ignored) ;; Finished compiling /tmp/gazonk_21018_0.o. #include "gazonk_21018_0.h" void init_code(){do_init((void *)VV);} /* local entry for function COMPILER::CMP-ANON */ static object LI1__CMP_ANON___gazonk_21018_0(object V2) { VMB1 VMS1 VMV1 {object V3 = make_longfloat(((double(*)(double))(dlatan))(lf(V2))); VMR1(V3);} } New bindings are made via the defdlfun macro: (defdlfun (:double "cblas_ddot" "libcblas.so") :int :double* :int :double* :int) |libcblas|:|cblas_ddot| (do-external-symbols (s :lib) (print s)) LIB:|libcblas| LIB:|libm| LIB:|libc| NIL (do-external-symbols (s :|libcblas|) (print s)) |libcblas|:|cblas_ddot| NIL The usage exactly follows C prototype rules. One limitation at present is that the function cannot be run when interpreted, i.e. must be compiled. I'll try to find time to fix this: (compile '|libcblas|:|cblas_ddot| ) ;; Compiling /tmp/gazonk_21018_0.lsp. ;; End of Pass 1. ;; End of Pass 2. ;; OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3, (Debug quality ignored) ;; Finished compiling /tmp/gazonk_21018_0.o. ;; Loading /tmp/gazonk_21018_0.o start address -T 0xb66b50 ;; Finished loading /tmp/gazonk_21018_0.o #<function 01cf2090> NIL NIL The function can now be used as any other function: (setq a (make-array 10 :element-type 'long-float :static t :initial-contents '(1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0))) #(1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0) (|libcblas|:|cblas_ddot| 10 a 1 a 1) 385.0 When the image is saved and restarted, the linking pointer is automatically updated. (si::save-system "ff") camm@localhost:~/debian/gcl/gclcvs/tmp/unixport$ ./ff GCL (GNU Common Lisp) NIL.NIL.NIL CLtL1 May 25 2012 14:58:14 Source License: LGPL(gcl,gmp,pargcl), GPL(unexec,bfd,xgcl) Binary License: GPL due to GPL'ed components: (XGCL READLINE UNEXEC) Modifications of this banner must retain notice of a compatible license Dedicated to the memory of W. Schelter Use (help) to get some basic information on how to use GCL. Temporary directory for compiler files set to /tmp/ >(in-package :compiler) #<"COMPILER" package> COMPILER>(|libcblas|:|cblas_ddot| 10 a 1 a 1) 385.0 COMPILER> On Debian based systems, such blas calls automatically proceed to optimized atlas calls (when installed) making use of specialized vector instruction extensions. One thing that should be kept in mind when using blas/lapack/atlas is that the latter can call malloc under certain circumstances. I wish these libraries had some means to toggle allocation on the stack, but they don't, or at least didn't last time I looked. This means that if you pass arrays allocated in relocatable memory, the result could be corrupted if a gc is triggered. We wrote gmp wrappers to handle a similar situation. But for a generic interface, this is impossible, so arrays should be allocated in contiguous block memory to be safe, i.e. using the :static t keywords in make-array. It is somewhat of a pity, as such memory is slower to GC. If you have any questions, please feel free. Take care, > Tim > > > > -- Camm Maguire [email protected] ========================================================================== "The earth is but one country, and mankind its citizens." -- Baha'u'llah _______________________________________________ Axiom-developer mailing list [email protected] https://lists.nongnu.org/mailman/listinfo/axiom-developer
