@@ -644,17 +644,20 @@ def replace_arange_start_step(
644644 else coreai .constant (1 , dtype = start .type .element_type )
645645 )
646646
647- # Squeeze rank-1 inputs to 0D scalars for coreai.range_.
647+ # coreai.range_ requires scalar (rank-0) operands that share an element
648+ # type. aten.arange promotes mixed-type scalars internally; we replicate
649+ # that here by squeezing each operand to rank-0 and casting to the FX
650+ # node's output dtype before the op.
651+ target_type = get_output_element_type_from_node (node )
652+
648653 def to_scalar (v : Value ) -> Value :
649654 if v .type .rank > 0 :
650- return coreai .shrink_dims (v , list (range (v .type .rank )))
655+ v = coreai .shrink_dims (v , list (range (v .type .rank )))
656+ if v .type .element_type != target_type :
657+ v = coreai .cast (v , target_type )
651658 return v
652659
653- result = coreai .range_ (to_scalar (start ), to_scalar (end ), to_scalar (step ))
654- target_type = get_output_element_type_from_node (node )
655- if result .type .element_type != target_type :
656- result = coreai .cast (result , target_type )
657- return result
660+ return coreai .range_ (to_scalar (start ), to_scalar (end ), to_scalar (step ))
658661
659662
660663def replace_batch_norm (
@@ -814,6 +817,7 @@ def replace_binary_ops(
814817 "pow.Scalar" : coreai .broadcasting_pow ,
815818 "pow.Tensor_Tensor" : coreai .broadcasting_pow ,
816819 "pow.Tensor_Scalar" : coreai .broadcasting_pow ,
820+ "pow" : coreai .broadcasting_pow ,
817821 "sub.Tensor" : coreai .broadcasting_sub ,
818822 "sub.Scalar" : coreai .broadcasting_sub ,
819823 "sub" : coreai .broadcasting_sub ,
@@ -944,6 +948,55 @@ def replace_cat(values_map: dict[str, Value], node: fx.Node, loc: Location) -> V
944948
945949 rank = inputs [0 ].type .rank
946950 dim = dim + rank if dim < 0 else dim
951+
952+ # coreai.concat requires all non-concat dims to be provably equal across
953+ # inputs. Under dynamic shapes, one branch can carry a dynamic non-concat
954+ # axis while a sibling has a static size for the same axis — the dynamic
955+ # side must in fact equal that static size, but the type system doesn't
956+ # know it. Reshape such inputs to the known static size before the concat.
957+ # Multiple distinct static sizes on one axis is a real mismatch and is
958+ # left for the dialect verifier to reject.
959+ dyn = ShapedType .get_dynamic_size ()
960+
961+ def known_static (axis : int ) -> int | None :
962+ if axis == dim :
963+ return None
964+ sizes = {inp .type .shape [axis ] for inp in inputs if inp .type .shape [axis ] != dyn }
965+ return next (iter (sizes )) if len (sizes ) == 1 else None
966+
967+ statics = [known_static (a ) for a in range (rank )]
968+ promoted : list [Value ] = []
969+ for inp in inputs :
970+ new_shape = [
971+ statics [a ]
972+ if statics [a ] is not None and inp .type .shape [a ] == dyn
973+ else inp .type .shape [a ]
974+ for a in range (rank )
975+ ]
976+ if new_shape != list (inp .type .shape ):
977+ if all (s != dyn for s in new_shape ):
978+ # All axes static post-promotion: list-form reshape packs
979+ # the shape into an int32 constant tensor.
980+ inp = coreai .reshape (inp , new_shape )
981+ else :
982+ # Mixed static / dynamic post-promotion: build the shape
983+ # vector at runtime by mixing the input's actual sizes
984+ # (via coreai.get_shape) for the still-dynamic axes with
985+ # constants for the promoted axes.
986+ runtime_shape = coreai .cast (coreai .get_shape (inp ), dtype = np .int32 )
987+ parts = [
988+ coreai .constant ([s ], dtype = np .int32 )
989+ if s != dyn
990+ else coreai .slice_ (runtime_shape , [a ], [a + 1 ], [1 ])
991+ for a , s in enumerate (new_shape )
992+ ]
993+ result_type = RankedTensorType .get (new_shape , inp .type .element_type )
994+ inp = coreai .ReshapeOp (
995+ inp , coreai .concat (0 , parts ), results = [result_type ]
996+ ).result
997+ promoted .append (inp )
998+ inputs = promoted
999+
9471000 return coreai .concat (dim , inputs )
9481001
9491002
@@ -2232,11 +2285,29 @@ def replace_remainder(
22322285
22332286def replace_repeat (values_map : dict [str , Value ], node : fx .Node , loc : Location ) -> Value :
22342287 x = _get_operand (values_map , node , 0 )
2235- repeats = np . array (node .args [1 ], dtype = np . uint32 )
2236- extra_dims = len (repeats ) - x .type .rank
2288+ repeat_args = list (node .args [1 ])
2289+ extra_dims = len (repeat_args ) - x .type .rank
22372290 if extra_dims > 0 :
22382291 x = coreai .expand_dims (x , list (range (extra_dims )))
2239- return coreai .tile (x , repeats )
2292+
2293+ if all (isinstance (r , int ) for r in repeat_args ):
2294+ return coreai .tile (x , np .array (repeat_args , dtype = np .uint32 ))
2295+
2296+ # At least one repeat is a SymInt fx.Node — build a rank-1 uint32 dim
2297+ # vector at runtime, with per-axis constants for plain ints and the
2298+ # resolved Value (cast to uint32, lifted to rank-1 if scalar) for
2299+ # SymInts. coreai.tile accepts a runtime Value for its dims.
2300+ chunks : list [Value ] = []
2301+ for r in repeat_args :
2302+ if isinstance (r , int ):
2303+ chunks .append (coreai .constant ([r ], dtype = np .uint32 ))
2304+ else :
2305+ assert isinstance (r , fx .Node )
2306+ v = coreai .cast (values_map [r .name ], dtype = np .uint32 )
2307+ if v .type .rank == 0 :
2308+ v = coreai .reshape (v , [1 ])
2309+ chunks .append (v )
2310+ return coreai .tile (x , coreai .concat (0 , chunks ))
22402311
22412312
22422313def replace_round_decimals (
@@ -2635,6 +2706,7 @@ def replace_unary_ops(
26352706 "log.default" : coreai .log ,
26362707 "relu.default" : coreai .relu ,
26372708 "round.default" : coreai .round_ ,
2709+ "round" : coreai .round_ ,
26382710 "rsqrt.default" : coreai .rsqrt ,
26392711 "sigmoid.default" : coreai .sigmoid ,
26402712 "silu.default" : coreai .silu ,
@@ -3475,13 +3547,15 @@ def sdpa_maskless(q: Value, k: Value, v: Value) -> Value:
34753547 "pow.Scalar" : replace_binary_ops ,
34763548 "pow.Tensor_Scalar" : replace_binary_ops ,
34773549 "pow.Tensor_Tensor" : replace_binary_ops ,
3550+ "pow" : replace_binary_ops ,
34783551 "prod.default" : replace_prod_default ,
34793552 "prod.dim_int" : replace_prod_dim_int ,
34803553 "reciprocal.default" : replace_reciprocal ,
34813554 "relu.default" : replace_unary_ops ,
34823555 "remainder.Tensor" : replace_remainder ,
34833556 "round.default" : replace_unary_ops ,
34843557 "round.decimals" : replace_round_decimals ,
3558+ "round" : replace_unary_ops ,
34853559 "repeat.default" : replace_repeat ,
34863560 "rsqrt.default" : replace_unary_ops ,
34873561 "scaled_dot_product_attention.default" : replace_sdpa ,
0 commit comments