Shop OBEX P1 Docs P2 Docs Learn Events
New CAST instruction for Spin2 - Page 5 — Parallax Forums

New CAST instruction for Spin2

12357

Comments

  • cgraceycgracey Posts: 14,133
    edited 2019-05-23 23:59
    jmg wrote: »
    cgracey wrote: »
    jmg wrote: »
    Do you plan to do a compact CASE that uses DECOD and SKIP opcode(s) ?
    It is common for there to be rather less than 256 choices so one that supports up to 32 would be useful, and I think can be smaller again ?

    No plans. For contiguous cases, this only uses 1 word per case branch. I don't think it could get any smaller, or faster.

    There is quite a bit of housekeeping in the table preamble, whilst I expected DECOD and SKIP/SKIPF to largely work in 2~3 lines plus up to 32 jumps. Did I miss something ?

    Remember that we are directing bytecode streams to the interpreter, so we can't use SKIP or anything. For compiled-to-PASM code, SKIP would be appropriate.
  • cgraceycgracey Posts: 14,133
    edited 2019-05-24 00:00
    ersmith wrote: »
    cgracey wrote: »
    This is a real pain in 80386 and it's made worse by this code being recursively called, making it necessary to implement some software stack to augment the limited register set. This would be so much easier to do in Spin2, or any high-level language. In assembly, you've often got to type a lot to make things like conditional sequences, anyway.

    You're planning to port the compiler to Spin2 eventually anyway, aren't you? So why not do that now, and save yourself the trouble of doing all this 80386 programming?

    At this point, I only have three trivial things on my checklist to finish, and all the hard work is done. So, I"m 97% there, already. And I need some functional version In which to work, in any case.
  • cgracey wrote: »
    ersmith wrote: »
    cgracey wrote: »
    This is a real pain in 80386 and it's made worse by this code being recursively called, making it necessary to implement some software stack to augment the limited register set. This would be so much easier to do in Spin2, or any high-level language. In assembly, you've often got to type a lot to make things like conditional sequences, anyway.

    You're planning to port the compiler to Spin2 eventually anyway, aren't you? So why not do that now, and save yourself the trouble of doing all this 80386 programming?

    At this point, I only have three trivial things on my checklist to finish, and all the hard work is done. So, I"m 97% there, already. And I need some functional version In which to work, in any case.

    My compiler is functional :)
  • cgraceycgracey Posts: 14,133
    ersmith wrote: »
    cgracey wrote: »
    ersmith wrote: »
    cgracey wrote: »
    This is a real pain in 80386 and it's made worse by this code being recursively called, making it necessary to implement some software stack to augment the limited register set. This would be so much easier to do in Spin2, or any high-level language. In assembly, you've often got to type a lot to make things like conditional sequences, anyway.

    You're planning to port the compiler to Spin2 eventually anyway, aren't you? So why not do that now, and save yourself the trouble of doing all this 80386 programming?

    At this point, I only have three trivial things on my checklist to finish, and all the hard work is done. So, I"m 97% there, already. And I need some functional version In which to work, in any case.

    My compiler is functional :)

    It's a lot smarter, too.
  • I’m late to the party, but how about PIVOT? It sort of suggests you have reached an intersection of many possible outcomes and need to pick one.

    Syntax:

    PIVOT 1 to 5
    1: routine_1
    2: routine_2
    3: routine_3
    4: routine_4
    5: routine_5
    OVER : routine_too_big
    UNDER : routine_too_small

    Wait! Dont hang up! :)
  • jmgjmg Posts: 15,148
    cgracey wrote: »
    jmg wrote: »
    cgracey wrote: »
    jmg wrote: »
    Do you plan to do a compact CASE that uses DECOD and SKIP opcode(s) ?
    It is common for there to be rather less than 256 choices so one that supports up to 32 would be useful, and I think can be smaller again ?

    No plans. For contiguous cases, this only uses 1 word per case branch. I don't think it could get any smaller, or faster.

    There is quite a bit of housekeeping in the table preamble, whilst I expected DECOD and SKIP/SKIPF to largely work in 2~3 lines plus up to 32 jumps. Did I miss something ?

    Remember that we are directing bytecode streams to the interpreter, so we can't use SKIP or anything. For compiled-to-PASM code, SKIP would be appropriate.

    Yes, but Spin2 compilers already can output PASM, so the keywords used, should make sense for all code-generation cases.
    ie a tag of FAST is a poor choice, as there may be faster variants in PASM, and how do you explain that in a manual ?
    I'm not seeing any problems with the already suggested CASE TABLE ?
  • RaymanRayman Posts: 13,898
    I'm back to thinking "JUMP" is best. Although similar to CASE, it's really different. If you call it CASE_anything, it will probably just cause confusion...
  • cgracey wrote: »
    ersmith wrote: »
    cgracey wrote: »
    ersmith wrote: »
    cgracey wrote: »
    This is a real pain in 80386 and it's made worse by this code being recursively called, making it necessary to implement some software stack to augment the limited register set. This would be so much easier to do in Spin2, or any high-level language. In assembly, you've often got to type a lot to make things like conditional sequences, anyway.

    You're planning to port the compiler to Spin2 eventually anyway, aren't you? So why not do that now, and save yourself the trouble of doing all this 80386 programming?

    At this point, I only have three trivial things on my checklist to finish, and all the hard work is done. So, I"m 97% there, already. And I need some functional version In which to work, in any case.

    My compiler is functional :)

    It's a lot smarter, too.

    Well, why don't you use it then? By all means develop your own compiler -- there's a lot of benefit to be had from a self-hosted solution, it'll be nice to have. But you could bootstrap it using another Spin2 compiler and save yourself all the headache of writing in x86 assembly language. If you have to port the compiler to Spin2 eventually anyway (which you will in order to get it to run on P2) then why not do that sooner rather than later?

    Or, if your compiler is 97% finished, why not use it to compile a "version 1" Spin2 compiler which could then be used to create "version 2" that is complete? CASE_FAST, for example, could have been implemented in the x86 assembler as just an alias for CASE, and then the "proper" version implemented in Spin2 for the version 2 compiler.
  • Rayman wrote: »
    I'm back to thinking "JUMP" is best. Although similar to CASE, it's really different. If you call it CASE_anything, it will probably just cause confusion...

    CASE_FAST and CASE have the exact same meaning in terms of what they do. The only difference is in the implementation underneath, i.e. in the timing of the code, and in some restrictions CASE_FAST imposes on the labels to make that implementation possible.

    I'm still not convinced CASE_FAST is really necessary (users that really need high performance are going to write in PASM or use a PASM producing compiler anyway) but it is basically the same as CASE, so naming it that way makes sense.
  • kwinnkwinn Posts: 8,697
    Instead of adding something after CASE why not TCASE to indicate that it is a Table CASE.
  • kwinn wrote: »
    Instead of adding something after CASE why not TCASE to indicate that it is a Table CASE.

    Why add a keyword that is so close to one that already exists for something that programmatically achieves the same thing, via a different mechanism, rather than adding a modifier keyword?
  • cgraceycgracey Posts: 14,133
    AJL wrote: »
    kwinn wrote: »
    Instead of adding something after CASE why not TCASE to indicate that it is a Table CASE.

    Why add a keyword that is so close to one that already exists for something that programmatically achieves the same thing, via a different mechanism, rather than adding a modifier keyword?

    A modifier key word would be better, but the problem is that we steal an important name away from the user. Something like TABLE or FAST are symbols that the user might want to use. If we make one of those into a key word, they won't be able to ever call anything that, themselves.
  • cgraceycgracey Posts: 14,133
    edited 2019-05-24 21:48
    ersmith wrote: »
    Rayman wrote: »
    I'm back to thinking "JUMP" is best. Although similar to CASE, it's really different. If you call it CASE_anything, it will probably just cause confusion...

    CASE_FAST and CASE have the exact same meaning in terms of what they do. The only difference is in the implementation underneath, i.e. in the timing of the code, and in some restrictions CASE_FAST imposes on the labels to make that implementation possible.

    I'm still not convinced CASE_FAST is really necessary (users that really need high performance are going to write in PASM or use a PASM producing compiler anyway) but it is basically the same as CASE, so naming it that way makes sense.

    There is always a need for a random branch without deliberation. And it needs to be invoked with some deliberateness so that checks can be applied to assure that the programmer is getting what he expects. Otherwise, some subtle change to his code might disqualify it from the fast implementation, without any warning (or awareness of his intent), and his code is suddenly running 100x slower.
  • It seems to me that the compiler could issue a warning about case structures that do not meet the requirements, or the IDE could colorize those that do. I still don't see a need for a separate keyword.

    But, for now, I rest my case. :)

    -Phil
  • cgraceycgracey Posts: 14,133
    ersmith wrote: »
    cgracey wrote: »
    ersmith wrote: »
    cgracey wrote: »
    ersmith wrote: »
    cgracey wrote: »
    This is a real pain in 80386 and it's made worse by this code being recursively called, making it necessary to implement some software stack to augment the limited register set. This would be so much easier to do in Spin2, or any high-level language. In assembly, you've often got to type a lot to make things like conditional sequences, anyway.

    You're planning to port the compiler to Spin2 eventually anyway, aren't you? So why not do that now, and save yourself the trouble of doing all this 80386 programming?

    At this point, I only have three trivial things on my checklist to finish, and all the hard work is done. So, I"m 97% there, already. And I need some functional version In which to work, in any case.

    My compiler is functional :)

    It's a lot smarter, too.

    Well, why don't you use it then? By all means develop your own compiler -- there's a lot of benefit to be had from a self-hosted solution, it'll be nice to have. But you could bootstrap it using another Spin2 compiler and save yourself all the headache of writing in x86 assembly language. If you have to port the compiler to Spin2 eventually anyway (which you will in order to get it to run on P2) then why not do that sooner rather than later?

    Or, if your compiler is 97% finished, why not use it to compile a "version 1" Spin2 compiler which could then be used to create "version 2" that is complete? CASE_FAST, for example, could have been implemented in the x86 assembler as just an alias for CASE, and then the "proper" version implemented in Spin2 for the version 2 compiler.

    The reason I don't want to use another compiler is because I'm not even sure of where I'm going, exactly, and I need total flexibility, at this point. Things form up as I proceed and I can't predict what direction they'll take. Anything is possible, still.
  • jmgjmg Posts: 15,148
    cgracey wrote: »
    The reason I don't want to use another compiler is because I'm not even sure of where I'm going, exactly, and I need total flexibility, at this point. Things form up as I proceed and I can't predict what direction they'll take. Anything is possible, still.
    Yes, but the Spin2 language define is largely done, this detail level of which versions of case code emits in the background, are not really compiler-dependent, at all.

  • cgraceycgracey Posts: 14,133
    edited 2019-05-25 01:47
    jmg wrote: »
    cgracey wrote: »
    The reason I don't want to use another compiler is because I'm not even sure of where I'm going, exactly, and I need total flexibility, at this point. Things form up as I proceed and I can't predict what direction they'll take. Anything is possible, still.
    Yes, but the Spin2 language define is largely done, this detail level of which versions of case code emits in the background, are not really compiler-dependent, at all.

    There are plenty of other issues, like function pointers, debugging, and in-cog terminate-stay-resident PASM that are fluid, at this point. A lot of it has to do with there being an interpreter.
  • cgracey wrote: »
    jmg wrote: »
    cgracey wrote: »
    The reason I don't want to use another compiler is because I'm not even sure of where I'm going, exactly, and I need total flexibility, at this point. Things form up as I proceed and I can't predict what direction they'll take. Anything is possible, still.
    Yes, but the Spin2 language define is largely done, this detail level of which versions of case code emits in the background, are not really compiler-dependent, at all.

    There are plenty of other issues, like function pointers, debugging, and in-cog terminate-stay-resident PASM that are fluid, at this point. A lot of it has to do with there being an interpreter.

    The language you write the compiler in doesn't have to be exactly the same as the one the compiler accepts. (Indeed, right now they're very far apart, since you're using x86 assembler!). Making all these changes would be easier in a high level language, wouldn't it? If you wrote in, say, Spin1, it would be much easier to port to Spin2 than porting x86 code to Spin2, and it would also be much easier to make changes to the compiler. At least, that's what I would find.

  • kwinnkwinn Posts: 8,697
    edited 2019-05-25 13:59
    AJL wrote: »
    kwinn wrote: »
    Instead of adding something after CASE why not TCASE to indicate that it is a Table CASE.

    Why add a keyword that is so close to one that already exists for something that programmatically achieves the same thing, via a different mechanism, rather than adding a modifier keyword?

    Why not when they are essentially performing the same function by a different mechanism? Why the fixation on placing the modifier after the keyword when we would call it a jump table case statement in everyday speech?
  • cgraceycgracey Posts: 14,133
    ersmith wrote: »
    cgracey wrote: »
    jmg wrote: »
    cgracey wrote: »
    The reason I don't want to use another compiler is because I'm not even sure of where I'm going, exactly, and I need total flexibility, at this point. Things form up as I proceed and I can't predict what direction they'll take. Anything is possible, still.
    Yes, but the Spin2 language define is largely done, this detail level of which versions of case code emits in the background, are not really compiler-dependent, at all.

    There are plenty of other issues, like function pointers, debugging, and in-cog terminate-stay-resident PASM that are fluid, at this point. A lot of it has to do with there being an interpreter.

    The language you write the compiler in doesn't have to be exactly the same as the one the compiler accepts. (Indeed, right now they're very far apart, since you're using x86 assembler!). Making all these changes would be easier in a high level language, wouldn't it? If you wrote in, say, Spin1, it would be much easier to port to Spin2 than porting x86 code to Spin2, and it would also be much easier to make changes to the compiler. At least, that's what I would find.

    I don't know. This is the only way I feel confident getting the job done, since I know my code base pretty well. Well, it took a month to get thoroughly reacquainted with it, but I'm there now. I wouldn't want to start off completely from scratch, making all the parser stuff over again. Right now I'm getting in-line PASM working. It's not that complicated, thankfully. After that, I just have to work out the Spin2-level COGNEW, which is way simpler than Spin1 was, since we now have code to handle function pointers. I'm almost there. Soon, I'll be able to try the compiler and interpreter out together. Just another day, or two.
  • kwinn wrote: »
    AJL wrote: »
    kwinn wrote: »
    Instead of adding something after CASE why not TCASE to indicate that it is a Table CASE.

    Why add a keyword that is so close to one that already exists for something that programmatically achieves the same thing, via a different mechanism, rather than adding a modifier keyword?

    Why not when they are essentially performing the same function by a different mechanism? Why the fixation on placing the modifier after the keyword when we would call it a jump table case statement in everyday speech?

    Yes, in English syntax you would put the modifier first. In many other languages you put the modifier after the noun. All of that is somewhat moot, as what matters is how the parser is scanning. Putting the modifier after CASE can simplify the parser logic, while keeping the same keyword for the programmer. This also supports the programmer being able to experiment with different versions more readily, by adding or removing the modifier, rather than changing the single keyword.

    As it stands, Chip appears to prefer the idea of a modifier keyword, but doesn't like the namespace limits it applies.

    Would these limits apply if the modifier was placed after the variable?

    e.g.
    Normal use
    CASE VARNAME
    0: example
    1: otherexample
    

    Table use
    CASE VARNAME TABLE
    0: example
    1: otherexample
    
  • kwinnkwinn Posts: 8,697
    Any of these ideas would work. Chip also likes to keep the keywords short, which I agree with to a point. I really don't see how adding another keyword (TCASE or CASET) would complicate parsing, and I'm pretty sure the average programmer would have no trouble remembering that a CASE with a T added in front or back is a table case. I am fairly certain that is why Chip wanted to call it CAST initially.

    I can recall working with an assembler back in the seventies that added a single character to the end of the assembly mnemonic to indicate the addressing mode, and found that to be much nicer to work with than 8080 assembly. Far fewer keywords to remember.
  • The latest release of fastspin (3.9.27) supports CASE_FAST. It will also automatically convert CASE to CASE_FAST if all the cases are constant and there are between 3 and 255 of them. C switch statements and BASIC select case are also converted to jump tables if this condition is satisfied.
  • jmgjmg Posts: 15,148
    ersmith wrote: »
    The latest release of fastspin (3.9.27) supports CASE_FAST. It will also automatically convert CASE to CASE_FAST if all the cases are constant and there are between 3 and 255 of them. C switch statements and BASIC select case are also converted to jump tables if this condition is satisfied.

    Sounds great, and should cover most common uses.
  • cgraceycgracey Posts: 14,133
    ersmith wrote: »
    The latest release of fastspin (3.9.27) supports CASE_FAST. It will also automatically convert CASE to CASE_FAST if all the cases are constant and there are between 3 and 255 of them. C switch statements and BASIC select case are also converted to jump tables if this condition is satisfied.

    Cool. A jump table with 256 cases would be, on average, probably 100x faster than sequential checks and branches. Plus, timing would be consistent.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2019-05-28 03:13
    Automatic promotion from case to case_fast should probably be accompanied by a reserved constant or compile-time pragma that specifies the maximum length of auto-promoted jump tables, particularly where large gaps appear between cases. If I were writing a program that included something like this:
    case x
      1: do_this
      5: do_that
      100: do_something else
    

    I would probably not want a 101-long jump table to accomplish it. (And yet, that is exactly what I was espousing earlier in this thread when I suggested that one keyword for all case structures was adequate. Now I see why it is not.)

    -Phil
  • CongaConga Posts: 51
    edited 2019-05-28 10:38
    Automatic promotion from case to case_fast should probably be accompanied by a reserved constant or compile-time pragma that specifies the maximum length of auto-promoted jump tables, particularly where large gaps appear between cases.

    Why not have support for arbitrary annotations in the Spin2 syntax?
    The annotations are directives or optimization-hints for the Spin2 compiler.

    Example for 'case' / 'case_fast' / 'case table':

    Using square brackets for compiler annotations:
    case expression [[table]]
        1: do_this
        ...
    

    Or using parens:
    case expression ((table))
        1: do_this
        ...
    

    This way 'table' or any other compiler annotation does not collide with identifiers,
    therefore 'table' does not need to be a keyword.

    Having the annotation at the end of the line allows easy disabling or enabling by adding or removing a comment character.
  • Automatic promotion from case to case_fast should probably be accompanied by a reserved constant or compile-time pragma that specifies the maximum length of auto-promoted jump tables, particularly where large gaps appear between cases. If I were writing a program that included something like this:
    case x
      1: do_this
      5: do_that
      100: do_something else
    

    I would probably not want a 101-long jump table to accomplish it. (And yet, that is exactly what I was espousing earlier in this thread when I suggested that one keyword for all case structures was adequate. Now I see why it is not.)

    Actually it seems like the density of the jump table (number of default entries / total number of entries) should be greater than some value before we do the conversion. For now I'll make a default of 1/2; that is, if more than half of the space between minvalue and maxvalue is used by real cases, then we'll use a jump table for CASE, otherwise go back to the old comparison method. Of course CASE_FAST will always use a jump table, if it can.

  • cgraceycgracey Posts: 14,133
    ersmith wrote: »
    Automatic promotion from case to case_fast should probably be accompanied by a reserved constant or compile-time pragma that specifies the maximum length of auto-promoted jump tables, particularly where large gaps appear between cases. If I were writing a program that included something like this:
    case x
      1: do_this
      5: do_that
      100: do_something else
    

    I would probably not want a 101-long jump table to accomplish it. (And yet, that is exactly what I was espousing earlier in this thread when I suggested that one keyword for all case structures was adequate. Now I see why it is not.)

    Actually it seems like the density of the jump table (number of default entries / total number of entries) should be greater than some value before we do the conversion. For now I'll make a default of 1/2; that is, if more than half of the space between minvalue and maxvalue is used by real cases, then we'll use a jump table for CASE, otherwise go back to the old comparison method. Of course CASE_FAST will always use a jump table, if it can.

    Yeah. I was thinking density would be the decider.

    I used a table of WORDs, since they can span 64KB, which the case code is unlikely to exceed. So, it's one word per table value. If you used a word table and were doing something like 'CMP + IF_Z JMP' to check cases, those two instructions would take 2 longs, or 4 words. So, a table implementation would still be as memory efficient at only 25% density.
  • Cluso99Cluso99 Posts: 18,069
    I wasn't thinking of density, but rather the requirement (as originally outlined in the first post) was for deterministic timing (and I read this to mean all cases will execute with the same timing.

    Now, on this assumption, I want to be able to enforce the compiler to do the table method regardless of size.

    BTW I know spin/spin2 will never be truly deterministic, but we should be able to get somewhat close.
Sign In or Register to comment.