automatically convert Spin to C or C++
ersmith
Posts: 6,095
I've just released a new version (0.96) of the spin2cpp tool for converting spin files to C++. It now also supports conversion to plain C as well, with some restrictions. At the moment the C output is static, so only one instance of each object is allowed. I've made an effort to make the C output generic (i.e. it should generally compile with either PropGCC or Catalina) but there are still a few C++ and/or GCC specific features output for some constructs, e.g. lookup and lookdown.
Catalina already has a way to interface with spin, of course, but sometimes one may want to convert a spin file in order to benefit from the improved speed of C.
This version of spin2cpp also supports putting inline C and C++ code in spin comments. Any comment starting with {++ is assumed to be C++ (or C) code and is passed through. The intent is that a spin driver that interfaces to PASM code can have C added to it as well, so that the same spin file can be used to produce a C/C++ driver.
The download link is at:
http://code.google.com/p/spin2cpp
Catalina already has a way to interface with spin, of course, but sometimes one may want to convert a spin file in order to benefit from the improved speed of C.
This version of spin2cpp also supports putting inline C and C++ code in spin comments. Any comment starting with {++ is assumed to be C++ (or C) code and is passed through. The intent is that a spin driver that interfaces to PASM code can have C added to it as well, so that the same spin file can be used to produce a C/C++ driver.
The download link is at:
http://code.google.com/p/spin2cpp
Comments
Thinking of a typical Spin program, there are generally a list of global variables in the VAR section. My understanding of C is that one tends not to use global variables and instead passes variables into functions and then the function returns new values. So I've been thinking of a way of writing Spin that is more like C to start with, ie no global variables.
I'm a C newbie but I think the general concept is you pass a pointer to a variable rather than the variable. Then inside the function you now have direct access to that variable and can change it and in effect, this makes it a global variable.
The simple case is passing one variable and returning one variable. The slightly more complex case is returning two variables. I think the general concept in Spin is to pass two pointers with the @, eg
I'm not 100% sure that is correct but the idea is for variables a and b in the main routine to be changed by the function.
So I ran this through Spin2CPP and it produced this code
If this is correct, one could presumably do the same thing with arrays?
Eventually spin2cpp will fully support objects even in C mode, and then the output of:
will look like:
i.e. a pointer to the struct containing the VAR variables will be passed in as a parameter to all the functions. This is the way C++ works "under the hood"
Ok, brainstorming here as I have been writing some spin code that is pushing up against the limits of what spin can do. Spin does not seem fully "object oriented". Consider an SD driver object. You can reference all its methods from the main program and that is fine. With the touchscreen however, it has been incredibly useful to create a common object called touch.spin which contains a whole bunch of useful methods. Using this object, it is possible to create a calculator program and a synth program using very little extra code. The problem is that integral to this touch.spin object is an SD driver. So the sd driver object becomes a sub-object of the touch.spin object. You can still reference the sd code from the main, but you have to do this via the touch.spin object. The next problem though is if you add in some new code that contains an object that references the sd card object. You either end up with two copies of the sd object (each of which takes about 1/3 of the hub ram), or you have to delete one and pass everything back up to the main, and then down into the tree of another object.
With your code you just posted, does this mean C++ can pass references to objects to and from functions? If so, that makes things a lot more flexible.
Or to put it another way, thinking graphically, Spin is like a tree with branches, and if one leaf on one branch talks to another leaf it needs to talk via the base of the tree. What would be more flexible would be to tell every leaf about a common object and then each leaf can put variables in this object and retrieve variables.
To take a specific example, create an object like a button. Put into that object two variables and a string. Pass the object with your routine *thisobj. Then every function can use the object.
I have this vague idea that if we take all the obex objects and convert them via spin2cpp to C objects, we could use them in ways we can't use them in spin (eg my .wav player which currently has two sd objects in it)
Help understanding this would be most appreciated.
Combine this with your other efforts and Spin2Cpp program binaries could end up faster and smaller than Spin binaries.
Sweet
@Dr_Acula, I've had the same problem with multiple copies of the SD drivers. I got around it by making a modified version of FSRW that uses a pointer to a file block rather than VAR variables. It's the same idea as passing a struct pointer in C, but doing it in Spin. However, since Spin doesn't handle structures I had to copy the contents of the file block to local variables in FSRW. Fortunately, a LONGMOVE of 11 variables in Spin only takes about the same amount of time as a couple of assignment statements, so there's not much overhead in doing this.
@Steve, when are you adding spin2cpp to the SimpleIDE? This would make Spin development very flexible, where it could be compiled to bytecodes or to the various PropGCC PASM models. Taking this one step further would allow C/C++ programs to call Spin routines that are converted by spin2cpp. This would make all of the Spin objects in the OBEX available to PropGCC programs.
Of course, spin2cpp doesn't help you much here, since it's taking Spin code as input, so it will never actually take advantage of the structure passing capability. Perhaps you could use spin2cpp to convert to C (or probably C++ is better if there are a lot of objects) and then hand optimize the output to use structures.
Great news. I can see a new way of working - convert a whole lot of obex objects into C, then start using them in new and creative ways.
Nice work, Eric. On the Prop II (where the necessary Hub space will probably be available) this will be a useful option.
By the way - lookup and lookdown are easy enough to program in C (or PASM for that matter). I've had to do this already for my own purposes - perhaps both GCC and Catalina should agree on C function prototypes for all the standard Spin functions (lookup, lookdownz, bytemove, wordfill, coginit, locknew etc etc) and then add the functions to their respective standard libraries. This would sure help people migrating from Spin to C (whether Catalina or GCC). If you want to explore this possibility further, send me a PM.
Ross.
The common case of lookup/lookdown (when the elements are constant) is easy enough to handle -- just generate a temporary array and index into it. It's the case where the element array includes a reference to the (possibly modified) index that causes me headaches. For example: I was trying to avoid changing the libraries, although that's obviously a possibility. Using a variadic function is also another possibility, I guess -- is that what you ended up doing?
Eric
It will handle the Spin code, but the result is too big to fit into hub memory without some manual changes (for Graphics_Demo, changing to single buffering; I haven't actually tried VGA_Demo yet).
Once the various C compilers have their compressed memory models (CMM) working then there's a good chance these demos will work without changes.
Eric
The LCD codes I'm interested in are very close to the VGA_Demo...
BTW, if you have access to Propeller-GCC with compressedcode, you can add the CMM model to the SimpleIDE by defining a file called memorymodels.txt in the SimpleIDE.exe folder.
Contents of my memorymodels.txt:
CMM
COG
LMM
XMMC
XMM-SINGLE
XMM-SPLIT
Hi Dave,
I would still like to have the Spin bytecode interpreter available on the Prop II. I originally thought there might not be one, but Parallax has assured us all that there will, and I see no reason for them to reconsider that just because of CMM. Spin will always give you the smallest code sizes on the Propeller - and code size will continue to be both a Prop I and a Prop II killer for many users and applications.
Of course, the Prop II will have more RAM than the Prop I, but even so we already run many C programs that wouldn't fit on the P2 without CMM (we currently run them using XMM). Where I believe CMM will get most traction is in entirely supplanting the need for bolting on expensive XMM hardware to both the Prop I and the Prop II.
While the GCC team is of course entirely free to use the Catalina CMM kernel (or a derivative), I hope they decide to go their own way. I'm interested in seeing if they can do better - and I'm sure they will try hard, since Catalina has effectively stolen a march on them with CMM support .
Ross.
I think it's great that we're experimenting with different solutions... I don't think there will ever be a single "one size fits all" best, and certainly a diverse selection of tools is good for the Propeller community (IMHO).
If you want to take a peek at the PropGCC CMM code, which is still in development, you can look at the "compressedcode" branch of the source repository. We're using a byte code based interpreter, which likely has more overhead than the Catalina CMM kernel but which allows for potentially better compression. Here's the btea function of xxtea as produced by propeller-elf-gcc -Os -mcmm:
Eric
I must admit I had not considered this case. Apart from this, I think a variadic function is the best answer (I think it may even be the only possible answer, given the way the function is supposed to handle character strings). The case you're trying to address seems to me to involve unspecified behavior anyway - do you know of any instances of people actually using these functions this way?
I agree. Unfortunately, I don't really have time to keep abreast of what's happening in GCC as well as keeping Catalina going, but thanks for the listing - very interesting. I tried byte encoding as my very first option, since I knew that would give you the best code sizes (I tried nearly everything - 8 bit, 16 bit, 24 bit and 32 bits, as well as many of the possible combinations of these). Yes, I could certainly get smaller code sizes with 8 bit encoding than the encoding I finally settled on (essentially, 16 bit) - but the resulting execution speeds were simply not compelling. Certainly not compelling enough to justify claiming that CMM could ever displace either LMM or Spin. Of course, you may be able to do a bit better than I managed - happy to wait and see.
Ross.
It probably is an edge case, but I wanted to handle it correctly. Actually though I think there is another solution. Since the mapping between Spin and C does not have to be one to one, I can scan over the lookup statement and extract the index, creating code like:
or something of that ilk. I'd like to stick with arrays if possible rather than variadic functions, because the performance will be better (especially for the common case of constant arrays). But providing a variadic lookup function in the library may be a good idea for people doing a port by hand.
Thanks for your help with this!
Eric
No worries, since I didn't actually do anything . Do you intend to handle character strings the same way? - e.g. lookup(i : "abcd", 2, 3, 4). I'm not actually sure what the behavior should be in such cases - the manual seems to imply that if i = 1 it should return "a", and if i = 2 it should return "b", whereas I would have expected it to return the address of the string "abcd" if i = 1, and 2 if i = 2 - but I've not actually tried it.
Ross.
x := lookupz(3 : i++, suba, y /= 5, subb, y *= 5)
The expressions i++, suba and y /=5 will all be evaluated, but subb and y *= 5 will not be. This is an undocumented feature of lookupz, so it is probably not important to emulate this feature.
I've tried to leave the Windows world behind and am using BST. Is there any way that this might be provided for other OSes?
Oh wow, that's interesting. It also implies a completely different way of implementing lookup. I should probably look into it -- it's been my experience that if there's a weird undocumented feature of the Spin interpreter, then some program somewhere uses it :-).
Thanks,
Eric
The source actually compiles under Linux (that's my "native" platform) and it's pretty generic code, so it should work on the Mac, too. If you're comfortable with the Linux or Mac command line, just pull down the source and type "make" in the spin2cpp directory. I should get around to posting a Linux binary one of these days, I just haven't had time.
(Oh, and the Windows version works fine under Wine on Linux as well, except that the --elf option looks for propeller-elf-gcc.exe in the emulated Windows drive.)
Eric
But, I just see a couple of zip files for the Windows version, not the source code.
I did manage to locate the source code and print a copy, but an unsure of how to go about downloading the actual file. The web site seems to insist that I 'clone' your site and I am not sure what that might lead to.
So I'll use the .exe for now.
Linux is an open source operating system with a Unix-like heritage. It means that a source distribution should be able to compile on the computer assuming a minimum set of tools (make and gcc).
Cloning means that you need a source control tool to get the source. Once you have the source, you can build the tool. That is all.
What Linux do you use? I could probably make an application .zip or .tar for you.
Cheers,
Eric
But if you actually are not bothered by this, I use Ubuntu Linux. I am sure there will be others that may be more timid about using 'make' to generate a program. But that is a little absurd since we are taking about a tool for learning C and C++ so that we may do that compile process.
Thanks for your concern. I will take a look at the Linux .zip