Adding simple bytecode versioning. (#9133)
This allows for minor version bumps for backward-compatible changes
(allowing an older vmfb to be loaded on a newer runtime) and major
version bumps (disallowing older vmfbs from being loaded on a newer
runtime).
This first version 0.0 allows all existing modules to be loaded.
Future bumps will result in errors like:
```
E D:\Dev\iree\runtime\src\iree\vm\bytecode_module.c:209: INVALID_ARGUMENT;
bytecode version mismatch; runtime supports 0.0, module has 0.1
```
Note that this only versions the bytecode - the module itself is already
versioned with flatbuffers and doesn't need any version numbers. This
will also allow us to embed bytecode in other containers (ELF/etc) and
reuse the same versioning information.
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 94d49a2..0b2e4c2 100644
--- a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.h
+++ b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeEncoder.h
@@ -31,6 +31,10 @@
// Abstract encoder used for function bytecode encoding.
class BytecodeEncoder : public VMFuncEncoder {
public:
+ static constexpr uint32_t kVersionMajor = 0;
+ static constexpr uint32_t kVersionMinor = 0;
+ static constexpr uint32_t kVersion = (kVersionMajor << 16) | kVersionMinor;
+
// Encodes a vm.func to bytecode and returns the result.
// Returns None on failure.
static Optional<EncodedBytecodeFunction> encodeFunction(
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 20c05fe..17c9d28 100644
--- a/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeModuleTarget.cpp
+++ b/compiler/src/iree/compiler/Dialect/VM/Target/Bytecode/BytecodeModuleTarget.cpp
@@ -768,6 +768,8 @@
iree_vm_BytecodeModuleDef_rwdata_segments_add(fbb, rwdataSegmentsRef);
iree_vm_BytecodeModuleDef_function_descriptors_add(fbb,
functionDescriptorsRef);
+ iree_vm_BytecodeModuleDef_bytecode_version_add(fbb,
+ BytecodeEncoder::kVersion);
iree_vm_BytecodeModuleDef_bytecode_data_add(fbb, bytecodeDataRef);
iree_vm_BytecodeModuleDef_debug_database_add(fbb, debugDatabaseRef);
iree_vm_BytecodeModuleDef_end_as_root(fbb);
diff --git a/runtime/src/iree/schemas/bytecode_module_def.fbs b/runtime/src/iree/schemas/bytecode_module_def.fbs
index df15e9e..21d5ca7 100644
--- a/runtime/src/iree/schemas/bytecode_module_def.fbs
+++ b/runtime/src/iree/schemas/bytecode_module_def.fbs
@@ -236,6 +236,11 @@
// bytecode_data.
function_descriptors:[FunctionDescriptor];
+ // Bytecode version required by the embedded bytecode data.
+ // Two 16-bit ints representing major and minor version, with minor versions
+ // being backwards compatible.
+ bytecode_version:uint32;
+
// Bytecode contents. One large buffer containing all of the function op data.
bytecode_data:[uint8];
diff --git a/runtime/src/iree/vm/bytecode_module.c b/runtime/src/iree/vm/bytecode_module.c
index 192464d..338043c 100644
--- a/runtime/src/iree/vm/bytecode_module.c
+++ b/runtime/src/iree/vm/bytecode_module.c
@@ -196,6 +196,22 @@
}
}
+ // 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).
+ const uint32_t bytecode_version =
+ iree_vm_BytecodeModuleDef_bytecode_version(module_def);
+ const uint32_t bytecode_version_major = bytecode_version >> 16;
+ const uint32_t bytecode_version_minor = bytecode_version & 0xFFFF;
+ if ((bytecode_version_major != IREE_VM_BYTECODE_VERSION_MAJOR) ||
+ (bytecode_version_minor > IREE_VM_BYTECODE_VERSION_MINOR)) {
+ return iree_make_status(
+ IREE_STATUS_INVALID_ARGUMENT,
+ "bytecode version mismatch; runtime supports %d.%d, module has %d.%d",
+ IREE_VM_BYTECODE_VERSION_MAJOR, IREE_VM_BYTECODE_VERSION_MINOR,
+ bytecode_version_major, bytecode_version_minor);
+ }
+
flatbuffers_uint8_vec_t bytecode_data =
iree_vm_BytecodeModuleDef_bytecode_data(module_def);
for (size_t i = 0;
diff --git a/runtime/src/iree/vm/bytecode_module_impl.h b/runtime/src/iree/vm/bytecode_module_impl.h
index 01031b1..75fadb5 100644
--- a/runtime/src/iree/vm/bytecode_module_impl.h
+++ b/runtime/src/iree/vm/bytecode_module_impl.h
@@ -30,6 +30,15 @@
#define VMMAX(a, b) (((a) > (b)) ? (a) : (b))
#define VMMIN(a, b) (((a) < (b)) ? (a) : (b))
+// Major bytecode version; mismatches on this will fail in either direction.
+// This allows coarse versioning of completely incompatible versions.
+#define IREE_VM_BYTECODE_VERSION_MAJOR 0
+// 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
+// otherwise cannot be executed by older runtimes.
+#define IREE_VM_BYTECODE_VERSION_MINOR 0
+
// Maximum register count per bank.
// This determines the bits required to reference registers in the VM bytecode.
#define IREE_I32_REGISTER_COUNT 0x7FFF