fix(qdq): skip DQ forward propagation when DQ input is constant (#28521)
## Summary
- Guard `QDQPropagationTransformer::PropagateDQForward` against
propagating a DQ whose data input is a constant (graph initializer or
`Constant` op output).
- Prevents a stale `QuantizeLinear` insertion that the S8-to-U8 weight
transformer fails to update, which silently clamps int8 negatives to
zero under `ORT_ENABLE_ALL`.
- Adds a regression test in `qdq_transformer_test.cc` covering both
constant-input shapes.
## Motivation
Fixes #28491.
Reported scenario: a model containing `Constant(int8) ->
DequantizeLinear -> Reshape` produces correct outputs under
`ORT_DISABLE_ALL` but wrong outputs (negatives clamped to 0) under
`ORT_ENABLE_ALL`. Root cause: `PropagateDQForward` inserts a `Q -> DQ`
pair after the Reshape, then `QDQS8ToU8Transformer` (or the avx2 weight
transformer) flips the upstream DQ from int8 to uint8 without touching
the freshly inserted `Q`. The orphaned uint8 `Q` then clamps the int8
weight's negative values to 0.
Propagating a DQ whose data is a constant weight has no benefit anyway:
the constant is folded, so there is no runtime tensor that downstream
nodes need re-quantized.
## Changes
- `onnxruntime/core/optimizer/qdq_transformer/qdq_propagation.cc`:
inside `PropagateDQForward`'s per-DQ loop, after existing skip checks
and before any graph mutation, `continue` if
`dq_node.InputDefs()[QDQ::InputIndex::INPUT_ID]` is a graph initializer
(`graph_utils::NodeArgIsConstant`) or the output of a `Constant` op node
(`graph.GetProducerNode(...)->OpType() == "Constant"`). Both checks are
required because `NodeArgIsConstant` only handles the initializer case.
- `onnxruntime/test/optimizer/qdq_transformer_test.cc`: new test
`QDQPropagation_DQForward_ConstantInput_NoPropagation` with two cases —
DQ fed by an initializer, and DQ fed by an explicit `Constant` op node —
asserting no `QuantizeLinear` is inserted after the downstream Reshape.
`PropagateQBackward` is structurally not affected (its data input is a
live activation, not a constant), so it does not need a symmetric guard.
## Test Plan
- New
`QDQTransformerTests.QDQPropagation_DQForward_ConstantInput_NoPropagation`
(gtest) covers both code paths and would fail on `main`.
- Existing `QDQPropagation_*` tests use `MakeInput` (graph inputs, not
initializers) for their DQ data tensors, so the new guard does not
regress them.
- The reproducer from #28491 should now produce identical results
between `ORT_DISABLE_ALL` and `ORT_ENABLE_ALL`.
- CI will exercise `--gtest_filter=QDQTransformerTests.QDQPropagation*`
under `onnxruntime_test_all`.
Fixes #28491