ebevhan added inline comments.

================
Comment at: include/clang/Basic/TargetInfo.h:89
+  // corresponding unsaturated types.
+  unsigned char ShortAccumFBits, ShortAccumIBits;
+  unsigned char AccumFBits, AccumIBits;
----------------
leonardchan wrote:
> ebevhan wrote:
> > I suspect it's still possible to calculate the ibits based on the fbits, 
> > even for _Accum.
> > 
> > Are the unsigned values needed? The fbits for unsigned _Fract are the same 
> > as for signed _Fract if SameFBits is set, and +1 otherwise. The same should 
> > go for unsigned _Accum, but I don't think it's entirely clear how this 
> > affects the integral part.
> Similar to the previous comment, if we choose to make SameFBits dependent 
> on/controlled by the target, then I think it would be better for the target 
> to explicitly specify the integral bits since there could be some cases where 
> there may be padding (such as for unsigned _Accum types if this flag is set), 
> and in this case, I think it should be explicit that the `bit_width != IBits 
> + FBits`.
> 
> We also can't fill in that padding for the unsigned _Accum types as an extra 
> integral bit since the standard says that `"each signed accum type has at 
> least as many integral bits as its corresponding unsigned accum type".`
> 
> For the unsigned _Fract values, I think we can get rid of them if we choose 
> to keep the flag instead, but it would no longer apply to unsigned _Accum 
> types since it would allow for extra padding in these types and would 
> conflict with the logic of `bit_width == IBits + FBits`
> 
> For now, I actually think the simplest option is to keep these target 
> properties, but have the target override them individually, with checks to 
> make sure the values adhere to the standard.
Is it not the case that `bitwidth != IBits + FBits` for signed types only? 
Signed types require a sign bit (which is neither a fractional bit nor an 
integral bit, according to spec) but unsigned types do not, and if IBits and 
FBits for the signed and unsigned types are the same, the MSB is simply a 
padding bit, at least for _Accum (for _Fract everything above the fbits is 
padding). 

My reasoning for keeping the number of configurable values down was to limit 
the number of twiddly knobs to make the implementation simpler. Granting a lot 
of freedom is nice, but I suspect that it will be quite hard to get the 
implementation working properly for every valid configuration. I also don't 
really see much of a reason for `FBits != UFBits` in general. I know the spec 
gives a recommendation to implement it that way, but I think the benefit of 
normalizing the signed and unsigned representations outweighs the lost bit in 
the unsigned type.

It's hard to say what the differences are beyond that since I'm not sure how 
you plan on treating padding bits. In our implementation, padding bits (the MSB 
in all of the unsigned types for us) after any operation are zeroed.


================
Comment at: lib/Basic/TargetInfo.cpp:797
+  // corresponding unsigned accum type.
+  assert(ShortAccumIBits == UnsignedShortAccumIBits ||
+         ShortAccumIBits == UnsignedShortAccumIBits - 1);
----------------
Shouldn't these be `ShortAccumIBits >= UnsignedShortAccumIBits` etc.?


================
Comment at: lib/Lex/LiteralSupport.cpp:737
+  if (!hadError && saw_fixed_point_suffix) {
+    assert(isFract || isAccum);
+    assert(radix == 16 || radix == 10);
----------------
Is `saw_fixed_point_suffix` only used for this assertion? Doesn't `isFract || 
isAccum` imply `saw_fixed_point_suffix`?


================
Comment at: lib/Lex/LiteralSupport.cpp:1049
+
+bool NumericLiteralParser::GetFixedPointValue(llvm::APInt &StoreVal, unsigned 
Scale) {
+  assert(radix == 16 || radix == 10);
----------------
The function does a lot of overflow checks that I think are superfluous if the 
required bits are calculated properly. The only overflow check that should be 
needed is the one at the very bottom that truncates the APInt to the final size.


================
Comment at: lib/Lex/LiteralSupport.cpp:1065
+
+    while (!IsExponentPart(*Ptr)) ++Ptr;
+    ++Ptr;
----------------
This whole block is very diligent but I wonder how common overflow in the 
exponent is.

I'm unsure if LLVM has a helper function with `atoll`-like functionality, but 
if it does it's a lot less code to just use that instead. There's also APInt's 
`fromString`, but that seems to assert if the integer doesn't fit in the 
bitwidth. The required bitwidth can be calculated here, though.


================
Comment at: lib/Lex/LiteralSupport.cpp:1085
+
+  if (radix == 10) {
+    // Number of bits needed for decimal literal is
----------------
These two calculations look very similar to me. The only difference seems to be 
that the exponent is multiplied by 4 in the decimal case, and not in the 
hexadecimal case.


================
Comment at: lib/Lex/LiteralSupport.cpp:1141
+    }
+    if ((radix == 16 && (*Ptr == 'p' || *Ptr == 'P')) ||
+        (radix == 10 && (*Ptr == 'e' || *Ptr == 'E'))) {
----------------
I don't think breaking is dependent on this, it's dependent on whether or not 
the input is a hex digit.

We've already parsed the exponent further up, so we should be able to have an 
`ExponentBegin` that we can use as the loop bound. 

(It's the same as `SuffixBegin` if there is no exponent)


================
Comment at: lib/Lex/LiteralSupport.cpp:1143
+        (radix == 10 && (*Ptr == 'e' || *Ptr == 'E'))) {
+      FoundExponent = true;
+      ++Ptr;
----------------
Does FoundExponent matter?


================
Comment at: lib/Lex/LiteralSupport.cpp:1175
+      // number of digits past the radix point.
+      --FractBaseShift;
+    }
----------------
This is technically Exponent.


================
Comment at: lib/Lex/LiteralSupport.cpp:1184
+  auto OldVal = Val;
+  Val <<= Scale;
+  IntOverflowOccurred |= Val.lshr(Scale) != OldVal;
----------------
If you want to verify overflow here, you can check that 
`Val.countLeadingZeroes() >= Scale`.


================
Comment at: lib/Lex/LiteralSupport.cpp:1187
+
+  uint64_t Base = (radix == 16) ? 2 : 10;
+  if (BaseShift > 0) {
----------------
I don't think loops are needed here. BaseShift is essentially Exponent so it 
should be possible to implement as a `pow(radix, abs(BaseShift))` and then 
either a mul or a div based on whether or not it was positive or negative.


================
Comment at: lib/Lex/LiteralSupport.cpp:1199
+
+  auto MaxVal = llvm::APInt::getMaxValue(StoreVal.getBitWidth());
+  if (Val.getBitWidth() > StoreVal.getBitWidth()) {
----------------
I think the final overflow check is the only one you need.


================
Comment at: lib/Sema/SemaExpr.cpp:1248
+  bool RHSFixed = RHSType->isFixedPointType();
+
+  if (LHSFixed && RHSFixed) {
----------------
leonardchan wrote:
> ebevhan wrote:
> > I don't see how these semantics work properly. The specification requires 
> > that operations be done in the full precision of both types. You cannot 
> > convert the types before performing the operation like this, since the 
> > operation will not be done in full precision in that case.
> > 
> > The operator semantics of Embedded-C require the operand types of binary 
> > operators to be different. It's only when you've performed the operation 
> > that you are allowed to convert the result to the resulting type.
> Initially the idea was to convert both sides to fixed point types, then 
> perform standard binary operations between the fixed point types.
> 
> For the example, a `fract * int` would have the int converted to a fixed 
> point type by left shifting it by the scale of the fract, multiplying, then 
> right shifting by the scale again to get the resulting fract. The only 
> unhandled thing is overflow, but the precision of the fract remains the same. 
> The operands would also be casted up beforehand so there was enough space to 
> store the result, which was casted down back to the original fract after 
> performing the right shift by the scale.
> 
> Operations between fixed point types would follow a similar process of 
> casting both operands to the higher rank fixed point type, and depending on 
> the operation, more underlying shifting and casting would be done to retain 
> full precision of the higher ranked type.
> 
> Though I will admit that I did not realize until now that multiplying a fixed 
> point type by an integer does not require shifting the integer.
I see how you've reasoned; this is how C normally works. The `fract` is of 
higher rank than `int` and therefore is the 'common type' of the operation. 
However, even though it is higher rank there is no guarantee that you can 
perform the operation without overflowing. And overflow matters here; the spec 
says that it must be done in the full precision (integral + fractional) of both 
types.

> The only unhandled thing is overflow, but the precision of the fract remains 
> the same. The operands would also be casted up beforehand so there was enough 
> space to store the result, which was casted down back to the original fract 
> after performing the right shift by the scale.

The precision remains the same (and while it doesn't have to be the same to 
perform an operation, it makes the implementation more regular; things like 
addition and subtraction 'just work'), but you cannot perform a conversion to 
`fract` *before* the operation itself, since if you do, there's nothing to 
'cast up'. Casting up is needed for things like `fract * fract` to prevent 
overflow, but for `fract * int` you need to cast to a type that can fit both 
all values of the int and all values of the fract, and *then* you can cast up 
before doing the multiplication.

> Operations between fixed point types would follow a similar process of 
> casting both operands to the higher rank fixed point type, and depending on 
> the operation, more underlying shifting and casting would be done to retain 
> full precision of the higher ranked type.

This might work, but I feel there could be edge cases. The E-C fixed-point 
ranks are very odd as they don't reflect reality; `short _Accum` cannot be 
considered strictly 'above' `long _Fract`, but the former has a higher rank 
than the latter. Depending on how the types are specified for a target, 
implicit casts between fixed-point types might inadvertantly discard bits, even 
though the spec says that operations must be done in full precision.


================
Comment at: lib/Sema/SemaExpr.cpp:1336
+  // Handle fixed point types
+  if (LHSType->isFixedPointType() || RHSType->isFixedPointType())
+    return handleFixedPointConversion(*this, LHS, RHS, LHSType, RHSType,
----------------
leonardchan wrote:
> ebevhan wrote:
> > I guess you haven't gotten there yet, but this should probably be before 
> > handleFloatConversion if you want to handle 'float + _fract' later.
> A later patch adds a case in `handleFloatConversion` where we check if one of 
> the operands is an integer and performs a cast from there.
> one of the operands is an integer 

I assume you meant fixed-point here.


Repository:
  rC Clang

https://reviews.llvm.org/D46915



_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to