From 53b5b96f2c45c035d375283a040ec4d5bd5f88b4 Mon Sep 17 00:00:00 2001 From: Matt Morley Date: Wed, 10 Jun 2026 21:37:43 -0700 Subject: [PATCH 01/12] Initialize vars --- src/mrcal-uncertainty.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/mrcal-uncertainty.cpp b/src/mrcal-uncertainty.cpp index 386611f..3ab555c 100644 --- a/src/mrcal-uncertainty.cpp +++ b/src/mrcal-uncertainty.cpp @@ -268,18 +268,27 @@ _dq_db_projection_uncertainty(mrcal_point3_t pcam, mrcal_lensmodel_t lensmodel, double _observed_pixel_uncertainty_from_inputs(std::vector &x, int num_measurements_board, int measurement_index_board) { - // Compute variance from residuals + // Bounds checking: ensure measurement range is within vector + if (measurement_index_board < 0 || num_measurements_board <= 0 || + static_cast(measurement_index_board + num_measurements_board) > x.size()) { + throw std::runtime_error( + "Invalid measurement indices in _observed_pixel_uncertainty_from_inputs"); + } + + // Compute variance from residuals over the specified range double sum = 0.0, sum_sq = 0.0; - for (size_t i = measurement_index_board; + for (int i = measurement_index_board; i < measurement_index_board + num_measurements_board; i++) { double val = x[i]; sum += val; sum_sq += val * val; } - double mean = sum / x.size(); - double variance = (sum_sq / x.size()) - (mean * mean); - return std::sqrt(variance); + // Compute statistics over the measurement range (not total vector) + double mean = sum / num_measurements_board; + double variance = (sum_sq / num_measurements_board) - (mean * mean); + + return std::sqrt(std::max(0.0, variance)); } CalibrationUncertaintyContext create_calibration_uncertainty_context( @@ -312,6 +321,9 @@ CalibrationUncertaintyContext create_calibration_uncertainty_context( std::vector Jt_i(N_j_nonzero); std::vector Jt_x(N_j_nonzero); + // Initialize Jt_p[0] to 0 for sparse matrix validity + Jt_p[0] = 0; + cholmod_sparse Jt = {.nrow = static_cast(Nstate), .ncol = static_cast(Nmeasurements), .nzmax = static_cast(N_j_nonzero), @@ -475,7 +487,9 @@ std::vector compute_uncertainty( cv::Size imagerSize, cv::Size calobjectSize, double calobjectSpacing, cv::Size sampleResolution) { - mrcal_lensmodel_t lensmodel; + // Completely initialize lensmodel to zero to avoid uninitialized memory on ARM64 + // This is critical: uninitialized fields can cause crashes in BLAS/LAPACK operations + mrcal_lensmodel_t lensmodel = {}; lensmodel.type = MRCAL_LENSMODEL_OPENCV8; // Create calibration uncertainty context once From af8c081e901b9d33e96d37715f158e57b9ee6d3a Mon Sep 17 00:00:00 2001 From: Matt Morley Date: Wed, 10 Jun 2026 21:37:46 -0700 Subject: [PATCH 02/12] cmake updates --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 08ef020..c68a9e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,11 @@ if(WITH_ASAN) add_compile_options(-fsanitize=address -g -Wall -fsanitize=undefined) endif() +# GCC-specific flags to catch uninitialized variables and undefined behavior +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + add_compile_options(-Wuninitialized -Werror=uninitialized -fno-common) +endif() + # Include FetchContent module for downloading dependencies include(FetchContent) From dcb4cc7006042d718e6bdbb5084d0f45205ca6bf Mon Sep 17 00:00:00 2001 From: Matt Morley Date: Wed, 10 Jun 2026 21:45:47 -0700 Subject: [PATCH 03/12] Maybe pass target on to openblass --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c68a9e1..215480c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ if( AND CMAKE_SYSTEM_NAME STREQUAL "Linux" ) set(TARGET ARMV8) + set(ENV{TARGET} ARMV8) endif() # We need PIC for this to work as a shared library set(CMAKE_POSITION_INDEPENDENT_CODE ON) From 24e7a9776fb2941dce48d857a72fc78716c15f8c Mon Sep 17 00:00:00 2001 From: Matt Morley Date: Wed, 10 Jun 2026 21:50:09 -0700 Subject: [PATCH 04/12] lint? --- src/mrcal-uncertainty.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mrcal-uncertainty.cpp b/src/mrcal-uncertainty.cpp index 3ab555c..8a55c9e 100644 --- a/src/mrcal-uncertainty.cpp +++ b/src/mrcal-uncertainty.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include using namespace cv; From e22aabf623c44a8fea02d7c19bac46fc71293a7b Mon Sep 17 00:00:00 2001 From: Matt Morley Date: Wed, 10 Jun 2026 22:01:05 -0700 Subject: [PATCH 05/12] lint --- src/mrcal-uncertainty.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/mrcal-uncertainty.cpp b/src/mrcal-uncertainty.cpp index 8a55c9e..0f46cce 100644 --- a/src/mrcal-uncertainty.cpp +++ b/src/mrcal-uncertainty.cpp @@ -17,10 +17,10 @@ #include "mrcal-uncertainty.hpp" #include +#include #include #include #include -#include #include using namespace cv; @@ -271,9 +271,10 @@ double _observed_pixel_uncertainty_from_inputs(std::vector &x, int measurement_index_board) { // Bounds checking: ensure measurement range is within vector if (measurement_index_board < 0 || num_measurements_board <= 0 || - static_cast(measurement_index_board + num_measurements_board) > x.size()) { - throw std::runtime_error( - "Invalid measurement indices in _observed_pixel_uncertainty_from_inputs"); + static_cast(measurement_index_board + num_measurements_board) > + x.size()) { + throw std::runtime_error("Invalid measurement indices in " + "_observed_pixel_uncertainty_from_inputs"); } // Compute variance from residuals over the specified range @@ -488,8 +489,9 @@ std::vector compute_uncertainty( cv::Size imagerSize, cv::Size calobjectSize, double calobjectSpacing, cv::Size sampleResolution) { - // Completely initialize lensmodel to zero to avoid uninitialized memory on ARM64 - // This is critical: uninitialized fields can cause crashes in BLAS/LAPACK operations + // Completely initialize lensmodel to zero to avoid uninitialized memory on + // ARM64 This is critical: uninitialized fields can cause crashes in + // BLAS/LAPACK operations mrcal_lensmodel_t lensmodel = {}; lensmodel.type = MRCAL_LENSMODEL_OPENCV8; From 76c956165a82b63562026ea5d799bb8a3d4c68e5 Mon Sep 17 00:00:00 2001 From: Matt M Date: Wed, 10 Jun 2026 22:08:41 -0700 Subject: [PATCH 06/12] be even more clear --- CMakeLists.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 215480c..f7815b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,14 +80,6 @@ set(USE_LOCKING ON) set(USE_THREAD OFF) set(NOFORTRAN ON) -# ARMv8 is what our coprocessors run, but not the CI machines. Ensure we don't accidentally include ARMv9 instructions -if( - CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64" - AND CMAKE_SYSTEM_NAME STREQUAL "Linux" -) - set(TARGET ARMV8) - set(ENV{TARGET} ARMV8) -endif() # We need PIC for this to work as a shared library set(CMAKE_POSITION_INDEPENDENT_CODE ON) @@ -95,11 +87,19 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w") if(NOT APPLE) set(BLA_VENDOR OpenBLAS) + # Ensure OpenBLAS uses only ARMv8 instructions and disables SVE + set(OPENBLAS_CFLAGS "-march=armv8-a") FetchContent_Declare( BLAS GIT_REPOSITORY https://github.com/OpenMathLib/OpenBLAS.git GIT_TAG v0.3.30 OVERRIDE_FIND_PACKAGE + CMAKE_ARGS + -DCMAKE_C_FLAGS=${OPENBLAS_CFLAGS} + -DCMAKE_CXX_FLAGS=${OPENBLAS_CFLAGS} + -DDYNAMIC_ARCH=0 + -DARCH=ARM64 + -DTARGET=ARMV8 ) FetchContent_MakeAvailable(BLAS) From 79bf15bdfb90e56fabf0d85138ca88500898de79 Mon Sep 17 00:00:00 2001 From: Matt M Date: Wed, 10 Jun 2026 22:18:20 -0700 Subject: [PATCH 07/12] be really really explicit plz --- .github/workflows/main.yml | 1 + CMakeLists.txt | 50 ++++++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b8d4506..5d7c8b3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,6 +21,7 @@ jobs: arch-name: linuxx86-64 - os: ubuntu-24.04-arm arch-name: linuxarm64 + extra-flags: -DARM64_BUILD=ON - os: windows-latest arch-name: windowsx86-64 extra-flags: -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER=clang-cl diff --git a/CMakeLists.txt b/CMakeLists.txt index f7815b6..80be047 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,21 +85,55 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) # Silence OpenBLAS/LAPACK warnings set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w") + if(NOT APPLE) set(BLA_VENDOR OpenBLAS) - # Ensure OpenBLAS uses only ARMv8 instructions and disables SVE - set(OPENBLAS_CFLAGS "-march=armv8-a") + + if(ARM64_BUILD) + # ---------------------------------------------------------------- + # Variables for subdirectory builds must be set in the parent cache + # BEFORE FetchContent_MakeAvailable. + # + # We also force CMAKE_CROSSCOMPILING so OpenBLAS skips its getarch + # host-CPU detection (which would detect the build machine's CPU, + # potentially a Neoverse/SVE host, and silently override TARGET). + # Without this, TARGET=ARMV8 is ignored when not cross-compiling. + # ---------------------------------------------------------------- + + # Force OpenBLAS to treat this as a cross-compile so it respects TARGET + # and does not run getarch to auto-detect the build host's CPU. + set(CMAKE_CROSSCOMPILING TRUE CACHE BOOL "" FORCE) + + # TARGET=ARMV8 selects the generic AArch64 kernel (NEON/ASIMD only). + # getarch2.c confirms ARMV8 sets: HAVE_NEON, HAVE_VFP, HAVE_VFPV3, + # HAVE_VFPV4 — and critically does NOT set HAVE_SVE. + # Do NOT use: ARMV8SVE, NEOVERSEN1, NEOVERSEN2, NEOVERSEV1, CORTEXA510, + # CORTEXX2, ARMV9 — all of those enable SVE kernels. + set(TARGET "ARMV8" CACHE STRING "" FORCE) + + # Disable dynamic multi-arch dispatch. If left ON, OpenBLAS compiles + # kernels for every supported core including SVE targets and picks one + # at runtime — your RK3588 could still land on an SVE kernel. + set(DYNAMIC_ARCH OFF CACHE BOOL "" FORCE) + set(DYNAMIC_OLDER OFF CACHE BOOL "" FORCE) + + # Lock the compiler to ARMv8-A baseline. This prevents the compiler + # from emitting SVE intrinsics even if OpenBLAS assembly kernels + # happen to include SVE .S files guarded by preprocessor. + set(CMAKE_C_FLAGS_OPENBLAS "-march=armv8-a" CACHE STRING "" FORCE) + set(COMMON_OPT "-march=armv8-a" CACHE STRING "" FORCE) + + # Explicitly set ARCH so OpenBLAS does not re-detect it. + set(ARCH "aarch64" CACHE STRING "" FORCE) + endif() + FetchContent_Declare( BLAS GIT_REPOSITORY https://github.com/OpenMathLib/OpenBLAS.git GIT_TAG v0.3.30 OVERRIDE_FIND_PACKAGE - CMAKE_ARGS - -DCMAKE_C_FLAGS=${OPENBLAS_CFLAGS} - -DCMAKE_CXX_FLAGS=${OPENBLAS_CFLAGS} - -DDYNAMIC_ARCH=0 - -DARCH=ARM64 - -DTARGET=ARMV8 + # NOTE: CMAKE_ARGS is intentionally absent — it does nothing for + # FetchContent subdirectory builds. All config is via cache vars above. ) FetchContent_MakeAvailable(BLAS) From 2727fa713513e2bdf316b307621abd3cd93b2d05 Mon Sep 17 00:00:00 2001 From: Matt M Date: Wed, 10 Jun 2026 22:18:50 -0700 Subject: [PATCH 08/12] lint --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 80be047..c865064 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,7 @@ if(NOT APPLE) if(ARM64_BUILD) # ---------------------------------------------------------------- - # Variables for subdirectory builds must be set in the parent cache + # Variables for subdirectory builds must be set in the parent cache # BEFORE FetchContent_MakeAvailable. # # We also force CMAKE_CROSSCOMPILING so OpenBLAS skips its getarch From 371eba19639b8d4c60ed57096e5cdf68f80f6530 Mon Sep 17 00:00:00 2001 From: Matt M Date: Wed, 10 Jun 2026 22:34:06 -0700 Subject: [PATCH 09/12] revert --- src/mrcal-uncertainty.cpp | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/mrcal-uncertainty.cpp b/src/mrcal-uncertainty.cpp index 0f46cce..7c12b29 100644 --- a/src/mrcal-uncertainty.cpp +++ b/src/mrcal-uncertainty.cpp @@ -269,15 +269,7 @@ _dq_db_projection_uncertainty(mrcal_point3_t pcam, mrcal_lensmodel_t lensmodel, double _observed_pixel_uncertainty_from_inputs(std::vector &x, int num_measurements_board, int measurement_index_board) { - // Bounds checking: ensure measurement range is within vector - if (measurement_index_board < 0 || num_measurements_board <= 0 || - static_cast(measurement_index_board + num_measurements_board) > - x.size()) { - throw std::runtime_error("Invalid measurement indices in " - "_observed_pixel_uncertainty_from_inputs"); - } - - // Compute variance from residuals over the specified range + // Compute variance from residuals double sum = 0.0, sum_sq = 0.0; for (int i = measurement_index_board; i < measurement_index_board + num_measurements_board; i++) { @@ -286,11 +278,10 @@ double _observed_pixel_uncertainty_from_inputs(std::vector &x, sum_sq += val * val; } - // Compute statistics over the measurement range (not total vector) - double mean = sum / num_measurements_board; - double variance = (sum_sq / num_measurements_board) - (mean * mean); + double mean = sum / x.size(); + double variance = (sum_sq / x.size()) - (mean * mean); - return std::sqrt(std::max(0.0, variance)); + return std::sqrt(variance); } CalibrationUncertaintyContext create_calibration_uncertainty_context( @@ -492,7 +483,7 @@ std::vector compute_uncertainty( // Completely initialize lensmodel to zero to avoid uninitialized memory on // ARM64 This is critical: uninitialized fields can cause crashes in // BLAS/LAPACK operations - mrcal_lensmodel_t lensmodel = {}; + mrcal_lensmodel_t lensmodel{}; lensmodel.type = MRCAL_LENSMODEL_OPENCV8; // Create calibration uncertainty context once From e87755d1040797f825543eca681ae86c49e6486d Mon Sep 17 00:00:00 2001 From: Matt M Date: Wed, 10 Jun 2026 22:34:45 -0700 Subject: [PATCH 10/12] gah --- src/mrcal-uncertainty.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/mrcal-uncertainty.cpp b/src/mrcal-uncertainty.cpp index 7c12b29..f0376eb 100644 --- a/src/mrcal-uncertainty.cpp +++ b/src/mrcal-uncertainty.cpp @@ -314,7 +314,6 @@ CalibrationUncertaintyContext create_calibration_uncertainty_context( std::vector Jt_i(N_j_nonzero); std::vector Jt_x(N_j_nonzero); - // Initialize Jt_p[0] to 0 for sparse matrix validity Jt_p[0] = 0; cholmod_sparse Jt = {.nrow = static_cast(Nstate), @@ -480,9 +479,6 @@ std::vector compute_uncertainty( cv::Size imagerSize, cv::Size calobjectSize, double calobjectSpacing, cv::Size sampleResolution) { - // Completely initialize lensmodel to zero to avoid uninitialized memory on - // ARM64 This is critical: uninitialized fields can cause crashes in - // BLAS/LAPACK operations mrcal_lensmodel_t lensmodel{}; lensmodel.type = MRCAL_LENSMODEL_OPENCV8; From a4837b00ef76af6e351699bddcd3f1c0c73702d1 Mon Sep 17 00:00:00 2001 From: Matt M Date: Wed, 10 Jun 2026 22:35:09 -0700 Subject: [PATCH 11/12] gah --- src/mrcal-uncertainty.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mrcal-uncertainty.cpp b/src/mrcal-uncertainty.cpp index f0376eb..1180651 100644 --- a/src/mrcal-uncertainty.cpp +++ b/src/mrcal-uncertainty.cpp @@ -17,7 +17,6 @@ #include "mrcal-uncertainty.hpp" #include -#include #include #include #include From 60b12f7517b8e0a4d8a4dedbf886b0eecf53e4a9 Mon Sep 17 00:00:00 2001 From: Matt M Date: Wed, 10 Jun 2026 22:35:59 -0700 Subject: [PATCH 12/12] bah --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c865064..54ae1ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,8 +132,6 @@ if(NOT APPLE) GIT_REPOSITORY https://github.com/OpenMathLib/OpenBLAS.git GIT_TAG v0.3.30 OVERRIDE_FIND_PACKAGE - # NOTE: CMAKE_ARGS is intentionally absent — it does nothing for - # FetchContent subdirectory builds. All config is via cache vars above. ) FetchContent_MakeAvailable(BLAS)