constant directive
Dave Hein
Posts: 6,347
I don't understand the purpose for the constant directive.· Why shouldn't the Spin compiler automatically compute the value of a constant expression instead of requiring the constant directive to do it?· It will produce the same numeric result whether it is computed·at compile time or run time· -- it will just run slower if done at run time.
Comments
Thanks for your response.· There also seems to be an error on page 148 of the Propeller manual.· It contains the following text in red:
CON
· _xinfreq = 4096000
· Reset = %00100000
· Initialize = %00010000
· WakeUp = Reset & Initialize
Here, WakeUp is still set to %00110000 at compile time, but it is now more obvious to future readers that the WakeUp symbol contains the binary codes for a Reset and an Initialize sequence for that particular application.
The expression Reset & Initialize actually has a value of zero.· Is there a list of errata for version 1.1 of the Propeller manual?
Dave
x := 4 * 5 + 3 * y + 8 * 2
You start out with a constant so you get the * operator, another constant, good go go that's constant(20). Next operator is + which means you drop an order of precedence -- you put the 20 on the math stack (of the internal constant evaluator) and evaluate the next expression at multiply/div precedence. So you get 3, constant, good, times OOPS you can't use the constant evaluator. That's a var.
At this point if you bail to the start you lose the bona fide constant 4*5=20. But bailing back to the addition operation leaves you with a bit of a muddle; you need to write 20 to the runtime Spin math stack and go to the runtime interpreter, which converts spin to bytecode. So you'd need to write push 20, push 3, push y, do multiply, do add. Your next operation is add and you're in spin mode, so you get the next symbol. It's a constant, 8. What do you do with it? Write spin code to push the 8 or go back to constant mode to see what's next?
It's not impossible to make this work -- you might even see how to do it from what I just wrote, but it's VERY hard to debug. Whereas making us specify constant expressions takes all that complexity out of the proptool evaluator and gives that control to the programmer, who needs to be thinking about stuff like that anyway. Most interpreted languages don't provide a way to fold constant expressions at all; they just perform the operations at runtime. Usually you'd fold that yourself because you know 4*5 is always 20. But it might be useful for program readability to say something like bytespersector / directoryentrysize, where both of those are constants and you don't want a "magic number" making your code hard to understand. With the constant directive you can specify that single constant while leaving it clear where it came from, and letting the code self-adapt should you ever need to change one of the source values.
In this case, the folding is done almost at the bytecode level. The compiler compiles to an intermediate token for each operation, which is then assembled into bytecode. All bstc does is scan the tokens. It increments a counter every time it sees a constant, ignores operators and zeros the counter if it sees anything else. When the counter >= 2 it knows there is a constant expression that can be reduced. The precedence is handled by the math evaluator in the main compiler, so the constant folder does not have to worry about any of the finer details.
Here is your example above compiled straight out. Constant values in the listing are displayed in Hex.
And with constants folded
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Life may be "too short", but it's the longest thing we ever do.
Yep, I lost count of the complete rip-up and re-writes I went through when writing the bstc math evaluator. At least 6. I had plenty that worked, but would all give slightly different output than the Parallax evaluator, or fail in strange ways on odd corner cases.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Life may be "too short", but it's the longest thing we ever do.
-Phil
This is why my PropellerBasic code generator is taking so long to get written/debugged; not only do I do constant folding, but I am also trying to generate near-optimal code in one pass, without a peep hole optimizer to back me up (yet).
To make it even more challenging, I am not even using an expression stack!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
www.mikronauts.com E-mail: mikronauts _at_ gmail _dot_ com 5.0" VGA LCD in stock!
Morpheusdual Prop SBC w/ 512KB kit $119.95, Mem+2MB memory IO board kit $89.95, both kits $189.95
Propteus and Proteus for Propeller prototyping 6.250MHz custom Crystals run Propellers at 100MHz
Las - Large model assembler Largos - upcoming nano operating system
It is a bit more complicated if you don't use a compile stack, and are handling not only constants, but byte / word / long / float / string variables, arrays (of basic types), and functions.
I actually have a long full of flags for each element, I think I currently use 9 flags.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
www.mikronauts.com E-mail: mikronauts _at_ gmail _dot_ com 5.0" VGA LCD in stock!
Morpheusdual Prop SBC w/ 512KB kit $119.95, Mem+2MB memory IO board kit $89.95, both kits $189.95
Propteus and Proteus for Propeller prototyping 6.250MHz custom Crystals run Propellers at 100MHz
Las - Large model assembler Largos - upcoming nano operating system
x := 4 * 5 + 3 * y + 8 * 2
through
x := 20 + 3 * y + 16
to
x := 36 + 3 * y
-Phil
Post Edited (Phil Pilgrim (PhiPi)) : 1/23/2010 2:55:28 AM GMT
Ooooo. That's a challenge.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Life may be "too short", but it's the longest thing we ever do.
And that is EXACTLY how the expression parser in PropellerBasic does it.
Here is the code generated for that, assuming a declaration of "LOCAL x,y" (and adding the peep hole optimizer)
MOV mula,#3
MOV mulb,y
call #MUL32
MOV x,mulr
Mind you, it will only be that clean once the peep hole optimizer is done. For now, it is uglier:
mov t0,y
mov mula,#3
mov mulb,t0
call #MUL32
mov x,mulr
The reason it can load mulb with the constant is because there is no need for a temp register for a small constant.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
www.mikronauts.com E-mail: mikronauts _at_ gmail _dot_ com 5.0" VGA LCD in stock!
Morpheus dual Prop SBC w/ 512KB kit $119.95, Mem+2MB memory/IO kit $89.95, both kits $189.95 SerPlug $9.95
Propteus and Proteus for Propeller prototyping 6.250MHz custom Crystals run Propellers at 100MHz
Las - Large model assembler Largos - upcoming nano operating system
Err.. I can see a simple multiply by 3 there, but where are the other constants in the equation?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Life may be "too short", but it's the longest thing we ever do.
Sorry, I forgot the rest. I checked the compiler, here is how it actually handles it:
x := 4 * 5 + 3 * y + 8 * 2
turns into
x := 20 + 3*y + 16
mov mula,#3
mov mulb,y
call #MUL32
mov t0,mulr
add t0,#20
add t0,#16
mov x,t0
It is smart enough to load the source registers for the multiply subroutine with the constant and variable directly without using temporaries as long as the arguments to the multiply are not sub-expressions in parenthases, then it would be uglier. Unfortunately it is not smart enough to combine the two constants as there is a higher priority non-constant op between them.
Later I will add optimization code for the case of multiplying by a small constant, in which case the output will change to
mov t0, y
add t0, y
add t0, y
add t0,#20
add t0,#16
mov x,t0
and the peep hole optimizer will turn that into
mov t0, y
add t0, y
add t0, y
add t0,#36
mov x,t0
which is pretty good code for x := 4 * 5 + 3 * y + 8 * 2
a human (or smarter peep hole optimizer) could only improve it to
mov x, y
add x, y
add x, y
add x, #36
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
www.mikronauts.com E-mail: mikronauts _at_ gmail _dot_ com 5.0" VGA LCD in stock!
Morpheus dual Prop SBC w/ 512KB kit $119.95, Mem+2MB memory/IO kit $89.95, both kits $189.95 SerPlug $9.95
Propteus and Proteus for Propeller prototyping 6.250MHz custom Crystals run Propellers at 100MHz
Las - Large model assembler Largos - upcoming nano operating system
Yeah, I like the concept of a tree based evaluator. It's what I tried first as it just seemed so logical to me, but for the life of me I could *not* get it to generate bytecode like the Parallax compiler does. The code was perfectly valid, but just not identical.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Life may be "too short", but it's the longest thing we ever do.
-Phil
Well, that would make perfect sense. I ended up build a stack based evaluator which coincidentally happened to produce identical code to the Parallax compiler out of the box. Must have been a fluke, but it took lots of near misses to get there. Michael Park poked me in the direction of some articles on recursive descent after I asked him how he made homespun work.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Life may be "too short", but it's the longest thing we ever do.