Pharo Libclang FFI, part 1, preamble

Table of contents

Background

I wanted to better understand the opensmalltalk-vm that Pharo runs on.  I started to manually chart and compare the C code between platforms, which was insightful but tedious and error prone.  What I needed was to automatically process these files.  Clang is a C language front-end for the LLVM compiler, designed to be integrated into external projects.  Libclang provides an interface suitable for the Pharo FFI, but I’d never used FFI before.  From a distance FFI had seemed somewhat daunting and complex, but it turns out reasonably straight forward.  I’m documenting my experience in the form of this tutorial that I can refer back to, and perhaps shines a newbie light on things that may encourage other FFI neophytes to give it a go.

New to Pharo?

If you’ve ended up here more from an interest in libclang than knowing anything about Pharo, these links will give you an idea of what Pharo is about…

You can download Pharo here.  It comes with some built in tutorials that should provide enough to follow along this tutorial. The best book to work through is Updated Pharo by Example.

Breaking the ice

As a FFI neophyte myself, the first simple example from the UniffiedFFI chapter helped settle my nerves, so I reproduce it here. Its nice to get something simple under the belt. The clock() function from the standard C library should exist as part of the system on all platforms.  The LibC class comes pre-defined in Pharo providing a link to the standard C library that most systems should have installed. Thus for this example, the only thing required is to add…

LibC class >> ticksSinceStart
      ^ self ffiCall: #( uint clock () ) module: LibC 

and in Playground inspect following…

LibC ticksSinceStart
"==> 32709046 (or something similar)"

If for some reason that doesn’t work, please report to the pharo-users mail list.

Class initializations

At times the FFI classes we define need to be initialized before use.  So you’ll know when this needs to be done, I’ll leave a comment “self initialize” in the method code where this should be done.  After saving the method, select the comment (by double-clicking just inside the quotes) and then Do-It.

Colour legend

To help distinguish things, C code is in blue, Pharo code in brown, and red will show any modifications to previously defined code.

Clang install

Right off the bat, lets get the pronunciation right. I thought it was natural to call it
“C-Lang” but apparently that’s wrong and its “clang”.  I guess they like things loud!?

Now forgive me – but I took the shotgun approach to installing the required libraries. I searched for anything “clang” and installed everything that looked core or libclang related.  This list is probably overkill, so if anyone can narrow it down please leave a comment.

$ apt-cache search clang$ sudo apt-get install \
     clang-3.5 \
     clang-3.5-doc \
     clang-3.5-examples \
     libclang-3.5-dev \
     libclang-common-3.5-dev \
     libclang1-3.5:i386 \
     libclang1-3.5-dbg

To test the install, intuitively I tried…
$ clang
bash: clang: command not found

huh?  So searching…    
$ find /usr -name “*clang*”

I discovered clang had been installed as clang-3.5 in /usr/bin. So this worked for me…
$ clang-3.5
clang: error: no input files

Your particulars may differ.

Create an AST file to process later…

Later we will need to an AST file to work with.  We create it now to test our clang installation.   We’ll process this very simple program. 
$ vi foo.c

int addtwo( int number )
{   return number + 2;
}

int main()
{   int result = addtwo( 5 );
}

To get an idea for goals of what we might try to do  in Pharo, we can text dump the AST to the console…
$ clang-3.5 -Xclang -ast-dump -fsyntax-only foo.c

TranslationUnitDecl 0xa7e8920 <>
 |-TypedefDecl 0xa7e8bf0 <>  implicit __builtin_va_list 'char *'
 |-FunctionDecl 0xa7e8ca0  line:2:5 used addtwo 'int (int)'
 | |-ParmVarDecl 0xa7e8c30 col:17 used number 'int'
 | `-CompoundStmt 0xa7e8d70
 |   `-ReturnStmt 0xa7e8d60
 |     `-BinaryOperator 0xa7e8d48 'int' '+'
 |       |-ImplicitCastExpr 0xa7e8d38 'int'
 |       | `-DeclRefExpr 0xa7e8d04 'int' lvalue ParmVar 0xa7e8c30 'number' 'int'
 |       `-IntegerLiteral 0xa7e8d20 'int' 2
 `-FunctionDecl 0xa7e8dd0 line:7:5 main 'int ()'
   `-CompoundStmt 0xa7e8ef8
     `-DeclStmt 0xa7e8ee8
       `-VarDecl 0xa7e8e40 col:7 result 'int' cinit
         `-CallExpr 0xa7e8ec8 'int'
           |-ImplicitCastExpr 0xa7e8eb8 'int (*)(int)'
           | `-DeclRefExpr 0xa7e8e70 'int (int)' Function 0xa7e8ca0 'addtwo' 'int (int)'
           `-IntegerLiteral 0xa7e8e88 'int' 5

And we can create the binary AST file that we’ll use for input later…
$ clang-3.5 -emit-ast foo.c
$ ls foo.ast
foo.ast

System configuration

I’ve been using 32-bit Debian  since 32-bit libraries automatically match 32-bit Pharo.  For 64-bit Linux you’ll currently need to ensure the 32-bit clang libraries are installed and used.  The imminent release of 64-bit Pharo should simplify FFI on 64-bit Linux.  Here is my configuration…

$ uname -a
Linux dom0 3.16.0-4-686-pae #1 SMP Debian 3.16.7-ckt25-2 (2016-04-08) i686 GNU/Linux

Pharo > World Menu > System > System Reporter
Image: Pharo5.0 Latest update #50761
Virtual Machine: 
CoInterpreter VMMaker.oscog-eem.1855 May  4 2016
StackToRegisterMappingCogit VMMaker.oscog-eem.1855 May  4 2016
Jenkins build #589

Parting thoughts

That concludes the preamble.  Each of the following parts start by presenting a clang facility as used from C, then shows how to use it from Pharo.
The next part performs a simple call out to the libclang library.

This entry was posted in FFI, Pharo. Bookmark the permalink.

Leave a Reply