This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] Fold VIEW_CONVERT_EXPR <type, STRING_CST> generated by Fortran FE a lot (PR target/35366)


Tobias Schlüter wrote, at 11/11/2008 8:21 AM:
> Jakub Jelinek wrote:
>> For TRANSFER I found:
>> http://gcc.gnu.org/ml/gcc-patches/2007-05/msg01903.html
>> but I'm still unconvinced.  I'd say arguing that the logical must
>> hold all the bits is the same as if you said
>> real(kind=8), parameter :: a = transfer (transfer (3.1415926d0, 0), 0.d0)
>> print *, a
>> end
>> must print 3.14159... 

IMO, this equivalence isn't entirely clear.  The double-transfer case is
called out as a special case in the standard, and it appears to be
different from what's required to actually be stored in a logical variable.

I'll start with the summary of my conclusions, since this gets a bit
complicated and tangly.  The conclusion is that the testcases in
question are not testing things required by the standard.

This bit of code in the transfer testcase is a complicated mess,
standard-wise; it's probably illegal, but if we can preserve it as a
quality-of-implementation issue, that arguably a good thing.  It should
be documented in the testcase as QOI, not standard-mandated, though:

  i = transfer(transfer(ip1, .true.), 0)
  if (i .ne. ip1) call abort ()

Meanwhile, these bits of the testcase are definitely relying on
undefined behavior when the compiler encounters illegal code, as the
function call returns an out-of-range value and the standard doesn't
call them out as exceptions.  I definitely should have documented these
as QOI when I wrote them.  It would be nice to preserve them, but I
think they're rather unlikely to show up in real code.

  i = transfer(ap1, 0)
  if (i .ne. ip1) call abort ()

  a = transfer(ip1, .true.)
  i = transfer(a, 0)
  if (i .ne. ip1) call abort ()

  i = ip1
  a = transfer(i, .true.)
  i = transfer(a, 0)
  if (i .ne. ip1) call abort ()

As for the Hollerith case: Hollerith with logicals was never included in
any Fortran standard, and IMHO there is no call whatsoever for
supporting it.

Now, to the analysis.  I apologize that it's as tangled as it is; I
don't have time to further clarify it.

>> A precondition of the two transfers giving identity
>> is IMHO that no bits are lost, but if the precision of the middle-type
>> is smaller than of the other type, it can't be fully preserved.  The
>> precision of integer(kind=4) is smaller than of real(kind=8), and similarly
>> I'd say for logical(kind=4) and integer(kind=4), because logical:
>> "The logical type has two values which represent true and false."

"IMHO" is unfortunately not the best guide here.  According to the F95
spec, the precondition of transfer(transfer(E,D),E) returning identity
is "the physical representation of D is as long as or longer than that
of E."  This is rather more clearly defined than "no bits are lost."  In
particular, ...

> Hm, I haven't found Richard Maine's post that is mentioned in the old 
> thread, but Fortran has the notion of storage unit.  LOGICAL*4, 
> INTEGER*4 and REAL*4 all occupy 4 storage units.

...the storage unit stuff requires that LOGICAL*4 and INTEGER*4
variables occupy the same amount of space.

I assume that we do meet this requirement, even though we use
BOOLEAN_TYPE, yes?  This is pretty critical, and is clearly required by
the standard.

> If a circular 
> transfer(transfer (...) ...) between types with the same number of 
> storage units is required to return the original value (as Richard Maine 
> apparently argued), then we'll probably have no choice but to stop using 
> BOOLEAN_TYPE for LOGICALs.
> 
> Maybe Brooks can point us to the comp.lang.fortran thread.

Wow, it's been a long time since I've looked at this!  :)  The thread is
titled "Transfer and variables that don't use all their storage space."
and occurred on 2007-04-08.

And, when I go back and reread that, I find that I appear to have made a
misinterpretation of what Richard was saying.  The crux of his argument
was this, about why TRANSFER(TRANSFER(-5_8, L), -5_8) is actually
illegal when L is a LOGICAL*8:

Richard Maine wrote, in comp.lang.fortran, on 2007-04-08:
> No. I'm using a back door argument to explain why the code is illegal.
> More or less a proof by contradiction, which is perhaps why it seems
> contradictory. :-) I arise at a contradiction if I assume the code is
> standard conforming. The contradiction is that the standard requires a
> result that the standard also prohibits. Thus, I conclude that the
> assumption of standard conformance is false.

(In particular, the definition of TRANSFER requires this to round-trip,
which means that the inner one must return a value that's not a legal
LOGICAL value, which means that it must be illegal to call TRANSFER with
those arguments.)

He then went on to say, on my question of whether it would be valid for
the compiler to just throw an error in this case:
> I think you could argue that. But then you might get users who
> considered it a poor quality of implementation; that one is harder. I
> have trouble imagining why anyone would ever want to do such a strange
> thing, but James Buskirk can probably come up with something useful. :-)
> 
> I think the more "obvious" thing to do is to use all 8 bytes. But I
> don't know what the costs of that are and what the tradoffs might be of
> those costs versus the (rare?) usage of an idiom like that.

So, given that we seem to have found that the costs of using all 8
bytes, or 4 bytes, or whatever, are rather higher than otherwise
expected.  As a result, I suppose that the tradeoff is that we go with
what might be considered a poor quality of implementation in this case,
in return for a better quality of implementation in the case of anything
that uses logical variables sensibly.

Jakub Jelinek wrote, at 11/11/2008 8:52 AM:
> I wonder what you can do with LOGICALs that have values transfered from
> INTEGER (other than 0/1).  Does the standard define whether something
> is executed or not in:
> logical, parameter :: l = transfer (234, .false.)
> if (l) print *, ".true."
> ?  I.e. is it supposed to be .false. or .true., or undefined behavior?

Even if we were to accept that the double-transfer idiom was legal, the
only thing you could legally do with such a LOGICAL value is TRANSFER it
to something else.  Anything else is undefined behavior.

Note that assigning the result of transfer(234, .false.) to a logical
variable, in the above example, is undefined behavior.  It seems fair to
me to declare that it normalizes the result to a standard .true. or
.false. value, even if we were to preserve the weird double-transfer
semantics.

- Brooks


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]