View previous topic :: View next topic |
Author |
Message |
shimpossible
Joined: 27 Aug 2018 Posts: 6
|
Duff's device |
Posted: Tue Sep 11, 2018 8:01 pm |
|
|
There is this handy loop unrolling trick call a Duff's device
https://en.wikipedia.org/wiki/Duff%27s_device
It seems the CCS compiler doesn't support this. I get the error
"Error#51 A numeric expression must appear here." on the 2nd case
As far as I can tell its because the case statement is inside the do/while braces. All the case statements are constants so the error doesn't make sense.
Does the compiler just not support the nested case statements? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Wed Sep 12, 2018 12:33 am |
|
|
No. CCS supports this fine.
Your problem I think is the variable declaration. Remember a 'short' in CCS C, is an int1, and you can't construct pointers to an int1. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Sep 12, 2018 6:35 pm |
|
|
Ttelmah, I converted it to CCS and it didn't compile.
I got these errors:
Quote: |
*** Error 51 "PCH_test.c" Line 14(5,9): A numeric expression must appear here
*** Error 51 "PCH_test.c" Line 15(5,9): A numeric expression must appear here
*** Error 51 "PCH_test.c" Line 16(5,9): A numeric expression must appear here
*** Error 51 "PCH_test.c" Line 17(5,9): A numeric expression must appear here
*** Error 51 "PCH_test.c" Line 18(5,9): A numeric expression must appear here
*** Error 51 "PCH_test.c" Line 19(5,9): A numeric expression must appear here
*** Error 51 "PCH_test.c" Line 20(5,9): A numeric expression must appear here
7 Errors, 0 Warnings.
Build Failed.
|
Test program:
Code: |
#include <18F46K22.h>
#fuses INTRC_IO, NOWDT
#use delay(clock=4M)
void send(int8 *to, int8 *from, int8 count)
{
int8 n;
n = (count + 7) / 8;
switch (count % 8) {
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}
}
//=========================================
void main()
{
while(TRUE);
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Fri Sep 14, 2018 1:30 am |
|
|
Yes. It is spotting that you are meeting the opening bracket only in the first case, so complains from there. You can though just use the unwrapped version:
Code: |
#include <18F46K22.h>
#fuses INTRC_IO, NOWDT
#use delay(clock=4M)
void send(int8 *to, int8 *from, int8 count)
{
int8 n;
n = (count + 7) / 8;
switch (count % 8) {
case 0: *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
}
while (--n > 0) {
*to = *from++;
*to = *from++;
*to = *from++;
*to = *from++;
*to = *from++;
*to = *from++;
*to = *from++;
*to = *from++;
}
}
//=========================================
void main()
{
int8 dest;
int8 source[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
send(&dest, source, 16);
while(TRUE);
}
|
Does what it is meant to. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Fri Sep 14, 2018 9:14 am |
|
|
Though breaking a rule (avoid goto if possible...), this compiles and gives more efficient code:
Code: |
send(int8 *to, int8 *from, int8 count)
{
int8 n=(count+7)/8;
switch(count%8){
case 0:
do_label: *to = *from++;
case 7:
*to = *from++;
case 6:
*to = *from++;
case 5:
*to = *from++;
case 4:
*to = *from++;
case 3:
*to = *from++;
case 2:
*to = *from++;
case 1:
*to = *from++;
if (--n>0)
goto do_label;
}
}
|
Probably the best that can be generated. |
|
|
shimpossible
Joined: 27 Aug 2018 Posts: 6
|
|
Posted: Sat Sep 15, 2018 4:51 am |
|
|
Its not breaking the rule, since you can't avoid it.
It seems CCS is not ANSI compliant with respect to switch statements. oh well, at least there are work arounds |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Sat Sep 15, 2018 5:04 am |
|
|
rules were made to be broken
using goto is fine by me, afterall it IS in the PIC instructin set.
and ...
CCS was never ever 100% ANSI compliant, Don't think any compiler or language is or can be. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Sat Sep 15, 2018 12:30 pm |
|
|
You will find quite a few other threads online with people having problems compiling this with other compilers. CCS is not alone in not permitting this.
It works under the key that switch components may contain any statement (correct). However it is failing here because the do itself contains only some cases, with the opening and closing brackets of the do statement not guaranteed to be met in all code paths. Now whether this is allowed is indeterminate, so it is probably fair to not allow it.
The goto is a sensible solution to this, and since it does not go outside a function (this is where goto becomes dangerous, since it can then leave the stack unbalanced), should be fine. |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1345
|
|
Posted: Sat Sep 15, 2018 9:19 pm |
|
|
shimpossible wrote: | Its not breaking the rule, since you can't avoid it.
It seems CCS is not ANSI compliant with respect to switch statements. oh well, at least there are work arounds |
You are correct. This is part of ANSI C but not supported by the CCS compiler at this time. In fact, when I implemented protothreads for CCS, I was unable to use the duff's based version and had to use something more similar to the gcc extension version (which CCS does support). This doesn't help with loop unrolling, but just extra info.
You might try tossing an email to CCS support and see if they would be willing to add it. It has definite uses. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Sun Sep 16, 2018 12:26 am |
|
|
It's actually a very questionable 'is it legal' thing.... That you can have the switch containing a 'do' like this is explicitly 'legal'. However the issue is if you arrive with a starting value that could result in you not getting to 'case 0', is not. In this case you would get to the 'while' without ever meeting the 'do'. Duff's original comment on this was 'it works' (which it did on the PDP C), but whether is is (or should be) 'allowed' is questionable!. If your compiler's parser actually verifies that the do/while components must always be matched, it'll fail.
As it stands this makes it 'not guaranteed' to be legal. but legal in most C's....
The 'while' treatment is not an explicit part of ANSI C compliance. The 'do' treatment though is. Now which is actually causing the compiler to complain is questionable here. So whether the handling is ANSI compliant would take a lot of checking to verify....
If I remember Duff did do a version that put the 'do' outside case 0 to deal with this explicitly, because he foresaw that compilers might not like this. Might be worth seeing if this is around somewhere and whether this compiles with the CCS compiler?. If it does it'll throw light on what is failing. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Sun Sep 16, 2018 1:49 pm |
|
|
Yes, I found the version he published when making some comments about this. It translates as:
Code: |
void send(int8 *to, int8 *from, int8 count)
{
int8 n, r;
n = (count + 7) / 8;
r=count % 8;
if (r==0)
do {
switch (r) {
case 0: *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
}
} while (--n > 0);
}
|
This compiles correctly.
This shows that the fault with CCS here is that it is not accepting the 'do{' inside the switch, which is required behaviour in ANSI... |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1345
|
|
Posted: Fri Sep 28, 2018 12:41 pm |
|
|
EDIT: I know you already addressed this Ttelmah, just adding some additional info.
Ttelmah wrote: | It's actually a very questionable 'is it legal' thing.... |
It's definitely legal in both C and C++. The language subcommittees reviewed it and decided it was legal.
It wasn't intentional, but once they saw it didn't violate any of the rules of the standard they left it legal.
Here's an old interview with the creator of Duff's Device:
https://www.lysator.liu.se/c/duffs-device.html
They are referring to the original version where the do is after CASE 0. Remember that cases do not provide scope in C or C++ so they cannot forbid partial scopes between cases. A switch statement's scope starts before the cases and ends after the cases, but inbetween is all the same scope, so CASE0 and CASE1 share the same scope. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Fri Sep 28, 2018 1:39 pm |
|
|
Not quite.
The question that was initially raised was whether it was legal to have the do statement start in a switch like this. They decided this was legal.
However it was also observed that because of the way it can operate, it is possible to reach the terminating condition without having met the initial condition. It should not happen with legitimate count values, but could. They observed that a compiler that implemented a test for this could refuse the code, and it was legitimate for it to do so.....
This then was why I had to prove 'which part' was making CCS fail. It _is_ the part that should be legal..... |
|
|
Darren Rook
Joined: 06 Sep 2003 Posts: 287 Location: Milwaukee, WI
|
|
Posted: Mon Jul 13, 2020 9:13 am |
|
|
FYI - this has been fixed by CCS a few months ago and is legal now. _________________ I came, I saw, I compiled. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19495
|
|
Posted: Mon Jul 13, 2020 9:33 am |
|
|
Thanks for the update Darren. |
|
|
|