[comp.lang.misc] gotos

cik@l.cc.purdue.edu (Herman Rubin) (04/16/88)

In article <3470@bunker.UUCP>, garys@bunker.UUCP (Gary M. Samuelson) writes:
> In article <748@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
> >
> >Here is a challenge to the denigrators of goto.
> 
> I am not sure I qualify, absolutely, but how about:
> 
> Case 5:	b >>= g16;
> 	m |= b;
> 	x = *(--geom);
> 	if (TEST)
> 	{	
> 		if(ODD(x))
> 			Case2();
> 		else
> 			Case1();
> 	}
> 	else
> 	{
> 		g4 = (x+1)/2;
> 		if(ODD(x))
> 			Case3();
> 		else
> 			Case4();
> 	}
> 
> Readability improved through indentation at no extra charge.
> Extraneous semi-colons tossed also.  And, yes, I normally do
> such things when I have to deal with ugly code.
> 
> Now give me something hard.
> 
> Gary Samuelson

--For those who have not seen the original posting, replace
	Casej() 	by	goto Casej.		---------

I have no qualms with using or not using indentations.  Also I think that
the initial use of semi-colons by C was a mistake, and rather than getting
obscure error messages, I admit I use too many.  Now to the main point.

I stated that this was a _fragment_ of code.  After leaving Case5, this
part of the code is not reached again until entered in the generation of
a new item.  That is, after going to one of the other cases, this block of
code is _not_ to be returned to.  Also, the cost of a subroutine call is
huge compared with the cost of a goto.

One of those responding by mail correctly deduced the original procedure,
which can easily be written without gotos.  The actual procedure as I would
code it uses gotos even more, as it can be recognized that certain information
may be in a reusable form.  For example, instead of 

	*(geom++) = z;

in one case, followed by reading the item in the transferred case, just goto
the intermediate step.

The idea of the whole section of code, in a non-goto format, with most of the
details omitted, as follows:  (Please ignore C grammatical errors.)
	
	{if(w==3)
		{i=3; g4=1; break}
	else if (EVEN(w)){....}
	else if(w==5)ABORT;
	else .....}

	while(i>1)
	{switch(i)
		case 2: b >>= *(--geom); m |= b; i=1; break;
		case 3: (details omitted)
		case 4: (details omitted)
		case 5: as in the posted code, except goto Casej is replaced
			by i = j; break;
		case 6: (details omitted)
		case 7: (details omitted)
		default: (details omitted)}
	
	termination code (details omitted)

A simple analysis of the instructions generated shows that much of the time is
spent in storing the value of i (even if you can get your compiler to put it
into a register) and carrying out the switch comparisons.  In addition, there
are many ways to combine operations using the structure of the code, and 
knowledge of the algorithm, to eliminate unnecesary transfers.  The goto-less
code has at least twice as many machine goto instructions as the goto code for
the transfers to casej, and each there is an additional comparison instruction
for each case move.  I deliberately eliminated the storage of i for i<8.

When your optimizing compiler can convert case switches with case numbers 
generated into the more efficient code such as I would write, I will consider
eliminating those gotos.  Alternatively, those features may be added to the
language.  However, I cannot see how even the most brilliant optimizing 
compiler could see some of the other uses of goto in the algorithm.

There seems to be far more that the algorithm producer can think of than the
language designer realizes is pos

peter@athena.mit.edu (Peter J Desnoyers) (04/17/88)

In article <751@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>When your optimizing compiler can convert case switches with case numbers 
>generated into the more efficient code such as I would write, I will consider
>eliminating those gotos. 

I don't know about optimizing compilers, but I've heard horror stories
of non-optimizing compilers using jumptables for switch statements.
(Oh no!! Why does "switch (n) {case 0: foo(); case 20000: bar();}" generate
an 80k executable?) It may have been an older VMS compiler. I dunno...

Personally, I think that doing such grevious harm to the source code
in the name of 'optimizing' is off the mark. The time spent writing
and maintaining that code could have been spent (1) buying an
optimizing compiler; (2) writing it in assembler; or (3) convincing
your boss to spring for a faster machine. 

Perhaps a better thing than docking people's pay for 'goto's would be
a compiler that put a delay in with every goto. Ten or twenty nop's
should be about right. Then people would only use goto when it was
really needed for stylistic reasons.

btw, my favorite reason for using goto - to cope with the problem that
the same word (break) is used to terminate loops and to signal the end
of a section of a switch. Thus you can write "if (foo) break;", but
you may have reason to write:
					(what you want to write: )
  while (foo)				while (foo)
    switch (bar)			  switch (bar)
    {					  {
      case n: 				    case n:
	goto (end_while); 		      break;
	break;				      esac; /* or whatever */
    }					  }
  end_while:

				Peter Desnoyers
				peter@athena.mit.edu