mirror of https://github.com/llvm/torch-mlir
Add TCF convolutional op with bias addition (#137)
parent
d818043986
commit
85898aaf10
|
@ -95,4 +95,25 @@ def TCF_MatmulOp : TCF_Op<"matmul"> {
|
|||
let assemblyFormat = "$lhs `,` $rhs attr-dict `:` functional-type(operands, results)";
|
||||
}
|
||||
|
||||
def TCF_ConvNCHWOp : TCF_Op<"conv_2d_nchw"> {
|
||||
let summary = "2-D convolution";
|
||||
let description = [{
|
||||
Performs 2-D convolution. This op is inspired by PyTorch's Conv2d layer (https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html).
|
||||
|
||||
The tensors have dimensions:
|
||||
- in: [N, Cin, H, W]
|
||||
- filter: [Cout, Cin, KH, KW]
|
||||
- result: [N, Cout, Hout, Wout]
|
||||
|
||||
The tensors must meet the following conditions; otherwise, this op aborts the program.
|
||||
- H is greater than or equal to KH
|
||||
- W is greater than or equal to KW
|
||||
- Cin matches between in and filter
|
||||
}];
|
||||
let arguments = (ins 4DTensorOf<[F32]>:$in, 4DTensorOf<[F32]>:$filter);
|
||||
let results = (outs 4DTensorOf<[F32]>:$result);
|
||||
|
||||
let assemblyFormat = "$in `,` $filter attr-dict `:` functional-type(operands, results)";
|
||||
}
|
||||
|
||||
#endif // #ifndef TCF_OPS
|
||||
|
|
|
@ -32,6 +32,51 @@ static SmallVector<Value, 6> bypassResultShapes(Operation *op,
|
|||
op->getLoc(), ValueRange({lhsRows, rhsCols}));
|
||||
return {shape};
|
||||
}
|
||||
// TODO: This only supports the NCHW data format. Consider other formats and lower ranks.
|
||||
if (auto conv2dNCHW = dyn_cast<tcf::ConvNCHWOp>(op)) {
|
||||
// TODO: Replace hard-coded stride/dilation/padding constant-ops.
|
||||
// TODO: Consider migrating this SSA shape-computing graph to a complex op or use the `mlir-linalg-ods-gen` approach and define a `*.tc` spec file.
|
||||
auto cI0 = builder.create<ConstantOp>(op->getLoc(), builder.getIntegerAttr(builder.getIndexType(), 0));
|
||||
auto cI1 = builder.create<ConstantOp>(op->getLoc(), builder.getIntegerAttr(builder.getIndexType(), 1));
|
||||
auto cI2 = builder.create<ConstantOp>(op->getLoc(), builder.getIntegerAttr(builder.getIndexType(), 2));
|
||||
auto stride = cI1;
|
||||
auto dilation = cI1;
|
||||
auto padding = cI0;
|
||||
auto strideHeight = stride;
|
||||
auto strideWidth = stride;
|
||||
auto dilationHeight = dilation;
|
||||
auto dilationWidth = dilation;
|
||||
auto paddingHeight = padding;
|
||||
auto paddingWidth = padding;
|
||||
auto batch = builder.create<DimOp>(op->getLoc(), conv2dNCHW.in(), 0);
|
||||
auto height = builder.create<DimOp>(op->getLoc(), conv2dNCHW.in(), 2);
|
||||
auto width = builder.create<DimOp>(op->getLoc(), conv2dNCHW.in(), 3);
|
||||
auto filterOutChannels = builder.create<DimOp>(op->getLoc(), conv2dNCHW.filter(), 0);
|
||||
auto filterHeight = builder.create<DimOp>(op->getLoc(), conv2dNCHW.filter(), 2);
|
||||
auto filterWidth = builder.create<DimOp>(op->getLoc(), conv2dNCHW.filter(), 3);
|
||||
// Output height
|
||||
auto twicePaddingHeight = builder.create<MulIOp>(op->getLoc(), paddingHeight, cI2);
|
||||
auto heightPlusTwicePadding = builder.create<SubIOp>(op->getLoc(), height, twicePaddingHeight);
|
||||
auto filterHeightMinusOne = builder.create<SubIOp>(op->getLoc(), filterHeight, cI1);
|
||||
auto dilationFilterHeight = builder.create<MulIOp>(op->getLoc(), dilationHeight, filterHeightMinusOne);
|
||||
auto outHeightUnstridedPlusOne = builder.create<SubIOp>(op->getLoc(), heightPlusTwicePadding, dilationFilterHeight);
|
||||
auto outHeightUnstrided = builder.create<SubIOp>(op->getLoc(), outHeightUnstridedPlusOne, cI1);
|
||||
auto outHeightMinusOne = builder.create<UnsignedDivIOp>(op->getLoc(), outHeightUnstrided, strideHeight);
|
||||
auto outHeight = builder.create<AddIOp>(op->getLoc(), outHeightMinusOne, cI1);
|
||||
// Output width
|
||||
auto twicePaddingWidth = builder.create<MulIOp>(op->getLoc(), paddingWidth, cI2);
|
||||
auto widthPlusTwicePadding = builder.create<SubIOp>(op->getLoc(), width, twicePaddingWidth);
|
||||
auto filterWidthMinusOne = builder.create<SubIOp>(op->getLoc(), filterWidth, cI1);
|
||||
auto dilationFilterWidth = builder.create<MulIOp>(op->getLoc(), dilationWidth, filterWidthMinusOne);
|
||||
auto outWidthUnstridedPlusOne = builder.create<SubIOp>(op->getLoc(), widthPlusTwicePadding, dilationFilterWidth);
|
||||
auto outWidthUnstrided = builder.create<SubIOp>(op->getLoc(), outWidthUnstridedPlusOne, cI1);
|
||||
auto outWidthMinusOne = builder.create<UnsignedDivIOp>(op->getLoc(), outWidthUnstrided, strideWidth);
|
||||
auto outWidth = builder.create<AddIOp>(op->getLoc(), outWidthMinusOne, cI1);
|
||||
// Output shape
|
||||
auto shape = builder.create<TensorFromElementsOp>(
|
||||
op->getLoc(), ValueRange({batch, filterOutChannels, outHeight, outWidth}));
|
||||
return {shape};
|
||||
}
|
||||
|
||||
// No shape transfer function.
|
||||
return {};
|
||||
|
@ -76,6 +121,59 @@ public:
|
|||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
class ConvertConvNCHW : public OpRewritePattern<tcf::ConvNCHWOp> {
|
||||
public:
|
||||
using OpRewritePattern::OpRewritePattern;
|
||||
LogicalResult matchAndRewrite(tcf::ConvNCHWOp op,
|
||||
PatternRewriter &rewriter) const override {
|
||||
// Create the constraints, and the assuming region.
|
||||
Value inputCin = rewriter.create<DimOp>(op.getLoc(), op.in(), 1);
|
||||
Value inputH = rewriter.create<DimOp>(op.getLoc(), op.in(), 2);
|
||||
Value inputW = rewriter.create<DimOp>(op.getLoc(), op.in(), 3);
|
||||
Value filterCin = rewriter.create<DimOp>(op.getLoc(), op.filter(), 1);
|
||||
Value filterKH = rewriter.create<DimOp>(op.getLoc(), op.filter(), 2);
|
||||
Value filterKW = rewriter.create<DimOp>(op.getLoc(), op.filter(), 3);
|
||||
Value matchingCin =
|
||||
rewriter.create<CmpIOp>(op.getLoc(), CmpIPredicate::eq, inputCin, filterCin);
|
||||
Value validFilterH =
|
||||
rewriter.create<CmpIOp>(op.getLoc(), CmpIPredicate::uge, inputH, filterKH);
|
||||
Value validFilterW =
|
||||
rewriter.create<CmpIOp>(op.getLoc(), CmpIPredicate::uge, inputW, filterKW);
|
||||
Value witnessCin = rewriter.create<shape::CstrRequireOp>(
|
||||
op.getLoc(), matchingCin, "input and filter in-channels must be equal");
|
||||
Value witnessFilterH = rewriter.create<shape::CstrRequireOp>(
|
||||
op.getLoc(), validFilterH, "input height must be greater than or equal to filter KH-dimension");
|
||||
Value witnessFilterW = rewriter.create<shape::CstrRequireOp>(
|
||||
op.getLoc(), validFilterW, "input width must be greater than or equal to filter KW-dimension");
|
||||
Value assumingAll = rewriter.create<shape::AssumingAllOp>(
|
||||
op.getLoc(), witnessCin.getType(), ValueRange({witnessCin, witnessFilterH, witnessFilterW}));
|
||||
auto assuming = rewriter.create<shape::AssumingOp>(
|
||||
op.getLoc(), ArrayRef<Type>{op.getType()}, assumingAll);
|
||||
|
||||
// Build the region body.
|
||||
rewriter.createBlock(&assuming.doRegion());
|
||||
// Create the init tensor for the ConvNCHW.
|
||||
// TODO: Expand supported data types.
|
||||
Value c0 =
|
||||
rewriter.create<ConstantOp>(op.getLoc(), rewriter.getF32FloatAttr(0.0));
|
||||
Value shape = bypassResultShapes(op, rewriter)[0];
|
||||
Value initTensor =
|
||||
rewriter.create<tcp::SplattedOp>(op.getLoc(), op.getType(), c0, shape);
|
||||
|
||||
// Create the ConvNCHW.
|
||||
auto conv2dNCHW = rewriter.create<linalg::ConvNCHWOp>(
|
||||
op.getLoc(), TypeRange(op.getType()), ValueRange({op.in(), op.filter()}), ValueRange(),
|
||||
ValueRange(initTensor));
|
||||
rewriter.create<shape::AssumingYieldOp>(op.getLoc(), conv2dNCHW.getResults());
|
||||
|
||||
// Finally, replace with the results of the shape.assuming
|
||||
rewriter.replaceOp(op, assuming.getResults());
|
||||
return success();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
class ConvertTCFToLinalg : public ConvertTCFToLinalgBase<ConvertTCFToLinalg> {
|
||||
public:
|
||||
|
@ -91,6 +189,7 @@ public:
|
|||
MLIRContext *context = &getContext();
|
||||
OwningRewritePatternList patterns;
|
||||
patterns.insert<ConvertMatmul>(context);
|
||||
patterns.insert<ConvertConvNCHW>(context);
|
||||
return std::move(patterns);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "npcomp/RefBackend/RefBackend.h"
|
||||
#include "PassDetail.h"
|
||||
|
||||
#include "mlir/Conversion/AffineToStandard/AffineToStandard.h"
|
||||
#include "mlir/Conversion/SCFToStandard/SCFToStandard.h"
|
||||
#include "mlir/Conversion/ShapeToStandard/ShapeToStandard.h"
|
||||
#include "mlir/Dialect/Linalg/IR/LinalgOps.h"
|
||||
|
@ -283,6 +284,9 @@ void mlir::NPCOMP::createRefBackendLoweringPipeline(
|
|||
// Final conversion to an LLVM module.
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
// Convert affine to std control flow in preparation for going to LLVM.
|
||||
pm.addNestedPass<FuncOp>(createLowerAffinePass());
|
||||
|
||||
// Convert scf to std control flow in preparation for going to LLVM.
|
||||
pm.addNestedPass<FuncOp>(createLowerToCFGPass());
|
||||
|
||||
|
|
|
@ -23,3 +23,50 @@ func @tcf_matmul(%arg0: tensor<?x?xf32>, %arg1: tensor<?x?xf32>) -> tensor<?x?xf
|
|||
%0 = tcf.matmul %arg0, %arg1 : (tensor<?x?xf32>, tensor<?x?xf32>) -> tensor<?x?xf32>
|
||||
return %0 : tensor<?x?xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @tcf_conv_2d_nchw(
|
||||
// CHECK-SAME: %[[IN:[a-zA-Z0-9]+]]: tensor<?x?x?x?xf32>
|
||||
// CHECK-SAME: %[[FILTER:[a-zA-Z0-9]+]]: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> {
|
||||
// CHECK: %[[C0F32:.*]] = constant 0.000000e+00 : f32
|
||||
// CHECK: %[[C1:.*]] = constant 1 : index
|
||||
// CHECK: %[[C0:.*]] = constant 0 : index
|
||||
// CHECK: %[[C2:.*]] = constant 2 : index
|
||||
// CHECK: %[[C3:.*]] = constant 3 : index
|
||||
// CHECK: %[[CHANNELS:.*]] = dim %[[IN]], %[[C1]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[HEIGHT:.*]] = dim %[[IN]], %[[C2]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[WIDTH:.*]] = dim %[[IN]], %[[C3]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[FILTERCHANNELS:.*]] = dim %[[FILTER]], %[[C1]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[FILTERHEIGHT:.*]] = dim %[[FILTER]], %[[C2]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[FILTERWIDTH:.*]] = dim %[[FILTER]], %[[C3]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[CMPCHANNELS:.*]] = cmpi "eq", %[[CHANNELS]], %[[FILTERCHANNELS]] : index
|
||||
// CHECK: %[[CMPHEIGHT:.*]] = cmpi "uge", %[[HEIGHT]], %[[FILTERHEIGHT]] : index
|
||||
// CHECK: %[[CMPWIDTH:.*]] = cmpi "uge", %[[WIDTH]], %[[FILTERWIDTH]] : index
|
||||
// CHECK: %[[CSTRCHANNELS:.*]] = shape.cstr_require %[[CMPCHANNELS]], "input and filter in-channels must be equal"
|
||||
// CHECK: %[[CSTRHEIGHT:.*]] = shape.cstr_require %[[CMPHEIGHT]], "input height must be greater than or equal to filter KH-dimension"
|
||||
// CHECK: %[[CSTRWIDTH:.*]] = shape.cstr_require %[[CMPWIDTH]], "input width must be greater than or equal to filter KW-dimension"
|
||||
// CHECK: %[[WITNESS:.*]] = shape.assuming_all %[[CSTRCHANNELS]], %[[CSTRHEIGHT]], %[[CSTRWIDTH]]
|
||||
// CHECK: %[[RET:.*]] = shape.assuming %[[WITNESS]] -> (tensor<?x?x?x?xf32>) {
|
||||
// CHECK: %[[BATCH:.*]] = dim %[[IN]], %[[C0]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[HEIGHT:.*]] = dim %[[IN]], %[[C2]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[WIDTH:.*]] = dim %[[IN]], %[[C3]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[OUTCHANNELS:.*]] = dim %[[FILTER]], %[[C0]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[FILTERHEIGHT:.*]] = dim %[[FILTER]], %[[C2]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[FILTERWIDTH:.*]] = dim %[[FILTER]], %[[C3]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[FILTERHEIGHTM1:.*]] = subi %[[FILTERHEIGHT]], %[[C1]] : index
|
||||
// CHECK: %[[HEIGHTV0:.*]] = subi %[[HEIGHT]], %[[FILTERHEIGHTM1]] : index
|
||||
// CHECK: %[[HEIGHTV0M1:.*]] = subi %[[HEIGHTV0]], %[[C1]] : index
|
||||
// CHECK: %[[OUTHEIGHT:.*]] = addi %[[HEIGHTV0M1]], %[[C1]] : index
|
||||
// CHECK: %[[FILTERWIDTHM1:.*]] = subi %[[FILTERWIDTH]], %[[C1]] : index
|
||||
// CHECK: %[[WIDTHV0:.*]] = subi %[[WIDTH]], %[[FILTERWIDTHM1]] : index
|
||||
// CHECK: %[[WIDTHV0M1:.*]] = subi %[[WIDTHV0]], %[[C1]] : index
|
||||
// CHECK: %[[OUTWIDTH:.*]] = addi %[[WIDTHV0M1]], %[[C1]] : index
|
||||
// CHECK: %[[SHAPE:.*]] = tensor_from_elements %[[BATCH]], %[[OUTCHANNELS]], %[[OUTHEIGHT]], %[[OUTWIDTH]] : tensor<4xindex>
|
||||
// CHECK: %[[INIT_TENSOR:.*]] = tcp.splatted %[[C0F32]], %[[SHAPE]] : (f32, tensor<4xindex>) -> tensor<?x?x?x?xf32>
|
||||
// CHECK: %[[CONVNCHW:.*]] = linalg.conv_2d_nchw ins(%[[IN]], %[[FILTER]] : tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) init(%[[INIT_TENSOR]] : tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32>
|
||||
// CHECK: shape.assuming_yield %[[CONVNCHW]] : tensor<?x?x?x?xf32>
|
||||
// CHECK: }
|
||||
// CHECK: return %[[RET:.*]] : tensor<?x?x?x?xf32>
|
||||
func @tcf_conv_2d_nchw(%arg0: tensor<?x?x?x?xf32>, %arg1: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> {
|
||||
%0 = tcf.conv_2d_nchw %arg0, %arg1 : (tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32>
|
||||
return %0 : tensor<?x?x?x?xf32>
|
||||
}
|
||||
|
|
|
@ -17,3 +17,10 @@ func @matmul(%arg0: tensor<?x?xf32>, %arg1: tensor<?x?xf32>) -> tensor<?x?xf32>
|
|||
%0 = tcf.matmul %arg0, %arg1 : (tensor<?x?xf32>, tensor<?x?xf32>) -> tensor<?x?xf32>
|
||||
return %0 : tensor<?x?xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @conv_2d_nchw
|
||||
func @conv_2d_nchw(%arg0: tensor<?x?x?x?xf32>, %arg1: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> {
|
||||
// CHECK: tcf.conv_2d_nchw %arg0, %arg1 : (tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32>
|
||||
%0 = tcf.conv_2d_nchw %arg0, %arg1 : (tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32>
|
||||
return %0 : tensor<?x?x?x?xf32>
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
// RUN: npcomp-run-mlir %s \
|
||||
// RUN: -invoke conv_2d_nchw \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<2x1x1x1xf32>" \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x1x1xf32>" \
|
||||
// RUN: -shared-libs=%npcomp_runtime_shlib 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=BATCH
|
||||
|
||||
// RUN: npcomp-run-mlir %s \
|
||||
// RUN: -invoke conv_2d_nchw \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x2x1x1xf32>" \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<2x2x1x1xf32>" \
|
||||
// RUN: -shared-libs=%npcomp_runtime_shlib 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=SAME_CHANNELS
|
||||
|
||||
// RUN: npcomp-run-mlir %s \
|
||||
// RUN: -invoke conv_2d_nchw \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x2x1x1xf32>" \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x2x1x1xf32>" \
|
||||
// RUN: -shared-libs=%npcomp_runtime_shlib 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=DIFFERENT_CHANNELS
|
||||
|
||||
// RUN: npcomp-run-mlir %s \
|
||||
// RUN: -invoke conv_2d_nchw \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x2x2xf32>" \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x1x1xf32>" \
|
||||
// RUN: -shared-libs=%npcomp_runtime_shlib 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=TINY_SQUARE
|
||||
|
||||
// RUN: npcomp-run-mlir %s \
|
||||
// RUN: -invoke conv_2d_nchw \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x32x32xf32>" \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x32x32xf32>" \
|
||||
// RUN: -shared-libs=%npcomp_runtime_shlib 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=HUGE_SQUARE
|
||||
|
||||
// RUN: npcomp-run-mlir %s \
|
||||
// RUN: -invoke conv_2d_nchw \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x2x2xf32>" \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x0x0xf32>" \
|
||||
// RUN: -shared-libs=%npcomp_runtime_shlib 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=ZERO_KH_KW
|
||||
|
||||
// RUN: npcomp-run-mlir %s \
|
||||
// RUN: -invoke conv_2d_nchw \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x0x0xf32>" \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x0x0xf32>" \
|
||||
// RUN: -shared-libs=%npcomp_runtime_shlib 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=ZERO_H_W
|
||||
|
||||
// BATCH: output #0: dense<0.000000e+00> : tensor<2x1x1x1xf32>
|
||||
|
||||
// SAME_CHANNELS: output #0: dense<0.000000e+00> : tensor<1x2x1x1xf32>
|
||||
|
||||
// DIFFERENT_CHANNELS: output #0: dense<0.000000e+00> : tensor<1x1x1x1xf32>
|
||||
|
||||
// TINY_SQUARE: output #0: dense<0.000000e+00> : tensor<1x1x2x2xf32>
|
||||
|
||||
// HUGE_SQUARE: output #0: dense<0.000000e+00> : tensor<1x1x1x1xf32>
|
||||
|
||||
// ZERO_KH_KW: output #0: dense<0.000000e+00> : tensor<1x1x3x3xf32>
|
||||
|
||||
// ZERO_H_W: output #0: dense<0.000000e+00> : tensor<1x1x1x1xf32>
|
||||
|
||||
func @conv_2d_nchw(%arg0: tensor<?x?x?x?xf32>, %arg1: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> {
|
||||
%0 = tcf.conv_2d_nchw %arg0, %arg1 : (tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32>
|
||||
return %0 : tensor<?x?x?x?xf32>
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// RUN: not npcomp-run-mlir %s \
|
||||
// RUN: -invoke conv_2d_nchw \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x2x2xf32>" \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x2x2x2xf32>" \
|
||||
// RUN: -shared-libs=%npcomp_runtime_shlib 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=CHANNELS
|
||||
|
||||
// RUN: not npcomp-run-mlir %s \
|
||||
// RUN: -invoke conv_2d_nchw \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x2x2xf32>" \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x3x2xf32>" \
|
||||
// RUN: -shared-libs=%npcomp_runtime_shlib 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=HEIGHT
|
||||
|
||||
// RUN: not npcomp-run-mlir %s \
|
||||
// RUN: -invoke conv_2d_nchw \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x2x2xf32>" \
|
||||
// RUN: -arg-value="dense<0.0> : tensor<1x1x2x3xf32>" \
|
||||
// RUN: -shared-libs=%npcomp_runtime_shlib 2>&1 \
|
||||
// RUN: | FileCheck %s --check-prefix=WIDTH
|
||||
|
||||
// CHANNELS: NPCOMP: aborting: input and filter in-channels must be equal
|
||||
// HEIGHT: NPCOMP: aborting: input height must be greater than or equal to filter KH-dimension
|
||||
// WIDTH: NPCOMP: aborting: input width must be greater than or equal to filter KW-dimension
|
||||
func @conv_2d_nchw(%arg0: tensor<?x?x?x?xf32>, %arg1: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> {
|
||||
%0 = tcf.conv_2d_nchw %arg0, %arg1 : (tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32>
|
||||
return %0 : tensor<?x?x?x?xf32>
|
||||
}
|
Loading…
Reference in New Issue