brintos

brintos / llvm-project-archived public Read only

0
0
Text · 45.3 KiB · b54ed55 Raw
1303 lines · cpp
1//===- TosaValidation.cpp ------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Validate if TOSA dialect input matchs with the specification for given10// requirements.11//12//===----------------------------------------------------------------------===//13 14#include "mlir/Dialect/Tosa/IR/TargetEnv.h"15#include "mlir/Dialect/Tosa/IR/TosaProfileCompliance.h"16#include "mlir/Dialect/Tosa/Transforms/Passes.h"17 18#include <string>19 20#include "mlir/Dialect/Func/IR/FuncOps.h"21#include "mlir/Dialect/Tosa/IR/TosaOps.h"22#include "mlir/Dialect/Tosa/Utils/ConversionUtils.h"23#include "mlir/IR/Builders.h"24#include "mlir/IR/BuiltinOps.h"25#include "mlir/IR/Matchers.h"26#include "mlir/IR/TypeUtilities.h"27#include "mlir/Pass/Pass.h"28#include "mlir/Transforms/DialectConversion.h"29#include "llvm/ADT/StringExtras.h"30 31namespace mlir {32namespace tosa {33#define GEN_PASS_DEF_TOSAVALIDATION34#include "mlir/Dialect/Tosa/Transforms/Passes.h.inc"35} // namespace tosa36} // namespace mlir37 38using namespace mlir;39using namespace mlir::tosa;40 41namespace {42 43static LogicalResult44checkConstantOperands(Operation *op, ArrayRef<unsigned int> operandIndices) {45  for (const auto index : operandIndices) {46    Attribute attr;47    if (!matchPattern(op->getOperand(index), m_Constant(&attr))) {48      return op->emitOpError("expected compile time resolvable constant, but "49                             "got variable value for operand #")50             << index;51    }52  }53  return success();54}55 56static LogicalResult checkConstantOperandMul(Operation *op,57                                             const TargetEnv &env) {58  if (!env.allows(Extension::dynamic) && isa<tosa::MulOp>(op)) {59    // Check 'shift'60    return checkConstantOperands(op, {2});61  }62  return success();63}64 65static LogicalResult checkConstantOperandTable(Operation *op,66                                               const TargetEnv &env) {67  if (!env.allows(Extension::dynamic) && isa<tosa::TableOp>(op)) {68    // Check 'table'69    return checkConstantOperands(op, {1});70  }71  return success();72}73 74static LogicalResult checkConstantOperandPad(Operation *op,75                                             const TargetEnv &env) {76  if (auto padOp = dyn_cast<tosa::PadOp>(op)) {77    // Assume this op is zero-padding if padConst is not presented78    if (!env.allows(Extension::dynamic) && padOp.getPadConst())79      // Check 'pad_const'80      // Note: 'padding' (operand 1) is not checked as it is a tosa.shape type81      return checkConstantOperands(op, {2});82  }83  return success();84}85 86static LogicalResult checkConstantOperandRescale(Operation *op,87                                                 const TargetEnv &env) {88  if (!env.allows(Extension::dynamic) && isa<tosa::RescaleOp>(op)) {89    // Check 'multiplier', 'shift', 'input_zp' and 'output_zp'90    return checkConstantOperands(op, {1, 2, 3, 4});91  }92  return success();93}94 95template <typename T>96static LogicalResult checkConstantOperandConvOps(Operation *op,97                                                 const TargetEnv &env) {98  if (!env.allows(Extension::dynamic) && isa<T>(op)) {99    // Check 'input_zp' and 'weight_zp'100    return checkConstantOperands(op, {3, 4});101  }102  return success();103}104 105static LogicalResult checkConstantOperandMatMul(Operation *op,106                                                const TargetEnv &env) {107  if (!env.allows(Extension::dynamic) && isa<tosa::MatMulOp>(op)) {108    // Check 'A_zp' and 'B_zp'109    return checkConstantOperands(op, {2, 3});110  }111  return success();112}113 114static LogicalResult checkConstantOperandAvgPool2d(Operation *op,115                                                   const TargetEnv &env) {116  if (!env.allows(Extension::dynamic) && isa<tosa::AvgPool2dOp>(op)) {117    // Check 'input_zp' and 'output_zp'118    return checkConstantOperands(op, {1, 2});119  }120  return success();121}122 123static LogicalResult checkConstantOperandNegate(Operation *op,124                                                const TargetEnv &env) {125  if (!env.allows(Extension::dynamic) && isa<tosa::NegateOp>(op)) {126    // Check 'input1_zp' and 'output_zp'127    return checkConstantOperands(op, {1, 2});128  }129  return success();130}131 132//===----------------------------------------------------------------------===//133// TOSA Validation Pass.134//===----------------------------------------------------------------------===//135 136struct TosaValidation : public tosa::impl::TosaValidationBase<TosaValidation> {137public:138  explicit TosaValidation() { populateConstantOperandChecks(); }139 140  explicit TosaValidation(const TosaValidationOptions &options)141      : TosaValidation() {142    this->strictOpSpecAlignment = options.strictOpSpecAlignment;143    this->allowInvalidOpDatatypeCombinations =144        options.allowInvalidOpDatatypeCombinations;145  }146  void runOnOperation() final;147 148  LogicalResult applyConstantOperandCheck(Operation *op) {149    for (auto &checker : constCheckers) {150      if (failed(checker(op, targetEnv)))151        return failure();152    }153    return success();154  }155 156  LogicalResult applyLevelCheck(Operation *op);157  LogicalResult applyAttributeCheck(Operation *op);158 159  // check variable read/write data types against variable declarations160  LogicalResult applyVariableCheck(Operation *op);161 162  // check error if conditions163  LogicalResult applyErrorIfCheck(Operation *op);164 165private:166  void populateConstantOperandChecks() {167    constCheckers.emplace_back(checkConstantOperandMul);168    constCheckers.emplace_back(checkConstantOperandTable);169    constCheckers.emplace_back(checkConstantOperandPad);170    constCheckers.emplace_back(checkConstantOperandRescale);171    constCheckers.emplace_back(checkConstantOperandConvOps<tosa::Conv2DOp>);172    constCheckers.emplace_back(checkConstantOperandConvOps<tosa::Conv3DOp>);173    constCheckers.emplace_back(174        checkConstantOperandConvOps<tosa::DepthwiseConv2DOp>);175    constCheckers.emplace_back(176        checkConstantOperandConvOps<tosa::TransposeConv2DOp>);177    constCheckers.emplace_back(checkConstantOperandMatMul);178    constCheckers.emplace_back(checkConstantOperandAvgPool2d);179    constCheckers.emplace_back(checkConstantOperandNegate);180  }181 182  LogicalResult levelCheckKernel(Operation *op, int32_t v,183                                 const StringRef checkDesc) {184    if (v > targetEnv.getLevel().MAX_KERNEL)185      return op->emitOpError() << "failed level check: " << checkDesc;186    return success();187  }188 189  LogicalResult levelCheckStride(Operation *op, int32_t v,190                                 const StringRef checkDesc) {191    if (v > targetEnv.getLevel().MAX_STRIDE)192      return op->emitOpError() << "failed level check: " << checkDesc;193    return success();194  }195 196  LogicalResult levelCheckScale(Operation *op, int32_t v,197                                const StringRef checkDesc) {198    if (v > targetEnv.getLevel().MAX_SCALE)199      return op->emitOpError() << "failed level check: " << checkDesc;200    return success();201  }202 203  LogicalResult levelCheckListSize(Operation *op, int32_t v,204                                   const StringRef checkDesc) {205    if (v > targetEnv.getLevel().MAX_TENSOR_LIST_SIZE)206      return op->emitOpError()207             << "failed level check for MAX_TENSOR_LIST_SIZE: " << checkDesc;208    return success();209  }210 211  // Perform the Level Rank check on the tensor type.212  LogicalResult levelCheckRank(Operation *op, const Type typeToCheck,213                               const StringRef operandOrResult,214                               int32_t highest_rank) {215    if (ShapedType type = dyn_cast<ShapedType>(typeToCheck)) {216      if (!type.hasRank())217        return op->emitOpError() << "failed level check: unranked tensor";218      if (type.getRank() > highest_rank)219        return op->emitOpError() << "failed level check: " << operandOrResult220                                 << " rank(shape) <= MAX_RANK";221    }222    return success();223  }224 225  // Perform the Level Rank check on the tensor value.226  LogicalResult levelCheckRank(Operation *op, const Value &v,227                               const StringRef operandOrResult,228                               int32_t highest_rank) {229    return levelCheckRank(op, v.getType(), operandOrResult, highest_rank);230  }231 232  // Perform the Level tensor size check on the tensor type.233  LogicalResult levelCheckSize(Operation *op, const Type &typeToCheck,234                               const StringRef operandOrResult);235 236  // Perform the Level tensor size check on the tensor value.237  LogicalResult levelCheckSize(Operation *op, const Value &v,238                               const StringRef operandOrResult) {239    return levelCheckSize(op, v.getType(), operandOrResult);240  }241 242  // Level check sizes of all operands and results of the operation.243  template <typename T>244  LogicalResult levelCheckSizes(T tosaOp) {245    auto op = tosaOp.getOperation();246    for (auto v : op->getOperands()) {247      if (failed(levelCheckSize(op, v, "operand")))248        return failure();249    }250 251    for (auto v : op->getResults()) {252      if (failed(levelCheckSize(op, v, "result")))253        return failure();254    }255    return success();256  }257 258  // Level check ranks of all operands, attribute and results of the operation.259  template <typename T>260  LogicalResult levelCheckRanks(T tosaOp) {261    auto op = tosaOp.getOperation();262    const TosaLevel tosaLevel = targetEnv.getLevel();263    for (auto v : op->getOperands()) {264      if (failed(levelCheckRank(op, v, "operand", tosaLevel.MAX_RANK)))265        return failure();266    }267 268    for (auto v : op->getResults()) {269      if (failed(levelCheckRank(op, v, "result", tosaLevel.MAX_RANK)))270        return failure();271    }272    return success();273  }274 275  // Level check ranks and sizes.276  LogicalResult levelCheckRanksAndSizes(Operation *op);277 278  // Pool Op: level check kernel/stride/pad values279  template <typename T>280  LogicalResult levelCheckPool(Operation *op) {281    if (auto poolOp = dyn_cast<T>(op)) {282      for (auto k : poolOp.getKernel()) {283        if (failed(levelCheckKernel(op, k, "kernel <= MAX_KERNEL"))) {284          return failure();285        }286      }287      for (auto s : poolOp.getStride()) {288        if (failed(levelCheckStride(op, s, "stride <= MAX_STRIDE"))) {289          return failure();290        }291      }292      for (auto p : poolOp.getPad()) {293        if (failed(levelCheckKernel(op, p, "pad <= MAX_KERNEL"))) {294          return failure();295        }296      }297    }298    return success();299  }300 301  // Conv Op: level check dilation/stride/pad values302  template <typename T>303  LogicalResult levelCheckConv(Operation *op) {304    if (auto convOp = dyn_cast<T>(op)) {305 306      for (auto k : convOp.getDilation()) {307        if (failed(levelCheckKernel(op, k, "dilation <= MAX_KERNEL"))) {308          return failure();309        }310      }311      for (auto p : convOp.getPad()) {312        if (failed(levelCheckKernel(op, p, "pad <= MAX_KERNEL"))) {313          return failure();314        }315      }316      for (auto s : convOp.getStride()) {317        if (failed(levelCheckStride(op, s, "stride <= MAX_STRIDE"))) {318          return failure();319        }320      }321      auto dilation = convOp.getDilation();322      if (ShapedType weightType =323              dyn_cast<ShapedType>(op->getOperand(1).getType())) {324        auto shape = weightType.getShape();325        if (isa<tosa::Conv2DOp>(op)) {326          assert(shape.size() == 4);327          assert(dilation.size() == 2);328          if (failed(levelCheckKernel(op, dilation[0] * shape[1],329                                      "dilation_y * KH <= MAX_KERNEL)")) ||330              failed(levelCheckKernel(op, dilation[1] * shape[2],331                                      "dilation_x * KW <= MAX_KERNEL)")))332            return failure();333        } else if (isa<tosa::Conv3DOp>(op)) {334          assert(shape.size() == 5);335          assert(dilation.size() == 3);336          if (failed(levelCheckKernel(op, dilation[0] * shape[1],337                                      "dilation_d * KD <= MAX_KERNEL)")) ||338              failed(levelCheckKernel(op, dilation[1] * shape[2],339                                      "dilation_y * KH <= MAX_KERNEL)")) ||340              failed(levelCheckKernel(op, dilation[2] * shape[3],341                                      "dilation_x * KW <= MAX_KERNEL)")))342            return failure();343        } else if (isa<tosa::DepthwiseConv2DOp>(op)) {344          assert(shape.size() == 4);345          assert(dilation.size() == 2);346          if (failed(levelCheckKernel(op, dilation[0] * shape[0],347                                      "dilation_y * KH <= MAX_KERNEL)")) ||348              failed(levelCheckKernel(op, dilation[1] * shape[1],349                                      "dilation_x * KW <= MAX_KERNEL)")))350            return failure();351        }352      }353    }354    return success();355  }356 357  // FFT op: level check H, W in input shape [N,H,W]358  template <typename T>359  LogicalResult levelCheckFFT(Operation *op) {360    if (isa<T>(op)) {361      for (auto v : op->getOperands()) {362        if (ShapedType type = dyn_cast<ShapedType>(v.getType())) {363          auto shape = type.getShape();364          assert(shape.size() == 3);365          if (failed(levelCheckKernel(op, shape[1], "H <= MAX_KERNEL")) ||366              failed(levelCheckKernel(op, shape[2], "W <= MAX_KERNEL"))) {367            return failure();368          }369        }370      }371    }372    return success();373  }374 375  // TransposeConv2d op: level check kH/kW, outpad, and stride376  LogicalResult levelCheckTransposeConv2d(Operation *op) {377    if (auto transpose = dyn_cast<tosa::TransposeConv2DOp>(op)) {378      if (ShapedType filterType =379              dyn_cast<ShapedType>(transpose.getWeight().getType())) {380        auto shape = filterType.getShape();381        assert(shape.size() == 4);382        // level check kernel sizes for kH and KW383        if (failed(levelCheckKernel(op, shape[1], "KH <= MAX_KERNEL")) ||384            failed(levelCheckKernel(op, shape[2], "KW <= MAX_KERNEL"))) {385          return failure();386        }387      }388      for (auto p : transpose.getOutPad()) {389        if (failed(levelCheckKernel(op, p, "pad <= MAX_KERNEL"))) {390          return failure();391        }392      }393      for (auto s : transpose.getStride()) {394        if (failed(levelCheckStride(op, s, "stride <= MAX_STRIDE"))) {395          return failure();396        }397      }398    }399    return success();400  }401 402  // Resize op: level check max scales403  LogicalResult levelCheckResize(Operation *op) {404    if (auto resize = dyn_cast<tosa::ResizeOp>(op)) {405      SmallVector<int64_t> scale;406      if (!tosa::getConstShapeValues(resize.getScale().getDefiningOp(),407                                     scale)) {408        return failure();409      }410      const int64_t scaleYN = scale[0];411      const int64_t scaleYD = scale[1];412      const int64_t scaleXN = scale[2];413      const int64_t scaleXD = scale[3];414      if (failed(levelCheckScale(op, scaleYN / scaleYD,415                                 "scale_y_n/scale_y_d <= MAX_SCALE")) ||416          failed(levelCheckScale(op, scaleXN / scaleXD,417                                 "scale_x_n/scale_x_d <= MAX_SCALE"))) {418        return failure();419      }420    }421    return success();422  }423 424  // Recursively perform a bottom-up search to determine the maximum nesting425  // depth, starting from a specific operation and continuing up to the function426  // or module scope. Tosa nesting_depth starts at 0 and increments by one each427  // time a new nested `region` is encountered.428  static void getMaxNestedDepth(Operation *op, int32_t &depth) {429    if (isa<mlir::func::FuncOp>(op) || isa<ModuleOp>(op))430      return;431 432    op = op->getParentOp();433    if (!op)434      return;435 436    depth++;437    getMaxNestedDepth(op, depth);438  }439 440  LogicalResult levelCheckMaxNesting(Operation *op) {441    int32_t maxNestedDepth = 0;442    getMaxNestedDepth(op, maxNestedDepth);443 444    if (maxNestedDepth >= targetEnv.getLevel().MAX_NESTING) {445      op->emitOpError() << "failed level check: " << maxNestedDepth446                        << " >= MAX_NESTING";447      return failure();448    }449    return success();450  }451 452  LogicalResult levelCheckListSize(Operation *op) {453    if (auto concat = dyn_cast<tosa::ConcatOp>(op)) {454      return levelCheckListSize(op, concat.getInput1().size(), "input1");455    }456    if (auto custom = dyn_cast<tosa::CustomOp>(op)) {457      if (failed(levelCheckListSize(op, custom.getInputList().size(),458                                    "input_list")) ||459          failed(levelCheckListSize(op, custom.getOutputList().size(),460                                    "output_list"))) {461        return failure();462      }463    }464    if (auto condIf = dyn_cast<tosa::IfOp>(op)) {465      if (failed(466              levelCheckListSize(op, condIf.getInputList().size(), "inputs")) ||467          failed(levelCheckListSize(op, condIf.getOutputList().size(),468                                    "outputs"))) {469        return failure();470      }471    }472    if (auto w = dyn_cast<tosa::WhileOp>(op)) {473      if (failed(levelCheckListSize(op, w.getInputList().size(), "inputs")) ||474          failed(levelCheckListSize(op, w.getOutputList().size(), "outputs"))) {475        return failure();476      }477    }478    return success();479  }480 481  LogicalResult attributeCheckRescale(Operation *op) {482    if (auto rescale = dyn_cast<tosa::RescaleOp>(op)) {483      if (rescale.getRoundingMode() == RoundingMode::DOUBLE_ROUND &&484          !targetEnv.allows(Extension::doubleround)) {485        op->emitOpError()486            << "failed attribute check: rounding_mode = DOUBLE_ROUND "487            << "requires extension [doubleround]";488        return failure();489      }490      if (rescale.getRoundingMode() == RoundingMode::INEXACT_ROUND &&491          !targetEnv.allows(Extension::inexactround)) {492        op->emitOpError()493            << "failed attribute check: rounding_mode = INEXACT_ROUND "494            << "requires extension [inexactround]";495        return failure();496      }497    }498    return success();499  }500 501  LogicalResult CheckVariable(Operation *op);502  LogicalResult CheckVariableReadOrWrite(Operation *op);503  bool isValidElementType(Type type, const bool allowUnsigned = false);504 505  SmallVector<506      std::function<LogicalResult(Operation *, const tosa::TargetEnv &)>>507      constCheckers;508  DenseMap<StringAttr, mlir::Type> variablesMap;509  TosaProfileCompliance profileComp;510  tosa::TargetEnv targetEnv;511};512 513template <>514LogicalResult TosaValidation::levelCheckRanks(tosa::ArgMaxOp tosaOp) {515  auto *op = tosaOp.getOperation();516  if (failed(levelCheckRank(op, tosaOp.getInput(), "operand",517                            targetEnv.getLevel().MAX_RANK)))518    return failure();519 520  // rank(output) = rank(input) - 1521  if (failed(levelCheckRank(op, tosaOp.getOutput(), "result",522                            targetEnv.getLevel().MAX_RANK - 1)))523    return failure();524 525  return success();526}527 528template <>529LogicalResult TosaValidation::levelCheckRanks(tosa::IfOp tosaOp) {530  auto *op = tosaOp.getOperation();531 532  // Only the condition input has rank limitation.533  if (failed(levelCheckRank(op, tosaOp.getCondition(), "operand",534                            targetEnv.getLevel().MAX_RANK)))535    return failure();536 537  return success();538}539 540template <>541LogicalResult TosaValidation::levelCheckRanks(tosa::VariableOp tosaOp) {542  auto *op = tosaOp.getOperation();543  auto variableType = getVariableType(tosaOp);544  if (failed(levelCheckRank(op, variableType, "variable type",545                            targetEnv.getLevel().MAX_RANK)))546    return failure();547 548  return success();549}550 551template <>552LogicalResult TosaValidation::levelCheckSizes(tosa::VariableOp tosaOp) {553  auto *op = tosaOp.getOperation();554  auto variableType = getVariableType(tosaOp);555  if (failed(levelCheckSize(op, variableType, "variable type")))556    return failure();557 558  return success();559}560 561LogicalResult TosaValidation::levelCheckRanksAndSizes(Operation *op) {562#define CHECK_RANKS_AND_SIZES(tosaOp)                                          \563  if (isa<tosa::tosaOp##Op>(op)) {                                             \564    if (failed(levelCheckRanks(cast<tosa::tosaOp##Op>(op))))                   \565      return failure();                                                        \566    if (failed(levelCheckSizes(cast<tosa::tosaOp##Op>(op))))                   \567      return failure();                                                        \568  }569 570#define CHECK_SIZES(tosaOp)                                                    \571  if (isa<tosa::tosaOp##Op>(op)) {                                             \572    if (failed(levelCheckSizes(cast<tosa::tosaOp##Op>(op))))                   \573      return failure();                                                        \574  }575 576  // Tensor Operators577  CHECK_RANKS_AND_SIZES(ArgMax);578  // Activation Functions579  CHECK_RANKS_AND_SIZES(Clamp);580  CHECK_RANKS_AND_SIZES(Erf);581  CHECK_RANKS_AND_SIZES(Sigmoid);582  CHECK_RANKS_AND_SIZES(Tanh);583  // Elementwise Binary Operators584  CHECK_RANKS_AND_SIZES(Add);585  CHECK_RANKS_AND_SIZES(ArithmeticRightShift);586  CHECK_RANKS_AND_SIZES(BitwiseAnd);587  CHECK_RANKS_AND_SIZES(BitwiseOr);588  CHECK_RANKS_AND_SIZES(BitwiseXor);589  CHECK_RANKS_AND_SIZES(IntDiv);590  CHECK_RANKS_AND_SIZES(LogicalAnd);591  CHECK_RANKS_AND_SIZES(LogicalLeftShift);592  CHECK_RANKS_AND_SIZES(LogicalRightShift);593  CHECK_RANKS_AND_SIZES(LogicalOr);594  CHECK_RANKS_AND_SIZES(LogicalXor);595  CHECK_RANKS_AND_SIZES(Maximum);596  CHECK_RANKS_AND_SIZES(Minimum);597  CHECK_RANKS_AND_SIZES(Mul);598  CHECK_RANKS_AND_SIZES(Pow);599  CHECK_RANKS_AND_SIZES(Sub);600  CHECK_RANKS_AND_SIZES(Table);601  // Elementwise Unary Operators602  CHECK_RANKS_AND_SIZES(Abs);603  CHECK_RANKS_AND_SIZES(BitwiseNot);604  CHECK_RANKS_AND_SIZES(Ceil);605  CHECK_RANKS_AND_SIZES(Clz);606  CHECK_RANKS_AND_SIZES(Cos);607  CHECK_RANKS_AND_SIZES(Exp);608  CHECK_RANKS_AND_SIZES(Floor);609  CHECK_RANKS_AND_SIZES(Log);610  CHECK_RANKS_AND_SIZES(LogicalNot);611  CHECK_RANKS_AND_SIZES(Negate);612  CHECK_RANKS_AND_SIZES(Reciprocal);613  CHECK_RANKS_AND_SIZES(Rsqrt);614  CHECK_RANKS_AND_SIZES(Sin);615  // Elementwise Ternary Operators616  CHECK_RANKS_AND_SIZES(Select);617  // Comparison Operators618  CHECK_RANKS_AND_SIZES(Equal);619  CHECK_RANKS_AND_SIZES(Greater);620  CHECK_RANKS_AND_SIZES(GreaterEqual);621  // Reduction Operators622  CHECK_RANKS_AND_SIZES(ReduceAll);623  CHECK_RANKS_AND_SIZES(ReduceAny);624  CHECK_RANKS_AND_SIZES(ReduceMax);625  CHECK_RANKS_AND_SIZES(ReduceMin);626  CHECK_RANKS_AND_SIZES(ReduceProduct);627  CHECK_RANKS_AND_SIZES(ReduceSum);628  // Data Layout Operators629  CHECK_RANKS_AND_SIZES(Concat);630  CHECK_RANKS_AND_SIZES(Pad);631  CHECK_RANKS_AND_SIZES(Reshape);632  CHECK_RANKS_AND_SIZES(Reverse);633  CHECK_RANKS_AND_SIZES(Slice);634  CHECK_RANKS_AND_SIZES(Tile);635  CHECK_RANKS_AND_SIZES(Transpose);636  // Type Conversion637  CHECK_RANKS_AND_SIZES(Cast);638  CHECK_RANKS_AND_SIZES(CastFromBlockScaled);639  CHECK_RANKS_AND_SIZES(CastToBlockScaled);640  CHECK_RANKS_AND_SIZES(Rescale);641  // Control Flow Operators642  CHECK_RANKS_AND_SIZES(If);643  // Variable Operators644  CHECK_RANKS_AND_SIZES(Variable);645  CHECK_RANKS_AND_SIZES(VariableWrite);646  CHECK_RANKS_AND_SIZES(VariableRead);647  // Data Nodes648  CHECK_RANKS_AND_SIZES(Const);649  CHECK_RANKS_AND_SIZES(Identity);650 651  // For the following operators, check whether the size of each tensor652  // operand is valid in a given Level.653 654  // Tensor Operators655  CHECK_SIZES(AvgPool2d);656  CHECK_SIZES(Conv2D);657  CHECK_SIZES(Conv3D);658  CHECK_SIZES(DepthwiseConv2D);659  CHECK_SIZES(TransposeConv2D);660  CHECK_SIZES(FFT2d);661  CHECK_SIZES(MatMul);662  CHECK_SIZES(MatmulTBlockScaled);663  CHECK_SIZES(MaxPool2d);664  CHECK_SIZES(RFFT2d);665  // Scatter/Gather Operators666  CHECK_SIZES(Gather);667  CHECK_SIZES(Scatter);668  // Image Operators669  CHECK_SIZES(Resize);670  // Custom Operators671  CHECK_SIZES(Custom);672  // Control Flow Operators673  CHECK_SIZES(While);674  // Shape Operators675  CHECK_SIZES(ConstShape);676 677#undef CHECK_RANKS_AND_SIZES678#undef CHECK_SIZES679  return success();680}681 682// Perform the Level tensor size check on the tensor type.683LogicalResult TosaValidation::levelCheckSize(Operation *op,684                                             const Type &typeToCheck,685                                             const StringRef operandOrResult) {686  if (ShapedType type = dyn_cast<ShapedType>(typeToCheck)) {687    if (!type.hasRank())688      return op->emitOpError() << "failed level check: unranked tensor";689    auto shape = type.getShape();690    for (auto dim : shape) {691      if (mlir::ShapedType::isDynamic(dim))692        return op->emitOpError() << "failed level check: " << operandOrResult693                                 << " shape dimension cannot be dynamic";694    }695 696    int64_t element_bits = tosa::getBitWidth(getElementTypeOrSelf(type));697    int64_t element_bytes = std::max(INT64_C(1), element_bits / 8);698    int64_t size = element_bytes * type.getNumElements();699 700    // According to 1.11. Tensor Definitions of Tosa spec, the value of701    // tensor_size_t is 1 << MAX_LOG2_SIZE) - 1 where MAX_LOG2_SIZE is702    // defined in 1.7. Levels.703    // For each tensor, the number of tensor elements multiplied by the704    // element size in bytes must be representable as a tensor_size_t.705    const int64_t max_size =706        (INT64_C(1) << targetEnv.getLevel().MAX_LOG2_SIZE) - 1;707    if (size > max_size)708      return op->emitOpError()709             << "failed level check: " << operandOrResult710             << " tensor size (in bytes) <= (1 << MAX_LOG2_SIZE - 1)";711  }712  return success();713}714 715LogicalResult TosaValidation::applyLevelCheck(Operation *op) {716  if (targetEnv.getLevel() == TOSA_LEVEL_NONE) {717    // no need to do level checks718    return success();719  }720 721  // check rank and sizes early so later checks can assume shaped operands722  if (failed(levelCheckRanksAndSizes(op)))723    return failure();724 725  // additional level checks from spec 0.70726  if (failed(levelCheckPool<tosa::AvgPool2dOp>(op)) ||727      failed(levelCheckConv<tosa::Conv2DOp>(op)) ||728      failed(levelCheckConv<tosa::Conv3DOp>(op)) ||729      failed(levelCheckConv<tosa::DepthwiseConv2DOp>(op)) ||730      failed(levelCheckFFT<tosa::FFT2dOp>(op)) ||731      failed(levelCheckPool<tosa::MaxPool2dOp>(op)) ||732      failed(levelCheckFFT<tosa::RFFT2dOp>(op)) ||733      failed(levelCheckTransposeConv2d(op)) || failed(levelCheckResize(op))) {734    return failure();735  }736 737  // level check MAX_TENSOR_LIST_SIZE738  if (failed(levelCheckListSize(op))) {739    return failure();740  }741 742  if (isa<tosa::IfOp>(op) || isa<tosa::WhileOp>(op)) {743    if (failed(levelCheckMaxNesting(op))) {744      return failure();745    }746  }747 748  return success();749}750 751LogicalResult TosaValidation::applyAttributeCheck(Operation *op) {752  if (failed(attributeCheckRescale(op)))753    return failure();754  return success();755}756 757inline bool CompatibleTypes(const mlir::Type &type,758                            const mlir::Type &declaredType) {759  // for now, simply use type equality comparison760  return type == declaredType;761}762 763LogicalResult TosaValidation::CheckVariable(Operation *op) {764  if (auto variableOp = dyn_cast<mlir::tosa::VariableOp>(op)) {765    mlir::StringAttr nameAttr = variableOp.getNameAttr();766 767    if (variablesMap.count(nameAttr))768      return op->emitOpError() << "name has already been declared";769 770    auto elementType = variableOp.getType();771    DenseIntElementsAttr varShapeAttr = variableOp.getVarShape();772    SmallVector<int64_t> shape = to_vector(varShapeAttr.getValues<int64_t>());773    RankedTensorType variableType =774        RankedTensorType::get(ArrayRef<int64_t>(shape), elementType);775 776    variablesMap[nameAttr] = variableType;777  }778 779  return success();780}781 782LogicalResult TosaValidation::CheckVariableReadOrWrite(Operation *op) {783  if (isa<mlir::tosa::VariableReadOp>(op) ||784      isa<mlir::tosa::VariableWriteOp>(op)) {785    mlir::StringAttr nameAttr = cast<mlir::StringAttr>(op->getAttr("name"));786    if (!variablesMap.count(nameAttr))787      return op->emitOpError() << "name has not been declared";788 789    auto varType = variablesMap[nameAttr];790 791    for (auto v : op->getOperands()) {792      auto type = v.getType();793      if (!CompatibleTypes(type, varType))794        return op->emitOpError() << "operand type does not equal variable type";795    }796 797    for (auto v : op->getResults()) {798      auto type = v.getType();799      if (!CompatibleTypes(type, varType))800        return op->emitOpError() << "result type does not equal variable type";801    }802  }803 804  return success();805}806 807LogicalResult TosaValidation::applyVariableCheck(Operation *op) {808  if (failed(CheckVariable(op)) || failed(CheckVariableReadOrWrite(op)))809    return failure();810  return success();811}812 813LogicalResult checkErrorIfResize(Operation *op) {814  auto resize = dyn_cast<tosa::ResizeOp>(op);815  if (!resize)816    return success();817 818  const Value input = resize.getInput();819  const Value output = resize.getOutput();820  const RankedTensorType inputType =821      llvm::dyn_cast<RankedTensorType>(input.getType());822  const RankedTensorType outputType =823      llvm::dyn_cast<RankedTensorType>(output.getType());824 825  if (!inputType || !outputType)826    return op->emitOpError("expect ranked input/output tensor");827 828  // Ensure the image size is supported by GPU APIs and that for integer829  // implementations, position * stride does not overflow int32_t.830  if (inputType.hasStaticShape() && outputType.hasStaticShape()) {831    const SmallVector<int64_t, 4> sizes = {832        outputType.getDimSize(1), outputType.getDimSize(2),833        inputType.getDimSize(1), inputType.getDimSize(2)};834    const int64_t *maxDim = llvm::max_element(sizes);835    if (maxDim != sizes.end() && *maxDim >= 16384)836      return op->emitOpError(837                 "expect input/output height/width dims to be < 16384, ")838             << "got [OH, OW, IH, IW] = " << sizes;839  }840 841  SmallVector<int64_t> scale;842  if (!tosa::getConstShapeValues(resize.getScale().getDefiningOp(), scale))843    return failure();844 845  const int64_t scaleYN = scale[0];846  const int64_t scaleYD = scale[1];847  const int64_t scaleXN = scale[2];848  const int64_t scaleXD = scale[3];849 850  // Ensure scale values don't overflow int32 accumulator851  if (scaleYN > (1 << 11) || scaleXN > (1 << 11))852    return op->emitOpError(853               "expect all scale numerator values to be <= (1 << 11), "854               "got scale_y_n=")855           << scaleYN << ", scale_x_n=" << scaleXN;856 857  if (scaleYD >= 16 * scaleYN || scaleXD >= 16 * scaleXN)858    return op->emitOpError("expect a downscale ratio larger than 1/16, got y=")859           << scaleYN << "/" << scaleYD << ", x=" << scaleXN << "/" << scaleXD;860 861  SmallVector<int64_t> offset;862  SmallVector<int64_t> border;863  if (!tosa::getConstShapeValues(resize.getOffset().getDefiningOp(), offset) ||864      !tosa::getConstShapeValues(resize.getBorder().getDefiningOp(), border))865    return failure();866 867  const int64_t offsetY = offset[0];868  const int64_t offsetX = offset[1];869  // Set a consistent lower limit of 1/16 downscale to simplify870  // implementations871  if (offsetY < -scaleYN || offsetY >= 16 * scaleYN)872    return op->emitOpError(873               "expect offsetY / scaleYNumerator to be in range [-1, 16), got ")874           << offsetY << "/" << scaleYN;875  if (offsetX < -scaleXN || offsetX >= 16 * scaleXN)876    return op->emitOpError(877               "expect offsetX / scaleXNumerator to be in range [-1, 16), got ")878           << offsetX << "/" << scaleXN;879 880  const int64_t borderY = border[0];881  const int64_t borderX = border[1];882  if (borderY < -16 * scaleYN || borderY >= scaleYN)883    return op->emitOpError(884               "expect borderY / scaleYNumerator to be in range [-16, 1), got ")885           << borderY << "/" << scaleYN;886  if (borderX < -16 * scaleXN || borderX >= scaleXN)887    return op->emitOpError(888               "expect borderX / scaleXNumerator to be in range [-16, 1), got ")889           << borderX << "/" << scaleXN;890 891  // The following section of code is mostly duplicated with ResizeOp::verify().892  //893  // In TOSA specification, we do not support broadcast behavior.894  // However, there is a rewrite pattern to materialize broadcast ResizeOp.895  // It makes invalid TOSA ResizeOp into valid one. To avoid breaking896  // existing code, we keep the rewrite pattern untouched. So, we need897  // loose the checking in ResizeOp::verify() to support broadcast ResizeOp.898  //899  // Here is a strict checking to conform TOSA specification.900  // FIXME: Remove the duplicated checkings when broadcast ResizeOp is removed.901  auto idivCheck = [](const int64_t lhs,902                      const int64_t rhs) -> std::optional<int64_t> {903    if (lhs % rhs != 0)904      return std::nullopt;905    return lhs / rhs;906  };907 908  const int64_t oh = outputType.getDimSize(1);909  const int64_t ow = outputType.getDimSize(2);910  const int64_t ih = inputType.getDimSize(1);911  const int64_t iw = inputType.getDimSize(2);912 913  if (ih != ShapedType::kDynamic) {914    const std::optional<int64_t> calculatedOutHeightMinusOne =915        idivCheck((ih - 1) * scaleYN - offsetY + borderY, scaleYD);916    if (!calculatedOutHeightMinusOne.has_value())917      return op->emitOpError(918                 "expected (input_height - 1) * scale_y_n - offset_y + "919                 "border_y ")920             << "to be wholly divisible by scale_y_d, got ((" << ih921             << " - 1) * " << scaleYN << " - " << offsetY << " + " << borderY922             << ") / " << scaleYD;923    const int64_t calculatedOutHeight = calculatedOutHeightMinusOne.value() + 1;924    if (oh != ShapedType::kDynamic && calculatedOutHeight != oh)925      return op->emitOpError(926                 "calculated output height did not match expected: ")927             << "calculated=" << calculatedOutHeight << ", expected=" << oh;928  }929 930  if (iw != ShapedType::kDynamic) {931    const std::optional<int64_t> calculatedOutWidthMinusOne =932        idivCheck((iw - 1) * scaleXN - offsetX + borderX, scaleXD);933    if (!calculatedOutWidthMinusOne.has_value())934      return op->emitOpError(935                 "expected (input_width - 1) * scale_x_n - offset_x + "936                 "border_x ")937             << "to be wholly divisible by scale_x_d, got ((" << iw938             << " - 1) * " << scaleXN << " - " << offsetX << " + " << borderX939             << ") / " << scaleXD;940    const int64_t calculatedOutWidth = calculatedOutWidthMinusOne.value() + 1;941    if (ow != ShapedType::kDynamic && calculatedOutWidth != ow)942      return op->emitOpError("calculated output width did not match expected: ")943             << "calculated=" << calculatedOutWidth << ", expected=" << ow;944  }945 946  return success();947}948 949LogicalResult checkErrorIfMul(Operation *op) {950  auto mul = dyn_cast<tosa::MulOp>(op);951  if (!mul)952    return success();953 954  // REQUIRE(0 <= shift && shift <= 63);955  // REQUIRE(is_same<in_t,int32_t>() || shift == 0);956  ElementsAttr shift_elem;957  if (!matchPattern(mul.getShift(), m_Constant(&shift_elem)))958    return success();959  int32_t shift = shift_elem.getValues<IntegerAttr>()[0].getInt();960  auto inputElemType = getElementTypeOrSelf(mul.getInput1());961  if (inputElemType.isInteger(32)) {962    // 0 <= shift <= 63 for int32_t type963    if (shift < 0 || shift > 63)964      return op->emitOpError()965             << "requires 0 <= shift && shift <= 63, but got: " << shift;966  } else {967    // shift must be 0 for all other types968    if (shift != 0)969      return op->emitOpError()970             << "requires shift = 0 for all input data types that "971                "are not int32_t, but got: "972             << shift;973  }974 975  return success();976}977 978LogicalResult checkErrorIfTable(Operation *op) {979  auto table = dyn_cast<tosa::TableOp>(op);980  if (!table)981    return success();982 983  // REQUIRE(length(table) == TABLE_SIZE) where TABLE_SIZE is 256 or 513984  const auto inputElemType = getElementTypeOrSelf(table.getInput1().getType());985  const int tableSize = inputElemType.isInteger(8) ? 256 : 513;986 987  const ShapeAdaptor tableShape(table.getTable().getType());988  if (tableShape.hasStaticShape()) {989    const auto numElements = tableShape.getNumElements();990    if (numElements != tableSize)991      return op->emitOpError() << "requires table size of " << tableSize992                               << ", got " << numElements;993  }994 995  return success();996}997 998LogicalResult checkErrorIfRescale(Operation *op) {999  auto rescale = dyn_cast<tosa::RescaleOp>(op);1000  if (!rescale)1001    return success();1002 1003  auto inputType = llvm::dyn_cast<ShapedType>(rescale.getInput().getType());1004  auto outputType = llvm::dyn_cast<ShapedType>(rescale.getOutput().getType());1005  if (!inputType || !outputType || !inputType.getElementType().isInteger() ||1006      !outputType.getElementType().isInteger())1007    return success();1008 1009  auto inElemType = inputType.getElementType();1010  auto outElemType = outputType.getElementType();1011  auto inWidth = inElemType.getIntOrFloatBitWidth();1012  auto outWidth = outElemType.getIntOrFloatBitWidth();1013 1014  bool inputUnsigned = rescale.getInputUnsigned();1015  bool outputUnsigned = rescale.getOutputUnsigned();1016 1017  bool scale32 = rescale.getScale32();1018  auto roundingMode = rescale.getRoundingMode();1019 1020  // ERROR_IF(scale32 && is_same<in_t,i48_t>())1021  if (scale32 && inWidth == 48)1022    return op->emitOpError() << "scale32 is not allowed with 48-bit input.";1023 1024  // ERROR_IF(!scale32 && (rounding_mode == DOUBLE_ROUND))1025  if (!scale32 && roundingMode == RoundingMode::DOUBLE_ROUND)1026    return op->emitOpError()1027           << "DOUBLE_ROUND is only allowed with scale32=true.";1028 1029  // ERROR_IF(input_unsigned && output_unsigned)1030  if (inputUnsigned && outputUnsigned)1031    return op->emitOpError() << "input and output cannot be both unsigned.";1032 1033  // ERROR_IF(is_same<out_t,i32_t>() && input_unsigned)1034  if (outWidth == 32 && inputUnsigned)1035    return op->emitOpError()1036           << "i32 output type is not allowed with unsigned input.";1037 1038  // ERROR_IF(is_same<in_t,i32_t>() && output_unsigned)1039  if (inWidth == 32 && outputUnsigned)1040    return op->emitOpError()1041           << "i32 input type is not allowed with unsigned output.";1042 1043  // ERROR_IF(is_same<in_t,i48_t>() && output_unsigned)1044  if (inWidth == 48 && outputUnsigned)1045    return op->emitOpError()1046           << "i48 input type is not allowed with unsigned output.";1047 1048  // ERROR_IF(is_same<in_t, i48_t> && input_unsigned)1049  if (inWidth == 48 && inputUnsigned)1050    return op->emitOpError() << "i48 input type cannot be unsigned.";1051 1052  // ERROR_IF(is_same<in_t, i32_t> && input_unsigned)1053  if (inWidth == 32 && inputUnsigned)1054    return op->emitOpError() << "i32 input type cannot be unsigned.";1055 1056  // ERROR_IF(is_same<out_t, i32_t> && output_unsigned)1057  if (outWidth == 32 && outputUnsigned)1058    return op->emitOpError() << "i32 output type cannot be unsigned.";1059 1060  return success();1061}1062 1063LogicalResult checkErrorIfPad(Operation *op) {1064  auto pad = dyn_cast<tosa::PadOp>(op);1065  if (!pad)1066    return success();1067 1068  DenseIntElementsAttr paddingAttr;1069  if (!matchPattern(pad.getPadding(), m_Constant(&paddingAttr)))1070    // Pad verifier will catch this1071    return success();1072 1073  for (const APInt &val : paddingAttr.getValues<APInt>()) {1074    if (val.getSExtValue() < 0)1075      return op->emitOpError() << "padding value must all be non-negative, got "1076                               << val.getSExtValue();1077  }1078 1079  return success();1080}1081 1082static bool isOpIsolatedWithinRegion(Operation *op, Region *region) {1083  return llvm::all_of(op->getOperands(), [&](auto operand) {1084    Region *operandRegion = operand.getParentRegion();1085    return operandRegion && region->isAncestor(operandRegion);1086  });1087}1088 1089static LogicalResult isRegionIsolatedFromAbove(Region &regionToCheck) {1090  bool noLiveInValue = true;1091  regionToCheck.walk([&noLiveInValue, &regionToCheck](Operation *op) {1092    if (!isOpIsolatedWithinRegion(op, &regionToCheck)) {1093      noLiveInValue = false;1094      return WalkResult::interrupt();1095    }1096    return WalkResult::advance();1097  });1098  return noLiveInValue ? success() : failure();1099}1100 1101LogicalResult checkIsolatedRegion(Operation *op, Region &regionToCheck,1102                                  StringRef regionName) {1103  if (succeeded(isRegionIsolatedFromAbove(regionToCheck)))1104    return success();1105  return op->emitOpError()1106         << "is not conformant to the TOSA specification. It requires the '"1107         << regionName << "' region is isolated from above.\n";1108}1109 1110LogicalResult checkErrorIfCondIf(Operation *op) {1111  auto ifOp = dyn_cast<tosa::IfOp>(op);1112  if (!ifOp)1113    return success();1114 1115  // Currently the dialect supports declaring cond_if operations that1116  // have then/else regions that reference values from outside these1117  // regions. According to the specification, all values used by the1118  // then/else regions must be explicitly declared within the regions.1119  // Therefore we must check that the then/else regions are1120  // "isolated from above", in order to be conformant to the1121  // specification.1122  //1123  // Note: the dialect currently supports two styles of syntax for1124  // declaring "cond_if" operations. We'll refer to these as follows:1125  //1126  // Generic:1127  // %0 = "tosa.cond_if"(%arg0, %arg1, %arg2) ({1128  //   ^bb0(%arg3, %arg4):1129  //     tosa.yield %arg31130  // },  {1131  //   ^bb0(%arg3, %arg4):1132  //     tosa.yield %arg41133  // })1134  //1135  // Simplified:1136  // %0 = tosa.cond_if %arg2 (%arg3 = %arg0, %arg4 = %arg1) {1137  //   ^bb0(%arg3, %arg4):1138  //   tosa.yield %arg31139  // } else {1140  //   ^bb0(%arg3, %arg4):1141  //   tosa.yield %arg41142  // }1143 1144  if (failed(checkIsolatedRegion(op, ifOp.getThenGraph(), "then")) ||1145      failed(checkIsolatedRegion(op, ifOp.getElseGraph(), "else")))1146    return failure();1147  return success();1148}1149 1150LogicalResult checkErrorIfWhileLoop(Operation *op) {1151  auto whileOp = dyn_cast<tosa::WhileOp>(op);1152  if (!whileOp)1153    return success();1154 1155  if (failed(checkIsolatedRegion(op, whileOp.getCondGraph(), "cond")) ||1156      failed(checkIsolatedRegion(op, whileOp.getBodyGraph(), "body")))1157    return failure();1158  return success();1159}1160 1161LogicalResult checkErrorIfScatter(Operation *op) {1162  auto scatterOp = dyn_cast<tosa::ScatterOp>(op);1163  if (!scatterOp)1164    return success();1165 1166  // for constant indices, check that there are no duplicate values1167  DenseIntElementsAttr indicesAttr;1168  if (!matchPattern(scatterOp.getIndices(), m_Constant(&indicesAttr)))1169    return success();1170 1171  auto const indicesType =1172      dyn_cast<ShapedType>(scatterOp.getIndices().getType());1173  if (!indicesType || !indicesType.hasRank()) {1174    op->emitOpError("expect ranked indices tensor");1175    return failure();1176  }1177 1178  if (!hasUniqueConstantScatterIndices(indicesType, indicesAttr)) {1179    op->emitOpError("indices values contain duplicates");1180    return failure();1181  }1182 1183  return success();1184}1185 1186LogicalResult TosaValidation::applyErrorIfCheck(Operation *op) {1187  if (failed(checkErrorIfResize(op)) || failed(checkErrorIfMul(op)) ||1188      failed(checkErrorIfTable(op)) || failed(checkErrorIfRescale(op)) ||1189      failed(checkErrorIfPad(op)) || failed(checkErrorIfCondIf(op)) ||1190      failed(checkErrorIfWhileLoop(op)) || failed(checkErrorIfScatter(op)))1191    return failure();1192  return success();1193}1194 1195bool TosaValidation::isValidElementType(Type type, const bool allowUnsigned) {1196  if (isa<FloatType>(type)) {1197    return isa<Float32Type, Float16Type, BFloat16Type, Float8E4M3FNType,1198               Float8E5M2Type, Float4E2M1FNType, Float6E2M3FNType,1199               Float6E3M2FNType, Float8E8M0FNUType>(type);1200  } else if (auto intTy = dyn_cast<IntegerType>(type)) {1201    if (intTy.isSignless()) {1202      switch (intTy.getWidth()) {1203      case 1:1204      case 4:1205      case 8:1206      case 16:1207      case 32:1208      case 48:1209      case 64:1210        return true;1211      }1212    } else if (allowUnsigned && intTy.isUnsigned()) {1213      switch (intTy.getWidth()) {1214      case 8:1215      case 16:1216      case 32:1217        return true;1218      }1219    }1220  } else if (isa<tosa::shapeType>(type))1221    return true;1222  else if (isa<tosa::mxint8Type>(type))1223    return true;1224  return false;1225}1226 1227void TosaValidation::runOnOperation() {1228  ModuleOp modOp = getOperation();1229  const TargetEnvAttr targetEnvAttr = lookupTargetEnvOrDefault(modOp);1230  const auto maybeTargetEnv =1231      tosa::TargetEnv::createTargetEnvFromAttr(targetEnvAttr, modOp.getLoc());1232  if (failed(maybeTargetEnv))1233    return signalPassFailure();1234  targetEnv = *maybeTargetEnv;1235 1236  TosaDialect *tosaDialect = getContext().getLoadedDialect<TosaDialect>();1237  if (!tosaDialect)1238    return;1239 1240  modOp.walk([&](Operation *op) {1241    if (op->getDialect() != tosaDialect)1242      return;1243 1244    // validate operator element types:1245    // - rescale operator is allowed to have ui8/ui16/ui321246    //   operands/results when strictOpSpecAlignment is false1247    // - perform valid element type check at the beginning to1248    //   protect rest of code against quantized element types1249    const bool allowUnsigned =1250        !strictOpSpecAlignment && isa<tosa::RescaleOp>(op);1251    for (Value operand : op->getOperands()) {1252      auto elementTy = getElementTypeOrSelf(operand);1253      if (!isValidElementType(elementTy, allowUnsigned)) {1254        op->emitOpError() << "is not profile-aligned: element type "1255                          << elementTy << " is not legal";1256        return signalPassFailure();1257      }1258    }1259    for (Type resultTy : op->getResultTypes()) {1260      auto elementTy = getElementTypeOrSelf(resultTy);1261      if (!isValidElementType(elementTy, allowUnsigned)) {1262        op->emitOpError() << "is not profile-aligned: element type "1263                          << elementTy << " is not legal";1264        return signalPassFailure();1265      }1266    }1267 1268    if (strictOpSpecAlignment &&1269        failed(profileComp.checkProfile(op, targetEnv)))1270      return signalPassFailure();1271 1272    if (strictOpSpecAlignment &&1273        failed(profileComp.checkExtension(op, targetEnv)))1274      return signalPassFailure();1275 1276    if (!allowInvalidOpDatatypeCombinations &&1277        failed(profileComp.checkInvalid(op)))1278      return signalPassFailure();1279 1280    // Some uses of TOSA rely on the constant operands of particular1281    // operations.1282    if (failed(applyConstantOperandCheck(op)))1283      signalPassFailure();1284 1285    // do level checks1286    if (failed(applyLevelCheck(op)))1287      signalPassFailure();1288 1289    // check additional attribute restrictions1290    if (failed(applyAttributeCheck(op)))1291      signalPassFailure();1292 1293    // do variable type checks1294    if (failed(applyVariableCheck(op)))1295      signalPassFailure();1296 1297    // do error if checks1298    if (strictOpSpecAlignment && failed(applyErrorIfCheck(op)))1299      signalPassFailure();1300  });1301}1302} // namespace1303