This is an archive of the discontinued LLVM Phabricator instance.

[APFloat] Switch from (PPCDoubleDoubleImpl, IEEEdouble) layout to (IEEEdouble, IEEEdouble)
ClosedPublic

Authored by timshen on Dec 16 2016, 4:28 PM.

Details

Summary

This patch changes the layout of DoubleAPFloat, and adjust all
operations to do either:

  1. (IEEEdouble, IEEEdouble) -> (uint64_t, uint64_t) -> PPCDoubleDoubleImpl, then run the old algorithm.
  2. Do the right thing directly.
  1. includes multiply, divide, remainder, mod, fusedMultiplyAdd, roundToIntegral, convertFromString, next, convertToInteger, convertFromAPInt, convertFromSignExtendedInteger, convertFromZeroExtendedInteger, convertToHexString, toString, getExactInverse.
  2. includes makeZero, makeLargest, makeSmallest, makeSmallestNormalized, compare, bitwiseIsEqual, bitcastToAPInt, isDenormal, isSmallest, isLargest, isInteger, ilogb, scalbn, frexp, hash_value, Profile.

I could split this into two patches, e.g. use

  1. for all operatoins first, then incrementally change some of them to

2). I didn't do that, because 1) involves code that converts data between
PPCDoubleDoubleImpl and (IEEEdouble, IEEEdouble) back and forth, and may
pessimize the compiler. Instead, I find easy functions and use
approach 2) for them directly.

Next step is to implement move multiply and divide from 1) to 2). I don't
have plans for other functions in 1).

Event Timeline

timshen updated this revision to Diff 81825.Dec 16 2016, 4:28 PM
timshen retitled this revision from to [APFloat] Switch from (PPCDoubleDoubleImpl, IEEEdouble) layout to (IEEEdouble, IEEEdouble).
timshen updated this object.
timshen added reviewers: hfinkel, kbarton, iteratee, echristo.
timshen added subscribers: llvm-commits, cfe-commits.
ahatanak added inline comments.
llvm/include/llvm/ADT/APFloat.h
773

You don't need else after return.

jtony added a subscriber: jtony.Dec 16 2016, 6:25 PM
jtony added inline comments.
llvm/include/llvm/ADT/APFloat.h
800

I know it is allowed to return a void function call inside a void function, but I think this reduces the code readability in general and causes some confusing to some people. Maybe it is better to avoid using this kind of coding style. I think we can simply call each function in each branch without the 'return' keyword, by default, the program will reach the end of function and return.

One possible equivalent code:

void makeNaN(bool SNaN, bool Neg, const APInt *fill) {

  if (usesLayout<IEEEFloat>(getSemantics())) {
    U.IEEE.makeNaN(SNaN, Neg, fill);
  } else if (usesLayout<DoubleAPFloat>(getSemantics())) {
    U.Double.makeNaN(SNaN, Neg, fill);
  } else {
    llvm_unreachable("Unexpected semantics");
  }
}
811

Same here.

821

Same here.

1100

Same here.

timshen updated this revision to Diff 81980.Dec 19 2016, 11:41 AM

Remove 'else' after return.
Remove 'return' on void type.

timshen updated this revision to Diff 81981.Dec 19 2016, 11:51 AM

Remove more 'else' after return.

timshen marked 5 inline comments as done.Dec 19 2016, 11:55 AM
mehdi_amini added inline comments.Dec 19 2016, 6:29 PM
llvm/include/llvm/ADT/APFloat.h
800

Two reasons not to do that:

  1. It makes it less consistent with all the other cases, and consistency is a high criteria for readability
  2. returning a function call in a void function makes it so that if the callee signature is changed to return something in the future, it can't be ignored here (i.e. it makes it less error prone somehow).

Alternatively, you can also write it this way:

if (usesLayout<IEEEFloat>(getSemantics())) {
  U.IEEE.makeNaN(SNaN, Neg, fill);
  return;
} 
if (usesLayout<DoubleAPFloat>(getSemantics())) {
  U.Double.makeNaN(SNaN, Neg, fill);
  return;
} 
llvm_unreachable("Unexpected semantics");
timshen updated this revision to Diff 82148.Dec 20 2016, 1:43 PM

Consistently use early return style.

I changed type style to early return.

For constructors and destructors, I use:

if (...) {
  // statement;
  return;
}

For normal functions that returns void, I chose:

if (...)
  return Foo();
llvm_unreachable(...);

since it's more compact. If returning void looks weird, we should add it to the style guide.

Other functions are not controversial.

echristo edited edge metadata.Jan 4 2017, 3:17 PM

I changed type style to early return.

For constructors and destructors, I use:

if (...) {
  // statement;
  return;
}

For normal functions that returns void, I chose:

if (...)
  return Foo();
llvm_unreachable(...);

since it's more compact. If returning void looks weird, we should add it to the style guide.

Looks pretty weird. Typically I'd suggest just:

if (foo) {

Foo();
return;

}

since that will keep cognitive overhead to a minimum.

-eric

Other functions are not controversial.

Looks pretty weird. Typically I'd suggest just:

if (foo) {

Foo();
return;

}

since that will keep cognitive overhead to a minimum.

-eric

Other functions are not controversial.

I'm happy to discuss on a style guide <del>bikeshedding</del>change, since you are the second person that raises this concern, and now it's like 2 vs 2 :).

Another reason to not to do so is consistency with other returning functions, and potential changes required in the future, when a void-returning function starts to return values.

Looks pretty weird. Typically I'd suggest just:

if (foo) {

Foo();
return;

}

since that will keep cognitive overhead to a minimum.

-eric

Other functions are not controversial.

I'm happy to discuss on a style guide <del>bikeshedding</del>change, since you are the second person that raises this concern, and now it's like 2 vs 2 :).

Another reason to not to do so is consistency with other returning functions, and potential changes required in the future, when a void-returning function starts to return values.

I'm pretty sure I've never seen return <void type function> widely used in the code base versus my suggestion. That said, if you've looked and it's roughly 50/50 then I care a lot less (and we can bike shed in some separate thread).

-eric

I'm pretty sure I've never seen return <void type function> widely used in the code base versus my suggestion. That said, if you've looked and it's roughly 50/50 then I care a lot less (and we can bike shed in some separate thread).

I didn't count, and I don't know how to count it in the code base. :(

Friendly ping :)

Is there anyone else I can add as a reviewer, if no one feels comfortable reviewing the semantic?

Adding Steve in an attempt to get him to review :)

hfinkel edited edge metadata.Jan 7 2017, 9:38 AM

(IEEEdouble, IEEEdouble) -> (uint64_t, uint64_t) -> PPCDoubleDoubleImpl, then run the old algorithm.

We need to document in the code what is going on here and why it works. Just looking at a bunch of code like this:

if (Semantics == &semPPCDoubleDouble) {
  APFloat Tmp(semPPCDoubleDoubleImpl, bitcastToAPInt());
  auto Ret = Tmp.next(nextDown);
  *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt());
  return Ret;
}

it is not at all obvious what is going on (i.e. why are we casting to integers?). Maybe semPPCDoubleDoubleImpl needs a better name now? It seems confusing to have semPPCDoubleDoubleImpl and semPPCDoubleDouble where one is not really an "implementation" class of the other.

llvm/include/llvm/ADT/APFloat.h
791–797

I realize that some of this existed before, but is there any way to refactor this so that we don't have so much boiler plate? Even using a utility macro is probably better than this.

timshen updated this revision to Diff 83700.Jan 9 2017, 2:24 PM
timshen edited edge metadata.

Rename semPPCDoubleDoubleImpl to semPPCDoubleDoubleLegacy to reflect its use more accurately.

it is not at all obvious what is going on (i.e. why are we casting to integers?). Maybe semPPCDoubleDoubleImpl needs a better name now? It seems confusing to have semPPCDoubleDoubleImpl and semPPCDoubleDouble where one is not really an "implementation" class of the other.

This is true. I renamed it to semPPCDoubleDoubleLegacy, and modified its documentation. I didn't put the documentation for each of the uses, because that'd be too verbose.

timshen marked an inline comment as done.Jan 9 2017, 2:35 PM
timshen added inline comments.
llvm/include/llvm/ADT/APFloat.h
791–797

I was thinking about using a macro. I can do that after this patch.

The right way though, is to use virtual functions to do the dynamic dispatch. Ideally, we will have APFloatInterface as a pure virtual base, IEEEFloat and DoubleAPFloat as its two derives, and APFloat managing the union buffer to hold the objects. This approach is not easy to transit to, since then the object needs to keep a vtable pointer around, which costs a word. In order to compensate that overhead, we need somehow stash the "semantics" data member into the vtable.

timshen marked an inline comment as done.Jan 18 2017, 11:55 AM

Friendly ping. :)

We still have internal test failures that this patch (and the next one) fixes, and I think this is the last "hard to review" patch in the APFloat refactoring.

Few comments inline. Generally looks OK, I do share Hal's comment on finding some way of simplifying the if (someSemantics) ... if (otherSemantics) ... llvm_unreachable(...) pattern.

What did you have in mind?

llvm/include/llvm/ADT/APFloat.h
800

I prefer the alternate way that Mehdi has it here rather than returning void if possible.

842–843

Go ahead and commit this separately. (And the rest of the assert changes to add descriptions of the assert).

1059

What's with the no error checking. Also, unless my font kerning is terrible you have nmNearestTiesToEven versus rmNearestTiesToEven.

llvm/lib/Support/APFloat.cpp
4026

To reduce indentation I'd probably assert first? (And all below)

timshen updated this revision to Diff 85217.Jan 20 2017, 4:50 PM
timshen marked 2 inline comments as done.

Stylish changes.

llvm/include/llvm/ADT/APFloat.h
1059

That original IEEEFloat does so. I think it's reasonable, since the operators are mostly for convenience, while a serious user should use add/subtract/multiply/divide.

Fixed the typo.

echristo accepted this revision.Jan 23 2017, 11:16 AM

This looks fine to me now, might be good to get someone else to ack as well though.

This revision is now accepted and ready to land.Jan 23 2017, 11:16 AM

Hal has given an ack (offline) as well. Go ahead Tim.

This revision was automatically updated to reflect the committed changes.
jlebar added a subscriber: jlebar.Jan 24 2017, 10:20 AM

Eric asked me to do a review of this. I notice now it was submitted while I was doing the review. Oh well. Here are the comments I had.

llvm/include/llvm/ADT/APFloat.h
1058
1066

We have autobrief, which I do not understand, but I believe renders "\brief" unnecessary?

1066

Nit, Since "operator" is a keyword, it should be capitalized as in the C++

1066

Nit, It's clear from the code that this is an operator+ overload. Perhaps all we need to say is

Add two APFloats using rmNearestTiesToEven semantics. No error checking.

llvm/lib/Support/APFloat.cpp
88

s/case/operation/?

88

s/have/having/

88

s/consecutive 106 bits/106 consecutive bits/

89

No comma before "and". (Comma before "and" is usually reserved for the case when "and" joins two "independent clauses" -- things which could themselves be complete sentences.)

89

Suggest

It's not equivalent to the true PPC double-double semantics, because in reality, the two 53-bit mantissa parts ...

91

Can we rephrase "the two 53-bit mantissa parts don't actually have to be consecutive, e.g. 1 + epsilon."? I don't understand what this means.

4139

Nit: makeZero(/* Neg = */ false)? (See aforementioned screed. :)

4181

Is it worth adding a comment here that this is correct because in a double-double (x, y), we always have x >= y?

At least, I assume that's why this is correct. :)

4192

Nit, do you want to include the semantics? I mean, I guess it doesn't really matter.

4276

This shouldn't be == cmpEqual or something? Otherwise this makes no sense to me; perhaps we could add a comment?

llvm/unittests/ADT/APFloatTest.cpp
3511

Can you use {} instead of make_tuple? https://godbolt.org/g/1Y1P0F

3534

Honestly I am not sure this is an improvement over writing it out by hand:

EXPECT_TRUE(APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, {0xblah, 0xblah})).bitwiseIsEqual(...)))

(You should be able to pass an initializer_list to the constructor, no problem, afaict.) Up to you though.

timshen marked 13 inline comments as done.Jan 24 2017, 5:47 PM
timshen added inline comments.
llvm/include/llvm/ADT/APFloat.h
1058

I totally agree, but I'm not going to change it right now, so I added a TODO.

llvm/lib/Support/APFloat.cpp
89

I re-worked on the comments to make it readable by a programmer with no IBM double-double knowledge.

4181

I think you mean |x| > |y|. Now it's mentioned in the semPPCDoubleDouble documentation.

4192

It's more like a defensive style, just in case DoubleAPFloat supports more than just two doubles.

4276

It's by IBM double-double's definition, as previously commented.

llvm/unittests/ADT/APFloatTest.cpp
3511

Nope, GCC 4.8 will puke. lul.

3534

It's more of being consistent with other tests. I might be too lazy to change, unless you insist. :)

jlebar added inline comments.Jan 24 2017, 5:50 PM
llvm/unittests/ADT/APFloatTest.cpp
3534

I mean, the only tests that use make_tuple also seem to be ppc long-double tests. :) Up to you, though; not a big deal.