Adding iree_vm_list_swap_storage and iree_vm_list_copy. (#12088)
This covers the C implementation of these routines and adds a bunch of
tests. Future changes will expose these to the compiler as VM ops.
Progress on #7014.
diff --git a/runtime/src/iree/vm/list.c b/runtime/src/iree/vm/list.c
index 689b228..d7bdef5 100644
--- a/runtime/src/iree/vm/list.c
+++ b/runtime/src/iree/vm/list.c
@@ -291,6 +291,7 @@
IREE_API_EXPORT iree_status_t
iree_vm_list_reserve(iree_vm_list_t* list, iree_host_size_t minimum_capacity) {
+ IREE_ASSERT_ARGUMENT(list);
if (list->capacity >= minimum_capacity) {
return iree_ok_status();
}
@@ -305,11 +306,13 @@
}
IREE_API_EXPORT iree_host_size_t iree_vm_list_size(const iree_vm_list_t* list) {
+ IREE_ASSERT_ARGUMENT(list);
return list->count;
}
IREE_API_EXPORT iree_status_t iree_vm_list_resize(iree_vm_list_t* list,
iree_host_size_t new_size) {
+ IREE_ASSERT_ARGUMENT(list);
if (new_size == list->count) {
return iree_ok_status();
} else if (new_size < list->count) {
@@ -333,6 +336,228 @@
list->count = 0;
}
+static void iree_memswap(void* a, void* b, iree_host_size_t size) {
+ uint8_t* a_ptr = (uint8_t*)a;
+ uint8_t* b_ptr = (uint8_t*)b;
+ for (iree_host_size_t i = 0; i < size; ++i) {
+ uint8_t t = a_ptr[i];
+ a_ptr[i] = b_ptr[i];
+ b_ptr[i] = t;
+ }
+}
+
+IREE_API_EXPORT void iree_vm_list_swap_storage(iree_vm_list_t* list_a,
+ iree_vm_list_t* list_b) {
+ IREE_ASSERT_ARGUMENT(list_a);
+ IREE_ASSERT_ARGUMENT(list_b);
+ if (list_a == list_b) return;
+ iree_memswap(&list_a->allocator, &list_b->allocator,
+ sizeof(list_a->allocator));
+ iree_memswap(&list_a->capacity, &list_b->capacity, sizeof(list_a->capacity));
+ iree_memswap(&list_a->count, &list_b->count, sizeof(list_a->count));
+ iree_memswap(&list_a->element_type, &list_b->element_type,
+ sizeof(list_a->element_type));
+ iree_memswap(&list_a->element_size, &list_b->element_size,
+ sizeof(list_a->element_size));
+ iree_memswap(&list_a->storage_mode, &list_b->storage_mode,
+ sizeof(list_a->storage_mode));
+ iree_memswap(&list_a->storage, &list_b->storage, sizeof(list_a->storage));
+}
+
+// Returns true if |src_type| can be converted into |dst_type|.
+static bool iree_vm_type_def_is_compatible(iree_vm_type_def_t src_type,
+ iree_vm_type_def_t dst_type) {
+ return memcmp(&src_type, &dst_type, sizeof(dst_type)) == 0;
+}
+
+// Copies from a |src_list| of any type (value, ref, variant) into a |dst_list|
+// in variant storage mode. This cannot fail as variant lists can store any
+// type.
+static void iree_vm_list_copy_to_variant_list(iree_vm_list_t* src_list,
+ iree_host_size_t src_i,
+ iree_vm_list_t* dst_list,
+ iree_host_size_t dst_i,
+ iree_host_size_t count) {
+ iree_vm_variant_t* dst_storage =
+ (iree_vm_variant_t*)dst_list->storage + dst_i;
+ switch (src_list->storage_mode) {
+ case IREE_VM_LIST_STORAGE_MODE_VALUE: {
+ uintptr_t src_storage =
+ (uintptr_t)src_list->storage + src_i * src_list->element_size;
+ for (iree_host_size_t i = 0; i < count; ++i) {
+ if (iree_vm_type_def_is_ref(&dst_storage[i].type)) {
+ iree_vm_ref_release(&dst_storage[i].ref);
+ }
+ dst_storage[i].type = src_list->element_type;
+ memcpy(dst_storage[i].value_storage,
+ (uint8_t*)src_storage + i * src_list->element_size,
+ src_list->element_size);
+ }
+ break;
+ }
+ case IREE_VM_LIST_STORAGE_MODE_REF: {
+ iree_vm_ref_t* src_storage = (iree_vm_ref_t*)src_list->storage + src_i;
+ for (iree_host_size_t i = 0; i < count; ++i) {
+ // NOTE: we retain first in case the lists alias and the ref is the
+ // same.
+ iree_vm_ref_t* ref = &src_storage[i];
+ iree_vm_ref_retain_inplace(ref);
+ if (iree_vm_type_def_is_ref(&dst_storage[i].type)) {
+ iree_vm_ref_release(&dst_storage[i].ref);
+ }
+ dst_storage->type = iree_vm_type_def_make_ref_type(ref->type);
+ dst_storage->ref = *ref;
+ }
+ break;
+ }
+ case IREE_VM_LIST_STORAGE_MODE_VARIANT: {
+ iree_vm_variant_t* src_storage =
+ (iree_vm_variant_t*)src_list->storage + src_i;
+ for (iree_host_size_t i = 0; i < count; ++i) {
+ // NOTE: we retain first in case the lists alias and the ref is the
+ // same.
+ if (iree_vm_type_def_is_ref(&src_storage[i].type)) {
+ iree_vm_ref_retain_inplace(&src_storage[i].ref);
+ }
+ if (iree_vm_type_def_is_ref(&dst_storage[i].type)) {
+ iree_vm_ref_release(&dst_storage[i].ref);
+ }
+ memcpy(&dst_storage[i], &src_storage[i], sizeof(dst_storage[i]));
+ }
+ break;
+ }
+ }
+}
+
+// Copies from a |src_list| in variant storage mode to a |dst_list| of any type
+// (value, ref) while checking each element. This first needs to ensure the
+// entire source range matches the expected destination type which makes this
+// much slower than the other paths that need not check or only check once per
+// copy operation instead.
+static iree_status_t iree_vm_list_copy_from_variant_list(
+ iree_vm_list_t* src_list, iree_host_size_t src_i, iree_vm_list_t* dst_list,
+ iree_host_size_t dst_i, iree_host_size_t count) {
+ iree_vm_variant_t* src_storage =
+ (iree_vm_variant_t*)src_list->storage + src_i;
+ switch (dst_list->storage_mode) {
+ case IREE_VM_LIST_STORAGE_MODE_VALUE:
+ case IREE_VM_LIST_STORAGE_MODE_REF:
+ for (iree_host_size_t i = 0; i < count; ++i) {
+ if (!iree_vm_type_def_is_compatible(src_storage[i].type,
+ dst_list->element_type)) {
+ return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
+ "destination list element type does not "
+ "match the source element %" PRIhsz,
+ src_i + i);
+ }
+ }
+ break;
+ default:
+ // Destination is a variant list and accepts all inputs.
+ break;
+ }
+ switch (dst_list->storage_mode) {
+ case IREE_VM_LIST_STORAGE_MODE_VALUE: {
+ uintptr_t dst_storage =
+ (uintptr_t)dst_list->storage + dst_i * dst_list->element_size;
+ for (iree_host_size_t i = 0; i < count; ++i) {
+ memcpy((uint8_t*)dst_storage + i * dst_list->element_size,
+ src_storage[i].value_storage, dst_list->element_size);
+ }
+ break;
+ }
+ case IREE_VM_LIST_STORAGE_MODE_REF: {
+ iree_vm_ref_t* dst_storage = (iree_vm_ref_t*)dst_list->storage + dst_i;
+ for (iree_host_size_t i = 0; i < count; ++i) {
+ iree_vm_ref_retain(&src_storage[i].ref, &dst_storage[i]);
+ }
+ break;
+ }
+ default:
+ case IREE_VM_LIST_STORAGE_MODE_VARIANT:
+ return iree_make_status(IREE_STATUS_FAILED_PRECONDITION,
+ "unhandled copy mode");
+ }
+ return iree_ok_status();
+}
+
+IREE_API_EXPORT iree_status_t iree_vm_list_copy(iree_vm_list_t* src_list,
+ iree_host_size_t src_i,
+ iree_vm_list_t* dst_list,
+ iree_host_size_t dst_i,
+ iree_host_size_t count) {
+ IREE_ASSERT_ARGUMENT(src_list);
+ IREE_ASSERT_ARGUMENT(dst_list);
+
+ // Fast-path no-op check.
+ if (count == 0) return iree_ok_status();
+
+ // Verify ranges.
+ const iree_host_size_t src_count = iree_vm_list_size(src_list);
+ if (src_i + count > src_count) {
+ return iree_make_status(
+ IREE_STATUS_OUT_OF_RANGE,
+ "source range [%" PRIhsz ", %" PRIhsz ") of %" PRIhsz
+ " elements out of range of source list with size %" PRIhsz,
+ src_i, src_i + count, count, src_count);
+ }
+ const iree_host_size_t dst_count = iree_vm_list_size(dst_list);
+ if (dst_i + count > dst_count) {
+ return iree_make_status(
+ IREE_STATUS_OUT_OF_RANGE,
+ "destination range [%" PRIhsz ", %" PRIhsz ") of %" PRIhsz
+ " elements out of range of destination list with size %" PRIhsz,
+ dst_i, dst_i + count, count, dst_count);
+ }
+
+ // Prevent overlap when copying within the same list.
+ if (src_list == dst_list && src_i + count > dst_i && dst_i + count > src_i) {
+ return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
+ "overlapping copy of range [%" PRIhsz ", %" PRIhsz
+ ") to [%" PRIhsz ", %" PRIhsz ") of %" PRIhsz
+ " elements not supported",
+ src_i, src_i + count, dst_i, dst_i + count, count);
+ }
+
+ // Copies into variant lists is a slow-path as we need to check the type of
+ // each element we copy. Note that the source of the copy can be of any type.
+ // When copying in the other direction of a variant list to a typed list we
+ // need to ensure all copied elements match the expected destination type.
+ if (dst_list->storage_mode == IREE_VM_LIST_STORAGE_MODE_VARIANT) {
+ iree_vm_list_copy_to_variant_list(src_list, src_i, dst_list, dst_i, count);
+ return iree_ok_status();
+ } else if (src_list->storage_mode == IREE_VM_LIST_STORAGE_MODE_VARIANT) {
+ return iree_vm_list_copy_from_variant_list(src_list, src_i, dst_list, dst_i,
+ count);
+ }
+
+ // If neither source or destination are variant lists we need to match the
+ // types exactly.
+ if (src_list->storage_mode != dst_list->storage_mode ||
+ memcmp(&src_list->element_type, &dst_list->element_type,
+ sizeof(src_list->element_type)) != 0) {
+ return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
+ "src/dst element type mismatch");
+ }
+
+ if (src_list->storage_mode == IREE_VM_LIST_STORAGE_MODE_VALUE) {
+ // Memcpy primitive values fast path.
+ memcpy((uint8_t*)dst_list->storage + dst_i * dst_list->element_size,
+ (uint8_t*)src_list->storage + src_i * src_list->element_size,
+ count * dst_list->element_size);
+ } else {
+ // Retain ref fast(ish) path - note that iree_vm_ref_retain will release
+ // any existing value in the dest list it overwrites.
+ iree_vm_ref_t* src_ref_storage = (iree_vm_ref_t*)src_list->storage + src_i;
+ iree_vm_ref_t* dst_ref_storage = (iree_vm_ref_t*)dst_list->storage + dst_i;
+ for (iree_host_size_t i = 0; i < count; ++i) {
+ iree_vm_ref_retain(src_ref_storage + i, dst_ref_storage + i);
+ }
+ }
+
+ return iree_ok_status();
+}
+
static void iree_vm_list_convert_value_type(
const iree_vm_value_t* source_value, iree_vm_value_type_t target_value_type,
iree_vm_value_t* out_value) {
@@ -720,7 +945,7 @@
iree_vm_ref_t* element_ref = (iree_vm_ref_t*)element_ptr;
out_value->type.ref_type = element_ref->type;
out_value->type.value_type = IREE_VM_VALUE_TYPE_NONE;
- iree_vm_ref_retain(element_ref, &out_value->ref);
+ iree_vm_ref_assign(element_ref, &out_value->ref);
break;
}
case IREE_VM_LIST_STORAGE_MODE_VARIANT: {
diff --git a/runtime/src/iree/vm/list.h b/runtime/src/iree/vm/list.h
index 361b2a8..b7d2ba3 100644
--- a/runtime/src/iree/vm/list.h
+++ b/runtime/src/iree/vm/list.h
@@ -104,6 +104,32 @@
// Clears the list contents. Equivalent to resizing to 0.
IREE_API_EXPORT void iree_vm_list_clear(iree_vm_list_t* list);
+// Swaps the storage of |list_a| and |list_b|. The list references remain the
+// same but the count, capacity, and underlying storage will be swapped. This
+// can be used to treat lists as persistent stable references to dynamically
+// mutated storage such as when emulating structs or dicts.
+//
+// WARNING: if a list is initialized in-place with iree_vm_list_initialize this
+// will still perform the storage swap but may lead to unexpected issues if the
+// lifetime of the storage is shorter than the lifetime of the newly-swapped
+// list.
+IREE_API_EXPORT void iree_vm_list_swap_storage(iree_vm_list_t* list_a,
+ iree_vm_list_t* list_b);
+
+// Copies |count| elements from |src_list| starting at |src_i| to |dst_list|
+// starting at |dst_i|. The ranges specified must be valid in both lists.
+//
+// Supported list types:
+// any type -> variant list
+// variant list -> compatible element types only
+// same value type -> same value type
+// same ref type -> same ref type
+IREE_API_EXPORT iree_status_t iree_vm_list_copy(iree_vm_list_t* src_list,
+ iree_host_size_t src_i,
+ iree_vm_list_t* dst_list,
+ iree_host_size_t dst_i,
+ iree_host_size_t count);
+
// Returns the value of the element at the given index.
// Note that the value type may vary from element to element in variant lists
// and callers should check the |out_value| type.
diff --git a/runtime/src/iree/vm/list_test.cc b/runtime/src/iree/vm/list_test.cc
index 2ee71f2..e9b1d08 100644
--- a/runtime/src/iree/vm/list_test.cc
+++ b/runtime/src/iree/vm/list_test.cc
@@ -39,10 +39,97 @@
IREE_VM_DECLARE_TYPE_ADAPTERS(test_b, B);
IREE_VM_DEFINE_TYPE_ADAPTERS(test_b, B);
+static bool operator==(const iree_vm_value_t& lhs,
+ const iree_vm_value_t& rhs) noexcept {
+ if (lhs.type != rhs.type) return false;
+ switch (lhs.type) {
+ default:
+ case IREE_VM_VALUE_TYPE_NONE:
+ return true; // none == none
+ case IREE_VM_VALUE_TYPE_I8:
+ return lhs.i8 == rhs.i8;
+ case IREE_VM_VALUE_TYPE_I16:
+ return lhs.i16 == rhs.i16;
+ case IREE_VM_VALUE_TYPE_I32:
+ return lhs.i32 == rhs.i32;
+ case IREE_VM_VALUE_TYPE_I64:
+ return lhs.i64 == rhs.i64;
+ case IREE_VM_VALUE_TYPE_F32:
+ return lhs.f32 == rhs.f32;
+ case IREE_VM_VALUE_TYPE_F64:
+ return lhs.f64 == rhs.f64;
+ }
+}
+
+static std::ostream& operator<<(std::ostream& os,
+ const iree_vm_value_t& value) {
+ switch (value.type) {
+ default:
+ case IREE_VM_VALUE_TYPE_NONE:
+ return os << "??";
+ case IREE_VM_VALUE_TYPE_I8:
+ return os << value.i8;
+ case IREE_VM_VALUE_TYPE_I16:
+ return os << value.i16;
+ case IREE_VM_VALUE_TYPE_I32:
+ return os << value.i32;
+ case IREE_VM_VALUE_TYPE_I64:
+ return os << value.i64;
+ case IREE_VM_VALUE_TYPE_F32:
+ return os << value.f32;
+ case IREE_VM_VALUE_TYPE_F64:
+ return os << value.f64;
+ }
+}
+
+template <size_t N>
+static std::vector<iree_vm_value_t> MakeValuesList(const int32_t (&values)[N]) {
+ std::vector<iree_vm_value_t> result;
+ result.resize(N);
+ for (size_t i = 0; i < N; ++i) result[i] = iree_vm_value_make_i32(values[i]);
+ return result;
+}
+
+template <size_t N>
+static std::vector<iree_vm_value_t> MakeValuesList(const float (&values)[N]) {
+ std::vector<iree_vm_value_t> result;
+ result.resize(N);
+ for (size_t i = 0; i < N; ++i) result[i] = iree_vm_value_make_f32(values[i]);
+ return result;
+}
+
+static std::vector<iree_vm_value_t> GetValuesList(iree_vm_list_t* list) {
+ std::vector<iree_vm_value_t> result;
+ result.resize(iree_vm_list_size(list));
+ for (iree_host_size_t i = 0; i < result.size(); ++i) {
+ iree_vm_variant_t variant = iree_vm_variant_empty();
+ IREE_CHECK_OK(iree_vm_list_get_variant(list, i, &variant));
+ if (iree_vm_type_def_is_value(&variant.type)) {
+ result[i].type = variant.type.value_type;
+ memcpy(result[i].value_storage, variant.value_storage,
+ sizeof(result[i].value_storage));
+ } else if (iree_vm_type_def_is_ref(&variant.type)) {
+ if (test_a_isa(variant.ref)) {
+ result[i] = iree_vm_value_make_f32(test_a_deref(variant.ref)->data());
+ } else if (test_b_isa(variant.ref)) {
+ result[i] = iree_vm_value_make_i32(test_b_deref(variant.ref)->data());
+ }
+ }
+ }
+ return result;
+}
+
+static bool operator==(const iree_vm_ref_t& lhs,
+ const iree_vm_ref_t& rhs) noexcept {
+ return lhs.type == rhs.type && lhs.ptr == rhs.ptr;
+}
+
namespace {
using ::iree::Status;
+using ::iree::StatusCode;
using ::iree::testing::status::StatusIs;
+using testing::Eq;
template <typename T>
static void RegisterRefType(iree_vm_ref_type_descriptor_t* descriptor,
@@ -70,7 +157,7 @@
return ref;
}
-static iree_vm_instance_t* instance = NULL;
+static iree_vm_instance_t* instance = nullptr;
struct VMListTest : public ::testing::Test {
static void SetUpTestSuite() {
IREE_CHECK_OK(iree_vm_instance_create(iree_allocator_system(), &instance));
@@ -208,7 +295,7 @@
iree_allocator_system(), &source_list));
// Clone list.
- iree_vm_list_t* target_list = NULL;
+ iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
@@ -241,7 +328,7 @@
}
// Clone list.
- iree_vm_list_t* target_list = NULL;
+ iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
@@ -268,7 +355,7 @@
&source_list));
// Clone list.
- iree_vm_list_t* target_list = NULL;
+ iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
@@ -299,7 +386,7 @@
}
// Clone list.
- iree_vm_list_t* target_list = NULL;
+ iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
@@ -330,7 +417,7 @@
&source_list));
// Clone list.
- iree_vm_list_t* target_list = NULL;
+ iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
@@ -364,7 +451,7 @@
}
// Clone list.
- iree_vm_list_t* target_list = NULL;
+ iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
@@ -577,6 +664,541 @@
iree_vm_list_release(list);
}
+// Tests that swapping the storage of a list with itself is a no-op.
+TEST_F(VMListTest, SwapStorageSelf) {
+ iree_vm_type_def_t element_type =
+ iree_vm_type_def_make_ref_type(test_a_type_id());
+ iree_vm_list_t* list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &list));
+ for (iree_host_size_t i = 0; i < 5; ++i) {
+ iree_vm_ref_t ref_a = MakeRef<A>((float)i);
+ IREE_ASSERT_OK(iree_vm_list_push_ref_move(list, &ref_a));
+ }
+
+ iree_vm_list_swap_storage(list, list);
+
+ for (iree_host_size_t i = 0; i < 5; ++i) {
+ iree_vm_ref_t ref_a{0};
+ IREE_ASSERT_OK(iree_vm_list_get_ref_retain(list, i, &ref_a));
+ EXPECT_TRUE(test_a_isa(ref_a));
+ auto* a = test_a_deref(ref_a);
+ EXPECT_EQ(i, a->data());
+ iree_vm_ref_release(&ref_a);
+ }
+
+ iree_vm_list_release(list);
+}
+
+// Tests swapping the storage of two lists with different types. The lists
+// should have their types, size, and storage swapped.
+TEST_F(VMListTest, SwapStorage) {
+ iree_vm_type_def_t element_type_a =
+ iree_vm_type_def_make_ref_type(test_a_type_id());
+ iree_vm_list_t* list_a = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&element_type_a, /*initial_capacity=*/8,
+ iree_allocator_system(), &list_a));
+ iree_host_size_t list_a_size = 4;
+ for (iree_host_size_t i = 0; i < list_a_size; ++i) {
+ iree_vm_ref_t ref_a = MakeRef<A>((float)i);
+ IREE_ASSERT_OK(iree_vm_list_push_ref_move(list_a, &ref_a));
+ }
+
+ iree_vm_type_def_t element_type_b =
+ iree_vm_type_def_make_ref_type(test_b_type_id());
+ iree_vm_list_t* list_b = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&element_type_b, /*initial_capacity=*/8,
+ iree_allocator_system(), &list_b));
+ iree_host_size_t list_b_size = 3;
+ for (iree_host_size_t i = 0; i < list_b_size; ++i) {
+ iree_vm_ref_t ref_b = MakeRef<B>((float)i);
+ IREE_ASSERT_OK(iree_vm_list_push_ref_move(list_b, &ref_b));
+ }
+
+ iree_vm_list_swap_storage(list_a, list_b);
+
+ // list_a should have b types.
+ iree_vm_type_def_t queried_type_a = iree_vm_list_element_type(list_a);
+ EXPECT_EQ(0,
+ memcmp(&queried_type_a, &element_type_b, sizeof(element_type_b)));
+ EXPECT_EQ(iree_vm_list_size(list_a), list_b_size);
+ for (iree_host_size_t i = 0; i < list_b_size; ++i) {
+ iree_vm_ref_t ref_b{0};
+ IREE_ASSERT_OK(iree_vm_list_get_ref_retain(list_a, i, &ref_b));
+ EXPECT_TRUE(test_b_isa(ref_b));
+ auto* b = test_b_deref(ref_b);
+ EXPECT_EQ(i, b->data());
+ iree_vm_ref_release(&ref_b);
+ }
+
+ // list_b should have a types.
+ iree_vm_type_def_t queried_type_b = iree_vm_list_element_type(list_b);
+ EXPECT_EQ(0,
+ memcmp(&queried_type_b, &element_type_a, sizeof(element_type_a)));
+ EXPECT_EQ(iree_vm_list_size(list_b), list_a_size);
+ for (iree_host_size_t i = 0; i < list_b_size; ++i) {
+ iree_vm_ref_t ref_a{0};
+ IREE_ASSERT_OK(iree_vm_list_get_ref_retain(list_b, i, &ref_a));
+ EXPECT_TRUE(test_a_isa(ref_a));
+ auto* a = test_a_deref(ref_a);
+ EXPECT_EQ(i, a->data());
+ iree_vm_ref_release(&ref_a);
+ }
+
+ iree_vm_list_release(list_a);
+ iree_vm_list_release(list_b);
+}
+
+// Tests the boundary conditions around out-of-range copies.
+// All of the logic for this is shared across all the various copy modes.
+TEST_F(VMListTest, CopyOutOfRange) {
+ iree_vm_type_def_t element_type =
+ iree_vm_type_def_make_value_type(IREE_VM_VALUE_TYPE_I32);
+ iree_vm_list_t* src_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &src_list));
+ iree_vm_list_t* dst_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &dst_list));
+
+ // Lists are both empty - everything should fail.
+ IREE_ASSERT_OK(iree_vm_list_resize(src_list, 0));
+ IREE_ASSERT_OK(iree_vm_list_resize(dst_list, 0));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 100, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 0, 100)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 0, 5)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 3, dst_list, 0, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 3, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+
+ // Source has valid ranges, destination does not.
+ IREE_ASSERT_OK(iree_vm_list_resize(src_list, 4));
+ IREE_ASSERT_OK(iree_vm_list_resize(dst_list, 0));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 100, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 0, 100)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 0, 5)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 3, dst_list, 0, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 3, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+
+ // Destination has valid ranges, source does not.
+ IREE_ASSERT_OK(iree_vm_list_resize(src_list, 0));
+ IREE_ASSERT_OK(iree_vm_list_resize(dst_list, 4));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 100, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 0, 100)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 0, 5)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 3, dst_list, 0, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 3, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+
+ // Mismatches.
+ IREE_ASSERT_OK(iree_vm_list_resize(src_list, 4));
+ IREE_ASSERT_OK(iree_vm_list_resize(dst_list, 4));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 100, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 0, 100)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 0, 5)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 4, dst_list, 0, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 4, 1)),
+ StatusIs(StatusCode::kOutOfRange));
+
+ iree_vm_list_release(src_list);
+ iree_vm_list_release(dst_list);
+}
+
+// Tests copying values between lists.
+TEST_F(VMListTest, CopyValues) {
+ iree_vm_type_def_t element_type =
+ iree_vm_type_def_make_value_type(IREE_VM_VALUE_TYPE_I32);
+
+ // src: [0, 1, 2, 3]
+ iree_vm_list_t* src_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &src_list));
+ IREE_ASSERT_OK(iree_vm_list_resize(src_list, 4));
+ for (iree_host_size_t i = 0; i < 4; ++i) {
+ iree_vm_value_t value = iree_vm_value_make_i32(i);
+ IREE_ASSERT_OK(iree_vm_list_set_value(src_list, i, &value));
+ }
+
+ // dst: [4, 5, 6, 7]
+ iree_vm_list_t* dst_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &dst_list));
+ IREE_ASSERT_OK(iree_vm_list_resize(dst_list, 4));
+ for (iree_host_size_t i = 0; i < 4; ++i) {
+ iree_vm_value_t value = iree_vm_value_make_i32(4 + i);
+ IREE_ASSERT_OK(iree_vm_list_set_value(dst_list, i, &value));
+ }
+
+ // Copy no items (no-op).
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 0, 0));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({4, 5, 6, 7})));
+
+ // Copy at start.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 0, 1));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 5, 6, 7})));
+
+ // Copy at end.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 3, dst_list, 3, 1));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 5, 6, 3})));
+
+ // Copy a range in the middle.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 1, dst_list, 1, 2));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 1, 2, 3})));
+
+ // Scatter.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 1, 3));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 0, 1, 2})));
+
+ // Try to copy over source - this should fail as we don't support memmove.
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, src_list, 1, 2)),
+ StatusIs(StatusCode::kInvalidArgument));
+
+ // But copying over non-overlapping source ranges should be ok.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, src_list, 2, 2));
+ EXPECT_THAT(GetValuesList(src_list), Eq(MakeValuesList({0, 1, 0, 1})));
+
+ iree_vm_list_release(src_list);
+ iree_vm_list_release(dst_list);
+}
+
+// Tests that trying to copy between values of different types will fail.
+// Note that we use sizeof(int) == sizeof(float) as even though the sizes match
+// we still want to fail (bitcasting would be bad).
+TEST_F(VMListTest, CopyWrongValues) {
+ // src: [0, 1, 2, 3]
+ iree_vm_type_def_t src_element_type =
+ iree_vm_type_def_make_value_type(IREE_VM_VALUE_TYPE_I32);
+ iree_vm_list_t* src_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&src_element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &src_list));
+ IREE_ASSERT_OK(iree_vm_list_resize(src_list, 4));
+ for (iree_host_size_t i = 0; i < 4; ++i) {
+ iree_vm_value_t value = iree_vm_value_make_i32(i);
+ IREE_ASSERT_OK(iree_vm_list_set_value(src_list, i, &value));
+ }
+
+ // dst: [4.0, 5.0, 6.0, 7.0]
+ iree_vm_type_def_t dst_element_type =
+ iree_vm_type_def_make_value_type(IREE_VM_VALUE_TYPE_F32);
+ iree_vm_list_t* dst_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&dst_element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &dst_list));
+ IREE_ASSERT_OK(iree_vm_list_resize(dst_list, 4));
+ for (iree_host_size_t i = 0; i < 4; ++i) {
+ iree_vm_value_t value = iree_vm_value_make_f32(4 + i);
+ IREE_ASSERT_OK(iree_vm_list_set_value(dst_list, i, &value));
+ }
+
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 0, 1)),
+ StatusIs(StatusCode::kInvalidArgument));
+
+ iree_vm_list_release(src_list);
+ iree_vm_list_release(dst_list);
+}
+
+// Tests copying refs between lists of !vm.ref<?>.
+TEST_F(VMListTest, CopyRefs) {
+ iree_vm_type_def_t element_type =
+ iree_vm_type_def_make_ref_type(IREE_VM_REF_TYPE_ANY);
+
+ // src: [0, 1, 2, 3]
+ iree_vm_list_t* src_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &src_list));
+ iree_host_size_t src_list_size = 4;
+ for (iree_host_size_t i = 0; i < src_list_size; ++i) {
+ iree_vm_ref_t ref = MakeRef<B>(i);
+ IREE_ASSERT_OK(iree_vm_list_push_ref_move(src_list, &ref));
+ }
+
+ // dst: [4, 5, 6, 7]
+ iree_vm_list_t* dst_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &dst_list));
+ iree_host_size_t dst_list_size = 4;
+ for (iree_host_size_t i = 0; i < dst_list_size; ++i) {
+ iree_vm_ref_t ref = MakeRef<B>(4 + i);
+ IREE_ASSERT_OK(iree_vm_list_push_ref_move(dst_list, &ref));
+ }
+
+ // Copy no items (no-op).
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 0, 0));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({4, 5, 6, 7})));
+
+ // Copy at start.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 0, 1));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 5, 6, 7})));
+
+ // Copy at end.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 3, dst_list, 3, 1));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 5, 6, 3})));
+
+ // Copy a range in the middle.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 1, dst_list, 1, 2));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 1, 2, 3})));
+
+ // Scatter.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 1, 3));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 0, 1, 2})));
+
+ // Try to copy over source - this should fail as we don't support memmove.
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, src_list, 1, 2)),
+ StatusIs(StatusCode::kInvalidArgument));
+
+ // But copying over non-overlapping source ranges should be ok.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, src_list, 2, 2));
+ EXPECT_THAT(GetValuesList(src_list), Eq(MakeValuesList({0, 1, 0, 1})));
+
+ iree_vm_list_release(src_list);
+ iree_vm_list_release(dst_list);
+}
+
+// Tests that trying to copy between refs of different types will fail.
+TEST_F(VMListTest, CopyWrongRefs) {
+ // src: type A
+ iree_vm_type_def_t src_element_type =
+ iree_vm_type_def_make_ref_type(test_a_type_id());
+ iree_vm_list_t* src_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&src_element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &src_list));
+ iree_host_size_t src_list_size = 4;
+ for (iree_host_size_t i = 0; i < src_list_size; ++i) {
+ iree_vm_ref_t ref = MakeRef<A>(i);
+ IREE_ASSERT_OK(iree_vm_list_push_ref_move(src_list, &ref));
+ }
+
+ // dst: type B
+ iree_vm_type_def_t dst_element_type =
+ iree_vm_type_def_make_ref_type(test_b_type_id());
+ iree_vm_list_t* dst_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&dst_element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &dst_list));
+ iree_host_size_t dst_list_size = 4;
+ for (iree_host_size_t i = 0; i < dst_list_size; ++i) {
+ iree_vm_ref_t ref = MakeRef<B>(4 + i);
+ IREE_ASSERT_OK(iree_vm_list_push_ref_move(dst_list, &ref));
+ }
+
+ // Copy no items (no-op). Should be ok.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 0, 0));
+
+ // Copies should fail because the types don't match.
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, dst_list, 0, 1)),
+ StatusIs(StatusCode::kInvalidArgument));
+ EXPECT_THAT(Status(iree_vm_list_copy(dst_list, 0, src_list, 0, 1)),
+ StatusIs(StatusCode::kInvalidArgument));
+
+ iree_vm_list_release(src_list);
+ iree_vm_list_release(dst_list);
+}
+
+// Tests copying between variant lists.
+TEST_F(VMListTest, CopyVariants) {
+ // src: [0, 1, B(2), B(3)]
+ iree_vm_list_t* src_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(/*element_type=*/nullptr,
+ /*initial_capacity=*/8,
+ iree_allocator_system(), &src_list));
+ IREE_ASSERT_OK(iree_vm_list_resize(src_list, 4));
+ for (iree_host_size_t i = 0; i < 2; ++i) {
+ iree_vm_value_t value = iree_vm_value_make_i32(i);
+ IREE_ASSERT_OK(iree_vm_list_set_value(src_list, i, &value));
+ }
+ for (iree_host_size_t i = 2; i < 4; ++i) {
+ iree_vm_ref_t ref = MakeRef<B>(i);
+ IREE_ASSERT_OK(iree_vm_list_set_ref_move(src_list, i, &ref));
+ }
+
+ // dst: [4, 5, B(6), B(7)]
+ iree_vm_list_t* dst_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(/*element_type=*/nullptr,
+ /*initial_capacity=*/8,
+ iree_allocator_system(), &dst_list));
+ IREE_ASSERT_OK(iree_vm_list_resize(dst_list, 4));
+ for (iree_host_size_t i = 0; i < 2; ++i) {
+ iree_vm_value_t value = iree_vm_value_make_i32(4 + i);
+ IREE_ASSERT_OK(iree_vm_list_set_value(dst_list, i, &value));
+ }
+ for (iree_host_size_t i = 2; i < 4; ++i) {
+ iree_vm_ref_t ref = MakeRef<B>(4 + i);
+ IREE_ASSERT_OK(iree_vm_list_set_ref_move(dst_list, i, &ref));
+ }
+
+ // Copy no items (no-op).
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 0, 0));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({4, 5, 6, 7})));
+
+ // Copy at start.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 0, 1));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 5, 6, 7})));
+
+ // Copy at end.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 3, dst_list, 3, 1));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 5, 6, 3})));
+
+ // Copy a range in the middle.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 1, dst_list, 1, 2));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 1, 2, 3})));
+
+ // Scatter.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 1, 3));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 0, 1, 2})));
+
+ // Try to copy over source - this should fail as we don't support memmove.
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, src_list, 1, 2)),
+ StatusIs(StatusCode::kInvalidArgument));
+
+ // But copying over non-overlapping source ranges should be ok.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, src_list, 2, 2));
+ EXPECT_THAT(GetValuesList(src_list), Eq(MakeValuesList({0, 1, 0, 1})));
+
+ iree_vm_list_release(src_list);
+ iree_vm_list_release(dst_list);
+}
+
+// Tests copying from variant lists to typed lists.
+TEST_F(VMListTest, CopyFromVariants) {
+ // src: [0, 1, B(2), B(3)]
+ iree_vm_list_t* src_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(/*element_type=*/nullptr,
+ /*initial_capacity=*/8,
+ iree_allocator_system(), &src_list));
+ IREE_ASSERT_OK(iree_vm_list_resize(src_list, 4));
+ for (iree_host_size_t i = 0; i < 2; ++i) {
+ iree_vm_value_t value = iree_vm_value_make_i32(i);
+ IREE_ASSERT_OK(iree_vm_list_set_value(src_list, i, &value));
+ }
+ for (iree_host_size_t i = 2; i < 4; ++i) {
+ iree_vm_ref_t ref = MakeRef<B>(i);
+ IREE_ASSERT_OK(iree_vm_list_set_ref_move(src_list, i, &ref));
+ }
+
+ // dst: [4, 5, 6, 7]
+ iree_vm_type_def_t dst_element_type =
+ iree_vm_type_def_make_value_type(IREE_VM_VALUE_TYPE_I32);
+ iree_vm_list_t* dst_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&dst_element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &dst_list));
+ IREE_ASSERT_OK(iree_vm_list_resize(dst_list, 4));
+ for (iree_host_size_t i = 0; i < 4; ++i) {
+ iree_vm_value_t value = iree_vm_value_make_i32(4 + i);
+ IREE_ASSERT_OK(iree_vm_list_set_value(dst_list, i, &value));
+ }
+
+ // Copy no items (no-op).
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 0, 0));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({4, 5, 6, 7})));
+
+ // Copy at start.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 0, 1));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 5, 6, 7})));
+
+ // Copy at end; should fail because the elements are ref types.
+ // We check that nothing in the list changed.
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 3, dst_list, 3, 1)),
+ StatusIs(StatusCode::kInvalidArgument));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 5, 6, 7})));
+
+ // Copy a range in the middle including wrong types.
+ // We check that nothing in the list changed.
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 1, dst_list, 1, 2)),
+ StatusIs(StatusCode::kInvalidArgument));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 5, 6, 7})));
+
+ // Scatter.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 1, 2));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 0, 1, 7})));
+
+ // Try to copy over source - this should fail as we don't support memmove.
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, src_list, 1, 2)),
+ StatusIs(StatusCode::kInvalidArgument));
+
+ // But copying over non-overlapping source ranges should be ok.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, src_list, 2, 2));
+ EXPECT_THAT(GetValuesList(src_list), Eq(MakeValuesList({0, 1, 0, 1})));
+
+ iree_vm_list_release(src_list);
+ iree_vm_list_release(dst_list);
+}
+
+// Tests copying from typed lists to variant lists.
+TEST_F(VMListTest, CopyToVariants) {
+ // src: [0, 1, 2, 3]
+ iree_vm_type_def_t src_element_type =
+ iree_vm_type_def_make_value_type(IREE_VM_VALUE_TYPE_I32);
+ iree_vm_list_t* src_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(&src_element_type, /*initial_capacity=*/8,
+ iree_allocator_system(), &src_list));
+ IREE_ASSERT_OK(iree_vm_list_resize(src_list, 4));
+ for (iree_host_size_t i = 0; i < 4; ++i) {
+ iree_vm_value_t value = iree_vm_value_make_i32(i);
+ IREE_ASSERT_OK(iree_vm_list_set_value(src_list, i, &value));
+ }
+
+ // dst: [4, 5, B(6), B(7)]
+ iree_vm_list_t* dst_list = nullptr;
+ IREE_ASSERT_OK(iree_vm_list_create(/*element_type=*/nullptr,
+ /*initial_capacity=*/8,
+ iree_allocator_system(), &dst_list));
+ IREE_ASSERT_OK(iree_vm_list_resize(dst_list, 4));
+ for (iree_host_size_t i = 0; i < 2; ++i) {
+ iree_vm_value_t value = iree_vm_value_make_i32(4 + i);
+ IREE_ASSERT_OK(iree_vm_list_set_value(dst_list, i, &value));
+ }
+ for (iree_host_size_t i = 2; i < 4; ++i) {
+ iree_vm_ref_t ref = MakeRef<B>(4 + i);
+ IREE_ASSERT_OK(iree_vm_list_set_ref_move(dst_list, i, &ref));
+ }
+
+ // Copy no items (no-op).
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 0, 0));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({4, 5, 6, 7})));
+
+ // Copy at start.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 0, 1));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 5, 6, 7})));
+
+ // Copy at end.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 3, dst_list, 3, 1));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 5, 6, 3})));
+
+ // Copy a range in the middle.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 1, dst_list, 1, 2));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 1, 2, 3})));
+
+ // Scatter.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, dst_list, 1, 3));
+ EXPECT_THAT(GetValuesList(dst_list), Eq(MakeValuesList({0, 0, 1, 2})));
+
+ // Try to copy over source - this should fail as we don't support memmove.
+ EXPECT_THAT(Status(iree_vm_list_copy(src_list, 0, src_list, 1, 2)),
+ StatusIs(StatusCode::kInvalidArgument));
+
+ // But copying over non-overlapping source ranges should be ok.
+ IREE_EXPECT_OK(iree_vm_list_copy(src_list, 0, src_list, 2, 2));
+ EXPECT_THAT(GetValuesList(src_list), Eq(MakeValuesList({0, 1, 0, 1})));
+
+ iree_vm_list_release(src_list);
+ iree_vm_list_release(dst_list);
+}
+
// TODO(benvanik): test value get/set.
// TODO(benvanik): test value conversion.
@@ -597,7 +1219,7 @@
// Pops when empty fail.
iree_vm_ref_t empty_ref{0};
EXPECT_THAT(Status(iree_vm_list_pop_front_ref_move(list, &empty_ref)),
- StatusIs(iree::StatusCode::kOutOfRange));
+ StatusIs(StatusCode::kOutOfRange));
// Push back [0, 5).
for (iree_host_size_t i = 0; i < 5; ++i) {
diff --git a/runtime/src/iree/vm/ref.c b/runtime/src/iree/vm/ref.c
index 64c7f16..5e4c822 100644
--- a/runtime/src/iree/vm/ref.c
+++ b/runtime/src/iree/vm/ref.c
@@ -187,7 +187,8 @@
IREE_API_EXPORT iree_status_t iree_vm_ref_retain_checked(
iree_vm_ref_t* ref, iree_vm_ref_type_t type, iree_vm_ref_t* out_ref) {
- if (ref->type != IREE_VM_REF_TYPE_NULL && ref->type != type) {
+ if (ref->type != IREE_VM_REF_TYPE_NULL && ref->type != type &&
+ type != IREE_VM_REF_TYPE_ANY) {
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"source ref type mismatch");
}
@@ -207,7 +208,8 @@
IREE_API_EXPORT iree_status_t iree_vm_ref_retain_or_move_checked(
int is_move, iree_vm_ref_t* ref, iree_vm_ref_type_t type,
iree_vm_ref_t* out_ref) {
- if (ref->type != IREE_VM_REF_TYPE_NULL && ref->type != type) {
+ if (ref->type != IREE_VM_REF_TYPE_NULL && ref->type != type &&
+ type != IREE_VM_REF_TYPE_ANY) {
// Make no changes on failure.
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"source ref type mismatch");