Adding requirement bits to bytecode modules and bumping version. The compiler now emits per-module and per-function requirement bitfields that indicate extensions needed to execute the bytecode they contain. This allows for nice error messages on loading when the runtime has certain extensions omitted: ``` D:\Dev\iree\runtime\src\iree\vm\bytecode\module.c:309: INVALID_ARGUMENT; required module features [EXT_F32] are not available in this runtime configuration; have [] while module requires [EXT_F32]; while invoking native function hal.executable.create; while calling import; [ 1] native hal.executable.create:0 - [ 0] bytecode module.__init:446 D:\Dev\iree/tests/e2e/models/unidirectional_lstm.mlir:0:0 ``` While making the breaking change I'm also adding a block_count field to each function descriptor and a special block opcode that can be used for verification and disassembly. All functions now have their signatures embedded (not just exports) to allow for internal function call verification.
diff --git a/compiler/src/iree/compiler/API2/Internal/Embed.cpp b/compiler/src/iree/compiler/API2/Internal/Embed.cpp index dcf0a1b..ca3bc23 100644 --- a/compiler/src/iree/compiler/API2/Internal/Embed.cpp +++ b/compiler/src/iree/compiler/API2/Internal/Embed.cpp
@@ -472,11 +472,13 @@ auto builtinModule = llvm::dyn_cast<mlir::ModuleOp>(*parsedModule); LogicalResult result = failure(); if (vmModule) { - result = translateModuleToBytecode(vmModule, session.bytecodeTargetOptions, + result = translateModuleToBytecode(vmModule, session.vmTargetOptions, + session.bytecodeTargetOptions, *output.outputStream); } else if (builtinModule) { - result = translateModuleToBytecode( - builtinModule, session.bytecodeTargetOptions, *output.outputStream); + result = translateModuleToBytecode(builtinModule, session.vmTargetOptions, + session.bytecodeTargetOptions, + *output.outputStream); } else { (*parsedModule)->emitError() << "expected a vm.module or builtin.module"; }
diff --git a/compiler/src/iree/compiler/API2/Internal/MLIRInterop.cpp b/compiler/src/iree/compiler/API2/Internal/MLIRInterop.cpp index 95317b3..ad09f51 100644 --- a/compiler/src/iree/compiler/API2/Internal/MLIRInterop.cpp +++ b/compiler/src/iree/compiler/API2/Internal/MLIRInterop.cpp
@@ -137,11 +137,13 @@ mlir::detail::CallbackOstream output(dataCallback, dataUserObject); if (auto op = llvm::dyn_cast<mlir::ModuleOp>(moduleOpCpp)) { result = iree_compiler::IREE::VM::translateModuleToBytecode( - op, optionsCpp->vmBytecodeTargetOptions, output); + op, optionsCpp->vmTargetOptions, optionsCpp->vmBytecodeTargetOptions, + output); } else if (auto op = llvm::dyn_cast<iree_compiler::IREE::VM::ModuleOp>( moduleOpCpp)) { result = iree_compiler::IREE::VM::translateModuleToBytecode( - op, optionsCpp->vmBytecodeTargetOptions, output); + op, optionsCpp->vmTargetOptions, optionsCpp->vmBytecodeTargetOptions, + output); } else { emitError(moduleOpCpp->getLoc()) << "expected a supported module operation"; result = failure();
diff --git a/compiler/src/iree/compiler/ConstEval/Runtime.cpp b/compiler/src/iree/compiler/ConstEval/Runtime.cpp index 51d191f..d14198a 100644 --- a/compiler/src/iree/compiler/ConstEval/Runtime.cpp +++ b/compiler/src/iree/compiler/ConstEval/Runtime.cpp
@@ -295,9 +295,10 @@ LogicalResult InMemoryCompiledBinary::translateFromModule( mlir::ModuleOp moduleOp) { llvm::raw_string_ostream os(binary); - iree_compiler::IREE::VM::BytecodeTargetOptions targetOptions; + iree_compiler::IREE::VM::TargetOptions vmOptions; + iree_compiler::IREE::VM::BytecodeTargetOptions bytecodeOptions; if (failed(iree_compiler::IREE::VM::translateModuleToBytecode( - moduleOp, targetOptions, os))) { + moduleOp, vmOptions, bytecodeOptions, os))) { return failure(); } os.flush();
diff --git a/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/VMVXTarget.cpp b/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/VMVXTarget.cpp index 031e7d4..c701689 100644 --- a/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/VMVXTarget.cpp +++ b/compiler/src/iree/compiler/Dialect/HAL/Target/VMVX/VMVXTarget.cpp
@@ -70,16 +70,22 @@ context, b.getStringAttr(deviceID()), configAttr); } + IREE::VM::TargetOptions getTargetOptions( + IREE::HAL::ExecutableTargetAttr targetAttr) { + // TODO(benvanik): derive these from a vm target triple. + auto vmOptions = IREE::VM::TargetOptions::FromFlags::get(); + vmOptions.f32Extension = true; + vmOptions.optimizeForStackSize = false; + return vmOptions; + } + void buildTranslationPassPipeline(IREE::HAL::ExecutableVariantOp variantOp, OpPassManager &passManager) override { IREE::VMVX::buildVMVXTransformPassPipeline(passManager); OpPassManager &nestedModulePM = passManager.nest<ModuleOp>(); - // TODO(benvanik): derive these from a vm target triple. - auto vmOptions = IREE::VM::TargetOptions::FromFlags::get(); - vmOptions.f32Extension = true; - vmOptions.optimizeForStackSize = false; + auto vmOptions = getTargetOptions(variantOp.getTargetAttr()); IREE::VM::buildVMTransformPassPipeline(nestedModulePM, vmOptions); } @@ -107,10 +113,15 @@ // Serialize the VM module to bytes and embed it directly. SmallVector<char> moduleData; { - IREE::VM::BytecodeTargetOptions bytecodeOptions; + auto vmOptions = getTargetOptions(variantOp.getTargetAttr()); + // TODO(benvanik): plumb this through somewhere? these options are mostly + // about output format stuff such as debug information so it's probably + // fine to share. + auto bytecodeOptions = IREE::VM::BytecodeTargetOptions::FromFlags::get(); llvm::raw_svector_ostream stream(moduleData); if (failed(translateModuleToBytecode(variantOp.getInnerModule(), - bytecodeOptions, stream))) { + vmOptions, bytecodeOptions, + stream))) { return variantOp.emitOpError() << "failed to serialize VM bytecode module"; }
diff --git a/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesCore.td b/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesCore.td index b170040..b271b5c 100644 --- a/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesCore.td +++ b/compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesCore.td
@@ -22,7 +22,7 @@ // but we are a long way out to stabilizing this format :) // // Some opcodes require an extension prefix to indicate that runtime support -// is optional. An op with the ExtI64 trait will require VM_OPC_ExtI64, for +// is optional. An op with the ExtF64 trait will require VM_OPC_ExtF64, for // example. Ops that bridge extension sets have a canonical form that may // require multiple prefix codes (for example, the i64<->f64 extensions). @@ -34,7 +34,7 @@ VM_OPC prefix = ?, list<VM_OPC> cases> : IntEnumAttr<I8, name, description, cases> { - let cppNamespace = "IREE::VM"; + let cppNamespace = "::mlir::iree_compiler::IREE::VM"; let returnType = cppNamespace # "::" # name; let underlyingType = "uint8_t"; let convertFromStorage = "static_cast<" # returnType # ">($_self.getInt())"; @@ -47,6 +47,8 @@ string opcodeEnumTag = enumTag; } +// Next available opcode: 0x7A + // Globals: def VM_OPC_GlobalLoadI32 : VM_OPC<0x00, "GlobalLoadI32">; def VM_OPC_GlobalStoreI32 : VM_OPC<0x01, "GlobalStoreI32">; @@ -170,6 +172,7 @@ def VM_OPC_CmpNZRef : VM_OPC<0x55, "CmpNZRef">; // Control flow: +def VM_OPC_Block : VM_OPC<0x79, "Block">; def VM_OPC_Branch : VM_OPC<0x56, "Branch">; def VM_OPC_CondBranch : VM_OPC<0x57, "CondBranch">; def VM_OPC_Call : VM_OPC<0x58, "Call">; @@ -358,6 +361,8 @@ VM_OPC_BufferCopy, VM_OPC_BufferCompare, + VM_OPC_Block, + // Extension opcodes (0xE0-0xFF): VM_OPC_PrefixExtF32, // VM_ExtF32OpcodeAttr VM_OPC_PrefixExtF64, // VM_ExtF64OpcodeAttr
diff --git a/compiler/src/iree/compiler/Dialect/VM/IR/VMTraits.h b/compiler/src/iree/compiler/Dialect/VM/IR/VMTraits.h index 70faec2..7a0c981 100644 --- a/compiler/src/iree/compiler/Dialect/VM/IR/VMTraits.h +++ b/compiler/src/iree/compiler/Dialect/VM/IR/VMTraits.h
@@ -42,15 +42,6 @@ }; template <typename ConcreteType> -class ExtI64 : public OpTrait::TraitBase<ConcreteType, ExtI64> { - public: - static LogicalResult verifyTrait(Operation *op) { - // TODO(benvanik): verify i64 ext is supported. - return success(); - } -}; - -template <typename ConcreteType> class ExtF32 : public OpTrait::TraitBase<ConcreteType, ExtF32> { public: static LogicalResult verifyTrait(Operation *op) {
diff --git a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BUILD b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BUILD index 43b609a..9cf32a1 100644 --- a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BUILD +++ b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BUILD
@@ -31,6 +31,7 @@ "//compiler/src/iree/compiler/Dialect/Util/IR", "//compiler/src/iree/compiler/Dialect/Util/Transforms", "//compiler/src/iree/compiler/Dialect/VM/Analysis", + "//compiler/src/iree/compiler/Dialect/VM/Conversion", "//compiler/src/iree/compiler/Dialect/VM/IR", "//compiler/src/iree/compiler/Dialect/VM/Transforms", "//compiler/src/iree/compiler/Dialect/VM/Utils:CallingConvention",
diff --git a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.cpp b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.cpp index 85e1d4e..9dedbdb 100644 --- a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.cpp +++ b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.cpp
@@ -9,6 +9,7 @@ #include "iree/compiler/Dialect/Util/IR/UtilTypes.h" #include "iree/compiler/Dialect/VM/Analysis/RegisterAllocation.h" #include "iree/compiler/Dialect/VM/IR/VMDialect.h" +#include "iree/compiler/Dialect/VM/IR/VMTypes.h" #include "llvm/ADT/STLExtras.h" #include "mlir/IR/Attributes.h" #include "mlir/IR/Diagnostics.h" @@ -32,7 +33,7 @@ LogicalResult beginBlock(Block *block) override { blockOffsets_[block] = bytecode_.size(); - return success(); + return writeUint8(static_cast<uint8_t>(Opcode::Block)); } LogicalResult endBlock(Block *block) override { return success(); } @@ -389,6 +390,7 @@ debugDatabase.addFunctionSourceMap(funcOp, sourceMap); + size_t finalLength = encoder.getOffset(); if (failed(encoder.ensureAlignment(8))) { funcOp.emitError() << "failed to pad function"; return std::nullopt; @@ -399,6 +401,8 @@ return std::nullopt; } result.bytecodeData = bytecodeData.value(); + result.bytecodeLength = finalLength; + result.blockCount = funcOp.getBlocks().size(); result.i32RegisterCount = registerAllocation.getMaxI32RegisterOrdinal() + 1; result.refRegisterCount = registerAllocation.getMaxRefRegisterOrdinal() + 1; return result;
diff --git a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.h b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.h index f1e7eeb..8463980 100644 --- a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.h +++ b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.h
@@ -18,8 +18,13 @@ namespace VM { struct EncodedBytecodeFunction { - // Encoded bytecode data for the function body. + // Encoded bytecode data for the function body including padding. std::vector<uint8_t> bytecodeData; + // Precise size of the bytecode. + size_t bytecodeLength = 0; + + // Total number of blocks including the entry block. + uint16_t blockCount = 0; // Total i32 register slots required for execution. // Note that larger types also use these slots (i64=2xi32). @@ -32,7 +37,7 @@ class BytecodeEncoder : public VMFuncEncoder { public: // Matches IREE_VM_BYTECODE_VERSION_MAJOR. - static constexpr uint32_t kVersionMajor = 13; + static constexpr uint32_t kVersionMajor = 14; // Matches IREE_VM_BYTECODE_VERSION_MINOR. static constexpr uint32_t kVersionMinor = 0; static constexpr uint32_t kVersion = (kVersionMajor << 16) | kVersionMinor;
diff --git a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeModuleTarget.cpp b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeModuleTarget.cpp index 31cbebe..2ab87eb 100644 --- a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeModuleTarget.cpp +++ b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeModuleTarget.cpp
@@ -179,8 +179,9 @@ // Canonicalizes the module to its final form prior to emission. // This verifies that we only have ops we can serialize and performs any of the // required transformations (such as debug op stripping). -static LogicalResult canonicalizeModule(BytecodeTargetOptions targetOptions, - IREE::VM::ModuleOp moduleOp) { +static LogicalResult canonicalizeModule( + IREE::VM::BytecodeTargetOptions bytecodeOptions, + IREE::VM::ModuleOp moduleOp) { RewritePatternSet patterns(moduleOp.getContext()); ConversionTarget target(*moduleOp.getContext()); target.addLegalDialect<IREE::VM::VMDialect>(); @@ -198,7 +199,7 @@ // Debug ops must not be present when stripping. // TODO(benvanik): add RemoveDisabledDebugOp pattern. if (op.hasTrait<OpTrait::IREE::VM::DebugOnly>() && - targetOptions.stripDebugOps) { + bytecodeOptions.stripDebugOps) { target.setOpAction(op, ConversionTarget::LegalizationAction::Illegal); } } @@ -219,7 +220,7 @@ modulePasses.addPass(IREE::VM::createGlobalInitializationPass()); modulePasses.addPass(IREE::VM::createDropEmptyModuleInitializersPass()); - if (targetOptions.optimize) { + if (bytecodeOptions.optimize) { // TODO(benvanik): run this as part of a fixed-point iteration. modulePasses.addPass(mlir::createInlinerPass()); modulePasses.addPass(mlir::createCSEPass()); @@ -274,9 +275,9 @@ } // Returns a serialized function signature. -static iree_vm_FunctionSignatureDef_ref_t makeExportFunctionSignatureDef( - IREE::VM::ExportOp exportOp, IREE::VM::FuncOp funcOp, - llvm::DenseMap<Type, int> &typeTable, FlatbufferBuilder &fbb) { +static iree_vm_FunctionSignatureDef_ref_t makeFunctionSignatureDef( + IREE::VM::FuncOp funcOp, llvm::DenseMap<Type, int> &typeTable, + FlatbufferBuilder &fbb) { // Generate the signature calling convention string based on types. auto cconv = makeCallingConventionString(funcOp); if (!cconv.has_value()) return {}; @@ -314,6 +315,20 @@ cconv.value(), /*attrsRef=*/0, fbb); } +// Walks |rootOp| to find all VM features required by it and its children. +static iree_vm_FeatureBits_enum_t findRequiredFeatures(Operation *rootOp) { + iree_vm_FeatureBits_enum_t result = 0; + rootOp->walk([&](Operation *op) { + if (op->hasTrait<OpTrait::IREE::VM::ExtF32>()) { + result |= iree_vm_FeatureBits_EXT_F32; + } + if (op->hasTrait<OpTrait::IREE::VM::ExtF64>()) { + result |= iree_vm_FeatureBits_EXT_F64; + } + }); + return result; +} + // Builds a complete BytecodeModuleDef FlatBuffer object in |fbb|. // The order of the encoding is ordered to ensure that all metadata is at the // front of the resulting buffer. Large read-only data and bytecode blobs always @@ -325,8 +340,10 @@ // here during serialization but a much more trivial (and cache-friendly) // representation at runtime. static LogicalResult buildFlatBufferModule( - BytecodeTargetOptions targetOptions, IREE::VM::ModuleOp moduleOp, - MutableArrayRef<RodataRef> rodataRefs, FlatbufferBuilder &fbb) { + IREE::VM::TargetOptions vmOptions, + IREE::VM::BytecodeTargetOptions bytecodeOptions, + IREE::VM::ModuleOp moduleOp, MutableArrayRef<RodataRef> rodataRefs, + FlatbufferBuilder &fbb) { // Start the buffer so that we can begin recording data prior to the root // table (which we do at the very end). This does not change the layout of the // file and is only used to prime the flatcc builder. @@ -376,20 +393,23 @@ SmallVector<iree_vm_FunctionDescriptor_t, 8> functionDescriptors; bytecodeDataParts.resize(internalFuncOps.size()); functionDescriptors.resize(internalFuncOps.size()); + iree_vm_FeatureBits_enum_t moduleRequirements = 0; size_t totalBytecodeLength = 0; - for (auto funcOp : llvm::enumerate(internalFuncOps)) { + for (auto [i, funcOp] : llvm::enumerate(internalFuncOps)) { auto encodedFunction = BytecodeEncoder::encodeFunction( - funcOp.value(), typeOrdinalMap, symbolTable, debugDatabase); + funcOp, typeOrdinalMap, symbolTable, debugDatabase); if (!encodedFunction) { - return funcOp.value().emitError() << "failed to encode function bytecode"; + return funcOp.emitError() << "failed to encode function bytecode"; } + auto funcRequirements = findRequiredFeatures(funcOp); + moduleRequirements |= funcRequirements; iree_vm_FunctionDescriptor_assign( - &functionDescriptors[funcOp.index()], totalBytecodeLength, - encodedFunction->bytecodeData.size(), encodedFunction->i32RegisterCount, - encodedFunction->refRegisterCount); + &functionDescriptors[i], totalBytecodeLength, + encodedFunction->bytecodeLength, funcRequirements, + /*reserved=*/0u, encodedFunction->blockCount, + encodedFunction->i32RegisterCount, encodedFunction->refRegisterCount); totalBytecodeLength += encodedFunction->bytecodeData.size(); - bytecodeDataParts[funcOp.index()] = - std::move(encodedFunction->bytecodeData); + bytecodeDataParts[i] = std::move(encodedFunction->bytecodeData); } flatbuffers_uint8_vec_start(fbb); uint8_t *bytecodeDataPtr = @@ -399,8 +419,7 @@ // for both security and determinism). memset(bytecodeDataPtr, 0, totalBytecodeLength); size_t currentBytecodeOffset = 0; - for (const auto &it : llvm::enumerate(internalFuncOps)) { - int ordinal = it.index(); + for (const auto &[ordinal, _] : llvm::enumerate(internalFuncOps)) { auto data = std::move(bytecodeDataParts[ordinal]); std::memcpy(bytecodeDataPtr + currentBytecodeOffset, data.data(), data.size()); @@ -450,16 +469,18 @@ // NOTE: rwdata is currently unused. SmallVector<iree_vm_RwdataSegmentDef_ref_t, 8> rwdataSegmentRefs; + auto signatureRefs = + llvm::to_vector<8>(llvm::map_range(internalFuncOps, [&](auto funcOp) { + return makeFunctionSignatureDef(funcOp, typeOrdinalMap, fbb); + })); + auto exportFuncRefs = llvm::to_vector<8>(llvm::map_range(exportFuncOps, [&](auto exportOp) { auto localNameRef = fbb.createString(exportOp.getExportName()); auto funcOp = symbolTable.lookup<IREE::VM::FuncOp>(exportOp.getFunctionRef()); - auto signatureRef = makeExportFunctionSignatureDef(exportOp, funcOp, - typeOrdinalMap, fbb); iree_vm_ExportFunctionDef_start(fbb); iree_vm_ExportFunctionDef_local_name_add(fbb, localNameRef); - iree_vm_ExportFunctionDef_signature_add(fbb, signatureRef); iree_vm_ExportFunctionDef_internal_ordinal_add( fbb, funcOp.getOrdinal()->getLimitedValue()); return iree_vm_ExportFunctionDef_end(fbb); @@ -516,7 +537,8 @@ // of the file. auto rodataSegmentsRef = fbb.createOffsetVecDestructive(rodataSegmentRefs); auto rwdataSegmentsRef = fbb.createOffsetVecDestructive(rwdataSegmentRefs); - auto exportFuncsOffset = fbb.createOffsetVecDestructive(exportFuncRefs); + auto signaturesRef = fbb.createOffsetVecDestructive(signatureRefs); + auto exportFuncsRef = fbb.createOffsetVecDestructive(exportFuncRefs); auto importFuncsRef = fbb.createOffsetVecDestructive(importFuncRefs); auto dependenciesRef = fbb.createOffsetVecDestructive(dependencyRefs); auto typesRef = fbb.createOffsetVecDestructive(typeRefs); @@ -533,21 +555,35 @@ } iree_vm_DebugDatabaseDef_ref_t debugDatabaseRef = 0; - if (!targetOptions.stripSourceMap) { + if (!bytecodeOptions.stripSourceMap) { debugDatabaseRef = debugDatabase.build(fbb); } auto moduleNameRef = fbb.createString( moduleOp.getSymName().empty() ? "module" : moduleOp.getSymName()); + // TODO(benvanik): let moduleRequirements be a subset of function requirements + // so that we can multi-version. For now the moduleRequirements will be the OR + // of all functions. + iree_vm_FeatureBits_enum_t allowedFeatures = 0; + if (vmOptions.f32Extension) allowedFeatures |= iree_vm_FeatureBits_EXT_F32; + if (vmOptions.f64Extension) allowedFeatures |= iree_vm_FeatureBits_EXT_F64; + if ((moduleRequirements & allowedFeatures) != moduleRequirements) { + return moduleOp.emitError() + << "module uses features not allowed by flags (requires " + << moduleRequirements << ", allowed " << allowedFeatures << ")"; + } + iree_vm_BytecodeModuleDef_name_add(fbb, moduleNameRef); iree_vm_BytecodeModuleDef_version_add(fbb, moduleOp.getVersion().value_or(0u)); + iree_vm_BytecodeModuleDef_requirements_add(fbb, moduleRequirements); // TODO(benvanik): iree_vm_BytecodeModuleDef_attrs_add iree_vm_BytecodeModuleDef_types_add(fbb, typesRef); iree_vm_BytecodeModuleDef_dependencies_add(fbb, dependenciesRef); iree_vm_BytecodeModuleDef_imported_functions_add(fbb, importFuncsRef); - iree_vm_BytecodeModuleDef_exported_functions_add(fbb, exportFuncsOffset); + iree_vm_BytecodeModuleDef_exported_functions_add(fbb, exportFuncsRef); + iree_vm_BytecodeModuleDef_function_signatures_add(fbb, signaturesRef); iree_vm_BytecodeModuleDef_module_state_add(fbb, moduleStateDef); iree_vm_BytecodeModuleDef_rodata_segments_add(fbb, rodataSegmentsRef); iree_vm_BytecodeModuleDef_rwdata_segments_add(fbb, rwdataSegmentsRef); @@ -562,28 +598,30 @@ return success(); } -LogicalResult translateModuleToBytecode(IREE::VM::ModuleOp moduleOp, - BytecodeTargetOptions targetOptions, - llvm::raw_ostream &output) { +LogicalResult translateModuleToBytecode( + IREE::VM::ModuleOp moduleOp, IREE::VM::TargetOptions vmOptions, + IREE::VM::BytecodeTargetOptions bytecodeOptions, + llvm::raw_ostream &output) { moduleOp.getContext()->getOrLoadDialect<IREE::Util::UtilDialect>(); - if (failed(canonicalizeModule(targetOptions, moduleOp))) { + if (failed(canonicalizeModule(bytecodeOptions, moduleOp))) { return moduleOp.emitError() << "failed to canonicalize vm.module to a serializable form"; } // Dump VM assembly source listing to a file and annotate IR locations. - if (!targetOptions.sourceListing.empty()) { + if (!bytecodeOptions.sourceListing.empty()) { OpPrintingFlags printFlags; printFlags.elideLargeElementsAttrs(8192); - if (failed(mlir::generateLocationsFromIR(targetOptions.sourceListing, "vm", - moduleOp, printFlags))) { + if (failed(mlir::generateLocationsFromIR(bytecodeOptions.sourceListing, + "vm", moduleOp, printFlags))) { return moduleOp.emitError() << "failed to write source listing to '" - << targetOptions.sourceListing << "'"; + << bytecodeOptions.sourceListing << "'"; } } - if (targetOptions.outputFormat == BytecodeOutputFormat::kAnnotatedMlirText) { + if (bytecodeOptions.outputFormat == + BytecodeOutputFormat::kAnnotatedMlirText) { // Run register allocation now and put the info in the IR so it's printed. for (auto funcOp : moduleOp.getBlock().getOps<IREE::VM::FuncOp>()) { if (!funcOp.empty()) { @@ -597,8 +635,9 @@ } // Debug-only formats: - if (targetOptions.outputFormat == BytecodeOutputFormat::kMlirText || - targetOptions.outputFormat == BytecodeOutputFormat::kAnnotatedMlirText) { + if (bytecodeOptions.outputFormat == BytecodeOutputFormat::kMlirText || + bytecodeOptions.outputFormat == + BytecodeOutputFormat::kAnnotatedMlirText) { // Use the standard MLIR text printer. moduleOp.getOperation()->print(output); output << "\n"; @@ -607,15 +646,15 @@ // Set up the output archive builder based on output format. std::unique_ptr<ArchiveWriter> archiveWriter; - if (targetOptions.emitPolyglotZip && - targetOptions.outputFormat == BytecodeOutputFormat::kFlatBufferBinary) { + if (bytecodeOptions.emitPolyglotZip && + bytecodeOptions.outputFormat == BytecodeOutputFormat::kFlatBufferBinary) { archiveWriter = std::make_unique<ZIPArchiveWriter>(moduleOp.getLoc(), output); - } else if (targetOptions.outputFormat == + } else if (bytecodeOptions.outputFormat == BytecodeOutputFormat::kFlatBufferBinary) { archiveWriter = std::make_unique<FlatArchiveWriter>(moduleOp.getLoc(), output); - } else if (targetOptions.outputFormat == + } else if (bytecodeOptions.outputFormat == BytecodeOutputFormat::kFlatBufferText) { archiveWriter = std::make_unique<JSONArchiveWriter>(moduleOp.getLoc(), output); @@ -673,7 +712,8 @@ // the first few pages need to be accessed to get the metadata and the rest // can be large bulk data. FlatbufferBuilder fbb; - if (failed(buildFlatBufferModule(targetOptions, moduleOp, rodataRefs, fbb))) { + if (failed(buildFlatBufferModule(vmOptions, bytecodeOptions, moduleOp, + rodataRefs, fbb))) { return failure(); } if (failed(archiveWriter->flush(fbb))) { @@ -684,15 +724,17 @@ return success(); } -LogicalResult translateModuleToBytecode(mlir::ModuleOp outerModuleOp, - BytecodeTargetOptions targetOptions, - llvm::raw_ostream &output) { +LogicalResult translateModuleToBytecode( + mlir::ModuleOp outerModuleOp, IREE::VM::TargetOptions vmOptions, + IREE::VM::BytecodeTargetOptions bytecodeOptions, + llvm::raw_ostream &output) { auto moduleOps = outerModuleOp.getOps<IREE::VM::ModuleOp>(); if (moduleOps.empty()) { return outerModuleOp.emitError() << "outer module does not contain a vm.module op"; } - return translateModuleToBytecode(*moduleOps.begin(), targetOptions, output); + return translateModuleToBytecode(*moduleOps.begin(), vmOptions, + bytecodeOptions, output); } void BytecodeTargetOptions::bindOptions(OptionsBinder &binder) {
diff --git a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeModuleTarget.h b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeModuleTarget.h index 6d4e884..aeed3c1 100644 --- a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeModuleTarget.h +++ b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeModuleTarget.h
@@ -7,6 +7,7 @@ #ifndef IREE_COMPILER_DIALECT_VM_TARGET_BYTECODE_BYTECODEMODULETARGET_H_ #define IREE_COMPILER_DIALECT_VM_TARGET_BYTECODE_BYTECODEMODULETARGET_H_ +#include "iree/compiler/Dialect/VM/Conversion/TargetOptions.h" #include "iree/compiler/Dialect/VM/IR/VMOps.h" #include "iree/compiler/Utils/OptionUtils.h" #include "llvm/Support/raw_ostream.h" @@ -65,12 +66,12 @@ // serialized module format. // // Exposed via the --iree-vm-ir-to-bytecode-module translation. -LogicalResult translateModuleToBytecode(IREE::VM::ModuleOp moduleOp, - BytecodeTargetOptions targetOptions, - llvm::raw_ostream &output); -LogicalResult translateModuleToBytecode(mlir::ModuleOp outerModuleOp, - BytecodeTargetOptions targetOptions, - llvm::raw_ostream &output); +LogicalResult translateModuleToBytecode( + IREE::VM::ModuleOp moduleOp, IREE::VM::TargetOptions vmOptions, + IREE::VM::BytecodeTargetOptions bytecodeOptions, llvm::raw_ostream &output); +LogicalResult translateModuleToBytecode( + mlir::ModuleOp outerModuleOp, IREE::VM::TargetOptions vmOptions, + IREE::VM::BytecodeTargetOptions bytecodeOptions, llvm::raw_ostream &output); } // namespace VM } // namespace IREE
diff --git a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/CMakeLists.txt b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/CMakeLists.txt index 6484f0b..a5092db 100644 --- a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/CMakeLists.txt +++ b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/CMakeLists.txt
@@ -34,6 +34,7 @@ iree::compiler::Dialect::Util::IR iree::compiler::Dialect::Util::Transforms iree::compiler::Dialect::VM::Analysis + iree::compiler::Dialect::VM::Conversion iree::compiler::Dialect::VM::IR iree::compiler::Dialect::VM::Transforms iree::compiler::Dialect::VM::Utils::CallingConvention
diff --git a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/TranslationRegistration.cpp b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/TranslationRegistration.cpp index b654328..35879f8 100644 --- a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/TranslationRegistration.cpp +++ b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/TranslationRegistration.cpp
@@ -20,7 +20,8 @@ "Translates a vm.module to a bytecode module", [](mlir::ModuleOp moduleOp, llvm::raw_ostream &output) { return translateModuleToBytecode( - moduleOp, BytecodeTargetOptions::FromFlags::get(), output); + moduleOp, TargetOptions::FromFlags::get(), + BytecodeTargetOptions::FromFlags::get(), output); }); }
diff --git a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/test/module_encoding_smoke.mlir b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/test/module_encoding_smoke.mlir index 7cbdc65..3ebcb22 100644 --- a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/test/module_encoding_smoke.mlir +++ b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/test/module_encoding_smoke.mlir
@@ -4,27 +4,42 @@ // CHECK: "name": "simple_module" vm.module @simple_module { // CHECK: "types": [{ - // CHECK: "full_name": "i32" + // CHECK: "full_name": "f32" // CHECK: "exported_functions": // CHECK: "local_name": "func" vm.export @func + // CHECK: "function_signatures": + // CHECK-NEXT: "calling_convention": "0f_f" + // CHECK: "function_descriptors": // CHECK-NEXT: { // CHECK-NEXT: "bytecode_offset": 0 - // CHECK-NEXT: "bytecode_length": 8 + // CHECK-NEXT: "bytecode_length": 14 + // CHECK-NEXT: "requirements": "EXT_F32" + // CHECK-NEXT: "reserved": 0 + // CHECK-NEXT: "block_count": 1 // CHECK-NEXT: "i32_register_count": 1 // CHECK-NEXT: "ref_register_count": 0 // CHECK-NEXT: } - vm.func @func(%arg0 : i32) -> i32 { - vm.return %arg0 : i32 + vm.func @func(%arg0 : f32) -> f32 { + %0 = vm.add.f32 %arg0, %arg0 : f32 + vm.return %0 : f32 } // CHECK: "bytecode_data": [ - // CHECK-NEXT: 90, + // CHECK-NEXT: 121, + // CHECK-NEXT: 224, + // CHECK-NEXT: 10, // CHECK-NEXT: 0, + // CHECK-NEXT: 0, + // CHECK-NEXT: 0, + // CHECK-NEXT: 0, + // CHECK-NEXT: 0, + // CHECK-NEXT: 0, + // CHECK-NEXT: 90, // CHECK-NEXT: 1, // CHECK-NEXT: 0, // CHECK-NEXT: 0,
diff --git a/compiler/src/iree/compiler/Dialect/VM/Transforms/Passes.cpp b/compiler/src/iree/compiler/Dialect/VM/Transforms/Passes.cpp index 26d6d21..53baa1c 100644 --- a/compiler/src/iree/compiler/Dialect/VM/Transforms/Passes.cpp +++ b/compiler/src/iree/compiler/Dialect/VM/Transforms/Passes.cpp
@@ -69,7 +69,7 @@ FunctionLikeNest(passManager) .addPass(mlir::createSCFForLoopCanonicalizationPass); - // This pass is sketchy as it can pessimizes tight loops due to affine + // This pass is sketchy as it can pessimize tight loops due to affine // treating all indices as signed and the unsigned conversion pass not being // able to handle that. The scf.for canonicalization does a decent job of // removing trivial loops above and this catches the rest. It inserts nasty
diff --git a/runtime/src/iree/schemas/bytecode_module_def.fbs b/runtime/src/iree/schemas/bytecode_module_def.fbs index 259ba91..a281e97 100644 --- a/runtime/src/iree/schemas/bytecode_module_def.fbs +++ b/runtime/src/iree/schemas/bytecode_module_def.fbs
@@ -10,6 +10,14 @@ file_identifier "IREE"; file_extension "vmfb"; +// Available runtime features supported by the active VM implementation. +enum FeatureBits:uint32 (bit_flags) { + // 32-bit floating point extension. + EXT_F32 = 0, // 1u << 0 + // 64-bit floating point extension. + EXT_F64 = 1, // 1u << 1 +} + // Arbitrary key/value reflection attribute. table AttrDef { key:string; @@ -82,24 +90,10 @@ // Local name of the function (excluding the module namespace). local_name:string; - // Signature of the function expected used for verifying that imports match. - signature:FunctionSignatureDef; - // Ordinal in the internal_functions table that implements this function. internal_ordinal:int32; } -// Defines a bytecode function. -table InternalFunctionDef { - // Local name of the function or empty if the names have been stripped. - // The full name of the function when referenced from external modules will - // include the BytecodeModuleDef.name prefix. - local_name:string; - - // Signature of the function used for reflection. - signature:FunctionSignatureDef; -} - table UncompressedDataDef { } @@ -146,7 +140,17 @@ bytecode_offset:int32; bytecode_length:int32; - // TODO(benvanik): remove counts and embed directly in bytecode. + // Features required in order to execute this function. + // Note that this may be a superset of the module-level features if some + // functions are conditionally executed based on runtime-derived capabilities. + requirements:FeatureBits; + + // Unused, must be 0. + reserved:int16; + + // Total number of blocks in the function. + block_count:int16; + // Total number of i32 registers used by the function. i32_register_count:int16; // Total number of ref registers used by the function. @@ -239,6 +243,11 @@ // resolve. version:uint32; + // Features required to load and run bytecode from the module. + // Some functions may have additional requirements for when there are multiple + // variants selected based on runtime feature detection. + requirements:FeatureBits = 0; + // Module-level attributes, if any. attrs:[AttrDef]; @@ -254,6 +263,9 @@ // Exported function definitions used to resolve imports. exported_functions:[ExportFunctionDef]; + // All local function signatures (internal and exports). + function_signatures:[FunctionSignatureDef]; + // Read-only data segments (like non-code .text). // May optionally be compressed and decompressed by the loader. rodata_segments:[RodataSegmentDef];
diff --git a/runtime/src/iree/vm/bytecode/disassembler.c b/runtime/src/iree/vm/bytecode/disassembler.c index 6b0f5bf..1b38ec7 100644 --- a/runtime/src/iree/vm/bytecode/disassembler.c +++ b/runtime/src/iree/vm/bytecode/disassembler.c
@@ -1515,6 +1515,12 @@ // Control flow //===------------------------------------------------------------------===// + DISASM_OP(CORE, Block) { + IREE_RETURN_IF_ERROR( + iree_string_builder_append_string(b, IREE_SV("<block>"))); + break; + } + DISASM_OP(CORE, Branch) { int32_t block_pc = VM_ParseBranchTarget("dest"); const iree_vm_register_remap_list_t* remap_list =
diff --git a/runtime/src/iree/vm/bytecode/dispatch.c b/runtime/src/iree/vm/bytecode/dispatch.c index 94c987d..592adb3 100644 --- a/runtime/src/iree/vm/bytecode/dispatch.c +++ b/runtime/src/iree/vm/bytecode/dispatch.c
@@ -1638,6 +1638,9 @@ // Control flow //===------------------------------------------------------------------===// + // No-op in the interpreter. + DISPATCH_OP(CORE, Block, {}); + DISPATCH_OP(CORE, Branch, { int32_t block_pc = VM_DecBranchTarget("dest"); const iree_vm_register_remap_list_t* remap_list =
diff --git a/runtime/src/iree/vm/bytecode/generated/op_table.h b/runtime/src/iree/vm/bytecode/generated/op_table.h index b1f3a0b..3ad3c5b 100644 --- a/runtime/src/iree/vm/bytecode/generated/op_table.h +++ b/runtime/src/iree/vm/bytecode/generated/op_table.h
@@ -128,7 +128,7 @@ IREE_VM_OP_CORE_CtlzI64 = 0x76, IREE_VM_OP_CORE_AbsI32 = 0x77, IREE_VM_OP_CORE_AbsI64 = 0x78, - IREE_VM_OP_CORE_RSV_0x79, + IREE_VM_OP_CORE_Block = 0x79, IREE_VM_OP_CORE_RSV_0x7A, IREE_VM_OP_CORE_RSV_0x7B, IREE_VM_OP_CORE_RSV_0x7C, @@ -387,7 +387,7 @@ OPC(0x76, CtlzI64) \ OPC(0x77, AbsI32) \ OPC(0x78, AbsI64) \ - RSV(0x79) \ + OPC(0x79, Block) \ RSV(0x7A) \ RSV(0x7B) \ RSV(0x7C) \
diff --git a/runtime/src/iree/vm/bytecode/module.c b/runtime/src/iree/vm/bytecode/module.c index b122f73..83739a8 100644 --- a/runtime/src/iree/vm/bytecode/module.c +++ b/runtime/src/iree/vm/bytecode/module.c
@@ -236,6 +236,33 @@ return status; } +// clang-format off +static const iree_bitfield_string_mapping_t iree_vm_bytecode_feature_mappings[] = { + {iree_vm_FeatureBits_EXT_F32, IREE_SVL("EXT_F32")}, + {iree_vm_FeatureBits_EXT_F64, IREE_SVL("EXT_F64")}, +}; +// clang-format on + +// Formats a buffer usage bitfield as a string. +// See iree_bitfield_format for usage. +static iree_string_view_t iree_vm_bytecode_features_format( + iree_vm_FeatureBits_enum_t value, iree_bitfield_string_temp_t* out_temp) { + return iree_bitfield_format_inline( + value, IREE_ARRAYSIZE(iree_vm_bytecode_feature_mappings), + iree_vm_bytecode_feature_mappings, out_temp); +} + +static iree_vm_FeatureBits_enum_t iree_vm_bytecode_available_features(void) { + iree_vm_FeatureBits_enum_t result = 0; +#if IREE_VM_EXT_F32_ENABLE + result |= iree_vm_FeatureBits_EXT_F32; +#endif // IREE_VM_EXT_F32_ENABLE +#if IREE_VM_EXT_F64_ENABLE + result |= iree_vm_FeatureBits_EXT_F64; +#endif // IREE_VM_EXT_F64_ENABLE + return result; +} + // Verifies the structure of the FlatBuffer so that we can avoid doing so during // runtime. There are still some conditions we must be aware of (such as omitted // names on functions with internal linkage), however we shouldn't need to @@ -258,6 +285,33 @@ iree_vm_BytecodeModuleDef_table_t module_def = iree_vm_BytecodeModuleDef_as_root(flatbuffer_contents.data); + const iree_vm_FeatureBits_enum_t available_features = + iree_vm_bytecode_available_features(); + const iree_vm_FeatureBits_enum_t required_features = + iree_vm_BytecodeModuleDef_requirements(module_def); + if (!iree_all_bits_set(available_features, required_features)) { +#if IREE_STATUS_MODE + const iree_vm_FeatureBits_enum_t needed_features = + required_features & ~available_features; + iree_bitfield_string_temp_t temp0, temp1, temp2; + iree_string_view_t available_features_str = + iree_vm_bytecode_features_format(available_features, &temp0); + iree_string_view_t required_features_str = + iree_vm_bytecode_features_format(required_features, &temp1); + iree_string_view_t needed_features_str = + iree_vm_bytecode_features_format(needed_features, &temp2); + return iree_make_status( + IREE_STATUS_INVALID_ARGUMENT, + "required module features [%.*s] are not available in this runtime " + "configuration; have [%.*s] while module requires [%.*s]", + (int)needed_features_str.size, needed_features_str.data, + (int)available_features_str.size, available_features_str.data, + (int)required_features_str.size, required_features_str.data); +#else + return iree_status_from_code(IREE_STATUS_INVALID_ARGUMENT); +#endif // IREE_STATUS_MODE + } + flatbuffers_string_t name = iree_vm_BytecodeModuleDef_name(module_def); if (!flatbuffers_string_len(name)) { return iree_make_status(IREE_STATUS_INVALID_ARGUMENT, @@ -317,9 +371,20 @@ iree_vm_BytecodeModuleDef_imported_functions(module_def); iree_vm_ExportFunctionDef_vec_t exported_functions = iree_vm_BytecodeModuleDef_exported_functions(module_def); + iree_vm_FunctionSignatureDef_vec_t function_signatures = + iree_vm_BytecodeModuleDef_function_signatures(module_def); iree_vm_FunctionDescriptor_vec_t function_descriptors = iree_vm_BytecodeModuleDef_function_descriptors(module_def); + if (iree_vm_FunctionSignatureDef_vec_len(function_signatures) != + iree_vm_FunctionDescriptor_vec_len(function_descriptors)) { + return iree_make_status( + IREE_STATUS_INVALID_ARGUMENT, + "function signature and descriptor table length mismatch (%zu vs %zu)", + iree_vm_FunctionSignatureDef_vec_len(function_signatures), + iree_vm_FunctionDescriptor_vec_len(function_descriptors)); + } + for (size_t i = 0; i < iree_vm_ImportFunctionDef_vec_len(imported_functions); ++i) { iree_vm_ImportFunctionDef_table_t import_def = @@ -362,6 +427,16 @@ } } + for (size_t i = 0; + i < iree_vm_FunctionSignatureDef_vec_len(function_signatures); ++i) { + iree_vm_FunctionSignatureDef_table_t function_signature = + iree_vm_FunctionSignatureDef_vec_at(function_signatures, i); + if (!function_signature) { + return iree_make_status(IREE_STATUS_INVALID_ARGUMENT, + "function_signatures[%zu] missing body", i); + } + } + // Verify that we can properly handle the bytecode embedded in the module. // We require that major versions match and allow loading of older minor // versions (we keep changes backwards-compatible). @@ -427,7 +502,8 @@ iree_vm_ExportFunctionDef_table_t function_def = iree_vm_ExportFunctionDef_vec_at(exported_functions, function.ordinal); ordinal = iree_vm_ExportFunctionDef_internal_ordinal(function_def); - signature_def = iree_vm_ExportFunctionDef_signature(function_def); + signature_def = iree_vm_FunctionSignatureDef_vec_at( + iree_vm_BytecodeModuleDef_function_signatures(module->def), ordinal); } else { // TODO(benvanik): support querying the internal functions, which could be // useful for debugging. Or maybe we just drop them forever? @@ -653,12 +729,16 @@ iree_vm_ExportFunctionDef_table_t export_def = iree_vm_ExportFunctionDef_vec_at(exported_functions, ordinal); name = iree_vm_ExportFunctionDef_local_name(export_def); - signature = iree_vm_ExportFunctionDef_signature(export_def); + signature = iree_vm_FunctionSignatureDef_vec_at( + iree_vm_BytecodeModuleDef_function_signatures(module->def), + iree_vm_ExportFunctionDef_internal_ordinal(export_def)); } else if (linkage == IREE_VM_FUNCTION_LINKAGE_INTERNAL) { #if IREE_VM_BACKTRACE_ENABLE name = iree_vm_bytecode_module_lookup_internal_function_name(module->def, ordinal); #endif // IREE_VM_BACKTRACE_ENABLE + signature = iree_vm_FunctionSignatureDef_vec_at( + iree_vm_BytecodeModuleDef_function_signatures(module->def), ordinal); } if (out_function) { @@ -692,6 +772,8 @@ iree_vm_bytecode_module_t* module = (iree_vm_bytecode_module_t*)self; iree_vm_ExportFunctionDef_vec_t exported_functions = iree_vm_BytecodeModuleDef_exported_functions(module->def); + iree_vm_FunctionSignatureDef_vec_t function_signatures = + iree_vm_BytecodeModuleDef_function_signatures(module->def); if (ordinal >= iree_vm_ExportFunctionDef_vec_len(exported_functions)) { return iree_make_status( @@ -703,7 +785,9 @@ iree_vm_ExportFunctionDef_table_t function_def = iree_vm_ExportFunctionDef_vec_at(exported_functions, ordinal); iree_vm_FunctionSignatureDef_table_t signature_def = - iree_vm_ExportFunctionDef_signature(function_def); + iree_vm_FunctionSignatureDef_vec_at( + function_signatures, + iree_vm_ExportFunctionDef_internal_ordinal(function_def)); if (!signature_def) { return iree_make_status( IREE_STATUS_NOT_FOUND,
diff --git a/runtime/src/iree/vm/bytecode/module_impl.h b/runtime/src/iree/vm/bytecode/module_impl.h index 6090f94..2b9728d 100644 --- a/runtime/src/iree/vm/bytecode/module_impl.h +++ b/runtime/src/iree/vm/bytecode/module_impl.h
@@ -33,7 +33,7 @@ // Major bytecode version; mismatches on this will fail in either direction. // This allows coarse versioning of completely incompatible versions. // Matches BytecodeEncoder::kVersionMajor in the compiler. -#define IREE_VM_BYTECODE_VERSION_MAJOR 13 +#define IREE_VM_BYTECODE_VERSION_MAJOR 14 // Minor bytecode version; lower versions are allowed to enable newer runtimes // to load older serialized files when there are backwards-compatible changes. // Higher versions are disallowed as they occur when new ops are added that
diff --git a/tests/compiler_driver/smoketest.mlir b/tests/compiler_driver/smoketest.mlir index 9f21cb3..b7fc954 100644 --- a/tests/compiler_driver/smoketest.mlir +++ b/tests/compiler_driver/smoketest.mlir
@@ -10,7 +10,10 @@ // CHECK: "function_descriptors": // CHECK-NEXT: { // CHECK-NEXT: "bytecode_offset": 0 -// CHECK-NEXT: "bytecode_length": 8 +// CHECK-NEXT: "bytecode_length": 6 +// CHECK-NEXT: "requirements": 0 +// CHECK-NEXT: "reserved": 0 +// CHECK-NEXT: "block_count": 1 // CHECK-NEXT: "i32_register_count": 1 // CHECK-NEXT: "ref_register_count": 0 // CHECK-NEXT: } @@ -19,11 +22,12 @@ } // CHECK: "bytecode_data": [ +// CHECK-NEXT: 121, // CHECK-NEXT: 90, -// CHECK-NEXT: 0, // CHECK-NEXT: 1, // CHECK-NEXT: 0, // CHECK-NEXT: 0, +// CHECK-NEXT: 0, } // -----
diff --git a/tools/iree-run-mlir-main.cc b/tools/iree-run-mlir-main.cc index 1a252e8..d7830fb 100644 --- a/tools/iree-run-mlir-main.cc +++ b/tools/iree-run-mlir-main.cc
@@ -285,12 +285,14 @@ // NOTE: if we have an output file specified then we could compile into that // for greater efficiency. Today we assume that users aren't passing multi-GB // models through this tool (or if they are they have the memory to run them). + auto vm_options = + mlir::iree_compiler::IREE::VM::TargetOptions::FromFlags::get(); auto bytecode_options = mlir::iree_compiler::IREE::VM::BytecodeTargetOptions::FromFlags::get(); std::string binary_contents; llvm::raw_string_ostream binary_output(binary_contents); if (failed(mlir::iree_compiler::IREE::VM::translateModuleToBytecode( - mlir_module.get(), bytecode_options, binary_output))) { + mlir_module.get(), vm_options, bytecode_options, binary_output))) { return iree_make_status( IREE_STATUS_INTERNAL, "serialization to flatbuffer bytecode (binary) failed"); @@ -305,7 +307,7 @@ std::string text_contents; llvm::raw_string_ostream text_output(text_contents); if (failed(mlir::iree_compiler::IREE::VM::translateModuleToBytecode( - mlir_module.get(), bytecode_options, text_output))) { + mlir_module.get(), vm_options, bytecode_options, text_output))) { return iree_make_status(IREE_STATUS_INTERNAL, "serialization to annotated MLIR (text) failed"); } @@ -318,7 +320,7 @@ std::string text_contents; llvm::raw_string_ostream text_output(text_contents); if (failed(mlir::iree_compiler::IREE::VM::translateModuleToBytecode( - mlir_module.get(), bytecode_options, text_output))) { + mlir_module.get(), vm_options, bytecode_options, text_output))) { return iree_make_status( IREE_STATUS_INTERNAL, "serialization to flatbuffer bytecode (text) failed");