0.3 Updates
Jaco van der Merwe
jjnvdm at yahoo.co.uk
Sun Jul 18 10:46:55 PDT 2004
Hi All,
I've been following the Slate project with great
interest. With the recent announcement of the 0.3
alpha release I downloaded all the sources. I use
mostly Windows XP as my OS, so I tried to compile and
run Slate 0.3 alpha on Windows, albeit without
success. Below I provide a summary of my experiences
and some of the issues I've identified. I am not sure
whether cross-compiler, or even cross-platform
capabilities are a focus of the Slate project, but I
would really like to be able to use Slate on Windows,
and maybe later on other platforms like QNX, or even
handhelds. I would gladly assist in making Slate
compile and run on Windows using the Visual C++
compiler. However, in order to do that I would have to
understand the inner workings of the VM better, and
some differences between GCC and Visual C++ (as per my
questions below).
My first attempt to compile and run Slate on Windows
was by using MINGW 3.2 (as embedded in Dev-C++
4.9.8.0). The sources compiled without problems, but
when I ran the executable it crashed soon after
calling the PSInterpreter_interpret() function from
the main() function in the file "boot.c". I still need
to spend time to find out exactly where this occurs.
In order to find the source of this crash I then tried
to compile the sources with the Microsoft Visual C++
7.1 compiler, hereafer called VC, which provides
really good debugging facilities on Windows,
especially with program crashes. On a program crash
the VC debugger breaks directly into the source code
at the point where the crash occurred. This process of
trying to compile slate using the VC compiler
highlighted some cross-compiler problems, as well as a
few other issues in the Slate source code. Here they
are:
A)------
The "vm.h" and "vm.c" files define a number of inline
C functions using the "inline" modifier. However, this
is not a standard C keyword and the VC compiler did
not understand that. The "inline" keyword is specific
to the GCC compiler. The VC equivalent to that is
"_inline". In order to make the code compile on
several compilers I'd suggest using an INLINE macro
definition that checks the compiler being used and
then defines it to either "inline" or "_inline", or
whatever else other compilers need. The predefined
macro that identifies the VC compiler is "_MSC_VER".
B)------
The VC compiler also choked on the definition of the
following two structs:
struct RoleTable {
struct ObjectHeader header;
struct Map * map;
ObjectPointer traits;
ObjectPointer elements[0];
struct RoleEntry roles[0];
};
struct SlotTable {
struct ObjectHeader header;
struct Map * map;
ObjectPointer traits;
ObjectPointer elements[0];
struct SlotEntry slots[0];
};
The VC compiler allows unsized or zero-sized arrays as
the last member in a struct definition. However, the
definition above declares two zero-sized arrays as the
final members of the struct. I understand the usage of
a single unsized array at the end of a struct, but I
don't understand how the above definitions are
supposed to work. Maybe it is something specific to
the GCC compiler? In order to make the code compile I
changed the definitions of
ObjectPointer elements[0];
to
ObjectPointer * elements;
in both struct definitions, but I'm sure that the
semantics is not the same and therefore the code will
not run :-( I would appreciate it if anyone can
explain to me what the two zero-sized arrays at the
end of a struct means in GCC. Then I can maybe find a
way to accomplish the same in VC.
C)------
After making the above sets of changes to the source
code, the VC compiler was finally able to compile the
code without syntactic errors. However, many warnings
remained which I then started investigating. The
following issues were identified in the process:
C.1) ---
The file "vm.c" does not include the file "vm.h", but
instead contains its own defininitions of everything
it requires, which looks almost like a carbon copy of
"vm.h". The result is that most of the definitions in
"vm.h" are duplicated in "vm.c". Is there a reason for
doing it this way? Why not just include the file
"vm.h" in "vm.c"? While trying to get the code to
compile I had to make changes to the same definitions
in two places, which is not good for maintainable
code.
C.2) ---
The file "vm.h" contains many static inline function
definitions. What is the purpose of putting static
function definitions in a header file, especially if
the only file that seems to be using them is "vm.c"?
Why not keep these definitions local to the C file
that uses them?
C.3) ---
Several of the function implementations call functions
that have not been defined yet. In these cases the VC
compiler assumes they are extern functions returning
an int. This happens at the following locations:
File: "vm.c"
line 743: error() undefined
line 1213: shrinkMemoryBy() undefined
line 2280: growMemoryBy() undefined
File: "vm.h"
line 429: error() undefined
line 586: PSInterpreter_sendMessage_withOptionals_()
undefined
line 612: PWord_copyWords_into_() undefined
File: "file.c"
line 134: PSObjectHeap_sweep() undefined
line 136: PSObjectHeap_adjustAllOopsBy_() undefined
C.4) ---
I notice that all the primitives that implement math
functions like sin, cos, log, etc., cast their double
results to floats. There is a possible loss of data
here. For example, see the function _primitive55()
which contains the following line of code:
*((float *) PSObject_arrayElements(z)) = (sin (a));
The same happens in all the other primitives for math
functions. Are the real-valued types in Slate always
floats, or can they be doubles as well? I'd prefer
them to be doubles as far as possible.
C.5) ---
In the file "file.c" the function
PSObject_payloadSize(Object * obj) is called with a
ByteArray* argument at lines 42 and 115. This may be a
potentially invalid implicit cast.
C.6) ---
In the file "file.c", line 136, the following function
call is performed:
PSObjectHeap_adjustAllOopsBy_ (CurrentMemory, -
(unsigned) CurrentMemory -> memory);
However, the signature of this function looks as
follows
unsigned long PSObjectHeap_adjustAllOopsBy_(struct
ObjectHeap * h, unsigned long shiftAmountInBytes)
Now this is the part that I don't understand. When the
function call is performed, the 2nd argument is cast
to an unsigned (int or long?), then negated, which
makes it a negative integer, and then implicitly cast
to an unsigned long because that is the type of the
2nd argument. Is this correct code?
C.7) ---
Finally, if there are any implicit assumptions made in
the code regarding the byte-alignment and packing of
struct members, then I'd like to see them made
explicit. This will greatly assist in making the code
compile on more compilers.
Regards
Jaco
__________________________________
Do you Yahoo!?
Vote for the stars of Yahoo!'s next ad campaign!
http://advision.webevents.yahoo.com/yahoo/votelifeengine/
More information about the Slate
mailing list