blob: 413c1073d1603d5c06ce231b23ba040c404afea8 [file]
// Copyright 2024 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 <memory>
#include <utility>
#include "iree/compiler/Dialect/HAL/IR/HALDialect.h"
#include "iree/compiler/Dialect/HAL/IR/HALOps.h"
#include "iree/compiler/Dialect/HAL/Transforms/Passes.h"
#include "iree/compiler/Dialect/Stream/IR/StreamTypes.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/Pass/Pass.h"
namespace mlir::iree_compiler::IREE::HAL {
#define GEN_PASS_DEF_RESOLVEDEVICEPROMISESPASS
#include "iree/compiler/Dialect/HAL/Transforms/Passes.h.inc"
namespace {
//===----------------------------------------------------------------------===//
// --iree-hal-resolve-device-promises
//===----------------------------------------------------------------------===//
struct ResolveDevicePromisesPass
: public IREE::HAL::impl::ResolveDevicePromisesPassBase<
ResolveDevicePromisesPass> {
using IREE::HAL::impl::ResolveDevicePromisesPassBase<
ResolveDevicePromisesPass>::ResolveDevicePromisesPassBase;
void runOnOperation() override {
auto moduleOp = getOperation();
// Resolves a #hal.device.promise attr to a #hal.device.affinity. Fails if
// the referenced device is not found.
SymbolTable symbolTable(moduleOp);
auto resolvePromise = [&](Operation *fromOp,
IREE::HAL::DevicePromiseAttr promiseAttr)
-> FailureOr<IREE::Stream::AffinityAttr> {
auto deviceOp =
symbolTable.lookupNearestSymbolFrom<IREE::Util::GlobalOpInterface>(
fromOp, promiseAttr.getDevice());
if (!deviceOp) {
return fromOp->emitOpError()
<< "references a promised device that was not declared: "
<< promiseAttr;
}
return cast<IREE::Stream::AffinityAttr>(
IREE::HAL::DeviceAffinityAttr::get(&getContext(),
FlatSymbolRefAttr::get(deviceOp),
promiseAttr.getQueueMask()));
};
// Resolves any #hal.device.promise attr on the op.
auto resolvePromiseAttrs = [&](Operation *op, DictionaryAttr attrDict)
-> std::optional<std::pair<DictionaryAttr, WalkResult>> {
bool didReplaceAny = false;
auto newDict = dyn_cast_if_present<DictionaryAttr>(attrDict.replace(
[&](Attribute attr)
-> std::optional<std::pair<Attribute, WalkResult>> {
if (auto promiseAttr =
dyn_cast_if_present<IREE::HAL::DevicePromiseAttr>(attr)) {
auto resolvedAttrOr = resolvePromise(op, promiseAttr);
if (failed(resolvedAttrOr)) {
return std::make_pair(attr, WalkResult::interrupt());
}
didReplaceAny = true;
return std::make_pair(resolvedAttrOr.value(),
WalkResult::advance());
}
return std::nullopt;
}));
if (newDict) {
return std::make_pair(newDict, didReplaceAny ? WalkResult::advance()
: WalkResult::skip());
} else {
return std::make_pair(attrDict, WalkResult::interrupt());
}
};
auto resolveAllPromiseAttrs =
[&](Operation *op,
MutableArrayRef<DictionaryAttr> attrDicts) -> WalkResult {
bool didReplaceAny = false;
for (auto &attrDict : attrDicts) {
auto resolveState = resolvePromiseAttrs(op, attrDict);
if (!resolveState) {
// Failed to resolve while recursively replacing.
return WalkResult::interrupt();
} else if (!resolveState->second.wasSkipped()) {
// Performed a replacement.
attrDict = resolveState->first;
didReplaceAny = true;
}
}
return didReplaceAny ? WalkResult::advance() : WalkResult::skip();
};
auto resolvePromisesOnOp = [&](Operation *op) -> WalkResult {
auto opAttrs = op->getAttrDictionary();
if (opAttrs) {
auto resolveState = resolvePromiseAttrs(op, opAttrs);
if (!resolveState) {
// Failed to resolve while recursively replacing.
return WalkResult::interrupt();
} else if (!resolveState->second.wasSkipped()) {
// Performed a replacement.
op->setAttrs(resolveState->first);
}
}
if (auto funcOp = dyn_cast<FunctionOpInterface>(op)) {
SmallVector<DictionaryAttr> argAttrs;
funcOp.getAllArgAttrs(argAttrs);
auto argStatus = resolveAllPromiseAttrs(op, argAttrs);
if (argStatus.wasInterrupted()) {
return argStatus;
} else if (!argStatus.wasSkipped()) {
funcOp.setAllArgAttrs(argAttrs);
}
SmallVector<DictionaryAttr> resultAttrs;
funcOp.getAllResultAttrs(resultAttrs);
auto resultStatus = resolveAllPromiseAttrs(op, resultAttrs);
if (resultStatus.wasInterrupted()) {
return resultStatus;
} else if (!resultStatus.wasSkipped()) {
funcOp.setAllResultAttrs(resultAttrs);
}
}
return WalkResult::advance();
};
// Walk the entire module and replace promises.
// We skip any symbol table op as all devices are top-level only.
if (resolvePromisesOnOp(moduleOp).wasInterrupted()) {
return signalPassFailure();
}
if (moduleOp
.walk([&](Operation *op) {
if (op->hasTrait<OpTrait::SymbolTable>()) {
return WalkResult::skip(); // ignore isolated ops
}
return resolvePromisesOnOp(op);
})
.wasInterrupted()) {
return signalPassFailure();
}
}
};
} // namespace
} // namespace mlir::iree_compiler::IREE::HAL