dart-sdk/runtime/lib/typed_data.cc
Tess Strickland df72359786 [vm/compiler] Convert _TypedList get and set methods to normal methods.
Previously, they were implemented as native methods with special
replacements in the inliner.

Instead, create force-compiled versions of the original inliner
replacements and use those instead of native methods, unless the
flow graph compiler doesn't support unboxing the requested element type.
In that case, the force-compiled version just calls a native method,
and we only keep the native methods that might be needed (that is,
for double/SIMD element access).

Also, revert the change in 26911a6176, since now the _getX/_setX
methods are appropriately inlined instead of failing to inline due
to being native methods.

TEST=vm/dart/typed_list_index_checkbound_il_test

Cq-Include-Trybots: luci.dart.try:vm-aot-linux-debug-x64-try,vm-aot-linux-release-x64-try,vm-linux-debug-x64-try,vm-aot-linux-release-simarm_x64-try,vm-linux-release-simarm-try,vm-ffi-qemu-linux-release-arm-try
Change-Id: I4840883d1fc12b36a450803da339406bec149044
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/330786
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2024-01-12 10:29:09 +00:00

156 lines
6.6 KiB
C++

// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/bootstrap_natives.h"
#include "include/dart_api.h"
#include "vm/exceptions.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_store.h"
namespace dart {
// TypedData.
DEFINE_NATIVE_ENTRY(TypedDataBase_length, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(TypedDataBase, array, arguments->NativeArgAt(0));
return Smi::New(array.Length());
}
DEFINE_NATIVE_ENTRY(TypedDataView_offsetInBytes, 0, 1) {
// "this" is either a _*ArrayView class or _ByteDataView.
GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0));
ASSERT(instance.IsTypedDataView());
return TypedDataView::Cast(instance).offset_in_bytes();
}
DEFINE_NATIVE_ENTRY(TypedDataView_typedData, 0, 1) {
// "this" is either a _*ArrayView class or _ByteDataView.
GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0));
ASSERT(instance.IsTypedDataView());
return TypedDataView::Cast(instance).typed_data();
}
static bool IsClamped(intptr_t cid) {
COMPILE_ASSERT((kTypedDataUint8ClampedArrayCid + 1 ==
kTypedDataUint8ClampedArrayViewCid) &&
(kTypedDataUint8ClampedArrayCid + 2 ==
kExternalTypedDataUint8ClampedArrayCid) &&
(kTypedDataUint8ClampedArrayCid + 3 ==
kUnmodifiableTypedDataUint8ClampedArrayViewCid));
return cid >= kTypedDataUint8ClampedArrayCid &&
cid <= kUnmodifiableTypedDataUint8ClampedArrayViewCid;
}
static bool IsUint8(intptr_t cid) {
COMPILE_ASSERT(
(kTypedDataUint8ArrayCid + 1 == kTypedDataUint8ArrayViewCid) &&
(kTypedDataUint8ArrayCid + 2 == kExternalTypedDataUint8ArrayCid) &&
(kTypedDataUint8ArrayCid + 3 ==
kUnmodifiableTypedDataUint8ArrayViewCid) &&
(kTypedDataUint8ArrayCid + 4 == kTypedDataUint8ClampedArrayCid));
return cid >= kTypedDataUint8ArrayCid &&
cid <= kUnmodifiableTypedDataUint8ClampedArrayViewCid;
}
DEFINE_NATIVE_ENTRY(TypedDataBase_setClampedRange, 0, 5) {
// This is called after bounds checking, so the numeric inputs are
// guaranteed to be Smis, and the length is guaranteed to be non-zero.
const TypedDataBase& dst =
TypedDataBase::CheckedHandle(zone, arguments->NativeArgAt(0));
const Smi& dst_start_smi =
Smi::CheckedHandle(zone, arguments->NativeArgAt(1));
const Smi& length_smi = Smi::CheckedHandle(zone, arguments->NativeArgAt(2));
const TypedDataBase& src =
TypedDataBase::CheckedHandle(zone, arguments->NativeArgAt(3));
const Smi& src_start_smi =
Smi::CheckedHandle(zone, arguments->NativeArgAt(4));
const intptr_t element_size_in_bytes = dst.ElementSizeInBytes();
ASSERT_EQUAL(src.ElementSizeInBytes(), element_size_in_bytes);
const intptr_t dst_start_in_bytes =
dst_start_smi.Value() * element_size_in_bytes;
const intptr_t length_in_bytes = length_smi.Value() * element_size_in_bytes;
const intptr_t src_start_in_bytes =
src_start_smi.Value() * element_size_in_bytes;
#if defined(DEBUG)
// Verify bounds checks weren't needed.
ASSERT(dst_start_in_bytes >= 0);
ASSERT(src_start_in_bytes >= 0);
// The callers of this native function never call it for a zero-sized copy.
ASSERT(length_in_bytes > 0);
const intptr_t dst_length_in_bytes = dst.LengthInBytes();
// Since the length is non-zero, the start can't be the same as the end.
ASSERT(dst_start_in_bytes < dst_length_in_bytes);
ASSERT(length_in_bytes <= dst_length_in_bytes - dst_start_in_bytes);
const intptr_t src_length_in_bytes = src.LengthInBytes();
// Since the length is non-zero, the start can't be the same as the end.
ASSERT(src_start_in_bytes < src_length_in_bytes);
ASSERT(length_in_bytes <= src_length_in_bytes - src_start_in_bytes);
#endif
ASSERT_EQUAL(element_size_in_bytes, 1);
ASSERT(IsClamped(dst.ptr()->GetClassId()));
ASSERT(!IsUint8(src.ptr()->GetClassId()));
NoSafepointScope no_safepoint;
uint8_t* dst_data =
reinterpret_cast<uint8_t*>(dst.DataAddr(dst_start_in_bytes));
int8_t* src_data =
reinterpret_cast<int8_t*>(src.DataAddr(src_start_in_bytes));
for (intptr_t ix = 0; ix < length_in_bytes; ix++) {
int8_t v = *src_data;
if (v < 0) v = 0;
*dst_data = v;
src_data++;
dst_data++;
}
return Object::null();
}
// The native getter and setter functions defined here are only called if
// unboxing doubles or SIMD values is not supported by the flow graph compiler,
// and the provided offsets have already been range checked by the calling code.
#define TYPED_DATA_GETTER(getter, object, ctor) \
DEFINE_NATIVE_ENTRY(TypedData_##getter, 0, 2) { \
GET_NON_NULL_NATIVE_ARGUMENT(TypedDataBase, array, \
arguments->NativeArgAt(0)); \
GET_NON_NULL_NATIVE_ARGUMENT(Smi, offsetInBytes, \
arguments->NativeArgAt(1)); \
return object::ctor(array.getter(offsetInBytes.Value())); \
}
#define TYPED_DATA_SETTER(setter, object, get_object_value, access_type) \
DEFINE_NATIVE_ENTRY(TypedData_##setter, 0, 3) { \
GET_NON_NULL_NATIVE_ARGUMENT(TypedDataBase, array, \
arguments->NativeArgAt(0)); \
GET_NON_NULL_NATIVE_ARGUMENT(Smi, offsetInBytes, \
arguments->NativeArgAt(1)); \
GET_NON_NULL_NATIVE_ARGUMENT(object, value, arguments->NativeArgAt(2)); \
array.setter(offsetInBytes.Value(), \
static_cast<access_type>(value.get_object_value())); \
return Object::null(); \
}
#define TYPED_DATA_NATIVES(type_name, object, ctor, get_object_value, \
access_type) \
TYPED_DATA_GETTER(Get##type_name, object, ctor) \
TYPED_DATA_SETTER(Set##type_name, object, get_object_value, access_type)
TYPED_DATA_NATIVES(Float32, Double, New, value, float)
TYPED_DATA_NATIVES(Float64, Double, New, value, double)
TYPED_DATA_NATIVES(Float32x4, Float32x4, New, value, simd128_value_t)
TYPED_DATA_NATIVES(Int32x4, Int32x4, New, value, simd128_value_t)
TYPED_DATA_NATIVES(Float64x2, Float64x2, New, value, simd128_value_t)
} // namespace dart