From 3b1c29c0785772781b92be7a0c32ad89e92e941f Mon Sep 17 00:00:00 2001 From: Alexandre Eichenberger Date: Tue, 25 Feb 2020 14:33:48 -0500 Subject: [PATCH] Using attribute setters for maxpool (#105) * using attribute setters for maxpool * fix typos, added handling of storage order, simplified code --- src/dialect/onnx/onnx_ops.cpp | 182 +++++++++--------- .../onnx/onnx_shape_inference_maxpool.mlir | 20 +- 2 files changed, 104 insertions(+), 98 deletions(-) diff --git a/src/dialect/onnx/onnx_ops.cpp b/src/dialect/onnx/onnx_ops.cpp index 5d93020..3666318 100644 --- a/src/dialect/onnx/onnx_ops.cpp +++ b/src/dialect/onnx/onnx_ops.cpp @@ -24,12 +24,29 @@ using namespace mlir; using namespace mlir::OpTrait::util; +//===----------------------------------------------------------------------===// +// ONNX Helper functions +//===----------------------------------------------------------------------===// + +static size_t ArrayAttrSize(ArrayAttr a) { return a.size(); } + +static size_t ArrayAttrSize(Optional a) { + return a.getValue().size(); +} + +static int64_t ArrayAttrIntVal(ArrayAttr a, int i) { + return (a.getValue()[i]).cast().getInt(); +} + +static int64_t ArrayAttrIntVal(Optional a, int i) { + return (a.getValue().getValue()[i]).cast().getInt(); +} + //===----------------------------------------------------------------------===// // Get reduction type //===----------------------------------------------------------------------===// -RankedTensorType getReductionOutputType(RankedTensorType operandTy, - Optional axesAttrs, - APInt keepdims) { +RankedTensorType getReductionOutputType( + RankedTensorType operandTy, Optional axesAttrs, APInt keepdims) { int64_t rank = operandTy.getRank(); SmallVector axes; @@ -87,19 +104,18 @@ ONNXOpsDialect::ONNXOpsDialect(mlir::MLIRContext *ctx) } void ONNXEntryPointOp::build(mlir::Builder *builder, - mlir::OperationState &state, mlir::FuncOp function, - int numInputs, int numOutputs) { + mlir::OperationState &state, mlir::FuncOp function, int numInputs, + int numOutputs) { state.addAttribute(ONNXEntryPointOp::getEntryPointFuncAttrName(), - builder->getSymbolRefAttr(function)); + builder->getSymbolRefAttr(function)); state.addAttribute(ONNXEntryPointOp::getNumInputsAttrName(), - builder->getI32IntegerAttr(numInputs)); + builder->getI32IntegerAttr(numInputs)); state.addAttribute(ONNXEntryPointOp::getNumOutputsAttrName(), - builder->getI32IntegerAttr(numOutputs)); + builder->getI32IntegerAttr(numOutputs)); } ONNXEntryPointOp ONNXEntryPointOp::create(mlir::Location location, - mlir::FuncOp &func, int numInputs, - int numOutputs) { + mlir::FuncOp &func, int numInputs, int numOutputs) { mlir::OperationState state(location, "onnx.EntryPoint"); Builder builder(location->getContext()); mlir::ONNXEntryPointOp::build(&builder, state, func, numInputs, numOutputs); @@ -552,9 +568,9 @@ void ONNXGemmOp::inferShapes() { int rank = shape.size(); if ((rank > 2) || (rank >= 1 && shape[rank - 1] != -1 && N != -1 && - N != shape[rank - 1] && shape[rank - 1] != 1) || + N != shape[rank - 1] && shape[rank - 1] != 1) || (rank == 2 && shape[rank - 2] != -1 && M != -1 && - M != shape[rank - 2] && shape[rank - 2] != 1)) { + M != shape[rank - 2] && shape[rank - 2] != 1)) { emitError("Bias shape mismatched."); } } @@ -885,111 +901,103 @@ void ONNXConvNoBiasOp::inferShapes() { //===----------------------------------------------------------------------===// // MaxPoolSingleOut +// Infer shape attributes output: +// - auto_pad set to NOTSET; +// - dilations, strides: set to 1 if not defined by user; +// - pads: set to proper value, 0 if not defined by user. void ONNXMaxPoolSingleOutOp::inferShapes() { // Cannot infer shape if no shape exists. if (!X().getType().isa()) return; + auto builder = mlir::Builder(this->getContext()); - // 1) get shape of input + // 1) Get shape of input. auto xTy = X().getType().cast(); auto xShape = xTy.getShape(); auto xRank = xShape.size(); - // 2) analyse parameters - // get kernel sizes from kernel_shape attribute + // 2) Analyse parameters. Get kernel sizes from kernel_shape attribute. auto kernelShape = kernel_shape(); if (!kernelShape) emitError( - "kernel_shape is a mandatory attribute for which there is no default."); - auto kernelShapeArray = kernelShape.getValue(); - auto kernelRank = kernelShape.size(); + "kernel_shape is a mandatory attribute for which there is no default"); + auto kernelRank = ArrayAttrSize(kernelShape); if (kernelRank > xRank) - emitError("kernel_shape spatial dimension is too large."); + emitError("kernel_shape spatial dimension is too large"); auto kernelOffset = xRank - kernelRank; - // ceil mode + // Ceil mode. auto ceilMode = ceil_mode().getSExtValue(); - // dilatation - SmallVector actualDilations; + // Dilatation. auto dilationsOpt = dilations(); if (dilationsOpt.hasValue()) { - auto dilationsArray = - dilationsOpt.getValue().getValue(); // opt -> attr -> array - if (dilationsArray.size() != kernelRank) - emitError("dialation rank is not the same as the spatial rank."); - // fill in the actual values + if (ArrayAttrSize(dilationsOpt) != kernelRank) + emitError("dialation rank is not the same as the spatial rank"); + // Test values. for (int i = 0; i < kernelRank; ++i) { - int64_t d = (dilationsArray[i]).cast().getInt(); - if (d < 1) - emitError("dialation value must be nonzero positive."); - actualDilations.emplace_back(d); + if (ArrayAttrIntVal(dilationsOpt, i) < 1) + emitError("dialation value must be nonzero positive"); } } else { - for (int i = 0; i < kernelRank; ++i) { - actualDilations.emplace_back(1); - } + // Default dilatation is needed. + SmallVector defaultVals(kernelRank, 1); + // Convert to ArrayRef, then build attribute, then store attribute. + ArrayRef defaultRefs(defaultVals); + auto defaultAttr = builder.getI64ArrayAttr(defaultRefs); + dilationsAttr(defaultAttr); + dilationsOpt = dilations(); } - // storage order + // Storage order. + auto storageOrder = storage_order().getSExtValue(); + if (storageOrder != 0) + emitError("column major storage order not supported at this time"); - // strides - SmallVector actualStrides; + // Strides. auto stridesOpt = strides(); if (stridesOpt.hasValue()) { - auto stridesArray = stridesOpt.getValue().getValue(); - if (stridesArray.size() != kernelRank) - emitError("strides rank is not the same as the spatial rank."); - // fill in the actual values + if (ArrayAttrSize(stridesOpt) != kernelRank) + emitError("strides rank is not the same as the spatial rank"); + // Check values. for (int i = 0; i < kernelRank; ++i) { - int64_t s = (stridesArray[i]).cast().getInt(); - if (s < 1) - emitError("strides value must be nonzero positive."); - actualStrides.emplace_back(s); + if (ArrayAttrIntVal(stridesOpt, i) < 1) + emitError("strides value must be nonzero positive"); } } else { - for (int i = 0; i < kernelRank; ++i) { - actualStrides.emplace_back(1); - } + SmallVector defaultVals(kernelRank, 1); + // Convert to ArrayRef, then build attribute, then store attribute. + ArrayRef defaultRefs(defaultVals); + auto defaultAttr = builder.getI64ArrayAttr(defaultRefs); + stridesAttr(defaultAttr); + stridesOpt = strides(); } - // now try to find padding, getting auto_pad attribute first + // Now try to find padding, getting auto_pad attribute first. auto autoPad = auto_pad(); - // and then investigate the various different cases - SmallVector actualPads; - auto defaultPads = false; + // And then investigate the various different cases. + SmallVector actualPads(2 * kernelRank, 0); if (autoPad == "NOTSET") { auto padsOpt = pads(); if (padsOpt.hasValue()) { - auto padsArray = padsOpt.getValue().getValue(); - // pads consists of two entries for each spatial axis. - if (padsArray.size() != 2 * kernelRank) - emitError("pads rank is not twice the spatial rank."); - // fill in the actual values + // Pads consists of two entries for each spatial axis. + if (ArrayAttrSize(padsOpt) != 2 * kernelRank) + emitError("pads rank is not twice the spatial rank"); + // Check values for (int i = 0; i < 2 * kernelRank; ++i) { - int64_t p = (padsArray[i]).cast().getInt(); + int64_t p = ArrayAttrIntVal(padsOpt, i); if (p < 0) - emitError("pads value must be nonnegative."); - actualPads.emplace_back(p); + emitError("pads value must be nonnegative"); + actualPads[i] = p; } - } else { - // pads are not defined, default to value 0 - defaultPads = true; } - } else if (autoPad == "VALID") { - defaultPads = true; } else if (autoPad == "SAME_UPPER" || autoPad == "SAME_LOWER") { - // init pad with zero - for (int i = 0; i < 2 * kernelRank; ++i) { - actualPads.emplace_back(0); - } for (int i = 0; i < kernelRank; ++i) { auto inputSpatialShape = xShape[kernelOffset + i]; - auto kernelSpatialShape = - (kernelShapeArray[i]).cast().getInt(); - auto dilations = actualDilations[i]; - auto strideSpatialShape = actualStrides[i]; + auto kernelSpatialShape = ArrayAttrIntVal(kernelShape, i); + auto dilations = ArrayAttrIntVal(dilationsOpt, i); + auto strideSpatialShape = ArrayAttrIntVal(stridesOpt, i); int64_t outputSpatialShape = ceil((1.0 * inputSpatialShape) / (1.0 * strideSpatialShape)); auto sumOfPad = (outputSpatialShape - 1) * strideSpatialShape + @@ -1004,29 +1012,27 @@ void ONNXMaxPoolSingleOutOp::inferShapes() { } } } - } else { + } else if (autoPad != "VALID") { emitError("auto_pad of unknown / unsupported value."); } - // handle case where default pad values must be used - if (defaultPads) { - for (int i = 0; i < 2 * kernelRank; ++i) { - actualPads.emplace_back(0); - } + // Set pads values in attributes. + { + ArrayRef defaultRefs(actualPads); + auto defaultAttr = builder.getI64ArrayAttr(defaultRefs); + padsAttr(defaultAttr); + auto defaultAutoPadAttr = builder.getStringAttr("NOTSET"); + auto_padAttr(defaultAutoPadAttr); } - // initialize output shape + // Initialize output shape. SmallVector yShape(xShape.begin(), xShape.end()); - // for all kernel dimensions + // Process for all kernel dimensions. for (int i = 0; i < kernelRank; ++i) { auto inputSpatialShape = xShape[kernelOffset + i]; auto padShape = actualPads[i] + actualPads[kernelRank + i]; - auto kernelSpatialShape = - (kernelShapeArray[i]).cast().getInt(); - auto dilations = actualDilations[i]; - auto strideSpatialShape = actualStrides[i]; - /// output_spatial_shape[i] = ceil( (input_spatial_shape[i] + pad_shape[i] - - // ((kernel_spatial_shape[i] - 1) * dilations[i] + 1)) / - // strides_spatial_shape[i] + 1) + auto kernelSpatialShape = ArrayAttrIntVal(kernelShape, i); + auto dilations = ArrayAttrIntVal(dilationsOpt, i); + auto strideSpatialShape = ArrayAttrIntVal(stridesOpt, i); double numerator = inputSpatialShape + padShape - ((kernelSpatialShape - 1) * dilations + 1); double denominator = strideSpatialShape; diff --git a/test/mlir/onnx/onnx_shape_inference_maxpool.mlir b/test/mlir/onnx/onnx_shape_inference_maxpool.mlir index 3ebaf34..1d83b8b 100644 --- a/test/mlir/onnx/onnx_shape_inference_maxpool.mlir +++ b/test/mlir/onnx/onnx_shape_inference_maxpool.mlir @@ -6,7 +6,7 @@ func @test_default_maxpoolsingleout(%arg0 : tensor<5x5x32x32xf32>) -> tensor<*xf "std.return"(%0) : (tensor<*xf32>) -> () } // CHECK-LABEL: test_default_maxpoolsingleout -// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "VALID", ceil_mode = 0 : i64, kernel_shape = [3, 3], pads = [1, 1, 1, 1]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x30x30xf32> +// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, dilations = [1, 1], kernel_shape = [3, 3], pads = [0, 0, 0, 0], strides = [1, 1]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x30x30xf32> // CHECK: return [[RES]] : tensor<5x5x30x30xf32> @@ -16,7 +16,7 @@ func @test_default_maxpoolsingleout_defpad(%arg0 : tensor<5x5x32x32xf32>) -> ten "std.return"(%0) : (tensor<*xf32>) -> () } // CHECK-LABEL: test_default_maxpoolsingleout_defpad -// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, kernel_shape = [3, 3]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x30x30xf32> +// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, dilations = [1, 1], kernel_shape = [3, 3], pads = [0, 0, 0, 0], strides = [1, 1]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x30x30xf32> // CHECK: return [[RES]] : tensor<5x5x30x30xf32> @@ -26,7 +26,7 @@ func @test_default_maxpoolsingleout_pad(%arg0 : tensor<5x5x32x32xf32>) -> tensor "std.return"(%0) : (tensor<*xf32>) -> () } // CHECK-LABEL: test_default_maxpoolsingleout_pad -// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, kernel_shape = [3, 3], pads = [1, 1, 1, 1]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x32x32xf32> +// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, dilations = [1, 1], kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [1, 1]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x32x32xf32> // CHECK: return [[RES]] : tensor<5x5x32x32xf32> @@ -36,7 +36,7 @@ func @test_default_maxpoolsingleout_pad_nonunif(%arg0 : tensor<5x5x32x32xf32>) - "std.return"(%0) : (tensor<*xf32>) -> () } // CHECK-LABEL: test_default_maxpoolsingleout_pad_nonunif -// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, kernel_shape = [5, 3], pads = [2, 1, 1, 0]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x31x31xf32> +// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, dilations = [1, 1], kernel_shape = [5, 3], pads = [2, 1, 1, 0], strides = [1, 1]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x31x31xf32> // CHECK: return [[RES]] : tensor<5x5x31x31xf32> @@ -46,7 +46,7 @@ func @test_default_maxpoolsingleout_strides(%arg0 : tensor<5x5x32x32xf32>) -> te "std.return"(%0) : (tensor<*xf32>) -> () } // CHECK-LABEL: test_default_maxpoolsingleout_strides -// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [2, 2]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x16x16xf32> +// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, dilations = [1, 1], kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [2, 2]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x16x16xf32> // CHECK: return [[RES]] : tensor<5x5x16x16xf32> @@ -56,7 +56,7 @@ func @test_default_maxpoolsingleout_strides_nonunifpad(%arg0 : tensor<5x5x30x32x "std.return"(%0) : (tensor<*xf32>) -> () } // CHECK-LABEL: test_default_maxpoolsingleout_strides_nonunifpad -// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, kernel_shape = [2, 2], pads = [1, 0, 0, 0], strides = [2, 2]} : (tensor<5x5x30x32xf32>) -> tensor<5x5x15x16xf32> +// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, dilations = [1, 1], kernel_shape = [2, 2], pads = [1, 0, 0, 0], strides = [2, 2]} : (tensor<5x5x30x32xf32>) -> tensor<5x5x15x16xf32> // CHECK: return [[RES]] : tensor<5x5x15x16xf32> @@ -66,7 +66,7 @@ func @test_default_maxpoolsingleout_strides_nonunifpad_ceil(%arg0 : tensor<5x5x3 "std.return"(%0) : (tensor<*xf32>) -> () } // CHECK-LABEL: test_default_maxpoolsingleout_strides_nonunifpad_ceil -// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 1 : i64, kernel_shape = [2, 2], pads = [1, 0, 0, 0], strides = [2, 2]} : (tensor<5x5x30x32xf32>) -> tensor<5x5x16x16xf32> +// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 1 : i64, dilations = [1, 1], kernel_shape = [2, 2], pads = [1, 0, 0, 0], strides = [2, 2]} : (tensor<5x5x30x32xf32>) -> tensor<5x5x16x16xf32> // CHECK: return [[RES]] : tensor<5x5x16x16xf32> @@ -76,7 +76,7 @@ func @test_default_maxpoolsingleout_strides_dilatation(%arg0 : tensor<5x5x8x8xf3 "std.return"(%0) : (tensor<*xf32>) -> () } // CHECK-LABEL: test_default_maxpoolsingleout_strides_dilatation -// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, dilations = [2, 2], kernel_shape = [2, 2], strides = [3, 3]} : (tensor<5x5x8x8xf32>) -> tensor<5x5x2x2xf32> +// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, dilations = [2, 2], kernel_shape = [2, 2], pads = [0, 0, 0, 0], strides = [3, 3]} : (tensor<5x5x8x8xf32>) -> tensor<5x5x2x2xf32> // CHECK: return [[RES]] : tensor<5x5x2x2xf32> /// Test the default behavior of Max Pool with dilatation @@ -85,7 +85,7 @@ func @test_default_maxpoolsingleout_upper(%arg0 : tensor<5x5x16x13xf32>) -> tens "std.return"(%0) : (tensor<*xf32>) -> () } // CHECK-LABEL: test_default_maxpoolsingleout_upper -// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "SAME_UPPER", ceil_mode = 0 : i64, kernel_shape = [4, 4], strides = [4, 4]} : (tensor<5x5x16x13xf32>) -> tensor<5x5x4x4xf32> +// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, dilations = [1, 1], kernel_shape = [4, 4], pads = [0, 1, 0, 2], strides = [4, 4]} : (tensor<5x5x16x13xf32>) -> tensor<5x5x4x4xf32> // CHECK: return [[RES]] : tensor<5x5x4x4xf32> @@ -95,6 +95,6 @@ func @test_default_maxpoolsingleout_lower(%arg0 : tensor<5x5x16x13xf32>) -> tens "std.return"(%0) : (tensor<*xf32>) -> () } // CHECK-LABEL: test_default_maxpoolsingleout_lower -// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "SAME_LOWER", ceil_mode = 0 : i64, kernel_shape = [4, 4], strides = [4, 4]} : (tensor<5x5x16x13xf32>) -> tensor<5x5x4x4xf32> +// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, dilations = [1, 1], kernel_shape = [4, 4], pads = [0, 2, 0, 1], strides = [4, 4]} : (tensor<5x5x16x13xf32>) -> tensor<5x5x4x4xf32> // CHECK: return [[RES]] : tensor<5x5x4x4xf32>