Skip to content

Review whether ITK_USE_64BITS_IDS is still needed #6774

Description

@hjmjohnson

ITK_USE_64BITS_IDS selects the width of SizeValueType,
IdentifierType, IndexValueType and OffsetValueType. It is
inert on Linux and macOS, load-bearing on Windows and WebAssembly,
untested in one of its two states, and it caused a live wrapping
defect (#6772). This issue asks whether it should still exist.

This is a deliberate parking issue. No action is proposed for now —
the decision needs a proper ABI/downstream impact assessment that
nobody currently has time to do, and the deciding facts are not in the
repository. Everything needed to pick this up cold is recorded below,
so a future reader needs no other context.

Raised by @N-Dekker on #6772: "Is it still useful nowadays? Why would
some users still want to switch off ITK_USE_64BITS_IDS on Windows,
nowadays?"


The one-paragraph version

OFF is not a Windows state — it is the default everywhere except
64-bit Windows
. The option only changes any type where long and
long long differ in width, which is Windows LLP64 and 32-bit
targets (today: WebAssembly)
; on LP64 Linux/macOS it is a no-op in
both positions. Forcing it ON globally would therefore change nothing
on Linux, macOS, or default Windows, and would land its entire cost on
WebAssembly — while still not producing a uniform type. Genuine
uniformity requires a wider change that carries a non-obvious ABI trap
on macOS (below).

Mechanics — exactly what the option does, with file:line

DeclarationCMakeLists.txt:668-678. Declared for all platforms;
only the default is platform-dependent:

set(ITK_USE_64BITS_IDS_DEFAULT "OFF")
# Note WIN32 is true when targeting any windows system
if(CMAKE_SIZEOF_VOID_P EQUAL "8" AND WIN32)
  set(ITK_USE_64BITS_IDS_DEFAULT "ON")
endif()
option(
  ITK_USE_64BITS_IDS
  "When ON, ITK will use 64-bit integer types instead of long types for sizes and indexes. This is needed for managing images larger than 4Gb in some platforms."
  "${ITK_USE_64BITS_IDS_DEFAULT}"
)
mark_as_advanced(ITK_USE_64BITS_IDS)

PropagationModules/Core/Common/src/itkConfigure.h.in:79
(#cmakedefine ITK_USE_64BITS_IDS).

ConsumptionModules/Core/Common/include/itkIntTypes.h:65:

#if defined(ITK_USE_64BITS_IDS) && ((ULLONG_MAX != ULONG_MAX) || (LLONG_MAX != LONG_MAX))
  using SizeValueType   = uint64_t;   // IdentifierType = SizeValueType
  using IndexValueType  = int64_t;
  using OffsetValueType = int64_t;
#else
  using SizeValueType   = unsigned long;
  using IndexValueType  = long;
  using OffsetValueType = long;
#endif

The second clause is the crux: the 64-bit branch is taken only
where long and long long differ in width.

Data model Platforms long Option's effect
LP64 Linux, macOS (64-bit) 64 Inert — both settings give 64-bit SizeValueType
LLP64 Windows (64-bit) 32 Live — OFF = 32-bit ids, ON = 64-bit. Default ON
ILP32 WebAssembly (wasm32, wasm32-wasi), 32-bit targets 32 Live — ON = 64-bit ids on a 32-bit address space. Default OFF

That inertness on LP64 is why the #6772 mismatch survived from #3502
(July 2022) undetected: every platform where Python wrapping is
routinely built resolves ITKM_IT to UL either way.

WebAssembly is a live, actively-developed ITK target, not a
hypothetical — ITK's own CMakeLists.txt:316 branches on
if(WASI OR EMSCRIPTEN), and
ITK-Wasm is
current.

History — why the option exists
Commit Change
feb637c0b09 ENH: Restore ITK_USE_64BITS_IDS option
fca71585536 COMP: Enable the correct integer types for 64bits Ids
07dfd5b9bd4 COMP: Prevent ITK_USE_64BITS_IDS with Wrapping on Windows (ITK 4.4)
8bb8c743267 ENH: Use ITK_USE_64BITS_IDS for windows 64 by default (ITK 4.10)

Release notes also record COMP: Fix HDF5IO with ITK_USE_64BITS_IDS on Windows (4.3). The arc: the option existed; wrapping could not cope
with it on Windows so it was prevented there in 4.4; it was then made
the Windows default in 4.10 once >4 GB image support outweighed the
wrapping friction.

Prior discussion: Discourse
#2053, "Could ITK drop ITK_USE_64BITS_IDS and just do SizeValueType = std::size_t?"
(2019, 8 posts). Positions:

  • @N-Dekker (opener) — proposed SizeValueType = std::size_t and
    dropping the option, because the profusion of size/offset types makes
    narrowing bugs hard to find. His PR BUG: Fix possible loss of data on type conversion #1087 fixed a batch; "this is
    just a tip of the iceberg."
    Follow-up: std::ptrdiff_t for
    IndexValueType/OffsetValueType.
  • @dzenanz"This is very much overdue. I support this change."
  • @blowekamp — named the two use cases the option actually serves:
    (1) Win64 compatibility with code historically assuming
    unsigned long; (2) 32-bit architectures streaming large images.
    Noted the option's name is inaccurate and that he was unsure either
    case was still relevant. Suggested adding ITK_USE_SIZE_T_IDS to
    experiment rather than making a breaking change during 5.0
    maintenance.
  • @matt.mccormick — objected that size_t is 32-bit on 32-bit ARM
    and ITK wanted to keep processing large images there via streaming.
  • @dzenanz — the cited ARM porting guide was from 2011; 64-bit ARM
    has 64-bit size_t. @matt.mccormick partly conceded: "most of
    the newer and high performance systems are 64-bit ARM … so size_t
    could be fine in practice."

Rough agreement in principle, then it stopped. ITK_USE_SIZE_T_IDS was
never added. Seven years on, the 32-bit-ARM premise that anchored the
main objection is weaker — but WebAssembly has since arrived as a new
ILP32 target, so the category of 32-bit concern is not empty.

What the option costs today

1. A wrapping-visible type divergence.
Wrapping/WrapBasicTypes.cmake:222 is the sole place the mangled
identifier type is chosen:

if(WIN32 AND ITK_USE_64BITS_IDS)
  set(ITKM_IT ${ITKM_ULL})
else()
  set(ITKM_IT ${ITKM_UL})
endif()

Note it keys on the CMake option and WIN32, never on the actual C++
type
. #6772 exists solely because itk/support/types.py re-derived
this independently and disagreed on Windows with the option OFF.

2. A filter silently missing from the Windows Python package.
Modules/Segmentation/Watersheds/wrapping/itkTobogganImageFilter.wrap:

# The itk::IdentifierType, which is unsigned long on Unix but can
# be unsigned long long on Windows when  ITK_USE_64BITS_IDS is
# enabled, is not wrapped in the supporting classes on Windows.
if(NOT WIN32 OR NOT ITK_USE_64BITS_IDS)
  itk_wrap_class("itk::TobogganImageFilter" POINTER)

Because the Windows default is ON, itk.TobogganImageFilter is
absent from the standard Windows Python package — a
platform-dependent API gap with no diagnostic.

3. A configuration nothing tests. No CI job builds Python wrapping
on Windows with ITK_USE_64BITS_IDS=OFF. #6772's defect was findable
only by reading CMake, never by running anything.

Options and trade-offs

A. Remove the option; IdentifierType follows the platform.
Removes the divergence, the Toboggan carve-out and the untested
configuration. But the Windows choice then has to be made for users:
hard-wiring 64-bit ids is an ABI break for anyone building OFF;
following long silently caps Windows at 32-bit ids — a functional
regression, and precisely what 4.10 fixed. Neither is free.

B. Remove only the ability to set OFF on 64-bit Windows.
Answers Niels' question directly and kills the divergence, the Toboggan
carve-out and the #6772 defect class in one step, while leaving the
option meaningful on ILP32/WASM. Narrowest blast radius. Cost: an ABI
break for any Windows consumer deliberately building OFF — existence
currently unknown. Cheapest real improvement, contingent on the
open question below.

C. SizeValueType = std::size_t, Index/Offset = std::ptrdiff_t
(the 2019 proposal). The only route to genuine uniformity and removes a
whole class of narrowing bugs. But see the ABI trap below — it is a
wider change than "same width, no problem" suggests, and would want
forest-build measurement of downstream cost first.

D. Keep it, document it, test it. Add a Windows CI job with
ITK_USE_64BITS_IDS=OFF + Python wrapping so the configuration stops
being unverified; un-mark_as_advanced it. Lowest risk; keeps all
three costs above.

Explicitly considered and rejected: force ON for all platforms.
It does not do what it appears to. Forcing the first clause of the
guard true leaves the second intact:

Platform Forced-ON result Change vs today
Linux / macOS (LP64) unsigned long (2nd clause false) none
Windows 64-bit (LLP64) uint64_t none (already default)
WASM / ILP32 uint64_t 32-bit ids become 64-bit

So it is a no-op on the platforms where most ITK is built, a no-op on
the Windows default, and lands its entire cost on WebAssembly: 64-bit
arithmetic emulated in a 32-bit VM, Index<3>/Offset<3> doubling
12 → 24 bytes, larger binaries — where binary size is a first-order
concern. And it still would not deliver type uniformity, which is the
only thing that retires this bug class.

⚠ The ABI trap in any "just make it uint64_t" approach

Getting one type everywhere requires dropping the
ULLONG_MAX != ULONG_MAX clause too, making SizeValueType = uint64_t
unconditional. That is not width-neutral on LP64 — it is
type-identity-changing, and inconsistently between Linux and macOS:

$ c++ -std=c++17 probe.cxx && ./probe        # macOS arm64, Apple clang
sizeof(long)=8  sizeof(long long)=8  sizeof(size_t)=8
ULLONG_MAX != ULONG_MAX  -> 0
uint64_t is 'unsigned long'      ? 0
uint64_t is 'unsigned long long' ? 1

On macOS uint64_t is unsigned long long; on Linux/glibc it is
unsigned long. Both 64 bits, but distinct types for overload
resolution, name mangling and template instantiation.

Concretely: SizeValueType on macOS would silently move from
unsigned long to unsigned long long while
Wrapping/WrapBasicTypes.cmake:222 still computes ITKM_IT as UL,
because it keys on WIN32 AND ITK_USE_64BITS_IDS and not on the C++
type. That reproduces the exact #6772 defect class on macOS — where CI
would catch it — and on Linux, where it would not. Any such change
must fix the wrapping mangling in the same commit.

Reproduce / re-verify the claims above
# Option declaration and platform-dependent default
sed -n '668,678p' CMakeLists.txt

# The guard that makes it inert on LP64
sed -n '60,95p' Modules/Core/Common/include/itkIntTypes.h

# Every consumer in the tree (should be 5 files)
grep -rn "ITK_USE_64BITS_IDS" --exclude-dir=.git .

# The wrapping divergence
sed -n '215,230p' Wrapping/WrapBasicTypes.cmake

# The Toboggan carve-out
sed -n '1,12p' Modules/Segmentation/Watersheds/wrapping/itkTobogganImageFilter.wrap

# WASM is a supported target
grep -n -A3 "WASI OR EMSCRIPTEN" CMakeLists.txt

Type-identity probe used in the ABI-trap section:

#include <cstdint>
#include <climits>
#include <typeinfo>
#include <cstdio>
int main(){
  printf("sizeof(long)=%zu  sizeof(long long)=%zu  sizeof(size_t)=%zu\n",
         sizeof(long),sizeof(long long),sizeof(size_t));
  printf("ULLONG_MAX != ULONG_MAX  -> %d\n", (ULLONG_MAX != ULONG_MAX));
  printf("uint64_t is 'unsigned long'      ? %d\n", (int)(typeid(uint64_t)==typeid(unsigned long)));
  printf("uint64_t is 'unsigned long long' ? %d\n", (int)(typeid(uint64_t)==typeid(unsigned long long)));
}

Open questions that must be answered before deciding

  1. Does anyone build ITK_USE_64BITS_IDS=OFF on 64-bit Windows, and
    why?
    @blowekamp's 2019 answer was Win64 compatibility with code
    assuming unsigned long. If that constituency is empty, option B is
    cheap and A becomes tractable. A Discourse post is the fastest way
    to find out. This is the single highest-value question here.
  2. Does ITK-Wasm want 64-bit ids, and is the size/perf cost
    acceptable on wasm32?
    Determines whether the option must survive
    for ILP32 at all.
  3. What is the downstream blast radius of option C? Measurable with
    the forest build across SimpleITK, Slicer, ANTs, BRAINSTools,
    elastix.

Suggested sequencing when this is picked up

  1. Ask (1) and (2) on Discourse; wait for a real answer, not silence.
  2. If (1) is empty → implement B, including deleting the Toboggan
    carve-out, and add the Windows-wrapping CI job from D.
  3. Only then evaluate C with forest-build evidence, and only with
    WrapBasicTypes.cmake fixed in the same commit.

Related: #6772 (the wrapping mismatch that surfaced this), #6769,
#3502 (introduced itk.IT, July 2022, commit
6a9aca9906b6660b1d04d7d6971453395c2dfc1d), PR #1087, Discourse
#2053.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:CoreIssues affecting the Core modulearea:Python wrappingPython bindings for a classtype:DesignImprovement in the design of a given area

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions