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