* Caching & Annex C.6 @ 1999-09-03 0:00 Simon Wright 1999-09-05 0:00 ` Robert Dewar 1999-09-07 0:00 ` David Kristola 0 siblings, 2 replies; 12+ messages in thread From: Simon Wright @ 1999-09-03 0:00 UTC (permalink / raw) In C.6 the AARM says 16 For a volatile object all reads and updates of the object as a whole are performed directly to memory. 16.a Implementation Note: This precludes any use of register temporaries, caches, and other similar optimizations for that object. We've been having an argument as to precisely what sort of "cache" we're talking about here. Is it the CPU cache? does this imply that an implementation must force a cache write-through for volatile objects? My personal view is that the existence or otherwise of the cache is transparent and that all that's required is that the write doesn't go to registers. But then why does the AARM mention cache? Are there (multi-processor, presumably) systems where you would have to force a write-through to get the proper effect? Do Ada implementations on such systems have to do that? -- Simon Wright Work Email: simon.j.wright@gecm.com Alenia Marconi Systems Voice: +44(0)1705-701778 Integrated Systems Division FAX: +44(0)1705-701800 ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Caching & Annex C.6 1999-09-03 0:00 Caching & Annex C.6 Simon Wright @ 1999-09-05 0:00 ` Robert Dewar 1999-09-05 0:00 ` Simon Wright 1999-09-07 0:00 ` David Kristola 1 sibling, 1 reply; 12+ messages in thread From: Robert Dewar @ 1999-09-05 0:00 UTC (permalink / raw) In article <x7vyaenahw3.fsf@pogner.moho>, Simon Wright <simon@pogner.demon.co.uk> wrote: > Are there (multi-processor, presumably) systems where you would have > to force a write-through to get the proper effect? yes Do Ada > implementations on such systems have to do that? yes The point is that for non-volatile objects, which you know cannot be shared variables in the Ada sense, it is safe to put them in cache, even if the cache is non-coherent, but the point is that Volatile prevents this "optimization". Machines with non-coherent caches always have a way of signalling selected data as being non-cachable, forcing access to a shared memory (well let's say, that's true of shared memory machines). Sent via Deja.com http://www.deja.com/ Share what you know. Learn what you don't. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Caching & Annex C.6 1999-09-05 0:00 ` Robert Dewar @ 1999-09-05 0:00 ` Simon Wright 0 siblings, 0 replies; 12+ messages in thread From: Simon Wright @ 1999-09-05 0:00 UTC (permalink / raw) Robert Dewar <robert_dewar@my-deja.com> writes: > Machines with non-coherent caches always have a way of > signalling selected data as being non-cachable, forcing > access to a shared memory Oh, that's good. Can anyone name a typical machine of this type? (so I know when to start worrying) > (well let's say, that's true > of shared memory machines). Of course if it didn't have shared memory you wouldn't care .. I suppose ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Caching & Annex C.6 1999-09-03 0:00 Caching & Annex C.6 Simon Wright 1999-09-05 0:00 ` Robert Dewar @ 1999-09-07 0:00 ` David Kristola 1999-09-08 0:00 ` Robert Dewar 1 sibling, 1 reply; 12+ messages in thread From: David Kristola @ 1999-09-07 0:00 UTC (permalink / raw) In article fsf@pogner.moho, Simon Wright <simon@pogner.demon.co.uk> () writes: >In C.6 the AARM says > >16 For a volatile object all reads and updates of the object as a > whole are performed directly to memory. > > 16.a Implementation Note: This precludes any use of register > temporaries, caches, and other similar optimizations for that > object. > >We've been having an argument as to precisely what sort of "cache" >we're talking about here. Is it the CPU cache? does this imply that an >implementation must force a cache write-through for volatile objects? > >My personal view is that the existence or otherwise of the cache is >transparent and that all that's required is that the write doesn't go >to registers. But then why does the AARM mention cache? > >Are there (multi-processor, presumably) systems where you would have >to force a write-through to get the proper effect? Do Ada >implementations on such systems have to do that? I work with real-time embedded systems, and we use pragma Volatile to mark variables that represent (and are located on) memory mapped registers. The data must be written to the memory mapped register when that code executes. Likewise, many of those registers return values set by hardware, so reads can't come from the cache. --djk, keeper of arcane lore & trivial fluff Home: David95037 at aol dot com Spam: goto.hades@welovespam.com ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Caching & Annex C.6 1999-09-07 0:00 ` David Kristola @ 1999-09-08 0:00 ` Robert Dewar 1999-09-08 0:00 ` David Kristola 0 siblings, 1 reply; 12+ messages in thread From: Robert Dewar @ 1999-09-08 0:00 UTC (permalink / raw) In article <7r1l93$9lq4@svlss.lmms.lmco.com>, dkristol@see-my.sig wrote: > I work with real-time embedded systems, and we use pragma > Volatile to mark variables that represent (and are located > on) memory mapped registers. The data must be written to > the memory mapped register when that code executes. > Likewise, many of those registers return values set by > hardware, so reads can't come from the cache. But usually the ensurance of non-caching of these addresses will be done at the hardware level, it is not something the compiler worries about. The compiler's responsibility is simply to issue loads and stores, someone else must make sure that the loads and stores work as planned (for example on the MIPS one bit of the address space is used to indicate cachability). There are machines with different loads and stores to control caching, but not many! Sent via Deja.com http://www.deja.com/ Share what you know. Learn what you don't. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Caching & Annex C.6 1999-09-08 0:00 ` Robert Dewar @ 1999-09-08 0:00 ` David Kristola 1999-09-08 0:00 ` Robert Dewar 0 siblings, 1 reply; 12+ messages in thread From: David Kristola @ 1999-09-08 0:00 UTC (permalink / raw) In article 1@nnrp1.deja.com, Robert Dewar <robert_dewar@my-deja.com> () writes: >In article <7r1l93$9lq4@svlss.lmms.lmco.com>, > dkristol@see-my.sig wrote: >> I work with real-time embedded systems, and we use pragma >> Volatile to mark variables that represent (and are located >> on) memory mapped registers. The data must be written to >> the memory mapped register when that code executes. >> Likewise, many of those registers return values set by >> hardware, so reads can't come from the cache. > >But usually the ensurance of non-caching of these addresses >will be done at the hardware level, it is not something the >compiler worries about. The compiler's responsibility is >simply to issue loads and stores, someone else must make >sure that the loads and stores work as planned (for example >on the MIPS one bit of the address space is used to indicate >cachability). There are machines with different loads and >stores to control caching, but not many! True. Those memory mapped register variables are also placed at the correct location using the uncached address in the representation specification. And after being burned by one compiler, i started using appropriately sized integers for the register overlay, and calling unchecked conversion to convert the data to the register's record structure. This eliminated the compiler's optimization which inadvertently read and wrote bytes on either side of the register. Those bytes happened to be parts of other registers, and the hardware did not handle byte access properly. That particular bug took a while to track down. --djk, keeper of arcane lore & trivial fluff Home: David95037 at aol dot com Spam: goto.hades@welovespam.com ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Caching & Annex C.6 1999-09-08 0:00 ` David Kristola @ 1999-09-08 0:00 ` Robert Dewar 1999-09-09 0:00 ` Memory mapped registers (was Re: Caching & Annex C.6) David Kristola 0 siblings, 1 reply; 12+ messages in thread From: Robert Dewar @ 1999-09-08 0:00 UTC (permalink / raw) In article <7r4ted$e892@svlss.lmms.lmco.com>, dkristol@see-my.sig wrote: > This eliminated > the compiler's optimization which inadvertently read and > wrote bytes on either side of the register. Those bytes > happened to be parts of other registers, and the hardware > did not handle byte access properly. That particular bug > took a while to track down. That is not an optimization, it is a perfectly reasonable and valid translation of the Ada code. It is always very risky, and very implementation dependent, to assume you know exactly what machine instructions the compiler will generate for a given construct. If you need an exact load/store instruction, it is really much more appropriate to use an asm insertion to specify the exact instruction you need, rather than coax the compiler into generating the instruction you want. The latter is often highly non-portable, and can introduce insufficiently documented implementation dependencies in innocent looking code. Who would think that A := 0; could be a highly target dependent piece of code? One of the worst cases I saw of this was the following, someone had: Size : integer := 31; ... type x is array (0 .. Size) of Boolean; pragma Pack (x); for x use at bla bla; so far, so good, a typical memory mapped I/O specification. The code had things like Status := x (13); This worked fine on compiler V, but blew up on GNAT. Why? Because on the machine in question there were both word and byte loads and stores, neither being particularly preferred. Compiler V did a word load and a bit mask. GNAT did a byte load and a bit mask But the hardware had been built only to recognize word loads, and it blew up at the hardware level for a byte load, delivering random data to the bus (wonderful eh?) Needless to say this was hard to find. The kludge put in to fix this was to make the size constant, in which case GNAT now knows that the variable in question is exactly 32 bits long, and happens to do a word load, but this is really a very brittle way of "fixing" this. Much better would be to issue the exact instruction required. That being said, the suggestion of using integer is probably a good one, and this wlil likely minimize the possibility of unexpected code generation. << That particular bug took a while to track down >> Yes, indeed bugs like this in Ada code are devilish hard to find. And all too often people decide that their code cannot be at fault because it worked once upon a time. We have too often encountered situations where managers have been sold on the fiction that because their code is in Ada, it will be zero effort to port it to new platforms or new compilers :-( Robert Dewar Ada Core Technologies Sent via Deja.com http://www.deja.com/ Share what you know. Learn what you don't. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Memory mapped registers (was Re: Caching & Annex C.6) 1999-09-08 0:00 ` Robert Dewar @ 1999-09-09 0:00 ` David Kristola 1999-09-09 0:00 ` Robert Dewar 0 siblings, 1 reply; 12+ messages in thread From: David Kristola @ 1999-09-09 0:00 UTC (permalink / raw) In article 1@nnrp1.deja.com, Robert Dewar <robert_dewar@my-deja.com> () writes: >In article <7r4ted$e892@svlss.lmms.lmco.com>, > dkristol@see-my.sig wrote: >> This eliminated >> the compiler's optimization which inadvertently read and >> wrote bytes on either side of the register. Those bytes >> happened to be parts of other registers, and the hardware >> did not handle byte access properly. That particular bug >> took a while to track down. > >That is not an optimization, it is a perfectly reasonable >and valid translation of the Ada code. It is always very >risky, and very implementation dependent, to assume you >know exactly what machine instructions the compiler will >generate for a given construct. A 32 bit record was located on a proper word boundary, and the compiler generated (MIPS) instructions to read 4 bytes from two different words (LWL, LWR if i remember the mnemonic correctly) so that it could save a shift to get to the bit it was interested in (it shifted the address instead). It may not have been an optimization. As far as i could tell, it was a less efficient way to get the desired bit from and then back out to memory. >If you need an exact load/store instruction, it is really >much more appropriate to use an asm insertion to specify >the exact instruction you need, rather than coax the >compiler into generating the instruction you want. The >latter is often highly non-portable, and can introduce >insufficiently documented implementation dependencies >in innocent looking code. Who would think that > > A := 0; > >could be a highly target dependent piece of code? Memory_Mapped_Register := Convert(Register_Record); The name and purpose of the package would give away the fact that it is not portable code. How many serial I/O boards (made for our program's concept of RS-422) are made equal? >One of the worst cases I saw of this was the following, >someone had: > Size : integer := 31; > ... > type x is array (0 .. Size) of Boolean; > pragma Pack (x); > for x use at bla bla; > >so far, so good, a typical memory mapped I/O specification. > >The code had things like > > Status := x (13); > >This worked fine on compiler V, but blew up on GNAT. Why? >Because on the machine in question there were both word >and byte loads and stores, neither being particularly >preferred. > > Compiler V did a word load and a bit mask. > GNAT did a byte load and a bit mask > >But the hardware had been built only to recognize word loads, >and it blew up at the hardware level for a byte load, delivering >random data to the bus (wonderful eh?) Been there, stepped through it in a debugger or watched it on the DAS, scratched my head, but never got the T-shirt. >Needless to say this was hard to find. The kludge put in to fix >this was to make the size constant, in which case GNAT now knows >that the variable in question is exactly 32 bits long, and >happens to do a word load, but this is really a very brittle >way of "fixing" this. Much better would be to issue the exact >instruction required. I suppose i could have come up with a generic to read/write 32 bit registers and had the body explicitly perform the correct assembly code, then instantiate it for all of the different registers. Would this be portable from compiler to compiler? >That being said, the suggestion of using integer is probably >a good one, and this wlil likely minimize the possibility of >unexpected code generation. Integers are atomic, and of the proper size. It did work well with that compiler (probably "V"). ><< That particular bug took a while to track down >> > >Yes, indeed bugs like this in Ada code are devilish hard >to find. And all too often people decide that their code >cannot be at fault because it worked once upon a time. We >have too often encountered situations where managers have >been sold on the fiction that because their code is in >Ada, it will be zero effort to port it to new platforms >or new compilers :-( My apologies to Bruce, it was his hardware, and i did blame him for a long time. In the end, it was excellent hardware. I wish we were reusing it on this project, it would solve one of my current dilemmas. Porting code is usually not an issue. There aren't too many uses for the special software/hardware that gets developed around here. There is a growing awareness of the usefulness of reuse, and an effort to actually start reusing software. Only it is a slow process. If more hardware were reused, there would be more call for software reuse. :-( --djk, keeper of arcane lore & trivial fluff Home: David95037 at aol dot com Spam: goto.hades@welovespam.com ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Memory mapped registers (was Re: Caching & Annex C.6) 1999-09-09 0:00 ` Memory mapped registers (was Re: Caching & Annex C.6) David Kristola @ 1999-09-09 0:00 ` Robert Dewar 1999-09-14 0:00 ` Memory mapped registers (was Re: Cachi David Kristola 0 siblings, 1 reply; 12+ messages in thread From: Robert Dewar @ 1999-09-09 0:00 UTC (permalink / raw) In article <7r7ljl$sdi6@svlss.lmms.lmco.com>, dkristol@see-my.sig wrote: > A 32 bit record was located on a proper word boundary, > and the compiler generated (MIPS) instructions to read > 4 bytes from two different words (LWL, LWR if i remember > the mnemonic correctly) so that it could save a shift > to get to the bit it was interested in (it shifted the > address instead). It may not have been an optimization. > As far as i could tell, it was a less efficient way to > get the desired bit from and then back out to memory. The LWL/LWR sequence is used when the compiler does not know that a word is located on a proper word boundary, and is typically the most efficient sequence in this case. Now the issue of *why* the compiler did not know is an interesting one, but that's the point, you should not be worrying at this level. In fact it sounds like this was not an optimization, but a LACK of an optimization that caused you trouble. > The name and purpose of the package would give away > the fact that it is not portable code. How many > serial I/O boards (made for our program's concept of > RS-422) are made equal? Ah ha! But what if you switch to another compiler. The issue is not porting to other targets, but other compilers. We have seen issues like this come up all the time! > > Porting code is usually not an issue. There aren't too > many uses for the special software/hardware that gets > developed around here. I think an awful lot of code is developed under this assumption, and then it turns out, for example, that there is a decision to switch to Ada 95, and suddenly you are in the porting business. Remember, I did not say that it was wrong to write non-portable code, that is often completely justified. What I said was that non-portable code should be clearly identified. Code that depends on the particular code generation behavior of the compiler is particularly brittle, because even a new version or minor update of a compiler can change the generated code in what should be a harmless manner, but which may not be if the code has made unwarranted assumptions. Some of the early Linux kernel code made this mistake (and depended on the exact sequence of code generated by gcc, resulting in continued discussions over whether gcc was doing the right thing -- in some cases, it was, but it still broke the kernel). This is now all fixed nicely in Linux. You should aim at fixing similar code you have in your applications :-) Sent via Deja.com http://www.deja.com/ Share what you know. Learn what you don't. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Memory mapped registers (was Re: Cachi 1999-09-09 0:00 ` Robert Dewar @ 1999-09-14 0:00 ` David Kristola 1999-09-14 0:00 ` Robert Dewar 0 siblings, 1 reply; 12+ messages in thread From: David Kristola @ 1999-09-14 0:00 UTC (permalink / raw) In article 1@nnrp1.deja.com, Robert Dewar <robert_dewar@my-deja.com> () writes: >In article <7r7ljl$sdi6@svlss.lmms.lmco.com>, > dkristol@see-my.sig wrote: >> The name and purpose of the package would give away >> the fact that it is not portable code. How many >> serial I/O boards (made for our program's concept of >> RS-422) are made equal? > >Ah ha! But what if you switch to another compiler. The issue >is not porting to other targets, but other compilers. We have >seen issues like this come up all the time! True. Now the question becomes "how do we programmers identify non-portable code?" The record with a rep clause carefully laying out the fields and placing it at the correct address seemed quite portable. It just didn't work correctly. >Remember, I did not say that it was wrong to write non-portable >code, that is often completely justified. What I said was that >non-portable code should be clearly identified. I agree with this. >Code that depends on the particular code generation behavior >of the compiler is particularly brittle, because even a new >version or minor update of a compiler can change the generated >code in what should be a harmless manner, but which may not >be if the code has made unwarranted assumptions. There are assumptions and there are assumptions. Someone once told me that they never make any assumptions. But i went for a ride with them once, and they did not stop at green lights (they assumed the cross traffic was stopping). The problem is defining "unwarranted". I still prefer the idea of mapping an appropriately sized integer to the memory mapped register then using unchecked conversion to turn the data into a proper record for that register. I think it is reasonable to assume that all compilers will use the appropriate load and store instructions under these circumstances. This should also prove to be more portable than machine code insertion (which is implementation dependent). If i had a register that crossed several memory cells, and they had to be referenced in a specific order, then i would resort to a low level solution, and not expect it to be portable to a new compiler (and flag it as such). >Some of the early Linux kernel code made this mistake (and >depended on the exact sequence of code generated by gcc, >resulting in continued discussions over whether gcc was >doing the right thing -- in some cases, it was, but it >still broke the kernel). This is now all fixed nicely in >Linux. You should aim at fixing similar code you have in >your applications :-) I always aim to improve my code. Sometimes i miss. If someone can point out a better way of doing something, then i will learn a new trick. That is a good day. --djk, keeper of arcane lore & trivial fluff Home: David95037 at aol dot com Spam: goto.hades@welovespam.com ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Memory mapped registers (was Re: Cachi 1999-09-14 0:00 ` Memory mapped registers (was Re: Cachi David Kristola @ 1999-09-14 0:00 ` Robert Dewar 1999-09-15 0:00 ` David Kristola 0 siblings, 1 reply; 12+ messages in thread From: Robert Dewar @ 1999-09-14 0:00 UTC (permalink / raw) In article <7rkkk6$iog2@svlss.lmms.lmco.com>, dkristol@see-my.sig wrote: > True. Now the question becomes "how do we programmers > identify non-portable code?" The record with a rep > clause carefully laying out the fields and placing it > at the correct address seemed quite portable. It just > didn't work correctly. Seemed quite portable? What does that mean? That the syntax was right, and the compiler did not reject it, and it worked? But that's not good enough! You need to *know* what is and what is not defined in the language, and the only way to know this is to read the definition of the language, and really know the definition well. This is a hard task for many programmers, and is the most common failure that results in non-portable code. > >Remember, I did not say that it was wrong to write non-portable > >code, that is often completely justified. What I said was that > >non-portable code should be clearly identified. > > I agree with this. > > >Code that depends on the particular code generation behavior > >of the compiler is particularly brittle, because even a new > >version or minor update of a compiler can change the generated > >code in what should be a harmless manner, but which may not > >be if the code has made unwarranted assumptions. > > There are assumptions and there are assumptions. > Someone once told me that they never make any > assumptions. But i went for a ride with them > once, and they did not stop at green lights (they > assumed the cross traffic was stopping). > > The problem is defining "unwarranted". Nope! There is no problem whatsoever in defining unwarranted. The definition of the language tells you quite precisely what you can and cannot count on, and any time you rely on behavior that is described as one of the following unspecified erroneous implementation dependent implementation defined bounded error you should document the fact, and similarly if you rely on behavior of the compiler that is nowhere guaranteed in the RM, e.g. performance requirements or, as in this case, specific generated code sequences, you also need to carefully document the non-portability. > I still prefer the idea of mapping an > appropriately sized integer to the memory mapped > register then using unchecked conversion to turn > the data into a proper record for that register. Neither is portable > I think it is reasonable to assume that all > compilers will use the appropriate load and store > instructions under these circumstances. Why is this a reasonable assumption? If you need an "appropriate" load/store instruction, then why not write the instruction you need explicitly! For example if you write: A := B; and these are 32-bit integers, it is quite fine on some machines to use a 32-bit floating load and store, to save integer register pressure. Furthermore, a clever compiler might be able to use a 64-bit floating load and get two loads for the price of one! > This > should also prove to be more portable than machine > code insertion (which is implementation > dependent). Well it is not portable, more portable means that you may get away with not having to rewrite it for a given port, but the huge danger, as apparently in this case, is that the potential non-portability does not get flagged. By using a machine code insertion, you definitely flag this as something that needs looking at in a port. Sent via Deja.com http://www.deja.com/ Share what you know. Learn what you don't. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Memory mapped registers (was Re: Cachi 1999-09-14 0:00 ` Robert Dewar @ 1999-09-15 0:00 ` David Kristola 0 siblings, 0 replies; 12+ messages in thread From: David Kristola @ 1999-09-15 0:00 UTC (permalink / raw) In article 1@nnrp1.deja.com, Robert Dewar <robert_dewar@my-deja.com> () writes: >Well it is not portable, more portable means that you may >get away with not having to rewrite it for a given port, >but the huge danger, as apparently in this case, is that The LWL/LWR & SWL/SWR problem was not a porting problem. That code was original, and now sits on a tar tape in a storage container locked in a room somewhere in building 151 (along with the hardware it ran on). The world is safe from that bit of code (the unchecked conversion to a 32 bit integer, the code that generated the LWL/LWR sequence was altered long ago). The LWL/LWR part worked. The SWL/SWR caused the hardware to place bad data in an unintended register. Even if the memory at the address functioned in a byte addressable way, memory outside the register would have been written. >the potential non-portability does not get flagged. By >using a machine code insertion, you definitely flag this >as something that needs looking at in a port. I shall use machine code insertions for this sort of implementation in the future. Thanks for pointing out the potential problems. Or, in other words: "Uncle!" ;-) --djk, keeper of arcane lore & trivial fluff Home: David95037 at aol dot com Spam: goto.hades@welovespam.com ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~1999-09-15 0:00 UTC | newest] Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 1999-09-03 0:00 Caching & Annex C.6 Simon Wright 1999-09-05 0:00 ` Robert Dewar 1999-09-05 0:00 ` Simon Wright 1999-09-07 0:00 ` David Kristola 1999-09-08 0:00 ` Robert Dewar 1999-09-08 0:00 ` David Kristola 1999-09-08 0:00 ` Robert Dewar 1999-09-09 0:00 ` Memory mapped registers (was Re: Caching & Annex C.6) David Kristola 1999-09-09 0:00 ` Robert Dewar 1999-09-14 0:00 ` Memory mapped registers (was Re: Cachi David Kristola 1999-09-14 0:00 ` Robert Dewar 1999-09-15 0:00 ` David Kristola
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox