blob: eefa70a14d0e0e134042db14742b93228b447381 [file]
// Copyright 2023 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/compiler/Codegen/LLVMCPU/Utils.h"
#include "iree/compiler/Codegen/Utils/Utils.h"
#include "llvm/ADT/STLExtras.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/Dialect/Vector/IR/VectorOps.h"
#define DEBUG_TYPE "iree-llvmcpu-utils"
namespace mlir::iree_compiler {
bool preferIntrinsicsOverAsm(IREE::HAL::ExecutableTargetAttr targetAttr) {
auto intrinsicsAttr =
getConfigBoolAttr(targetAttr, "prefer_intrinsics_over_asm");
return intrinsicsAttr && intrinsicsAttr->getValue();
}
bool hasAVX2Feature(IREE::HAL::ExecutableTargetAttr targetAttr) {
return hasFeature(targetAttr, "+avx2");
}
bool hasAVX512fFeature(IREE::HAL::ExecutableTargetAttr targetAttr) {
return hasFeature(targetAttr, "+avx512f");
}
bool hasVFeature(IREE::HAL::ExecutableTargetAttr targetAttr) {
return hasFeature(targetAttr, "+v");
}
bool hasZve32xFeature(IREE::HAL::ExecutableTargetAttr targetAttr) {
return hasFeature(targetAttr, "+zve32x");
}
bool hasZve32fFeature(IREE::HAL::ExecutableTargetAttr targetAttr) {
return hasFeature(targetAttr, "+zve32f");
}
bool hasZve64xFeature(IREE::HAL::ExecutableTargetAttr targetAttr) {
return hasFeature(targetAttr, "+zve64x");
}
bool hasAnyVFeature(IREE::HAL::ExecutableTargetAttr targetAttr) {
return hasVFeature(targetAttr) || hasZve32xFeature(targetAttr) ||
hasZve32fFeature(targetAttr) || hasZve64xFeature(targetAttr) ||
hasFeature(targetAttr, "+zve64f") || hasFeature(targetAttr, "+zve64d");
}
bool hasAnySVEFeature(IREE::HAL::ExecutableTargetAttr targetAttr) {
return hasFeature(targetAttr, "+sve") || hasFeature(targetAttr, "+sve2") ||
hasFeature(targetAttr, "+v9a");
}
bool hasSMEFeature(IREE::HAL::ExecutableTargetAttr targetAttr) {
return hasFeature(targetAttr, "+sme");
}
bool hasI8mmFeature(IREE::HAL::ExecutableTargetAttr targetAttr) {
return hasFeature(targetAttr, "+i8mm");
}
bool isLinalgGeneric2DTranspose(linalg::GenericOp genericOp) {
// Check op has 2 dimensions.
if (genericOp.getNumLoops() != 2)
return false;
// Check op has single input and output.
if (genericOp.getNumDpsInputs() != 1 || genericOp.getNumDpsInits() != 1)
return false;
// Check all iterators are parallel.
if (genericOp.getNumParallelLoops() != genericOp.getNumLoops())
return false;
// Check that the two indexing maps are a permutation of each other.
SmallVector<AffineMap> indexingMaps = genericOp.getIndexingMapsArray();
bool isTranspose =
(indexingMaps[0].isPermutation() && indexingMaps[1].isIdentity()) ||
(indexingMaps[1].isPermutation() && indexingMaps[0].isIdentity());
if (!isTranspose)
return false;
// Make sure the region only contains a yield op.
Block &body = genericOp.getRegion().front();
if (!llvm::hasSingleElement(body))
return false;
auto yieldOp = cast<linalg::YieldOp>(body.getTerminator());
// The yield op should return the block argument corresponding to the input.
auto yieldArg = dyn_cast<BlockArgument>(yieldOp.getValues()[0]);
if (!yieldArg || yieldArg.getArgNumber() != 0 || yieldArg.getOwner() != &body)
return false;
return true;
}
bool mayHaveUndefinedBehaviorInMasking(Operation *op) {
// Those operations will be lowered to division or related instructions,
// and they might result in divide-by-zero.
if (isa<mlir::arith::RemSIOp, mlir::arith::RemUIOp, mlir::arith::DivSIOp,
mlir::arith::DivUIOp, mlir::arith::CeilDivSIOp,
mlir::arith::CeilDivUIOp, mlir::arith::FloorDivSIOp,
mlir::arith::DivFOp, mlir::arith::RemFOp>(op)) {
return true;
}
return false;
}
} // namespace mlir::iree_compiler