blob: eebbf842a3f6c911ea0df7c68fc1f596025ab99d [file] [log] [blame]
// Copyright 2021 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.
#include "src/objects/turbofan-types.h"
const kMaxIntPtr: constexpr IntegerLiteral
generates 'IntegerLiteral(ca_.Is64() ? 0x7FFFFFFFFFFFFFFF : 0x7FFFFFFF)';
const kMinIntPtr: constexpr IntegerLiteral
generates 'IntegerLiteral(ca_.Is64() ? 0x8000000000000000 : 0x80000000)';
@export
@abstract
class TurbofanType extends HeapObject {}
// TurbofanBitsetType is 64 bit.
// We use two separate 32 bit bitsets in Torque, due to limitted support
// of 64 bit bitsets.
bitfield struct TurbofanTypeLowBits extends uint32 {
_unused_padding_field_1: bool: 1 bit;
other_unsigned31: bool: 1 bit;
other_unsigned32: bool: 1 bit;
other_signed32: bool: 1 bit;
other_number: bool: 1 bit;
other_string: bool: 1 bit;
negative31: bool: 1 bit;
null: bool: 1 bit;
undefined: bool: 1 bit;
boolean: bool: 1 bit;
unsigned30: bool: 1 bit;
minus_zero: bool: 1 bit;
naN: bool: 1 bit;
symbol: bool: 1 bit;
internalized_string: bool: 1 bit;
other_callable: bool: 1 bit;
other_object: bool: 1 bit;
other_undetectable: bool: 1 bit;
callable_proxy: bool: 1 bit;
other_proxy: bool: 1 bit;
callable_function: bool: 1 bit;
class_constructor: bool: 1 bit;
bound_function: bool: 1 bit;
other_internal: bool: 1 bit;
external_pointer: bool: 1 bit;
array: bool: 1 bit;
unsigned_big_int_63: bool: 1 bit;
other_unsigned_big_int_64: bool: 1 bit;
negative_big_int_63: bool: 1 bit;
other_big_int: bool: 1 bit;
wasm_object: bool: 1 bit;
sandboxed_pointer: bool: 1 bit;
}
bitfield struct TurbofanTypeHighBits extends uint32 {
machine: bool: 1 bit;
hole: bool: 1 bit;
string_wrapper: bool: 1 bit;
}
@export
class TurbofanBitsetType extends TurbofanType {
bitset_low: TurbofanTypeLowBits;
bitset_high: TurbofanTypeHighBits;
}
@export
class TurbofanUnionType extends TurbofanType {
type1: TurbofanType;
type2: TurbofanType;
}
@export
class TurbofanRangeType extends TurbofanType {
min: float64;
max: float64;
}
@export
class TurbofanHeapConstantType extends TurbofanType {
constant: HeapObject;
}
@export
class TurbofanOtherNumberConstantType extends TurbofanType {
constant: float64;
}
macro IsMinusZero(x: float64): bool {
return x == 0 && 1.0 / x < 0;
}
macro TestTurbofanBitsetType(
value: Object, bitsetLow: TurbofanTypeLowBits,
bitsetHigh: TurbofanTypeHighBits): bool {
// Silence unused warnings on builds that don't need {bitsetHigh}.
const _unused = bitsetHigh;
typeswitch (value) {
case (value: Number): {
const valueF = Convert<float64>(value);
if (IsInteger(value)) {
if (IsMinusZero(valueF)) {
return bitsetLow.minus_zero;
} else if (valueF < -0x80000000) {
return bitsetLow.other_number;
} else if (valueF < -0x40000000) {
return bitsetLow.other_signed32;
} else if (valueF < 0) {
return bitsetLow.negative31;
} else if (valueF < 0x40000000) {
return bitsetLow.unsigned30;
} else if (valueF < 0x80000000) {
return bitsetLow.other_unsigned31;
} else if (valueF <= 0xffffffff) {
return bitsetLow.other_unsigned32;
} else {
return bitsetLow.other_number;
}
} else if (Float64IsNaN(valueF)) {
return bitsetLow.naN;
} else {
return bitsetLow.other_number;
}
}
case (Null): {
return bitsetLow.null;
}
case (Undefined): {
return bitsetLow.undefined;
}
case (Boolean): {
return bitsetLow.boolean;
}
case (Symbol): {
return bitsetLow.symbol;
}
case (s: String): {
if (s.IsNotInternalized()) {
return bitsetLow.other_string;
} else {
return bitsetLow.internalized_string;
}
}
case (proxy: JSProxy): {
return Is<Callable>(proxy) ? bitsetLow.callable_proxy :
bitsetLow.other_proxy;
}
case (fun: JSFunction): {
if (fun.shared_function_info.flags.is_class_constructor) {
return bitsetLow.class_constructor;
} else {
return bitsetLow.callable_function;
}
}
case (JSBoundFunction): {
return bitsetLow.bound_function;
}
case (Hole): {
return bitsetHigh.hole;
}
case (JSArray): {
return bitsetLow.array;
}
case (bi: BigInt): {
dcheck(!bitsetLow.other_big_int || bitsetLow.other_unsigned_big_int_64);
dcheck(!bitsetLow.other_big_int || bitsetLow.negative_big_int_63);
dcheck(
!bitsetLow.other_unsigned_big_int_64 ||
bitsetLow.unsigned_big_int_63);
dcheck(!bitsetLow.negative_big_int_63 || bitsetLow.unsigned_big_int_63);
// On 32 bit architectures, [Un]signedBigInt64 types are not used, yet.
if (!Is64()) {
return bitsetLow.other_big_int;
}
const length = bigint::ReadBigIntLength(bi);
if (length > 1) {
return bitsetLow.other_big_int;
} else if (length == 0) {
return bitsetLow.unsigned_big_int_63;
}
dcheck(length == 1);
const sign = bigint::ReadBigIntSign(bi);
const digit = bigint::LoadBigIntDigit(bi, 0);
if (sign == bigint::kPositiveSign) {
return bitsetLow.other_unsigned_big_int_64 ||
(digit <= Convert<uintptr>(kMaxIntPtr) &&
bitsetLow.unsigned_big_int_63);
} else {
return bitsetLow.other_big_int ||
(digit <= Convert<uintptr>(kMinIntPtr) &&
bitsetLow.negative_big_int_63);
}
}
case (wrapper: JSPrimitiveWrapper): {
if (Is<String>(wrapper.value)) {
return bitsetHigh.string_wrapper;
} else {
return bitsetLow.other_object;
}
}
case (object: JSObject): {
if (object.map.IsUndetectable()) {
return bitsetLow.other_undetectable;
} else if (Is<Callable>(object)) {
return bitsetLow.other_callable;
} else {
return bitsetLow.other_object;
}
}
@if(V8_ENABLE_WEBASSEMBLY)
case (WasmObject): {
return bitsetLow.wasm_object;
}
case (Object): {
return false;
}
}
}
builtin TestTurbofanType(
implicit context: Context)(value: Object,
expectedType: TurbofanType): Boolean {
typeswitch (expectedType) {
case (t: TurbofanBitsetType): {
return Convert<Boolean>(
TestTurbofanBitsetType(value, t.bitset_low, t.bitset_high));
}
case (t: TurbofanUnionType): {
return Convert<Boolean>(
TestTurbofanType(value, t.type1) == True ||
TestTurbofanType(value, t.type2) == True);
}
case (t: TurbofanRangeType): {
const value = Cast<Number>(value) otherwise return False;
if (!IsIntegerOrSomeInfinity(value)) return False;
const valueF = Convert<float64>(value);
return Convert<Boolean>(
!IsMinusZero(valueF) && t.min <= valueF && valueF <= t.max);
}
case (t: TurbofanHeapConstantType): {
return Convert<Boolean>(TaggedEqual(value, t.constant));
}
case (t: TurbofanOtherNumberConstantType): {
const value =
Convert<float64>(Cast<Number>(value) otherwise return False);
return Convert<Boolean>(value == t.constant);
}
case (TurbofanType): {
unreachable;
}
}
}
builtin CheckTurbofanType(
implicit context: Context)(value: Object, expectedType: TurbofanType,
nodeId: Smi): Undefined {
if (TestTurbofanType(value, expectedType) == True) {
return Undefined;
}
PrintErr('Type assertion failed! (value/expectedType/nodeId)');
PrintErr(value);
PrintErr(expectedType);
PrintErr(nodeId);
unreachable;
}