blob: 72c4a359aa5ca83004b56ba0192e4818f2255b2a [file]
// Copyright 2019 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/VM/Conversion/ImportUtils.h"
#include "iree/compiler/Dialect/VM/Conversion/ConversionDialectInterface.h"
#include "iree/compiler/Dialect/VM/IR/VMOps.h"
#include "llvm/ADT/StringExtras.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Matchers.h"
#include "mlir/Parser.h"
namespace mlir {
namespace iree_compiler {
LogicalResult appendImportModule(IREE::VM::ModuleOp importModuleOp,
ModuleOp targetModuleOp) {
SymbolTable symbolTable(targetModuleOp);
OpBuilder targetBuilder(targetModuleOp);
targetBuilder.setInsertionPoint(&targetModuleOp.getBody()->back());
importModuleOp.walk([&](IREE::VM::ImportOp importOp) {
std::string fullName =
(importModuleOp.getName() + "." + importOp.getName()).str();
auto *existingOp = symbolTable.lookup(fullName);
// TODO(benvanik): verify that the imports match.
if (!existingOp) {
auto clonedOp = cast<IREE::VM::ImportOp>(targetBuilder.clone(*importOp));
mlir::StringAttr fullNameAttr =
mlir::StringAttr::get(clonedOp.getContext(), fullName);
clonedOp.setName(fullNameAttr);
clonedOp.setPrivate();
}
});
return success();
}
LogicalResult appendImportModule(StringRef importModuleSrc,
ModuleOp targetModuleOp) {
auto importModuleRef =
mlir::parseSourceString(importModuleSrc, targetModuleOp.getContext());
if (!importModuleRef) {
return targetModuleOp.emitError()
<< "unable to append import module; import module failed to parse";
}
for (auto importModuleOp : importModuleRef->getOps<IREE::VM::ModuleOp>()) {
if (failed(appendImportModule(importModuleOp, targetModuleOp))) {
importModuleOp.emitError() << "failed to import module";
}
}
return success();
}
void copyImportAttrs(IREE::VM::ImportOp importOp, Operation *callOp) {
if (importOp->hasAttr("nosideeffects")) {
callOp->setAttr("nosideeffects", UnitAttr::get(importOp.getContext()));
}
}
namespace detail {
// Makes a human-readable symbol name for the given string value.
// This is not uniqued and may need uniquing before being added to the symbol
// table.
//
// For example:
// 'Some string!' -> '_utf8_some_string'
// 'I'm a really long'... -> '_utf8_im_a_really_long'
static std::string makeSafeIdentifier(StringRef unsafeIdentifier) {
std::string result = "_utf8_";
llvm::raw_string_ostream os(result);
bool lastUnderscore = true;
for (char c : unsafeIdentifier) {
if (!llvm::isPrint(c)) continue;
if (llvm::isAlnum(c)) {
os << llvm::toLower(c);
lastUnderscore = false;
} else if (!lastUnderscore) {
os << "_";
lastUnderscore = true;
}
}
std::string prefix = os.str().substr(0, 32);
if (!StringRef(prefix).endswith("_")) {
prefix += "_";
}
return prefix + llvm::utohexstr(static_cast<uint64_t>(
llvm::hash_value(unsafeIdentifier)));
}
// Creates a module-level vm.rodata containing the string contents and returns
// the dereferenced byte buffer.
static Value createStringTableValue(Location loc, StringAttr attrValue,
Type inputType,
ConversionPatternRewriter &rewriter) {
auto stringValue = attrValue.getValue();
// Make an identifier-friendly version of the string so that the value is
// more readable in IR (so "I'm some string" becomes "im_some_string", etc).
auto safeIdentifier = makeSafeIdentifier(stringValue);
// Encode the string value bytes into an elements attr as UTF-8 bytes.
SmallVector<APInt, 16> stringBytes(stringValue.size());
for (int i = 0; i < stringValue.size(); ++i) {
stringBytes[i] = APInt(8, stringValue[i]);
}
auto utf8Bytes = DenseIntElementsAttr::get(
VectorType::get({static_cast<int64_t>(stringBytes.size())},
rewriter.getIntegerType(8)),
stringBytes);
// Embed the UTF-8 bytes as a vm.rodata blob.
return rewriter.create<IREE::VM::RodataInlineOp>(
loc,
IREE::VM::RefType::get(IREE::VM::BufferType::get(rewriter.getContext())),
rewriter.getStringAttr(safeIdentifier), utf8Bytes,
/*alignment=*/rewriter.getI64IntegerAttr(1));
}
size_t getSegmentSpanSize(Type spanType) {
if (auto tupleType = spanType.dyn_cast<TupleType>()) {
return tupleType.size();
} else {
return 1;
}
}
Optional<SmallVector<Value, 4>> rewriteAttrToOperands(
Location loc, Attribute attrValue, Type inputType,
ConversionPatternRewriter &rewriter) {
if (auto intAttr = attrValue.dyn_cast<IntegerAttr>()) {
// NOTE: we intentionally go to std.constant ops so that the standard
// conversions can do their job. If we want to remove the dependency
// from standard ops in the future we could instead go directly to
// one of the vm constant ops.
auto constValue = rewriter.createOrFold<mlir::arith::ConstantOp>(
loc, inputType,
IntegerAttr::get(inputType,
APInt(32, static_cast<int32_t>(intAttr.getInt()))));
return {{constValue}};
} else if (auto elementsAttr = attrValue.dyn_cast<DenseIntElementsAttr>()) {
SmallVector<Value, 4> elementValues;
elementValues.reserve(elementsAttr.getNumElements());
for (auto intAttr : elementsAttr.getValues<Attribute>()) {
elementValues.push_back(rewriter.createOrFold<mlir::arith::ConstantOp>(
loc, elementsAttr.getType().getElementType(), intAttr));
}
return elementValues;
} else if (auto arrayAttr = attrValue.dyn_cast<ArrayAttr>()) {
SmallVector<Value, 4> allValues;
for (auto elementAttr : arrayAttr) {
auto flattenedValues =
rewriteAttrToOperands(loc, elementAttr, inputType, rewriter);
if (!flattenedValues) return llvm::None;
allValues.append(flattenedValues->begin(), flattenedValues->end());
}
return allValues;
} else if (auto strAttr = attrValue.dyn_cast<StringAttr>()) {
return {{createStringTableValue(loc, strAttr, inputType, rewriter)}};
}
// This may be a custom dialect type. As we can't trivially access the storage
// of these we need to ask the dialect to do it for us.
auto *conversionInterface =
attrValue.getDialect()
.getRegisteredInterface<VMConversionDialectInterface>();
if (conversionInterface) {
bool anyFailed = false;
SmallVector<Value, 4> allValues;
if (auto tupleType = inputType.dyn_cast<TupleType>()) {
// Custom dialect type maps into a tuple; we expect 1:1 tuple elements to
// attribute storage elements.
auto tupleTypes = llvm::to_vector<4>(tupleType.getTypes());
int ordinal = 0;
conversionInterface->walkAttributeStorage(
attrValue, [&](Attribute elementAttr) {
if (anyFailed) return;
auto elementType = tupleTypes[ordinal++];
auto flattenedValues =
rewriteAttrToOperands(loc, elementAttr, elementType, rewriter);
if (!flattenedValues) {
anyFailed = true;
return;
}
allValues.append(flattenedValues->begin(), flattenedValues->end());
});
} else {
// Custom dialect type maps into zero or more input types (ala arrays).
conversionInterface->walkAttributeStorage(
attrValue, [&](Attribute elementAttr) {
if (anyFailed) return;
auto flattenedValues =
rewriteAttrToOperands(loc, elementAttr, inputType, rewriter);
if (!flattenedValues) {
anyFailed = true;
return;
}
allValues.append(flattenedValues->begin(), flattenedValues->end());
});
}
if (anyFailed) return llvm::None;
return allValues;
}
emitError(loc) << "unsupported attribute encoding: " << attrValue.getType();
return llvm::None;
}
} // namespace detail
} // namespace iree_compiler
} // namespace mlir