blob: 6c0808d73d8f1eaba389091373a20e0df8745e98 [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 "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/Util/IR/UtilDialect.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Diagnostics.h"
#include "mlir/Pass/Pass.h"
namespace mlir::iree_compiler::IREE::HAL {
#define GEN_PASS_DEF_INITIALIZEDEVICESPASS
#include "iree/compiler/Dialect/HAL/Transforms/Passes.h.inc"
namespace {
// Converts an initialized device global to one with a util.initializer that
// performs the device initialization. The initializer is added immediately
// following the global in its parent op.
static void initializeDeviceGlobal(
IREE::Util::GlobalOpInterface globalOp,
IREE::HAL::DeviceInitializationAttrInterface initialValue,
const IREE::HAL::TargetRegistry &targetRegistry) {
auto loc = globalOp.getLoc();
// Clear the initial value as we'll be initializing from the initializer.
globalOp.setGlobalInitialValue({});
// Build a new util.initializer.
OpBuilder moduleBuilder(globalOp);
moduleBuilder.setInsertionPointAfter(globalOp);
auto initializerOp = moduleBuilder.create<IREE::Util::InitializerOp>(loc);
auto *block = moduleBuilder.createBlock(&initializerOp.getBody());
auto initializerBuilder = OpBuilder::atBlockBegin(block);
// Get the device from the attribute builder; note that it may be null.
Value enumeratedDevice = initialValue.buildDeviceEnumeration(
loc,
[&](Location loc, Value device, IREE::HAL::DeviceTargetAttr targetAttr,
OpBuilder &builder) {
auto targetDevice =
targetRegistry.getTargetDevice(targetAttr.getDeviceID());
return targetDevice ? targetDevice->buildDeviceTargetMatch(
loc, device, targetAttr, builder)
: Value{};
},
initializerBuilder);
// Check if the device is null and error out. We could support optional
// devices that are allowed to be null but don't support that anywhere else in
// the compiler today and may never want to. If selecting from multiple
// devices queries can be used to detect what the selected device was and
// those will be memoized.
Value nullDevice = initializerBuilder.create<IREE::Util::NullOp>(
loc, enumeratedDevice.getType());
Value isNull = initializerBuilder.create<IREE::Util::CmpEQOp>(
loc, enumeratedDevice, nullDevice);
initializerBuilder.create<scf::IfOp>(
loc, isNull, [&](OpBuilder &thenBuilder, Location thenLoc) {
Value status = thenBuilder.create<arith::ConstantIntOp>(
thenLoc, static_cast<int64_t>(IREE::Util::StatusCode::Incompatible),
32);
std::string str;
{
llvm::raw_string_ostream os(str);
os << "HAL device `" << globalOp.getGlobalName().getValue()
<< "` not found or unavailable: ";
initialValue.printStatusDescription(os);
}
thenBuilder.create<IREE::Util::StatusCheckOkOp>(thenLoc, status, str);
thenBuilder.create<scf::YieldOp>(thenLoc);
});
// Store the device back to the global to complete initialization.
globalOp.createStoreOp(loc, enumeratedDevice, initializerBuilder);
initializerBuilder.create<IREE::Util::ReturnOp>(loc);
}
//===----------------------------------------------------------------------===//
// --iree-hal-initialize-devices
//===----------------------------------------------------------------------===//
struct InitializeDevicesPass
: public IREE::HAL::impl::InitializeDevicesPassBase<InitializeDevicesPass> {
using IREE::HAL::impl::InitializeDevicesPassBase<
InitializeDevicesPass>::InitializeDevicesPassBase;
void runOnOperation() override {
auto moduleOp = getOperation();
for (auto globalOp : moduleOp.getOps<IREE::Util::GlobalOpInterface>()) {
auto initialValue =
dyn_cast_if_present<IREE::HAL::DeviceInitializationAttrInterface>(
globalOp.getGlobalInitialValue());
if (initialValue) {
initializeDeviceGlobal(globalOp, initialValue, *targetRegistry.value);
}
}
}
};
} // namespace
} // namespace mlir::iree_compiler::IREE::HAL