T.R | Title | User | Personal Name | Date | Lines |
---|
14.1 | | ADVAX::A_VESPER | | Fri May 11 1984 10:26 | 75 |
| The primary benefit of the way the C switch statement works is that
you can drop through to the next case when you want to:
...
case '-': /* '-' means negative number */
sign = -1;
/* drop through to next case statement */
case '+': /* explicit plus sign, ignore */
ptr++; /* move to next character */
/* drop through to next case statement */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
value = get_number (&ptr);
break;
...
Here the concept is used two different ways. The bottom 'case'
is actually 9 cases, each of which points to the same code. The
other way this feature is used is in the top two cases. Here,
some unique processing is done in the appropriate case, but some
processing is the same between two cases (one must be the 'tail'
of the other) and the code is shared.
This does not allow for all possible ways that code might be
sharable. For the example above consider what we could do to
make '+' explicitly set the sign to +1. There are two
possibilities:
...
case '-': /* '-' means negative number */
sign = -1;
/* drop through to next case statement */
case '+': /* explicit '+' sign, make sure 'sign' is right */
if (*ptr == '+') sign = 1;
ptr++; /* move to next character */
/* drop through to next case statement */
...
and
...
case '-': /* '-' means negative number */
sign = -1;
go to ignore_char_and_get_number;
case '+': /* explicit plus sign, force 'sign' to +1 */
sign = 1;
/* drop through to ignore_char_and_get_number */
ignore_char_and_get_number:
ptr++; /* move to next character */
/* drop through to next case statement */
...
As this last example shows, the 'switch' command is simply a
FORTRAN-style computed GOTO -- what you do when you get to the
new execution point is up to you. C does not have 'pure
structured programming' constructs; in this regard it is one step
better than FORTRAN but a couple of steps behind PASCAL.
Andy Vesper
P.s. I will not restart the C vs. BLISS controversy in this note
by asking BLISS advocates if you can explicitly share code in
this way using the BLISS CASE construct. (I know you can do
similar things with the SELECT construct, but that makes multiple
tests.) Did you hear me? I am NOT asking.
|
14.2 | | PBSVAX::CAMPBELL | | Fri May 11 1984 12:00 | 2 |
| Now, if only the expressions in case arms didn't have to be compile-
time-constant-expressions!! Then switch would be truly useful.
|
14.3 | | NEWTON::NORTON | | Sat May 12 1984 02:03 | 9 |
| I really have seen a statement either in K&R or in some random "do C
this way" unix doc that said "some people actually prefer the break in
switch thing". I don't mind it at all, but I am certain that I have
never ever used a switch case w/o a break.
If I knew it wouldn't offend .1, I'd say that it sounds like hes asking,
but not listening. Note that I'm NOT saying that, since I'm not listening
either. (Any suggestion that "either" in the last sentence implies that I'm
stating .-3 is incorrect).
|
14.4 | | LATOUR::AMARTIN | | Sat May 12 1984 11:42 | 24 |
| Re .0:
I have one data point on what effect having to say "break" explicitly means
to programmers. My friend Phil Budne (BUDNE@MRFORT) wrote a Pascal
compiler in C for a compiler course last year. He is sharp enough of
a guy to have been able to recognize where he could share code, and never
found a case where he could take adventage of this, though he used a fair
number of switches. But one time (in the middle of devlopment) he was
demoing his compiler, and we noticed it generating two instructions with
similar looking effective addresses to reference an array element (two
similar looking sequence ending up that way, actually). It turned out
that there was a switch in his code generator routine that would handle
operand references that was missing a break, and he was generating code
to reference the location as both an array reference, and a field name
reference for a structure. So the score is one bug, and no code sharing.
One data point is not a statistically significant sample, but I can see
this happenning in production software. Considering that the alleged cost
of an average SPR to engineering alone was quoted at well over $1000 at
an inspection moderator's workshop last year, we could do without this
"feature" of C.
More flames when I re-read 1, 2 and 3.
/AHM
|
14.5 | | LATOUR::AMARTIN | | Sat May 12 1984 12:14 | 84 |
| Re .1:
I would have mentioned this even if you WANTED to hear it:
Code sharing between arms of CASE statements in Bliss is easy to achieve
(at some loss in readability, if you want to be sure you get it). I am
speaking of Bliss-10 here, but Bliss-10 is the lowest common denominator
of Bliss compilers in many ways (it was the first, it is allowed to
be that way).
Bliss-10 has a peephole optimizer that does cross-jumping. That is, it
transforms this:
lots of stuff
GOTO FOO
...
same stuff
FOO:
into:
GOTO FOO
...
FOO: same stuff
(Well, don't worry about it moving the label FOO if there are other
references to it, I explained this badly).
Therefore, if you write:
CASE e OF
SET
...
(FLAG=TRUE;lots of stuff);
...
lots of the same stuff;
...
TES;
there will be only one copy of "lots of stuff", and it will be shared. As
long as the register allocator chose the same ACs for the common code
in the different arms in the first place, this will happen automatically.
I think there is an optimization to remove jumps to .+1, hence if the
two arms were adjacent, even the generated branch to the shared code
is later removed.
Where the shared code is small, or efficiency is unimportant, the code
is just duplicated, and the compiler quietly fixes things up. If someone
edits an arm and sticks extra code at the end by accident, where it could
have commuted to the front of the arm, they are beat up in code review.
It gives us the illusion of putting one over
damned line noise.
universe. (I wonder who generated the line noise while I was typing that).
In applications where efficiency matters (or people think it does), the
common code is put in the body of a macro, and the macro is used in the
CASE arms. This is most notoriously used in the Fortran-10/20 compiler's
lexical analyzer, which is 4 nested infinite WHILE loops, with LEAVEs
and labels. The body of the innermost loop is an (n-hundred?) arm CASE,
with all of the arms being macro invocations. Code sharing supposedly
makes up for the fact that it is almost unmaintainable.
While C compilers are not good at long macros, this should apply to
them as well. Particularly since everyone claims that they use nothing
BUT peephole optimizers. It would be interesting to see if any C compiler
was able to apply cross-jumping to code that used "break" everywhere, but
provided ample opportunity for code sharing.
Re .2:
Yeah, if the case labels were permitted to be non-ctce's, then you could
code n-way tests of sparse sets in an obvious fashion. But then there
would be twice as much to implement, blah, blah, blah, etc. I assume
that Wirth would characterize C as "a horrid language, but at least the
designers knew when to stop throwing features in". (No, I have never
heard Wirth say any such thing, it just sounds Wirthian). I am going
to be moving Vax-11 C to the PDP-10, and I noticed how you can't really
take much away from it and still have C. I was wondering whether we could
minimize our dependance on VMS during porting by making the Vax compiler
generate code for the subset of the language it is implemented in. But
we couldn't think of anything it wouldn't be likely to use.
/AHM
|
14.6 | | LATOUR::AMARTIN | | Sat May 12 1984 13:03 | 16 |
| Sigh.
1. No, I don't want to get involved in one of those quaint explainations
about how cross-jumping is bad because any Vax's biggest problem is just
*finding* the instructions that are somewhere out in memory. Let's just
leave it at the fact that when you are falling through, the jump disappears
anyhow, so you even win when worrying about prefetching, etc. You can
instead only remove the code when you don't have to redirect the jump,
only remove it.
2. Vax-11 C doesn't do this on the two cases I tried (one was the example
in .1).
/AHM
P. S. But it did thow away my whole program when I was modifying local
variables to no purpose instead of externals. That's what I call data
flow analysis!
|
14.7 | | ADVAX::A_VESPER | | Mon May 14 1984 09:52 | 11 |
| re .3: I was not asking, not because I didn't want to listen, but
because I already knew that the BLISS language does not allow any explicit
sharing. Use of MACROS (as suggested by other replies) is not to my
liking because the MACROS are typically defined many pages before they
are used. That's OK for things used many places, but I am not sure
about something used twice on the same page.
There is a difference in >philosophy< between C and BLISS -- a recent
reply to the main C vs. BLISS note discusses this well.
Andy
|
14.8 | | VLNVAX::AMARTIN | | Mon May 14 1984 20:24 | 9 |
| I am told that it is the form of branch chaining that substitutes short branches
to nearby branch instructions for long jump instructions that kills
pipelined machines. Not cross-jumping. So putting cross jumping in could
only help, not hurt. Especially in shops that require breaks in all switch
arms (which I hear is a popular rule).
Using macros is only permissable as a way to get around proven performance
problems. ^acceptable^ Because it sure makes the code unreadable.
/AHM
|
14.9 | | REX::MINOW | | Wed May 16 1984 17:50 | 34 |
| The C switch statement's lack of "structure" allows you to do some
pretty bizarre things. Excuse if the following is unreadable --
REPLY/EDIT didn't work.
stuff_into_fifo(vector, count)
char *vector; /* Output to hardware fifo */
int count; /* Number of bytes to output */
{
extern char *fifo; /* hardware thing */
int chunks; /* 8 byte groups */
chunks = count / 8;
switch (count % 8) { /* modulus 8 */
do { /* Do loop !! */
case 7: *fifo = *vector++;
case 6: *fifo = *vector++;
...
case 0:
} while (chunks-- > 0);
}
}
I.e., this thing unwinds the inner loop of a byte mover into
eight in-line instructions. The switch statement branches
into the middle of the first iteration and the do/while
continues the iteration for each succeeding chunk.
Note that this is an argument for goto style switches, as
opposed to Pascal's subroutine style switches.
This example was originally posted by someone (don't remember who)
to the Unix USENET. Yes, it really is legal C.
Martin.
|
14.10 | | VLNVAX::AMARTIN | | Wed May 16 1984 20:44 | 18 |
| Re .9:
Yes, that program is interesting. Vax-11 C X2.0-023 yields 8 of the
following diagnostics:
%CC-E-NOTSWITCH, Default labels and case labels valid only
in switch statements.
If you use a plain compound ({}) or an if, the compiler doesn't mind. But it
does freak out when the labels are in the body of a while, or for.
The manual says:
Error. You have used case or default as a label outside the body of a switch
statement.
User Action. Change the offending label(s).
/AHM/SIGH
|
14.11 | | REX::MINOW | | Wed May 16 1984 21:55 | 4 |
| re .10 -- will you QAR it, or should I? (First syntax bug I've seen
in Vax-11C in a couple of years.)
Martin.
|
14.12 | from the USENET | CSSAUS::MOSS | cogito cogito ergo cogito sum. | Wed Jan 21 1987 07:16 | 36 |
| Newsgroups: net.lang.c
Path: decwrl!decvax!genrad!panda!talcott!harvard!seismo!brl-tgr!tgr!cottrell@nbs-vms.ARPA
Subject: Break Continued
Posted: 2 Oct 85 01:09:19 GMT
Organization:
/*
> > Some of us feel the same about breaks as we do about gotos. I won't use
> > it, or continue either, and I won't let anyone in my shop use it.
> > It is just a goto with an implicit label,...
>
> Well, now we know that this cat's either got some impressive coding
> techniques or doesn't use switch statements. There are only two uses of
> break, namely to exit the middle of a loop or to get out of (and usually
> terminate) a branch of a switch. If switch is used without break, I'd like
> to see how the coding works. Other than that, using break only to get out
> of the middle of a loop is fairly unspectacular(!).
Here's how he does his switches! It *really* works (under bsd 4.2 at least)
main(argc)
{ int never = 0;
switch (argc) { /* switch man sleepin' */
case 1: /* train a hundred & two */
printf("argc = 1\n"); /* is on the wrong track */
if (never) { /* & headin' for you! */
case 2: printf("argc = 2\n");
if (never) {
case 3: printf("argc = 3\n");
if (never) {
case 4: printf("argc = 4\n");
if (never) {
default: printf("argc = ???\n");
} } } } } }
|