Skip to content

Expand llvm.{u,s}mul.with.overflow before SPIR-V emission - #1395

Open
pvelesko wants to merge 2 commits into
mainfrom
2026-07-29-github-1391-umul-overflow
Open

Expand llvm.{u,s}mul.with.overflow before SPIR-V emission#1395
pvelesko wants to merge 2 commits into
mainfrom
2026-07-29-github-1391-umul-overflow

Conversation

@pvelesko

Copy link
Copy Markdown
Collaborator

Fixes #1391

Expands llvm.umul.with.overflow and llvm.smul.with.overflow into plain integer arithmetic in the post-link pass pipeline, so the construct never reaches SPIR-V.

Without this, clang emits llvm.umul.with.overflow.i64 for a device-side new T[n] or __builtin_mul_overflow, the SPIR-V producer turns it into a spirv.llvm_umul_with_overflow_i64 helper, and the consumer maps that back onto the intrinsic, renames the declaration to old_llvm.umul.with.overflow.i64 and leaves it undefined. Since chipStar puts all device code of a binary into one module, the whole program stops working.

Before and after on Aurora PVC (Intel Data Center GPU Max 1550), Level Zero, LLVM 22.1.3 with the in-tree SPIR-V backend, running the new regression test:

### BEFORE
  case 0: got lo=0 ov=0, want lo=42 ov=0
  case 1: got lo=0 ov=0, want lo=18446744073709551614 ov=1
  case 3: got lo=0 ov=0, want lo=0 ov=1
FAIL

CHIP error : hipErrorInvalidImage (Level Zero module build failed:
error: undefined reference to `old_llvm.umul.with.overflow.i64'
in function: 'old_llvm.umul.with.overflow.i64' called by kernel: 'testUmulOverflow(unsigned long*, unsigned long*)'
error: backend compiler failed build.

### AFTER
PASS

Counting umul_with_overflow in the extracted SPIR-V of the same source goes from 7 to 0.

The overflow predicate uses a division rather than a 128-bit multiply, since i128 is not portable across the SPIR-V consumers chipStar targets. These intrinsics come from allocation-size checks, so they are not hot. The signed case splits out a == -1 instead of folding it into the division, because sdiv INT_MIN, -1 is itself undefined.

The regression test uses __builtin_mul_overflow rather than new T[n], so that it exercises the intrinsic without also depending on the device heap, which has a separate problem.

@pvelesko
pvelesko force-pushed the 2026-07-29-github-1391-umul-overflow branch from 4e20bc8 to 353a165 Compare July 31, 2026 03:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Device array-new leaves an undefined old_llvm.umul.with.overflow.i64, killing every kernel in the program

1 participant