@@ -995,36 +995,63 @@ def forward(self, x: Tensor) -> Tensor:
995995 dynamic_shapes = {"x" : {0 : torch .export .Dim ("batch" , min = 1 )}},
996996 )
997997 # ``end`` is a SymInt (rank-1 si32) sliced from x.shape[0];
998- # ``start``/``step`` are scalar (rank-0) si32 constants. The
999- # lowering casts each operand to the FX node's output dtype (f32)
1000- # before ``coreai.range_`` so the op sees uniform-typed scalars;
1001- # the optimizer then constant-folds the casts on start/step into
1002- # f32 constants directly.
998+ # ``start``/``step`` are scalar (rank-0) si32 constants.
999+ # The lowering keeps all operands as si32 so that coreai.range_
1000+ # can infer a static shape from constant int operands, then casts
1001+ # the result to the requested output dtype. (Casting operands to
1002+ # float *before* range_ causes it to return tensor<?> even when the
1003+ # bounds are compile-time constants.)
10031004 filecheck_pattern (
10041005 ir ,
10051006 check_file = """
10061007 // CHECK-LABEL: coreai.graph @main
10071008 // CHECK-SAME: %[[ARG0:.*]]: tensor<?x3xf32>
10081009 // CHECK-SAME: -> (tensor<?xf32>
10091010 //
1010- // start and step land as f32 constants directly (the si32
1011- // constant + cast-to-f32 pair gets folded by the optimizer):
1012- // CHECK-DAG: %[[STEP:.+]] = coreai.constant dense<1.000000e+00> : tensor<f32>
1013- // CHECK-DAG: %[[START:.+]] = coreai.constant dense<0.000000e+00> : tensor<f32>
1011+ // start and step remain as si32 constants; no pre-range cast:
1012+ // CHECK-DAG: %[[STEP:.+]] = coreai.constant dense<{{.*}}> : tensor<si32>
1013+ // CHECK-DAG: %[[START:.+]] = coreai.constant dense<{{.*}}> : tensor<si32>
10141014 //
1015- // end: get_shape -> slice -> cast(ui32->si32) -> reshape(rank-1 to rank-0) -> cast(si32->f32):
1015+ // end: get_shape -> slice -> cast(ui32->si32) -> reshape(rank-1 to rank-0);
1016+ // stays si32, no cast to f32 before range_:
10161017 // CHECK: %[[END_RANK1:.+]] = coreai.cast {{.*}} : tensor<1xui32> to tensor<1xsi32>
10171018 // CHECK: %[[END_RANK0:.+]] = coreai.reshape %[[END_RANK1]], {{.*}} : (tensor<1xsi32>, tensor<0xui32>) -> tensor<si32>
1018- // CHECK: %[[END_F32:.+]] = coreai.cast %[[END_RANK0]] : tensor<si32> to tensor<f32>
10191019 //
1020- // range called with all-f32 scalars; result is f32 directly,
1021- // no post-range cast on the result:
1022- // CHECK: %[[OUT:.+]] = coreai.range %[[START]], %[[END_F32]], %[[STEP]] : (tensor<f32>, tensor<f32>, tensor<f32>) -> tensor<?xf32>
1023- // CHECK-NOT: coreai.cast %[[OUT]]
1020+ // range_ called with all-si32 scalars; result is si32 with dynamic shape:
1021+ // CHECK: %[[RANGE_OUT:.+]] = coreai.range %[[START]], %[[END_RANK0]], %[[STEP]] : (tensor<si32>, tensor<si32>, tensor<si32>) -> tensor<?xsi32>
1022+ //
1023+ // post-range cast to the requested f32 output dtype:
1024+ // CHECK: %[[OUT:.+]] = coreai.cast %[[RANGE_OUT]] : tensor<?xsi32> to tensor<?xf32>
10241025 // CHECK: coreai.output %[[OUT]] : tensor<?xf32>
10251026 """ ,
10261027 )
10271028
1029+ def test_static_float_dtype_preserves_shape (self ) -> None :
1030+ """Regression: arange with a float dtype must keep a static output shape.
1031+
1032+ Casting operands to float *before* coreai.range_ causes the op to
1033+ return tensor<?xf32> even when all bounds are compile-time constants.
1034+ The fix is to run range_ on int operands and cast the result.
1035+ """
1036+
1037+ class ArangeFloat (nn .Module ):
1038+ def forward (self , x : Tensor ) -> Tensor :
1039+ # Constant int bounds, float output dtype — the problematic case.
1040+ return torch .arange (0 , 8 , 2 , dtype = torch .float32 , device = x .device )
1041+
1042+ ir = get_ir (ArangeFloat ().eval (), x = torch .rand (4 ))
1043+ # The output shape must be static (4 elements: 0,2,4,6).
1044+ filecheck_pattern (
1045+ ir ,
1046+ check_file = """
1047+ // CHECK-LABEL: module {
1048+ // CHECK-NEXT: coreai.graph @main(%[[ARG0:.*]]: tensor<4xf32>
1049+ // CHECK-SAME: -> (tensor<4xf32>
1050+ // The optimizer constant-folds the whole thing to a dense literal:
1051+ // CHECK: coreai.constant dense<{{.*}}> : tensor<4xf32>
1052+ """ ,
1053+ )
1054+
10281055
10291056class TestArgmaxIR :
10301057 def test_static (self ) -> None :
0 commit comments