Hello there!

Today, as a (likely useless) pet project, I've modified the C source code of 
pil32 so that it can be compiled with clang.

TD;LR: because clang refuses to implement a gcc extension[1], this patch 
plagues some (small) parts of the source code with macro calls, but compiled 
code works and is smaller than the one generated by gcc (on OSX, using the 
default ancient gcc and the latest clang).

This source concerned, the only two differences between gcc and clang are:
a) variable-sized arrays inside structures are not supported[1];
b) functions _appear_ to be aligned by default[2] (no formal proof about that, 
just evidence in disassemblies on both OSX and Linux).

Issue (b) being solved by definition, issue (a) remains. In pil32 source code, 
there are just a few spots where variable-sized arrays inside structures have 
been used, the pattern being (luckily) mostly the same: a bindFrame structure 
is defined inline with the internal array field "bnd" being set to a 
non-constant size. Its size is (sometimes) used to compute the number of 
elements to put into an index variable, and the current binding frame of the 
global environment is updated accordingly[3].

My solution has been to insert, near the definition of each bindFrame, a char 
array in the stack big enough to hold the whole bindFrame structure plus the 
variable size of 'bnd' inside (alloca() being unreliable). Then I've modified 
the in-place definitions of bindFrame so that bnd[...] would become bnd[0] 
(that is, a flexible array whose size isn't known at the end of a structure, as 
accepted by C99), and I've patched every access to sizeof(bnd) and access to 
bindFrame accordingly. This is an example:

--- before ---
/* ... */
struct {
   struct bindFrame *link;
   int i, cnt;
   struct {any sym; any val;} bnd[length(y)+2];
} f;
/* ... */ Env.bind = (bindFrame *)&f;
f.i = sizeof(f.bnd) / (2*sizeof(any)) - 1;
/* access to f.<fields> follow */

--- after ---
size_t _f_size = sizeof(any)*2*(length(y)+2);
struct {
   struct bindFrame *link;
   int i, cnt;
   struct {any sym; any val;} bnd[0];
} *f;
char _f_space[sizeof (*f) + _f_size];
f = (void*)_f_space;
/* ... */ Env.bind = (bindFrame *)f;
(*f).i = _f_space / (2*sizeof(any)) - 1;
/* access to (*f).<fields> follow */

-------------

Code changes have been performed so that the following goals could be reached:
- although the changes are compatible with gcc, I wanted the source to be the 
same as the original one when compiled with gcc, and produce the very same 
output;
- same performance;
- same stack usage (no heap allocations!);
- the source would have to be left mostly untouched[4] since I don't really 
know what it does (ahem);
- it should be possible to play with other (better) solutions.

With that in mind, I've coded four macros, awkwardly named.
- BNDFRAMEDEF(variable, non-const size) (e.g. using the previous example, 
BNDFRAMEDEF(f, length(y)+2))
  declares and defines a new bindFrame on the stack, assigned to (bindFrame *) 
<variable>
- SIZEOFBND(variable) (e.g. SIZEOFBND(f))
  returns the size of the bnd array
- B(variable) (e.g. B(f).i = ...)
  used to access a field 
- BP(variable) (e.g. Env.bind = BP(f))
  used to access the bindFrame address on stack

These macros map to the new code when compiling with clang, and to the old code 
otherwise. Code follows:

---------------8><---------------8><---------------
#ifdef __clang__
#define INSTRUCT_VARLEN_ARRAYS_SUPPORT
#endif

#ifdef INSTRUCT_VARLEN_ARRAYS_SUPPORT
#warning "remapping in-struct verlength arrays"

#define BNDFRAMEDEF(v, s)\
        size_t _##v##_size = sizeof(any)*2*(s);\
        struct { \
                struct bindFrame *link;\
                int i, cnt;\
                struct {any sym; any val;} bnd[0];\
        } *v;\
        char _##v##_space[sizeof(*v)+_##v##_size];\
        v=(void *)_##v##_space
#define SIZEOFBND(v) _##v##_size
#define B(v) (*v)
#define BP(v) ((bindFrame*)v)

#else

#define BNDFRAMEDEF(v, s)\
        struct {\
                struct bindFrame *link;\
                int i, cnt;\
                struct {any sym; any val;} bnd[s];\
        } v
#define SIZEOFBND(v) sizeof(v.bnd)
#define B(v) v
#define BP(v) ((bindFrame*)&v)

#endif /* INSTRUCT_VARLEN_ARRAYS_SUPPORT */
---------------8><---------------8><---------------

If somebody is interested in getting the whole patch, let me know. It's a 
Sunday morning divertissement (both trivial and useless since pil64 is 
(deservingly) the new hype). It's the fist time I take a serious look at 
picoLisp, and I hope some day to have the time to port pil64 on OSX -- although 
I hope /Mr.Burger/ will do it a lot sooner ;)

Ciao,
Illo.  

[1] 
http://clang.llvm.org/docs/UsersManual.html#intentionally-unsupported-gcc-extensions
[2] hence (maybe) the option "-falign-functions" being unsupported. It skips 
the option anyway so I left it in the Makefile.
[3] other parts of the code use the same pattern with constant sizes of 'bnd' 
and those haven't been modified.
[4] the best way to achieve this is by leaving the bindFrame structure 
declaration in pico.h as it is.--
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe

Reply via email to