fastspin C compiler

124

Comments

  • ersmith wrote: »
    Hmmm, that was an interesting bug -- the bytes[] array was being kept in a register because it was only 4 bytes long, instead of being put on the stack. I think that should be fixed now; the listing looks more reasonable, at least. For me I'm currently seeing that variables aren't being set; when I do "a=4" it doesn't seem to change the value of a.

    Thanks,
    Eric
    BTW, even though it is possible to type immediate statements and have them executed, I don't think that feature is completely implemented yet. So if you type "a=1" as an immediate statement followed by "print a" you'll likely see a zero printed since the value of "a" was not remembered from the previous immediate statement. I should fix that or remove the immediate execution feature.

  • jmgjmg Posts: 13,261
    David Betz wrote: »
    The latest version of fastspin in GitHub seems to work quite well! I've pushed the latest sources to GitHub.
    Nice progress.
    Do you have a simple summary of ebasic3 - like lines of code compiled, compile times, code binary size, memory footprints, and speeds for the PC-targeting version, and the P2-targeting version ?
    ( I expect those may change/improve over time, so maybe in a new thread ?)
    Could be useful to compare P2 vs PC code size/speeds.

  • Excellent! I'm pretty happy with the fastspin C coverage so far. Eric, your craftmanship is very much appreciated. I have most of a rudimentary SPI function working as of a couple of hours ago. David Betz, thank you as well for your concise feedback to Eric - bugs are being slayed left and right. Good stuff.

    Mike
  • jmg wrote: »
    Do you have a simple summary of ebasic3 - like lines of code compiled ?
    ebasic3 is about 5K lines of code. Actually, that includes comments and blank lines as well as actual code.
  • Is this a legal BASIC program? I wanted to print a value followed by a tab but not end with a newline. This works with basic but it seems like it should generate a syntax error. Is comma followed immediately by a semicolon allowed in other BASICs?
    list
    10  for y=1 to 5
    20  for x=1 to 5
    30  print x*y,;
    40  next x
    45  print
    50  next y
    OK
    run
    1	2	3	4	5	
    2	4	6	8	10	
    3	6	9	12	15	
    4	8	12	16	20	
    5	10	15	20	25	
    OK
    
  • pmrobert wrote: »
    Excellent! I'm pretty happy with the fastspin C coverage so far. Eric, your craftmanship is very much appreciated. I have most of a rudimentary SPI function working as of a couple of hours ago. David Betz, thank you as well for your concise feedback to Eric - bugs are being slayed left and right. Good stuff.
    Thanks! David's bug reports have been great and very helpful.

  • jmgjmg Posts: 13,261
    David Betz wrote: »
    Is this a legal BASIC program?

    This compiles in FreeBasic
    dim as integer x, y
    
     for y=1 to 5
      for x=1 to 5
       print x*y,;
      next x
      print
     next y
     sleep
    

    produces
     1             2             3             4             5
     2             4             6             8             10
     3             6             9             12            15
     4             8             12            16            20
     5             10            15            20            25
    
  • David Betz wrote: »
    Is this a legal BASIC program? I wanted to print a value followed by a tab but not end with a newline. This works with basic but it seems like it should generate a syntax error. Is comma followed immediately by a semicolon allowed in other BASICs?
    It looks like freebasic accepts it if you give it the -lang qb option (otherwise it wants all the variables dim'd).

    fastspin doesn't like it. Both fastspin and freebasic will do what you want if you end the print with just a comma:
      print x*y,
    
    which is like ending it with a semicolon in that no newline is produced, but it does produce a tab.

    I guess I should update the fastspin BASIC parser to accept having both comma and semicolon at the end of a line.
  • ersmith wrote: »
    David Betz wrote: »
    Is this a legal BASIC program? I wanted to print a value followed by a tab but not end with a newline. This works with basic but it seems like it should generate a syntax error. Is comma followed immediately by a semicolon allowed in other BASICs?
    It looks like freebasic accepts it if you give it the -lang qb option (otherwise it wants all the variables dim'd).

    fastspin doesn't like it. Both fastspin and freebasic will do what you want if you end the print with just a comma:
      print x*y,
    
    which is like ending it with a semicolon in that no newline is produced, but it does produce a tab.

    I guess I should update the fastspin BASIC parser to accept having both comma and semicolon at the end of a line.
    I just tried it and ebasic3 also accepts the line with just a comma at the end and does the right thing.
  • Well, ebasic3 doesn't completely work yet. This program just hangs and doesn't print anything.
    10  def fact(n)
    20    if n = 1 then
    25       return 1
    30    else
    35       return fact(n-1) * n
    37.   end if
    40  end def
    50  for i=1 to 10
    60    print fact(i)
    70  next I
    
  • The problem seems to be in compiling a function call. It doesn't look like this code works as expected. It seems to generate an improperly terminated argument list.
    /* ParseCall - parse a function or subroutine call */
    static ParseTreeNode *ParseCall(ParseContext *c, ParseTreeNode *functionNode)
    {
        ParseTreeNode *node = NewParseTreeNode(c, NodeTypeFunctionCall);
        ExprListEntry **pLast;
        int tkn;
    
        /* intialize the function call node */
        node->u.functionCall.fcn = functionNode;
        pLast = &node->u.functionCall.args;
    
        /* parse the argument list */
        if ((tkn = GetToken(c)) != ')') {
            SaveToken(c, tkn);
            do {
                ExprListEntry *actual;
                actual = (ExprListEntry *)LocalAlloc(c, sizeof(ExprListEntry));
                actual->expr = ParseExpr(c);
                actual->next = NULL;
                *pLast = actual;
                pLast = &actual->next;
                ++node->u.functionCall.argc;
            } while ((tkn = GetToken(c)) == ',');
            Require(c, tkn, ')');
        }
    
        /* return the function call node */
        return node;
    }
    
  • ersmith,
    I grabbed version 1.3.9 of spin2gui, and started attempting to compile simpletools inside of the Simple-Libraries set. Here's the initial things I ran into:
    // lib includes needed:
    
    #include <driver.h>
    #include <sys/stat.h>
    #include <dirent.h>
    #include <sys/sd.h>
    
    // need __attribute__() on functions, and format attribute
    
    error from spin2gui compile:
    C:/Github/Simple-Libraries/Learn/Simple Libraries/TextDevices/libsimpletext/simpletext.h(309) error: syntax error, unexpected identifier `__attribute__', expecting ',' or ';'
    
    Line 309 of simpletext.h:
    int print(const char *format, ...) __attribute__((format (printf, 1, 2)));
    

    Additionally, everything assumes SimpleIDE's feature that does automatic pathing/finding of Simple-Libraries includes and also auto linking of any libraries.

    So doing this in the source file:
    #include "simpletext.h"
    
    Automatically finds simpletext.h in the Simple-Libraries/TextDevices/libsimpletext/ folder. All of the includes are buried in folders like this, there isn't one path with all the includes.
    That include also causes the compile to automatically link in libsimpletext.a which is prebuilt in the library folders along with the includes.
    The source for the libraries are there also, but they are built separately and individually. Also, note, that most of the Simple-Libraries implementations are split into many c files, almost but not quite one c file per function for some of them. Sadly, it's not consistent.

    So, if fastspin allows it, I could give it all the separate include paths ( all 47 of them ), but that still leaves the linking issue. I'm not sure I want to go edit every single include file with the implementation info for each function in them. I assume your _IMPL() thing can handle the cases where one c file has several functions in it and all of them are in an include so need _IMPL()'s to the same file? I mean I am pretty sure this would work, but... I think I need to make a script or something that will do this for me.

    I can't remember if you were planning on having linking in the future or not?



  • David Betz wrote: »
    The problem seems to be in compiling a function call. It doesn't look like this code works as expected. It seems to generate an improperly terminated argument list.

    I'm not sure what could be wrong there. Do you think one of the unions might not be working? In general I would suspect the C specific parts of fastspin (struct and union handling, nested local variables, and varargs, for example) before other parts, since those are better tested. Although it might not hurt to try compiling with -O0 to turn off the optimizer; that's probably the second most suspicious part of fastspin.

    Thanks,
    Eric
  • Roy Eltham wrote: »
    // lib includes needed:
    #include <driver.h>
    #include <sys/stat.h>
    #include <dirent.h>
    #include <sys/sd.h>
    
    I'm guessing that you could comment those out (or put #ifndef __FLEXC__ around them). I doubt very many of the simple libraries use those features, so you could probably make progress on, say, the text libraries without them. (I suspect it's only the SD card that will need sys/sd.h and dirent.h, for example).
    // need __attribute__() on functions, and format attribute
    
    __attribute__ is a GCC specific extension which most compilers don't have, so we should add something like:
    #ifndef __GNUC__
    #define __attribute__(x) 
    #endif
    
    to the top of files that use it.
    Additionally, everything assumes SimpleIDE's feature that does automatic pathing/finding of Simple-Libraries includes and also auto linking of any libraries.

    So doing this in the source file:
    #include "simpletext.h"
    
    Automatically finds simpletext.h in the Simple-Libraries/TextDevices/libsimpletext/ folder. All of the includes are buried in folders like this, there isn't one path with all the includes.
    So the simple libraries only work with SimpleIDE, and rely on SimpleIDE to find the paths? That's unfortunate. How does SimpleIDE pass the include and library paths to the compiler? Does it just add appropriate -I and -L command line options?

    I wonder how hard it would be to get SimpleIDE to work with fastspin instead of GCC?

    Alternatively, I guess another path forward would be to first get the libraries to work with the command line version of PropGCC. Normally the way one would do this kind of thing would be to have a top level include file like "simplelibrary.h" located in the Simple-Libraries folder, which would then have relative includes like:
    #include "TextDevices/libsimpletext/simpletext.h"
    #include "OtherDevices/libotherdevice/otherdevice.h"
    
    Then the app would just include that one header file, and the compiler only needs one include path (at the root of Simple-Libraries).
    I'm not sure I want to go edit every single include file with the implementation info for each function in them. I assume your _IMPL() thing can handle the cases where one c file has several functions in it and all of them are in an include so need _IMPL()'s to the same file? I mean I am pretty sure this would work, but... I think I need to make a script or something that will do this for me.
    Yes, having multiple functions in the same file is fine, _IMPL will only load the file once in that case (and fastspin will remove any unused functions).

    The whole _IMPL() mechanism is kind of an attempt to have the automatic linking feature that newbies seem to expect -- that is, as long as you #include the appropriate header file the library functions will automatically get linked to the application, rather than PropGCC's way of requiring both the header file and the library file to be given. Kind of like what SimpleIDE does, but at the compiler level instead of the IDE level. I agree it is a pain to add the _IMPL() lines, but I hope it only has to be done once.
    I can't remember if you were planning on having linking in the future or not?
    No plans to, although we may find we have to.

    Thanks,
    Eric
  • David BetzDavid Betz Posts: 13,229
    edited 2019-02-27 - 13:19:36
    I wrote a Makefile to build Simple Libraries a while ago. I'm sure it isn't up to date but it worked at the time. I've attached it here.

    Eric: I wonder if you might consider adding a feature to fastspin to accept a separate file that lists the mappings between function names and source files. This would be sort of a poor man's library facility. The code itself wouldn't be in the file, just information about where to find it. Something like this:
    memset: lib/string/memset.c
    fopen: lib/stdio/fopen.c
    

    The advantage of this would be to not have to edit every header file to add the _IMPL macro calls.
  • Roy, now I understand the "why" of having to explicitly include every include and library path when using the Simple libs, thank you. Eric,
    How does SimpleIDE pass the include and library paths to the compiler? Does it just add appropriate -I and -L command line options?
    in order to use VS Code as my editor of choice a batch file needed to be created. After much hair pulling and dead ends I ended up just copy/pasting the command line issued by SimpleIDE for the compile step and slightly modding it. But it does appear that each -I and -L path needs to be passed to the compiler.
    propeller-elf-gcc.exe -I . -L . -I ./SimpleLibraries/Utility/libsimpletools -L ./SimpleLibraries/Utility/libsimpletools/%memmodel%/ 
    -I ./SimpleLibraries/TextDevices/libsimpletext -L ./SimpleLibraries/TextDevices/libsimpletext/%memmodel%/ 
    -I ./SimpleLibraries/Protocol/libsimplei2c -L ./SimpleLibraries/Protocol/libsimplei2c/%memmodel%/ 
    -I ./SimpleLibraries/TextDevices/libfdserial -L ./SimpleLibraries/TextDevices/libfdserial/%memmodel%/ 
    -o %target%-%memmodel%.elf %optimization% -m%memmodel% %fcache% -Wall -m32bit-doubles -fno-exceptions -std=c99 
    -ffunction-sections -fdata-sections -Wl,--gc-sections -ffunction-sections %target%.c 
    -lsimpletools -lsimpletext -lsimplei2c -lfdserial -lsimpletools -lsimpletext -lsimplei2c -lsimpletools -lsimpletext -lsimpletools -ltiny
    
    Another thing to be aware of is that one or two of the directory names have embedded spaces - "Text Devices" for one and there may have been
    another.
  • Another way to handle the simple libraries is to just create include and lib directories that contain all the *.h and library files.
  • ersmith,
    I like your method better for the "auto linking". I especially like David Betz idea above to have a separate file with the mappings.

  • jmgjmg Posts: 13,261
    When browsing for other stuff, I came across this on github..

    https://github.com/OpenNuvoton/NuMicroPy/tree/master/M48x

    Could be a good c-source test for the compiler ?

    Says:
    Supported target
    Board	                MCU	ROM size    RAM size
    NuMaker-PFM-M487	M487	315kB	    76kB
    MCU Size                M487    512kF       160kR 
    

    So it is large, but should? fit into a P2 ?
  • jmg wrote: »
    When browsing for other stuff, I came across this on github..

    https://github.com/OpenNuvoton/NuMicroPy/tree/master/M48x

    Could be a good c-source test for the compiler ?

    Says:
    Supported target
    Board	                MCU	ROM size    RAM size
    NuMaker-PFM-M487	M487	315kB	    76kB
    MCU Size                M487    512kF       160kR 
    

    So it is large, but should? fit into a P2 ?
    I think Tubular is already in contact with the author of MicroPython. There is talk about creating a port that makes use of XBYTE and friends to create a fast interpreter. I've looked at the source myself but decided to start with ebasic3 since I already know how it works and it is much smaller than MicroPython. I figure if I can get it working with fastspin then I can attempt MicroPython as a second step. That is unless Tubular gets it done before then!
  • jmgjmg Posts: 13,261
    David Betz wrote: »
    I think Tubular is already in contact with the author of MicroPython. There is talk about creating a port that makes use of XBYTE and friends to create a fast interpreter. I've looked at the source myself but decided to start with ebasic3 since I already know how it works and it is much smaller than MicroPython. I figure if I can get it working with fastspin then I can attempt MicroPython as a second step. That is unless Tubular gets it done before then!

    Well, yes, smaller steps are always a good idea, as is working with something you already know... :) At least the ROM/RAM they report are useful pegs in the sand..

  • jmg wrote: »
    David Betz wrote: »
    I think Tubular is already in contact with the author of MicroPython. There is talk about creating a port that makes use of XBYTE and friends to create a fast interpreter. I've looked at the source myself but decided to start with ebasic3 since I already know how it works and it is much smaller than MicroPython. I figure if I can get it working with fastspin then I can attempt MicroPython as a second step. That is unless Tubular gets it done before then!

    Well, yes, smaller steps are always a good idea, as is working with something you already know... :) At least the ROM/RAM they report are useful pegs in the sand..
    It will be quite cool and very interesting to Parallax if someone were to get MicroPython working on the P2!

  • I'm still trying to track down what's going wrong with my ParseCall function. I added some debug output and got some very strange results. Here is the instrumented code. The idea is to print out the argument list right after constructing it. It's a simple linked list with a structure containing two fields: expr and next.
    /* ParseCall - parse a function or subroutine call */
    static ParseTreeNode *ParseCall(ParseContext *c, ParseTreeNode *functionNode)
    {
        ParseTreeNode *node = NewParseTreeNode(c, NodeTypeFunctionCall);
        ExprListEntry **pLast;
        int tkn;
    
        /* intialize the function call node */
        node->u.functionCall.fcn = functionNode;
        pLast = &node->u.functionCall.args;
    
        /* parse the argument list */
        if ((tkn = GetToken(c)) != ')') {
            SaveToken(c, tkn);
            do {
                ExprListEntry *actual;
                actual = (ExprListEntry *)LocalAlloc(c, sizeof(ExprListEntry));
                actual->expr = ParseExpr(c);
                actual->next = NULL;
                *pLast = actual;
                pLast = &actual->next;
                ++node->u.functionCall.argc;
            } while ((tkn = GetToken(c)) == ',');
            Require(c, tkn, ')');
        }
    { ExprListEntry *e = node->u.functionCall.args;
      while (e) {
        VM_printf("entry %08x, expr %08x, next %08x\n", e, e->expr, e->next);
        e = e->next;
        VM_printf("e %08x\n", e);
      }
    }
    
        /* return the function call node */
        return node;
    }
    
    Here is what was printed when I typed "print foo(3)" into the interpreter:
    entry 0000B8F0, expr 0000B8E0, next 00000000
    e 0000000d
    entry 0000000d, expr 00000000, next 0
    p %%%%
    e 0
    p  00entry 0
    p  00e 0CD4E48D
    entry 0CD4E48D, expr 0DC301E0, next 0C458573
    e 0C45856F
    entry 0C45856F, expr 02576C2C, next 2A932226
    e 2A932222
    entry 2A932222, expr 6BF08DA4, next 480F2923
    e 480F291F
    entry 480F291F, expr 00000000, next 00000000
    e 0000000d
    
    It then repeats that over and over. The odd thing is that the first entry looks pretty good. The "next" pointer is zero indicating the end of the list. The odd part though is that "e" is printed out as 0000000d. But if you look at the code, e is just set to the next pointer that printed as zero in the immediately preceding printf call. It looks like "e = e->next" is not working as expected.
  • ersmith,
    Did some of your suggestions to get farther along in compiling things, and ran into this:

    This is the fastspin error:
    C:/Github/Simple-Libraries/Learn/Simple Libraries/TextDevices/libsimpletext/simpletext.h(1064) error: Internal error: unknown type 125 passed to TypeSize
    

    This is a function declared near the end of simpletext.h
    static inline void simpleterm_set(text_t *ptr)
    {
      extern text_t *dport_ptr;      // <-- this is line 1064 in simpletext.h
      simpleterm_close();
      dport_ptr = ptr;
    }
    

    dport_ptr is instanciated in another c file.

    This is text_t defined near the top of simpletext.h
    typedef struct text_struct
    {
      /** Pointer to text device library's character receiving function. */
      int  (*rxChar)(struct text_struct *p);         
      /** Pointer to text device library's character transmitting function. */
      int  (*txChar)(struct text_struct *p, int ch); 
      /** Pointer to text device library's cog variable(s). */      
      int  cogid[TERM_COG_LEN];                      
      /** Pointer to text device library's info. */ 
      volatile void *devst;                          
      /** Echo setting, typically for usage with a terminal. */ 
      volatile int terminalEcho;                          
      /** List of end characters. */ 
      //char ec[3];                          
      /** End character sequence when an end character is encountered. */ 
      //char ecs[3];                          
      volatile char ecA;
      volatile char ecB;
      volatile char ecsA;
      volatile char ecsB;
    } text_t;
    


    I assume it's related to the "extern" keyword. Since you don't link, you don't really have extern stuff, right? Piles of this Simple Libraries code assumes things built in libs and using extern to "see" them and it all gets fixed up when linking.
  • jmg wrote: »
    When browsing for other stuff, I came across this on github..

    https://github.com/OpenNuvoton/NuMicroPy/tree/master/M48x

    Could be a good c-source test for the compiler ?

    Says:
    Supported target
    Board	                MCU	ROM size    RAM size
    NuMaker-PFM-M487	M487	315kB	    76kB
    MCU Size                M487    512kF       160kR 
    

    So it is large, but should? fit into a P2 ?
    I exchanged some messages with Tubular and it seems the MicroPython project is already in progress. I think I need to come up with a different plan for my P2 work. There is really no point in getting ebasic3 working since it was only supposed to be a stepping stone to MicroPython.

  • jmgjmg Posts: 13,261
    David Betz wrote: »
    I exchanged some messages with Tubular and it seems the MicroPython project is already in progress. I think I need to come up with a different plan for my P2 work. There is really no point in getting ebasic3 working since it was only supposed to be a stepping stone to MicroPython.

    Anything that speeds knocking bugs out of the P2 C compiler path, has to be very useful for all other P2 work :)

  • jmg wrote: »
    David Betz wrote: »
    I exchanged some messages with Tubular and it seems the MicroPython project is already in progress. I think I need to come up with a different plan for my P2 work. There is really no point in getting ebasic3 working since it was only supposed to be a stepping stone to MicroPython.

    Anything that speeds knocking bugs out of the P2 C compiler path, has to be very useful for all other P2 work :)
    Yeah but I think ebasic3 is too complicated to be useful as a test program. There must be a C test suite somewhere we could run.

  • @David Betz : weird that it's able to print e->next correctly (using a varargs call even) but is getting e = e->next wrong. That's very odd indeed. I haven't had a chance t look further at it, sorry, but if you're feeling adventurous you could try looking at the generated assembly. I guess one slightly odd thing is that the structs don't seem to be aligned on longword boundaries. I know this isn't necessary on P2, but it is on P1 and so fastspin generally pads structs out to multiples of 4 bytes. Perhaps these structs are dynamically allocated, but again, one would expect the code to use sizeof() to figure out the size of the structures.

    @Roy Eltham : You've found a bug in the parsing of variable declarations inside functions. I'll try to fix that soon, but in the meantime hoisting any "extern" declarations outside of the function should get you going further.

    Thanks,
    Eric
  • ersmith wrote: »
    Perhaps these structs are dynamically allocated
    Nope. No calls to malloc in ebasic3. It's all static allocation. I'll take your advice and look at the generated code. It will give me some practice understanding P2 assembly code.

  • David Betz wrote: »
    ersmith wrote: »
    Perhaps these structs are dynamically allocated
    Nope. No calls to malloc in ebasic3. It's all static allocation.
    Sorry, I wasn't clear there... I meant dynamically allocated by ebasic3's internal memory allocation routines (I'm pretty sure it does have some kind of allocator acting on a static heap, doesn't it?)


Sign In or Register to comment.