www.digitalmars.com         C & C++   DMDScript  

D - Export and Volatile

reply "Luigi" <igiul.reverse email.it> writes:
Hello,

I have one simple question:

Export in D, is equal to volatile in C++

Thanks

--
Luigi
Sep 09 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Luigi" <igiul.reverse email.it> wrote in message
news:alisiu$2599$1 digitaldaemon.com...
 Export in D, is equal to volatile in C++
No, export means it is made available to users of the DLL. There is no volatile in D, as in my opinion volatile is next to useless in real applications.
Sep 09 2002
next sibling parent "Luigi" <igiul.reverse email.it> writes:
 No, export means it is made available to users of the DLL. There is no
 volatile in D, as in my opinion volatile is next to useless in real
 applications.
Ok, thanks -- Luigi
Sep 09 2002
prev sibling parent reply "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
Walter wrote:

 There is no
 volatile in D, as in my opinion volatile is next to useless in real
 applications.
Arrgh! This is true only from the perspective of a single program running on a memory-protected system. The vast majority of the code I write runs on systems with no MMU, on systems where multiple processors share the same memory, and on systems where applications need direct access to hardware. In all these cases, and in all their possible intersections and unions, "volatile" is useful simply to tell the compiler to NEVER enregister a variable. That is, all references to a given variable should go "to the metal" every time. No caching of such data items anywhere, ever. With "volatile", I can use any level of abstraction to represent my data. I can map a structure over raw memory or hardware, and I can pass pointers to such data that retain the volatile property. When working with compilers that lacked a fully functional "volatile" (sometimes ignored by compilers, like its anti-twin "register") I've had to resort to cumbersome indirect dynamic dereferences or inline assembler just to bypass a compiler "feature". And in order to guarantee I was smarter than the peep-hole optimizer, I'd still have to disassemble the binary to be sure. (Note: The common practice of examining the compiler assembly output is insufficient: Many assemblers, most notably Intel's, also have peep-hole optimizers!) It gets worse: Most CPUs do some form of data bus caching, even those without MMUs. On such platforms, "volatile" must also ensure it correctly drills through the hardware cache. "Volatile" may be an unnecessary feature to many, but if D is to truly be a "systems programming language", it is a mandatory requirement. Unless, of course, the equivalent functionality already exists in D, and this discussion is really about syntactic sugar. In which case, I say give me my sugar! I'm used to having it in C. Otherwise, it seems to me it will be impossible to use D to write any "serious" low-level code, not even a simple DMA handler (often needed when working with custom hardware in FPGAs because it is a simple and powerful interface). Without that capability, just what kind of "systems programming language" could D really be? C++ lets me write device drivers. Java doesn't. Focusing on system programming capabilities alone (not implementation), which one is D going to be most like? My entire interest in D focuses on pushing the power of "reasonable" OOP and "useful" GC further down in to embedded systems. Ideally, over 99% of all the programming could be done in D, using or avoiding D language features as appropriate to the context, with just a minimal smattering of assembler "glue" code (preferably inlined in D as well). Embedded apps are like small operating systems in that they need to be able to marshall all the resources of the system. Could I write an OS 99% in D? -BobC
Sep 09 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
news:3D7D3F59.E537224C users.sourceforge.net...
 Walter wrote:
 There is no
 volatile in D, as in my opinion volatile is next to useless in real
 applications.
Arrgh! This is true only from the perspective of a single program running on a memory-protected system.
Ah, I was hoping for a discussion!
 The vast majority of the code I write runs on systems with no MMU, on
 systems where multiple processors share the same memory, and on systems
 where applications need direct access to hardware.
Ok.
 In all these cases, and in all their possible intersections and unions,
 "volatile" is useful simply to tell the compiler to NEVER enregister a
 variable.  That is, all references to a given variable should go "to the
 metal" every time.  No caching of such data items anywhere, ever.

 With "volatile", I can use any level of abstraction to represent my
 data.  I can map a structure over raw memory or hardware, and I can pass
 pointers to such data that retain the volatile property.

 When working with compilers that lacked a fully functional "volatile"
 (sometimes ignored by compilers, like its anti-twin "register") I've had
 to resort to cumbersome indirect dynamic dereferences or inline assembler
 just to bypass a compiler "feature".  And in order to guarantee I was
 smarter than the peep-hole optimizer, I'd still have to disassemble the
 binary to be sure.  (Note:  The common practice of examining the compiler
 assembly output is insufficient:  Many assemblers, most notably Intel's,
 also have peep-hole optimizers!)
The Digital Mars inline assembler is w-y-w-i-w-y-g.
 It gets worse:  Most CPUs do some form of data bus caching, even those
 without MMUs.  On such platforms, "volatile" must also ensure it
 correctly drills through the hardware cache.

 "Volatile" may be an unnecessary feature to many, but if D is to truly be
 a "systems programming language", it is a mandatory requirement.

 Unless, of course, the equivalent functionality already exists in D, and
 this discussion is really about syntactic sugar.  In which case, I say
 give me my sugar!  I'm used to having it in C.
I've always found I needed to use a LOCK prefix anyway, so resorted to inline assembler.
 Otherwise, it seems to me it will be impossible to use D to write any
 "serious" low-level code, not even a simple DMA handler (often needed
 when working with custom hardware in FPGAs because it is a simple and
 powerful interface).
Why? What volatility does a DMA handler have?
 Without that capability, just what kind of "systems programming language"
 could D really be?  C++ lets me write device drivers.  Java doesn't.
 Focusing on system programming capabilities alone (not implementation),
 which one is D going to be most like?
You can still read/write to any memory address, including I/O ports.
 My entire interest in D focuses on pushing the power of "reasonable" OOP
 and "useful" GC further down in to embedded systems.  Ideally, over 99%
 of all the programming could be done in D, using or avoiding D language
 features as appropriate to the context, with just a minimal smattering of
 assembler "glue" code (preferably inlined in D as well).

 Embedded apps are like small operating systems in that they need to be
 able to marshall all the resources of the system.  Could I write an
 OS 99% in D?
I don't see why not. I've looked at the linux kernel sources, and don't see anything there D can't do.
Sep 09 2002
parent reply Joe Battelle <Joe_member pathlink.com> writes:
 Without that capability, just what kind of "systems programming language"
 could D really be?  C++ lets me write device drivers.  Java doesn't.
 Focusing on system programming capabilities alone (not implementation),
 which one is D going to be most like?
You can still read/write to any memory address, including I/O ports.
What happens when the memory is just memory-mapped I/O on an add-in card? Every memory read gives you new volatile data. There is no LOCK prefix needed in this case, but you need to be able to tell the compiler not to enregister this data and to go get it again on each reference.
Sep 10 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Joe Battelle" <Joe_member pathlink.com> wrote in message
news:all4et$2qg8$1 digitaldaemon.com...
 What happens when the memory is just memory-mapped I/O on an add-in card?
Every
 memory read gives you new volatile data.  There is no LOCK prefix needed
in this
 case, but you need to be able to tell the compiler not to enregister this
data
 and to go get it again on each reference.
That can be resolved by crafting a function like: byte read_byte_from_volatile_location(byte *p); For efficiency, the compiler can even make it an intrinsic function (like sin(), cos(), etc.). It solves the problem without the volatile type modifier permeating the type system and adding pages of complexity to overloading, type deduction, partial specialization, partial ordering, etc.
Sep 10 2002
parent reply Burton Radons <loth users.sourceforge.net> writes:
Walter wrote:

 "Joe Battelle" <Joe_member pathlink.com> wrote in message
 news:all4et$2qg8$1 digitaldaemon.com...
 
What happens when the memory is just memory-mapped I/O on an add-in card?
Every
memory read gives you new volatile data.  There is no LOCK prefix needed
in this
case, but you need to be able to tell the compiler not to enregister this
data
and to go get it again on each reference.
That can be resolved by crafting a function like: byte read_byte_from_volatile_location(byte *p); For efficiency, the compiler can even make it an intrinsic function (like sin(), cos(), etc.). It solves the problem without the volatile type modifier permeating the type system and adding pages of complexity to overloading, type deduction, partial specialization, partial ordering, etc.
There could be a single throttle point; that would stick everything in one section of the code generator and a small expression type, using a "volatile" intrinsic: int mouse_x; /* This is volatile */ int a, b; a = volatile (mouse_x); b = volatile (mouse_x); Afterwards, a and b are potentially different.
Sep 10 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:3D7E85CB.2030805 users.sourceforge.net...
 There could be a single throttle point; that would stick everything in
 one section of the code generator and a small expression type, using a
 "volatile" intrinsic:

     int mouse_x; /* This is volatile */

     int a, b;

     a = volatile (mouse_x);
     b = volatile (mouse_x);

 Afterwards, a and b are potentially different.
Yes, that might be a better way to do it.
Sep 10 2002
parent reply Joe Battelle <Joe_member pathlink.com> writes:
     a = volatile (mouse_x);
     b = volatile (mouse_x);

 Afterwards, a and b are potentially different.
Yes, that might be a better way to do it.
Your function approach works, but it is really nice to be able to lay a structure over a portion of memory-mapped I/O, call the whole darn thing volatile and reference away. I much prefer this to introducing more intrinsics.
Sep 10 2002
next sibling parent reply "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
Joe Battelle wrote:

     a = volatile (mouse_x);
     b = volatile (mouse_x);

 Afterwards, a and b are potentially different.
Yes, that might be a better way to do it.
Your function approach works, but it is really nice to be able to lay a structure over a portion of memory-mapped I/O, call the whole darn thing volatile and reference away. I much prefer this to introducing more intrinsics.
Same here! In the old days, we had to write lots of #defines and do peeks and pokes to access hardware from userland. Walter's function-based approach would force us back to those bad old days (with better naming, but the same functionality). Being able to overlay an intricate structure over a hardware interface abstracts away a huge portion of the hassle of writing to the bare metal. Lack of a true volatile attribute will exclude D from use on any system I build. It is that important to what I do and how I do it. D has to win HANDS DOWN over C in every domain to break into embedded systems. Don't think small in this area, unless you are prepared to abandon it. C++ has only a limited presence in embedded systems, and poses no competition to D. Unless, of course, D lacks volatile. Then C++ wins, despite the hassle and baggage. Just today I had to write a routine to talk to a "gas gauge" chip used to monitor a Li-ion battery. I simply declared a struct that mapped 1:1 to the device's registers, declared a pointer to a volatile instance of that structure (different, of course, from a volatile pointer to an instance), and was programming the device in C within minutes. The whole thing, including the time needed to read the cryptic spec sheet, examine the board schematics, verify the chip select address decode logic in the FPGA, craft the struct and write the code, was done in about 4 hours. Without volatile, it could easily have taken that long just to write all the #defines for the gas gauge register locations and content. Ugh. Don't even try to make me go back to #defines and PEEK/POKE! -BobC
Sep 10 2002
next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
news:3D7E9EF8.3A33B3C2 users.sourceforge.net...
 Just today I had to write a routine to talk to a "gas gauge" chip used to
monitor a
 Li-ion battery.  I simply declared a struct that mapped 1:1 to the
device's
 registers, declared a pointer to a volatile instance of that structure
(different,
 of course, from a volatile pointer to an instance), and was programming
the device
 in C within minutes.  The whole thing, including the time needed to read
the
 cryptic spec sheet, examine the board schematics, verify the chip select
address
 decode logic in the FPGA, craft the struct and write the code, was done in
about 4
 hours.

 Without volatile, it could easily have taken that long just to write all
the
 #defines for the gas gauge register locations and content.  Ugh.  Don't
even try to
 make me go back to #defines and PEEK/POKE!
Ok, but why did it need to be volatile access? I'm not trying to be obtuse here, I'm trying to understand the problem you're solving.
Sep 10 2002
parent reply Joe Battelle <Joe_member pathlink.com> writes:
Ok, but why did it need to be volatile access? I'm not trying to be obtuse
here, I'm trying to understand the problem you're solving.
I won't steal Robert's thunder, but I guess I don't understand what your objection is to the volatile construct. When interfacing using memory-mapped I/O (as opposed to Port I/O) you frequently deal with pointers to structs that aren't pointing to main memory. The "memory" is just some latch on the other side of an address decoder that has a transient value. The data shouldn't ever be cached. Think of a simple data acquisition system that has three hardware registers all memory-mapped on a PCI-bus card. Look at the following example: timestamp and data dereferences better not be enregistered or hoisted outside the loop or you're in trouble. volatile struct DataAcq { int timestamp; // Base+0 int count; // Base+4 int data; // Base+8 } DataAcq * p = cast(DataAcq *) 0xF0000000; // Base address int[] log; for (int i = 0; i < p.count; ++i) { if (p.timestamp < endtime) log[i] = p.data; else break; } ---------------------- Now I would be just as happy doing this: for (int i = 0; i < p.count; ++i) { volatile(p) { if (p.timestamp < endtime) log[i] = p.data; else break; } } ----------------------- or even just something generic that says don't enregister or hoist any pointer dereferences in the block of code. for (int i = 0; i < p.count; ++i) { volatile() { if (p.timestamp < endtime) log[i] = p.data; else break; } }
Sep 11 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Joe Battelle" <Joe_member pathlink.com> wrote in message
news:almuqb$22cm$1 digitaldaemon.com...
 Think of a simple data acquisition system that has three hardware
registers all
 memory-mapped on a PCI-bus card.  Look at the following example: timestamp
and
 data dereferences better not be enregistered or hoisted outside the loop
or
 you're in trouble.
Ok, but in the particular example here it cannot be hoisted anyway because the assignment to the array log[] invalidates all pointer references.
 volatile struct DataAcq {
 int timestamp; // Base+0
 int count;     // Base+4
 int data;      // Base+8
 }

 DataAcq * p = cast(DataAcq *) 0xF0000000; // Base address
 int[] log;

 for (int i = 0; i < p.count; ++i) {
 if (p.timestamp < endtime)
 log[i] = p.data;
 else
 break;
 }

 ----------------------
 Now I would be just as happy doing this:

 for (int i = 0; i < p.count; ++i) {
 volatile(p) {
 if (p.timestamp < endtime)
 log[i] = p.data;
 else
 break;
 }
 }

 -----------------------
 or even just something generic that says don't enregister or hoist any
pointer
 dereferences in the block of code.

 for (int i = 0; i < p.count; ++i) {
 volatile() {
 if (p.timestamp < endtime)
 log[i] = p.data;
 else
 break;
 }
 }
That's probably a better solution. The trouble with volatile as a type modifier is its ripple effects throughout the typing system.
Sep 11 2002
parent reply Joe Battelle <Joe_member pathlink.com> writes:
Ok, but in the particular example here it cannot be hoisted anyway because
the assignment to the array log[] invalidates all pointer references.
OK. I guess my point is that it doesn't have to. I mean, I think without the volatile specifier, it would be natural for an optimizer to deref p.data once above the loop and put it in a register. I don't see how assigning the deref'd value to an array element would keep the deref (not the assignment) from being hoisted in all D implementations.
That's probably a better solution. The trouble with volatile as a type
modifier is its ripple effects throughout the typing system.
Understood. And I think it's very reasonable to do things differently than in C++ if it keeps the complexity of the compiler down.
Sep 11 2002
parent "Walter" <walter digitalmars.com> writes:
"Joe Battelle" <Joe_member pathlink.com> wrote in message
news:alnutu$i46$1 digitaldaemon.com...
Ok, but in the particular example here it cannot be hoisted anyway
because
the assignment to the array log[] invalidates all pointer references.
OK. I guess my point is that it doesn't have to. I mean, I think without
the
 volatile specifier, it would be natural for an optimizer to deref p.data
once
 above the loop and put it in a register.  I don't see how assigning the
deref'd
 value to an array element would keep the deref (not the assignment) from
being
 hoisted in all D implementations.
The optimizer has to be painfully conservative. In this case, there's no way the optimizer can verify that the array log[] does not overlap whatever p points to. Do people write code like that? Often enough - and the people that do are usually the ones unable to figure out why their code breaks when optimized and they blame the compiler. It's the famed C 'aliasing' problem.
That's probably a better solution. The trouble with volatile as a type
modifier is its ripple effects throughout the typing system.
Understood. And I think it's very reasonable to do things differently
than in
 C++ if it keeps the complexity of the compiler down.
Yes. Glad you understand! It's important for me, in designing a language, to: 1) understand what the problem is, not just implement a solution If I understand the problem correctly, I have a chance at implementing a better solution. 2) recognize the scope of the problem If the scope of the problem is fairly small, it can justify a more targetted solution. For example, volatile permeates partial specialization rules in C++ templates - but why on earth would you be using such to access hardware registers? But you can't get away from it because volatile is a type modifier, so everything that uses types must account for it. It's using a sledgehammer to squash a flea.
Sep 11 2002
prev sibling parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
What about defining a class with gettors and settors?  You would still 
have to do some of the hard work, but it would allow you to have 
something like a struct with volatile members...
Sep 11 2002
parent reply "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
Russell Lewis wrote:

 What about defining a class with gettors and settors?  You would still
 have to do some of the hard work, but it would allow you to have
 something like a struct with volatile members...
That's still a function call, which amount to nothing more than a PEEK or POKE. Plus, whatever you do within the gettor/settor could still get cached! You really need to be able to "drill-down" to the physical environment. That is, if I want a bus cycle to occur (read or write), then by God it must occur when and how I want it to! Presently, not only does D not allow you to prevent enregistration, it does not allow you to atomically bypass the CPU cache. Both are needed tigether in order to obtain true "volatile" behavior. -BobC
Sep 11 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
news:3D7FD186.4480CC4A users.sourceforge.net...
 Presently, not only does D not allow you to prevent enregistration, it
 does not allow you to atomically bypass the CPU cache.  Both are needed
 tigether in order to obtain true "volatile" behavior.
I've got the "volatile" statement now implemented, as in: volatile a = *p; It will prevent any caching of values into and out of the volatile statement.
Sep 14 2002
next sibling parent reply Burton Radons <loth users.sourceforge.net> writes:
Walter wrote:
 "Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
 news:3D7FD186.4480CC4A users.sourceforge.net...
 
Presently, not only does D not allow you to prevent enregistration, it
does not allow you to atomically bypass the CPU cache.  Both are needed
tigether in order to obtain true "volatile" behavior.
I've got the "volatile" statement now implemented, as in: volatile a = *p; It will prevent any caching of values into and out of the volatile statement.
Uh... struct Registers { struct Data { int a; } Data data; int a () { return volatile data.a; } void a (int value) { data.a = value; } } Would that work, or would the get property have to be: int a () { int value; volatile value = data.a; return value; } If so, that's pretty heavy stuff for what will be the most common usage. I'd rather have a function that's turned intrinsic in semantic0, as it retains the "easy to parse dumbly" rule and doesn't have bad worst cases for syntax.
Sep 14 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:3D842697.6080208 users.sourceforge.net...
      struct Registers
      {
          struct Data
          {
              int a;
          }

          Data data;

          int a () { return volatile data.a; }
          void a (int value) { data.a = value; }
      }

 Would that work, or would the get property have to be:

      int a ()
      {
          int value;
          volatile value = data.a;
          return value;
      }

 If so, that's pretty heavy stuff for what will be the most common usage.
   I'd rather have a function that's turned intrinsic in semantic0, as it
 retains the "easy to parse dumbly" rule and doesn't have bad worst cases
 for syntax.
volatile return data.a; should work fine.
Sep 14 2002
parent reply Burton Radons <loth users.sourceforge.net> writes:
Walter wrote:
 "Burton Radons" <loth users.sourceforge.net> wrote in message
 news:3D842697.6080208 users.sourceforge.net...
 
     struct Registers
     {
         struct Data
         {
             int a;
         }

         Data data;

         int a () { return volatile data.a; }
         void a (int value) { data.a = value; }
     }

Would that work, or would the get property have to be:

     int a ()
     {
         int value;
         volatile value = data.a;
         return value;
     }

If so, that's pretty heavy stuff for what will be the most common usage.
  I'd rather have a function that's turned intrinsic in semantic0, as it
retains the "easy to parse dumbly" rule and doesn't have bad worst cases
for syntax.
volatile return data.a; should work fine.
Oh... kay. So what's the advantage over a function? Why does it earn its additional parsing complexity? This also requires the compiler to have knowledge of every statement and expression and how to make it non-caching; the function-based method is just a guaranteed dereference. The property-based read is going to be the most common - I'd even predict that using stuff like "volatile for..." will be consigned to the criminally insane, as leaving it to the caller to manage volatile is a bad idea (but as a property it's fine). So supporting it doesn't look to have much particular extra value.
Sep 15 2002
parent "Walter" <walter digitalmars.com> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:3D8466B4.3080008 users.sourceforge.net...
 volatile return data.a;

 should work fine.
Oh... kay. So what's the advantage over a function?
It's more convenient. No dummy functions.
 Why does it earn
 its additional parsing complexity?
Not sure what you mean. A volatile statement is almost no code to implement.
  This also requires the compiler to
 have knowledge of every statement and expression and how to make it
 non-caching; the function-based method is just a guaranteed dereference.
No, it's pretty trivial to implement. It requires no knowledge of the statements being processed. Each statement gets processed into a sequence of basic blocks. The compiler just marks those blocks as being conceptually like asm blocks - already taken care of within the optimizer.
 The property-based read is going to be the most common - I'd even
 predict that using stuff like "volatile for..." will be consigned to the
 criminally insane, as leaving it to the caller to manage volatile is a
 bad idea (but as a property it's fine).  So supporting it doesn't look
 to have much particular extra value.
I was thinking it would be good for things like: for (int i = 0; i < 1000; i++) volatile sum += *p;
Sep 15 2002
prev sibling parent reply "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
Walter wrote:

 "Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
 news:3D7FD186.4480CC4A users.sourceforge.net...
 Presently, not only does D not allow you to prevent enregistration, it
 does not allow you to atomically bypass the CPU cache.  Both are needed
 tigether in order to obtain true "volatile" behavior.
I've got the "volatile" statement now implemented, as in: volatile a = *p; It will prevent any caching of values into and out of the volatile statement.
Hmmm... That's very different from what I'm used to. I'd prefer to be able to say "when taking the value of this thing, treat it as volatile, no matter how it is dereferenced, or when, or where". Suppse I have a global pointer to a hardware register (say, a 32-bit real-time clock/counter). Adding "volatile" to each and every use will be a nightmare. Missing just one could ruin an entire application, and be next to impossible to detect and debug. Which means all my volatile items would probably need to be single-reference, and thus need to be wrapped within a function call or an object. I'd need an explicit "inline" statement to ensure I can access the dereferenced pointer value in "real-time". Unless I have misunderstood the meaning and use, this interpretation of "volatile" looks like a non-starter to me. -BobC
Sep 15 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
news:3D84342E.948C934 users.sourceforge.net...
 Walter wrote:
 "Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
 news:3D7FD186.4480CC4A users.sourceforge.net...
 Presently, not only does D not allow you to prevent enregistration, it
 does not allow you to atomically bypass the CPU cache.  Both are
needed
 tigether in order to obtain true "volatile" behavior.
I've got the "volatile" statement now implemented, as in: volatile a = *p; It will prevent any caching of values into and out of the volatile statement.
Hmmm... That's very different from what I'm used to.
It is a different way of looking at the issue, but I think it does address your concerns.
 I'd prefer to be able to say "when taking the value of this thing, treat
it
 as volatile, no matter how it is dereferenced, or when, or where".
That's the type modifier approach.
 Suppse I have a global pointer to a hardware register (say, a 32-bit
 real-time clock/counter).
 Adding "volatile" to each and every use will be a
 nightmare.  Missing just one could ruin an entire application, and be next
 to impossible to detect and debug.
The compiler isn't that good at caching values. <g> Caching is nearly always very localized, and is flushed anyway when any assignments through a pointer are done. The only time this can be an issue is if two reads are done with no intervening indirect assignments of any sort. That includes assigning to any globals/statics, assigning to arrays, or calling functions.
 Which means all my volatile items would probably need to be
 single-reference, and thus need to be wrapped within a function call or an
 object.
You could do it that way - and is a common oo technique with property gettors and settors. The gettors and settors get inline expanded, so there's no performance penalty for using them.
 I'd need an explicit "inline" statement to ensure I can access the
 dereferenced pointer value in "real-time".
I think that would be unnecessary. Even the most basic inlining capability will inline a function that consists of nothing but return *p;
 Unless I have misunderstood the meaning and use, this interpretation of
 "volatile" looks like a non-starter to me.
Can you post a bit of code that would cause particular grief?
Sep 15 2002
parent reply "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
Walter wrote:

 "Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
 news:3D84342E.948C934 users.sourceforge.net...
 Suppse I have a global pointer to a hardware register (say, a 32-bit
 real-time clock/counter).
 Adding "volatile" to each and every use will be a
 nightmare.  Missing just one could ruin an entire application, and be next
 to impossible to detect and debug.
The compiler isn't that good at caching values. <g> Caching is nearly always very localized, and is flushed anyway when any assignments through a pointer are done. The only time this can be an issue is if two reads are done with no intervening indirect assignments of any sort. That includes assigning to any globals/statics, assigning to arrays, or calling functions.
It seems you may have a biased view of the vast array of caching strategies used by the wide variety of processors out there! Generally, read and write (and instruction fetch) caching are handled by separate, independent hardware subsystems. Whatever caching the compiler may or may not do is secondary to whatever hardware caching may be going on. I don't really care all that much about assignment (write) caching (for the hardware, I configure it when the processor boots and can generally forget about it). I do care very much about access (read) caching (since the hardware read cache is always enabled, and I want to bypass it when I need to). So, I do agree that compiler caching isn't much of an issue. But that is only a small part of what "volatile" must handle.
 Which means all my volatile items would probably need to be
 single-reference, and thus need to be wrapped within a function call or an
 object.
You could do it that way - and is a common oo technique with property gettors and settors. The gettors and settors get inline expanded, so there's no performance penalty for using them.
Other than they are MUCH heavier than simply having a *p wherever I need to access a volatile value. A typical snippet, say for a timer: if (*p > last_time) { ... } I do this all the time in C. It is especially handy when many items need to be tested very quickly. Such as an array of 8000 IPC mailboxes. If any access were to fetch a prior value from a hardware cache or a CPU register, the program would not be getting the data it needs (which is a true read cycle to the hardware).
 I'd need an explicit "inline" statement to ensure I can access the
 dereferenced pointer value in "real-time".
I think that would be unnecessary. Even the most basic inlining capability will inline a function that consists of nothing but return *p;
It just seems like extra window dressing (baggage) and ahssle for D to do something that is so easy in C. It's like training a dog: If I say "fetch" and the dog doesn't fetch, the dog will be disciplined. Well, OK, it's not exactly like training a dog: If I say "fetch" and my compiler doesn't do a true "fetch", the compiler will be taken out and shot. -BobC
Sep 15 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
news:3D8571DA.5BAE5977 users.sourceforge.net...
 So, I do agree that compiler caching isn't much of an issue.  But that is
only
 a small part of what "volatile" must handle.
How should code be generated differently?
 Other than they are MUCH heavier than simply having a *p wherever I need
to
 access a volatile value.
 A typical snippet, say for a timer:

 if (*p > last_time) { ... }

 I do this all the time in C.  It is especially handy when many items need
to be
 tested very quickly.  Such as an array of 8000 IPC mailboxes.
 If any access were to fetch a prior value from a hardware cache or a CPU
 register, the program would not be getting the data it needs (which is a
true
 read cycle to the hardware).
p.get() is only 5 characters more, and no extra code is actually generated.
 It just seems like extra window dressing (baggage) and ahssle for D to do
 something that is so easy in C.
The trouble is how it percolates through the typing system, affecting everything from function overloading to template partial ordering to template argument deduction to template partial specialization. It just winds up adding a great deal of complexity.
Sep 15 2002
parent reply "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
Walter wrote:

 "Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
 news:3D8571DA.5BAE5977 users.sourceforge.net...
 So, I do agree that compiler caching isn't much of an issue.  But that is
only
 a small part of what "volatile" must handle.
How should code be generated differently?
I don't keep this stuff in my head! Back in the day, I'd go to the processor manual and look it up. And I haven't used an x86 processor on an embedded project since the 8086. But I do know that "volatile" works just fine on an x86! I don't have to look at CPU manuals any more: These days, GCC does "volatile" well on all platforms I use it on. Which probably means that GCC-D will likely have a "proper" volatile, since GCC itself already has the code to handle it on all platforms. That's good enough for me!
 Other than they are MUCH heavier than simply having a *p wherever I need
to
 access a volatile value.
 A typical snippet, say for a timer:

 if (*p > last_time) { ... }

 I do this all the time in C.  It is especially handy when many items need
to be
 tested very quickly.  Such as an array of 8000 IPC mailboxes.
 If any access were to fetch a prior value from a hardware cache or a CPU
 register, the program would not be getting the data it needs (which is a
true
 read cycle to the hardware).
p.get() is only 5 characters more, and no extra code is actually generated.
But how much more code is generated? More importantly, how many times longer does it take to execute that code compared to "*p"? If there is absolutely zero difference (it is one or two instructions), then you and I are describing identical functionality, only from different perspectives. But if there is any difference, then your version of volatile will be both "big" and "slow", and not all that useful for systems programming.
 It just seems like extra window dressing (baggage) and ahssle for D to do
 something that is so easy in C.
The trouble is how it percolates through the typing system, affecting everything from function overloading to template partial ordering to template argument deduction to template partial specialization. It just winds up adding a great deal of complexity.
Then simply outlaw "volatile" for ALL those uses! I want a fast, simple, clean volatile. Not a new type (sub)system. I only need "volatile" for specific pointers that, when dereferenced, will ALWAYS drill through the cache and read the hardware directly. It need not be elegant or general: "Possible" and "fast" will suffice. Hmmm... If the implementation of your proposed volatile syntax is both light and fast, I suppose I could make your initial proposal meet my needs, but I'd need to add a notational layer that would need to be handled by a preprocessor. If that will work, I'm fine with it. For example, I'd place my "volatile" declarations in encoded comments, use an initial preprocessor pass to extract the list of variable names that are "volatile", then use a final preprocessor pass to wrap all references with your notation. So long as I don't have to do it manually, it will probably work for me. I've written several such systems that used encoded comments. The key is to do so in a way that keeps debugging sane enough to use the original source file (instead of the post-processed file). I don't see that being a problem here. But I'd much prefer a solution that didn't demand preprocessing for it to be both general and robust. Preprocessing would kill one of D's special characteristics. But it would be OK if it enabled the use of D for direct hardware-level programming. Hmmmm.... I could do that same transformation if I had access to your parse tree. Which means your compiler should be able to do it just as easily. -BobC
Sep 17 2002
next sibling parent Mark Evans <Mark_member pathlink.com> writes:
I've scanned the postings on this subject and tried to follow them.  I do
appreciate the need for direct hardware access.  Thanks Walter for taking time
with this subject and putting volatile into D.

Someone should take the D compiler and attempt to port one of the free embedded
operating systems to D.  It would have to be customized hardware, not something
like PC/104 which is just a PC in a different mechanical form factor.  Of course
it would have to run an Intel CPU for the time being.

http://rome.sourceforge.net/

Many others exist.

Mark
Sep 17 2002
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
news:3D87F4A4.D37093A8 users.sourceforge.net...
 Walter wrote:

 "Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
 news:3D8571DA.5BAE5977 users.sourceforge.net...
 So, I do agree that compiler caching isn't much of an issue.  But that
is
 only
 a small part of what "volatile" must handle.
How should code be generated differently?
I don't keep this stuff in my head! Back in the day, I'd go to the
processor
 manual and look it up.  And I haven't used an x86 processor on an embedded
 project since the 8086.  But I do know that "volatile" works just fine on
an
 x86!
All it does is prevent caching of loads in a register, and prevent elimination of dead stores.
 p.get() is only 5 characters more, and no extra code is actually
generated.
 But how much more code is generated?  More importantly, how many times
longer
 does it take to execute that code compared to "*p"?
No difference at all.
 If there is absolutely zero difference (it is one or two instructions),
then
 you and I are describing identical functionality, only from different
 perspectives.
Yes.
 But if there is any difference, then your version of volatile will be both
 "big" and "slow", and not all that useful for systems programming.
There should not be any difference.
 The trouble is how it percolates through the typing system, affecting
 everything from function overloading to template partial ordering to
 template argument deduction to template partial specialization. It just
 winds up adding a great deal of complexity.
Then simply outlaw "volatile" for ALL those uses! I want a fast, simple, clean volatile. Not a new type (sub)system.
Offhand, I think it would be hard (meaning kludgy compiler code) to distinguish them.
Sep 18 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Joe Battelle" <Joe_member pathlink.com> wrote in message
news:alm5h9$15fv$1 digitaldaemon.com...
 Your function approach works, but it is really nice to be able to lay a
 structure over a portion of memory-mapped I/O, call the whole darn thing
 volatile and reference away.  I much prefer this to introducing more
intrinsics. The downside is the huge increase in complexity of the type system.
Sep 10 2002
parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
If you can have it work as a storage class that may be good enough.

I'm not sure it's possible!

Sean


"Walter" <walter digitalmars.com> wrote in message
news:almj8g$1k8u$1 digitaldaemon.com...
 "Joe Battelle" <Joe_member pathlink.com> wrote in message
 news:alm5h9$15fv$1 digitaldaemon.com...
 Your function approach works, but it is really nice to be able to lay a
 structure over a portion of memory-mapped I/O, call the whole darn thing
 volatile and reference away.  I much prefer this to introducing more
intrinsics. The downside is the huge increase in complexity of the type system.
Sep 11 2002
parent reply "Walter" <walter digitalmars.com> writes:
While const will work as a storage class, it doesn't really extend to
volatile. This is because it's easy to think of constants, but with
volatiles you'll have pointers to volatiles.

"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:aln4pi$292b$1 digitaldaemon.com...
 If you can have it work as a storage class that may be good enough.

 I'm not sure it's possible!

 Sean


 "Walter" <walter digitalmars.com> wrote in message
 news:almj8g$1k8u$1 digitaldaemon.com...
 "Joe Battelle" <Joe_member pathlink.com> wrote in message
 news:alm5h9$15fv$1 digitaldaemon.com...
 Your function approach works, but it is really nice to be able to lay
a
 structure over a portion of memory-mapped I/O, call the whole darn
thing
 volatile and reference away.  I much prefer this to introducing more
intrinsics. The downside is the huge increase in complexity of the type system.
Sep 11 2002
parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
You can have pointers to constants too.  How do you deal with that?

I'm sorry... I don't have a problem with C++ type modifiers.  In fact I'd
rather have more modifiers, even user-defined ones.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:alnshj$4fo$3 digitaldaemon.com...
 While const will work as a storage class, it doesn't really extend to
 volatile. This is because it's easy to think of constants, but with
 volatiles you'll have pointers to volatiles.

 "Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
 news:aln4pi$292b$1 digitaldaemon.com...
 If you can have it work as a storage class that may be good enough.

 I'm not sure it's possible!

 Sean


 "Walter" <walter digitalmars.com> wrote in message
 news:almj8g$1k8u$1 digitaldaemon.com...
 "Joe Battelle" <Joe_member pathlink.com> wrote in message
 news:alm5h9$15fv$1 digitaldaemon.com...
 Your function approach works, but it is really nice to be able to
lay
 a
 structure over a portion of memory-mapped I/O, call the whole darn
thing
 volatile and reference away.  I much prefer this to introducing more
intrinsics. The downside is the huge increase in complexity of the type system.
Sep 11 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:alnu26$e30$1 digitaldaemon.com...
 You can have pointers to constants too.  How do you deal with that?
D doesn't have pointers to constants as a type. I eventually abandoned all use of const pointers in my C++ code, having discovered: 1) It never once saved me from a bug. 2) There was a significant nuisance involved in getting it all const-correct. 3) The optimizer can't make use of const info, since one can legitimately cast away const-ness. 4) I write compilers, and I still have a hard time remembering if the const goes to the left of the * or the right, or exactly where it goes in more complex type declarations. While const as a storage class makes sense, and is implemented in D, const as a type does not.
 I'm sorry... I don't have a problem with C++ type modifiers.  In fact I'd
 rather have more modifiers, even user-defined ones.
Ok <g>.
Sep 11 2002
next sibling parent reply "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
Walter wrote:

 "Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
 news:alnu26$e30$1 digitaldaemon.com...
 You can have pointers to constants too.  How do you deal with that?
D doesn't have pointers to constants as a type. I eventually abandoned all use of const pointers in my C++ code, having discovered: 1) It never once saved me from a bug. 2) There was a significant nuisance involved in getting it all const-correct. 3) The optimizer can't make use of const info, since one can legitimately cast away const-ness. 4) I write compilers, and I still have a hard time remembering if the const goes to the left of the * or the right, or exactly where it goes in more complex type declarations. While const as a storage class makes sense, and is implemented in D, const as a type does not.
 I'm sorry... I don't have a problem with C++ type modifiers.  In fact I'd
 rather have more modifiers, even user-defined ones.
Ok <g>.
I agree! Let's replace "const" with "ROM", just to make the difference perfectly clear. The implementation should simply disallow write cycles to that location. This can be supported in the compiler, but it can be absolutely guaranteed by placing consts in a separate memory area/page and setting the appropriate MMU bits! Similarly, the compiler can support "volatile", but volatile items should be assigned MMU protection flags that force cache invalidation upon access (which is how hardware does volatile). When there is no MMU, forcing "read-thru" on a cache can be a bit harder, but most embedded CPUs allow you to enable cache for only a specified memory range. Anything outside that range (no matter if it is RAM, ROM or hardware) cannot and will not be cached. Just be sure the hardware designers keep all the cacheable stuff contiguous within the memory map, and all will be well. For example, on my embedded multiprocessor projects, I often intentionally place part of the RAM outside the cache. It may then be used as mailboxes and semaphores by all processors, with the guarantee the all processors have the same view of that memory at all times. (And all embedded processors I'm aware of support an atomic read-modify-write cycle, so again hardware helps.) The central OS has to enforce the "guarantee" for processes residing in common memory that is cached. All RTOS's I've used do this quite well. -BobC
Sep 11 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message
news:3D7FD42F.DA459077 users.sourceforge.net...
 I agree!  Let's replace "const" with "ROM", just to make the difference
 perfectly clear.  The implementation should simply disallow write cycles
to
 that location.  This can be supported in the compiler, but it can be
 absolutely guaranteed by placing consts in a separate memory area/page and
 setting the appropriate MMU bits!
That's how const works as a storage class now - but it is up to the implementation if it wants to place them in ROM or not.
 Similarly, the compiler can support "volatile", but volatile items should
be
 assigned MMU protection flags that force cache invalidation upon access
(which
 is how hardware does volatile).

 When there is no MMU, forcing "read-thru" on a cache can be a bit harder,
but
 most embedded CPUs allow you to enable cache for only a specified memory
 range.  Anything outside that range (no matter if it is RAM, ROM or
hardware)
 cannot and will not be cached.  Just be sure the hardware designers keep
all
 the cacheable stuff contiguous within the memory map, and all will be
well.
 For example, on my embedded multiprocessor projects, I often intentionally
 place part of the RAM outside the cache.  It may then be used as mailboxes
and
 semaphores by all processors, with the guarantee the all processors have
the
 same view of that memory at all times.  (And all embedded processors I'm
aware
 of support an atomic read-modify-write cycle, so again hardware helps.)
The
 central OS has to enforce the "guarantee" for processes residing in common
 memory that is cached.  All RTOS's I've used do this quite well.
Do you see a "volatile" segment in the executable, analogously to a "rom" segment?
Sep 12 2002
parent "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
Walter wrote:

 Do you see a "volatile" segment in the executable, analogously to a "rom"
 segment?
Yup! But only if there was no other way to obtain it. Since a compiler has to be targeted for its platform (CPU + OS), that targeting would have to provide the most logical implementation of volatile for that platform. Some CPU instruction sets have instruction prefixes that flag the associated data access(es) as "read-thru". For such CPUs, D could handle the entire issue itself, with no need for a volatile segment in the executable or OS support. The problem with volatile segments is that many of the standard executable formats don't allow for it, so it can't be presented to the loader. Fortunately, most platforms where this might be needed have implemented suitable extensions to the executable format. Extensions which, unfortunately, tend to be proprietary. Most platforms provide an OS call to map any memory segment as desired, so the needed logic can generally be added to the program startup code (the venerable cstart.a on C systems, which I've hacked many a time). -BobC
Sep 13 2002
prev sibling parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
So there's nothing preventing you from writing code like this?

void BreakStuff(char* c)
{
    *c = 2;
}

int main()
{
    const char s = 1;
    BreakStuff(&s);
    printf("now it's %d\n",s); // prints 2.
}

Yeah that const storage class really does a lot of good.

You say const pointers never once saved you from a bug.  Congratulations.
They certainly help me out, if only as sanity checks.  If I can get the
compiler to verify any part of the correctness of my code, that's great.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:alo3rr$1ab1$1 digitaldaemon.com...
 "Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
 news:alnu26$e30$1 digitaldaemon.com...
 You can have pointers to constants too.  How do you deal with that?
D doesn't have pointers to constants as a type. I eventually abandoned all use of const pointers in my C++ code, having discovered: 1) It never once saved me from a bug. 2) There was a significant nuisance involved in getting it all const-correct. 3) The optimizer can't make use of const info, since one can legitimately cast away const-ness. 4) I write compilers, and I still have a hard time remembering if the
const
 goes to the left of the * or the right, or exactly where it goes in more
 complex type declarations.

 While const as a storage class makes sense, and is implemented in D, const
 as a type does not.

 I'm sorry... I don't have a problem with C++ type modifiers.  In fact
I'd
 rather have more modifiers, even user-defined ones.
Ok <g>.
Sep 12 2002
next sibling parent reply Pavel Minayev <evilone omen.ru> writes:
Sean L. Palmer wrote:

 So there's nothing preventing you from writing code like this?
 
 void BreakStuff(char* c)
 {
     *c = 2;
 }
 
 int main()
 {
     const char s = 1;
     BreakStuff(&s);
     printf("now it's %d\n",s); // prints 2.
 }
You cannot write such code in D. You simply can't take address of a constant, because constants don't have addresses - they are just values, inserted in-place where constant is used. There is no such C++ nonsense as "constant variable" in D. Constant is a constant. Variable is a variable. You cannot use variables where constants are expected (in case-block, for example). Nor can you use constants where variables (and lvalues in general) are required - as out arguments etc
Sep 12 2002
parent "Sean L. Palmer" <seanpalmer earthlink.net> writes:
So it's not possible to force the compiler to pass a large constant by
reference?

It's often tricky to initialize something using initializer syntax.  How
will you build complex constants?  Do constants then always have to be known
at compile time?

If what you're saying is true I'd expect not to be able to build any kind of
structure out of constants such as a static const linked list.  Can't take
addresses, can't have lists.  Or graphs.

I suppose if nobody modifies a thing, and nobody externally can modify a
thing (not public) then for all intents and purposes it is const, but it's
sure nice to be able to keep people from modifying stuff without the
no-address restriction.  Is there a way to get around this?  People will
abuse it (witness mutable in C++, a way to declare a something as constant
to everyone but me.  Not constant so much as read-only;. at least to you.)

I guess there are two different things people use const for... real
constants (forever unchanging values) and read-only access protection.  D
only supports the former.

Sean

"Pavel Minayev" <evilone omen.ru> wrote in message
news:alqlrj$26ks$1 digitaldaemon.com...
 Sean L. Palmer wrote:

 So there's nothing preventing you from writing code like this?

 void BreakStuff(char* c)
 {
     *c = 2;
 }

 int main()
 {
     const char s = 1;
     BreakStuff(&s);
     printf("now it's %d\n",s); // prints 2.
 }
You cannot write such code in D. You simply can't take address of a constant, because constants don't have addresses - they are just values, inserted in-place where constant is used. There is no such C++ nonsense as "constant variable" in D. Constant is a constant. Variable is a variable. You cannot use variables where constants are expected (in case-block, for example). Nor can you use constants where variables (and lvalues in general) are required - as out arguments etc
Sep 13 2002
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:alqi86$1pla$1 digitaldaemon.com...
 So there's nothing preventing you from writing code like this?

 void BreakStuff(char* c)
 {
     *c = 2;
 }

 int main()
 {
     const char s = 1;
     BreakStuff(&s);
     printf("now it's %d\n",s); // prints 2.
 }

 Yeah that const storage class really does a lot of good.

 You say const pointers never once saved you from a bug.  Congratulations.
 They certainly help me out, if only as sanity checks.  If I can get the
 compiler to verify any part of the correctness of my code, that's great.
I agree const as a type modifier does have some utility, I argue that it is minor compared with the cost of it. Some other points: o D does not allow taking the address of a const. Storage is not necessarilly even allocated for a const. o While D allows one to pass pointers around, it is almost never necessary to do so. Use out and inout parameters, which should give an error when used with const variables. o In D, if you do wind up using a pointer, it is for either interfacing with C or for doing something unusual that presumptively you're careful with. You can do anything with pointers in D - they're meant as a way around the typing system. o C doesn't help much there anyway, nothing stops casting away const'ness and modifying it. o The example will still print 1, not 2, because the implementation assumes that s is 1 and will substitute in the 1 in the printf. This is unlike C, since because const isn't guaranteed to be immutable, the compiler cannot make such an assumption.
Sep 14 2002