Start on "dylib" HAL backend for running code from dynamic libraries.

The plan here is to use this for LLVM ahead-of-time (AOT) execution: a new compiler target will generate modules targeting this HAL backend that will contain ahead-of-time compiled platform/architecture-specific dynamic libraries (.so/.dll/.dylib).

This is modeled after iree/hal/llvmjit, but is intended to be generic and should not directly reference LLVM code or concepts. Existing restrictions on dynamic shapes, op coverage, etc. from llvmjit are mirrored here for the moment.

I've partially tested manually using some test .dll files and a (next PR) LLVMAOTTarget on the compiler side. I'd like to expand on `iree/hal/cts/` coverage soon.

Closes https://github.com/google/iree/pull/2099

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/iree/pull/2099 from ScottTodd:dylib d6356b683104d9e6d9bbd9f592a983787013e716
PiperOrigin-RevId: 314794646
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1eeeb07..bcb4876 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,6 +69,7 @@
 
 # List of all HAL drivers to be built by default:
 set(IREE_ALL_HAL_DRIVERS
+  DyLib
   LLVM
   Vulkan
   VMLA
diff --git a/iree/build_defs.oss.bzl b/iree/build_defs.oss.bzl
index f059a54..6b1184d 100644
--- a/iree/build_defs.oss.bzl
+++ b/iree/build_defs.oss.bzl
@@ -56,6 +56,7 @@
 IREE_DRIVER_MODULES = [
     # TODO(b/142004903): enable when Dawn HAL implementation is functional
     # "//iree/hal/dawn:dawn_driver_module",
+    "//iree/hal/dylib:dylib_driver_module",
     "//iree/hal/vmla:vmla_driver_module",
     "//iree/hal/vulkan:vulkan_driver_module",
     "//iree/hal/llvmjit:llvmjit_driver_module",
diff --git a/iree/compiler/Dialect/HAL/IR/HALBase.td b/iree/compiler/Dialect/HAL/IR/HALBase.td
index e39f135..dacc716 100644
--- a/iree/compiler/Dialect/HAL/IR/HALBase.td
+++ b/iree/compiler/Dialect/HAL/IR/HALBase.td
@@ -236,6 +236,7 @@
 def HAL_EF_VMLA : I32EnumAttrCase<"VMLA", 1447906369>;
 def HAL_EF_SpirV : I32EnumAttrCase<"SpirV", 1397773893>;
 def HAL_EF_LLVM : I32EnumAttrCase<"LLVM", 1280071245>;
+def HAL_EF_DyLib : I32EnumAttrCase<"DyLib", 1145850178>;
 def HAL_ExecutableFormatAttr :
     I32EnumAttr<"ExecutableFormat", "IREE HAL Executable format", [
       HAL_EF_Unspecified,
@@ -243,7 +244,8 @@
       HAL_EF_IreeBytecode,
       HAL_EF_VMLA,
       HAL_EF_SpirV,
-      HAL_EF_LLVM
+      HAL_EF_LLVM,
+      HAL_EF_DyLib
     ]> {
   let returnType = "IREE::HAL::ExecutableFormat";
   let convertFromStorage = "static_cast<IREE::HAL::ExecutableFormat>($_self.getInt())";
diff --git a/iree/hal/dylib/BUILD b/iree/hal/dylib/BUILD
new file mode 100644
index 0000000..b4ba1f1
--- /dev/null
+++ b/iree/hal/dylib/BUILD
@@ -0,0 +1,126 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# HAL implementation for executing functions provided by dynamic libraries.
+
+package(
+    default_visibility = ["//visibility:public"],
+    licenses = ["notice"],  # Apache 2.0
+)
+
+cc_library(
+    name = "dylib_executable",
+    srcs = ["dylib_executable.cc"],
+    hdrs = ["dylib_executable.h"],
+    deps = [
+        "//iree/base:dynamic_library",
+        "//iree/base:file_io",
+        "//iree/base:status",
+        "//iree/hal:allocator",
+        "//iree/hal:executable",
+        "//iree/hal:executable_spec",
+        "//iree/schemas:dylib_executable_def_cc_fbs",
+        "@com_github_google_flatbuffers//:flatbuffers",
+        "@com_google_absl//absl/container:inlined_vector",
+        "@com_google_absl//absl/types:span",
+    ],
+)
+
+cc_library(
+    name = "dylib_command_processor",
+    srcs = ["dylib_command_processor.cc"],
+    hdrs = ["dylib_command_processor.h"],
+    deps = [
+        ":dylib_executable",
+        ":memref_runtime",
+        "//iree/base:tracing",
+        "//iree/hal:buffer",
+        "//iree/hal/host:host_local_command_processor",
+        "@com_google_absl//absl/container:inlined_vector",
+    ],
+)
+
+cc_library(
+    name = "dylib_executable_cache",
+    srcs = ["dylib_executable_cache.cc"],
+    hdrs = ["dylib_executable_cache.h"],
+    deps = [
+        ":dylib_executable",
+        "//iree/base:source_location",
+        "//iree/base:status",
+        "//iree/base:tracing",
+        "//iree/hal:allocator",
+        "//iree/hal:executable",
+        "//iree/hal:executable_cache",
+        "//iree/hal:executable_format",
+    ],
+)
+
+cc_library(
+    name = "dylib_device",
+    srcs = ["dylib_device.cc"],
+    hdrs = ["dylib_device.h"],
+    deps = [
+        ":dylib_command_processor",
+        ":dylib_executable_cache",
+        "//iree/base:memory",
+        "//iree/base:status",
+        "//iree/base:tracing",
+        "//iree/hal:command_buffer_validation",
+        "//iree/hal:command_queue",
+        "//iree/hal:device",
+        "//iree/hal:semaphore",
+        "//iree/hal/host:async_command_queue",
+        "//iree/hal/host:host_descriptor_set",
+        "//iree/hal/host:host_event",
+        "//iree/hal/host:host_executable_layout",
+        "//iree/hal/host:host_local_allocator",
+        "//iree/hal/host:host_submission_queue",
+        "//iree/hal/host:inproc_command_buffer",
+        "@com_google_absl//absl/container:inlined_vector",
+        "@com_google_absl//absl/memory",
+        "@com_google_absl//absl/strings",
+        "@com_google_absl//absl/types:span",
+    ],
+)
+
+cc_library(
+    name = "dylib_driver",
+    srcs = ["dylib_driver.cc"],
+    hdrs = ["dylib_driver.h"],
+    deps = [
+        ":dylib_device",
+        "//iree/hal:device_info",
+        "//iree/hal:driver",
+    ],
+)
+
+cc_library(
+    name = "dylib_driver_module",
+    srcs = ["dylib_driver_module.cc"],
+    deps = [
+        ":dylib_driver",
+        "//iree/base:init",
+        "//iree/base:status",
+        "//iree/hal:driver_registry",
+    ],
+    alwayslink = 1,
+)
+
+cc_library(
+    name = "memref_runtime",
+    hdrs = [
+        "memref_runtime.h",
+    ],
+)
diff --git a/iree/hal/dylib/CMakeLists.txt b/iree/hal/dylib/CMakeLists.txt
new file mode 100644
index 0000000..9bd4e7d
--- /dev/null
+++ b/iree/hal/dylib/CMakeLists.txt
@@ -0,0 +1,139 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+iree_add_all_subdirs()
+
+iree_cc_library(
+  NAME
+    dylib_executable
+  HDRS
+    "dylib_executable.h"
+  SRCS
+    "dylib_executable.cc"
+  DEPS
+    absl::inlined_vector
+    absl::span
+    flatbuffers
+    iree::base::dynamic_library
+    iree::base::file_io
+    iree::base::status
+    iree::hal::allocator
+    iree::hal::executable
+    iree::hal::executable_spec
+    iree::schemas::dylib_executable_def_cc_fbs
+  PUBLIC
+)
+
+iree_cc_library(
+  NAME
+    dylib_command_processor
+  HDRS
+    "dylib_command_processor.h"
+  SRCS
+    "dylib_command_processor.cc"
+  DEPS
+    ::dylib_executable
+    ::memref_runtime
+    absl::inlined_vector
+    iree::base::tracing
+    iree::hal::buffer
+    iree::hal::host::host_local_command_processor
+  PUBLIC
+)
+
+iree_cc_library(
+  NAME
+    dylib_executable_cache
+  HDRS
+    "dylib_executable_cache.h"
+  SRCS
+    "dylib_executable_cache.cc"
+  DEPS
+    ::dylib_executable
+    iree::base::source_location
+    iree::base::status
+    iree::base::tracing
+    iree::hal::allocator
+    iree::hal::executable
+    iree::hal::executable_cache
+    iree::hal::executable_format
+  PUBLIC
+)
+
+iree_cc_library(
+  NAME
+    dylib_device
+  HDRS
+    "dylib_device.h"
+  SRCS
+    "dylib_device.cc"
+  DEPS
+    ::dylib_command_processor
+    ::dylib_executable_cache
+    absl::inlined_vector
+    absl::memory
+    absl::span
+    absl::strings
+    iree::base::memory
+    iree::base::status
+    iree::base::tracing
+    iree::hal::command_buffer_validation
+    iree::hal::command_queue
+    iree::hal::device
+    iree::hal::host::async_command_queue
+    iree::hal::host::host_descriptor_set
+    iree::hal::host::host_event
+    iree::hal::host::host_executable_layout
+    iree::hal::host::host_local_allocator
+    iree::hal::host::host_submission_queue
+    iree::hal::host::inproc_command_buffer
+    iree::hal::semaphore
+  PUBLIC
+)
+
+iree_cc_library(
+  NAME
+    dylib_driver
+  HDRS
+    "dylib_driver.h"
+  SRCS
+    "dylib_driver.cc"
+  DEPS
+    ::dylib_device
+    iree::hal::device_info
+    iree::hal::driver
+  PUBLIC
+)
+
+iree_cc_library(
+  NAME
+    dylib_driver_module
+  SRCS
+    "dylib_driver_module.cc"
+  DEPS
+    ::dylib_driver
+    iree::base::init
+    iree::base::status
+    iree::hal::driver_registry
+  ALWAYSLINK
+  PUBLIC
+)
+
+iree_cc_library(
+  NAME
+    memref_runtime
+  HDRS
+    "memref_runtime.h"
+  PUBLIC
+)
diff --git a/iree/hal/dylib/dylib_command_processor.cc b/iree/hal/dylib/dylib_command_processor.cc
new file mode 100644
index 0000000..3e180a8
--- /dev/null
+++ b/iree/hal/dylib/dylib_command_processor.cc
@@ -0,0 +1,69 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "iree/hal/dylib/dylib_command_processor.h"
+
+#include <memory>
+#include <vector>
+
+#include "absl/container/inlined_vector.h"
+#include "iree/base/tracing.h"
+#include "iree/hal/buffer.h"
+#include "iree/hal/dylib/dylib_executable.h"
+#include "iree/hal/dylib/memref_runtime.h"
+
+namespace iree {
+namespace hal {
+namespace dylib {
+
+DyLibCommandProcessor::DyLibCommandProcessor(
+    Allocator* allocator, CommandCategoryBitfield command_categories)
+    : HostLocalCommandProcessor(allocator, command_categories) {}
+
+DyLibCommandProcessor::~DyLibCommandProcessor() = default;
+
+Status DyLibCommandProcessor::DispatchInline(
+    Executable* executable, int32_t entry_point,
+    std::array<uint32_t, 3> workgroups, const PushConstantBlock& push_constants,
+    absl::Span<const absl::Span<const DescriptorSet::Binding>> set_bindings) {
+  IREE_TRACE_SCOPE0("DyLibCommandProcessor::DispatchInline");
+
+  auto* dylib_executable = static_cast<DyLibExecutable*>(executable);
+
+  absl::InlinedVector<UnrankedMemRefType<uint32_t>*, 4> descriptors;
+  absl::InlinedVector<void*, 4> args;
+  for (size_t set = 0; set < set_bindings.size(); ++set) {
+    for (size_t binding = 0; binding < set_bindings[set].size(); ++binding) {
+      const auto& io_binding = set_bindings[set][binding];
+      ASSIGN_OR_RETURN(auto memory, io_binding.buffer->MapMemory<uint8_t>(
+                                        MemoryAccessBitfield::kWrite,
+                                        io_binding.offset, io_binding.length));
+      auto data = memory.mutable_data();
+      auto descriptor = allocUnrankedDescriptor<uint32_t>(data);
+      descriptors.push_back(descriptor);
+      args.push_back(&descriptor->descriptor);
+    }
+  }
+  auto status = dylib_executable->Invoke(entry_point, absl::MakeSpan(args));
+
+  for (int i = 0; i < descriptors.size(); ++i) {
+    freeUnrankedDescriptor(descriptors[i]);
+  }
+
+  return status;
+}
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
diff --git a/iree/hal/dylib/dylib_command_processor.h b/iree/hal/dylib/dylib_command_processor.h
new file mode 100644
index 0000000..716f9ec
--- /dev/null
+++ b/iree/hal/dylib/dylib_command_processor.h
@@ -0,0 +1,42 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef IREE_HAL_DYLIB_DYLIB_COMMAND_PROCESSOR_H_
+#define IREE_HAL_DYLIB_DYLIB_COMMAND_PROCESSOR_H_
+
+#include "iree/hal/host/host_local_command_processor.h"
+
+namespace iree {
+namespace hal {
+namespace dylib {
+
+class DyLibCommandProcessor final : public HostLocalCommandProcessor {
+ public:
+  DyLibCommandProcessor(Allocator* allocator,
+                        CommandCategoryBitfield command_categories);
+  ~DyLibCommandProcessor() override;
+
+  Status DispatchInline(
+      Executable* executable, int32_t entry_point,
+      std::array<uint32_t, 3> workgroups,
+      const PushConstantBlock& push_constants,
+      absl::Span<const absl::Span<const DescriptorSet::Binding>> set_bindings)
+      override;
+};
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
+
+#endif  // IREE_HAL_DYLIB_DYLIB_COMMAND_PROCESSOR_H_
diff --git a/iree/hal/dylib/dylib_device.cc b/iree/hal/dylib/dylib_device.cc
new file mode 100644
index 0000000..da71caa
--- /dev/null
+++ b/iree/hal/dylib/dylib_device.cc
@@ -0,0 +1,193 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "iree/hal/dylib/dylib_device.h"
+
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "absl/strings/str_cat.h"
+#include "iree/base/status.h"
+#include "iree/base/tracing.h"
+#include "iree/hal/command_buffer_validation.h"
+#include "iree/hal/command_queue.h"
+#include "iree/hal/dylib/dylib_command_processor.h"
+#include "iree/hal/dylib/dylib_executable_cache.h"
+#include "iree/hal/host/async_command_queue.h"
+#include "iree/hal/host/host_descriptor_set.h"
+#include "iree/hal/host/host_event.h"
+#include "iree/hal/host/host_executable_layout.h"
+#include "iree/hal/host/host_submission_queue.h"
+#include "iree/hal/host/inproc_command_buffer.h"
+#include "iree/hal/semaphore.h"
+
+namespace iree {
+namespace hal {
+namespace dylib {
+
+namespace {
+
+// A CommandQueue that performs no synchronization (semaphores/fences) and just
+// directly executes command buffers inline.
+//
+// This is meant to be wrapped by SyncCommandQueue or AsyncCommandQueue that
+// themselves perform the synchronization/threading/etc. As such we ignore
+// all semaphores in the provided batches under the assumption that if Submit is
+// being called then all dependencies are valid. The wrapping queue is also
+// responsible for signaling the fence as well as propagating errors in a way
+// that is dependent on how it is performing its synchronization.
+class UnsynchronizedCommandQueue final : public CommandQueue {
+ public:
+  UnsynchronizedCommandQueue(Allocator* allocator, std::string name,
+                             CommandCategoryBitfield supported_categories)
+      : CommandQueue(std::move(name), supported_categories),
+        allocator_(allocator) {}
+  ~UnsynchronizedCommandQueue() override = default;
+
+  Status Submit(absl::Span<const SubmissionBatch> batches) override {
+    IREE_TRACE_SCOPE0("UnsynchronizedCommandQueue::Submit");
+
+    // Process command buffers and propagate errors asynchronously through the
+    // fence. This ensures that even if we are running synchronously we still
+    // get consistent failure behavior with drivers that are purely async.
+    for (auto& batch : batches) {
+      DCHECK(batch.wait_semaphores.empty() && batch.signal_semaphores.empty())
+          << "Semaphores must be handled by the wrapping queue";
+      RETURN_IF_ERROR(ProcessCommandBuffers(batch.command_buffers));
+    }
+
+    return OkStatus();
+  }
+
+  Status WaitIdle(absl::Time deadline) override {
+    // No-op.
+    return OkStatus();
+  }
+
+ private:
+  // Processes each command buffer in-turn with a fresh processor.
+  // This ensures we don't have any state that can carry across buffers.
+  Status ProcessCommandBuffers(
+      absl::Span<CommandBuffer* const> command_buffers) {
+    IREE_TRACE_SCOPE0("UnsynchronizedCommandQueue::ProcessCommandBuffers");
+    for (auto* command_buffer : command_buffers) {
+      auto* inproc_command_buffer =
+          static_cast<InProcCommandBuffer*>(command_buffer->impl());
+      DyLibCommandProcessor command_processor(allocator_,
+                                              supported_categories());
+      RETURN_IF_ERROR(inproc_command_buffer->Process(&command_processor));
+    }
+    return OkStatus();
+  }
+
+  Allocator* const allocator_;
+};
+
+}  // namespace
+
+DyLibDevice::DyLibDevice(DeviceInfo device_info)
+    : Device(std::move(device_info)) {
+  // We currently only expose a single command queue.
+  auto command_queue = absl::make_unique<UnsynchronizedCommandQueue>(
+      &allocator_, "cpu0",
+      CommandCategory::kTransfer | CommandCategory::kDispatch);
+
+  // TODO(benvanik): allow injection of the wrapper type to support
+  // SyncCommandQueue without always linking in both.
+  auto async_command_queue =
+      absl::make_unique<AsyncCommandQueue>(std::move(command_queue));
+  command_queues_.push_back(std::move(async_command_queue));
+}
+
+StatusOr<ref_ptr<DyLibDevice>> DyLibDevice::CreateDyLibDevice(
+    DeviceInfo device_info) {
+  return make_ref<DyLibDevice>(device_info);
+}
+
+DyLibDevice::~DyLibDevice() = default;
+
+std::string DyLibDevice::DebugString() const {
+  return absl::StrCat(Device::DebugString(),  //
+                      "\n[DyLibDevice]",      //
+                      "\n  Command Queues: ", command_queues_.size());
+}
+
+ref_ptr<ExecutableCache> DyLibDevice::CreateExecutableCache() {
+  IREE_TRACE_SCOPE0("DyLibDevice::CreateExecutableCache");
+  return make_ref<DyLibExecutableCache>(&allocator_);
+}
+
+StatusOr<ref_ptr<DescriptorSetLayout>> DyLibDevice::CreateDescriptorSetLayout(
+    DescriptorSetLayout::UsageType usage_type,
+    absl::Span<const DescriptorSetLayout::Binding> bindings) {
+  IREE_TRACE_SCOPE0("DyLibDevice::CreateDescriptorSetLayout");
+  return make_ref<HostDescriptorSetLayout>(usage_type, bindings);
+}
+
+StatusOr<ref_ptr<ExecutableLayout>> DyLibDevice::CreateExecutableLayout(
+    absl::Span<DescriptorSetLayout* const> set_layouts, size_t push_constants) {
+  IREE_TRACE_SCOPE0("DyLibDevice::CreateExecutableLayout");
+  return make_ref<HostExecutableLayout>(set_layouts, push_constants);
+}
+
+StatusOr<ref_ptr<DescriptorSet>> DyLibDevice::CreateDescriptorSet(
+    DescriptorSetLayout* set_layout,
+    absl::Span<const DescriptorSet::Binding> bindings) {
+  IREE_TRACE_SCOPE0("DyLibDevice::CreateDescriptorSet");
+  return make_ref<HostDescriptorSet>(set_layout, bindings);
+}
+
+StatusOr<ref_ptr<CommandBuffer>> DyLibDevice::CreateCommandBuffer(
+    CommandBufferModeBitfield mode,
+    CommandCategoryBitfield command_categories) {
+  // TODO(b/140026716): conditionally enable validation.
+  auto impl =
+      make_ref<InProcCommandBuffer>(&allocator_, mode, command_categories);
+  return WrapCommandBufferWithValidation(std::move(impl));
+}
+
+StatusOr<ref_ptr<Event>> DyLibDevice::CreateEvent() {
+  return make_ref<HostEvent>();
+}
+
+StatusOr<ref_ptr<Semaphore>> DyLibDevice::CreateSemaphore(
+    uint64_t initial_value) {
+  IREE_TRACE_SCOPE0("DyLibDevice::CreateSemaphore");
+  return make_ref<HostSemaphore>(initial_value);
+}
+
+Status DyLibDevice::WaitAllSemaphores(
+    absl::Span<const SemaphoreValue> semaphores, absl::Time deadline) {
+  IREE_TRACE_SCOPE0("DyLibDevice::WaitAllSemaphores");
+  return HostSemaphore::WaitForSemaphores(semaphores, /*wait_all=*/true,
+                                          deadline);
+}
+
+StatusOr<int> DyLibDevice::WaitAnySemaphore(
+    absl::Span<const SemaphoreValue> semaphores, absl::Time deadline) {
+  IREE_TRACE_SCOPE0("DyLibDevice::WaitAnySemaphore");
+  return HostSemaphore::WaitForSemaphores(semaphores, /*wait_all=*/false,
+                                          deadline);
+}
+
+Status DyLibDevice::WaitIdle(absl::Time deadline) {
+  for (auto& command_queue : command_queues_) {
+    RETURN_IF_ERROR(command_queue->WaitIdle(deadline));
+  }
+  return OkStatus();
+}
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
diff --git a/iree/hal/dylib/dylib_device.h b/iree/hal/dylib/dylib_device.h
new file mode 100644
index 0000000..3bcea9a
--- /dev/null
+++ b/iree/hal/dylib/dylib_device.h
@@ -0,0 +1,84 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef IREE_HAL_DYLIB_DYLIB_DEVICE_H_
+#define IREE_HAL_DYLIB_DYLIB_DEVICE_H_
+
+#include "absl/container/inlined_vector.h"
+#include "absl/types/span.h"
+#include "iree/base/memory.h"
+#include "iree/hal/device.h"
+#include "iree/hal/host/host_local_allocator.h"
+
+namespace iree {
+namespace hal {
+namespace dylib {
+
+class DyLibDevice final : public Device {
+ public:
+  static StatusOr<ref_ptr<DyLibDevice>> CreateDyLibDevice(
+      DeviceInfo device_info);
+  explicit DyLibDevice(DeviceInfo device_info);
+  ~DyLibDevice() override;
+
+  std::string DebugString() const override;
+
+  Allocator* allocator() const override { return &allocator_; }
+
+  absl::Span<CommandQueue*> dispatch_queues() const override {
+    return RawPtrSpan(absl::MakeSpan(command_queues_));
+  }
+
+  absl::Span<CommandQueue*> transfer_queues() const override {
+    return RawPtrSpan(absl::MakeSpan(command_queues_));
+  }
+
+  ref_ptr<ExecutableCache> CreateExecutableCache() override;
+
+  StatusOr<ref_ptr<DescriptorSetLayout>> CreateDescriptorSetLayout(
+      DescriptorSetLayout::UsageType usage_type,
+      absl::Span<const DescriptorSetLayout::Binding> bindings) override;
+
+  StatusOr<ref_ptr<ExecutableLayout>> CreateExecutableLayout(
+      absl::Span<DescriptorSetLayout* const> set_layouts,
+      size_t push_constants) override;
+
+  StatusOr<ref_ptr<DescriptorSet>> CreateDescriptorSet(
+      DescriptorSetLayout* set_layout,
+      absl::Span<const DescriptorSet::Binding> bindings) override;
+
+  StatusOr<ref_ptr<CommandBuffer>> CreateCommandBuffer(
+      CommandBufferModeBitfield mode,
+      CommandCategoryBitfield command_categories) override;
+
+  StatusOr<ref_ptr<Event>> CreateEvent() override;
+
+  StatusOr<ref_ptr<Semaphore>> CreateSemaphore(uint64_t initial_value) override;
+  Status WaitAllSemaphores(absl::Span<const SemaphoreValue> semaphores,
+                           absl::Time deadline) override;
+  StatusOr<int> WaitAnySemaphore(absl::Span<const SemaphoreValue> semaphores,
+                                 absl::Time deadline) override;
+
+  Status WaitIdle(absl::Time deadline) override;
+
+ private:
+  mutable HostLocalAllocator allocator_;
+  mutable absl::InlinedVector<std::unique_ptr<CommandQueue>, 1> command_queues_;
+};
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
+
+#endif  // IREE_HAL_DYLIB_DYLIB_DEVICE_H_
diff --git a/iree/hal/dylib/dylib_driver.cc b/iree/hal/dylib/dylib_driver.cc
new file mode 100644
index 0000000..249fc55
--- /dev/null
+++ b/iree/hal/dylib/dylib_driver.cc
@@ -0,0 +1,63 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "iree/hal/dylib/dylib_driver.h"
+
+#include <memory>
+
+#include "iree/hal/device_info.h"
+#include "iree/hal/dylib/dylib_device.h"
+
+namespace iree {
+namespace hal {
+namespace dylib {
+namespace {
+
+DeviceInfo GetDefaultDeviceInfo() {
+  DeviceFeatureBitfield supported_features = DeviceFeature::kNone;
+  // TODO(benvanik): implement debugging/profiling features.
+  // supported_features |= DeviceFeature::kDebugging;
+  // supported_features |= DeviceFeature::kCoverage;
+  // supported_features |= DeviceFeature::kProfiling;
+  DeviceInfo device_info("dylib", "Dynamic Library (dylib)",
+                         supported_features);
+  // TODO(benvanik): device info.
+  return device_info;
+}
+
+}  // namespace
+
+DyLibDriver::DyLibDriver() : Driver("dylib") {}
+
+DyLibDriver::~DyLibDriver() = default;
+
+StatusOr<std::vector<DeviceInfo>> DyLibDriver::EnumerateAvailableDevices() {
+  std::vector<DeviceInfo> device_infos;
+  device_infos.push_back(GetDefaultDeviceInfo());
+  return device_infos;
+}
+
+StatusOr<ref_ptr<Device>> DyLibDriver::CreateDefaultDevice() {
+  // Only one device, pass a dummy device_id.
+  return CreateDevice(0);
+}
+
+StatusOr<ref_ptr<Device>> DyLibDriver::CreateDevice(DriverDeviceID device_id) {
+  // Only one device, ignore device_id.
+  return DyLibDevice::CreateDyLibDevice(GetDefaultDeviceInfo());
+}
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
diff --git a/iree/hal/dylib/dylib_driver.h b/iree/hal/dylib/dylib_driver.h
new file mode 100644
index 0000000..a840271
--- /dev/null
+++ b/iree/hal/dylib/dylib_driver.h
@@ -0,0 +1,40 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef IREE_HAL_DYLIB_DYLIB_DRIVER_H_
+#define IREE_HAL_DYLIB_DYLIB_DRIVER_H_
+
+#include "iree/hal/driver.h"
+
+namespace iree {
+namespace hal {
+namespace dylib {
+
+class DyLibDriver final : public Driver {
+ public:
+  DyLibDriver();
+  ~DyLibDriver() override;
+
+  StatusOr<std::vector<DeviceInfo>> EnumerateAvailableDevices() override;
+
+  StatusOr<ref_ptr<Device>> CreateDefaultDevice() override;
+
+  StatusOr<ref_ptr<Device>> CreateDevice(DriverDeviceID device_id) override;
+};
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
+
+#endif  // IREE_HAL_DYLIB_DYLIB_DRIVER_H_
diff --git a/iree/hal/dylib/dylib_driver_module.cc b/iree/hal/dylib/dylib_driver_module.cc
new file mode 100644
index 0000000..2ef0089
--- /dev/null
+++ b/iree/hal/dylib/dylib_driver_module.cc
@@ -0,0 +1,38 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <memory>
+
+#include "iree/base/init.h"
+#include "iree/base/status.h"
+#include "iree/hal/driver_registry.h"
+#include "iree/hal/dylib/dylib_driver.h"
+
+namespace iree {
+namespace hal {
+namespace dylib {
+
+static StatusOr<ref_ptr<Driver>> CreateDyLibDriver() {
+  return make_ref<DyLibDriver>();
+}
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
+
+IREE_REGISTER_MODULE_INITIALIZER(iree_hal_dylib_driver, {
+  QCHECK_OK(::iree::hal::DriverRegistry::shared_registry()->Register(
+      "dylib", ::iree::hal::dylib::CreateDyLibDriver));
+});
+IREE_REGISTER_MODULE_INITIALIZER_SEQUENCE(iree_hal, iree_hal_dylib_driver);
diff --git a/iree/hal/dylib/dylib_executable.cc b/iree/hal/dylib/dylib_executable.cc
new file mode 100644
index 0000000..a7b7c14
--- /dev/null
+++ b/iree/hal/dylib/dylib_executable.cc
@@ -0,0 +1,99 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "iree/hal/dylib/dylib_executable.h"
+
+#include "flatbuffers/flatbuffers.h"
+#include "iree/base/file_io.h"
+#include "iree/schemas/dylib_executable_def_generated.h"
+
+namespace iree {
+namespace hal {
+namespace dylib {
+
+// static
+StatusOr<ref_ptr<DyLibExecutable>> DyLibExecutable::Load(
+    hal::Allocator* allocator, ExecutableSpec spec) {
+  auto executable = make_ref<DyLibExecutable>(spec);
+  RETURN_IF_ERROR(executable->Initialize());
+  return executable;
+}
+
+Status DyLibExecutable::Initialize() {
+  auto dylib_executable_def =
+      ::flatbuffers::GetRoot<DyLibExecutableDef>(spec_.executable_data.data());
+
+  if (!dylib_executable_def->entry_points() ||
+      dylib_executable_def->entry_points()->size() == 0) {
+    return InvalidArgumentErrorBuilder(IREE_LOC) << "No entry points defined";
+  }
+  if (!dylib_executable_def->library_embedded() ||
+      dylib_executable_def->library_embedded()->size() == 0) {
+    return InvalidArgumentErrorBuilder(IREE_LOC) << "No embedded library";
+  }
+
+  // Write the embedded library out to a temp file, since all of the dynamic
+  // library APIs work with files. We could instead use in-memory files on
+  // platforms where that is convenient.
+  std::string base_name = "dylib_executable";
+  ASSIGN_OR_RETURN(executable_library_temp_path_,
+                   file_io::GetTempFile(base_name));
+  // Add platform-specific file extensions so opinionated dynamic library
+  // loaders are more likely to find the file:
+#if defined(IREE_PLATFORM_WINDOWS)
+  executable_library_temp_path_ += ".dll";
+#else
+  executable_library_temp_path_ += ".so";
+#endif
+
+  absl::string_view embedded_library_data(
+      reinterpret_cast<const char*>(
+          dylib_executable_def->library_embedded()->data()),
+      dylib_executable_def->library_embedded()->size());
+  RETURN_IF_ERROR(file_io::SetFileContents(executable_library_temp_path_,
+                                           embedded_library_data));
+
+  ASSIGN_OR_RETURN(executable_library_,
+                   DynamicLibrary::Load(executable_library_temp_path_.c_str()));
+
+  const auto& entry_points = *dylib_executable_def->entry_points();
+  entry_functions_.resize(entry_points.size());
+  for (int i = 0; i < entry_functions_.size(); ++i) {
+    void* symbol = executable_library_->GetSymbol(entry_points[i]->c_str());
+    if (!symbol) {
+      return NotFoundErrorBuilder(IREE_LOC)
+             << "Could not find symbol: " << entry_points[i];
+    }
+    entry_functions_[i] = symbol;
+  }
+
+  return OkStatus();
+}
+
+DyLibExecutable::DyLibExecutable(ExecutableSpec spec) : spec_(spec) {}
+
+DyLibExecutable::~DyLibExecutable() {
+  executable_library_.reset();
+  if (!executable_library_temp_path_.empty()) {
+    file_io::DeleteFile(executable_library_temp_path_).IgnoreError();
+  }
+}
+
+Status DyLibExecutable::Invoke(int func_id, absl::Span<void*> args) const {
+  return UnimplementedErrorBuilder(IREE_LOC) << "DyLibExecutable::Invoke NYI";
+}
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
diff --git a/iree/hal/dylib/dylib_executable.h b/iree/hal/dylib/dylib_executable.h
new file mode 100644
index 0000000..627642a
--- /dev/null
+++ b/iree/hal/dylib/dylib_executable.h
@@ -0,0 +1,59 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef IREE_HAL_DYLIB_DYLIB_EXECUTABLE_H_
+#define IREE_HAL_DYLIB_DYLIB_EXECUTABLE_H_
+
+#include <memory>
+#include <string>
+
+#include "absl/container/inlined_vector.h"
+#include "absl/types/span.h"
+#include "iree/base/dynamic_library.h"
+#include "iree/base/status.h"
+#include "iree/hal/allocator.h"
+#include "iree/hal/executable.h"
+#include "iree/hal/executable_spec.h"
+
+namespace iree {
+namespace hal {
+namespace dylib {
+
+struct MemrefType;
+
+class DyLibExecutable final : public Executable {
+ public:
+  static StatusOr<ref_ptr<DyLibExecutable>> Load(hal::Allocator* allocator,
+                                                 ExecutableSpec spec);
+  DyLibExecutable(ExecutableSpec spec);
+  ~DyLibExecutable() override;
+
+  bool supports_debugging() const override { return false; }
+
+  Status Invoke(int func_id, absl::Span<void*> args) const;
+
+ private:
+  Status Initialize();
+
+  ExecutableSpec spec_;
+  std::string executable_library_temp_path_;
+  std::unique_ptr<DynamicLibrary> executable_library_;
+  absl::InlinedVector<void*, 4> entry_functions_;
+};
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
+
+#endif  // IREE_HAL_DYLIB_DYLIB_EXECUTABLE_H_
diff --git a/iree/hal/dylib/dylib_executable_cache.cc b/iree/hal/dylib/dylib_executable_cache.cc
new file mode 100644
index 0000000..07bdf7d
--- /dev/null
+++ b/iree/hal/dylib/dylib_executable_cache.cc
@@ -0,0 +1,53 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "iree/hal/dylib/dylib_executable_cache.h"
+
+#include "iree/base/source_location.h"
+#include "iree/base/status.h"
+#include "iree/base/tracing.h"
+#include "iree/hal/dylib/dylib_executable.h"
+#include "iree/hal/executable_format.h"
+
+namespace iree {
+namespace hal {
+namespace dylib {
+
+DyLibExecutableCache::DyLibExecutableCache(hal::Allocator* allocator)
+    : allocator_(allocator) {}
+
+DyLibExecutableCache::~DyLibExecutableCache() = default;
+
+bool DyLibExecutableCache::CanPrepareFormat(ExecutableFormat format) const {
+  return format == kExecutableFormatDyLib;
+}
+
+StatusOr<ref_ptr<Executable>> DyLibExecutableCache::PrepareExecutable(
+    ExecutableLayout* executable_layout, ExecutableCachingModeBitfield mode,
+    const ExecutableSpec& spec) {
+  IREE_TRACE_SCOPE0("DyLibExecutableCache::PrepareExecutable");
+
+  // TODO(scotttodd): Options for using in-memory files where supported, or not
+  //    writing to temp files on disk (and failing if necessary) if not allowed.
+  // TODO(scotttodd): Use stable (possibly temp, but reusable) files when
+  //    ExecutableCachingMode::AllowPersistentCaching is set. For example,
+  //    hash data into a filename and read from / write to GetTempPath() or
+  //    GetCachePath() rather than use GetTempFile().
+
+  return DyLibExecutable::Load(allocator_, spec);
+}
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
diff --git a/iree/hal/dylib/dylib_executable_cache.h b/iree/hal/dylib/dylib_executable_cache.h
new file mode 100644
index 0000000..eb495b8
--- /dev/null
+++ b/iree/hal/dylib/dylib_executable_cache.h
@@ -0,0 +1,45 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef IREE_HAL_DYLIB_EXECUTABLE_CACHE_H_
+#define IREE_HAL_DYLIB_EXECUTABLE_CACHE_H_
+
+#include "iree/hal/allocator.h"
+#include "iree/hal/executable.h"
+#include "iree/hal/executable_cache.h"
+
+namespace iree {
+namespace hal {
+namespace dylib {
+
+class DyLibExecutableCache final : public ExecutableCache {
+ public:
+  explicit DyLibExecutableCache(hal::Allocator* allocator);
+  ~DyLibExecutableCache() override;
+
+  bool CanPrepareFormat(ExecutableFormat format) const override;
+
+  StatusOr<ref_ptr<Executable>> PrepareExecutable(
+      ExecutableLayout* executable_layout, ExecutableCachingModeBitfield mode,
+      const ExecutableSpec& spec) override;
+
+ private:
+  hal::Allocator* allocator_;
+};
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
+
+#endif  // IREE_HAL_DYLIB_EXECUTABLE_CACHE_H_
diff --git a/iree/hal/dylib/memref_runtime.h b/iree/hal/dylib/memref_runtime.h
new file mode 100644
index 0000000..c4b9e9e
--- /dev/null
+++ b/iree/hal/dylib/memref_runtime.h
@@ -0,0 +1,114 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef IREE_HAL_DYLIB_MEMREF_RUNTIME_H_
+#define IREE_HAL_DYLIB_MEMREF_RUNTIME_H_
+
+#include <assert.h>
+
+#include <cstdint>
+#include <vector>
+
+namespace iree {
+namespace hal {
+namespace dylib {
+
+template <int N>
+void dropFront(int64_t arr[N], int64_t *res) {
+  for (unsigned i = 1; i < N; ++i) *(res + i - 1) = arr[i];
+}
+
+/// StridedMemRef descriptor type with static rank.
+template <typename T, int N>
+struct StridedMemRefType {
+  T *basePtr;
+  T *data;
+  int64_t offset;
+  int64_t sizes[N];
+  int64_t strides[N];
+  // This operator[] is extremely slow and only for sugaring purposes.
+  StridedMemRefType<T, N - 1> operator[](int64_t idx) {
+    StridedMemRefType<T, N - 1> res;
+    res.basePtr = basePtr;
+    res.data = data;
+    res.offset = offset + idx * strides[0];
+    dropFront<N>(sizes, res.sizes);
+    dropFront<N>(strides, res.strides);
+    return res;
+  }
+};
+
+/// StridedMemRef descriptor type specialized for rank 1.
+template <typename T>
+struct StridedMemRefType<T, 1> {
+  T *basePtr;
+  T *data;
+  int64_t offset;
+  int64_t sizes[1];
+  int64_t strides[1];
+  T &operator[](int64_t idx) { return *(data + offset + idx * strides[0]); }
+};
+
+/// StridedMemRef descriptor type specialized for rank 0.
+template <typename T>
+struct StridedMemRefType<T, 0> {
+  T *basePtr;
+  T *data;
+  int64_t offset;
+};
+
+// Unranked MemRef
+template <typename T>
+struct UnrankedMemRefType {
+  int64_t rank;
+  void *descriptor;
+};
+
+// Mallocs a StridedMemRefDescriptor<T, 0>* (i.e. a pointer to scalar) that
+// matches the MLIR ABI. This is an implementation detail that is kept in sync
+// with MLIR codegen conventions.
+template <typename T>
+StridedMemRefType<T, 0> *makeStridedMemRefDescriptor(
+    void *ptr, const std::vector<int64_t> &shape) {
+  StridedMemRefType<T, 0> *descriptor = static_cast<StridedMemRefType<T, 0> *>(
+      malloc(sizeof(StridedMemRefType<T, 0>)));
+  descriptor->basePtr = static_cast<T *>(ptr);
+  descriptor->data = static_cast<T *>(ptr);
+  descriptor->offset = 0;
+  return descriptor;
+}
+
+// Shape and strides aren't used in the generated code (yet).
+// TODO(ataei): Delete this version once we can pass shapes.
+template <typename T>
+UnrankedMemRefType<T> *allocUnrankedDescriptor(void *data) {
+  UnrankedMemRefType<T> *res = static_cast<UnrankedMemRefType<T> *>(
+      malloc(sizeof(UnrankedMemRefType<T>)));
+  res->descriptor = makeStridedMemRefDescriptor<T>(data, {});
+  return res;
+}
+
+// Frees an UnrankedMemRefType<T>*
+template <typename T>
+void freeUnrankedDescriptor(UnrankedMemRefType<T> *desc) {
+  free(desc->descriptor);
+  free(desc);
+}
+
+}  // namespace dylib
+}  // namespace hal
+}  // namespace iree
+
+#endif  // IREE_HAL_DYLIB_MEMREF_RUNTIME_H_
diff --git a/iree/hal/executable_format.h b/iree/hal/executable_format.h
index 89eaf18..013ca42 100644
--- a/iree/hal/executable_format.h
+++ b/iree/hal/executable_format.h
@@ -67,6 +67,12 @@
 constexpr ExecutableFormat kExecutableFormatLLVM =
     MakeExecutableFormatID("LLVM");
 
+// Dynamic Library (dylib) executable in FlatBuffer format using the
+// https://github.com/google/iree/tree/master/iree/schemas/dylib_executable_def.fbs
+// schema
+constexpr ExecutableFormat kExecutableFormatDyLib =
+    MakeExecutableFormatID("DLIB");
+
 // LINT.ThenChange(https://github.com/google/iree/tree/master/iree/compiler/Dialect/HAL/IR/HALBase.td:executable_format)
 
 }  // namespace hal
diff --git a/iree/schemas/BUILD b/iree/schemas/BUILD
index b814ddd..7c5030d 100644
--- a/iree/schemas/BUILD
+++ b/iree/schemas/BUILD
@@ -46,12 +46,24 @@
 )
 
 iree_flatbuffer_cc_library(
+    name = "dylib_executable_def_cc_fbs",
+    srcs = ["dylib_executable_def.fbs"],
+    flatc_args = FLATC_ARGS,
+)
+
+iree_flatbuffer_cc_library(
     name = "interpreter_module_def_cc_fbs",
     srcs = ["interpreter_module_def.fbs"],
     flatc_args = FLATC_ARGS,
 )
 
 iree_flatbuffer_cc_library(
+    name = "llvmir_executable_def_cc_fbs",
+    srcs = ["llvmir_executable_def.fbs"],
+    flatc_args = FLATC_ARGS,
+)
+
+iree_flatbuffer_cc_library(
     name = "spirv_executable_def_cc_fbs",
     srcs = ["spirv_executable_def.fbs"],
     flatc_args = FLATC_ARGS,
@@ -63,18 +75,14 @@
     flatc_args = FLATC_ARGS,
 )
 
-iree_flatbuffer_cc_library(
-    name = "llvmir_executable_def_cc_fbs",
-    srcs = ["llvmir_executable_def.fbs"],
-    flatc_args = FLATC_ARGS,
-)
-
 iree_build_test(
     name = "schema_build_test",
     targets = [
         ":buffer_data_def_cc_fbs",
         ":bytecode_module_def_cc_fbs",
+        ":dylib_executable_def_cc_fbs",
         ":interpreter_module_def_cc_fbs",
+        ":llvmir_executable_def_cc_fbs",
         ":spirv_executable_def_cc_fbs",
         ":vmla_executable_def_cc_fbs",
     ],
@@ -83,7 +91,9 @@
 REFLECTION_SRCS = [] if not FLATBUFFER_SUPPORTS_REFLECTIONS else [
     "buffer_data_def.bfbs",
     "bytecode_module_def.bfbs",
+    "dylib_executable_def.bfbs",
     "interpreter_module_def.bfbs",
+    "llvmir_executable_def.bfbs",
     "spirv_executable_def.bfbs",
     "vmla_executable_def.bfbs",
 ]
diff --git a/iree/schemas/CMakeLists.txt b/iree/schemas/CMakeLists.txt
index ba5b8b9..ebabfc0 100644
--- a/iree/schemas/CMakeLists.txt
+++ b/iree/schemas/CMakeLists.txt
@@ -42,6 +42,19 @@
 
 flatbuffer_cc_library(
   NAME
+    dylib_executable_def_cc_fbs
+  SRCS
+    "dylib_executable_def.fbs"
+  FLATC_ARGS
+    "--keep-prefix"
+    "--scoped-enums"
+    "--reflect-names"
+    "--gen-object-api"
+  PUBLIC
+)
+
+flatbuffer_cc_library(
+  NAME
     interpreter_module_def_cc_fbs
   SRCS
     "interpreter_module_def.fbs"
@@ -55,6 +68,19 @@
 
 flatbuffer_cc_library(
   NAME
+    llvmir_executable_def_cc_fbs
+  SRCS
+    "llvmir_executable_def.fbs"
+  FLATC_ARGS
+    "--keep-prefix"
+    "--scoped-enums"
+    "--reflect-names"
+    "--gen-object-api"
+  PUBLIC
+)
+
+flatbuffer_cc_library(
+  NAME
     spirv_executable_def_cc_fbs
   SRCS
     "spirv_executable_def.fbs"
@@ -79,19 +105,6 @@
   PUBLIC
 )
 
-flatbuffer_cc_library(
-  NAME
-    llvmir_executable_def_cc_fbs
-  SRCS
-    "llvmir_executable_def.fbs"
-  FLATC_ARGS
-    "--keep-prefix"
-    "--scoped-enums"
-    "--reflect-names"
-    "--gen-object-api"
-  PUBLIC
-)
-
 iree_cc_embed_data(
   NAME
     reflection_data
diff --git a/iree/schemas/dylib_executable_def.fbs b/iree/schemas/dylib_executable_def.fbs
new file mode 100644
index 0000000..62ab26d
--- /dev/null
+++ b/iree/schemas/dylib_executable_def.fbs
@@ -0,0 +1,36 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace iree;
+
+// 'Dynamic Library (dylib) Executable'.
+
+file_identifier "DLIB";
+file_extension "dlib";
+
+// Dynamic library (.so/.dll/.dylib) executable module.
+table DyLibExecutableDef {
+  // A map of entry points to string names with the same order as in the executable op.
+  entry_points:[string];
+  // An embedded (as opposed to external) dynamic library file.
+  // TODO(scotttodd): List of embedded files?
+  // TODO(scotttodd): Format of files, platform information (x86/arm/etc.)
+  library_embedded:[byte];
+
+  // TODO(scotttodd): Relative file path from this flatbuffer file
+
+  // TODO(scotttodd): pdb debug symbols
+}
+
+root_type DyLibExecutableDef;
diff --git a/iree/tools/CMakeLists.txt b/iree/tools/CMakeLists.txt
index 8660532..0e5e43a 100644
--- a/iree/tools/CMakeLists.txt
+++ b/iree/tools/CMakeLists.txt
@@ -18,6 +18,7 @@
 # TODO: skip targets disabled by options.
 iree_select_compiler_opts(IREE_HAL_DRIVER_MODULES
   ALL
+    "iree::hal::dylib::dylib_driver_module"
     "iree::hal::llvmjit::llvmjit_driver_module"
     "iree::hal::vmla::vmla_driver_module"
     "iree::hal::vulkan::vulkan_driver_module"