torque-internal.tq 11.8 KB
Newer Older
1 2 3 4
// 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.

5 6 7 8 9
// 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>;

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
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);
}

27
namespace torque_internal {
28 29 30 31 32 33 34 35 36
// 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;

37 38 39 40
macro TimesSizeOf<T: type>(i: intptr): intptr {
  return i * %SizeOf<T>();
}

41
struct Reference<T: type> {
42
  const object: HeapObject|TaggedZeroPattern;
43 44 45 46 47 48
  const offset: intptr;
  unsafeMarker: Unsafe;
}
type ConstReference<T: type> extends Reference<T>;
type MutableReference<T: type> extends ConstReference<T>;

49
namespace unsafe {
50 51
macro NewReference<T: type>(
    object: HeapObject|TaggedZeroPattern, offset: intptr):&T {
52 53 54
  return %RawDownCast<&T>(
      Reference<T>{object: object, offset: offset, unsafeMarker: Unsafe {}});
}
55 56 57 58 59 60 61
macro NewOffHeapReference<T: type>(ptr: RawPtr<T>):&T {
  return %RawDownCast<&T>(Reference<T>{
    object: kZeroBitPattern,
    offset: Convert<intptr>(Convert<RawPtr>(ptr)) + kHeapObjectTag,
    unsafeMarker: Unsafe {}
  });
}
62 63 64 65 66
macro ReferenceCast<T: type, U: type>(ref:&U):&T {
  const ref = NewReference<T>(ref.object, ref.offset);
  UnsafeCast<T>(*ref);
  return ref;
}
67 68 69 70

extern macro GCUnsafeReferenceToRawPtr(
    HeapObject | TaggedZeroPattern, intptr): RawPtr;

71
}  // namespace unsafe
72

73 74
struct Slice<T: type, Reference: type> {
  macro TryAtIndex(index: intptr): Reference labels OutOfBounds {
75
    if (Convert<uintptr>(index) < Convert<uintptr>(this.length)) {
76
      return unsafe::NewReference<T>(
77
          this.object, this.offset + TimesSizeOf<T>(index));
78 79
    } else {
      goto OutOfBounds;
80
    }
81 82
  }

83
  macro AtIndex(index: intptr): Reference {
84
    return this.TryAtIndex(index) otherwise unreachable;
85 86
  }

87
  macro AtIndex(index: uintptr): Reference {
88
    return this.TryAtIndex(Convert<intptr>(index)) otherwise unreachable;
89 90
  }

91
  macro AtIndex(index: constexpr int31): Reference {
92 93
    const i: intptr = Convert<intptr>(index);
    return this.TryAtIndex(i) otherwise unreachable;
94
  }
Tobias Tebbi's avatar
Tobias Tebbi committed
95

96
  macro AtIndex(index: Smi): Reference {
97 98
    const i: intptr = Convert<intptr>(index);
    return this.TryAtIndex(i) otherwise unreachable;
99 100
  }

101
  macro Iterator(): SliceIterator<T, Reference> {
102
    const end = this.offset + TimesSizeOf<T>(this.length);
103
    return SliceIterator<T, Reference>{
104 105 106 107 108
      object: this.object,
      start: this.offset,
      end: end,
      unsafeMarker: Unsafe {}
    };
Tobias Tebbi's avatar
Tobias Tebbi committed
109
  }
110 111
  macro Iterator(
      startIndex: intptr, endIndex: intptr): SliceIterator<T, Reference> {
112 113 114
    check(
        Convert<uintptr>(endIndex) <= Convert<uintptr>(this.length) &&
        Convert<uintptr>(startIndex) <= Convert<uintptr>(endIndex));
115 116
    const start = this.offset + TimesSizeOf<T>(startIndex);
    const end = this.offset + TimesSizeOf<T>(endIndex);
117
    return SliceIterator<T, Reference>{
118 119 120 121 122
      object: this.object,
      start,
      end,
      unsafeMarker: Unsafe {}
    };
Tobias Tebbi's avatar
Tobias Tebbi committed
123
  }
124

125 126 127 128 129 130
  // 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));
  }

131
  const object: HeapObject|TaggedZeroPattern;
132 133 134 135 136
  const offset: intptr;
  const length: intptr;
  unsafeMarker: Unsafe;
}

137 138
namespace unsafe {

139
macro NewMutableSlice<T: type>(
140 141
    object: HeapObject|TaggedZeroPattern, offset: intptr,
    length: intptr): MutableSlice<T> {
142 143 144 145 146 147 148 149 150
  return %RawDownCast<MutableSlice<T>>(Slice<T, &T>{
    object: object,
    offset: offset,
    length: length,
    unsafeMarker: Unsafe {}
  });
}

macro NewConstSlice<T: type>(
151 152
    object: HeapObject|TaggedZeroPattern, offset: intptr,
    length: intptr): ConstSlice<T> {
153
  return %RawDownCast<ConstSlice<T>>(Slice<T, const &T>{
154 155 156 157
    object: object,
    offset: offset,
    length: length,
    unsafeMarker: Unsafe {}
158
  });
159 160
}

161 162 163
macro NewOffHeapConstSlice<T: type>(
    startPointer: RawPtr<T>, length: intptr): ConstSlice<T> {
  return %RawDownCast<ConstSlice<T>>(Slice<T, const &T>{
164 165 166 167
    object: kZeroBitPattern,
    offset: Convert<intptr>(Convert<RawPtr>(startPointer)) + kHeapObjectTag,
    length: length,
    unsafeMarker: Unsafe {}
168
  });
169 170 171 172
}

}  // namespace unsafe

173
struct SliceIterator<T: type, Reference: type> {
174 175 176
  macro Empty(): bool {
    return this.start == this.end;
  }
177

178
  macro Next(): T labels NoMore {
179
    return *this.NextReference() otherwise NoMore;
180 181
  }

182
  macro NextReference(): Reference labels NoMore {
183 184 185
    if (this.Empty()) {
      goto NoMore;
    } else {
186
      const result = unsafe::NewReference<T>(this.object, this.start);
187 188
      this.start += %SizeOf<T>();
      return result;
189 190
    }
  }
191

192
  object: HeapObject|TaggedZeroPattern;
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
  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;
227 228
extern macro Allocate(
    intptr, constexpr AllocationFlag): UninitializedHeapObject;
229

230 231 232 233
const kAllocateBaseFlags: constexpr AllocationFlag =
    AllocationFlag::kAllowLargeObjectAllocation;
macro AllocateFromNew(
    sizeInBytes: intptr, map: Map, pretenured: bool): UninitializedHeapObject {
234
  assert(ValidAllocationSize(sizeInBytes, map));
235 236 237 238 239 240 241 242
  if (pretenured) {
    return Allocate(
        sizeInBytes,
        %RawConstexprCast<constexpr AllocationFlag>(
            kAllocateBaseFlags | AllocationFlag::kPretenured));
  } else {
    return Allocate(sizeInBytes, kAllocateBaseFlags);
  }
243 244 245
}

macro InitializeFieldsFromIterator<T: type, Iterator: type>(
246
    target: MutableSlice<T>, originIterator: Iterator) {
247 248 249 250
  let targetIterator = target.Iterator();
  let originIterator = originIterator;
  while (true) {
    const ref:&T = targetIterator.NextReference() otherwise break;
251
    *ref = originIterator.Next() otherwise unreachable;
252
  }
253 254 255
}
// Dummy implementations: do not initialize for UninitializedIterator.
InitializeFieldsFromIterator<char8, UninitializedIterator>(
256
    _target: MutableSlice<char8>, _originIterator: UninitializedIterator) {}
257
InitializeFieldsFromIterator<char16, UninitializedIterator>(
258
    _target: MutableSlice<char16>, _originIterator: UninitializedIterator) {}
259 260 261 262 263 264

extern macro IsDoubleHole(HeapObject, intptr): bool;
extern macro StoreDoubleHole(HeapObject, intptr);

macro LoadFloat64OrHole(r:&float64_or_hole): float64_or_hole {
  return float64_or_hole{
265 266
    is_hole: IsDoubleHole(
        %RawDownCast<HeapObject>(r.object), r.offset - kHeapObjectTag),
267
    value: *unsafe::NewReference<float64>(r.object, r.offset)
268 269 270 271
  };
}
macro StoreFloat64OrHole(r:&float64_or_hole, value: float64_or_hole) {
  if (value.is_hole) {
272 273
    StoreDoubleHole(
        %RawDownCast<HeapObject>(r.object), r.offset - kHeapObjectTag);
274
  } else {
275
    *unsafe::NewReference<float64>(r.object, r.offset) = value.value;
276
  }
277
}
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299

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);
}

300 301
extern macro StaticAssert(bool, constexpr string);

302 303 304 305 306
// 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);

307 308 309 310 311 312 313 314 315
// 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);

316 317 318 319 320
}  // namespace torque_internal

// Indicates that an array-field should not be initialized.
// For safety reasons, this is only allowed for untagged types.
struct UninitializedIterator {}
Tobias Tebbi's avatar
Tobias Tebbi committed
321 322 323 324 325 326

// %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;
327

328 329 330 331 332 333
intrinsic %MinInstanceType<T: type>(): constexpr InstanceType;
intrinsic %MaxInstanceType<T: type>(): constexpr InstanceType;

intrinsic %ClassHasMapConstant<T: type>(): constexpr bool;
intrinsic %GetClassMapConstant<T: type>(): Map;

334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
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};
}