Files
tensorflow--tensorflow/tensorflow/dtensor/mlir/op_utils.cc
T
wehub-resource-sync 8a852e4b4e
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:14:16 +08:00

167 lines
6.1 KiB
C++

/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/dtensor/mlir/op_utils.h"
#include <cstdint>
#include <optional>
#include <string>
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
#include "mlir/Dialect/Func/IR/FuncOps.h" // from @llvm-project
#include "mlir/IR/Builders.h" // from @llvm-project
#include "mlir/IR/BuiltinAttributes.h" // from @llvm-project
#include "mlir/IR/BuiltinOps.h" // from @llvm-project
#include "mlir/IR/Operation.h" // from @llvm-project
#include "mlir/IR/OperationSupport.h" // from @llvm-project
#include "mlir/IR/SymbolTable.h" // from @llvm-project
#include "mlir/IR/Value.h" // from @llvm-project
#include "mlir/Interfaces/CallInterfaces.h" // from @llvm-project
#include "mlir/Support/LogicalResult.h" // from @llvm-project
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_ops.h"
#include "tensorflow/dtensor/cc/constants.h"
#include "tensorflow/dtensor/mlir/ir/tf_dtensor.h"
namespace tensorflow {
namespace dtensor {
// OpHash prints the `op` into a string and performs hash value on the output
// string.
//
// The `print` includes the full representation of the `op`, e.g., target
// register, layout, shape, etc. This should be sufficient to uniquely
// identify the operation in most cases. This does not capture function scope
// (identical op in 2 separate functions).
uint64_t OpHash(mlir::Operation* op) {
std::string output;
llvm::raw_string_ostream output_stream(output);
mlir::OpPrintingFlags flags;
flags.elideLargeElementsAttrs(1024);
op->print(output_stream, flags);
return llvm::hash_value(output);
}
// Returns FuncOp if `op` is a callable.
std::optional<mlir::func::FuncOp> MaybeFindFunction(mlir::Operation* op) {
auto call_op = llvm::dyn_cast<mlir::CallOpInterface>(op);
if (!call_op) return std::nullopt;
mlir::CallInterfaceCallable callable = call_op.getCallableForCallee();
mlir::SymbolRefAttr sym = callable.dyn_cast<mlir::SymbolRefAttr>();
if (!sym) return std::nullopt;
mlir::func::FuncOp func = llvm::dyn_cast<mlir::func::FuncOp>(
mlir::SymbolTable::lookupNearestSymbolFrom(op, sym));
if (!func) return std::nullopt;
return func;
}
void RemoveDTensorLayoutOps(mlir::ModuleOp module,
bool remove_xla_spmd_layouts) {
llvm::SmallVector<mlir::TF::DTensorLayout, 4> layout_ops;
module.walk([&](mlir::TF::DTensorLayout layout) {
// Remove layout ops only for layouts running on DTensor SPMD.
// Layout ops will be preserved for XLA SPMD to annotate sharding
// later down the DTensor stack.
if (remove_xla_spmd_layouts || !layout.getLayout().mesh().use_xla_spmd()) {
layout_ops.emplace_back(layout);
}
});
for (auto layout_op : layout_ops) {
layout_op.getOutput().replaceAllUsesWith(layout_op.getInput());
layout_op.erase();
}
}
mlir::LogicalResult ReplaceAuxiliaryDTensorLayoutOpsWithIdentity(
mlir::ModuleOp module) {
llvm::SmallVector<mlir::TF::DTensorLayout, 4> layout_ops;
module.walk([&](mlir::TF::DTensorLayout op) { layout_ops.emplace_back(op); });
llvm::DenseSet<mlir::TF::DTensorLayout> deleted_layout_ops;
for (auto layout_op : llvm::reverse(layout_ops)) {
if (deleted_layout_ops.contains(layout_op)) {
continue;
}
while (auto input_layout_op =
llvm::dyn_cast_or_null<mlir::TF::DTensorLayout>(
layout_op.getInput().getDefiningOp())) {
// Check that layout of input DTensorLayout op is equivalent to
// the layout of its connected DTensorLayout op.
if (layout_op.getLayout() != input_layout_op.getLayout()) {
return layout_op.emitOpError(
"Found inconsistent layout. This should never happen.");
}
// Replace DTensorLayout op with identity op.
mlir::OpBuilder builder(input_layout_op);
auto new_identity = mlir::TF::IdentityOp::create(
builder, input_layout_op->getLoc(), input_layout_op.getType(),
input_layout_op.getInput());
input_layout_op.getOutput().replaceAllUsesWith(new_identity.getOutput());
input_layout_op.erase();
deleted_layout_ops.insert(input_layout_op);
}
}
return mlir::success();
}
// For all constants with multiple usages, clone the constants so that each
// constant operation has at most 1 usage.
void DuplicateConstants(mlir::Operation* op) {
llvm::SmallVector<mlir::TF::ConstOp, 4> const_ops;
op->walk(
[&](mlir::TF::ConstOp const_op) { const_ops.emplace_back(const_op); });
for (mlir::TF::ConstOp const_op : const_ops) {
mlir::OpBuilder builder(const_op);
auto uses = const_op->getUses();
if (uses.empty()) return;
llvm::SmallDenseMap<mlir::Operation*, mlir::OpOperand*> const_use_map;
mlir::OpOperand& first_use = *uses.begin();
for (mlir::OpOperand& use : uses) {
if (&use == &first_use) continue;
mlir::Operation* new_const = builder.clone(*const_op);
const_use_map.try_emplace(new_const, &use);
}
for (const auto& it : const_use_map) it.second->set(it.first->getResult(0));
}
}
std::string GetOperationName(mlir::ModuleOp module) {
auto operation_name_attr =
module->getAttrOfType<mlir::StringAttr>(kEagerOperationName);
const std::string operation_name =
operation_name_attr ? operation_name_attr.getValue().str() : "unknown";
return operation_name;
}
} // namespace dtensor
} // namespace tensorflow