You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PythonTensorAnalysisEngine.getSetShapeCallsSyntactic (introduced in ponder-lab/ML#333 as a near-term fix for #509) recognizes x.set_shape(s) callsites by scanning every CGNode's IR for the PythonPropertyRead("set_shape") + invoke pattern. This works but lives in Java, separate from the existing TF API modeling layer (tensorflow.xml).
The natural home for the recognition is tensorflow.xml, in line with how Dataset already models its instance methods.
Investigation Findings (2026-05-26)
A draft PR (ponder-lab/ML#338, now closed) explored declaring <method name="set_shape"> directly on each Tensor/SparseTensor <class> block. Two structural problems surfaced:
The trampoline indirection. Declaring <method> on a <class> triggers WALA's PythonInstanceMethodTrampolineTargetSelector. User code's tensor.set_shape(shape) invokes a TRAMPOLINE (L$<class>/set_shape.trampoline<N>()LRoot;, with the $ prefix added at PythonInstanceMethodTrampolineTargetSelector.java:239 and the trampoline<numTotalParameters> selector per PythonMethodTrampolineTargetSelector.java:50), which then dispatches to the underlying do(). The legacy getShapeSourceCalls machinery either walks the wrong direction (targeting do() finds the trampoline, not user code) or, if pointed at the trampoline, can't recover the receiver because the trampoline's auto-generated body doesn't preserve the def-to-receiver aliasing that the legacy callable-as-attribute pattern relied on.
Receiver vs. def semantics.set_shape's purpose is to MUTATE the receiver's tensor classification. getShapeSourceCalls pins call.getDef() (the call's return value). The LEGACY mechanism worked because <class name="set_shape">.do() had <return value="self"/> — in the callable-as-attribute context, self referred to the callable instance, aliasing through PA to the receiver. With the trampoline indirection, that aliasing path is broken.
The Dataset class doesn't declare its instance methods (batch, map, shuffle, etc.) as <method> blocks inside <class name="Dataset">. Instead, each is a STANDALONE callable class (<class name="batch">, <class name="shuffle">, etc.), and Dataset instances have these callables attached via <putfield> at every Dataset-producing endpoint (from_tensor_slices.do, shuffle.do, etc.).
This bypasses the trampoline entirely: PropertyRead-based attribute access on a putfield-attached callable invokes the callable's do() directly, no trampoline.
The legacy set_shape already uses this pattern on FixedLenFeature (tensorflow.xml:2129: <putfield field="set_shape" ref="x" value="set_shape_callable"/>). Extending the pattern to every Tensor allocation site would resolve #550 without touching the trampoline mechanism.
Proposed Migration
Keep the existing <class name="set_shape"> callable at tensorflow.xml:1660.
Add<putfield ref="x" field="set_shape" value="set_shape_callable"/> to every <new def="x" class="Ltensorflow/.../Tensor"/> (and SparseTensor) site in tensorflow.xml. Estimated count: ~130 Tensor allocations.
UpdatePythonTensorAnalysisEngine to remove getSetShapeCallsSyntactic and restore the legacy getShapeSourceCalls(set_shape, ...) call now that every Tensor allocation has the attribute attached.
Motivation
PythonTensorAnalysisEngine.getSetShapeCallsSyntactic(introduced in ponder-lab/ML#333 as a near-term fix for #509) recognizesx.set_shape(s)callsites by scanning every CGNode's IR for thePythonPropertyRead("set_shape")+ invoke pattern. This works but lives in Java, separate from the existing TF API modeling layer (tensorflow.xml).The natural home for the recognition is
tensorflow.xml, in line with howDatasetalready models its instance methods.Investigation Findings (2026-05-26)
A draft PR (ponder-lab/ML#338, now closed) explored declaring
<method name="set_shape">directly on each Tensor/SparseTensor<class>block. Two structural problems surfaced:The trampoline indirection. Declaring
<method>on a<class>triggers WALA'sPythonInstanceMethodTrampolineTargetSelector. User code'stensor.set_shape(shape)invokes a TRAMPOLINE (L$<class>/set_shape.trampoline<N>()LRoot;, with the$prefix added atPythonInstanceMethodTrampolineTargetSelector.java:239and thetrampoline<numTotalParameters>selector perPythonMethodTrampolineTargetSelector.java:50), which then dispatches to the underlyingdo(). The legacygetShapeSourceCallsmachinery either walks the wrong direction (targetingdo()finds the trampoline, not user code) or, if pointed at the trampoline, can't recover the receiver because the trampoline's auto-generated body doesn't preserve the def-to-receiver aliasing that the legacy callable-as-attribute pattern relied on.Receiver vs. def semantics.
set_shape's purpose is to MUTATE the receiver's tensor classification.getShapeSourceCallspinscall.getDef()(the call's return value). The LEGACY mechanism worked because<class name="set_shape">.do()had<return value="self"/>— in the callable-as-attribute context,selfreferred to the callable instance, aliasing through PA to the receiver. With the trampoline indirection, that aliasing path is broken.Architecturally-Consistent Alternative: Dataset Pattern
The
Datasetclass doesn't declare its instance methods (batch,map,shuffle, etc.) as<method>blocks inside<class name="Dataset">. Instead, each is a STANDALONE callable class (<class name="batch">,<class name="shuffle">, etc.), and Dataset instances have these callables attached via<putfield>at every Dataset-producing endpoint (from_tensor_slices.do,shuffle.do, etc.).This bypasses the trampoline entirely: PropertyRead-based attribute access on a putfield-attached callable invokes the callable's
do()directly, no trampoline.The legacy
set_shapealready uses this pattern onFixedLenFeature(tensorflow.xml:2129:<putfield field="set_shape" ref="x" value="set_shape_callable"/>). Extending the pattern to every Tensor allocation site would resolve #550 without touching the trampoline mechanism.Proposed Migration
<class name="set_shape">callable attensorflow.xml:1660.<putfield ref="x" field="set_shape" value="set_shape_callable"/>to every<new def="x" class="Ltensorflow/.../Tensor"/>(and SparseTensor) site intensorflow.xml. Estimated count: ~130 Tensor allocations.PythonTensorAnalysisEngineto removegetSetShapeCallsSyntacticand restore the legacygetShapeSourceCalls(set_shape, ...)call now that every Tensor allocation has the attribute attached.castpass_throughalias (already done by Decoupleset_shapefrom cast pass_through alias chain ponder-lab/ML#333).Cost / Trade-Off
<putfield>sites is significant. Same per-allocation duplication pattern Audit dataset-method field-duplication intensorflow.xml— comment cites 1-CFA but engine uses 2-CFA #549 audited for Dataset/Model field blocks — proven structurally load-bearing, accepted as the current XML convention.com.ibm.wala.classLoader.IClass.getSuperclass()incorrectly returnsLobject#118, Inheritance not supported #107) lands, the 130 putfields collapse to a single declaration on a base Tensor class. This issue becomes the consumer of those frontend fixes.Related
tensorflow.xml— comment cites 1-CFA but engine uses 2-CFA #549 — prior audit accepting the per-allocation duplication pattern.com.ibm.wala.classLoader.IClass.getSuperclass()incorrectly returnsLobject#118, Inheritance not supported #107 — class-hierarchy resolution; the collapse path.TensorType.shapeArgdeclaresthrows IOExceptionwithout doing I/O #555 —TensorType.shapeArg's spuriousthrows IOExceptiondeclaration (surfaced during PR Prevent future uses of print statements where logging should be used instead #333 review; orthogonal cleanup).