// 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; hole: 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; } bitfield struct TurbofanTypeHighBits extends uint32 { sandboxed_pointer: 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 (TheHole): { return bitsetLow.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 (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; } Print('Type assertion failed! (value/expectedType/nodeId)'); Print(value); Print(expectedType); Print(nodeId); unreachable; }