rjmccall added inline comments.

================
Comment at: clang/lib/Sema/SemaExpr.cpp:1304
+    RHSTy = ResultTy;
+  }
+
----------------
ebevhan wrote:
> rjmccall wrote:
> > 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.
> Wait, wait... You might be on to something.
> 
> The spec says in 4.1.4:
> > In order to get useful and attainable results, the usual arithmetic 
> > conversions do not apply to the combination of an integer type and a 
> > fixed-point type, or the combination of two fixed-point types.
> > In these cases:
> > 1. the result of the operation is calculated using the values of the two 
> > operands, with their full precision;
> > 2. if one operand has signed fixed-point type and the other operand has 
> > unsigned fixed-point type, then the unsigned fixed-point operand is 
> > converted to its corresponding signed fixed-point type and the resulting 
> > type is the type of the converted operand;
> > 3. the result type is the type with the highest rank, [...]
> 
> (1) comes before (2); not after. In other words, shouldn't the true result of 
> the operation be computed **before** the unsigned-signed correction? Point 2 
> is just worded really oddly, with 'operand is converted' and 'resulting type'.
> 
> It says that "the unsigned fixed-point operand is converted to its 
> corresponding signed fixed-point type and the resulting type is the type of 
> the converted operand", but this doesn't mean that the resulting converted 
> operand is actually used, only its type is. This means that for the purpose 
> of result type calculation in (3), the converted operand type from (2) will 
> be used as that operand's type, rather than the unsigned type itself. Or?
> 
> The wording here is very hard to interpret.
Sure, I think you could make an argument for (2) only being meant to 
participate in determining the result type.  At the very least, that seems more 
consistent, especially with the behavior for fixed-point + integer operations.  
Do you have any existing contact with the committee?


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