From 5c4a43c9f82b7e9d633db511de5cd01ab1b3321a Mon Sep 17 00:00:00 2001 From: n-io Date: Wed, 18 Dec 2024 17:39:27 +0100 Subject: [PATCH 1/9] transforms: (memref-to-dsd) Support 1d subview of nd memref --- tests/filecheck/transforms/memref-to-dsd.mlir | 32 +++++++------------ xdsl/dialects/csl/csl.py | 4 +-- xdsl/transforms/memref_to_dsd.py | 32 ++++++++++++++++++- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/tests/filecheck/transforms/memref-to-dsd.mlir b/tests/filecheck/transforms/memref-to-dsd.mlir index 410066c436..3af8c9018c 100644 --- a/tests/filecheck/transforms/memref-to-dsd.mlir +++ b/tests/filecheck/transforms/memref-to-dsd.mlir @@ -98,27 +98,17 @@ builtin.module { // CHECK-NEXT: %26 = "test.op"() : () -> !csl // CHECK-NEXT: "csl.fadds"(%26, %26, %26) : (!csl, !csl, !csl) -> () -%33 = "csl.variable"() : () -> !csl.var> -%34 = "csl.load_var"(%33) : (!csl.var>) -> memref<512xf32> -"csl.store_var"(%33, %34) : (!csl.var>, memref<512xf32>) -> () - -// CHECK-NEXT: %27 = "csl.variable"() : () -> !csl.var> -// CHECK-NEXT: %28 = "csl.load_var"(%27) : (!csl.var>) -> !csl -// CHECK-NEXT: "csl.store_var"(%27, %28) : (!csl.var>, !csl) -> () - -// ensure that pre-existing get_mem_dsd ops access the underlying buffer, not the get_mem_dsd created on top of it - -%36 = arith.constant 510 : i16 -%37 = "csl.get_mem_dsd"(%b, %36) : (memref<510xf32>, i16) -> !csl - -// CHECK-NEXT: %29 = arith.constant 510 : i16 -// CHECK-NEXT: %30 = "csl.get_mem_dsd"(%b, %29) : (memref<510xf32>, i16) -> !csl - -%38 = memref.load %b[%28] : memref<510xf32> -"test.op"(%38) : (f32) -> () - -// CHECK-NEXT: %31 = memref.load %b[%13] : memref<510xf32> -// CHECK-NEXT: "test.op"(%31) : (f32) -> () +%39 = memref.alloc() {"alignment" = 64 : i64} : memref<3x64xf32> +%40 = "memref.subview"(%39, %0) <{"operandSegmentSizes" = array, "static_offsets" = array, "static_sizes" = array, "static_strides" = array}> : (memref<3x64xf32>, index) -> memref<32xf32, strided<[1], offset: ?>> + +// CHECK-NEXT: %27 = "csl.zeros"() : () -> memref<3x64xf32> +// CHECK-NEXT: %28 = arith.constant 3 : i16 +// CHECK-NEXT: %29 = arith.constant 64 : i16 +// CHECK-NEXT: %30 = "csl.get_mem_dsd"(%27, %28, %29) : (memref<3x64xf32>, i16, i16) -> !csl +// CHECK-NEXT: %31 = arith.constant 32 : i16 +// CHECK-NEXT: %32 = "csl.get_mem_dsd"(%27, %31) <{"offsets" = [0 : i16, -9223372036854775808 : i64]}> : (memref<3x64xf32>, i16) -> !csl +// CHECK-NEXT: %33 = arith.index_cast %0 : index to si16 +// CHECK-NEXT: %34 = "csl.increment_dsd_offset"(%32, %33) <{"elem_type" = f32}> : (!csl, si16) -> !csl }) {sym_name = "program"} : () -> () } diff --git a/xdsl/dialects/csl/csl.py b/xdsl/dialects/csl/csl.py index e8f6da22e0..fe60d2e5d3 100644 --- a/xdsl/dialects/csl/csl.py +++ b/xdsl/dialects/csl/csl.py @@ -1166,9 +1166,9 @@ def verify_(self) -> None: raise VerifyException( "DSD of type mem4d_dsd must have between 1 and 4 dimensions" ) - if self.offsets is not None and len(self.offsets) != len(self.sizes): + if self.offsets is not None and len(self.offsets) < len(self.sizes): raise VerifyException( - "Dimensions of offsets must match dimensions of sizes" + "Dimensions of offsets must match (or exceed) dimensions of sizes" ) if self.strides is not None and len(self.strides) != len(self.sizes): raise VerifyException( diff --git a/xdsl/transforms/memref_to_dsd.py b/xdsl/transforms/memref_to_dsd.py index 98f4a59a7d..8d3a384f21 100644 --- a/xdsl/transforms/memref_to_dsd.py +++ b/xdsl/transforms/memref_to_dsd.py @@ -1,3 +1,4 @@ +import collections from collections.abc import Sequence from dataclasses import dataclass from typing import cast @@ -120,6 +121,35 @@ class LowerSubviewOpPass(RewritePattern): @op_type_rewrite_pattern def match_and_rewrite(self, op: memref.SubviewOp, rewriter: PatternRewriter, /): assert isa(op.source.type, MemRefType[Attribute]) + assert isa(op.result.type, MemRefType[Attribute]) + + if len(op.result.type.get_shape()) == 1 and len(op.source.type.get_shape()) > 1: + # 1d subview onto a nd memref + sizes = op.static_sizes.get_values() + scounts = collections.Counter(sizes) + if 1 in scounts: + scounts.pop(1) + assert ( + len(scounts) == 1 + ), "1d access into nd memref must specify one size > 1" + size, counts = scounts.most_common()[0] + assert ( + counts == 1 + ), "1d access into nd memref can only specify one size > 1, which can occur only once" + size_op = arith.ConstantOp.from_int_and_width(size, 16) + offsets = [ + IntegerAttr(o, 16 if o != memref.SubviewOp.DYNAMIC_INDEX else 64) + for o in op.static_offsets.get_values() + ] + dsd_op = csl.GetMemDsdOp( + operands=[op.source, [size_op]], + properties={"offsets": ArrayAttr(offsets)}, + result_types=[csl.DsdType(csl.DsdKind.mem1d_dsd)], + ) + offset_ops = self._update_offsets(op, dsd_op) if op.offsets else [] + rewriter.replace_matched_op([size_op, dsd_op, *offset_ops]) + return + assert len(op.static_sizes) == 1, "not implemented" assert len(op.static_offsets) == 1, "not implemented" assert len(op.static_strides) == 1, "not implemented" @@ -214,7 +244,7 @@ def _update_offsets( static_offsets = cast(Sequence[int], subview.static_offsets.get_values()) - if static_offsets[0] == memref.SubviewOp.DYNAMIC_INDEX: + if subview.offsets: ops.append(cast_op := arith.IndexCastOp(subview.offsets[0], csl.i16_value)) ops.append( csl.IncrementDsdOffsetOp.build( From d7af02e18b3f0dca27355e3331d4acee1abbcbc0 Mon Sep 17 00:00:00 2001 From: n-io Date: Wed, 18 Dec 2024 17:42:48 +0100 Subject: [PATCH 2/9] fix filecheck --- tests/filecheck/transforms/memref-to-dsd.mlir | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/tests/filecheck/transforms/memref-to-dsd.mlir b/tests/filecheck/transforms/memref-to-dsd.mlir index 3af8c9018c..1609c1d4c4 100644 --- a/tests/filecheck/transforms/memref-to-dsd.mlir +++ b/tests/filecheck/transforms/memref-to-dsd.mlir @@ -98,17 +98,39 @@ builtin.module { // CHECK-NEXT: %26 = "test.op"() : () -> !csl // CHECK-NEXT: "csl.fadds"(%26, %26, %26) : (!csl, !csl, !csl) -> () +%33 = "csl.variable"() : () -> !csl.var> +%34 = "csl.load_var"(%33) : (!csl.var>) -> memref<512xf32> +"csl.store_var"(%33, %34) : (!csl.var>, memref<512xf32>) -> () + +// CHECK-NEXT: %27 = "csl.variable"() : () -> !csl.var> +// CHECK-NEXT: %28 = "csl.load_var"(%27) : (!csl.var>) -> !csl +// CHECK-NEXT: "csl.store_var"(%27, %28) : (!csl.var>, !csl) -> () + +// ensure that pre-existing get_mem_dsd ops access the underlying buffer, not the get_mem_dsd created on top of it + +%36 = arith.constant 510 : i16 +%37 = "csl.get_mem_dsd"(%b, %36) : (memref<510xf32>, i16) -> !csl + +// CHECK-NEXT: %29 = arith.constant 510 : i16 +// CHECK-NEXT: %30 = "csl.get_mem_dsd"(%b, %29) : (memref<510xf32>, i16) -> !csl + +%38 = memref.load %b[%28] : memref<510xf32> +"test.op"(%38) : (f32) -> () + +// CHECK-NEXT: %31 = memref.load %b[%13] : memref<510xf32> +// CHECK-NEXT: "test.op"(%31) : (f32) -> () + %39 = memref.alloc() {"alignment" = 64 : i64} : memref<3x64xf32> %40 = "memref.subview"(%39, %0) <{"operandSegmentSizes" = array, "static_offsets" = array, "static_sizes" = array, "static_strides" = array}> : (memref<3x64xf32>, index) -> memref<32xf32, strided<[1], offset: ?>> -// CHECK-NEXT: %27 = "csl.zeros"() : () -> memref<3x64xf32> -// CHECK-NEXT: %28 = arith.constant 3 : i16 -// CHECK-NEXT: %29 = arith.constant 64 : i16 -// CHECK-NEXT: %30 = "csl.get_mem_dsd"(%27, %28, %29) : (memref<3x64xf32>, i16, i16) -> !csl -// CHECK-NEXT: %31 = arith.constant 32 : i16 -// CHECK-NEXT: %32 = "csl.get_mem_dsd"(%27, %31) <{"offsets" = [0 : i16, -9223372036854775808 : i64]}> : (memref<3x64xf32>, i16) -> !csl -// CHECK-NEXT: %33 = arith.index_cast %0 : index to si16 -// CHECK-NEXT: %34 = "csl.increment_dsd_offset"(%32, %33) <{"elem_type" = f32}> : (!csl, si16) -> !csl +// CHECK-NEXT: %32 = "csl.zeros"() : () -> memref<3x64xf32> +// CHECK-NEXT: %33 = arith.constant 3 : i16 +// CHECK-NEXT: %34 = arith.constant 64 : i16 +// CHECK-NEXT: %35 = "csl.get_mem_dsd"(%32, %33, %34) : (memref<3x64xf32>, i16, i16) -> !csl +// CHECK-NEXT: %36 = arith.constant 32 : i16 +// CHECK-NEXT: %37 = "csl.get_mem_dsd"(%32, %36) <{"offsets" = [0 : i16, -9223372036854775808 : i64]}> : (memref<3x64xf32>, i16) -> !csl +// CHECK-NEXT: %38 = arith.index_cast %0 : index to si16 +// CHECK-NEXT: %39 = "csl.increment_dsd_offset"(%37, %38) <{"elem_type" = f32}> : (!csl, si16) -> !csl }) {sym_name = "program"} : () -> () } From 44dec4e85bb84ad4f046977f1978317135c226fd Mon Sep 17 00:00:00 2001 From: n-io Date: Wed, 18 Dec 2024 17:48:11 +0100 Subject: [PATCH 3/9] fix pyright --- xdsl/transforms/memref_to_dsd.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xdsl/transforms/memref_to_dsd.py b/xdsl/transforms/memref_to_dsd.py index 8d3a384f21..6cf2321f40 100644 --- a/xdsl/transforms/memref_to_dsd.py +++ b/xdsl/transforms/memref_to_dsd.py @@ -133,12 +133,15 @@ def match_and_rewrite(self, op: memref.SubviewOp, rewriter: PatternRewriter, /): len(scounts) == 1 ), "1d access into nd memref must specify one size > 1" size, counts = scounts.most_common()[0] + size = cast(int, size) assert ( counts == 1 ), "1d access into nd memref can only specify one size > 1, which can occur only once" size_op = arith.ConstantOp.from_int_and_width(size, 16) offsets = [ - IntegerAttr(o, 16 if o != memref.SubviewOp.DYNAMIC_INDEX else 64) + IntegerAttr( + cast(int, o), 16 if o != memref.SubviewOp.DYNAMIC_INDEX else 64 + ) for o in op.static_offsets.get_values() ] dsd_op = csl.GetMemDsdOp( From 27452d65715146d0258d6c82601c64b434eafc74 Mon Sep 17 00:00:00 2001 From: n-io Date: Thu, 19 Dec 2024 16:26:58 +0100 Subject: [PATCH 4/9] change to using affine maps --- tests/filecheck/transforms/memref-to-dsd.mlir | 4 +-- xdsl/transforms/memref_to_dsd.py | 28 ++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/tests/filecheck/transforms/memref-to-dsd.mlir b/tests/filecheck/transforms/memref-to-dsd.mlir index 1609c1d4c4..2c38de193c 100644 --- a/tests/filecheck/transforms/memref-to-dsd.mlir +++ b/tests/filecheck/transforms/memref-to-dsd.mlir @@ -121,14 +121,14 @@ builtin.module { // CHECK-NEXT: "test.op"(%31) : (f32) -> () %39 = memref.alloc() {"alignment" = 64 : i64} : memref<3x64xf32> -%40 = "memref.subview"(%39, %0) <{"operandSegmentSizes" = array, "static_offsets" = array, "static_sizes" = array, "static_strides" = array}> : (memref<3x64xf32>, index) -> memref<32xf32, strided<[1], offset: ?>> +%40 = "memref.subview"(%39, %0) <{"operandSegmentSizes" = array, "static_offsets" = array, "static_sizes" = array, "static_strides" = array}> : (memref<3x64xf32>, index) -> memref<32xf32, strided<[1], offset: ?>> // CHECK-NEXT: %32 = "csl.zeros"() : () -> memref<3x64xf32> // CHECK-NEXT: %33 = arith.constant 3 : i16 // CHECK-NEXT: %34 = arith.constant 64 : i16 // CHECK-NEXT: %35 = "csl.get_mem_dsd"(%32, %33, %34) : (memref<3x64xf32>, i16, i16) -> !csl // CHECK-NEXT: %36 = arith.constant 32 : i16 -// CHECK-NEXT: %37 = "csl.get_mem_dsd"(%32, %36) <{"offsets" = [0 : i16, -9223372036854775808 : i64]}> : (memref<3x64xf32>, i16) -> !csl +// CHECK-NEXT: %37 = "csl.get_mem_dsd"(%32, %36) <{"tensor_access" = affine_map<(d0) -> (2, d0)>}> : (memref<3x64xf32>, i16) -> !csl // CHECK-NEXT: %38 = arith.index_cast %0 : index to si16 // CHECK-NEXT: %39 = "csl.increment_dsd_offset"(%37, %38) <{"elem_type" = f32}> : (!csl, si16) -> !csl diff --git a/xdsl/transforms/memref_to_dsd.py b/xdsl/transforms/memref_to_dsd.py index 1cbc270505..7cdb402321 100644 --- a/xdsl/transforms/memref_to_dsd.py +++ b/xdsl/transforms/memref_to_dsd.py @@ -6,6 +6,7 @@ from xdsl.context import MLContext from xdsl.dialects import arith, builtin, csl, memref from xdsl.dialects.builtin import ( + AffineMapAttr, ArrayAttr, Float16Type, Float32Type, @@ -20,6 +21,7 @@ UnrealizedConversionCastOp, ) from xdsl.ir import Attribute, Operation, OpResult, SSAValue +from xdsl.ir.affine import AffineConstantExpr, AffineDimExpr, AffineExpr, AffineMap from xdsl.passes import ModulePass from xdsl.pattern_rewriter import ( GreedyRewritePatternApplier, @@ -126,27 +128,33 @@ def match_and_rewrite(self, op: memref.SubviewOp, rewriter: PatternRewriter, /): if len(op.result.type.get_shape()) == 1 and len(op.source.type.get_shape()) > 1: # 1d subview onto a nd memref sizes = op.static_sizes.get_values() - scounts = collections.Counter(sizes) - if 1 in scounts: - scounts.pop(1) + size_counts = collections.Counter(sizes) + if 1 in size_counts: + size_counts.pop(1) assert ( - len(scounts) == 1 + len(size_counts) == 1 ), "1d access into nd memref must specify one size > 1" - size, counts = scounts.most_common()[0] + size, counts = size_counts.most_common()[0] size = cast(int, size) + assert ( counts == 1 ), "1d access into nd memref can only specify one size > 1, which can occur only once" - size_op = arith.ConstantOp.from_int_and_width(size, 16) - offsets = [ - IntegerAttr( - cast(int, o), 16 if o != memref.SubviewOp.DYNAMIC_INDEX else 64 + + amap: list[AffineExpr] = [ + AffineConstantExpr( + cast(int, o) if o != memref.SubviewOp.DYNAMIC_INDEX else 0 ) for o in op.static_offsets.get_values() ] + amap[sizes.index(size)] += AffineDimExpr(0) + + size_op = arith.ConstantOp.from_int_and_width(size, 16) dsd_op = csl.GetMemDsdOp( operands=[op.source, [size_op]], - properties={"offsets": ArrayAttr(offsets)}, + properties={ + "tensor_access": AffineMapAttr(AffineMap(1, 0, tuple(amap))) + }, result_types=[csl.DsdType(csl.DsdKind.mem1d_dsd)], ) offset_ops = self._update_offsets(op, dsd_op) if op.offsets else [] From eb7b73f56752b53260e58db694bfb8d3c576eb0d Mon Sep 17 00:00:00 2001 From: n-io Date: Thu, 19 Dec 2024 16:28:44 +0100 Subject: [PATCH 5/9] use AnyMemRefType --- xdsl/transforms/memref_to_dsd.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xdsl/transforms/memref_to_dsd.py b/xdsl/transforms/memref_to_dsd.py index 7cdb402321..c81c728a90 100644 --- a/xdsl/transforms/memref_to_dsd.py +++ b/xdsl/transforms/memref_to_dsd.py @@ -7,6 +7,7 @@ from xdsl.dialects import arith, builtin, csl, memref from xdsl.dialects.builtin import ( AffineMapAttr, + AnyMemRefType, ArrayAttr, Float16Type, Float32Type, @@ -122,8 +123,8 @@ class LowerSubviewOpPass(RewritePattern): @op_type_rewrite_pattern def match_and_rewrite(self, op: memref.SubviewOp, rewriter: PatternRewriter, /): - assert isa(op.source.type, MemRefType[Attribute]) - assert isa(op.result.type, MemRefType[Attribute]) + assert isa(op.source.type, AnyMemRefType) + assert isa(op.result.type, AnyMemRefType) if len(op.result.type.get_shape()) == 1 and len(op.source.type.get_shape()) > 1: # 1d subview onto a nd memref From 7d1db3846c5311298ae4342aafee7a06c20861f7 Mon Sep 17 00:00:00 2001 From: n-io Date: Thu, 19 Dec 2024 16:32:06 +0100 Subject: [PATCH 6/9] renaming vars --- xdsl/transforms/memref_to_dsd.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/xdsl/transforms/memref_to_dsd.py b/xdsl/transforms/memref_to_dsd.py index c81c728a90..bb79d3ee18 100644 --- a/xdsl/transforms/memref_to_dsd.py +++ b/xdsl/transforms/memref_to_dsd.py @@ -129,17 +129,17 @@ def match_and_rewrite(self, op: memref.SubviewOp, rewriter: PatternRewriter, /): if len(op.result.type.get_shape()) == 1 and len(op.source.type.get_shape()) > 1: # 1d subview onto a nd memref sizes = op.static_sizes.get_values() - size_counts = collections.Counter(sizes) - if 1 in size_counts: - size_counts.pop(1) + counter_sizes = collections.Counter(sizes) + if 1 in counter_sizes: + counter_sizes.pop(1) assert ( - len(size_counts) == 1 + len(counter_sizes) == 1 ), "1d access into nd memref must specify one size > 1" - size, counts = size_counts.most_common()[0] + size, size_count = counter_sizes.most_common()[0] size = cast(int, size) assert ( - counts == 1 + size_count == 1 ), "1d access into nd memref can only specify one size > 1, which can occur only once" amap: list[AffineExpr] = [ From e902129a175cda37be85b349e48ae4303cde76f3 Mon Sep 17 00:00:00 2001 From: n-io Date: Thu, 19 Dec 2024 16:39:58 +0100 Subject: [PATCH 7/9] safety check for strides --- xdsl/transforms/memref_to_dsd.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xdsl/transforms/memref_to_dsd.py b/xdsl/transforms/memref_to_dsd.py index bb79d3ee18..c5d1f6c325 100644 --- a/xdsl/transforms/memref_to_dsd.py +++ b/xdsl/transforms/memref_to_dsd.py @@ -141,6 +141,9 @@ def match_and_rewrite(self, op: memref.SubviewOp, rewriter: PatternRewriter, /): assert ( size_count == 1 ), "1d access into nd memref can only specify one size > 1, which can occur only once" + assert all( + stride == 1 for stride in op.static_strides.get_values() + ), "All strides must equal 1" amap: list[AffineExpr] = [ AffineConstantExpr( From 1ffe566ad74a6a719914d3d43cfd6f7baae2ced0 Mon Sep 17 00:00:00 2001 From: n-io Date: Fri, 20 Dec 2024 15:38:25 +0100 Subject: [PATCH 8/9] accumulator fix --- xdsl/transforms/lower_csl_stencil.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/xdsl/transforms/lower_csl_stencil.py b/xdsl/transforms/lower_csl_stencil.py index 3aa43841e6..d260dd6594 100644 --- a/xdsl/transforms/lower_csl_stencil.py +++ b/xdsl/transforms/lower_csl_stencil.py @@ -202,24 +202,25 @@ def match_and_rewrite(self, op: csl_stencil.ApplyOp, rewriter: PatternRewriter, # ensure we send only core data assert isa(op.accumulator.type, memref.MemRefType[Attribute]) assert isa(op.field.type, memref.MemRefType[Attribute]) + # the accumulator might have additional dims when used for holding prefetched data + send_buf_shape = op.accumulator.type.get_shape()[ + -len(op.field.type.get_shape()) : + ] send_buf = memref.SubviewOp.get( op.field, [ (d - s) // 2 # symmetric offset - for s, d in zip( - op.accumulator.type.get_shape(), op.field.type.get_shape() - ) + for s, d in zip(send_buf_shape, op.field.type.get_shape(), strict=True) ], - op.accumulator.type.get_shape(), - len(op.accumulator.type.get_shape()) * [1], - op.accumulator.type, + send_buf_shape, + len(send_buf_shape) * [1], + memref.MemRefType(op.field.type.get_element_type(), send_buf_shape), ) # add api call num_chunks = arith.ConstantOp(IntegerAttr(op.num_chunks.value, i16)) chunk_ref = csl.AddressOfFnOp(chunk_fn) done_ref = csl.AddressOfFnOp(done_fn) - # send_buf = memref.Subview.get(op.field, [], op.accumulator.type.get_shape(), ) api_call = csl.MemberCallOp( "communicate", None, From 0e461a8f641ed240c9c93dae6efd6a38992b5f28 Mon Sep 17 00:00:00 2001 From: n-io Date: Fri, 20 Dec 2024 21:55:31 +0100 Subject: [PATCH 9/9] default val on function --- xdsl/transforms/memref_to_dsd.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xdsl/transforms/memref_to_dsd.py b/xdsl/transforms/memref_to_dsd.py index c5d1f6c325..958ffd5e34 100644 --- a/xdsl/transforms/memref_to_dsd.py +++ b/xdsl/transforms/memref_to_dsd.py @@ -130,8 +130,7 @@ def match_and_rewrite(self, op: memref.SubviewOp, rewriter: PatternRewriter, /): # 1d subview onto a nd memref sizes = op.static_sizes.get_values() counter_sizes = collections.Counter(sizes) - if 1 in counter_sizes: - counter_sizes.pop(1) + counter_sizes.pop(1, None) assert ( len(counter_sizes) == 1 ), "1d access into nd memref must specify one size > 1"