New Issue: [Bug]: LICM incorrectly hoists part of conditionals

29034, "jabraham17", "[Bug]: LICM incorrectly hoists part of conditionals", "2026-06-29T16:27:55Z"

The following code works fine, however it results in signed integer overflow

proc tester(maxBytes, maxChars) {
  var buffSz = 10;
  var n = 0;
  var nChars = 0;
  while nChars < maxChars {
    var requestSz = 2*buffSz;
    // make sure to at least request 16 bytes
    if requestSz < n + 16 then requestSz = n + 16;
    // but don't ever ask for more bytes than maxBytes + 5
    if maxBytes < max(int) && requestSz > maxBytes + 5 then
      requestSz = maxBytes + 5;
    nChars += requestSz;
  }
  return n;
}
config const maxBytes:int, maxChars:int;
proc main() {
  tester(maxBytes, maxChars);
}

requestSz > maxBytes + 5 can overflow, however its guarded by maxBytes < max(int), so in practice it shouldn't. There are two issues with that

  • Technically, if maxBytes is 2**63 - 2, it will still overflow because the comparison will pass. This can be fixed by adjusting the code (and it should be)
  • When compiled by default, the Chapel compiler actually LICM's several subexpressions out of the loop, including maxBytes + 5 and maxBytes < max(int). This means that maxBytes + 5 is actually executed unconditionally.

This code snippet is from IO:readStringImpl, and the overflow can be triggered by compiling chpl test/io/serializers/maps.chpl -sformat=FormatKind.json and then running it. Compiling with --no-loop-invariant-code-motion results in the overflow going away. The best way to observe this failure is with CHPL_SANITIZE_EXE=undefined

CHPL_HOST_PLATFORM: linux64
CHPL_HOST_COMPILER: clang
  CHPL_HOST_CC: /llvm-22.1.6-aowpbepnipvtffkn6ccoirruwirq46mv/bin/clang
  CHPL_HOST_CXX: /llvm-22.1.6-aowpbepnipvtffkn6ccoirruwirq46mv/bin/clang++
CHPL_HOST_ARCH: x86_64
CHPL_TARGET_PLATFORM: linux64
CHPL_TARGET_COMPILER: clang *
  CHPL_TARGET_CC: clang
  CHPL_TARGET_CXX: clang++
  CHPL_TARGET_LD: clang++
CHPL_TARGET_ARCH: x86_64
CHPL_TARGET_CPU: native
CHPL_LOCALE_MODEL: flat
CHPL_COMM: none
CHPL_TASKS: qthreads
CHPL_LAUNCHER: none
CHPL_TIMERS: generic
CHPL_UNWIND: bundled
CHPL_HOST_MEM: jemalloc
  CHPL_HOST_JEMALLOC: bundled
CHPL_TARGET_MEM: cstdlib *
CHPL_ATOMICS: cstdlib
CHPL_GMP: bundled
CHPL_HWLOC: bundled
  CHPL_HWLOC_PCI: disable
CHPL_RE2: bundled
CHPL_LLVM: system
  CHPL_LLVM_SUPPORT: system
  CHPL_LLVM_CONFIG: /llvm-22.1.6-aowpbepnipvtffkn6ccoirruwirq46mv/bin/llvm-config *
  CHPL_LLVM_VERSION: 22
CHPL_AUX_FILESYS: none
CHPL_LIB_PIC: none
CHPL_SANITIZE: none
CHPL_SANITIZE_EXE: undefined *

The bad comparison can easily be fixed by adjusting readStringImpl (and should be), but the LICM bug will require a compiler fix (have not investigated further yet). LICM should not violate the semantics of &&. If you replace the + with a function call (even with a user defined overloaded +), the hoist will not occur, so its not possible to get side effects, but undefined behavior will still occur