rjmccall added inline comments.

================
Comment at: clang/lib/Sema/SemaExpr.cpp:1304
+    RHSTy = ResultTy;
+  }
+
----------------
leonardchan wrote:
> rjmccall wrote:
> > leonardchan wrote:
> > > rjmccall wrote:
> > > > leonardchan wrote:
> > > > > rjmccall wrote:
> > > > > > ebevhan wrote:
> > > > > > > rjmccall wrote:
> > > > > > > > leonardchan wrote:
> > > > > > > > > rjmccall wrote:
> > > > > > > > > > ebevhan wrote:
> > > > > > > > > > > rjmccall wrote:
> > > > > > > > > > > > Hmm.  So adding a signed integer to an unsigned 
> > > > > > > > > > > > fixed-point type always converts the integer to 
> > > > > > > > > > > > unsigned?  That's a little weird, but only because the 
> > > > > > > > > > > > fixed-point rule seems to be the other way.  Anyway, I 
> > > > > > > > > > > > assume it's not a bug in the spec.
> > > > > > > > > > > `handleFixedPointConversion` only calculates the result 
> > > > > > > > > > > type of the expression, not the calculation type. The 
> > > > > > > > > > > final result type of the operation will be the unsigned 
> > > > > > > > > > > fixed-point type, but the calculation itself will be done 
> > > > > > > > > > > in a signed type with enough precision to represent both 
> > > > > > > > > > > the signed integer and the unsigned fixed-point type. 
> > > > > > > > > > > 
> > > > > > > > > > > Though, if the result ends up being negative, the final 
> > > > > > > > > > > result is undefined unless the type is saturating. I 
> > > > > > > > > > > don't think there is a test for the saturating case 
> > > > > > > > > > > (signed integer + unsigned saturating fixed-point) in the 
> > > > > > > > > > > SaturatedAddition tests.
> > > > > > > > > > > `handleFixedPointConversion` only calculates the result 
> > > > > > > > > > > type of the expression, not the calculation type.
> > > > > > > > > > 
> > > > > > > > > > Right, I understand that, but the result type of the 
> > > > > > > > > > expression obviously impacts the expressive range of the 
> > > > > > > > > > result, since you can end up with a negative value.
> > > > > > > > > > 
> > > > > > > > > > Hmm.  With that said, if the general principle is to 
> > > > > > > > > > perform the operation with full precision on the original 
> > > > > > > > > > operand values, why are unsigned fixed-point operands 
> > > > > > > > > > coerced to their corresponding signed types *before* the 
> > > > > > > > > > operation?  This is precision-limiting if the unsigned 
> > > > > > > > > > representation doesn't use a padding bit.  That seems like 
> > > > > > > > > > a bug in the spec.
> > > > > > > > > > Hmm. With that said, if the general principle is to perform 
> > > > > > > > > > the operation with full precision on the original operand 
> > > > > > > > > > values, why are unsigned fixed-point operands coerced to 
> > > > > > > > > > their corresponding signed types *before* the operation? 
> > > > > > > > > > This is precision-limiting if the unsigned representation 
> > > > > > > > > > doesn't use a padding bit. That seems like a bug in the 
> > > > > > > > > > spec.
> > > > > > > > > 
> > > > > > > > > Possibly. When the standard mentions converting from signed 
> > > > > > > > > to unsigned fixed point, the only requirement involved is 
> > > > > > > > > conservation of magnitude (the number of integral bits 
> > > > > > > > > excluding the sign)
> > > > > > > > > 
> > > > > > > > > ```
> > > > > > > > > when signed and unsigned fixed-point types are mixed, the 
> > > > > > > > > unsigned type is converted to the corresponding signed type, 
> > > > > > > > > and this should go without loss of magnitude
> > > > > > > > > ```
> > > > > > > > > 
> > > > > > > > > This is just speculation, but I'm under the impression that 
> > > > > > > > > not as much "attention" was given for unsigned types as for 
> > > > > > > > > signed types since "`In the embedded processor world, support 
> > > > > > > > > for unsigned fixed-point data types is rare; normally only 
> > > > > > > > > signed fixed-point data types are supported`", but was kept 
> > > > > > > > > anyway since unsigned types are used a lot.
> > > > > > > > > 
> > > > > > > > > ```
> > > > > > > > > However, to disallow unsigned fixed-point arithmetic from 
> > > > > > > > > programming languages (in general, and from C in particular) 
> > > > > > > > > based on this observation, seems overly restrictive.
> > > > > > > > > ```
> > > > > > > > > 
> > > > > > > > > I also imagine that if the programmer needs more precision, 
> > > > > > > > > the correct approach would be to cast up to a type with a 
> > > > > > > > > higher scale. The standard seems to make an effort to expose 
> > > > > > > > > as much in regards to the underlying fixed point types as 
> > > > > > > > > possible:
> > > > > > > > > 
> > > > > > > > > ```
> > > > > > > > > it should be possible to write fixed-point algorithms that 
> > > > > > > > > are independent of the actual fixed-point hardware support. 
> > > > > > > > > This implies that a programmer (or a running program) should 
> > > > > > > > > have access to all parameters that define the behavior of the 
> > > > > > > > > underlying hardware (in other words: even if these parameters 
> > > > > > > > > are implementation-defined).
> > > > > > > > > ```
> > > > > > > > > 
> > > > > > > > > So the user would at least know that unsigned types may not 
> > > > > > > > > have the same scale as their corresponding signed types if 
> > > > > > > > > the hardware handles them with different scales.
> > > > > > > > > 
> > > > > > > > > Also added test for signed integer + unsigned saturating 
> > > > > > > > > fixed point.
> > > > > > > > As long as we maintain the same typing behavior, does the 
> > > > > > > > standard permit us to just Do The Right Thing here and do the 
> > > > > > > > extended arithmetic with the original unsigned operand?  I'm 
> > > > > > > > sure there are some cases where we would produce a slightly 
> > > > > > > > different value than an implementation that does the coercion 
> > > > > > > > before the operation, but that might be permitted under the 
> > > > > > > > standard, and as you say, it would only affect some situations 
> > > > > > > > that it doesn't seem the standard has given much thought to.
> > > > > > > The coercion of unsigned to signed is likely done for pragmatic 
> > > > > > > reasons; if you have a signed and unsigned type of the same rank, 
> > > > > > > operating on them together won't require any 'extra bits'. This 
> > > > > > > means that if your hardware has native support for the 
> > > > > > > operations, you won't end up in a situation where you really need 
> > > > > > > 33 or 34 bits (for a 32 bit value) to actually perform the 
> > > > > > > operation. This is one of the benefits of these kinds of implicit 
> > > > > > > conversions.
> > > > > > > 
> > > > > > > It probably makes the implementation a bit easier as well, since 
> > > > > > > you don't have to consider cases where both sides have different 
> > > > > > > signedness. The loss of the extra bit of precision from the 
> > > > > > > unsigned operand is probably not considered to be very important. 
> > > > > > > As Leonard said, if you wanted higher precision you can simply 
> > > > > > > use the next rank of types. ...Of course, they seem to have 
> > > > > > > forgotten that that exact issue with differing signedness also 
> > > > > > > applies to operations with fixed-point and integer operands.
> > > > > > > 
> > > > > > > Personally, I think the spec could be a bit more restrictive with 
> > > > > > > how it handles these conversions, as the way they are now makes 
> > > > > > > it really hard to generate code sensibly for machines that have 
> > > > > > > native support for these kinds of operations; in many cases 
> > > > > > > you'll end up requiring a couple bits too many or a couple bits 
> > > > > > > too few to fit in a sensible register size, and ensuring that the 
> > > > > > > scales match what your hardware can handle is also a challenge.
> > > > > > > The coercion of unsigned to signed is likely done for pragmatic 
> > > > > > > reasons; if you have a signed and unsigned type of the same rank, 
> > > > > > > operating on them together won't require any 'extra bits'. This 
> > > > > > > means that if your hardware has native support for the 
> > > > > > > operations, you won't end up in a situation where you really need 
> > > > > > > 33 or 34 bits (for a 32 bit value) to actually perform the 
> > > > > > > operation. This is one of the benefits of these kinds of implicit 
> > > > > > > conversions.
> > > > > > 
> > > > > > Yeah, I can see that.  I think it really depends on the target.  If 
> > > > > > you have hardware support, and you're using an unpadded 
> > > > > > representation for unsigned fixed-point values, then preserving the 
> > > > > > extra bit on mixed-signedness operations will sometimes prevent you 
> > > > > > from using the hardware optimally.  On the other hand, if you don't 
> > > > > > have hardware support, preserving the extra bit is usually easier 
> > > > > > than not: e.g., IIUC, multiplying a 32-bit signed _Fract with a 
> > > > > > 32-bit unpadded unsigned _Fract means just doing a 64-bit 
> > > > > > multiplication and dropping the low 32 bits, whereas doing the 
> > > > > > conversion first actually requires extra operations to ensure that 
> > > > > > the low bit doesn't contribute to the result.  And a target with 
> > > > > > hardware support is probably going to use a padded format for 
> > > > > > unsigned fixed-point precisely so that it can take advantage of the 
> > > > > > hardware.
> > > > > > 
> > > > > > > ...Of course, they seem to have forgotten that that exact issue 
> > > > > > > with differing signedness also applies to operations with 
> > > > > > > fixed-point and integer operands.
> > > > > > 
> > > > > > Right.
> > > > > For signed-unsigned multiplication, we could do that without having 
> > > > > to do the conversion first, but in the case of addition and 
> > > > > subtraction, we would still need to drop a bit with unpadded unsigned 
> > > > > types so both have the same scales.
> > > > > 
> > > > > I don't want to make too many assumptions about hardware support, but 
> > > > > I imagine that if one supports 32 bit signed addition, and unsigned 
> > > > > types did not have padding, it would be better instead to LSHR on 
> > > > > unsigned fixed point type then do 32 bit signed addition rather than 
> > > > > SEXT+SHL on the signed fixed point type then do 33 bit signed 
> > > > > addition.
> > > > > [I}n the case of addition and subtraction, we would still need to 
> > > > > drop a bit with unpadded unsigned types so both have the same scales.
> > > > 
> > > > Right, but I don't think it matters because you're dropping that bit 
> > > > either way: with the specified semantics, you do the conversion 
> > > > beforehand which drops the bit, and with full-precision semantics, the 
> > > > bit isn't going to be representable and dropping it during the 
> > > > computation is equivalent to either rounding down (addition) or up 
> > > > (subtraction), either of which is allowed.
> > > > 
> > > > > I don't want to make too many assumptions about hardware support, but 
> > > > > I imagine that if one supports 32 bit signed addition, and unsigned 
> > > > > types did not have padding, it would be better instead to LSHR on 
> > > > > unsigned fixed point type then do 32 bit signed addition rather than 
> > > > > SEXT+SHL on the signed fixed point type then do 33 bit signed 
> > > > > addition.
> > > > 
> > > > My point is just that (1) the 33-bit signed addition can just be a 
> > > > 32-bit signed addition given that you ultimately have to produce a 
> > > > 32-bit result and (2) targets with hardware support are unlikely to use 
> > > > an unpadded unsigned type anyway.  Although I suppose (2) is a bit 
> > > > questionable because hardware support could be added well after an ABI 
> > > > has been settled on.
> > > Ok. So should anything be changed here then if both semantics work? 
> > > Currently we still do the 32 bit addition for signed and unsigned types.
> > Well, most of what we're doing here is trying to lay good groundwork for 
> > all operations, and "both semantics work" is only true for same-rank 
> > addition, which is why I'm asking whether we should consider what semantics 
> > we actually want in general.
> I think the semantics for all binary arithmetic operators (+, -, *, /) can be 
> simplified to:
> 
> 1. If one operand is a signed fixed point type and the other an unsigned 
> type, convert the unsigned type to a signed type (which may involve reducing 
> the scale).
> 2. Find the common type of both operands that can hold both the larger scale 
> and magnitude of the 2 operands, and cast them to this common type.
> 3. Perform the binary operation.
> 4. Cast to result type.
> 
> I think the semantics for addition are already settled with these semantics, 
> and I believe subtraction shares the same semantics as those for addition. As 
> you said before, the extra bit would not even matter with full precision 
> since it would be trimmed anyway when converting from the common type to the 
> result, and rounding can be either up or down.
> 
> Signed integer addition/subtraction with an unsigned fixed point type with 
> these semantics will only lead to undefined behavior if the difference in the 
> values between both types is negative since a negative value cannot be 
> represented by an unsigned fixed point type (unless the result is saturating).
> 
> For multiplication and division, the result could technically be calculated 
> also without having to cast between both sides, although for using the fixed 
> point mul/div intrinsics, both operands would need to be casted to a common 
> type with the same scale anyway. If a bit does end up getting dropped when 
> casting an operand from unsigned to signed in (1), that dropped bit would not 
> be representable when casting to the result anyway and the standard still 
> allows us to return a rounded result.
> 
> When multiplying or dividing between a fixed point and integer though, we 
> could just perform a regular integer mul/div if the result is non-saturating 
> without having to perform the steps above. For saturating mul/div with an 
> int, it might be easier to cast both sides to a common type then call the 
> saturating fixed point mul/div intrinsics. In either case, I do not think 
> rounding will be an issue here since we can still calculate the result 
> precisely and the result will be either rounded up or down.
> 
> These semantics do not apply to comparison/relational operands, where if we 
> do compare operands of different types or ranks, we would just need to find 
> the common type between the 2 and perform the comparison.
> As you said before, the extra bit would not even matter with full precision 
> since it would be trimmed anyway when converting from the common type to the 
> result, and rounding can be either up or down.

Note that this is only true if the unsigned value has a scale that's greater 
than or equal to the scale of the result type, which is true for same-rank 
addition but not necessarily with mixed-rank.

> For multiplication and division, the result could technically be calculated 
> also without having to cast between both sides, although for using the fixed 
> point mul/div intrinsics, both operands would need to be casted to a common 
> type with the same scale anyway. If a bit does end up getting dropped when 
> casting an operand from unsigned to signed in (1), that dropped bit would not 
> be representable when casting to the result anyway and the standard still 
> allows us to return a rounded result.

In a `_Fract` multiplication (or a mixed-rank multiplication), the extra bit of 
precision can affect higher bits in the result.  Consider an 8-bit format where 
you're multiplying unsigned `000.00001` with signed `+010.0000`: if you do this 
multiplication in full precision, the result is `+000.0001`, but if you do the 
conversion first, the result is `0`.

On some level, it just seems really strange to me that the spec calls for doing 
arbitrary-precision arithmetic and then converting to the result type for every 
situation except a signed/unsigned mismatch.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D53738/new/

https://reviews.llvm.org/D53738



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

Reply via email to