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. 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). 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 *)
- 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 be