blob: d9f05f55331c097cba7601ad549008f50d8fc3d7 [file] [log] [blame]
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Unfortunately, MutableSlice<> is currently not a subtype of ConstSlice.
// This would require struct subtyping, which is not yet supported.
type MutableSlice<T: type> extends torque_internal::Slice<T, &T>;
type ConstSlice<T: type> extends torque_internal::Slice<T, const &T>;
macro Subslice<T: type>(slice: ConstSlice<T>, start: intptr, length: intptr):
ConstSlice<T>labels OutOfBounds {
if (Unsigned(length) > Unsigned(slice.length)) goto OutOfBounds;
if (Unsigned(start) > Unsigned(slice.length - length)) goto OutOfBounds;
const offset = slice.offset + torque_internal::TimesSizeOf<T>(start);
return torque_internal::unsafe::NewConstSlice<T>(
slice.object, offset, length);
}
macro Subslice<T: type>(slice: MutableSlice<T>, start: intptr, length: intptr):
MutableSlice<T>labels OutOfBounds {
if (Unsigned(length) > Unsigned(slice.length)) goto OutOfBounds;
if (Unsigned(start) > Unsigned(slice.length - length)) goto OutOfBounds;
const offset = slice.offset + torque_internal::TimesSizeOf<T>(start);
return torque_internal::unsafe::NewMutableSlice<T>(
slice.object, offset, length);
}
namespace torque_internal {
// Unsafe is a marker that we require to be passed when calling internal APIs
// that might lead to unsoundness when used incorrectly. Unsafe markers should
// therefore not be instantiated anywhere outside of this namespace.
struct Unsafe {}
// Size of a type in memory (on the heap). For class types, this is the size
// of the pointer, not of the instance.
intrinsic %SizeOf<T: type>(): constexpr int31;
macro TimesSizeOf<T: type>(i: intptr): intptr {
return i * %SizeOf<T>();
}
struct Reference<T: type> {
const object: HeapObject|TaggedZeroPattern;
const offset: intptr;
unsafeMarker: Unsafe;
}
type ConstReference<T: type> extends Reference<T>;
type MutableReference<T: type> extends ConstReference<T>;
namespace unsafe {
macro NewReference<T: type>(
object: HeapObject|TaggedZeroPattern, offset: intptr):&T {
return %RawDownCast<&T>(
Reference<T>{object: object, offset: offset, unsafeMarker: Unsafe {}});
}
macro NewOffHeapReference<T: type>(ptr: RawPtr<T>):&T {
return %RawDownCast<&T>(Reference<T>{
object: kZeroBitPattern,
offset: Convert<intptr>(Convert<RawPtr>(ptr)) + kHeapObjectTag,
unsafeMarker: Unsafe {}
});
}
macro ReferenceCast<T: type, U: type>(ref:&U):&T {
const ref = NewReference<T>(ref.object, ref.offset);
UnsafeCast<T>(*ref);
return ref;
}
extern macro GCUnsafeReferenceToRawPtr(
HeapObject | TaggedZeroPattern, intptr): RawPtr;
} // namespace unsafe
struct Slice<T: type, Reference: type> {
macro TryAtIndex(index: intptr): Reference labels OutOfBounds {
if (Convert<uintptr>(index) < Convert<uintptr>(this.length)) {
return unsafe::NewReference<T>(
this.object, this.offset + TimesSizeOf<T>(index));
} else {
goto OutOfBounds;
}
}
macro AtIndex(index: intptr): Reference {
return this.TryAtIndex(index) otherwise unreachable;
}
macro AtIndex(index: uintptr): Reference {
return this.TryAtIndex(Convert<intptr>(index)) otherwise unreachable;
}
macro AtIndex(index: constexpr int31): Reference {
const i: intptr = Convert<intptr>(index);
return this.TryAtIndex(i) otherwise unreachable;
}
macro AtIndex(index: Smi): Reference {
const i: intptr = Convert<intptr>(index);
return this.TryAtIndex(i) otherwise unreachable;
}
macro Iterator(): SliceIterator<T, Reference> {
const end = this.offset + TimesSizeOf<T>(this.length);
return SliceIterator<T, Reference>{
object: this.object,
start: this.offset,
end: end,
unsafeMarker: Unsafe {}
};
}
macro Iterator(
startIndex: intptr, endIndex: intptr): SliceIterator<T, Reference> {
check(
Convert<uintptr>(endIndex) <= Convert<uintptr>(this.length) &&
Convert<uintptr>(startIndex) <= Convert<uintptr>(endIndex));
const start = this.offset + TimesSizeOf<T>(startIndex);
const end = this.offset + TimesSizeOf<T>(endIndex);
return SliceIterator<T, Reference>{
object: this.object,
start,
end,
unsafeMarker: Unsafe {}
};
}
// WARNING: This can return a raw pointer into the heap, which is not GC-safe.
macro GCUnsafeStartPointer(): RawPtr<T> {
return %RawDownCast<RawPtr<T>>(
unsafe::GCUnsafeReferenceToRawPtr(this.object, this.offset));
}
const object: HeapObject|TaggedZeroPattern;
const offset: intptr;
const length: intptr;
unsafeMarker: Unsafe;
}
namespace unsafe {
macro NewMutableSlice<T: type>(
object: HeapObject|TaggedZeroPattern, offset: intptr,
length: intptr): MutableSlice<T> {
return %RawDownCast<MutableSlice<T>>(Slice<T, &T>{
object: object,
offset: offset,
length: length,
unsafeMarker: Unsafe {}
});
}
macro NewConstSlice<T: type>(
object: HeapObject|TaggedZeroPattern, offset: intptr,
length: intptr): ConstSlice<T> {
return %RawDownCast<ConstSlice<T>>(Slice<T, const &T>{
object: object,
offset: offset,
length: length,
unsafeMarker: Unsafe {}
});
}
macro NewOffHeapConstSlice<T: type>(
startPointer: RawPtr<T>, length: intptr): ConstSlice<T> {
return %RawDownCast<ConstSlice<T>>(Slice<T, const &T>{
object: kZeroBitPattern,
offset: Convert<intptr>(Convert<RawPtr>(startPointer)) + kHeapObjectTag,
length: length,
unsafeMarker: Unsafe {}
});
}
} // namespace unsafe
struct SliceIterator<T: type, Reference: type> {
macro Empty(): bool {
return this.start == this.end;
}
macro Next(): T labels NoMore {
return *this.NextReference() otherwise NoMore;
}
macro NextReference(): Reference labels NoMore {
if (this.Empty()) {
goto NoMore;
} else {
const result = unsafe::NewReference<T>(this.object, this.start);
this.start += %SizeOf<T>();
return result;
}
}
object: HeapObject|TaggedZeroPattern;
start: intptr;
end: intptr;
unsafeMarker: Unsafe;
}
macro AddIndexedFieldSizeToObjectSize(
baseSize: intptr, arrayLength: intptr, fieldSize: constexpr int32): intptr {
const arrayLength = Convert<int32>(arrayLength);
const byteLength = TryInt32Mul(arrayLength, fieldSize)
otherwise unreachable;
return TryIntPtrAdd(baseSize, Convert<intptr>(byteLength))
otherwise unreachable;
}
macro AlignTagged(x: intptr): intptr {
// Round up to a multiple of kTaggedSize.
return (x + kObjectAlignmentMask) & ~kObjectAlignmentMask;
}
macro IsTaggedAligned(x: intptr): bool {
return (x & kObjectAlignmentMask) == 0;
}
macro ValidAllocationSize(sizeInBytes: intptr, map: Map): bool {
if (sizeInBytes <= 0) return false;
if (!IsTaggedAligned(sizeInBytes)) return false;
const instanceSizeInWords = Convert<intptr>(map.instance_size_in_words);
return instanceSizeInWords == kVariableSizeSentinel ||
instanceSizeInWords * kTaggedSize == sizeInBytes;
}
type UninitializedHeapObject extends HeapObject;
extern macro GetInstanceTypeMap(constexpr InstanceType): Map;
extern macro Allocate(
intptr, constexpr AllocationFlag): UninitializedHeapObject;
const kAllocateBaseFlags: constexpr AllocationFlag =
AllocationFlag::kAllowLargeObjectAllocation;
macro AllocateFromNew(
sizeInBytes: intptr, map: Map, pretenured: bool): UninitializedHeapObject {
assert(ValidAllocationSize(sizeInBytes, map));
if (pretenured) {
return Allocate(
sizeInBytes,
%RawConstexprCast<constexpr AllocationFlag>(
kAllocateBaseFlags | AllocationFlag::kPretenured));
} else {
return Allocate(sizeInBytes, kAllocateBaseFlags);
}
}
macro InitializeFieldsFromIterator<T: type, Iterator: type>(
target: MutableSlice<T>, originIterator: Iterator) {
let targetIterator = target.Iterator();
let originIterator = originIterator;
while (true) {
const ref:&T = targetIterator.NextReference() otherwise break;
*ref = originIterator.Next() otherwise unreachable;
}
}
// Dummy implementations: do not initialize for UninitializedIterator.
InitializeFieldsFromIterator<char8, UninitializedIterator>(
_target: MutableSlice<char8>, _originIterator: UninitializedIterator) {}
InitializeFieldsFromIterator<char16, UninitializedIterator>(
_target: MutableSlice<char16>, _originIterator: UninitializedIterator) {}
extern macro IsDoubleHole(HeapObject, intptr): bool;
extern macro StoreDoubleHole(HeapObject, intptr);
macro LoadFloat64OrHole(r:&float64_or_hole): float64_or_hole {
return float64_or_hole{
is_hole: IsDoubleHole(
%RawDownCast<HeapObject>(r.object), r.offset - kHeapObjectTag),
value: *unsafe::NewReference<float64>(r.object, r.offset)
};
}
macro StoreFloat64OrHole(r:&float64_or_hole, value: float64_or_hole) {
if (value.is_hole) {
StoreDoubleHole(
%RawDownCast<HeapObject>(r.object), r.offset - kHeapObjectTag);
} else {
*unsafe::NewReference<float64>(r.object, r.offset) = value.value;
}
}
macro DownCastForTorqueClass<T : type extends HeapObject>(o: HeapObject):
T labels CastError {
const map = o.map;
const minInstanceType = %MinInstanceType<T>();
const maxInstanceType = %MaxInstanceType<T>();
if constexpr (minInstanceType == maxInstanceType) {
if constexpr (%ClassHasMapConstant<T>()) {
if (map != %GetClassMapConstant<T>()) goto CastError;
} else {
if (map.instance_type != minInstanceType) goto CastError;
}
} else {
const diff: int32 = maxInstanceType - minInstanceType;
const offset = Convert<int32>(Convert<uint16>(map.instance_type)) -
Convert<int32>(Convert<uint16>(
FromConstexpr<InstanceType>(minInstanceType)));
if (Unsigned(offset) > Unsigned(diff)) goto CastError;
}
return %RawDownCast<T>(o);
}
extern macro StaticAssert(bool, constexpr string);
// This is for the implementation of the dot operator. In any context where the
// dot operator is available, the correct way to get the length of an indexed
// field x from object o is `(&o.x).length`.
intrinsic %IndexedFieldLength<T: type>(o: T, f: constexpr string);
// If field x is defined as optional, then &o.x returns a reference to the field
// or crashes the program (unreachable) if the field is not present. Usually
// that's the most convenient behavior, but in rare cases such as the
// implementation of the dot operator, we may instead need to get a Slice to the
// optional field, which is either length zero or one depending on whether the
// field is present. This intrinsic provides Slices for both indexed fields
// (equivalent to &o.x) and optional fields.
intrinsic %FieldSlice<T: type>(o: T, f: constexpr string);
} // namespace torque_internal
// Indicates that an array-field should not be initialized.
// For safety reasons, this is only allowed for untagged types.
struct UninitializedIterator {}
// %RawDownCast should *never* be used anywhere in Torque code except for
// in Torque-based UnsafeCast operators preceeded by an appropriate
// type assert()
intrinsic %RawDownCast<To: type, From: type>(x: From): To;
intrinsic %RawConstexprCast<To: type, From: type>(f: From): To;
intrinsic %MinInstanceType<T: type>(): constexpr InstanceType;
intrinsic %MaxInstanceType<T: type>(): constexpr InstanceType;
intrinsic %ClassHasMapConstant<T: type>(): constexpr bool;
intrinsic %GetClassMapConstant<T: type>(): Map;
struct IteratorSequence<T: type, FirstIterator: type, SecondIterator: type> {
macro Empty(): bool {
return this.first.Empty() && this.second.Empty();
}
macro Next(): T labels NoMore {
return this.first.Next()
otherwise return (this.second.Next() otherwise NoMore);
}
first: FirstIterator;
second: SecondIterator;
}
macro IteratorSequence<T: type, FirstIterator: type, SecondIterator: type>(
first: FirstIterator, second: SecondIterator):
IteratorSequence<T, FirstIterator, SecondIterator> {
return IteratorSequence<T>{first, second};
}