1019 Commits

Author SHA1 Message Date
Noah Goldstein
71ef04d7cd [InstCombine] fold (icmp eq/ne (or disjoint x, C0), C1) -> (icmp eq/ne x, C0^C1)
Proof: https://alive2.llvm.org/ce/z/m3xoo_

Closes #87734
2024-04-09 15:38:18 -05:00
Noah Goldstein
7599d478ef [InstCombine] Fold (icmp eq/ne (add nuw x, y), 0) -> (icmp eq/ne (or x, y), 0)
`(icmp eq/ne (or x, y), 0)` is probably easier to analyze than `(icmp
eq/ne x, -y)`

Proof: https://alive2.llvm.org/ce/z/2-VTb6

Closes #88088
2024-04-09 13:56:28 -05:00
Noah Goldstein
b60cf84e09 [InstCombine] Add more cases for simplifying (icmp (and/or x, Mask), y)
This cleans up basically all the regressions assosiated from #84688

Proof of all new cases: https://alive2.llvm.org/ce/z/5yYWLb

Closes #85445
2024-03-19 17:17:35 -05:00
Noah Goldstein
5ca325e49c [InstCombine] Detect (x ^ -x) as a ~Mask
Proof: https://alive2.llvm.org/ce/z/TAFmPw

This is a lemma for clearing up some of the regressions that #84688
causes.

Closes #84868
2024-03-12 13:26:18 -05:00
Noah Goldstein
60dda1fc6e [InstCombine] fold (icmp eq/ne (and (shl -1, X), Y), 0) -> (icmp eq/ne (lshr Y, X), 0)
Proofs: https://alive2.llvm.org/ce/z/oSRGBt

Closes #84691
2024-03-10 18:16:00 -05:00
Noah Goldstein
193b3d6733 [InstCombine] Recognize (icmp eq/ne (and X, ~Mask), 0) pattern in foldICmpWithLowBitMaskedVal
`(icmp eq/ne (and X, ~Mask), 0)` is equivilent to `(icmp eq/ne (and X,
Mask), X` and we sometimes generate the former pattern intentionally
to reduce number of uses of `X`.
Proof: https://alive2.llvm.org/ce/z/3u-usC

Differential Revision: https://reviews.llvm.org/D159329

Closes #81562
2024-03-10 14:33:34 -05:00
Noah Goldstein
d77eb9ea59 [InstCombine] Improve mask detection in foldICmpWithLowBitMaskedVal
Make recursive matcher that is able to detect a lot more patterns.
Proofs for all supported patterns: https://alive2.llvm.org/ce/z/fSQ3nZ

Differential Revision: https://reviews.llvm.org/D159058
2024-03-10 14:33:34 -05:00
Noah Goldstein
f89e4e339f [InstCombine] Move foldICmpWithLowBitMaskedVal to foldICmpCommutative; NFC 2024-03-10 14:33:34 -05:00
Jeremy Morse
2fe81edef6 [NFC][RemoveDIs] Insert instruction using iterators in Transforms/
As part of the RemoveDIs project we need LLVM to insert instructions using
iterators wherever possible, so that the iterators can carry a bit of
debug-info. This commit implements some of that by updating the contents of
llvm/lib/Transforms/Utils to always use iterator-versions of instruction
constructors.

There are two general flavours of update:
 * Almost all call-sites just call getIterator on an instruction
 * Several make use of an existing iterator (scenarios where the code is
   actually significant for debug-info)
The underlying logic is that any call to getFirstInsertionPt or similar
APIs that identify the start of a block need to have that iterator passed
directly to the insertion function, without being converted to a bare
Instruction pointer along the way.

Noteworthy changes:
 * FindInsertedValue now takes an optional iterator rather than an
   instruction pointer, as we need to always insert with iterators,
 * I've added a few iterator-taking versions of some value-tracking and
   DomTree methods -- they just unwrap the iterator. These are purely
   convenience methods to avoid extra syntax in some passes.
 * A few calls to getNextNode become std::next instead (to keep in the
   theme of using iterators for positions),
 * SeparateConstOffsetFromGEP has it's insertion-position field changed.
   Noteworthy because it's not a purely localised spelling change.

All this should be NFC.
2024-03-05 15:12:22 +00:00
Yingwei Zheng
d51fcd4ed8
[InstCombine] Handle scalable splat in getFlippedStrictnessPredicateAndConstant (#83980)
This patch adds support for canonicalization of icmp with a scalable
splat. Some optimizations assume that `icmp pred X, APInt C` is in
canonical form.

Fixes https://github.com/llvm/llvm-project/issues/83931.
2024-03-05 21:08:15 +08:00
SahilPatidar
94a0dd5a19
[InstCombine] Fix Failure to convert vector fp comparisons that can be represented as integers #82241 (#83274)
Resolve #82241

---------

Co-authored-by: SahilPatidar <patidarsahil@2001gmail.com>
2024-03-05 17:19:01 +05:30
Simon Pilgrim
d2173d8f53 [InstCombine] foldFCmpIntToFPConst - simplify repeated calls to getBitWidth/getScalarSizeInBits. NFC.
Noticed on #82241 - we don't need to use the IntegerType just for the scalar width, and we were calling it 3 times in different forms - we can just call Type::getScalarSizeInBits once and reuse.
2024-02-20 18:10:25 +00:00
Yingwei Zheng
dc866ae49e
[ValueTracking] Move the isSignBitCheck helper into ValueTracking. NFC. (#81704)
This patch moves the `isSignBitCheck` helper into ValueTracking to reuse
the logic in ValueTracking/InstSimplify.

Addresses the comment
https://github.com/llvm/llvm-project/pull/80740#discussion_r1488440050.
2024-02-14 15:33:08 +08:00
Nikita Popov
b1b8a383fc
[InstCombine] Remove one-use restriction on icmp of gep fold (#76730)
The fold for icmp (gep (p, i1), gep (p, i2)) to icmp (i1, i2) is
currently limited to one of the GEPs either having one use or a constant
offset. I believe this is to avoid duplicating complex arithmetic both
in the GEP and the offset comparison.

This patch instead does the same thing that the indexed compare fold
does, which is to rewrite the GEP into i8 form if necessary, so that the
offset arithmetic is not repeated after the transform.

I ran into this problem in a case where there are multiple conditions on
the same pointer, which prevents them from getting folded.
2024-02-09 15:25:24 +01:00
Yingwei Zheng
c8ca98a2a9
[InstCombine] Handle IsInf/IsZero idioms (#80607)
This patch does the following folds:
```
icmp eq/ne (bitcast X to int), (bitcast +/-inf to int) -> llvm.is.fpclass(X, (~)fcPosInf/fcNegInf)
icmp eq/ne (bitcast X to int), (bitcast +0/-0 to int) -> llvm.is.fpclass(X, (~)fcPosZero/fcNegZero)
```
Alive2: https://alive2.llvm.org/ce/z/JJmEE9
2024-02-08 13:45:27 +08:00
Yingwei Zheng
934ba0d59e
[InstCombine] Handle missing cases in visitFCmpInst
Fiix buildbot failures.
2024-02-08 00:15:32 +08:00
Yingwei Zheng
7a71ac2b00
[InstCombine] Canonicalize fcmp with inf (#80986)
This patch canonicalizes floating-point comparisons with inf:
```
fcmp olt X, +inf -> fcmp one X, +inf
fcmp ole X, +inf -> fcmp ord X, 0
fcmp ogt X, +inf -> false
fcmp oge X, +inf -> fcmp oeq X, +inf
fcmp ult X, +inf -> fcmp une X, +inf
fcmp ule X, +inf -> true
fcmp ugt X, +inf -> fcmp uno X, 0
fcmp uge X, +inf -> fcmp ueq X, +inf
fcmp olt X, -inf -> false
fcmp ole X, -inf -> fcmp oeq X, -inf
fcmp ogt X, -inf -> fcmp one X, -inf
fcmp oge X, -inf -> fcmp ord X, 0
fcmp ult X, -inf -> fcmp uno X, 0
fcmp ule X, -inf -> fcmp ueq X, -inf
fcmp ugt X, -inf -> fcmp une X, -inf
fcmp uge X, -inf -> true
```
Alive2: https://alive2.llvm.org/ce/z/FRqqDg

The motivation of this patch is to fix the regression found in
https://github.com/dtcxzyw/llvm-opt-benchmark/pull/199#discussion_r1480974120.
2024-02-07 23:27:41 +08:00
Yingwei Zheng
f37d81f8a3
[PatternMatch] Add a matching helper m_ElementWiseBitCast. NFC. (#80764)
This patch introduces a matching helper `m_ElementWiseBitCast`, which is
used for matching element-wise int <-> fp casts.
The motivation of this patch is to avoid duplicating checks in
https://github.com/llvm/llvm-project/pull/80740 and
https://github.com/llvm/llvm-project/pull/80414.
2024-02-07 21:02:13 +08:00
Nikita Popov
e60c4b61f8 [InstCombine] Change order of checks for dominating conditions (NFC)
Check whether the condition is in the expected format before
performing more expensive dominator checks.
2024-02-07 11:17:31 +01:00
Yingwei Zheng
930996e9e4
[ValueTracking][NFC] Pass SimplifyQuery to computeKnownFPClass family (#80657)
This patch refactors the interface of the `computeKnownFPClass` family
to pass `SimplifyQuery` directly.
The motivation of this patch is to compute known fpclass with
`DomConditionCache`, which was introduced by
https://github.com/llvm/llvm-project/pull/73662. With
`DomConditionCache`, we can do more optimization with context-sensitive
information.

Example (extracted from
[fmt/format.h](e17bc67547/include/fmt/format.h (L3555-L3566))):
```
define float @test(float %x, i1 %cond) {
  %i32 = bitcast float %x to i32
  %cmp = icmp slt i32 %i32, 0
  br i1 %cmp, label %if.then1, label %if.else

if.then1:
  %fneg = fneg float %x
  br label %if.end

if.else:
  br i1 %cond, label %if.then2, label %if.end

if.then2:
  br label %if.end

if.end:
  %value = phi float [ %fneg, %if.then1 ], [ %x, %if.then2 ], [ %x, %if.else ]
  %ret = call float @llvm.fabs.f32(float %value)
  ret float %ret
}
```
We can prove the signbit of `%value` is always zero. Then the fabs can
be eliminated.
2024-02-06 02:30:12 +08:00
Yingwei Zheng
390b99743b
[InstCombine] Handle isNanOrInf idioms (#80414)
This patch folds:
```
(icmp eq (and (bitcast X to int), ExponentMask), ExponentMask) --> llvm.is.fpclass(X, fcInf|fcNan)
(icmp ne (and (bitcast X to int), ExponentMask), ExponentMask) --> llvm.is.fpclass(X, ~(fcInf|fcNan))
```
Alive2: https://alive2.llvm.org/ce/z/_hXAAF
2024-02-04 07:09:37 +08:00
Yingwei Zheng
817d0cb485
[InstCombine] Simplify commutative compares of symmetric pairs (#80134)
Fixes #78038.
2024-01-31 21:21:27 +08:00
Mikhail Gudim
701ec45f2f
[InstCombine] Fix a comment. (#79422) 2024-01-26 23:10:19 -05:00
Nikita Popov
de8f782355 Revert "Simplify (a % b) lt/ge (b-1) into (a % b) eq/ne (b-1) (#72504)"
This reverts commit 01f4d40aad58c5c34a8ae30edbf4e0ebbf235838.

Causes test failures.
2024-01-16 11:39:42 +01:00
elhewaty
01f4d40aad
Simplify (a % b) lt/ge (b-1) into (a % b) eq/ne (b-1) (#72504)
Alive2: https://alive2.llvm.org/ce/z/i7zYtE
Fixes: https://github.com/llvm/llvm-project/issues/71280
2024-01-16 10:15:15 +01:00
Noah Goldstein
60e8915d22 [InstCombine] Add folds for (add/sub/disjoint_or/icmp C, (ctpop (not x)))
`(ctpop (not x))` <-> `(sub nuw nsw BitWidth(x), (ctpop x))`. The
`sub` expression can sometimes be constant folded depending on the use
case of `(ctpop (not x))`.

This patch adds fold for the following cases:

`(add/sub/disjoint_or C, (ctpop (not x))`
    -> `(add/sub/disjoint_or C', (ctpop x))`
`(cmp pred C, (ctpop (not x))`
    -> `(cmp swapped_pred C', (ctpop x))`

Where `C'` depends on how we constant fold `C` with `BitWidth(x)` for
the given opcode.

Proofs: https://alive2.llvm.org/ce/z/qUgfF3

Closes #77859
2024-01-15 12:05:38 -08:00
Vitaly Buka
253d2f931e
Revert "[InstCombine] Fold icmp pred (inttoptr X), (inttoptr Y) -> icmp pred X, Y" (#78023)
Reverts llvm/llvm-project#77832

To fix https://lab.llvm.org/buildbot/#/builders/236/builds/8673

Also truncation to shorter type looks incorrect.

Issue for tracking #78024 .
2024-01-13 11:15:30 -08:00
Yingwei Zheng
2aae304cbc
[InstCombine] Fold icmp pred (inttoptr X), (inttoptr Y) -> icmp pred X, Y (#77832)
NOTE: Alive2 proofs are unavailable because `inttoptr` is unsupported.
2024-01-12 23:03:07 +08:00
Yingwei Zheng
2eb7a82af3
[InstCombine] Relax the one-use constraints for icmp pred (binop X, Z), (binop Y, Z) (#76384)
This patch relaxes the one-use constraints for `icmp pred (binop X, Z),
(binop Y, Z)`. It will enable more optimizations with pointer
arithmetic.
One example in `boost::match_results::set_size`:

```
declare void @use(i64)
define i1 @src(ptr %a1, ptr %a2, ptr %add.ptr.i66, i64 %sub.ptr.rhs.cast.i) {
  %sub.ptr.lhs.cast.i = ptrtoint ptr %a1 to i64
  %sub.ptr.rhs.cast.i = ptrtoint ptr %a2 to i64
  %sub.ptr.sub.i = sub i64 %sub.ptr.lhs.cast.i, %sub.ptr.rhs.cast.i
  %sub.ptr.div.i = sdiv exact i64 %sub.ptr.sub.i, 24
  call void @use(i64 %sub.ptr.div.i)
  %sub.ptr.lhs.cast.i.i = ptrtoint ptr %add.ptr.i66 to i64
  %sub.ptr.sub.i.i = sub i64 %sub.ptr.lhs.cast.i.i, %sub.ptr.rhs.cast.i
  %sub.ptr.div.i.i = sdiv exact i64 %sub.ptr.sub.i.i, 24
  %cmp.i.not.i.i = icmp eq i64 %sub.ptr.div.i.i, %sub.ptr.div.i
  ret i1 %cmp.i.not.i.i
}
define i1 @tgt(ptr %a1, ptr %a2, ptr %add.ptr.i66, i64 %sub.ptr.rhs.cast.i) {
  %sub.ptr.lhs.cast.i = ptrtoint ptr %a1 to i64
  %sub.ptr.rhs.cast.i = ptrtoint ptr %a2 to i64
  %sub.ptr.sub.i = sub i64 %sub.ptr.lhs.cast.i, %sub.ptr.rhs.cast.i
  %sub.ptr.div.i = sdiv exact i64 %sub.ptr.sub.i, 24
  call void @use(i64 %sub.ptr.div.i)
  %cmp.i.not.i.i = icmp eq i64 %sub.ptr.sub.i.i, %sub.ptr.sub.i
  ret i1 %cmp.i.not.i.i
}
```
2024-01-07 20:16:12 +08:00
Z572
86ef039220
[InstCombine] Simplify compare abs(X) and X. (#76385)
fix https://github.com/llvm/llvm-project/issues/72653
proof: https://alive2.llvm.org/ce/z/LZzZaj
2024-01-05 17:08:49 +08:00
Yingwei Zheng
6681650025
[InstCombine] Revert the signed icmp -> unsigned icmp canonicalization when folding icmp Pred min|max(X, Y), Z (#76685)
This patch tries to flip the signedness of predicates when folding an
unsigned icmp with a signed min/max. It will enable more optimizations
as we canonicalizes a signed icmp into an unsigned icmp when both
operands are known to have the same sign.
Fixes #76672.

Compile-time impact:
http://llvm-compile-time-tracker.com/compare.php?from=949ec83eaf6fa6dbffb94c2ea9c0a4d5efdbd239&to=2deca1aea8a4e13609bab72c522a97d424f0fc2d&stat=instructions:u


|stage1-O3|stage1-ReleaseThinLTO|stage1-ReleaseLTO-g|stage1-O0-g|stage2-O3|stage2-O0-g|stage2-clang|
|--|--|--|--|--|--|--|
|-0.00%|+0.01%|+0.05%|-0.12%|-0.01%|-0.03%|-0.00%|

NOTE: We can flip the signedness of predicate if both operands are
negative. But I don't see the benefit of handling these cases.
2024-01-05 14:39:16 +08:00
Nikita Popov
9d5b0965c4 [InstCombine] Add helper for commutative icmp folds (NFCI)
Add a common place for icmp folds that should be tried with both
operand orders, so we don't have to repeat this pattern for
individual folds.
2024-01-02 16:16:32 +01:00
Mikhail Gudim
7a581c34f1
Reland "[InstCombine] Extend foldICmpBinOp to add-like or" (#76531)
The original PR had a typo which was causing a bug.
2023-12-30 01:55:07 -05:00
XChy
dafd17895f [InstCombine][NFC] Format code in foldCmpLoadFromIndexedGlobal 2023-12-29 17:42:38 +08:00
Yingwei Zheng
aacff347af
[InstCombine] Simplify icmp pred (sdiv exact X, C), (sdiv exact Y, C) into icmp pred X, Y when C is positive (#76409)
Alive2: https://alive2.llvm.org/ce/z/u49dQ9
It will improve the codegen of `std::_Vector_base<T>::~_Vector_base()` when `sizeof(T)` is not a power of 2.

NOTE: We can also fold `icmp signed-pred (sdiv exact X, C), (sdiv exact Y, C)` into `icmp signed-pred (sdiv exact Y, C), (sdiv exact X, C)` when C is negative. But I don't think it enables more optimizations for real-world applications.
2023-12-27 06:06:16 +08:00
Mikhail Gudim
411cba215a
Revert "[InstCombine] Extend foldICmpBinOp to add-like or. (#71… (#76167)
…396)"

This reverts commit 8773c9be3d9868288f1f46957945d50ff58e4e91.
2023-12-21 11:41:09 -05:00
Mikhail Gudim
8773c9be3d
[InstCombine] Extend foldICmpBinOp to add-like or. (#71396)
InstCombine canonicalizes `add` to `or` when possible, but this makes
some optimizations applicable to `add` to be missed because they don't
realize that the `or` is equivalent to `add`.

In this patch we generalize `foldICmpBinOp` to handle such cases.
2023-12-20 17:28:57 -05:00
Yingwei Zheng
b7f50e13d8
[InstCombine] Improve foldICmpWithDominatingICmp with DomConditionCache (#75370)
This patch uses affected values from DomConditionCache(introduced by #73662), instead of a cheap/incomplete check `getSinglePredecessor`.
2023-12-14 21:02:10 +08:00
Kazu Hirata
a16429365c [Transforms] Remove unnecessary includes (NFC) 2023-12-09 18:23:06 -08:00
Nikita Popov
4a2a6397f1 [InstCombine] Relax one-use check for icmp of gep fold
Instead of checking whether the GEP as a whole is constant, only
check whether it has constant incides. This matches what we do in
other places in this code.

This has little practical impact, because it is mostly already
handled through other cases anyway. We see a difference for
non-inbounds equality comparisons.
2023-12-08 15:45:58 +01:00
Nikita Popov
cf47af493b
[InstCombine] Generalize folds for inversion of icmp operands (#74317)
We have a bunch of folds that basically perform X pred Y to ~Y pred ~X
for various special cases where this saves an instruction.

Generalize these folds to use isFreeToInvert(). We have to make sure
that we consume an instruction in either of the inversions, otherwise
we're just going to swap the icmp back and forth.

Fixes https://github.com/llvm/llvm-project/issues/74302.
2023-12-08 11:25:41 +01:00
Nikita Popov
d77067d08a
[ValueTracking] Add dominating condition support in computeKnownBits() (#73662)
This adds support for using dominating conditions in computeKnownBits()
when called from InstCombine. The implementation uses a
DomConditionCache, which stores which branches may provide information
that is relevant for a given value.

DomConditionCache is similar to AssumptionCache, but does not try to do
any kind of automatic tracking. Relevant branches have to be explicitly
registered and invalidated values explicitly removed. The necessary
tracking is done inside InstCombine.

The reason why this doesn't just do exactly the same thing as
AssumptionCache is that a lot more transforms touch branches and branch
conditions than assumptions. AssumptionCache is an immutable analysis
and mostly gets away with this because only a handful of places have to
register additional assumptions (mostly as a result of cloning). This is
very much not the case for branches.

This change regresses compile-time by about ~0.2%. It also improves
stage2-O0-g builds by about ~0.2%, which indicates that this change results
in additional optimizations inside clang itself.

Fixes https://github.com/llvm/llvm-project/issues/74242.
2023-12-06 14:17:18 +01:00
Nikita Popov
d6e8f3b9a2 [ValueTracking] Convert isKnownPositive() to use SimplifyQuery (NFC) 2023-11-29 11:08:39 +01:00
Nikita Popov
d01237c45b
[InstCombine] Make indexed compare fold GEP source type independent (#71663)
The indexed compare fold converts comparisons of GEPs with same
(indirect) base into comparisons of offset. Currently, it only supports
GEPs with the same source element type.

This change makes the transform operate on offsets instead, which
removes the type dependence. To keep closer to the scope of the original
implementation, this keeps the limitation that we should only have at
most one variable index per GEP.

This addresses the main regression from
https://github.com/llvm/llvm-project/pull/68882.

TBH I have some doubts that this is really a useful transform (at least
for the case where there are extra pointer users, so we have to
rematerialize pointers at some point). I can only assume it exists for a
reason...
2023-11-28 09:16:04 +01:00
Noah Goldstein
b7c0f79926 [InstCombine] Replace isFreeToInvert + CreateNot with getFreelyInverted
This is nearly an NFC, the only change is potentially to order that
values are created/names.

Otherwise it is a slight speed boost/simplification to avoid having to
go through the `getFreelyInverted` recursive logic twice to simplify
the extra `not` op.
2023-11-20 17:59:27 -06:00
Noah Goldstein
99387e33dc [InstCombine] Add transforms for (icmp uPred (trunc x),(truncOrZext(y)))->(icmp uPred x,y)
Three transforms (all commutative):
https://alive2.llvm.org/ce/z/Bc-nh4

Closes #71309
2023-11-19 12:15:04 -06:00
elhewaty
daddf402d9
[InstCombine] Fold xored one-complemented operand comparisons (#69882)
- [InstCombine] Add test coverage for comparisons of operands including
one-complemented oparands(NFC).
- [InstCombine] Fold xored one-complemented operand comparisons.
Alive2: https://alive2.llvm.org/ce/z/PZMJeB
Fixes #69803.
2023-11-14 21:54:03 +08:00
Léonard Oest O'Leary
ff36411b23
[InstCombine] Use zext's nneg flag for icmp folding (#70845)
This PR fixes https://github.com/llvm/llvm-project/issues/55013 : the
max intrinsics is not generated for this simple loop case :
https://godbolt.org/z/hxz1xhMPh. This is caused by a ICMP not being
folded into a select, thus not generating the max intrinsics.

For the story :

Since LLVM 14, SCCP pass got smarter by folding sext into zext for
positive ranges : https://reviews.llvm.org/D81756. After this change,
InstCombine was sometimes unable to fold ICMP correctly as both of the
arguments pointed to mismatched zext/sext. To fix this, @rotateright
implemented this fix : https://reviews.llvm.org/D124419 that tries to
resolve the mismatch by knowing if the argument of a zext is positive
(in which case, it is like a sext) by using ValueTracking, however
ValueTracking is not smart enough to infer that the value is positive in
some cases. Recently, @nikic implemented #67982 which keeps the
information that a zext is non-negative. This PR simply uses this
information to do the folding accordingly.

TLDR : This PR uses the recent nneg tag on zext to fold the icmp
accordingly in instcombine.

This PR also contains test cases for sext/zext folding with InstCombine
as well as a x86 regression tests for the max/min case.
2023-11-13 00:53:53 +08:00
Nikita Popov
567c02a80e [InstCombine] Remove inttoptr/ptrtoint handling from indexed compare fold
Looking through inttoptr / ptrtoint intermixed with GEPs is very
questionable from a provenance perspective. We also don't seem to
have any test coverage that shows this is useful (apart from one
test I added to guard against a crash).
2023-11-08 11:13:57 +01:00
Nikita Popov
abc27bd31f [InstCombine] Avoid some FP cast constant expressions (NFCI)
Instead of doing fptoxi and xitofp casts to check for round-trip,
directly check the IsExact flag on the convertToInteger() API.
2023-11-06 14:42:42 +01:00