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"); ??>