Skip to content

GH-55613: [C++][Gandiva] Fix wrong results for negative integer constants - #55607

Open
dmitry-chirkov-dremio wants to merge 2 commits into
apache:mainfrom
dmitry-chirkov-dremio:pr1-signed-int-constants
Open

dmitry-chirkov-dremio wants to merge 2 commits into
apache:mainfrom
dmitry-chirkov-dremio:pr1-signed-int-constants

Conversation

@dmitry-chirkov-dremio

@dmitry-chirkov-dremio dmitry-chirkov-dremio commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Rationale for this change

LLVMTypes::int_constant builds llvm::APInt(N, val) without the signed flag. For a negative value narrower than 64 bits, the APInt has bits set above its width, and an i128 value is not sign-extended. The effect depends on the LLVM version:

  • LLVM 21, optimization on: Gandiva returns wrong results. For example if (k == 0) 10 else -5 returns -5 for every row.
  • LLVM 23: assertion Value is not an N-bit unsigned value in the APInt constructor.
  • LLVM 18: the constructor always clears the unused bits, so 16, 32 and 64 bit constants are correct. An i128 constant is wrong on every version.

The same fix exists in the Dremio fork (dremio/arrow 3950ea6).

What changes are included in this PR?

The fix is a one-line change in llvm_types.h: pass std::is_signed<ctype>::value as the signed flag of APInt in LLVMTypes::int_constant.

The PR has two commits: the tests first, then the fix.

Are these changes tested?

Yes. Two new tests:

  • TestLLVMTypes.SignedIntegerConstants checks the 8, 16, 32, 64 and 128 bit constants. Before the fix it fails on every LLVM version tested (18, 21, 23).
  • TestIfExpr.TestNegativeConstants evaluates if (k == 0) 10 else -5 and if (k == 0) -5 else 10 for int16, int32 and int64, with optimization off and on. Before the fix it returns wrong results on LLVM 21 and aborts on LLVM 23. It passes on LLVM 18, where the APInt constructor already clears the unused bits.

Both pass with the fix. The CI run on the tests-only commit shows the results per platform.

Are there any user-facing changes?

No.

This PR contains a "Critical Fix". It fixes a bug that produced incorrect data on recent LLVM versions.

Was AI used for this PR?

AI tools helped investigate the bug and write the tests and description.

PR code and description written by:

  • Human
  • AI

Reviewed before submission by:

  • Human
  • AI
  • Not reviewed

@github-actions

Copy link
Copy Markdown

Thanks for opening a pull request!

This pull request has been automatically converted to a draft because its title doesn't match Arrow's required format.

If this is not a minor PR, could you open an issue for this pull request on GitHub? https://github.com/apache/arrow/issues/new/choose

Opening GitHub issues ahead of time contributes to the Openness of the Apache Arrow project.

Then could you also rename the pull request title in the following format?

GH-${GITHUB_ISSUE_ID}: [${COMPONENT}] ${SUMMARY}

or

MINOR: [${COMPONENT}] ${SUMMARY}

After updating the title, you can mark the pull request as ready for review.

See also:

@dmitry-chirkov-dremio dmitry-chirkov-dremio changed the title [C++][Gandiva] Fix wrong results for negative integer constants [GH-TBD] [C++][Gandiva] Fix wrong results for negative integer constants Nov 14, 2026
@dmitry-chirkov-dremio dmitry-chirkov-dremio changed the title [GH-TBD] [C++][Gandiva] Fix wrong results for negative integer constants GH-TBD: [C++][Gandiva] Fix wrong results for negative integer constants Nov 14, 2026
@dmitry-chirkov-dremio

dmitry-chirkov-dremio commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor Author

CI results for the new tests.

Tests-only commit, before the fix:

Job LLVM TestLLVMTypes.SignedIntegerConstants TestIfExpr.TestNegativeConstants
ARM64 macOS 26 C++ 23.1.4 aborts in APInt (Value is not an N-bit unsigned value) aborts, same assertion
ARM64 Ubuntu 22.04 C++ 18.1.8 fails, then aborts passes, the LLVM 18 APInt constructor always clears the unused bits
AMD64 macOS 15-intel C++ 18.1.8 fails, then aborts passes, same reason
AMD64 Ubuntu 24.04 C++ ASAN UBSAN 18.1.3 fails, then aborts passes, same reason
  • macOS ARM64: this is the [C++][Gandiva] Crashes with LLVM 20.1.1 #45999 assertion.
  • LLVM 18: SignedIntegerConstants fails on i128_constant(-1).isAllOnes() (llvm_types_test.cc:71), then getSExtValue() on the i128 value asserts.
  • Not as predicted: the IF tests pass on LLVM 18. In LLVM 18.1.8 the APInt constructor always calls clearUnusedBits() for a single-word value. In LLVM 21.1.7 it does that only when the signed flag is set. So for 16, 32 and 64 bits the bug shows only on newer LLVM: wrong results on 21 locally, abort on 23 in CI. The i128 constant is wrong on every version, so on LLVM 18 only SignedIntegerConstants catches the bug.

With the fix commit, gandiva-internals-test, gandiva-precompiled-test and gandiva-projector-test pass on all four platforms.

A few known failures seen previously also appeared. They are not related to this PR.

@dmitry-chirkov-dremio dmitry-chirkov-dremio changed the title GH-TBD: [C++][Gandiva] Fix wrong results for negative integer constants GH-55613: [C++][Gandiva] Fix wrong results for negative integer constants Nov 14, 2026
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #55613 has been automatically assigned in GitHub to PR creator.

Add tests for the LLVM integer constants and for IF with negative results.
The constants test fails on every LLVM version tested. The IF test fails or
aborts on recent LLVM and passes on LLVM 18, until the next commit.
…:int_constant

LLVMTypes::int_constant built llvm::APInt(N, val) without the signed flag.
For a negative value narrower than 64 bits the APInt had bits set above its
width, which gives wrong results or an assertion on recent LLVM. An i128
value was not sign-extended.

Co-authored-by: Logan Riggs <logan.riggs@dremio.com>
@dmitry-chirkov-dremio
dmitry-chirkov-dremio marked this pull request as ready for review November 14, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant