blob: 0645f5032e848af3eed1983a8e0230825f3e88c8 [file]
// Copyright 2020 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/vm/list.h"
#include <cstdint>
#include <cstring>
#include "iree/base/api.h"
#include "iree/testing/gtest.h"
#include "iree/testing/status_matchers.h"
#include "iree/vm/instance.h"
#include "iree/vm/ref.h"
class A : public iree::vm::RefObject<A> {
public:
float data() const { return data_; }
void set_data(float value) { data_ = value; }
private:
float data_ = 1.0f;
};
IREE_VM_DECLARE_TYPE_ADAPTERS(test_a, A);
IREE_VM_DEFINE_TYPE_ADAPTERS(test_a, A);
class B : public iree::vm::RefObject<B> {
public:
int data() const { return data_; }
void set_data(int value) { data_ = value; }
private:
int data_ = 2;
};
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_assign(list, i, &variant));
if (iree_vm_variant_is_value(variant)) {
result[i] = iree_vm_variant_value(variant);
} else if (iree_vm_variant_is_ref(variant)) {
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_instance_t* instance, const char* type_name,
iree_vm_ref_type_t* out_registration) {
static iree_vm_ref_type_descriptor_t storage = {0};
storage.type_name = iree_make_cstring_view(type_name);
storage.offsetof_counter = T::offsetof_counter();
storage.destroy = T::DirectDestroy;
IREE_CHECK_OK(
iree_vm_instance_register_type(instance, &storage, out_registration));
}
static void RegisterRefTypes(iree_vm_instance_t* instance) {
RegisterRefType<A>(instance, "AType", &test_a_registration);
RegisterRefType<B>(instance, "BType", &test_b_registration);
}
template <typename T, typename V>
static iree_vm_ref_t MakeRef(V value) {
iree_vm_ref_t ref = {0};
auto* obj = new T();
obj->set_data(value);
IREE_CHECK_OK(iree_vm_ref_wrap_assign(
obj, iree::vm::ref_type_descriptor<T>::type(), &ref));
return ref;
}
static iree_vm_instance_t* instance = nullptr;
struct VMListTest : public ::testing::Test {
static void SetUpTestSuite() {
// Note: VM instance creation registers list types, which is required before
// using the list APIs.
IREE_CHECK_OK(iree_vm_instance_create(IREE_VM_TYPE_CAPACITY_DEFAULT,
iree_allocator_system(), &instance));
RegisterRefTypes(instance);
}
static void TearDownTestSuite() { iree_vm_instance_release(instance); }
};
// Tests simple primitive value list usage, mainly just for demonstration.
// Stores only i32 element types, equivalent to `!vm.list<i32>`.
TEST_F(VMListTest, UsageI32) {
iree_vm_type_def_t element_type =
iree_vm_make_value_type_def(IREE_VM_VALUE_TYPE_I32);
iree_host_size_t initial_capacity = 123;
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, initial_capacity,
iree_allocator_system(), &list));
iree_vm_type_def_t queried_element_type = iree_vm_list_element_type(list);
EXPECT_TRUE(iree_vm_type_def_is_value(queried_element_type));
EXPECT_TRUE(iree_vm_type_def_equal(element_type, queried_element_type));
EXPECT_LE(initial_capacity, iree_vm_list_capacity(list));
EXPECT_EQ(0, iree_vm_list_size(list));
IREE_ASSERT_OK(iree_vm_list_resize(list, 5));
EXPECT_EQ(5, iree_vm_list_size(list));
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_value_t value = iree_vm_value_make_i32((int32_t)i);
IREE_ASSERT_OK(iree_vm_list_set_value(list, i, &value));
}
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_value_t value;
IREE_ASSERT_OK(
iree_vm_list_get_value_as(list, i, IREE_VM_VALUE_TYPE_I32, &value));
EXPECT_EQ(IREE_VM_VALUE_TYPE_I32, value.type);
EXPECT_EQ(i, value.i32);
}
iree_vm_list_release(list);
}
// Tests simple ref object list usage, mainly just for demonstration.
// Stores ref object type A elements only, equivalent to `!vm.list<!vm.ref<A>>`.
TEST_F(VMListTest, UsageRef) {
iree_vm_type_def_t element_type = iree_vm_make_ref_type_def(test_a_type());
iree_host_size_t initial_capacity = 123;
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, initial_capacity,
iree_allocator_system(), &list));
iree_vm_type_def_t queried_element_type = iree_vm_list_element_type(list);
EXPECT_TRUE(iree_vm_type_def_is_ref(queried_element_type));
EXPECT_TRUE(iree_vm_type_def_equal(element_type, queried_element_type));
EXPECT_LE(initial_capacity, iree_vm_list_capacity(list));
EXPECT_EQ(0, iree_vm_list_size(list));
IREE_ASSERT_OK(iree_vm_list_resize(list, 5));
EXPECT_EQ(5, iree_vm_list_size(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_set_ref_move(list, i, &ref_a));
}
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);
}
TEST_F(VMListTest, GetRefRetainOrMove) {
iree_vm_type_def_t element_type = iree_vm_make_ref_type_def(test_a_type());
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, /*initial_capacity=*/1,
iree_allocator_system(), &list));
IREE_ASSERT_OK(iree_vm_list_resize(list, 1));
// Retain path keeps the element intact.
{
iree_vm_ref_t ref_a = MakeRef<A>(1.0f);
IREE_ASSERT_OK(iree_vm_list_set_ref_move(list, 0, &ref_a));
iree_vm_ref_t retained{0};
IREE_ASSERT_OK(iree_vm_list_get_ref_retain_or_move(
list, 0, /*is_move=*/false, &retained));
EXPECT_TRUE(test_a_isa(retained));
iree_vm_ref_release(&retained);
iree_vm_ref_t still_there{0};
IREE_ASSERT_OK(iree_vm_list_get_ref_assign(list, 0, &still_there));
EXPECT_TRUE(test_a_isa(still_there));
}
// Move path transfers ownership and clears the element.
{
iree_vm_ref_t ref_a = MakeRef<A>(2.0f);
IREE_ASSERT_OK(iree_vm_list_set_ref_move(list, 0, &ref_a));
iree_vm_ref_t moved{0};
IREE_ASSERT_OK(
iree_vm_list_get_ref_retain_or_move(list, 0, /*is_move=*/true, &moved));
EXPECT_TRUE(test_a_isa(moved));
iree_vm_ref_release(&moved);
iree_vm_ref_t cleared{0};
IREE_ASSERT_OK(iree_vm_list_get_ref_assign(list, 0, &cleared));
EXPECT_EQ(IREE_VM_REF_TYPE_NULL, cleared.type);
}
iree_vm_list_release(list);
}
// Tests that moving a ref from a variant list properly marks the slot as empty.
TEST_F(VMListTest, VariantListRefMoveMarksSlotEmpty) {
// Create a variant list (stores any type).
iree_vm_type_def_t element_type = iree_vm_make_undefined_type_def();
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, /*initial_capacity=*/1,
iree_allocator_system(), &list));
IREE_ASSERT_OK(iree_vm_list_resize(list, 1));
// Set a ref into the variant slot.
iree_vm_ref_t ref_a = MakeRef<A>(1.0f);
IREE_ASSERT_OK(iree_vm_list_set_ref_move(list, 0, &ref_a));
// Verify the slot contains a ref.
{
iree_vm_variant_t variant;
IREE_ASSERT_OK(iree_vm_list_get_variant_assign(list, 0, &variant));
EXPECT_TRUE(iree_vm_variant_is_ref(variant));
EXPECT_FALSE(iree_vm_variant_is_empty(variant));
}
// Move the ref out of the variant list.
iree_vm_ref_t moved{0};
IREE_ASSERT_OK(
iree_vm_list_get_ref_retain_or_move(list, 0, /*is_move=*/true, &moved));
EXPECT_TRUE(test_a_isa(moved));
iree_vm_ref_release(&moved);
// Verify the slot is now empty (type should be undefined/variant).
{
iree_vm_variant_t variant;
IREE_ASSERT_OK(iree_vm_list_get_variant_assign(list, 0, &variant));
EXPECT_TRUE(iree_vm_variant_is_empty(variant))
<< "After move, variant slot should be empty";
}
// Also test get_variant_move marks the slot empty.
{
iree_vm_ref_t ref_b = MakeRef<A>(2.0f);
IREE_ASSERT_OK(iree_vm_list_set_ref_move(list, 0, &ref_b));
iree_vm_variant_t moved_variant;
IREE_ASSERT_OK(iree_vm_list_get_variant_move(list, 0, &moved_variant));
EXPECT_TRUE(iree_vm_variant_is_ref(moved_variant));
iree_vm_ref_release(&moved_variant.ref);
iree_vm_variant_t after_move;
IREE_ASSERT_OK(iree_vm_list_get_variant_assign(list, 0, &after_move));
EXPECT_TRUE(iree_vm_variant_is_empty(after_move))
<< "After get_variant_move, slot should be empty";
}
iree_vm_list_release(list);
}
// Tests simple variant list usage, mainly just for demonstration.
// Stores any heterogeneous element type, equivalent to `!vm.list<?>`.
TEST_F(VMListTest, UsageVariant) {
iree_vm_type_def_t element_type = iree_vm_make_undefined_type_def();
iree_host_size_t initial_capacity = 123;
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, initial_capacity,
iree_allocator_system(), &list));
iree_vm_type_def_t queried_element_type = iree_vm_list_element_type(list);
EXPECT_TRUE(iree_vm_type_def_is_variant(queried_element_type));
EXPECT_LE(initial_capacity, iree_vm_list_capacity(list));
EXPECT_EQ(0, iree_vm_list_size(list));
IREE_ASSERT_OK(iree_vm_list_resize(list, 10));
EXPECT_EQ(10, iree_vm_list_size(list));
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_value_t value = iree_vm_value_make_i32((int32_t)i);
IREE_ASSERT_OK(iree_vm_list_set_value(list, i, &value));
}
for (iree_host_size_t i = 5; i < 10; ++i) {
iree_vm_ref_t ref_a = MakeRef<A>(static_cast<float>(i));
IREE_ASSERT_OK(iree_vm_list_set_ref_move(list, i, &ref_a));
}
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_value_t value;
IREE_ASSERT_OK(
iree_vm_list_get_value_as(list, i, IREE_VM_VALUE_TYPE_I32, &value));
EXPECT_EQ(IREE_VM_VALUE_TYPE_I32, value.type);
EXPECT_EQ(i, value.i32);
}
for (iree_host_size_t i = 5; i < 10; ++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 cloning lists of value types.
TEST_F(VMListTest, CloneValuesEmpty) {
// Create source list.
iree_vm_type_def_t element_type =
iree_vm_make_value_type_def(IREE_VM_VALUE_TYPE_I32);
iree_host_size_t initial_capacity = 123;
iree_vm_list_t* source_list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, initial_capacity,
iree_allocator_system(), &source_list));
// Clone list.
iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
// Verify the target list matches source parameters.
iree_vm_type_def_t queried_element_type =
iree_vm_list_element_type(target_list);
EXPECT_TRUE(iree_vm_type_def_is_value(queried_element_type));
EXPECT_TRUE(iree_vm_type_def_equal(element_type, queried_element_type));
EXPECT_LE(iree_vm_list_capacity(target_list),
iree_vm_list_capacity(source_list));
EXPECT_EQ(iree_vm_list_size(target_list), iree_vm_list_size(source_list));
iree_vm_list_release(source_list);
iree_vm_list_release(target_list);
}
TEST_F(VMListTest, CloneValues) {
// Create source list.
iree_vm_type_def_t element_type =
iree_vm_make_value_type_def(IREE_VM_VALUE_TYPE_I32);
iree_host_size_t initial_capacity = 123;
iree_vm_list_t* source_list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, initial_capacity,
iree_allocator_system(), &source_list));
IREE_ASSERT_OK(iree_vm_list_resize(source_list, 5));
EXPECT_EQ(5, iree_vm_list_size(source_list));
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_value_t value = iree_vm_value_make_i32((int32_t)i);
IREE_ASSERT_OK(iree_vm_list_set_value(source_list, i, &value));
}
// Clone list.
iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
// Verify the contents match.
EXPECT_EQ(iree_vm_list_size(target_list), iree_vm_list_size(source_list));
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_value_t value;
IREE_ASSERT_OK(iree_vm_list_get_value_as(target_list, i,
IREE_VM_VALUE_TYPE_I32, &value));
EXPECT_EQ(IREE_VM_VALUE_TYPE_I32, value.type);
EXPECT_EQ(i, value.i32);
}
iree_vm_list_release(source_list);
iree_vm_list_release(target_list);
}
// Tests cloning lists of ref types.
TEST_F(VMListTest, CloneRefsEmpty) {
iree_vm_type_def_t element_type = iree_vm_make_ref_type_def(test_a_type());
iree_vm_list_t* source_list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, 8, iree_allocator_system(),
&source_list));
// Clone list.
iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
// Verify the target list matches source parameters.
iree_vm_type_def_t queried_element_type =
iree_vm_list_element_type(target_list);
EXPECT_TRUE(iree_vm_type_def_is_ref(queried_element_type));
EXPECT_TRUE(iree_vm_type_def_equal(element_type, queried_element_type));
EXPECT_LE(iree_vm_list_capacity(target_list),
iree_vm_list_capacity(source_list));
EXPECT_EQ(iree_vm_list_size(target_list), iree_vm_list_size(source_list));
iree_vm_list_release(source_list);
iree_vm_list_release(target_list);
}
TEST_F(VMListTest, CloneRefs) {
iree_vm_type_def_t element_type = iree_vm_make_ref_type_def(test_a_type());
iree_vm_list_t* source_list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, 8, iree_allocator_system(),
&source_list));
IREE_ASSERT_OK(iree_vm_list_resize(source_list, 5));
EXPECT_EQ(5, iree_vm_list_size(source_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_set_ref_move(source_list, i, &ref_a));
}
// Clone list.
iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
// Verify the contents match. Since they are refs we compare pointer equality
// to ensure they were shallowly cloned.
EXPECT_EQ(iree_vm_list_size(target_list), iree_vm_list_size(source_list));
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_ref_t source_ref_a{0};
IREE_ASSERT_OK(iree_vm_list_get_ref_assign(source_list, i, &source_ref_a));
EXPECT_TRUE(test_a_isa(source_ref_a));
auto* source_a = test_a_deref(source_ref_a);
iree_vm_ref_t target_ref_a{0};
IREE_ASSERT_OK(iree_vm_list_get_ref_assign(target_list, i, &target_ref_a));
EXPECT_TRUE(test_a_isa(target_ref_a));
auto* target_a = test_a_deref(target_ref_a);
EXPECT_EQ(source_a, target_a);
}
iree_vm_list_release(source_list);
iree_vm_list_release(target_list);
}
// Tests cloning lists of variant types.
TEST_F(VMListTest, CloneVariantsEmpty) {
iree_vm_type_def_t element_type = iree_vm_make_undefined_type_def();
iree_vm_list_t* source_list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, 10, iree_allocator_system(),
&source_list));
// Clone list.
iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
// Verify the target list matches source parameters.
iree_vm_type_def_t queried_element_type =
iree_vm_list_element_type(target_list);
EXPECT_TRUE(iree_vm_type_def_is_variant(queried_element_type));
EXPECT_TRUE(iree_vm_type_def_equal(element_type, queried_element_type));
EXPECT_LE(iree_vm_list_capacity(target_list),
iree_vm_list_capacity(source_list));
EXPECT_EQ(iree_vm_list_size(target_list), iree_vm_list_size(source_list));
iree_vm_list_release(source_list);
iree_vm_list_release(target_list);
}
TEST_F(VMListTest, CloneVariants) {
iree_vm_type_def_t element_type = iree_vm_make_undefined_type_def();
iree_vm_list_t* source_list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, 10, iree_allocator_system(),
&source_list));
IREE_ASSERT_OK(iree_vm_list_resize(source_list, 10));
EXPECT_EQ(10, iree_vm_list_size(source_list));
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_value_t value = iree_vm_value_make_i32((int32_t)i);
IREE_ASSERT_OK(iree_vm_list_set_value(source_list, i, &value));
}
for (iree_host_size_t i = 5; i < 10; ++i) {
iree_vm_ref_t ref_a = MakeRef<A>(static_cast<float>(i));
IREE_ASSERT_OK(iree_vm_list_set_ref_move(source_list, i, &ref_a));
}
// Clone list.
iree_vm_list_t* target_list = nullptr;
IREE_ASSERT_OK(
iree_vm_list_clone(source_list, iree_allocator_system(), &target_list));
// Verify the contents match. Since they are refs we compare pointer equality
// to ensure they were shallowly cloned.
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_value_t value;
IREE_ASSERT_OK(iree_vm_list_get_value_as(target_list, i,
IREE_VM_VALUE_TYPE_I32, &value));
EXPECT_EQ(IREE_VM_VALUE_TYPE_I32, value.type);
EXPECT_EQ(i, value.i32);
}
for (iree_host_size_t i = 5; i < 10; ++i) {
iree_vm_ref_t source_ref_a{0};
IREE_ASSERT_OK(iree_vm_list_get_ref_assign(source_list, i, &source_ref_a));
EXPECT_TRUE(test_a_isa(source_ref_a));
auto* source_a = test_a_deref(source_ref_a);
iree_vm_ref_t target_ref_a{0};
IREE_ASSERT_OK(iree_vm_list_get_ref_assign(target_list, i, &target_ref_a));
EXPECT_TRUE(test_a_isa(target_ref_a));
auto* target_a = test_a_deref(target_ref_a);
EXPECT_EQ(source_a, target_a);
}
iree_vm_list_release(source_list);
iree_vm_list_release(target_list);
}
// Tests capacity reservation.
TEST_F(VMListTest, Reserve) {
// Allocate with 0 initial capacity (which may get rounded up).
iree_vm_type_def_t element_type = iree_vm_make_undefined_type_def();
iree_host_size_t initial_capacity = 0;
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, initial_capacity,
iree_allocator_system(), &list));
EXPECT_LE(initial_capacity, iree_vm_list_capacity(list));
EXPECT_EQ(0, iree_vm_list_size(list));
// Reserve some capacity, which may allocate.
IREE_ASSERT_OK(iree_vm_list_reserve(list, 100));
iree_host_size_t current_capacity = iree_vm_list_capacity(list);
EXPECT_LE(100, current_capacity);
// Resize to add items, which should not change capacity.
IREE_ASSERT_OK(iree_vm_list_resize(list, 1));
EXPECT_EQ(1, iree_vm_list_size(list));
EXPECT_EQ(current_capacity, iree_vm_list_capacity(list));
// Reserving <= the current capacity should be a no-op.
IREE_ASSERT_OK(iree_vm_list_reserve(list, current_capacity));
EXPECT_EQ(current_capacity, iree_vm_list_capacity(list));
iree_vm_list_release(list);
}
// Tests that reserving extremely large capacities fails gracefully.
// This verifies the overflow checking in reserve().
TEST_F(VMListTest, ReserveOverflow) {
iree_vm_type_def_t element_type = iree_vm_make_undefined_type_def();
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, /*initial_capacity=*/0,
iree_allocator_system(), &list));
// Request a capacity that would overflow when aligned to 64-element boundary.
// IREE_HOST_SIZE_MAX - 32 when aligned up to 64 would overflow.
iree_host_size_t overflow_capacity = IREE_HOST_SIZE_MAX - 32;
EXPECT_THAT(Status(iree_vm_list_reserve(list, overflow_capacity)),
StatusIs(StatusCode::kOutOfRange));
// List should still be usable after failed reserve.
EXPECT_EQ(0, iree_vm_list_size(list));
IREE_ASSERT_OK(iree_vm_list_resize(list, 10));
EXPECT_EQ(10, iree_vm_list_size(list));
iree_vm_list_release(list);
}
// Tests that resizing to extremely large sizes fails gracefully.
TEST_F(VMListTest, ResizeOverflow) {
iree_vm_type_def_t element_type =
iree_vm_make_value_type_def(IREE_VM_VALUE_TYPE_I32);
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, /*initial_capacity=*/4,
iree_allocator_system(), &list));
// Request a size that would overflow when computing storage requirements.
iree_host_size_t overflow_size = IREE_HOST_SIZE_MAX / 2;
EXPECT_THAT(Status(iree_vm_list_resize(list, overflow_size)),
StatusIs(StatusCode::kOutOfRange));
// List should still be usable after failed resize.
EXPECT_EQ(0, iree_vm_list_size(list));
IREE_ASSERT_OK(iree_vm_list_resize(list, 10));
EXPECT_EQ(10, iree_vm_list_size(list));
iree_vm_list_release(list);
}
// Tests the behavior of resize for truncation and extension on primitives.
TEST_F(VMListTest, ResizeI32) {
iree_vm_type_def_t element_type =
iree_vm_make_value_type_def(IREE_VM_VALUE_TYPE_I32);
iree_host_size_t initial_capacity = 4;
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, initial_capacity,
iree_allocator_system(), &list));
EXPECT_LE(initial_capacity, iree_vm_list_capacity(list));
EXPECT_EQ(0, iree_vm_list_size(list));
// Extend and zero-initialize.
IREE_ASSERT_OK(iree_vm_list_resize(list, 5));
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_value_t value;
IREE_ASSERT_OK(
iree_vm_list_get_value_as(list, i, IREE_VM_VALUE_TYPE_I32, &value));
EXPECT_EQ(0, value.i32);
}
// Overwrite with [0, 5).
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_value_t value = iree_vm_value_make_i32((int32_t)i);
IREE_ASSERT_OK(iree_vm_list_set_value(list, i, &value));
}
// Truncate to [0, 2) and then extend again.
// This ensures that we test the primitive clearing path during cleanup:
// [int, int, int, int, int]
// |___________| <- truncation region
IREE_ASSERT_OK(iree_vm_list_resize(list, 2));
IREE_ASSERT_OK(iree_vm_list_resize(list, 5));
// Ensure that elements 2+ are zeroed after having been reset while 0 and 1
// are still valid as before.
for (iree_host_size_t i = 0; i < 2; ++i) {
iree_vm_value_t value;
IREE_ASSERT_OK(
iree_vm_list_get_value_as(list, i, IREE_VM_VALUE_TYPE_I32, &value));
EXPECT_EQ(i, value.i32);
}
for (iree_host_size_t i = 2; i < 5; ++i) {
iree_vm_value_t value;
IREE_ASSERT_OK(
iree_vm_list_get_value_as(list, i, IREE_VM_VALUE_TYPE_I32, &value));
EXPECT_EQ(0, value.i32);
}
iree_vm_list_release(list);
}
// Tests the behavior of resize for truncation and extension on refs.
TEST_F(VMListTest, ResizeRef) {
iree_vm_type_def_t element_type = iree_vm_make_ref_type_def(test_a_type());
iree_host_size_t initial_capacity = 4;
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, initial_capacity,
iree_allocator_system(), &list));
EXPECT_LE(initial_capacity, iree_vm_list_capacity(list));
EXPECT_EQ(0, iree_vm_list_size(list));
// Extend and zero-initialize.
IREE_ASSERT_OK(iree_vm_list_resize(list, 5));
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_assign(list, i, &ref_a));
EXPECT_TRUE(iree_vm_ref_is_null(&ref_a));
}
// Overwrite with [0, 5).
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_set_ref_move(list, i, &ref_a));
}
// Truncate to [0, 2) and then extend again.
// This ensures that we test the ref path during cleanup:
// [ref, ref, ref, ref, ref]
// |___________| <- truncation region
IREE_ASSERT_OK(iree_vm_list_resize(list, 2));
IREE_ASSERT_OK(iree_vm_list_resize(list, 5));
// Ensure that elements 2+ are reset after having been reset while 0 and 1
// are still valid as before.
for (iree_host_size_t i = 0; i < 2; ++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);
}
for (iree_host_size_t i = 2; i < 5; ++i) {
iree_vm_ref_t ref_a{0};
IREE_ASSERT_OK(iree_vm_list_get_ref_assign(list, i, &ref_a));
EXPECT_TRUE(iree_vm_ref_is_null(&ref_a));
}
iree_vm_list_release(list);
}
// Tests the behavior of resize for truncation and extension on variants.
TEST_F(VMListTest, ResizeVariant) {
iree_vm_type_def_t element_type = iree_vm_make_undefined_type_def();
iree_host_size_t initial_capacity = 4;
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, initial_capacity,
iree_allocator_system(), &list));
EXPECT_LE(initial_capacity, iree_vm_list_capacity(list));
EXPECT_EQ(0, iree_vm_list_size(list));
// Extend and zero-initialize.
IREE_ASSERT_OK(iree_vm_list_resize(list, 5));
for (iree_host_size_t i = 0; i < 5; ++i) {
iree_vm_variant_t value = iree_vm_variant_empty();
IREE_ASSERT_OK(iree_vm_list_get_variant_assign(list, i, &value));
EXPECT_TRUE(iree_vm_variant_is_empty(value));
}
// Overwrite with [0, 5) in mixed types.
for (iree_host_size_t i = 0; i < 4; ++i) {
iree_vm_ref_t ref_a = MakeRef<A>((float)i);
IREE_ASSERT_OK(iree_vm_list_set_ref_move(list, i, &ref_a));
}
for (iree_host_size_t i = 4; i < 5; ++i) {
iree_vm_value_t value = iree_vm_value_make_i32((int32_t)i);
IREE_ASSERT_OK(iree_vm_list_set_value(list, i, &value));
}
// Truncate to [0, 2) and then extend again.
// This ensures that we test the variant path during cleanup:
// [ref, ref, ref, ref, int]
// |___________| <- truncation region
IREE_ASSERT_OK(iree_vm_list_resize(list, 2));
IREE_ASSERT_OK(iree_vm_list_resize(list, 5));
// Ensure that elements 2+ are reset after having been reset while 0 and 1
// are still valid as before.
for (iree_host_size_t i = 0; i < 2; ++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);
}
for (iree_host_size_t i = 2; i < 5; ++i) {
iree_vm_variant_t value = iree_vm_variant_empty();
IREE_ASSERT_OK(iree_vm_list_get_variant_assign(list, i, &value));
EXPECT_TRUE(iree_vm_variant_is_empty(value));
}
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_make_ref_type_def(test_a_type());
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_make_ref_type_def(test_a_type());
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_make_ref_type_def(test_b_type());
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_TRUE(iree_vm_type_def_equal(queried_type_a, 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_TRUE(iree_vm_type_def_equal(queried_type_b, 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_make_value_type_def(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_make_value_type_def(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_make_value_type_def(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_make_value_type_def(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_make_ref_type_def(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_make_ref_type_def(test_a_type());
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_make_ref_type_def(test_b_type());
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(iree_vm_make_undefined_type_def(),
/*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(iree_vm_make_undefined_type_def(),
/*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(iree_vm_make_undefined_type_def(),
/*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_make_value_type_def(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_make_value_type_def(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(iree_vm_make_undefined_type_def(),
/*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.
// TODO(benvanik): test ref get/set.
// Tests pushing and popping ref objects.
TEST_F(VMListTest, PushPopRef) {
iree_vm_type_def_t element_type = iree_vm_make_ref_type_def(test_a_type());
iree_host_size_t initial_capacity = 4;
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, initial_capacity,
iree_allocator_system(), &list));
EXPECT_LE(initial_capacity, iree_vm_list_capacity(list));
EXPECT_EQ(0, iree_vm_list_size(list));
// 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(StatusCode::kOutOfRange));
// Push back [0, 5).
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));
}
// Pop the first two [0, 1] and leave [2, 5).
// This ensures that we test the ref path during cleanup:
// [ref, ref, ref, ref, ref]
// |______| <- popped region
for (iree_host_size_t i = 0; i < 2; ++i) {
iree_vm_ref_t ref_a{0};
IREE_ASSERT_OK(iree_vm_list_pop_front_ref_move(list, &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);
}
// Ensure that elements 2+ are valid but now at offset 0.
for (iree_host_size_t i = 2; i < 5; ++i) {
iree_vm_ref_t ref_a{0};
IREE_ASSERT_OK(iree_vm_list_get_ref_retain(list, i - 2, &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);
}
// Push back two more to get [2, 7).
for (iree_host_size_t i = 5; i < 7; ++i) {
iree_vm_ref_t ref_a = MakeRef<A>((float)i);
IREE_ASSERT_OK(iree_vm_list_push_ref_move(list, &ref_a));
}
// Ensure the new elements got added to the end.
for (iree_host_size_t i = 2; i < 7; ++i) {
iree_vm_ref_t ref_a{0};
IREE_ASSERT_OK(iree_vm_list_get_ref_retain(list, i - 2, &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);
}
// TODO(benvanik): test primitive variant get/set.
// TODO(benvanik): test ref variant get/set.
// Test that F64 values (eg Python floats) can be converted to F32 values
// when pushed to a VM list expecting F32 values.
TEST_F(VMListTest, F64ToF32Conversion) {
iree_vm_type_def_t element_type =
iree_vm_make_value_type_def(IREE_VM_VALUE_TYPE_F32);
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, /*capacity=*/10,
iree_allocator_system(), &list));
// Python passes floats as F64 (double)
double python_value = 0.7;
iree_vm_value_t f64_value = iree_vm_value_make_f64(python_value);
// Push the F64 value to a list expecting F32
// This internally calls iree_vm_list_convert_value_type(F64 -> F32)
IREE_ASSERT_OK(iree_vm_list_push_value(list, &f64_value));
// Retrieve the value and verify it was converted correctly
iree_vm_value_t retrieved_value;
IREE_ASSERT_OK(iree_vm_list_get_value(list, 0, &retrieved_value));
EXPECT_EQ(retrieved_value.type, IREE_VM_VALUE_TYPE_F32);
float expected_f32 = (float)python_value;
EXPECT_NEAR(retrieved_value.f32, expected_f32, 1e-6f);
EXPECT_NE(retrieved_value.f32, 0.0f);
iree_vm_list_release(list);
}
// Test F32 to F64 conversion
TEST_F(VMListTest, F32ToF64Conversion) {
iree_vm_type_def_t element_type =
iree_vm_make_value_type_def(IREE_VM_VALUE_TYPE_F64);
iree_vm_list_t* list = nullptr;
IREE_ASSERT_OK(iree_vm_list_create(element_type, /*capacity=*/10,
iree_allocator_system(), &list));
// Push an F32 value to a list expecting F64
float f32_value = 3.14159f;
iree_vm_value_t value = iree_vm_value_make_f32(f32_value);
IREE_ASSERT_OK(iree_vm_list_push_value(list, &value));
// Retrieve and verify
iree_vm_value_t retrieved_value;
IREE_ASSERT_OK(iree_vm_list_get_value(list, 0, &retrieved_value));
EXPECT_EQ(retrieved_value.type, IREE_VM_VALUE_TYPE_F64);
EXPECT_NEAR(retrieved_value.f64, (double)f32_value, 1e-6);
EXPECT_NE(retrieved_value.f64, 0.0);
iree_vm_list_release(list);
}
} // namespace