[comp.lang.c] Ambiguous C?

roelof@idca.tds.PHILIPS.nl (R. Vuurboom) (04/26/89)

The following piece of code got me into trouble.
I needed to access a register which only allows long accesses.

The intention of the code is to do an int (on the motorola a long) 
access and then determine the 14th bit.

The compiler generated a byte access for 2 bytes further and then tested
the 6th bit. For locations in memory this will deliver the same result
however a byte access to the register caused a bus timeout error to occur.

There are basically (at least) 2 interpretations to the code:

1. dereference the location as an int then bit and with bit 13. In which
   case the generated code is probably wrong.

2. Determine the status of the 14th bit at the given location in which
   case the generated code is probably right.

Does C specify which (if any) interpretation is correct?




#define	SCUCMD		0 
#define	SCU_BDID	0x2000		/* bit 14 */

error()
{
	if ( (*(int *)(SCUCMD)) & SCU_BDID )
		;
}



	.text
	.globl	_error
_error
	jra	.L40000
.L40001
	btst.b	#5,2			<== dereferenced as byte
	jeq	.L13			
.L13
.L12
	unlk	a6
	rts
.L40000
	link.w	a6,#-4
	jra	.L40001
	.data
	.data

chris@mimsy.UUCP (Chris Torek) (04/27/89)

In article <111@ssp1.idca.tds.philips.nl> roelof@idca.tds.PHILIPS.nl
(R. Vuurboom) writes:
>The intention of the code is to do an int (on the motorola a long) 
>access and then determine the 14th bit.
>
>The compiler generated a byte access for 2 bytes further and then tested
>the 6th bit. For locations in memory this will deliver the same result
>however a byte access to the register caused a bus timeout error to occur.

>... Does C specify which (if any) interpretation is correct?

No.  In the absence of external constraints, the compiler is free to make
this sort of `optimisation'.  The traditional approach has been to compile
device drivers with some or all optimisations disabled.  Another would be
to teach your compiler that locations near 0 (this example uses 0) are
`special'.  The simplest approach, however, is probably to use volatile:

>#define	SCUCMD		0 
>#define	SCU_BDID	0x2000		/* bit 14 */
>
>error()
>{
>	if ( (*(int *)(SCUCMD)) & SCU_BDID )
>		;
>}

	if (*(int volatile *)SCUCMD & SCU_BDID)
		/* void */;

or, better,

	struct scudevice {
		long	scu_csr;	/* command/status register */
		long	scu_data;	/* or whatever */
	};
	#define SCUADDR 0
	#define SCU ((struct scudevice volatile *)SCUADDR)

	/*
	 * Bits in scu_csr.
	 */
		...
	#define	SCU_BDID	0x2000

	...
		if (SCU->scu_csr & SCU_BDID)

If your compiler does not understand `volatile', and has no way to
disable optimisation, you are out of luck.  (You can resort to assembly
language subroutines.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@smoke.BRL.MIL (Doug Gwyn) (04/27/89)

In article <111@ssp1.idca.tds.philips.nl> roelof@idca.tds.PHILIPS.nl (R. Vuurboom) writes:
>The intention of the code is to do an int (on the motorola a long) 
>access and then determine the 14th bit.
>The compiler generated a byte access for 2 bytes further and then tested
>the 6th bit.
>Does C specify which (if any) interpretation is correct?

If you tell the compiler that the object being accessed is "volatile",
it should do the best it can to perform the access just the way you
thought you had specified when you wrote the code.  Otherwise it is
entitled to do what it did.

Now, your compiler may not support the "volatile" type qualifier, in
which case you'll need to resort to something else such as assigning
(a copy of) the object to an external variable, which forces the
compiler to pick it all up.  Then you can test the copy for the bit
you were interested in.

gwyn@smoke.BRL.MIL (Doug Gwyn) (04/27/89)

In article <17133@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>If your compiler does not understand `volatile', and has no way to
>disable optimisation, you are out of luck.  (You can resort to assembly
>language subroutines.)

Back, back!  (Making the sign of the cross.)  No need to resort to
assembly language for something so simple.

What is the real problem here?  It's that the compiler knows that
we only need to inspect one byte in order to determine the state of
the bit.  So how do we outwit the compiler?  We have to make it unsure
of whether or not the other bytes are also needed.  One simple way to
do that is to resolve the bit test into two phases, the first of which
performs the proper access, after which the second determines what the
state of the bit is.  To force a longword access, it suffices to copy
the addressed object into an external longword, because the compiler
cannot know what other use might be made of its contents (by other
independently-compiled modules) and must therefore pick up the whole
object.  Having gotten the object into an ordinary storage cell, it
can then be inspected to our heart's content.

The one thing you can't really accomplish via such a trick is an
atomic read-modify-write operation, such as ORing a bit into a memory-
mapped device register.  I can't recall ever needing to ensure that,
but it is theoretically possible that one might.  In such a case we
might be inspired by the trick and code the operation something like:
	if ( (*(long *)DEVADDR |= BIT) == BIT )
		something_innocuous();
which will also force the compiler to examine the whole longword.
Odds are good that it will generate the desired code for the OR
operation.  (If not, additional effort along these general lines
should eventually outwit the compiler's optimization.)

ark@alice.UUCP (Andrew Koenig) (04/27/89)

In article <111@ssp1.idca.tds.philips.nl>, roelof@idca.tds.PHILIPS.nl (R. Vuurboom) writes:

> The following piece of code got me into trouble.
> I needed to access a register which only allows long accesses.

 . . .

> Does C specify which (if any) interpretation is correct?

> #define	SCUCMD		0 
> #define	SCU_BDID	0x2000		/* bit 14 */

> 	if ( (*(int *)(SCUCMD)) & SCU_BDID )

There are two separate questions:

	1.  Does C define the meaning of this program fragement?

	2.  How do you get the compiler to do what you want?

The answer to (1) is `no' -- once you cast an integer into
a pointer, you're on your own.

In practice, what's probably happening is that the compiler
realizes that there's only one bit turned on in the value
of SCU_BDID, so it's optimizing the code by not fetching
the entire word to test.

To suppress the optimization, you can probably conceal the
value of SCU_BDID from the compiler by using it in a way that
the compiler thinks might change.  For instance:

	static int SCU_BDID = 0x2000;

Now the compiler probably won't be smart enough to realize that
SCU_BDID is really a constant, so it will have to generate code
that caters to any bit in the whole word being on.
-- 
				--Andrew Koenig
				  ark@europa.att.com

piet@cs.ruu.nl (Piet van Oostrum) (04/28/89)

In article <111@ssp1.idca.tds.philips.nl>, roelof@idca (R. Vuurboom) writes:

 `#define	SCUCMD		0 
 `#define	SCU_BDID	0x2000		/* bit 14 */
 `
 `error()
 `{
 `	if ( (*(int *)(SCUCMD)) & SCU_BDID )
 `		;
 `}

In article <10136@smoke.BRL.MIL>, gwyn@smoke (Doug Gwyn) writes:

 `state of the bit is.  To force a longword access, it suffices to copy
 `the addressed object into an external longword, because the compiler
 `cannot know what other use might be made of its contents (by other
 `independently-compiled modules) and must therefore pick up the whole

Another simple way is to put the bit to be extracted in a variable, so the
compiler doesn't know to optimize it:

 long	SCU_BDID =	0x2000		/* bit 14 */

This has the advantage that it also works for bit sets.
-- 
Piet van Oostrum, Dept of Computer Science, University of Utrecht
Padualaan 14, P.O. Box 80.089, 3508 TB Utrecht, The Netherlands
Telephone: +31-30-531806. piet@cs.ruu.nl (mcvax!hp4nl!ruuinf!piet)

orn@rsp.is (Orn E. Hansen) (04/28/89)

In article <111@ssp1.idca.tds.philips.nl>, roelof@idca.tds.PHILIPS.nl (R. Vuurboom) writes:
> 
> The compiler generated a byte access for 2 bytes further and then tested
> the 6th bit. For locations in memory this will deliver the same result
> however a byte access to the register caused a bus timeout error to occur.
> 
It reads the byte in question, thats what I would expect it to do.  If the
hardware does not support such access it wouldn't respond, causing a BUS
error condition (absence of data transfer acknowledge).

The result to this problem is to read the register into memory (long access)
and then query the copy.  You could also just modify the assembler code for
long access in which case you'd need to use an An for memory reference and
Dn for the bit count.

	MOVE.L  EffAdr,An               * Get address
	MOVE.L  #14,Dn                  * Get Bit count
	BTST.L  Dn,(An)                 * Thest the data in question

HEY! by the way.  What are JEQ, JRA etc. NEW instructions?  What are there
format?  I haven't seen these in any DATA Manual on Motorola mnemonics.
What is a JRA? Jump_Routine_Always, Jump_Relative_Always?  I've only heard
of BRanch Always shorthanded as BRA.

mike@thor.acc.stolaf.edu (Mike Haertel) (04/28/89)

In article <10136@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
:In article <17133@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
:>If your compiler does not understand `volatile', and has no way to
:>disable optimisation, you are out of luck.  (You can resort to assembly
:>language subroutines.)
:
:Back, back!  (Making the sign of the cross.)  No need to resort to
:assembly language for something so simple.
:
: [ ... portable trick for faking volatile ... ]
:
:The one thing you can't really accomplish via such a trick is an
:atomic read-modify-write operation, such as ORing a bit into a memory-
:mapped device register.  I can't recall ever needing to ensure that,
:but it is theoretically possible that one might.  In such a case we
:might be inspired by the trick and code the operation something like:
:	if ( (*(long *)DEVADDR |= BIT) == BIT )
:		something_innocuous();
:which will also force the compiler to examine the whole longword.
:Odds are good that it will generate the desired code for the OR
:operation.  (If not, additional effort along these general lines
:should eventually outwit the compiler's optimization.)

I think this would be a really bad idea, and that it would be *much*
better to use the assembly language.  You're never going to port code
like this to a different machine anyway, so there is no loss for using
the assembly language.  But, suppose you wanted to switch to a different
compiler?  And suppose that compiler used the same calling conventions
(they usually do) but generated different code for this particular
reference.  Then the assembly language approach would work, but possibly
not the tweaked C approach.  The insidious thing is, this code looks
portable, so when things stopped working you would have a large puzzle
on your hands.

In general I believe programs should say what they mean, and if they
mean something obviously machine dependent they should say it in an
obviously machine dependent way.  It is not hard to port machine
dependent code, as long as the dependencies are clearly labeled and
isolated from the portable code.
-- 
Mike Haertel <mike@stolaf.edu>
main() ??< printf("hello, world??/n"); ??>

gwyn@smoke.BRL.MIL (Doug Gwyn) (04/29/89)

In article <1943@thor.acc.stolaf.edu> mike@stolaf.edu writes:
>I think this would be a really bad idea, and that it would be *much*
>better to use the assembly language.  You're never going to port code
>like this to a different machine anyway, so there is no loss for using
>the assembly language.

Ah, but there is a loss.
Such an access to a device register is only a tiny part of the code;
one is much better off using a higher-level system programming language
for the bulk of system programming.  You thereby obtain support for
data structures and so forth.  Portability is not the only reason for
using C (although it is commonly an important one).
To branch from mostly-C code to assembly language level would require
either a function call, which MIGHT break were you to change compilers,
or in-line assembler (such as the "asm" extension found in many C
implementations).  If you've done either of these, you've probably
found reasons why they're less than fully satisfactory.

>In general I believe programs should say what they mean, and if they
>mean something obviously machine dependent they should say it in an
>obviously machine dependent way.  It is not hard to port machine
>dependent code, as long as the dependencies are clearly labeled and
>isolated from the portable code.

Device driver code is obviously extremely machine dependent, but for
example UNIX device drivers, entirely written in C, have been ported
much more readily from PDP-11s to VAXes than would have been possible
if they had been written in assembly language.

I agree that nonportable assumptions should be clearly identified in
the source code, but you can do that in C.

daveb@gonzo.UUCP (Dave Brower) (04/30/89)

In article <9249@alice.UUCP> ark@alice.UUCP (Andrew Koenig) suggests:
>To suppress the optimization, you can probably conceal the
>value of SCU_BDID from the compiler by using it in a way that
>the compiler thinks might change.  For instance:
>
>	static int SCU_BDID = 0x2000;
>
>Now the compiler probably won't be smart enough to realize that
                  ^^^^^^^^
>SCU_BDID is really a constant, so it will have to generate code
>that caters to any bit in the whole word being on.

Since it is static, the compiler might be able to figure it out.  

To get a higher degree of confidence, you can remove the static, showing
the compiler that some other file might know about the variable and
change it.

-dB
-- 
"An expert is someone who's right 75% of the time"
{sun,mtxinu,amdahl,hoptoad}!rtech!gonzo!daveb	daveb@gonzo.uucp

mike@thor.acc.stolaf.edu (Mike Haertel) (04/30/89)

In article <10163@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
> [ ... omission ... ]
>To branch from mostly-C code to assembly language level would require
>either a function call, which MIGHT break were you to change compilers,
>or in-line assembler (such as the "asm" extension found in many C
>implementations).  If you've done either of these, you've probably
>found reasons why they're less than fully satisfactory.
>
> [ ... omission ... ]
>
>Device driver code is obviously extremely machine dependent, but for
>example UNIX device drivers, entirely written in C, have been ported
>much more readily from PDP-11s to VAXes than would have been possible
>if they had been written in assembly language.

I'd like to make it clear that I am not advocating writing device drivers
in assembly language.  I was once involved with moving an RAxx driver
from a Vax to a pdp11, so I am, to say the least, not wholly unappreciative
of the benefits of writing device drivers in a real language.  I am also
not an advocate of asm escapes.

But for the particular situation you were suggesting, which might pop
up more than once on the same machine anyway, rather than resort to
compiler hacks to do the atomic set-a-bit operation, I would say
in the source code something like:

	...
	atomically_or_to_address(random_value, DEVICE_REGISTER_ADDRESS);
	...

Unlike any (open coded) compiler hack, this says exactly what it means.
And then atomically_or_to_address() could be a macro that expands to a
compiler hack, it could be an assembly language subroutine, or whatever.
The point is that the icky machine *and* compiler dependent atomic or
stuff would be isolated in one tiny spot, well away from the code that
is conceptually independent of exactly how atomic or'ing is done, and
clearly labeled as machine- and compiler- dependent.
-- 
Mike Haertel <mike@stolaf.edu>
main() ??< printf("hello, world??/n"); ??>