[comp.lang.perl] Bugs in perl 3.0 pl 8 -- return statement

brian@hpausla.aso.hp.com (Brian Coogan) (01/23/90)

I've just been fighting with the return statement of perl 3.0 pl 8 and
losing badly.  There appear to be some bugs, or something rather
non-intuitive, about the way return works.  I'm not sure of the exact
circumstances, but return will fail where simply evaluating an
expression still returns a value.  If it's a programming error on my
part, it's sophisticated, as I've stepped through and verified the
subroutines and they do appear to work.  It's occurred to me that perl
might be getting confused about numeric vs string context vs array
context and I've tried quite a few permutations to get around that, no
luck.  Would be glad of any help from gurus out there - read on
McDuff...  (longish)

This all started when I thought I'd add a binary option to my little
conversion program.  Two hours later I have a working version, which I
had to hack to death to get it to behave!!

The interesting thing is that one of the failing cases works with
perl -d (atob (-bd) works, but btoa still fails).  This is not
encouraging (stack problems?).  I'm running perl on an HP9000/800 (RISC)
(HP-uX 3.1) machine; I get the same symptoms on a HP9000/300 (68030) (HP-UX
6.5) machine, except that perl -d doesn't help either case.

Details:  The program is meant to convert from a given base to another,
including characters.  The failures only occur with binary mode.  Binary
mode involves two extra subroutines, atob and btoa.  Before I added
them, everything else worked fine.  It still does, only binary breaks.
Some examples, the way they should look:

	$ cvt -dx 60 120 10
	3c
	78
	a
	$ cvt -bd 10000
	16
	$ cvt -db 34
	100010

At least, that's what's meant to happen.  In real life,
	$ cvt -bd 10000

	$ cvt -db 34

	$ perl -d cvt -bd 10000
	Loading DB from perldb.pl 3.0.1.1 89/10/26
	main(20):	($myname = $0) =~ s%.*/%%;
	  DB<1> c
	16
	$ perl -d cvt -db 34
	Loading DB from perldb.pl 3.0.1.1 89/10/26
	main(20):	($myname = $0) =~ s%.*/%%;
	  DB<1> c

	$       [ie: no output]

I've included for your perusal two versions of the script, one hacked
to make it work, the other the way I would like it to be.  The working
version is cvt.hack, but it has a lot of debug output.  The failure is
called cvt.

If you can fix this for me, or correct some mistake, I'll be greatly
grateful.  Once this is fixed I hope to post it.

thanks,

Brian Coogan,
Hewlett-Packard Australian Software Operation.

ACSnet:		brian@hpausla.oz		UUCP: hplabs!hpausla!brian
Internet:	brian%hpausla@hplabs.hp.com	Tel: +61 3 871 1648 (TZ=EST-10)

#---------------------------------- cut here ----------------------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Brian Coogan <brian@hpausln> on Tue Jan 23 22:23:56 1990
#
# This archive contains:
#	cvt	cvt.dbg	
#
# Error checking via wc(1) will be performed.

unset LANG

echo x - cvt
cat >cvt <<'@EOF'
#! /usr/bin/perl

# cvt
#	Convert between bases
#
#	Usage: cvt -<from><to> [from ...]
#
#	where <from> and <to> are each single letters meaning:
#		o	octal
#		d	decimal
#		x	hex
#		b	binary
#		c	character
#
#		Brian Coogan 6 Jan 90
#
# $Header: cvt,v 1.1.1.1 90/01/23 21:50:44 brian Exp $
# $Locker:  $

($myname = $0) =~ s%.*/%%;
$mode = shift;

die "Usage: $myname -[odbcx][odbcx] [args]\n"
	unless ($mode =~ m|-([odbcx])([odbcx])[^/]*$|);

$from = $1;
$to = $2;


if ($#ARGV >= $[)
{
	while ($val = shift)
	{
		print do cvt($val) . "\n";
	}
	exit(0);
}

while ($val = <>)
{
	print do cvt($val) . "\n";
}
exit(0);

sub cvt
{
	local($val) = @_;

	#
	#	Input
	#	if decimal, its already ok
	#
	$val = oct($val) if $from eq 'o';
	$val = hex($val) if $from eq 'x';
	$val = ord($val) if $from eq 'c';
	$val = &atob($val) if $from eq 'b';

	#
	#	Output
	#
	return(&prettychar($val)) if $to eq 'c';
	return(&btoa($val)) if $to eq 'b';
	return(sprintf("%$to", $val));
}

sub prettychar
{
	local($val) = @_;

	# isprint simulation
	return(sprintf("%c", $val)) if ($val > 32 && $val <= 126);

	# known control chars
	return('\r') if $val == 015;
	return('\n') if $val == 012;
	return('\f') if $val == 014;
	return('\t') if $val == 011;
	return('SP') if $val == 040;

	# misc control chars
	return(sprintf("^%c", ($val + 0100))) if ($val < 32);

	#
	#	If we get this far, it may be a terminal dependent character,
	#	but as we can't portably determine whether it is, we print
	#	it in octal
	#
	return(sprintf("\\%3o", $val));
}

#
#	Ascii binary (aka base 2) to internal
#
sub atob
{
	local($string) = @_;
	local($val, $dig) = 0;

	foreach $dig (split(/ */, $string))
	{
		$val = $val * 2 + $dig;
	}
	return($val);
}

#
#	Internal binary (aka base 2) to ascii.  No particular reason why
#	they're signed; that code can be removed if desired (comment out
#	tagged lines).
#
sub btoa
{
	local($val) = @_;
	local($sign) = '';		# signed
	local(@digits) = ();

	if ($val < 0)			# signed
	{				# signed
		$sign = '-';		# signed
		$val = - $val;		# signed
	}				# signed
	$val = int($val);		# sorry, we're dumb here
	while ($val != 0)
	{
		unshift(@digits, ($val & 1) ? "1" : "0");
		$val = int($val/2);
	}
	return($sign . join('', @digits));
}
@EOF
set `wc -lwc <cvt`
if test $1$2$3 != 1294012229
then
	echo ERROR: wc results of cvt are $* should be 129 401 2229
fi

chmod 555 cvt

echo x - cvt.dbg
cat >cvt.dbg <<'@EOF'
#! /usr/bin/perl

# cvt
#	Convert between bases
#
#	Usage: cvt -<from><to> [from ...]
#
#	where <from> and <to> are each single letters meaning:
#		o	octal
#		d	decimal
#		x	hex
#		b	binary
#		c	character
#
#		Brian Coogan 6 Jan 90
#
# $Header$
# $Locker$

($myname = $0) =~ s%.*/%%;
$mode = shift;

die "Usage: $myname -[odbcx][odbcx] [args]\n"
	unless ($mode =~ m|-([odbcx])([odbcx])[^/]*$|);

$from = $1;
$to = $2;


if ($#ARGV >= $[)
{
	while ($val = shift)
	{
		print do cvt($val) . "\n";
		#$xx = do cvt($val);
		#print "top output $xx\n";
	}
	exit(0);
}

while ($val = <>)
{
	#print do cvt($val) . "\n";
	$xx = do cvt($val);
	print "output $xx\n";
}

sub cvt
{
	local($val) = @_;

	#
	#	Input
	#	if decimal, its already ok
	#
	$val = $val + 0 if $from eq 'd';
	$val = oct($val) if $from eq 'o';
	$val = hex($val) if $from eq 'x';
	$val = ord($val) if $from eq 'c';
	$val = &atob($val) if $from eq 'b';
	printf "cvt  got number in as $val\n";

	#
	#	Output
	#
	return(&prettychar($val)) if $to eq 'c';
	if ($to eq 'b')
	{
		$tmp = &btoa($val);
		print "cvt: retval = $tmp, global = $result\n";
		#$tmp.' '; $tmp; # method 1 - works
		"$tmp";		 # method 2 - works
		#return "$tmp";   # method 3 - fails.
		last;
	}
	#return(&btoa($val)) if $to eq 'b';  # what I really want to do
	sprintf("%$to", $val);
}

sub prettychar
{
	local($val) = @_;

	# isprint simulation
	return(sprintf("%c", $val)) if ($val > 32 && $val <= 126);

	# known control chars
	return('\r') if $val == 015;
	return('\n') if $val == 012;
	return('\f') if $val == 014;
	return('\t') if $val == 011;
	return('SP') if $val == 040;

	# misc control chars
	return(sprintf("^%c", ($val + 0100))) if ($val < 32);

	#
	#	If we get this far, it may be a terminal dependent character,
	#	but as we can't portably determine whether it is, we print
	#	it in octal.
	#
	return(sprintf("\\%3o", $val));
}

#
#	Ascii binary (aka base 2) to internal
#
sub atob
{
	local($string) = @_;
	local($val, $dig) = 0;

	foreach $dig (split(/ */, $string))
	{
		$val = $val * 2 + $dig;
	}
	#return($val); # only works for single digits
	$val;
}

#
#	Internal binary (aka base 2) to ascii.  No particular reason why
#	they're signed; that code can be removed if desired (comment out
#	tagged lines).
#
sub btoa
{
	local($val) = @_;
	local($sign) = '';		# signed
	local(@digits) = ();

	print "btoa: input $val\n";
	if ($val < 0)			# signed
	{				# signed
		$sign = '-';		# signed
		$val = - $val;		# signed
	}				# signed
	$val = int($val);		# sorry, we're dumb here
	while ($val != 0)
	{
		unshift(@digits, ($val & 1) ? "1" : "0");
		$val = int($val/2);
	}
	printf "btoa: ascii output %s\n", $sign . join('', @digits);
	$result = $sign . join('', @digits); # try a global
	#return($sign . join('', @digits)); # returns nothing
	$sign . join('', @digits);
}
@EOF
set `wc -lwc <cvt.dbg`
if test $1$2$3 != 1485062808
then
	echo ERROR: wc results of cvt.dbg are $* should be 148 506 2808
fi

chmod 755 cvt.dbg

exit 0

brian@hpausla.aso.hp.com (Brian Coogan) (01/24/90)

Brian Coogan <brian@hpausla.aso.hp.com> writes in comp.lang.perl:
> I've just been fighting with the return statement of perl 3.0 pl 8 and
> losing badly.  There appear to be some bugs...

I just recompiled cmd.c with JMPCLOBBER defined and the bug went away.
Thanks for your help (especially Paul Maisano who suggested the fix).
Serves me right for not watching the fixes going by more closely.

thanks folks,
Brian.

glen@proexam.UUCP (Glen Brydon) (02/04/90)

In article <4080005@hpausla.aso.hp.com> brian@hpausla.aso.hp.com (Brian Coogan) writes:
>Brian Coogan <brian@hpausla.aso.hp.com> writes in comp.lang.perl:
>> I've just been fighting with the return statement of perl 3.0 pl 8 and
>> losing badly.  There appear to be some bugs...
>
>I just recompiled cmd.c with JMPCLOBBER defined and the bug went away.
>Thanks for your help (especially Paul Maisano who suggested the fix).
>Serves me right for not watching the fixes going by more closely.
>
>thanks folks,
>Brian.

I haven't seen any reply from Larry Wall about this stuff. It concerned
me when Anders Pilegaard posted his alarming article giving rather
mysterious symptoms (non of which I experienced). As he wrote to me:

>From cucard!daimi.dk!andersp Tue Jan 30 01:18:04 1990
>Date: Mon, 29 Jan 90 14:45:49 +0100
>From: Anders Pilegaard <cucard!daimi.dk!andersp>
>Subject: Re: IMPORTANT: Mysterious errors in perl patch level 8, solved
>. . .
>I then began actually debugging perl and after some time discovered
>that a free string struct had some wild references resulting in
>attempts to dereference a NIL pointer.  After some more debugging it
>appeared that this wild value was put there while the string struct
>was free'd, indicating stray pointers.  Quite some hours later I found
>the real culprit, setjmp/longjmp.  Perl uses a local register variable
>                  ==============
>st to hold a stack.  As this stack may be changed by various routines
>(foremost eval) it is often reset to the value of a global variable.
>This is not done around setjmp, and setjmp/longjmp does NOT guarantee
>                                                   ==================
>handling of register variables correctly (At least is says so in the
>========================================
>manual page on our sun-3). . . .
>
>It is both subtle and yet so simple. Because of the nature of this
>bug, virtually ANY variable may be clobbered. It may appear in any
>     =======================================
>script utilising longjmp's, and that includes not only subroutines,
>but also last, next, redo, and goto. But the fix is quite simple.
>
>Actually it seems that Larry Wall has foreseen this. He has introduced
>                       ============================
>an #ifdef JMPCLOBBER around the critical sections. This activates
>four assignments around each setjmp and should take care of all
>critical variables. The problem is that he only defined it for
>cray's and ANSI compilers. And it seems that at least sun's and hp's
>should also define this. And who knows how many other
>architectures/operating systems are affected?
>
>The reason that I posted my article with such an alarming header is
>that it is quite difficult to determine whether a system is affected
>                 ===================================================
>by this bug.  On our systems the same script produced two very visible
>reactions (core dump) and two actions I wouldn't have noticed had I
>not been debugging that particular script --- at least it would have
>been some time before I found out.

On my system (SunOS version 4.0.3 running on 3/280 arch) the man page
for setjmp(3) is quite informative (more so than 4.3 BSD):

SETJMP(3)              C LIBRARY FUNCTIONS              SETJMP(3)

NAME
     setjmp, longjmp, sigsetjmp, siglongjmp - non-local goto

. . . . .

DESCRIPTION
. . . . .
     setjmp() saves its stack environment in env for later use by
     longjmp.   A normal call to setjmp() returns zero.  setjmp()
     also saves the register environment.  If  a  longjmp()  call
          ==============================
. . . . .
     point data registers are restored to the values they had  at
     the  time that setjmp() was called.  But, because the regis-
     ter storage class is only a hint to the  C  compiler,  vari-
                          ===========
     ables  declared as register variables may not necessarily be
     assigned  to  machine  registers,  so   their   values   are
     unpredictable after a longjmp.  This is especially a problem
     for programmers trying to write machine-independent  C  rou-
                                     ===================
     tines.

. . . . .

EXAMPLE
     The following code fragment indicates the flow of control of
     the setjmp() and longjmp() combination:

          function declaration
          ...
             jmp_buf     my_environment;
               ...
             if (setjmp(my_environment)) {
               /* register variables have unpredictable values
                                          ====================
               code after the return from longjmp
                    ...
             } else {
               /* do not modify register vars
               this is the return from setjmp
                    ...
             }

SEE ALSO
     cc(1V), sigsetmask(2), sigvec(2), ieee_flags(3M), signal(3),
     setjmp(3V)

BUGS
. . . . .
>>   On Sun-2 and Sun-3 systems setjmp() also saves the  register  <<
>>   environment.   Therefore,  all data that are bound to regis-  <<
>>   ters are restored to the values they had at  the  time  that  <<
>>   setjmp()  was  called.  All memory-bound data have values as  <<
>>   of the time longjmp()  was  called.   However,  because  the  <<
>>   register  storage  class  is  only a hint to the C compiler,  <<
>>   variables declared as register variables may not necessarily  <<
>>   be  assigned  to  machine  registers,  so  their  values are  <<
>>   unpredictable after a longjmp.  When using compiler  options  <<
>>   that specify automatic register allocation (see cc(1V)), the  <<
>>   compiler will not attempt to assign variables  to  registers  <<
>>   in routines that call setjmp.                                 <<

=====================================================================

It seems only too clear that my computer and its operating system
should be treated like the cray and those with ANSI compilers. I
will be interested in seeing how Larry deals with this problem in
his upcoming bugfix (#9). Are there systems out there which guarantee
both that the compiler will in fact put your variable in a register
AND that all the registers will be restored as Larry has assumed most
will? Needless to say I have applied a temporary fix and for me this
fixed Brian Coogan's return problem (as it did for him).

While the gray areas of the language seem to be more interesting to
most people (including me), this stuff scares me to death. Setjmp(3)
seems in fact to be one of those gray areas of UNIX. As I wrote to
Anders Pilegaard, it surprises me the way this particular problem has
been publcized (or not). It is especially confusing then Larry already
anticipated it, but seemed not to realize the danger of his assumptions.

I hope to see the official fix for this one.

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (02/06/90)

In article <4080004@hpausla.aso.hp.com> brian@hpausla.aso.hp.com (Brian Coogan) writes:
: I've just been fighting with the return statement of perl 3.0 pl 8 and
: losing badly.  There appear to be some bugs, or something rather
: non-intuitive, about the way return works.

There were at least three little return buglets that should be fixed in patch 9.

	1) Couldn't return ().
	2) Occasional return of garbage value (or dump of core) in calling
		a subroutine the first time through (i.e. when growing the
		stack).  This is caused by some statements in cmd.c misplaced
		inside #ifdef JMPCLOBBER.  If defining #JMPCLOBBER manually
		fixes this, it's probably this bug.  Patch 9 will fix it
		so you can undef JMPCLOBBER again for a little efficiency.
	3) A return embedded in an expression could lose track of
		whether an array value or scalar was required for the function.

Larry

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (02/07/90)

In article <418@proexam.UUCP> glen@proexam.UUCP (Glen Brydon) writes:
: In article <4080005@hpausla.aso.hp.com> brian@hpausla.aso.hp.com (Brian Coogan) writes:
: >I just recompiled cmd.c with JMPCLOBBER defined and the bug went away.
: >Thanks for your help (especially Paul Maisano who suggested the fix).
: >Serves me right for not watching the fixes going by more closely.
: 
: I haven't seen any reply from Larry Wall about this stuff. It concerned
: me when Anders Pilegaard posted his alarming article giving rather
: mysterious symptoms (non of which I experienced). As he wrote to me:
:
[message deleted]
: 
: It seems only too clear that my computer and its operating system
: should be treated like the cray and those with ANSI compilers. I
: will be interested in seeing how Larry deals with this problem in
: his upcoming bugfix (#9). Are there systems out there which guarantee
: both that the compiler will in fact put your variable in a register
: AND that all the registers will be restored as Larry has assumed most
: will? Needless to say I have applied a temporary fix and for me this
: fixed Brian Coogan's return problem (as it did for him).
: 
: While the gray areas of the language seem to be more interesting to
: most people (including me), this stuff scares me to death. Setjmp(3)
: seems in fact to be one of those gray areas of UNIX. As I wrote to
: Anders Pilegaard, it surprises me the way this particular problem has
: been publcized (or not). It is especially confusing then Larry already
: anticipated it, but seemed not to realize the danger of his assumptions.
: 
: I hope to see the official fix for this one.

There are several things going on here.  The first is that there is a bug
in cmd.c that hides a statement inside #ifdef JMPCLOBBER that should be
outside.  These are the statements that say st = stack->ary_array.  These
are necessary after any expression evaluation because the stack may have
been realloced.  This has nothing to do with registers.  But I believe
this is why defining JMPCLOBBER fixed some flakiness.  Patch 9 moves
these outside the JMPCLOBBER code.

Now, as to registers and setjmp/longjmp.  We have to distinguish two levels
of support here.  First, as in the long manual section you quoted, we have
machines such as Sun which profess to restore ALL values, but it is
indeterminate whether they are restored to their value at setjmp() time or
longjmp() time.  That's okay, because perl doesn't rely on any variables
that could change between the setjmp() and the longjmp().  You shouldn't
need JMPCLOBBER on a Sun, at least not if their documentation tells the
truth on all Sun architectures.

There is a second level of (non-)support here, which is the ANSI level--namely
that registers are not guaranteed to have ANY reasonable value after a
longjmp(), that is, the value will be neither that at setjmp() time, nor that
at longjmp() time.  The official solution to this is to declare any
variables that you care about as volatile, so that they aren't kept in
registers over the setjmp()/longjmp().  Several of you sent me patches to
this effect.

I don't like the "official" solution.  It's overkill.  And cmd_exec() is the
one routine that can't afford any excess overhead.  The routine really wants
registers, and much of the routine is outside of setjmp()/longjmp().  So,
instead of abandoning the registers, I merely make sure that I have some
alternate way of restoring the registers after a longjmp().  This is the
code you will find inside of #ifdef JMPCLOBBER.  It's unnecessary overhead
if registers are restored by longjmp().

The JMPCLOBBER code only adds overhead when entering or exiting a loop
or block, because that's the only code that can longjmp.  There's one
extra statement going in, and two extra going out.  And the ones going
out are only exercised if the longjmp() actually happens.  This has got
to be less overhead than abandoning registers altogether.

The only problem with it is when to define JMPCLOBBER.  It's currently
defined on Cray and ANSI C machines.  We run the risk of finding
a new compiler or machine which isn't ANSI but has ANSI behavior.  I
think that's a risk I'm willing to take.  It may be possible to write
a Configure test for it that will usually be right, but it looks hard.
For the moment, I'm content to say that if you're on an oddball architecture,
and you get flaky results, try defining JMPCLOBBER.

I don't think it's something worth panicking about.

Larry

tom@ssd.csd.harris.com (Tom Horsley) (02/09/90)

I think the solution of re-loading the register variables rather than
declaring everything as volatile is a good idea, but it needs one extra
frill (at least the way it is implemented in patch level 8). The variables
you restore the register variables from *DO* need to be declared volatile.
Quite a few of the newer optimizing compilers don't care if you declare a
variable register or not, if they can keep it in a register, they will do
so.

This also means that some of the non-register variables (which appear to be
referenced across a setjmp) need to be declared volatile, or restored from a
volatile copy just like the register variables.

I have a problem now compiling with the highest optimization level getting
"panic: corrupt saved stack index" messages, I am perfectly willing to believe
it is an optimizer bug in my compiler, but it may also be a problem with
setjmp/longjmp, I have not had enough time to actually debug it yet (but it
happens the same way with or without JMPCLOBBER).
--
=====================================================================
domain: tahorsley@ssd.csd.harris.com  USMail: Tom Horsley
  uucp: ...!novavax!hcx1!tahorsley            511 Kingbird Circle
      or  ...!uunet!hcx1!tahorsley            Delray Beach, FL  33444
======================== Aging: Just say no! ========================

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (02/14/90)

In article <TOM.90Feb9070637@hcx2.ssd.csd.harris.com> tom@ssd.csd.harris.com (Tom Horsley) writes:
: I think the solution of re-loading the register variables rather than
: declaring everything as volatile is a good idea, but it needs one extra
: frill (at least the way it is implemented in patch level 8). The variables
: you restore the register variables from *DO* need to be declared volatile.
: Quite a few of the newer optimizing compilers don't care if you declare a
: variable register or not, if they can keep it in a register, they will do
: so.
: 
: This also means that some of the non-register variables (which appear to be
: referenced across a setjmp) need to be declared volatile, or restored from a
: volatile copy just like the register variables.

Er, there are three variables that are used to restore registers.  cmdparm
is already declared volatile.  cmd->c_flags is part of a structure, and
goto_targ is a global.  Are you saying that parts of a structure
and global variables also might get put into registers?

Actually, I can believe a super-optimizing compiler might put a global
into a register.  I can't imagine a stucture element, tho.  I doubt
either is your problem.

: I have a problem now compiling with the highest optimization level getting
: "panic: corrupt saved stack index" messages, I am perfectly willing to believe
: it is an optimizer bug in my compiler, but it may also be a problem with
: setjmp/longjmp, I have not had enough time to actually debug it yet (but it
: happens the same way with or without JMPCLOBBER).

This indicates that the "oldsave" variable is getting clobbered somehow.
But that one is also declared volatile.

Actually, it's declared VOLATILE.  Perhaps VOLATILE isn't getting
defined right.  Does your cpp define __STDC__?

Larry

bart@videovax.tv.tek.com (Bart Massey) (02/15/90)

In article <7068@jpl-devvax.JPL.NASA.GOV> lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes:
> Are you saying that parts of a structure
> and global variables also might get put into registers?
> 
> Actually, I can believe a super-optimizing compiler might put a global
> into a register.  I can't imagine a stucture element, tho.  I doubt
> either is your problem.

Heck, I'll say it.  Try running GCC-1.36 -O over the following C code,
and be surprised that, at least on the VAX and 68k, the structure member
ends up in a register.  Note that, at least as of 1.36, this is a very
fragile optimization, but it's bound to get nothing but better.

-----
struct foo { int a, b; };

main()
  {
    struct foo bar;

    for( bar.b = 0; bar.b < 10; bar.b++ )
        nothing( bar.b );  /* force an external reference */
  }
-----

And the brand-new DecStation 3100 (PMAX) Ultrix compiler does global
register optimizations, or so I am told.

Aren't compilers wonderful?

					Bart Massey
					..tektronix!videovax.tv.tek.com!bart
					..tektronix!reed.bitnet!bart

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (02/16/90)

In article <5716@videovax.tv.tek.com> bart@videovax.tv.tek.com (Bart Massey) writes:
: Heck, I'll say it.  Try running GCC-1.36 -O over the following C code,
: and be surprised that, at least on the VAX and 68k, the structure member
: ends up in a register.  Note that, at least as of 1.36, this is a very
: fragile optimization, but it's bound to get nothing but better.
: 
: -----
: struct foo { int a, b; };
: 
: main()
:   {
:     struct foo bar;
: 
:     for( bar.b = 0; bar.b < 10; bar.b++ )
:         nothing( bar.b );  /* force an external reference */
:   }
: -----
: 
: And the brand-new DecStation 3100 (PMAX) Ultrix compiler does global
: register optimizations, or so I am told.
: 
: Aren't compilers wonderful?

Gack.

Ok, I'll spread a few more judicious volatiles here and there on my globals
and structure elements.  Blech.  All this gobblygook just to make life
a little easier for a few compiler writers.  setjmp()/longjmp() are NOT
in the same class as asynchronous interrupts.

"volatile" must go.  This is non-negotiable.    :-) :-) :-)

Just another puerile hacker,
Larry

mike@umn-cs.cs.umn.edu (Mike Haertel) (02/16/90)

In article <7080@jpl-devvax.JPL.NASA.GOV> lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes:
>Ok, I'll spread a few more judicious volatiles here and there on my globals
>and structure elements.  Blech.  All this gobblygook just to make life
>a little easier for a few compiler writers.  setjmp()/longjmp() are NOT
>in the same class as asynchronous interrupts.
>
>"volatile" must go.  This is non-negotiable.    :-) :-) :-)

Note that if -traditional is used with gcc, it automatically recognizes
functions that call setjmp() and effectively forces all references from
within those functions to be volatile.

Of course, particularly if you call setjmp() from somewhere within
a big, oft-used function, this is an efficiency hit, so volatile
declarations may be more appropriate, if more work.
-- 
Mike Haertel <mike@ai.mit.edu>
"COBOL programmers are destined to code COBOL for the rest of
 their lives, and thereafter." -- Bertrand Meyer

bart@videovax.tv.tek.com (Bart Massey) (02/16/90)

In article <1990Feb15.202214.14987@umn-cs.cs.umn.edu> mike@umn-cs.cs.umn.edu (Mike Haertel) writes:
> In article <7080@jpl-devvax.JPL.NASA.GOV> lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes:
> >Ok, I'll spread a few more judicious volatiles here and there on my globals
> >and structure elements.  Blech.  All this gobblygook just to make life
> >a little easier for a few compiler writers.  setjmp()/longjmp() are NOT
> >in the same class as asynchronous interrupts.
> >
> >"volatile" must go.  This is non-negotiable.    :-) :-) :-)
> 
> Note that if -traditional is used with gcc, it automatically recognizes
> functions that call setjmp() and effectively forces all references from
> within those functions to be volatile.
> 
> Of course, particularly if you call setjmp() from somewhere within
> a big, oft-used function, this is an efficiency hit, so volatile
> declarations may be more appropriate, if more work.

This whole discussion probably belongs somewhere else, but I couldn't resist
one last word.  The problem, as Mike points out, is not the "volatile"
keyword, which is a serious feature for those of us writing memory-mapped
device drivers.  The problem is the gratuituous, superfluous, and altogether
obnoxious change by the ANSI folks of the most common traditional semantics
of setjmp() .  Under BSD Unix, at any rate, unless you call _setjmp(), you
get a *system call* anyway, to save the signal mask as well as the machine
state.  What ANSI *should* have done, IMHO, is to specify setjmp() and
longjmp() to save all auto variables, and _setjmp() and _longjmp() to have
the current more efficient but less comprehensible and useful semantics.

The perceived problem is that old-style setjmp() forces either the save of
essentially the entire machine state, including possibly a gazillion
registers, or implementation of setjmp() as compiler builtins.  My claim is
that the cost of saving the machine state is swamped on many UNIX machines by
the cost of the system call, and that it wouldn't be that hard to implement
setjmp() and longjmp() as compiler builtins which save the live state and
call a library routine, as I assume GCC does based on Mike's comments above.

Please don't take away my "volatile"! :-)

					Bart Massey
					..tektronix!videovax.tv.tek.com!bart
					..tektronix!reed.bitnet!bart

tom@ssd.csd.harris.com (Tom Horsley) (02/17/90)

>Ok, I'll spread a few more judicious volatiles here and there on my globals
>and structure elements.  Blech.  All this gobblygook just to make life
>a little easier for a few compiler writers.  setjmp()/longjmp() are NOT
>in the same class as asynchronous interrupts.

That's right, they aren't, they are worse, they are a perversion that is
being hidden from the compiler disguised as ordinary function calls. We
don't have these problems with our Ada compiler, because exception frames
and non-local gotos and wot-not are actually a part of the language.

setjmp()/longjmp() must go. This is non-negotiable. :-) e+9.

Meanwhile, to make this article actually have something to do with perl, I
should report the results I got when I finally debugged (or at least
suppressed) my "panic: corrupt saved stack index" problems:

   1) You were right, VOLATILE was not being defined as volatile, but fixing
      this did not make the problem go away.

   2) There are goto's in the if (setjmp()) code. These gotos imply that the
      entire body of the routine is reachable following a setjmp(), not just
      a few little sections of code.

   3) The variables gimme, sp, retstr, tmps, go_to, and newsp are not
      declared volatile and are also not necessarily restored correctly
      following the longjmp (for some of them, it depends on whether or not
      any of the switch body is executed, and that depends on the value
      returned from longjmp).

   4) When I declared these 6 variables VOLATILE, the code started working
      perfectly.

   5) I don't actually know what specifically was happening, I stopped
      working on it once I got it to work in step 4, but one other
      experiment I made is worth reporting. I made 6 volatile variables to
      save these six values in, and did saves and restores around the
      setjmp() calls, and I *still* got the panic messages. My two best
      theories are a compiler bug, or one of these variables is really
      updated prior to the longjmp and wants to keep its updated value, not
      the value it had when the first setjmp() was called.

P.S. Our compiler does not (yet) keep fields of structs in registers.  The
back end is capable of doing it, but the front end currently does not do
very aggressive alias analysis (for C, Ada is another matter).
--
=====================================================================
domain: tahorsley@ssd.csd.harris.com  USMail: Tom Horsley
  uucp: ...!novavax!hcx1!tahorsley            511 Kingbird Circle
      or  ...!uunet!hcx1!tahorsley            Delray Beach, FL  33444
======================== Aging: Just say no! ========================

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (02/20/90)

In article <TOM.90Feb17084626@hcx2.ssd.csd.harris.com> tom@ssd.csd.harris.com (Tom Horsley) writes:
: setjmp()/longjmp() must go. This is non-negotiable. :-) e+9.

I confess I like Ada exceptions a lot better too.

:    1) You were right, VOLATILE was not being defined as volatile, but fixing
:       this did not make the problem go away.

I can see another Configure test coming...    *sigh*

:    2) There are goto's in the if (setjmp()) code. These gotos imply that the
:       entire body of the routine is reachable following a setjmp(), not just
:       a few little sections of code.

Yes, but it's in a loop anyway, so that's true whatever.  And gotos are just
C's little way of telling you it wishes it had a real exception mechanism
that would let you exit blocks by default instead of restarting them...

:    3) The variables gimme, sp, retstr, tmps, go_to, and newsp are not
:       declared volatile and are also not necessarily restored correctly
:       following the longjmp (for some of them, it depends on whether or not
:       any of the switch body is executed, and that depends on the value
:       returned from longjmp).

Variables gimme and sp are function parameters, which is why I overlooked
them.  Presuming JMPCLOBBER is defined, retstr, newsp and go_to are
always taken care of if you assume that the longjmp() never returns a
value that isn't handled by the switch, which it doesn't (but I ought
to have a default case for paranoia reasons anyway.)  tmps is a scratch
variable, and is only used locally, never over a setjump()/longjmp().

It looks like your problem was either gimme or sp.  Probably sp.  They
will both be volatilized.

:    4) When I declared these 6 variables VOLATILE, the code started working
:       perfectly.

If you could tell me if it keeps working with only gimme and sp volatilized,
I'd be much obliged.  I'm not planning on volatilizing the others.

:    5) I don't actually know what specifically was happening, I stopped
:       working on it once I got it to work in step 4, but one other
:       experiment I made is worth reporting. I made 6 volatile variables to
:       save these six values in, and did saves and restores around the
:       setjmp() calls, and I *still* got the panic messages. My two best
:       theories are a compiler bug, or one of these variables is really
:       updated prior to the longjmp and wants to keep its updated value, not
:       the value it had when the first setjmp() was called.

Neither gimme nor sp is updated at any time in the routine, let alone
between setjmp() and longjmp().  And the others are supposedly accounted for.

Thanks for the input.

#5, er, I mean, Larry