Aharrypotter opened a new pull request, #19897:
URL: https://github.com/apache/tvm/pull/19897

   ## Summary
   
   This PR fixes Relax ONNX frontend handling for integer `Div` when the divisor
   is dynamic and may contain zero.
   
   Issue #19541 reports a `SIGFPE` crash when an ONNX `Div` model has integer
   inputs and the divisor is supplied as a graph input containing zero. PR 
#19566
   already added an import-time error for constant integer divisors containing
   zero, but dynamic divisors still imported to a bare `relax.divide`. After
   legalization, LLVM could still see integer `sdiv` / `udiv` with a zero 
divisor.
   
   ONNX integer division by zero is undefined. This PR keeps exact integer
   division for every non-zero divisor lane, and returns zero for lanes where 
the
   runtime divisor is zero. The result is deterministic and avoids exposing
   integer division by zero to LLVM.
   
   ## Design
   
   ### Guarded Dynamic Integer Div
   
   For integer operands with a non-constant RHS, `Div._impl_v7` now emits a
   guarded Relax subgraph:
   
   ```python
   rhs_nonzero = relax.op.not_equal(rhs, relax.op.zeros_like(rhs))
   safe_rhs = relax.op.where(rhs_nonzero, rhs, relax.op.ones_like(rhs))
   quotient = relax.op.divide(lhs, safe_rhs)
   result = relax.op.where(rhs_nonzero, quotient, relax.op.zeros_like(quotient))
   ```
   
   The divisor passed to `relax.divide` is therefore never zero. The final
   `where` restores the deterministic zero result for the originally-zero 
divisor
   lanes.
   
   The importer normalizes the intermediate expressions whose inferred type is
   needed by later `where` / `zeros_like` construction.
   
   ### Constant and Non-Integer Paths
   
   The existing non-integer behavior is unchanged: non-integer `Div` still uses
   the normal binary frontend implementation.
   
   For integer `Div` with a constant RHS:
   
   - constants containing zero still raise `ValueError` at import time
   - constants without zero still use the normal binary frontend implementation
   
   This keeps the PR #19566 behavior for constants while only adding runtime
   guarding where import-time zero detection is impossible.
   
   ### No Float Fallback
   
   The implementation intentionally avoids casting integer tensors through float
   types. The guarded Relax graph preserves integer division semantics on all
   non-zero lanes and avoids precision loss or float-to-int `inf` behavior.
   
   ### Frontend-Local Runtime Guard
   
   This is intentionally a frontend-local runtime guard rather than a global
   Relax, TIR, or LLVM integer-division policy. The check is not an import-time
   Python decision for dynamic inputs; it is expressed as Relax IR and executes
   with the model at runtime.
   
   Handling this in a generic Relax/TIR pass would affect all producers of 
integer
   division and would need a project-wide policy for undefined integer
   division-by-zero behavior: whether to trap, raise a controlled runtime error,
   or return a deterministic value. This PR only addresses the ONNX frontend bug
   reported in #19541 and keeps the deterministic zero result local to imported
   ONNX integer `Div`.
   
   ## Updated Converter Behavior
   
   | Case | Previous behavior | New behavior |
   |---|---|---|
   | integer `Div` with dynamic integer RHS | imports to bare `relax.divide`; 
LLVM can see integer division by zero at runtime | imports to a guarded Relax 
subgraph; non-zero divisor lanes keep exact integer division, zero-divisor 
lanes return zero |
   | integer `Div` with constant integer RHS containing zero | import-time 
`ValueError` from PR #19566 | unchanged |
   | integer `Div` with constant integer RHS without zero | normal binary 
lowering | unchanged |
   | non-integer `Div` | normal binary lowering | unchanged |
   
   ## Safety Checks
   
   - Constant integer divisors containing zero raise
     `ValueError("ONNX Div with integer inputs encountered divisor value 0.")`.
   - Dynamic integer divisors are replaced with `ones_like(rhs)` only for the
     division operand when the divisor lane is zero.
   - Zero-divisor lanes are overwritten with `zeros_like(quotient)` after the
     division.
   - The guarded graph is asserted with structural Relax IR tests, so the 
frontend
     cannot silently regress to a bare `relax.divide` or wire the guard operands
     incorrectly.
   
   ## Out of Scope / Non-Goals
   
   - This PR does not define global Relax, TIR, or LLVM integer division-by-zero
     semantics.
   - This PR does not use a float-cast fallback for integer division.
   - This PR does not change ONNX `Div` floating-point behavior.
   - This PR does not change other frontends or other ONNX operators.
   
   ## Tests
   
   The tests primarily lock the imported Relax graph shape, with one minimal VM
   runtime smoke test for the original execution-time crash path.
   
   | Test | Coverage |
   |---|---|
   | `test_div_integer_constant_zero_divisor_raises_valueerror` | keeps PR 
#19566 constant-zero import-time error |
   | `test_div_integer_dynamic_zero_divisor_runtime` | runs one dynamic INT32 
ONNX `Div` model through `DecomposeOpsForInference`, `LegalizeOps`, LLVM 
compile, and VM execution; asserts dtype, shape, exact non-zero divisor 
results, and deterministic zero results for zero-divisor lanes |
   | `test_div_integer_dynamic_zero_divisor_ir_guard` | uses 
`tvm.ir.assert_structural_equal` to lock the exact guarded Relax subgraph 
before legalization |
   | `test_div_integer_dynamic_zero_divisor_broadcast_ir_guard` | uses 
`tvm.ir.assert_structural_equal` to lock the guarded Relax subgraph when RHS 
broadcasting is required |
   
   Local validation:
   
   ```bash
   python -m pytest \
     
tests/python/relax/test_frontend_onnx.py::test_div_integer_dynamic_zero_divisor_runtime
 \
     -xvs
   
   python -m pytest tests/python/relax/test_frontend_onnx.py -k "div" -xvs
   
   python -m ruff check \
     python/tvm/relax/frontend/onnx/onnx_frontend.py \
     tests/python/relax/test_frontend_onnx.py
   ```
   
   Result:
   
   ```text
   test_div_integer_dynamic_zero_divisor_runtime: passed
   test_frontend_onnx.py -k "div": 7 passed
   ruff check: All checks passed
   ```
   
   ## References
   
   - Closes [#19541](https://github.com/apache/tvm/issues/19541): ONNX integer
     `Div` with a dynamic zero divisor can crash at runtime.
   - Follows up PR #19566: previous constant-zero divisor import-time error.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to