This is an archive of the discontinued LLVM Phabricator instance.

[CFE, SystemZ] Emit s390.tdc instrincic for __builtin_isnan in Constrained FP mode.
ClosedPublic

Authored by jonpa on Feb 11 2021, 6:17 PM.

Details

Summary

The recent commit 00a6254 "Stop traping on sNaN in builtin_isnan" changed the lowering in constrained FP mode of builtin_isnan from an FP comparison to integer operations to avoid trapping.

SystemZ has a special instruction "Test Data Class" which is the preferred way to do this check. This patch adds a new target hook "emit_FPConstrained_builtin_isnan()" that lets SystemZ emit the s390_tdc intrinsic instead.

Diff Detail

Event Timeline

jonpa requested review of this revision.Feb 11 2021, 6:17 PM
jonpa created this revision.

That's interesting. I presume that can be used to implement isinf as well? Perhaps better call the hook fpclassify or similar?

That's interesting. I presume that can be used to implement isinf as well? Perhaps better call the hook fpclassify or similar?

Hmm, the instruction doesn't really implement fpclassify in itself, it is more like a combined check for fpclassify() == <some constant>. Specifically, the TEST DATA CLASS instruction takes an immediate operand that represents a bit mask, which each bit standing for one type of floating-point value (zero, normal, subnormal, infinity, QNaN, SNaN -- each in positive and negative versions). The instruction sets the condition code depending on whether the input FP number is in one of the classes selected by the bit mask, or not.

This is why Jonas' patch uses a bit mask of 0x0F -- this has the bits for the four types of NaN set (pos/neg QNaN/SNan). The instruction could indeed also be used to implement an isinf check (bit mask 0x30) or many other checks. We actually have a SystemZ back-end pass that tries to multiple combine FP checks into a single TEST DATA CLASS instruction.

However, the instruction does not directly implement the fpclassify semantics. To implement fpclassify, you'd still have to use multiple invocations of the instruction with different flags to determine the fpclassify output value.

That's interesting. I presume that can be used to implement isinf as well? Perhaps better call the hook fpclassify or similar?

Hmm, the instruction doesn't really implement fpclassify in itself, it is more like a combined check for fpclassify() == <some constant>. Specifically, the TEST DATA CLASS instruction takes an immediate operand that represents a bit mask, which each bit standing for one type of floating-point value (zero, normal, subnormal, infinity, QNaN, SNaN -- each in positive and negative versions). The instruction sets the condition code depending on whether the input FP number is in one of the classes selected by the bit mask, or not.

This is why Jonas' patch uses a bit mask of 0x0F -- this has the bits for the four types of NaN set (pos/neg QNaN/SNan). The instruction could indeed also be used to implement an isinf check (bit mask 0x30) or many other checks. We actually have a SystemZ back-end pass that tries to multiple combine FP checks into a single TEST DATA CLASS instruction.

However, the instruction does not directly implement the fpclassify semantics. To implement fpclassify, you'd still have to use multiple invocations of the instruction with different flags to determine the fpclassify output value.

I see. I'm not sure whether it's better to have several target hooks or a single one like testFPKind that would take a flag saying what do we want to test (NaN, Inf, etc.).

jonpa added a comment.Feb 16 2021, 5:00 PM

That's interesting. I presume that can be used to implement isinf as well? Perhaps better call the hook fpclassify or similar?

Hmm, the instruction doesn't really implement fpclassify in itself, it is more like a combined check for fpclassify() == <some constant>. Specifically, the TEST DATA CLASS instruction takes an immediate operand that represents a bit mask, which each bit standing for one type of floating-point value (zero, normal, subnormal, infinity, QNaN, SNaN -- each in positive and negative versions). The instruction sets the condition code depending on whether the input FP number is in one of the classes selected by the bit mask, or not.

This is why Jonas' patch uses a bit mask of 0x0F -- this has the bits for the four types of NaN set (pos/neg QNaN/SNan). The instruction could indeed also be used to implement an isinf check (bit mask 0x30) or many other checks. We actually have a SystemZ back-end pass that tries to multiple combine FP checks into a single TEST DATA CLASS instruction.

However, the instruction does not directly implement the fpclassify semantics. To implement fpclassify, you'd still have to use multiple invocations of the instruction with different flags to determine the fpclassify output value.

I see. I'm not sure whether it's better to have several target hooks or a single one like testFPKind that would take a flag saying what do we want to test (NaN, Inf, etc.).

Personally I think testFPKind should work well - it gives a single target hook for related purposes which should be more readable, and it is also natural for SystemZ since we will be using the same (Test Data Class) instruction for differnet checks only with different flag bits... Any one else has an opinion? Should I go ahead and change the patch to this end?

That's interesting. I presume that can be used to implement isinf as well? Perhaps better call the hook fpclassify or similar?

Hmm, the instruction doesn't really implement fpclassify in itself, it is more like a combined check for fpclassify() == <some constant>. Specifically, the TEST DATA CLASS instruction takes an immediate operand that represents a bit mask, which each bit standing for one type of floating-point value (zero, normal, subnormal, infinity, QNaN, SNaN -- each in positive and negative versions). The instruction sets the condition code depending on whether the input FP number is in one of the classes selected by the bit mask, or not.

This is why Jonas' patch uses a bit mask of 0x0F -- this has the bits for the four types of NaN set (pos/neg QNaN/SNan). The instruction could indeed also be used to implement an isinf check (bit mask 0x30) or many other checks. We actually have a SystemZ back-end pass that tries to multiple combine FP checks into a single TEST DATA CLASS instruction.

However, the instruction does not directly implement the fpclassify semantics. To implement fpclassify, you'd still have to use multiple invocations of the instruction with different flags to determine the fpclassify output value.

I see. I'm not sure whether it's better to have several target hooks or a single one like testFPKind that would take a flag saying what do we want to test (NaN, Inf, etc.).

Personally I think testFPKind should work well - it gives a single target hook for related purposes which should be more readable, and it is also natural for SystemZ since we will be using the same (Test Data Class) instruction for differnet checks only with different flag bits... Any one else has an opinion? Should I go ahead and change the patch to this end?

Sounds good to me. Hopefully I'll get round to __builtin_isinf soon and a single hook will make the patch slightly smaller.

jonpa updated this revision to Diff 324376.Feb 17 2021, 11:52 AM

Sounds good to me. Hopefully I'll get round to __builtin_isinf soon and a single hook will make the patch slightly smaller.

Patch updated to call the new hook testFPKind() and make it take a BuiltinID as argument (that seems to work at least for the moment - maybe an enum type will become necessary at some point per your suggestion..?)

I am not sure if this is "only" or "typically" used in constrained FP mode, or if the mode should be independent of calling this hook. The patch as it is asserts that it is called for an FP type but leaves it to the target to decide based on the FP mode, where SystemZ opts out unless it is constrained (which I think is what is wanted...).

Sounds good to me. Hopefully I'll get round to __builtin_isinf soon and a single hook will make the patch slightly smaller.

Patch updated to call the new hook testFPKind() and make it take a BuiltinID as argument (that seems to work at least for the moment - maybe an enum type will become necessary at some point per your suggestion..?)

I am not sure if this is "only" or "typically" used in constrained FP mode, or if the mode should be independent of calling this hook. The patch as it is asserts that it is called for an FP type but leaves it to the target to decide based on the FP mode, where SystemZ opts out unless it is constrained (which I think is what is wanted...).

LGTM, we can adapt the hook later if needed. I do not know whether allowing the hook to be used for non constrained FP will prove useful but it is easy enough to ignore it for non FP so why not. Thanks for changing that!

jonpa added a comment.Feb 17 2021, 3:49 PM

Sounds good to me. Hopefully I'll get round to __builtin_isinf soon and a single hook will make the patch slightly smaller.

Patch updated to call the new hook testFPKind() and make it take a BuiltinID as argument (that seems to work at least for the moment - maybe an enum type will become necessary at some point per your suggestion..?)

I am not sure if this is "only" or "typically" used in constrained FP mode, or if the mode should be independent of calling this hook. The patch as it is asserts that it is called for an FP type but leaves it to the target to decide based on the FP mode, where SystemZ opts out unless it is constrained (which I think is what is wanted...).

LGTM, we can adapt the hook later if needed. I do not know whether allowing the hook to be used for non constrained FP will prove useful but it is easy enough to ignore it for non FP so why not. Thanks for changing that!

Thanks for review!

@uweigand: looks good on the SystemZ parts?

uweigand accepted this revision.Feb 18 2021, 2:42 AM

LGTM as well, thanks!

This revision is now accepted and ready to land.Feb 18 2021, 2:42 AM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 18 2021, 10:39 AM

Sounds good to me. Hopefully I'll get round to __builtin_isinf soon and a single hook will make the patch slightly smaller.

Patch updated to call the new hook testFPKind() and make it take a BuiltinID as argument (that seems to work at least for the moment - maybe an enum type will become necessary at some point per your suggestion..?)

I am not sure if this is "only" or "typically" used in constrained FP mode, or if the mode should be independent of calling this hook. The patch as it is asserts that it is called for an FP type but leaves it to the target to decide based on the FP mode, where SystemZ opts out unless it is constrained (which I think is what is wanted...).

I forgot to ask at the time, why do you restrict this code to the constrained case? Presumably it's a lot faster than fabs+cmp and should be faster in all cases?

jonpa added a comment.Jun 22 2021, 3:58 PM

Sounds good to me. Hopefully I'll get round to __builtin_isinf soon and a single hook will make the patch slightly smaller.

Patch updated to call the new hook testFPKind() and make it take a BuiltinID as argument (that seems to work at least for the moment - maybe an enum type will become necessary at some point per your suggestion..?)

I am not sure if this is "only" or "typically" used in constrained FP mode, or if the mode should be independent of calling this hook. The patch as it is asserts that it is called for an FP type but leaves it to the target to decide based on the FP mode, where SystemZ opts out unless it is constrained (which I think is what is wanted...).

I forgot to ask at the time, why do you restrict this code to the constrained case? Presumably it's a lot faster than fabs+cmp and should be faster in all cases?

There are later optimizations that does this already in place (SystemZTDCPass). I checked now and it seems to make no difference at all to do this in the front-end always in non-constrained FP mode... (SPEC)