fix(linalg): register missing Int16/Uint16/Bool rows in CPU Outer dispatch (fixes segfault)#1099
fix(linalg): register missing Int16/Uint16/Bool rows in CPU Outer dispatch (fixes segfault)#1099yingjerkao wants to merge 1 commit into
Conversation
…patch
linalg::Outer segfaulted for Int16, Uint16, or Bool CPU inputs. The
CPU Outer_ii dispatch table in linalg_internal_interface.cpp was never
populated with the Int16/Uint16/Bool *source* rows (every other source
dtype has a full 11-entry row; these three had none). Outer.cpp casts
both operands to the promoted dtype and dispatches through
Outer_ii[out_dtype][out_dtype]; when out_dtype is Int16/Uint16/Bool that
entry was a null function pointer, so the call jumped to address 0 and
crashed. Every sibling table (MM_ii, Sum_ii, Diag_ii, Matmul_ii,
Matvec_ii, and GPU cuOuter_ii) already registers these dtypes, so the
omission was an oversight, not intent.
Fix: register the 33 missing rows. The Outer_internal_{i16,u16,b}t*
implementations already exist and are declared -- this is registration
only, no kernel or algorithm change.
Testing: new tests/linalg_test/Outer_test.cpp (OuterDtypeCoverage:
Int16, Uint16, Bool, Int16xBool) checks independently hand-computed
values. On the pre-fix binary these crash with an ASan SEGV at address
0x0 (null function-pointer call) at the linalg::Outer call site;
post-fix all four pass. Existing DtypePromotion.OuterComplexfloatDouble
and LinalgKronTest tests still pass. clang-format (v14) clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request registers the missing Int16, Uint16, and Bool source rows in the CPU Outer_ii dispatch table to prevent null function pointer dereferences and crashes when using these types as the left operand in linalg::Outer. It also adds comprehensive regression tests in tests/linalg_test/Outer_test.cpp to verify correctness across these data types. The review feedback points out several instances in the new test file where legacy cytnx_uint64 typedefs are used as loop counters, which violates the repository style guide's preference for standard C++ types like size_t or uint64_t in new code.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| for (cytnx_uint64 i = 0; i < 3; i++) | ||
| for (cytnx_uint64 j = 0; j < 2; j++) |
There was a problem hiding this comment.
According to the repository style guide, legacy cytnx_XXXX typedefs (such as cytnx_uint64) should not be propagated as ordinary loop counters in new or touched code. Standard C++ types like size_t or uint64_t should be preferred instead.
| for (cytnx_uint64 i = 0; i < 3; i++) | |
| for (cytnx_uint64 j = 0; j < 2; j++) | |
| for (size_t i = 0; i < 3; i++) | |
| for (size_t j = 0; j < 2; j++) |
References
- For new or touched code, avoid propagating legacy cytnx_XXXX typedefs as ordinary loop counters, sizes, flags, or local arithmetic types. Prefer standard C++ types unless the value is specifically a dtype-backed tensor/storage scalar. (link)
| for (cytnx_uint64 i = 0; i < 2; i++) | ||
| for (cytnx_uint64 j = 0; j < 3; j++) |
There was a problem hiding this comment.
According to the repository style guide, legacy cytnx_XXXX typedefs (such as cytnx_uint64) should not be propagated as ordinary loop counters in new or touched code. Standard C++ types like size_t or uint64_t should be preferred instead.
| for (cytnx_uint64 i = 0; i < 2; i++) | |
| for (cytnx_uint64 j = 0; j < 3; j++) | |
| for (size_t i = 0; i < 2; i++) | |
| for (size_t j = 0; j < 3; j++) |
References
- For new or touched code, avoid propagating legacy cytnx_XXXX typedefs as ordinary loop counters, sizes, flags, or local arithmetic types. Prefer standard C++ types unless the value is specifically a dtype-backed tensor/storage scalar. (link)
| for (cytnx_uint64 i = 0; i < 2; i++) | ||
| for (cytnx_uint64 j = 0; j < 3; j++) |
There was a problem hiding this comment.
According to the repository style guide, legacy cytnx_XXXX typedefs (such as cytnx_uint64) should not be propagated as ordinary loop counters in new or touched code. Standard C++ types like size_t or uint64_t should be preferred instead.
| for (cytnx_uint64 i = 0; i < 2; i++) | |
| for (cytnx_uint64 j = 0; j < 3; j++) | |
| for (size_t i = 0; i < 2; i++) | |
| for (size_t j = 0; j < 3; j++) |
References
- For new or touched code, avoid propagating legacy cytnx_XXXX typedefs as ordinary loop counters, sizes, flags, or local arithmetic types. Prefer standard C++ types unless the value is specifically a dtype-backed tensor/storage scalar. (link)
| for (cytnx_uint64 i = 0; i < 2; i++) | ||
| for (cytnx_uint64 j = 0; j < 2; j++) |
There was a problem hiding this comment.
According to the repository style guide, legacy cytnx_XXXX typedefs (such as cytnx_uint64) should not be propagated as ordinary loop counters in new or touched code. Standard C++ types like size_t or uint64_t should be preferred instead.
| for (cytnx_uint64 i = 0; i < 2; i++) | |
| for (cytnx_uint64 j = 0; j < 2; j++) | |
| for (size_t i = 0; i < 2; i++) | |
| for (size_t j = 0; j < 2; j++) |
References
- For new or touched code, avoid propagating legacy cytnx_XXXX typedefs as ordinary loop counters, sizes, flags, or local arithmetic types. Prefer standard C++ types unless the value is specifically a dtype-backed tensor/storage scalar. (link)
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1099 +/- ##
==========================================
+ Coverage 72.71% 72.77% +0.05%
==========================================
Files 226 226
Lines 28151 28184 +33
Branches 71 71
==========================================
+ Hits 20471 20510 +39
+ Misses 7659 7653 -6
Partials 21 21
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 1 file with indirect coverage changes Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
IvanaGyro
left a comment
There was a problem hiding this comment.
The comments from bots are worth to be resolved. Except that LGTM.
|
Outer.cpp casts both operands to I'm wondering why this PR doesn't replace the whole mechanism by a much simpler implementation. src/linalg/Outer.cpp:24 rejects either operand unless Tensor Outer(const Tensor &lhs, const Tensor &rhs) {
cytnx_error_msg(lhs.is_void(), "[ERROR] lhs must be initialized.%s", "\n");
cytnx_error_msg(rhs.is_void(), "[ERROR] rhs must be initialized.%s", "\n");
cytnx_error_msg(lhs.device() != rhs.device(),
"[ERROR] the two tensors must be on the same device.%s", "\n");
cytnx_error_msg(lhs.rank() != 1, "[ERROR] tensor #1 should have rank-1.%s", "\n");
cytnx_error_msg(rhs.rank() != 1, "[ERROR] tensor #2 should have rank-1.%s", "\n");
return Kron(lhs, rhs).reshape(std::vector<cytnx_uint64>{lhs.shape()[0], rhs.shape()[0]});
} |
ianmccul
left a comment
There was a problem hiding this comment.
see top-level comment above
|
Addressing @ianmccul's review comment: rather than register the missing Int16/Uint16/Bool rows, #1105 reimplements |
ianmccul
left a comment
There was a problem hiding this comment.
hmm, not sure how to 'unsubmit' approval but it doesn't matter, I assume this PR is no longer relevant
|
Closing as superseded by #1105. This PR registered the missing As a result, on current
Nothing here is left to salvage, so closing rather than rebasing. Thanks! |
Problem
linalg::Outersegfaults forInt16,Uint16, orBoolinputs on the CPU.Outer.cpppromotes both operands to a common dtype and dispatches throughOuter_ii[out_dtype][out_dtype]. The CPUOuter_iidispatch table inlinalg_internal_interface.cppwas never populated with the Int16, Uint16,or Bool source rows — every other source dtype has a full 11-entry row, but
these three had none. So when the promoted dtype is
Int16/Uint16/Bool,Outer_ii[out_dtype][out_dtype]is a null function pointer, and the call jumpsto address
0:Every sibling dispatch table in the same constructor —
MM_ii,Sum_ii,Diag_ii,Matmul_ii,Matvec_ii, and even the GPUcuOuter_ii— alreadyregisters these dtypes, so the omission is an oversight, not intent. (The GPU
path works; this is CPU-only.)
Reproduces on
master(86cb96c). Minimal repro:Fix
Register the 33 missing rows (
Int16,Uint16,Boolsources × 11 targetdtypes). The
Outer_internal_{i16,u16,b}t*implementations already exist andare declared — this is registration only, no kernel or algorithm change.
Testing
tests/linalg_test/Outer_test.cpp(OuterDtypeCoverage:Int16,Uint16,Bool,Int16xBool) checks independently hand-computed values(dtype and value, with negative and boolean cases).
SEGV above at the
linalg::Outercall site; post-fix all four pass.DtypePromotion.OuterComplexfloatDoubleandLinalgKronTesttestsstill pass.
clang-format(v14) clean.