From: Gabriel Dos Reis on
"Peter Dimov" <pdimov(a)gmail.com> writes:

| Walter Bright wrote:
|
| > Here's what Digital Mars C++ does, which implements C99 complex numbers:
| >
| > ------------------ program ------------------
| > #include <complex.h>
| >
| > complex long double f( complex long double c )
| > {
| > return c;
| > }
| > ------------------- asm ---------------------------
| > ?f@@YA_W_W@Z:
| > fld tbyte ptr 4[ESP]
| > fld tbyte ptr 0Eh[ESP]
| > ret
| > --------------------------------------------------
|
| So your point is that having complex as a built-in allows you to define
| an ABI that returns it in ST0:ST1 (but you still pass it on the stack.)

I believe I must again inject facts into this surreal discussion.
Not just because a type is blessed built-in by the language definition
means that compilers will effectively use registers in passing arguments
or returning values. The 32-bit PowerPC processor specific ABI


http://www.opensolaris.org/os/community/power_pc/powerpc_doc_library/elfspec_ppc.pdf

specifies that (page 3-19) that when values of the built-in type "long
double" are passed "by value" in function call, the compiler must
pass that value "by reference" and introduce a copy (on stack) where
necessary to enforce pass-by=value semantics.

Similarly, when a value of the built-in type "long double" is
returned, the ABI specifies that the compiler should use the stack and
instead return the address of the value on stack (page 3-22)

Values of type long double and structures or unions that do not meet
the requirements for being returned in registers are returned in a
storage buffer allocated by the caller. The address of
this buffer is passed as a hidden argument in r3 as if it were the
first argument, causing gr in the argument passing algorithm above
to be initialized to 4 instead of 3.


That also suggests that the 32-bit PowerPC ABI specifies that values
of UDT can be returned in registers when they meet specific
conditions. Indeed, on page 3-22:

A structure or union whose size is less than or equal to 8 bytes
shall be returned in r3 and r4, as if it were first stored in an
8-byte aligned memory area and then the low-addressed word were
loaded into r3 and the high-addressed word into r4. Bits beyond the
last member of the structure or union are not defined.


This psABI is a living example of an ABI which does not guarantee
register use for passing and returning values of built-in types, but
which use registers to return values of structure types if they
are small enough.

| I admit that it's unlikely for any ABI to return UDTs in registers.

Fortunately, not all compilers out there made the decision to actively
unsupport abstractions like the Digital Mars compiler.

The Itanium Software Conventions and Runtime Architecture Guide
specifies (page 8-13) that certain aggregates are returned in
registers. That is lift to C++ UDT if they don't have non-trivial
*copy-constructor* or *destructor*. See the "common C++ ABI"
specification

http://www.codesourcery.com/cxx-abi/abi.html#calls

In general, C++ return values are handled just like C return
values. This includes class type results returned in
registers. However, if the return value type has a non-trivial copy
constructor or destructor, the caller allocates space for a
temporary, and passes a pointer to the temporary as an implicit first
parameter preceding both the this parameter and user parameters. The
callee constructs the return value into this temporary.

A result of an empty class type will be returned as though it were a
struct containing a single char, i.e. struct S { char c; };. The
actual content of the return register is unspecified. On Itanium, the
associated NaT bit must not be set.

--
Gabriel Dos Reis
gdr(a)integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Andrei Alexandrescu (See Website For Email) on
James Kanze wrote:
> I don't know quite what different definitions we could be using.
> Undefined behavior occurs when the language specification places
> no definition on the behavior. I don't know how you can easily
> search for it, because it is the absence of a definition. Java
> (and most other languages) don't use the term, or even specify
> explicitely what they don't specify. So the reponse is rather
> the opposite: unless you can find some statement in the language
> specification which defines this behavior, it is undefined
> behavior.

I was hoping I'd be saved of searching online docs, but now it looks
like I had to, so so be it.

There might be a terminology confusion here, which I'd like to clear
from the beginning:

1. A program "has undefined behavior" = effectively anything could
happen as the result of executing that program. The metaphor with the
demons flying out of one's nose comes to mind. Anything.

2. A program "produces an undefined value" = the program could produce
an unexpected value, while all other values, and that program's
integrity, are not violated.

The two are fundamentally different because in the second case you can
still count on objects being objects etc.; the memory safety of the
program has not been violated. Therefore the program is much easier to
debug.

C++ allows programs with (1). We might also consider that it allows
programs with (2) under the name of "unspecified behavior" or
"implementation-dependent behavior". (There would be a subtle difference
there, but passons.)

My current understanding is that Java programs never exhibit (1), and
might exhibit (2) only on values that can't be read atomically (which
remarkably are never pointers). To find out whether my understanding is
correct, I looked up the language spec, which says after a discussion of
the memory model (see
http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.3):

"Therefore, a data race cannot cause incorrect behavior such as
returning the wrong length for an array."

Later on that page, there is a section "17.7 Non-atomic Treatment of
double and long" that discusses the exact issue we are talking about here.

"Some implementations may find it convenient to divide a single write
action on a 64-bit long or double value into two write actions on
adjacent 32 bit values. For efficiency's sake, this behavior is
implementation specific; Java virtual machines are free to perform
writes to long and double values atomically or in two parts.

For the purposes of the Java programming language memory model, a single
write to a non-volatile long or double value is treated as two separate
writes: one to each 32-bit half. This can result in a situation where a
thread sees the first 32 bits of a 64 bit value from one write, and the
second 32 bits from another write. Writes and reads of volatile long and
double values are always atomic. Writes to and reads of references are
always atomic, regardless of whether they are implemented as 32 or 64
bit values.

VM implementors are encouraged to avoid splitting their 64-bit values
where possible. Programmers are encouraged to declare shared 64-bit
values as volatile or synchronize their programs correctly to avoid
possible complications."

This section can be understood only if we know what a Java program does
once it's read an invalid (say, NaN) value. Will it crash?


Andrei

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Fred Long on
James Kanze wrote:
> ...
>>>> Writing to a double while another thread is reading it is
>>>> undefined behavior in Java.
>
>>> I've ran a number of searches for that ("java undefined behavior
>>> double", "java undefined behavior threads double" etc.), no avail. I'd
>>> be glad if you provided a reference. Thanks!
>
>>> Maybe (also) we're using slightly different definitions for undefined
>>> behavior?
>
>> I am still waiting for a response on this issue, or a retraction of the
>> initial statement.
>
> Sorry, I missed your previous posting. (I've not been following
> this thread too closely, since D is not a topic which interests
> me much.)
>
> I don't know quite what different definitions we could be using.
> Undefined behavior occurs when the language specification places
> no definition on the behavior. I don't know how you can easily
> search for it, because it is the absence of a definition. Java
> (and most other languages) don't use the term, or even specify
> explicitely what they don't specify. So the reponse is rather
> the opposite: unless you can find some statement in the language
> specification which defines this behavior, it is undefined
> behavior.
>
> Actually, I think there are even more cases, involving multiple
> writes to different variables. But the case of double or long
> is flagrant, since the language specification does not require
> the writes to be atomic.
> ...

See: http://java.sun.com/docs/books/jls/third_edition/html/memory.html

Section 17.7 Non-atomic Treatment of double and long

Fred Long.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Gabriel Dos Reis on
"Andrei Alexandrescu (See Website For Email)"
<SeeWebsiteForEmail(a)erdani.org> writes:

[...]

| There might be a terminology confusion here, which I'd like to clear
| from the beginning:
|
| 1. A program "has undefined behavior" = effectively anything could
| happen as the result of executing that program. The metaphor with the
| demons flying out of one's nose comes to mind. Anything.

Why is not that the value of the computation?

| 2. A program "produces an undefined value" = the program could produce
| an unexpected value, while all other values, and that program's
| integrity, are not violated.
|
| The two are fundamentally different because in the second case you can
| still count on objects being objects etc.;

I don't see anything fundamental in that difference.

--
Gabriel Dos Reis
gdr(a)integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: David Abrahams on
"Andrei Alexandrescu (See Website For Email)"
<SeeWebsiteForEmail(a)erdani.org> writes:

> There might be a terminology confusion here, which I'd like to clear
> from the beginning:
>
> 1. A program "has undefined behavior" = effectively anything could
> happen as the result of executing that program. The metaphor with the
> demons flying out of one's nose comes to mind. Anything.
>
> 2. A program "produces an undefined value" = the program could produce
> an unexpected value, while all other values, and that program's
> integrity, are not violated.
>
> The two are fundamentally different because in the second case you can
> still count on objects being objects etc.; the memory safety of the
> program has not been violated. Therefore the program is much easier to
> debug.

Seriously?

IME you're at least likely to crash noisily close to the undefined
behavior. If you make everything defined the program necessarily
soldiers on until one of your own internal checks is able to notice
that something went wrong. Or am I missing something?

I don't have any real experience with Java, but Python generally
exhibits Java-like behavior, and I don't find it easier to debug than
C++.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]