Skip to content

fix: guard the 3D convolutions the pad-folding pass hides from the conv guard - #4644

Open
tp5uiuc wants to merge 1 commit into
tp5uiuc/trtrtx-turing-foundationfrom
tp5uiuc/trtrtx-turing-conv3d-padfold
Open

fix: guard the 3D convolutions the pad-folding pass hides from the conv guard#4644
tp5uiuc wants to merge 1 commit into
tp5uiuc/trtrtx-turing-foundationfrom
tp5uiuc/trtrtx-turing-conv3d-padfold

Conversation

@tp5uiuc

@tp5uiuc tp5uiuc commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

What — Registers a capability validator on the tensorrt::conv_asym_pad custom op so a padded 3D
convolution falls back to PyTorch on Turing.

Why — The fuse_pad_into_convolution lowering pass rewrites constant_pad_nd -> convolution
into tensorrt::conv_asym_pad, erasing the aten.convolution node the 3D-conv guard keys on.
That converter was registered with no capability_validator, so a padded conv3d walked straight past
the guard and hit a null execution context (TRTEngine.cpp:959). It fires for any non-transposed,
zero-fill, non-negative pad, so symmetric cases fail too. This is a production path, not just tests.

How — Share the convolution capability predicate between the aten converter and the fused
custom op, reading rank from the fused node, so the guard survives the rewrite instead of being
erased with the node it keyed on.

Testing — Confirmation sweep, both arms: 8 lowering/ failures closed, 0 Turing-specific
remaining in that module. No status change on the L40S.

Cost / Gotchas — Accounts for 8 of the 105 tests the guards switch off on Turing.

🤖 Generated with Claude Code

Comment thread py/torch_tensorrt/dynamo/conversion/custom_ops_converters.py Outdated
Comment thread py/torch_tensorrt/dynamo/conversion/aten_ops_converters.py Outdated
Comment thread py/torch_tensorrt/dynamo/conversion/aten_ops_converters.py Outdated
Comment thread py/torch_tensorrt/dynamo/conversion/aten_ops_converters.py Outdated
@tp5uiuc
tp5uiuc force-pushed the tp5uiuc/trtrtx-turing-conv3d-padfold branch 2 times, most recently from 749d1b9 to 3c19297 Compare August 30, 2026 00:52
@github-actions
github-actions Bot requested a review from narendasan August 30, 2026 04:12
…nv guard

convolution_capability_validator rejects forward 3D convolutions when Turing
(SM 7.5) is a build target, because TensorRT-RTX builds an engine for them whose
createExecutionContext() then returns nullptr. It is registered on
aten.convolution.default only.

The fuse_pad_into_convolution lowering pass rewrites constant_pad_nd -> convolution
into tensorrt::conv_asym_pad, and that op's converter had no capability_validator at
all. By the time partitioning runs there is no aten.convolution node left to
validate, so a padded 3D convolution reaches TensorRT-RTX regardless of the guard
and surfaces at runtime as

  Expected exec_ctx_.get() != nullptr to be true but got false
  Unable to (re)create TensorRT execution context

This is a production gap, not just a test gap. The pass runs in the normal compile
path, and it fires for any non-transposed, zero-fill, non-negative
constant_pad_nd -> conv pair -- asymmetry is not required -- so any real model with a
padded conv3d hits it on Turing.

  * aten_ops_converters.py: extract turing_rejects_forward_convolution() so the
    "3D forward conv is unsupported on this target" rule lives in one place.
    convolution_capability_validator delegates to it; its behaviour is unchanged,
    including failing open when meta["val"] is absent.

  * custom_ops_converters.py: add conv_asym_pad_capability_validator and register
    it on the fused op. It reads the spatial rank off the argument list rather than
    node.meta -- the fused op's args are (source, weight, bias, stride, pre_padding,
    post_padding, dilation, groups), so len(stride) is the rank -- which makes the
    guard hold whether or not the caller ran the dynamo tracer. The pass never fuses
    a transposed convolution, so there is no deconvolution case to handle here. Only
    rank 3 is rejected; 2D keeps running on TensorRT.

  * test_fuse_pad_into_convolution.py: skip test_padded_conv3d on Turing.
    DispatchTestCase has no PyTorch-fallback path -- run_test hands the graph
    straight to TRTInterpreter, skipping the partitioner -- so a rejected node
    raises UnsupportedOperatorException instead of falling back. Guarding without
    skipping would only swap one failure for another.

  * test_turing_capability_guards.py: add a padded-conv3d fallback case and a
    padded-conv2d positive control. Both key on target_compute_capabilities=[(7, 5)]
    rather than the live device, so they exercise the guard on any GPU and therefore
    in CI. Reverting just the registration makes the conv3d case fail, so it covers
    the gap rather than merely restating it.

Deliberately not fixed in the lowering pass. Declining to fuse when the underlying
convolution would be rejected keeps one rule in one place and would pick up any
future guard on aten.convolution for free, and the pass already receives the
settings it would need. But TestFusePadIntoConvolutionPass builds its graphs with a
default CompilationSettings(), and test_graph_contains_fused_op_after_lowering calls
post_lowering with one too. Default settings mean target_compute_capabilities=None,
which resolves against the current device -- so on a Turing GPU the pass would
decline to fuse and the five tests that assert the fusion *does* happen would fail.
Each would then need a pinned non-Turing target or a skip.

Testing (T4 / SM 7.5 and L40S / SM 8.9, driver 595.58.03, identical pinned stacks):

  * All 8 test_padded_conv3d cases go fail -> skip on Turing and still run and pass
    on the L40S control, confirming the guard is inert off Turing.

  * Full lowering/ sweep on both arms, all 259 tests accounted for on each. On
    Turing exactly 8 tests change status, all of them fail -> skip and all of them
    test_padded_conv3d: 9 -> 1 failed, 4 -> 12 skipped, and 242 passing either way.
    (The other 4 pre-existing skips, and 4 fp16 SDPA tests that abort the worker,
    are unrelated and unchanged.) On the non-Turing control every one of the 259
    keeps its previous status and the skip count stays at 0.

  * The 11 other tests in that file stay green on both arms -- in particular
    test_graph_contains_fused_op_after_lowering and
    test_fuses_causal_3d_pad, both of which build 3D graphs and assert the fusion
    still happens on the T4.

  * test_turing_capability_guards.py passes on both arms: T4 17 -> 19 passed
    / 1 skipped, L40S 12 -> 14 passed / 6 skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tp5uiuc
tp5uiuc force-pushed the tp5uiuc/trtrtx-turing-conv3d-padfold branch from 3c19297 to 2e84e29 Compare August 30, 2026 18:37
@tp5uiuc

tp5uiuc commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator Author

CI summary

failing check cause
standard / test (dynamo-models, standard, l2) test_cross_runtime_serde::test_save_cpp_load_python and ::test_save_python_load_cpp — Windows only
executorch-runtime-test OSError: libcurand.so.10
gate ×2 aggregators

Suite ran to completion: 2 failed, 150 passed, 54 skipped, 4 xpassed.

Why these are not from this PR: the same test_cross_runtime_serde pair fails identically on #4643, #4644, #4645, #4646, #4647 and #4648, whose change sets are disjoint — e.g. #4648 changes _exporter.py (the save path) and #4647 changes neither that nor anything else in common, yet both fail the same two tests with byte-identical error text. executorch-runtime-test reproduces on main nightlies and on unrelated PRs (#4638, #4642). The gate entries are aggregators of the above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci: full cla signed component: api [Python] Issues re: Python API component: conversion Issues re: Conversion stage component: core Issues re: The core compiler component: dynamo Issues relating to the `torch.compile` or `torch._dynamo.export` paths component: tests Issues re: Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant