From: Yaroslav K. <kav...@je...> - 2010-08-17 11:57:17
|
Hi! This is example of c-code (mix from .h and .c files): // definitions from .h file LLVMBool LLVMVerifyModule(LLVMModuleRef M, LLVMVerifierFailureAction Action, char **OutMessage); void LLVMDisposeMessage(char *Message); LLVMBool LLVMCreateJITCompiler(LLVMExecutionEngineRef *OutJIT, LLVMModuleProviderRef MP, unsigned OptLevel, char **OutError); // code char *error = NULL; // Used to retrieve messages from functions LLVMVerifyModule(mod, LLVMAbortProcessAction, &error); LLVMDisposeMessage(error); // Handler == LLVMAbortProcessAction -> No need to check errors ... LLVMExecutionEngineRef engine; error = NULL; if(LLVMCreateJITCompiler(&engine, provider, 2, &error) != 0) { fprintf(stderr, "%s\n", error); LLVMDisposeMessage(error); abort(); } I wrote next ffi-code as analog: ;; definitions (ffi:def-call-out LLVMVerifyModule (:name "LLVMVerifyModule") (:arguments (M LLVMModuleRef) (Action LLVMVerifierFailureAction) (OutMessage (ffi:c-ptr ffi:c-string) :out)) (:return-type ffi:int)) (ffi:def-call-out LLVMDisposeMessage (:name "LLVMDisposeMessage") (:arguments (Message ffi:c-string))) (ffi:def-call-out LLVMCreateJITCompiler (:name "LLVMCreateJITCompiler") (:arguments (OutJIT (ffi:c-ptr LLVMExecutionEngineRef) :out) (MP LLVMModuleProviderRef) (OptLevel ffi:uint) (OutError (ffi:c-ptr ffi:c-string) :out)) (:return-type ffi:int)) ;; code (multiple-value-bind (res err) (llvm:LLVMVerifyModule mod llvm:LLVMAbortProcessAction) (llvm:LLVMDisposeMessage err)) ... (defvar engine (multiple-value-bind (res _engine errmsg) (llvm:LLVMCreateJITCompiler provider 2) (unless (zerop res) (let ((s errmsg)) (llvm:LLVMDisposeMessage errmsg) (error "~S~%" s))) _engine)) Program works... But something vague doubts gnaw at me ... I'm wrong? -- WBR, Yaroslav Kavenchuk |
From: <Joe...@t-...> - 2010-08-17 12:18:52
|
Hi, Yaroslav Kavenchuk wrote: >This is example of c-code (mix from .h and .c files): It is generally not possible *without API documentation* to translate C's poor type declarations to CLISP's rich FFI. C does not tell you anything about :in, :out or :in-out. Only the docs do. Please always provide a link to the documentation when asking such questions. Of course, there are patterns attached to names, e.g. char**outerror looks like an obvious :out parameter candidate. However even then, translation to (OutError (ffi:c-ptr ffi:c-string) :out) is not necessarily correct. The FFI expects an unconditional out parameter and will always try to access the string, where as most APIs are of the form "the error out-parameter is only valid when an error actually occurred [and undefined otherwise]." Therefore, always dereferencing outerror may cause a crash when the call succeeded, as its not guaranteed to be NULL. >void LLVMDisposeMessage(char *Message); >(ffi:def-call-out LLVMDisposeMessage (:name "LLVMDisposeMessage") > (:arguments (Message ffi:c-string))) This may or may not be completely broken. Does the function expect a pointer to an object that it creates itself (e.g. by malloc)? CLISP's will pass the address of a new stack-allocated string. Looking at your C code, > char *error = NULL; // Used to retrieve messages from functions > error = NULL; > if(LLVMCreateJITCompiler(&engine, provider, 2, &error) != 0) { LLVMDisposeMessage(error); I'd say you're better off using an explicit c-pointer for [out]error using with-foreig-object or with-c-var - to keep the original pointer (for DisposeMessage) - to deref the string only when an error occurred (like you do in C). (with-foreign-object (ptr 'c-pointer NULL) (when (create ... ptr) (princ (mem-read ptr 'c-string)) (disposemessage ptr))) Regards, Jörg Höhle |
From: <Joe...@t-...> - 2010-08-17 15:52:00
|
Hi, Yaroslav Kavenchuk wrote: you're good, you quickly found the errors in my text. >and last: 'LLVMCreateJITCompiler' require 'char **OutError' >but 'LLVMDisposeMessage' require 'char *Message' >The code will be by the same? It cannot be. Always remember that the ptr object already is a pointer to the "struct" you allocated on the stack. You can go both ways. Either think about using FOREIGN-VALUE when you need to dereference, or use types or conversions that match. The more conversions you'll need, the more with-c-var will become interesting instead of with-foreign-object. In the present case you need one conversion since there's a dual view on one pointer: once as c-string (for printing), once as c-pointer (void* disguised as char*) for DisposeMessage. >ok, but "NULL" is "NIL"? And what is "mem-read"? Yes. The AFFI name of what became in FFI: (memory-as ptr (parse-c-type 'c-string)) Regards, Jörg Höhle |
From: <Joe...@t-...> - 2010-08-18 10:04:12
|
Yaroslav Kavenchuk wrote: > (:return-type int)) > (unless res >It is right? No. You didn't try it out, did you? This is not C. What branch is (unless <integer>) going to take in Lisp? > (LLVMCreateJITCompiler provider 2 err) > (LLVMDisposeMessage err) Compare with > if(LLVMCreateJITCompiler(&engine, provider, 2, &error) != 0) { > LLVMDisposeMessage(error); Obviously you cannot use the same object in Lisp. One time you need a pointer to pointer-sized slot, another time the value of that slot. >Or 'LLVMCreateJITCompiler' will be called with a value of >'err', rather than its address? I find "value of err" is confusing in this sentence. err "represents" the object you asked to allocate on the stack, You can view it as a pointer to this object. The Lisp side likes to ignore pointers and consider the value that object holds. The C sides values the pointer itself. So it's suitable for the &error call. Note that this is not the only way to interface to these functions. I wrote: >I'd say you're better off using an explicit c-pointer for [out]error Perhaps you'd get less confusion by defining LLVMCreateJITCompiler with (OutErr (c-ptr c-pointer) :out) as you initially wanted. You'll be returned a pointer that you can pass back or interpret as a string with memory-as. Beware: This pointer is not the same as the above err object! It does not represent a pointer to a pointer, rather than the string data itself. (memory-as error 'string) ; not ffi:c-string! This is a special pattern that reads a string from memory, not a pointer to a string. It appears that this is not documented in the impnotes. Strange... That is an oversight. Regards, Jörg Höhle |
From: Yaroslav K. <kav...@je...> - 2010-08-18 11:01:42
|
Many thanks for your explanation! Joe...@t-... wrote: >> > (unless res >> >It is right? >> > No. You didn't try it out, did you? > oops, excuse me =) >> > (LLVMCreateJITCompiler provider 2 err) >> > (LLVMDisposeMessage err) >> > Compare with > >> > if(LLVMCreateJITCompiler(&engine, provider, 2,&error) != 0) { >> > LLVMDisposeMessage(error); >> > Obviously you cannot use the same object in Lisp. One time you need > a pointer to pointer-sized slot, another time the value of that slot. > if so (LLVMCreateJITCompiler provider 2 (foreign-address err)) ? > Note that this is not the only way to interface to these functions. > I wrote: > >> >I'd say you're better off using an explicit c-pointer for [out]error >> > Perhaps you'd get less confusion by defining > LLVMCreateJITCompiler with (OutErr (c-ptr c-pointer) :out) > as you initially wanted. You'll be returned a pointer that you can pass > back or interpret as a string with memory-as. > Beware: This pointer is not the same as the above err object! > It does not represent a pointer to a pointer, rather than the string data itself. > It is right (def-call-out LLVMCreateJITCompiler (:name "LLVMCreateJITCompiler") (:arguments (OutJIT (c-ptr LLVMExecutionEngineRef) :out) (MP LLVMModuleProviderRef) (OptLevel uint) (OutError (c-ptr c-pointer) :out)) (:return-type int)) (defvar engine (multiple-value-bind (res _engine err) (LLVMCreateJITCompiler provider 2) (unless (zerop res) (let ((s (memory-as err (parse-c-type 'c-string)))) (LLVMDisposeMessage err) (error "~S~%" s))) _engine)) ? > (memory-as error 'string) ; not ffi:c-string! > This is a special pattern that reads a string from memory, not a pointer to a string. > CL-USER> (with-foreign-object (str 'c-string "hallo") (with-foreign-object (ptr 'c-pointer (memory-as str (parse-c-type 'c-pointer))) (memory-as ptr (parse-c-type 'c-string)))) "hallo" CL-USER> (with-foreign-object (str 'c-string "hallo") (with-foreign-object (ptr 'c-pointer (memory-as str (parse-c-type 'c-pointer))) (memory-as ptr 'string))) "фЃгNЛI" CL-USER> Or did you mean something else? -- WBR, Yaroslav Kavenchuk |
From: Yaroslav K. <kav...@je...> - 2010-08-17 13:38:08
|
Joe...@t-... wrote: > > This is example of c-code (mix from .h and .c files): > > It is generally not possible *without API documentation* to translate > C's poor type declarations to CLISP's rich FFI. C does not tell you > anything about :in, :out or :in-out. Only the docs do. > Ok, but all documentation it is source: http://llvm.org/svn/llvm-project/llvm/trunk/include/llvm-c/Core.h http://llvm.org/svn/llvm-project/llvm/trunk/include/llvm-c/Analysis.h > Please always provide a link to the documentation when asking such questions. > ok, excuse me And many thanks for your answer. > Looking at your C code, >> char *error = NULL; // Used to retrieve messages from functions >> error = NULL; >> if(LLVMCreateJITCompiler(&engine, provider, 2,&error) != 0) { >> > LLVMDisposeMessage(error); > I'd say you're better off using an explicit c-pointer for [out]error using > with-foreig-object or with-c-var > - to keep the original pointer (for DisposeMessage) > - to deref the string only when an error occurred (like you do in C). > (with-foreign-object (ptr 'c-pointer NULL) > (when (create ... ptr) > (princ (mem-read ptr 'c-string)) > (disposemessage ptr))) > ok, but "NULL" is "NIL"? And what is "mem-read"? and last: 'LLVMCreateJITCompiler' require 'char **OutError' but 'LLVMDisposeMessage' require 'char *Message' The code will be by the same? > (with-foreign-object (ptr 'c-pointer) > (when (LLVMCreateJITCompiler ... ptr) > ... > (LLVMDisposeMessage ptr))) and how declare argument OutError in LLVMCreateJITCompiler - as (OutError ffi:c-pointer) or as (OutError (ffi:c-ptr ffi:c-pointer)) ? Many thanks for your answer again. -- WBR, Yaroslav Kavenchuk |
From: Yaroslav K. <kav...@je...> - 2010-08-18 08:19:03
|
Joe...@t-... wrote: > Always remember that the ptr object already is a pointer to the "struct" you > allocated on the stack. > > You can go both ways. Either think about using FOREIGN-VALUE when > you need to dereference, or use types or conversions that match. > The more conversions you'll need, the more with-c-var will become > interesting instead of with-foreign-object. In the present case you > need one conversion since there's a dual view on one pointer: once as > c-string (for printing), once as c-pointer (void* disguised as char*) for > DisposeMessage. > Ok, if I understand correctly: (def-call-out LLVMCreateJITCompiler (:name "LLVMCreateJITCompiler") (:arguments (OutJIT (c-ptr LLVMExecutionEngineRef) :out) (MP LLVMModuleProviderRef) (OptLevel uint) (OutError (c-ptr ffi:c-pointer))) (:return-type int)) (def-call-out LLVMDisposeMessage (:name "LLVMDisposeMessage") (:arguments (Message c-pointer))) (defvar engine (with-foreign-object (err 'c-pointer nil) (multiple-value-bind (res _engine) (LLVMCreateJITCompiler provider 2 err) (unless res (let ((s (memory-as err (parse-c-type 'c-string)))) (LLVMDisposeMessage err) (error "~S~%" s))) _engine))) It is right? Or 'LLVMCreateJITCompiler' will be called with a value of 'err', rather than its address? -- WBR, Yaroslav Kavenchuk |