www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Friendly-C

reply "bearophile" <bearophileHUGS lycos.com> writes:
Found through Reddit. A nice incomplete list that specifies a 
"friendly C" that replaces many occurrences of "X has undefined 
behavior" with "X results in an unspecified value":

http://blog.regehr.org/archives/1180

But I agree with one comment:

memcpy and memmove ought to remain distinct, but I think that an 
overlapping memcpy ought to result in unspecified garbage being 
written rather than undefined behavior.<
Bye, bearophile
Aug 28 2014
next sibling parent reply "Kagamin" <spam here.lot> writes:
This prohibits diagnostic of integer overflow. The runtime is no 
longer allowed to throw exception in such case.
Aug 28 2014
parent "Kagamin" <spam here.lot> writes:
Though good as an additional dialect.
Aug 28 2014
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Thursday, 28 August 2014 at 07:36:28 UTC, bearophile wrote:
 Found through Reddit. A nice incomplete list that specifies a 
 "friendly C" that replaces many occurrences of "X has undefined 
 behavior" with "X results in an unspecified value":

 http://blog.regehr.org/archives/1180

 But I agree with one comment:

memcpy and memmove ought to remain distinct, but I think that 
an overlapping memcpy ought to result in unspecified garbage 
being written rather than undefined behavior.<
Bye, bearophile
It forces all the load to potentially have side effects, which, in turn, limit dramatically what the optimizer can do.
Aug 28 2014
next sibling parent reply ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Thu, 28 Aug 2014 09:12:15 +0000
deadalnix via Digitalmars-d <digitalmars-d puremagic.com> wrote:

 It forces all the load to potentially have side effects, which,=20
 in turn, limit dramatically what the optimizer can do.
but there is alot code that doesn't need "super-speed". it's ok to fallback to "standard C" for the parts that need all speed we can have w/o assembly. but most programs are ok with not-so-extensive optimizations, and writing code in "friendly c" is much easier than in "standard c". i myself compiling all my C code with -fwrapv -fno-strict-aliasing -fno-delete-null-pointer-checks. i believe that compiler was made to make my life easier, not to make it harder. so it's the compiler who should obey my orders, not vice versa. ;-)
Aug 28 2014
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Friday, 29 August 2014 at 02:10:55 UTC, ketmar via 
Digitalmars-d wrote:
 On Thu, 28 Aug 2014 09:12:15 +0000
 deadalnix via Digitalmars-d <digitalmars-d puremagic.com> wrote:

 It forces all the load to potentially have side effects, 
 which, in turn, limit dramatically what the optimizer can do.
but there is alot code that doesn't need "super-speed". it's ok to fallback to "standard C" for the parts that need all speed we can have w/o assembly. but most programs are ok with not-so-extensive optimizations, and writing code in "friendly c" is much easier than in "standard c". i myself compiling all my C code with -fwrapv -fno-strict-aliasing -fno-delete-null-pointer-checks. i believe that compiler was made to make my life easier, not to make it harder. so it's the compiler who should obey my orders, not vice versa. ;-)
It is not only about null checks. If the value in unspecified, rather than the behavior undefined, it means that no load or store can be optimized away or reordered, unless the compiler can prove that is won't fault. I'm talking about doubtful optimization to gain 0.5% here, but that everything single variable except locals must be considered volatile in the C sense.
Aug 28 2014
next sibling parent reply "Ola Fosheim Gr" <ola.fosheim.grostad+dlang gmail.com> writes:
On Friday, 29 August 2014 at 05:31:02 UTC, deadalnix wrote:
 If the value in unspecified, rather than the behavior 
 undefined, it means that no load or store can be optimized away 
 or reordered, unless the compiler can prove that is won't fault.

 I'm talking about doubtful optimization to gain 0.5% here, but 
 that everything single variable except locals must be 
 considered volatile in the C sense.
Why is that? If the value is unspecified then you can assume a random value, so no single store is needed unless explicitly volatile? Wrapping semantics is also bad for optimization, though. You cannot reduce length < length+N to true in generic code, so you need to establish bounds on x or create multiple execution paths. This is usually silly when using a 64 bit int...
Aug 28 2014
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Friday, 29 August 2014 at 05:55:18 UTC, Ola Fosheim Gr wrote:
 On Friday, 29 August 2014 at 05:31:02 UTC, deadalnix wrote:
 If the value in unspecified, rather than the behavior 
 undefined, it means that no load or store can be optimized 
 away or reordered, unless the compiler can prove that is won't 
 fault.

 I'm talking about doubtful optimization to gain 0.5% here, but 
 that everything single variable except locals must be 
 considered volatile in the C sense.
Why is that? If the value is unspecified then you can assume a random value, so no single store is needed unless explicitly volatile? Wrapping semantics is also bad for optimization, though. You cannot reduce length < length+N to true in generic code, so you need to establish bounds on x or create multiple execution paths. This is usually silly when using a 64 bit int...
Because, if you don't have undefined BEHAVIOR, you can't change the BEHAVIOR. So you have to prove that store/loads won't fault before doing anything with them. As per spec. Or you'll change the BEHAVIOR.
Aug 29 2014
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Friday, 29 August 2014 at 08:29:50 UTC, deadalnix wrote:
 Because, if you don't have undefined BEHAVIOR, you can't change 
 the BEHAVIOR. So you have to prove that store/loads won't fault 
 before doing anything with them. As per spec. Or you'll change 
 the BEHAVIOR.
By fault you mean trap an illegal address. I don't think that should be required unless it is volatile, but that would depend on the spec. "Undefined behaviour" goes much further and can in theory propagate all over the program…
Aug 29 2014
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Friday, 29 August 2014 at 08:50:59 UTC, Ola Fosheim Grøstad
wrote:
 On Friday, 29 August 2014 at 08:29:50 UTC, deadalnix wrote:
 Because, if you don't have undefined BEHAVIOR, you can't 
 change the BEHAVIOR. So you have to prove that store/loads 
 won't fault before doing anything with them. As per spec. Or 
 you'll change the BEHAVIOR.
By fault you mean trap an illegal address. I don't think that should be required unless it is volatile, but that would depend on the spec. "Undefined behaviour" goes much further and can in theory propagate all over the program…
Not only illegal address. You can trap for various reason, for instance GC read/write barrier. Indeed, undefined behavior can be anything, by definition, and this cannot. Still, removing undefined behavior and replace it by undefined values cause the optimizer to have to prove that load/store won't fault, which is going to kill any expectation of performance.
Aug 29 2014
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Friday, 29 August 2014 at 17:29:49 UTC, deadalnix wrote:
 Indeed, undefined behavior can be anything, by definition, and 
 this cannot. Still, removing undefined behavior and replace it 
 by undefined values cause the optimizer to have to prove that 
 load/store won't fault, which is going to kill any expectation 
 of performance.
I still don't understand what kind of fault your are talking about. Granted if you store in more than one location you have to make at least one move, but if you only store in one location you have the same situation as if you are optimizing: a=a; Clearly you can remove this without doing any loads or stores?
Aug 29 2014
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Friday, 29 August 2014 at 17:33:14 UTC, Ola Fosheim Grøstad
wrote:
 On Friday, 29 August 2014 at 17:29:49 UTC, deadalnix wrote:
 Indeed, undefined behavior can be anything, by definition, and 
 this cannot. Still, removing undefined behavior and replace it 
 by undefined values cause the optimizer to have to prove that 
 load/store won't fault, which is going to kill any expectation 
 of performance.
I still don't understand what kind of fault your are talking about. Granted if you store in more than one location you have to make at least one move, but if you only store in one location you have the same situation as if you are optimizing: a=a; Clearly you can remove this without doing any loads or stores?
No, as the load or the store involved can fault/trap.
Aug 29 2014
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Friday, 29 August 2014 at 17:34:42 UTC, deadalnix wrote:
 No, as the load or the store involved can fault/trap.
What kind of trap? Not getting an exception will not necessarily change correctness. Removing a division by zero is not changing correctness: ok = (1.0/0.0 == 1.0) || true assert(ok==true) I would expect a compiler to remove a non-volatile "a=a" even if a is in read-only memory. It does not change correctness?
Aug 29 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Friday, 29 August 2014 at 17:46:31 UTC, Ola Fosheim Grøstad 
wrote:
 On Friday, 29 August 2014 at 17:34:42 UTC, deadalnix wrote:
 No, as the load or the store involved can fault/trap.
What kind of trap? Not getting an exception will not necessarily change correctness.
It does. An access that can potentially fault is more or less equivalent to this: if(p is null) throw new Error(); *p = ...; When you remove the access, you're removing the test, which changes program semantics.
Aug 29 2014
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Friday, 29 August 2014 at 18:46:20 UTC, Marc Schütz wrote:
 On Friday, 29 August 2014 at 17:46:31 UTC, Ola Fosheim Grøstad 
 wrote:
 On Friday, 29 August 2014 at 17:34:42 UTC, deadalnix wrote:
 No, as the load or the store involved can fault/trap.
What kind of trap? Not getting an exception will not necessarily change correctness.
It does. An access that can potentially fault is more or less equivalent to this: if(p is null) throw new Error(); *p = ...; When you remove the access, you're removing the test, which changes program semantics.
NULL or any address, as you can change memory protection on a per page basis, and have different protection for read write and execution. Even better, the fault do not have to result in an exception or other form of termination. in fact, it is demonstrated that the fault mechanism on x86 is Turing complete.
Aug 29 2014
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Friday, 29 August 2014 at 19:37:28 UTC, deadalnix wrote:
 NULL or any address, as you can change memory protection on a 
 per
 page basis, and have different protection for read write and
 execution.
If your semantics require page faults to trap then you should declare the storage volatile. You cannot claim that the semantics of your program presumes that the program is incorrect? Now, the programming language might require compiled programs to probe measures of incorrectness at some specific point in time: before compilation, before output is written to disk or after completion (with a rollback), but that is the semantics of a given language, not the semantics of the program. Thus it bears little relevance to a discussion of whether it is sound to assume unspeficied values and avoid stores.
 Even better, the fault do not have to result in an exception or
 other form of termination. in fact, it is demonstrated that the
 fault mechanism on x86 is Turing complete.
I don't see what TMs have to do with it. The language compiler controls generated code.
Aug 29 2014
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Friday, 29 August 2014 at 21:03:59 UTC, Ola Fosheim Grøstad
wrote:
 On Friday, 29 August 2014 at 19:37:28 UTC, deadalnix wrote:
 NULL or any address, as you can change memory protection on a 
 per
 page basis, and have different protection for read write and
 execution.
If your semantics require page faults to trap then you should declare the storage volatile.
Or what ? Or it is undefined behavior ?
Aug 29 2014
parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Saturday, 30 August 2014 at 00:50:23 UTC, deadalnix wrote:
 On Friday, 29 August 2014 at 21:03:59 UTC, Ola Fosheim Grøstad
 If your semantics require page faults to trap then you should 
 declare the storage volatile.
Or what ? Or it is undefined behavior ?
Or else you cannot rely on them to fire. Traps are for specifying "extensions" to the hardware logic defaults (e.g. creating an emulator). If traps don't fire it basically means the extensions aren't needed for a correct program?
Aug 29 2014
prev sibling parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Friday, 29 August 2014 at 18:46:20 UTC, Marc Schütz wrote:
 It does. An access that can potentially fault is more or less 
 equivalent to this:

     if(p is null)
         throw new Error();
     *p = ...;

 When you remove the access, you're removing the test, which 
 changes program semantics.
No, if the access can trap unexpectedly then the program was incorrect to begin with. A deliberate exception is not the same as an unexpected trap. Removing the reason for the unexpected trap does not make the program more incorrect. So you don't change the semantics of a correct program, you only change when an incorrect program is classified as incorrect. If the semantics is that of nondeterministic evaluation then a fault (bottom) might not be incorrect, you might mitigate exceptional partial results if you can find another path that does not lead to an exception. Like this: return select( f(x) or g(x) or h(x) ) It is sufficient here that just one of f, g, and h produces a valid result, the other ones might go down into infinite recursion or whatever.
Aug 29 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Friday, 29 August 2014 at 20:41:34 UTC, Ola Fosheim Grøstad 
wrote:
 On Friday, 29 August 2014 at 18:46:20 UTC, Marc Schütz wrote:
 It does. An access that can potentially fault is more or less 
 equivalent to this:

    if(p is null)
        throw new Error();
    *p = ...;

 When you remove the access, you're removing the test, which 
 changes program semantics.
No, if the access can trap unexpectedly then the program was incorrect to begin with. A deliberate exception is not the same as an unexpected trap.
If it was indeed unexpected then you're right, but how do you know it wasn't intentional? If accessing an invalid pointer isn't unspecified behaviour (which this discussion is about), then why shouldn't someone rely on it?
Aug 29 2014
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Friday, 29 August 2014 at 20:54:10 UTC, Marc Schütz wrote:
 If it was indeed unexpected then you're right, but how do you 
 know it wasn't intentional?
Because then it should have been declared volatile?
 If accessing an invalid pointer isn't unspecified behaviour 
 (which this discussion is about), then why shouldn't someone 
 rely on it?
Not sure what you mean by unspecified behaviour for pointers. The discussion was about unspecified values for value types. The statement "a = a;" does nothing unless a is volatile. If a is read-only then the statement is incorrect. Removing an incorrect do-nothing statement is quite acceptable.
Aug 29 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Friday, 29 August 2014 at 21:09:09 UTC, Ola Fosheim Grøstad 
wrote:
 On Friday, 29 August 2014 at 20:54:10 UTC, Marc Schütz wrote:
 If it was indeed unexpected then you're right, but how do you 
 know it wasn't intentional?
Because then it should have been declared volatile?
 If accessing an invalid pointer isn't unspecified behaviour 
 (which this discussion is about), then why shouldn't someone 
 rely on it?
Not sure what you mean by unspecified behaviour for pointers. The discussion was about unspecified values for value types.
I didn't really get that from reading the exchange between you and deadalnix, sorry. You were talking about loads and stores, so I assumed pointers are involved.
 The statement "a = a;" does nothing unless a is volatile. If a 
 is read-only then the statement is incorrect. Removing an 
 incorrect do-nothing statement is quite acceptable.
This is what I was thinking about in my first reply to you, from the article in the OP: "4. Reading from an invalid pointer either traps or produces an unspecified value. In particular, all but the most arcane hardware platforms can produce a trap when dereferencing a null pointer, and the compiler should preserve this behavior." As I read it, they suggest that an invalid pointer access should trap (but permit yielding an unspecified value). On the other hand, point 13. directly covers the "a = a;" case: "The compiler retains the ability to optimize away pointer dereferences that it can prove are redundant or otherwise useless." But still, taken together with point 4, it is debatable whether trapping counts as "redundant or otherwise useless".
Aug 29 2014
parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Friday, 29 August 2014 at 21:35:11 UTC, Marc Schütz wrote:
 "4. Reading from an invalid pointer either traps or produces an 
 unspecified value. In particular, all but the most arcane 
 hardware platforms can produce a trap when dereferencing a null 
 pointer, and the compiler should preserve this behavior."

 As I read it, they suggest that an invalid pointer access 
 should trap (but permit yielding an unspecified value).
"either" => it can produce an unspecified value without trapping. (But Friendly-C is not a coherent spec, it is a blog post…)
 But still, taken together with point 4, it is debatable whether 
 trapping counts as "redundant or otherwise useless".
You have to look at the intended semantics of the program and how to make it correct. If loads and stores are meant to have side effects then it should be declared volatile, that is what "volatile" is for. (or use asm or an intrinsic with volatile semantics).
Aug 29 2014
prev sibling parent ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Fri, 29 Aug 2014 05:31:00 +0000
deadalnix via Digitalmars-d <digitalmars-d puremagic.com> wrote:

 If the value in unspecified, rather than the behavior undefined,=20
 it means that no load or store can be optimized away or=20
 reordered, unless the compiler can prove that is won't fault.
will it really hurt most programs? compiler is still be able to remove unused assignments, dead code and so on. and turning `while (v) {}` to `while (true) {}` too, btw, so no, not 'volatile'. and i can't see why it should prevent load reordering too. what it actually forbids is throwing away overflow checks like 'if (a+100 < a)' -- the same effect as '-fwrapv'. aliasing sux too: only a small fraction of code gains something from optimisations based on aliasing rules, yet that rules pushed down the throat for everyone. those who need speed will be able to use 'C with UB' (and chase mysterious bugs) and majority of people will get much nicer C language to write their code. current C is anti-human.
Aug 28 2014
prev sibling next sibling parent ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Thu, 28 Aug 2014 09:12:15 +0000
deadalnix via Digitalmars-d <digitalmars-d puremagic.com> wrote:

p.s. code generated by DMD, for example, at least two times slower than
code generated by GDC. but most people are ok with it.
Aug 28 2014
prev sibling parent "Kagamin" <spam here.lot> writes:
On Thursday, 28 August 2014 at 09:12:17 UTC, deadalnix wrote:
 It forces all the load to potentially have side effects, which, 
 in turn, limit dramatically what the optimizer can do.
Why? I could be true in case of unspecified behavior, but not in case of unspecified value.
Aug 28 2014