| // 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 "mlir/IR/AsmState.h" |
| #include "mlir/Pass/Pass.h" |
| #include "mlir/Transforms/LocationSnapshot.h" |
| |
| namespace mlir::iree_compiler::IREE::HAL { |
| |
| #define GEN_PASS_DEF_CAPTUREEXECUTABLESOURCESPASS |
| #include "iree/compiler/Dialect/HAL/Transforms/Passes.h.inc" |
| |
| namespace { |
| |
| //===----------------------------------------------------------------------===// |
| // --iree-hal-capture-executable-sources |
| //===----------------------------------------------------------------------===// |
| |
| static bool hasDictionaryAttrEntry(Operation *op, StringRef dictionaryName, |
| StringRef key) { |
| auto dictionaryAttr = op->getAttrOfType<DictionaryAttr>(dictionaryName); |
| return dictionaryAttr && dictionaryAttr.get(key); |
| } |
| |
| static void insertDictionaryAttrEntry(Operation *op, StringRef dictionaryName, |
| StringRef key, Attribute value) { |
| NamedAttrList attrs; |
| auto dictionaryAttr = op->getAttrOfType<DictionaryAttr>(dictionaryName); |
| if (dictionaryAttr) |
| attrs.assign(dictionaryAttr.getValue()); |
| attrs.set(key, value); |
| op->setAttr(dictionaryName, DictionaryAttr::get(op->getContext(), attrs)); |
| } |
| |
| static Attribute getSourceAttr(MLIRContext *context, StringRef fileName, |
| StringRef source) { |
| // TODO(benvanik): use our own resource attribute that allows us to store the |
| // source string verbatim (and out-of-band) in the file. Today only element |
| // attrs have resource equivalents upstream (no string resource attr). |
| Builder b(context); |
| auto blob = HeapAsmResourceBlob::allocateAndCopyInferAlign( |
| ArrayRef<char>(source.data(), source.size())); |
| return DenseI8ResourceElementsAttr::get( |
| VectorType::get({static_cast<int64_t>(source.size())}, b.getI8Type()), |
| fileName, std::move(blob)); |
| } |
| |
| struct CaptureExecutableSourcesPass |
| : public IREE::HAL::impl::CaptureExecutableSourcesPassBase< |
| CaptureExecutableSourcesPass> { |
| using IREE::HAL::impl::CaptureExecutableSourcesPassBase< |
| CaptureExecutableSourcesPass>::CaptureExecutableSourcesPassBase; |
| void runOnOperation() override { |
| auto moduleOp = getOperation(); |
| auto moduleName = moduleOp.getName().value_or("module"); |
| |
| for (auto executableOp : moduleOp.getOps<IREE::HAL::ExecutableOp>()) { |
| for (auto variantOp : |
| executableOp.getOps<IREE::HAL::ExecutableVariantOp>()) { |
| // Skip externally defined variants as there's no source to capture. |
| if (variantOp.isExternal()) |
| continue; |
| |
| // Ignore if there is already source assigned. |
| auto fileName = (moduleName + "_" + executableOp.getName() + "_" + |
| variantOp.getName() + "." + stage + ".mlir") |
| .str(); |
| if (hasDictionaryAttrEntry(variantOp, "sources", fileName)) |
| continue; |
| |
| // Create a standalone executable with just the variant being captured. |
| // This allows the source to be passed to iree-compile in the |
| // hal-executable compilation mode. |
| auto clonedExecutableOp = executableOp.cloneWithoutRegions(); |
| clonedExecutableOp.setVisibility(SymbolTable::Visibility::Public); |
| OpBuilder clonedBuilder = OpBuilder::atBlockBegin( |
| &clonedExecutableOp.getBody().emplaceBlock()); |
| auto clonedVariantOp = cast<IREE::HAL::ExecutableVariantOp>( |
| clonedBuilder.clone(*variantOp)); |
| clonedBuilder.create<IREE::HAL::ExecutableEndOp>( |
| clonedBuilder.getUnknownLoc()); |
| |
| // Capture the source contents and update the locations in the IR to |
| // reference it. |
| std::string source; |
| llvm::raw_string_ostream os(source); |
| OpPrintingFlags flags; |
| flags.useLocalScope(); |
| mlir::generateLocationsFromIR(os, fileName, clonedExecutableOp, flags); |
| os << "\n"; // newline at end of file |
| |
| // Wrap up the contents and attach them to the variant. |
| auto sourceAttr = |
| getSourceAttr(variantOp.getContext(), fileName, source); |
| insertDictionaryAttrEntry(variantOp, "sources", fileName, sourceAttr); |
| |
| // Extract the new locations of the exported functions and attach them |
| // to the original. |
| SymbolTable symbolTable(variantOp.getInnerModule()); |
| SymbolTable clonedSymbolTable(clonedVariantOp.getInnerModule()); |
| for (auto [exportOp, clonedExportOp] : llvm::zip_equal( |
| variantOp.getExportOps(), clonedVariantOp.getExportOps())) { |
| // Attach the cloned function location that was updated to point into |
| // the source file and attach it to the original function. |
| auto clonedFuncOp = |
| clonedSymbolTable.lookup(clonedExportOp.getSymName()); |
| if (clonedFuncOp) { |
| insertDictionaryAttrEntry(exportOp, "source_locs", stage, |
| clonedFuncOp->getLoc()); |
| } |
| } |
| |
| clonedExecutableOp.erase(); |
| } |
| } |
| } |
| }; |
| |
| } // namespace |
| |
| } // namespace mlir::iree_compiler::IREE::HAL |