blob: c1269b20a3a72315ac5c2e4858dc94d0e24d9c0b [file] [log] [blame]
// Copyright 2019 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/module.h"
#include <string.h>
#include "iree/base/internal/atomics.h"
#include "iree/base/tracing.h"
#include "iree/vm/ref.h"
#include "iree/vm/stack.h"
//===----------------------------------------------------------------------===//
// Function ABI management
//===----------------------------------------------------------------------===//
IREE_API_EXPORT iree_status_t iree_vm_function_call_get_cconv_fragments(
const iree_vm_function_signature_t* signature,
iree_string_view_t* out_arguments, iree_string_view_t* out_results) {
memset(out_arguments, 0, sizeof(*out_arguments));
memset(out_results, 0, sizeof(*out_results));
iree_string_view_t cconv = signature->calling_convention;
if (!cconv.size) {
// No cconv string, so function is `()->()`.
return iree_ok_status();
} else if (cconv.data[0] != '0') {
return iree_make_status(IREE_STATUS_UNIMPLEMENTED,
"unsupported cconv version %c", cconv.data[0]);
}
iree_string_view_t cconv_body = iree_string_view_substr(cconv, 1, INTPTR_MAX);
if (iree_string_view_split(cconv_body, '_', out_arguments, out_results) ==
-1) {
*out_arguments = cconv_body;
}
return iree_ok_status();
}
static iree_status_t iree_vm_function_call_count_fragment_values(
iree_string_view_t cconv_fragment, iree_host_size_t* out_count) {
IREE_ASSERT_ARGUMENT(out_count);
*out_count = 0;
iree_host_size_t count = 0;
for (iree_host_size_t i = 0; i < cconv_fragment.size; ++i) {
switch (cconv_fragment.data[i]) {
case IREE_VM_CCONV_TYPE_VOID:
break;
case IREE_VM_CCONV_TYPE_I32:
case IREE_VM_CCONV_TYPE_F32:
case IREE_VM_CCONV_TYPE_I64:
case IREE_VM_CCONV_TYPE_F64:
case IREE_VM_CCONV_TYPE_REF:
++count;
break;
case IREE_VM_CCONV_TYPE_SPAN_START: {
for (i = i + 1; i < cconv_fragment.size &&
cconv_fragment.data[i] != IREE_VM_CCONV_TYPE_SPAN_END;
++i) {
switch (cconv_fragment.data[i]) {
case IREE_VM_CCONV_TYPE_VOID:
break;
case IREE_VM_CCONV_TYPE_I32:
case IREE_VM_CCONV_TYPE_F32:
case IREE_VM_CCONV_TYPE_I64:
case IREE_VM_CCONV_TYPE_F64:
case IREE_VM_CCONV_TYPE_REF:
++count;
break;
default:
return iree_make_status(IREE_STATUS_UNIMPLEMENTED,
"unsupported cconv span type '%c'",
cconv_fragment.data[i]);
}
}
} break;
default:
return iree_make_status(IREE_STATUS_UNIMPLEMENTED,
"unsupported cconv type '%c'",
cconv_fragment.data[i]);
}
}
*out_count = count;
return iree_ok_status();
}
IREE_API_EXPORT iree_status_t iree_vm_function_call_count_arguments_and_results(
const iree_vm_function_signature_t* signature,
iree_host_size_t* out_argument_count, iree_host_size_t* out_result_count) {
IREE_ASSERT_ARGUMENT(signature);
IREE_ASSERT_ARGUMENT(out_argument_count);
IREE_ASSERT_ARGUMENT(out_result_count);
*out_argument_count = 0;
*out_result_count = 0;
iree_string_view_t arguments, results;
IREE_RETURN_IF_ERROR(iree_vm_function_call_get_cconv_fragments(
signature, &arguments, &results));
IREE_RETURN_IF_ERROR(iree_vm_function_call_count_fragment_values(
arguments, out_argument_count));
IREE_RETURN_IF_ERROR(
iree_vm_function_call_count_fragment_values(results, out_result_count));
return iree_ok_status();
}
IREE_API_EXPORT bool iree_vm_function_call_is_variadic_cconv(
iree_string_view_t cconv) {
return iree_string_view_find_char(cconv, IREE_VM_CCONV_TYPE_SPAN_START, 0) !=
IREE_STRING_VIEW_NPOS;
}
IREE_API_EXPORT iree_status_t iree_vm_function_call_compute_cconv_fragment_size(
iree_string_view_t cconv_fragment,
const iree_vm_register_list_t* segment_size_list,
iree_host_size_t* out_required_size) {
iree_host_size_t required_size = 0;
for (iree_host_size_t i = 0, seg_i = 0; i < cconv_fragment.size;
++i, ++seg_i) {
switch (cconv_fragment.data[i]) {
case IREE_VM_CCONV_TYPE_VOID:
break;
case IREE_VM_CCONV_TYPE_I32:
case IREE_VM_CCONV_TYPE_F32:
required_size += sizeof(int32_t);
break;
case IREE_VM_CCONV_TYPE_I64:
case IREE_VM_CCONV_TYPE_F64:
required_size += sizeof(int64_t);
break;
case IREE_VM_CCONV_TYPE_REF:
required_size += sizeof(iree_vm_ref_t);
break;
case IREE_VM_CCONV_TYPE_SPAN_START: {
if (IREE_UNLIKELY(!segment_size_list) ||
IREE_UNLIKELY(seg_i >= segment_size_list->size)) {
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"variadic argument found but segment size "
"list is missing/underflowed");
}
iree_host_size_t span_count = segment_size_list->registers[seg_i];
required_size += sizeof(int32_t); // count
iree_host_size_t span_size = 0;
for (i = i + 1; i < cconv_fragment.size &&
cconv_fragment.data[i] != IREE_VM_CCONV_TYPE_SPAN_END;
++i) {
switch (cconv_fragment.data[i]) {
case IREE_VM_CCONV_TYPE_VOID:
break;
case IREE_VM_CCONV_TYPE_I32:
case IREE_VM_CCONV_TYPE_F32:
span_size += sizeof(int32_t);
break;
case IREE_VM_CCONV_TYPE_I64:
case IREE_VM_CCONV_TYPE_F64:
span_size += sizeof(int64_t);
break;
case IREE_VM_CCONV_TYPE_REF:
span_size += sizeof(iree_vm_ref_t);
break;
default:
return iree_make_status(IREE_STATUS_UNIMPLEMENTED,
"unsupported cconv span type '%c'",
cconv_fragment.data[i]);
}
}
required_size += span_size * span_count;
} break;
default:
return iree_make_status(IREE_STATUS_UNIMPLEMENTED,
"unsupported cconv type '%c'",
cconv_fragment.data[i]);
}
}
*out_required_size = required_size;
return iree_ok_status();
}
//===----------------------------------------------------------------------===//
// Source locations
//===----------------------------------------------------------------------===//
IREE_API_EXPORT iree_status_t
iree_vm_source_location_format(iree_vm_source_location_t* source_location,
iree_vm_source_location_format_flags_t flags,
iree_string_builder_t* builder) {
IREE_ASSERT_ARGUMENT(builder);
if (!source_location || !source_location->format) {
return iree_status_from_code(IREE_STATUS_UNAVAILABLE);
}
return source_location->format(source_location->self, source_location->data,
flags, builder);
}
//===----------------------------------------------------------------------===//
// iree_vm_module_t
//===----------------------------------------------------------------------===//
IREE_API_EXPORT iree_status_t
iree_vm_module_initialize(iree_vm_module_t* module, void* self) {
IREE_ASSERT_ARGUMENT(module);
IREE_TRACE_ZONE_BEGIN(z0);
memset(module, 0, sizeof(iree_vm_module_t));
module->self = self;
iree_atomic_ref_count_init(&module->ref_count);
IREE_TRACE_ZONE_END(z0);
return iree_ok_status();
}
IREE_API_EXPORT void iree_vm_module_retain(iree_vm_module_t* module) {
if (module) {
iree_atomic_ref_count_inc(&module->ref_count);
}
}
IREE_API_EXPORT void iree_vm_module_release(iree_vm_module_t* module) {
if (module && iree_atomic_ref_count_dec(&module->ref_count) == 1) {
module->destroy(module->self);
}
}
IREE_API_EXPORT iree_string_view_t
iree_vm_module_name(const iree_vm_module_t* module) {
if (!module) {
return iree_make_cstring_view("null");
}
return module->name(module->self);
}
IREE_API_EXPORT iree_vm_module_signature_t
iree_vm_module_signature(const iree_vm_module_t* module) {
if (!module) {
iree_vm_module_signature_t empty;
memset(&empty, 0, sizeof(empty));
return empty;
}
return module->signature(module->self);
}
IREE_API_EXPORT iree_string_view_t iree_vm_module_lookup_attr_by_name(
const iree_vm_module_t* module, iree_string_view_t key) {
IREE_ASSERT_ARGUMENT(module);
if (!module->get_module_attr) {
return iree_string_view_empty();
}
for (iree_host_size_t index = 0;; ++index) {
iree_string_pair_t attr;
iree_status_t status = module->get_module_attr(module->self, index, &attr);
if (!iree_status_is_ok(status)) {
iree_status_ignore(status);
break;
} else if (iree_string_view_equal(key, attr.key)) {
return attr.value;
}
}
return iree_string_view_empty();
}
IREE_API_EXPORT iree_status_t
iree_vm_module_get_attr(const iree_vm_module_t* module, iree_host_size_t index,
iree_string_pair_t* out_attr) {
IREE_ASSERT_ARGUMENT(module);
if (!module->get_module_attr) {
return iree_status_from_code(IREE_STATUS_OUT_OF_RANGE);
}
return module->get_module_attr(module->self, index, out_attr);
}
IREE_API_EXPORT iree_status_t iree_vm_module_enumerate_dependencies(
iree_vm_module_t* module, iree_vm_module_dependency_callback_t callback,
void* user_data) {
IREE_ASSERT_ARGUMENT(module);
IREE_ASSERT_ARGUMENT(callback);
if (!module->enumerate_dependencies) {
return iree_ok_status();
}
IREE_TRACE_ZONE_BEGIN(z0);
iree_status_t status =
module->enumerate_dependencies(module->self, callback, user_data);
IREE_TRACE_ZONE_END(z0);
return status;
}
IREE_API_EXPORT iree_status_t iree_vm_module_lookup_function_by_name(
const iree_vm_module_t* module, iree_vm_function_linkage_t linkage,
iree_string_view_t name, iree_vm_function_t* out_function) {
IREE_ASSERT_ARGUMENT(module);
return module->lookup_function(module->self, linkage, name,
/*expected_signature=*/NULL, out_function);
}
IREE_API_EXPORT iree_status_t iree_vm_module_lookup_function_by_ordinal(
const iree_vm_module_t* module, iree_vm_function_linkage_t linkage,
iree_host_size_t ordinal, iree_vm_function_t* out_function) {
IREE_ASSERT_ARGUMENT(module);
return module->get_function(module->self, linkage, ordinal, out_function,
/*out_name=*/NULL,
/*out_signature=*/NULL);
}
IREE_API_EXPORT iree_status_t iree_vm_module_resolve_source_location(
const iree_vm_module_t* module, iree_vm_function_t function,
iree_vm_source_offset_t pc,
iree_vm_source_location_t* out_source_location) {
IREE_ASSERT_ARGUMENT(module);
IREE_ASSERT_ARGUMENT(out_source_location);
memset(out_source_location, 0, sizeof(*out_source_location));
if (module->resolve_source_location) {
return module->resolve_source_location(module->self, function, pc,
out_source_location);
}
return iree_status_from_code(IREE_STATUS_UNAVAILABLE);
}
//===----------------------------------------------------------------------===//
// iree_vm_function_t
//===----------------------------------------------------------------------===//
IREE_API_EXPORT iree_string_view_t
iree_vm_function_name(const iree_vm_function_t* function) {
IREE_ASSERT_ARGUMENT(function);
if (!function->module) return iree_string_view_empty();
iree_string_view_t name;
iree_status_t status = function->module->get_function(
function->module->self, function->linkage, function->ordinal,
/*out_function=*/NULL,
/*out_name=*/&name,
/*out_signature=*/NULL);
if (!iree_status_is_ok(status)) {
iree_status_ignore(status);
return iree_make_cstring_view("<error>");
}
return name;
}
IREE_API_EXPORT iree_vm_function_signature_t
iree_vm_function_signature(const iree_vm_function_t* function) {
IREE_ASSERT_ARGUMENT(function);
iree_vm_function_signature_t signature;
memset(&signature, 0, sizeof(signature));
IREE_IGNORE_ERROR(function->module->get_function(
function->module->self, function->linkage, function->ordinal,
/*out_function=*/NULL,
/*out_name=*/NULL,
/*out_signature=*/&signature));
return signature;
}
IREE_API_EXPORT iree_string_view_t iree_vm_function_lookup_attr_by_name(
const iree_vm_function_t* function, iree_string_view_t key) {
IREE_ASSERT_ARGUMENT(function);
iree_vm_module_t* module = function->module;
IREE_ASSERT_ARGUMENT(module);
if (!module->get_function_attr) {
return iree_string_view_empty();
}
for (iree_host_size_t index = 0;; ++index) {
iree_string_pair_t attr;
iree_status_t status = module->get_function_attr(
module->self, function->linkage, function->ordinal, index, &attr);
if (!iree_status_is_ok(status)) {
iree_status_ignore(status);
break;
} else if (iree_string_view_equal(key, attr.key)) {
return attr.value;
}
}
return iree_string_view_empty();
}
IREE_API_EXPORT iree_status_t
iree_vm_function_get_attr(iree_vm_function_t function, iree_host_size_t index,
iree_string_pair_t* out_attr) {
IREE_ASSERT_ARGUMENT(function.module);
if (!function.module->get_function_attr) {
return iree_status_from_code(IREE_STATUS_OUT_OF_RANGE);
}
return function.module->get_function_attr(function.module->self,
function.linkage, function.ordinal,
index, out_attr);
}