%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/compiler/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/compiler/wasm-gc-lowering.cc |
// Copyright 2022 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/compiler/wasm-gc-lowering.h" #include "src/base/logging.h" #include "src/common/globals.h" #include "src/compiler/common-operator.h" #include "src/compiler/compiler-source-position-table.h" #include "src/compiler/node-properties.h" #include "src/compiler/opcodes.h" #include "src/compiler/operator.h" #include "src/compiler/wasm-graph-assembler.h" #include "src/objects/heap-number.h" #include "src/objects/string.h" #include "src/wasm/object-access.h" #include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-linkage.h" #include "src/wasm/wasm-objects.h" #include "src/wasm/wasm-subtyping.h" namespace v8 { namespace internal { namespace compiler { WasmGCLowering::WasmGCLowering(Editor* editor, MachineGraph* mcgraph, const wasm::WasmModule* module, bool disable_trap_handler, SourcePositionTable* source_position_table) : AdvancedReducer(editor), null_check_strategy_(trap_handler::IsTrapHandlerEnabled() && V8_STATIC_ROOTS_BOOL && !disable_trap_handler ? NullCheckStrategy::kTrapHandler : NullCheckStrategy::kExplicit), gasm_(mcgraph, mcgraph->zone()), module_(module), dead_(mcgraph->Dead()), mcgraph_(mcgraph), source_position_table_(source_position_table) {} Reduction WasmGCLowering::Reduce(Node* node) { switch (node->opcode()) { case IrOpcode::kWasmTypeCheck: return ReduceWasmTypeCheck(node); case IrOpcode::kWasmTypeCheckAbstract: return ReduceWasmTypeCheckAbstract(node); case IrOpcode::kWasmTypeCast: return ReduceWasmTypeCast(node); case IrOpcode::kWasmTypeCastAbstract: return ReduceWasmTypeCastAbstract(node); case IrOpcode::kAssertNotNull: return ReduceAssertNotNull(node); case IrOpcode::kNull: return ReduceNull(node); case IrOpcode::kIsNull: return ReduceIsNull(node); case IrOpcode::kIsNotNull: return ReduceIsNotNull(node); case IrOpcode::kRttCanon: return ReduceRttCanon(node); case IrOpcode::kTypeGuard: return ReduceTypeGuard(node); case IrOpcode::kWasmExternInternalize: return ReduceWasmExternInternalize(node); case IrOpcode::kWasmExternExternalize: return ReduceWasmExternExternalize(node); case IrOpcode::kWasmStructGet: return ReduceWasmStructGet(node); case IrOpcode::kWasmStructSet: return ReduceWasmStructSet(node); case IrOpcode::kWasmArrayGet: return ReduceWasmArrayGet(node); case IrOpcode::kWasmArraySet: return ReduceWasmArraySet(node); case IrOpcode::kWasmArrayLength: return ReduceWasmArrayLength(node); case IrOpcode::kWasmArrayInitializeLength: return ReduceWasmArrayInitializeLength(node); case IrOpcode::kStringAsWtf16: return ReduceStringAsWtf16(node); case IrOpcode::kStringPrepareForGetCodeunit: return ReduceStringPrepareForGetCodeunit(node); default: return NoChange(); } } Node* WasmGCLowering::Null(wasm::ValueType type) { RootIndex index = wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_) ? RootIndex::kNullValue : RootIndex::kWasmNull; return gasm_.LoadImmutable(MachineType::Pointer(), gasm_.LoadRootRegister(), IsolateData::root_slot_offset(index)); } Node* WasmGCLowering::IsNull(Node* object, wasm::ValueType type) { Tagged_t static_null = wasm::GetWasmEngine()->compressed_wasm_null_value_or_zero(); Node* null_value = !wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_) && static_null != 0 ? gasm_.UintPtrConstant(static_null) : Null(type); return gasm_.TaggedEqual(object, null_value); } // TODO(manoskouk): Use the Callbacks infrastructure from wasm-compiler.h to // unify all check/cast implementations. // TODO(manoskouk): Find a way to optimize branches on typechecks. Reduction WasmGCLowering::ReduceWasmTypeCheck(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmTypeCheck); Node* object = node->InputAt(0); Node* rtt = node->InputAt(1); Node* effect_input = NodeProperties::GetEffectInput(node); Node* control_input = NodeProperties::GetControlInput(node); auto config = OpParameter<WasmTypeCheckConfig>(node->op()); int rtt_depth = wasm::GetSubtypingDepth(module_, config.to.ref_index()); bool object_can_be_null = config.from.is_nullable(); bool object_can_be_i31 = wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), config.from, module_); gasm_.InitializeEffectControl(effect_input, control_input); auto end_label = gasm_.MakeLabel(MachineRepresentation::kWord32); bool is_cast_from_any = config.from.is_reference_to(wasm::HeapType::kAny); // If we are casting from any and null results in check failure, then the // {IsDataRefMap} check below subsumes the null check. Otherwise, perform // an explicit null check now. if (object_can_be_null && (!is_cast_from_any || config.to.is_nullable())) { const int kResult = config.to.is_nullable() ? 1 : 0; gasm_.GotoIf(IsNull(object, wasm::kWasmAnyRef), &end_label, BranchHint::kFalse, gasm_.Int32Constant(kResult)); } if (object_can_be_i31) { gasm_.GotoIf(gasm_.IsSmi(object), &end_label, gasm_.Int32Constant(0)); } Node* map = gasm_.LoadMap(object); if (module_->types[config.to.ref_index()].is_final) { gasm_.Goto(&end_label, gasm_.TaggedEqual(map, rtt)); } else { // First, check if types happen to be equal. This has been shown to give // large speedups. gasm_.GotoIf(gasm_.TaggedEqual(map, rtt), &end_label, BranchHint::kTrue, gasm_.Int32Constant(1)); // Check if map instance type identifies a wasm object. if (is_cast_from_any) { Node* is_wasm_obj = gasm_.IsDataRefMap(map); gasm_.GotoIfNot(is_wasm_obj, &end_label, BranchHint::kTrue, gasm_.Int32Constant(0)); } Node* type_info = gasm_.LoadWasmTypeInfo(map); DCHECK_GE(rtt_depth, 0); // If the depth of the rtt is known to be less that the minimum supertype // array length, we can access the supertype without bounds-checking the // supertype array. if (static_cast<uint32_t>(rtt_depth) >= wasm::kMinimumSupertypeArraySize) { Node* supertypes_length = gasm_.BuildChangeSmiToIntPtr(gasm_.LoadImmutableFromObject( MachineType::TaggedSigned(), type_info, wasm::ObjectAccess::ToTagged( WasmTypeInfo::kSupertypesLengthOffset))); gasm_.GotoIfNot(gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth), supertypes_length), &end_label, BranchHint::kTrue, gasm_.Int32Constant(0)); } Node* maybe_match = gasm_.LoadImmutableFromObject( MachineType::TaggedPointer(), type_info, wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset + kTaggedSize * rtt_depth)); gasm_.Goto(&end_label, gasm_.TaggedEqual(maybe_match, rtt)); } gasm_.Bind(&end_label); ReplaceWithValue(node, end_label.PhiAt(0), gasm_.effect(), gasm_.control()); node->Kill(); return Replace(end_label.PhiAt(0)); // Meaningless argument. } Reduction WasmGCLowering::ReduceWasmTypeCheckAbstract(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmTypeCheckAbstract); Node* object = node->InputAt(0); Node* effect_input = NodeProperties::GetEffectInput(node); Node* control_input = NodeProperties::GetControlInput(node); WasmTypeCheckConfig config = OpParameter<WasmTypeCheckConfig>(node->op()); const bool object_can_be_null = config.from.is_nullable(); const bool null_succeeds = config.to.is_nullable(); const bool object_can_be_i31 = wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), config.from, module_) || config.from.heap_representation() == wasm::HeapType::kExtern; gasm_.InitializeEffectControl(effect_input, control_input); Node* result = nullptr; auto end_label = gasm_.MakeLabel(MachineRepresentation::kWord32); wasm::HeapType::Representation to_rep = config.to.heap_representation(); do { // The none-types only perform a null check. They need no control flow. if (to_rep == wasm::HeapType::kNone || to_rep == wasm::HeapType::kNoExtern || to_rep == wasm::HeapType::kNoFunc) { result = IsNull(object, config.from); break; } // Null checks performed by any other type check need control flow. We can // skip the null check if null fails, because it's covered by the Smi check // or instance type check we'll do later. if (object_can_be_null && null_succeeds) { const int kResult = null_succeeds ? 1 : 0; gasm_.GotoIf(IsNull(object, wasm::kWasmAnyRef), &end_label, BranchHint::kFalse, gasm_.Int32Constant(kResult)); } // i31 is special in that the Smi check is the last thing to do. if (to_rep == wasm::HeapType::kI31) { // If earlier optimization passes reached the limit of possible graph // transformations, we could DCHECK(object_can_be_i31) here. result = object_can_be_i31 ? gasm_.IsSmi(object) : gasm_.Int32Constant(0); break; } if (to_rep == wasm::HeapType::kEq) { if (object_can_be_i31) { gasm_.GotoIf(gasm_.IsSmi(object), &end_label, BranchHint::kFalse, gasm_.Int32Constant(1)); } result = gasm_.IsDataRefMap(gasm_.LoadMap(object)); break; } // array, struct, string: i31 fails. if (object_can_be_i31) { gasm_.GotoIf(gasm_.IsSmi(object), &end_label, BranchHint::kFalse, gasm_.Int32Constant(0)); } if (to_rep == wasm::HeapType::kArray) { result = gasm_.HasInstanceType(object, WASM_ARRAY_TYPE); break; } if (to_rep == wasm::HeapType::kStruct) { result = gasm_.HasInstanceType(object, WASM_STRUCT_TYPE); break; } if (to_rep == wasm::HeapType::kString) { Node* instance_type = gasm_.LoadInstanceType(gasm_.LoadMap(object)); result = gasm_.Uint32LessThan(instance_type, gasm_.Uint32Constant(FIRST_NONSTRING_TYPE)); break; } UNREACHABLE(); } while (false); DCHECK_NOT_NULL(result); if (end_label.IsUsed()) { gasm_.Goto(&end_label, result); gasm_.Bind(&end_label); result = end_label.PhiAt(0); } ReplaceWithValue(node, result, gasm_.effect(), gasm_.control()); node->Kill(); return Replace(result); // Meaningless argument. } Reduction WasmGCLowering::ReduceWasmTypeCast(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmTypeCast); Node* object = node->InputAt(0); Node* rtt = node->InputAt(1); Node* effect_input = NodeProperties::GetEffectInput(node); Node* control_input = NodeProperties::GetControlInput(node); auto config = OpParameter<WasmTypeCheckConfig>(node->op()); int rtt_depth = wasm::GetSubtypingDepth(module_, config.to.ref_index()); bool object_can_be_null = config.from.is_nullable(); bool object_can_be_i31 = wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), config.from, module_); gasm_.InitializeEffectControl(effect_input, control_input); auto end_label = gasm_.MakeLabel(); bool is_cast_from_any = config.from.is_reference_to(wasm::HeapType::kAny); // If we are casting from any and null results in check failure, then the // {IsDataRefMap} check below subsumes the null check. Otherwise, perform // an explicit null check now. if (object_can_be_null && (!is_cast_from_any || config.to.is_nullable())) { Node* is_null = IsNull(object, wasm::kWasmAnyRef); if (config.to.is_nullable()) { gasm_.GotoIf(is_null, &end_label, BranchHint::kFalse); } else if (!v8_flags.experimental_wasm_skip_null_checks) { gasm_.TrapIf(is_null, TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); } } if (object_can_be_i31) { gasm_.TrapIf(gasm_.IsSmi(object), TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); } Node* map = gasm_.LoadMap(object); if (module_->types[config.to.ref_index()].is_final) { gasm_.TrapUnless(gasm_.TaggedEqual(map, rtt), TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); gasm_.Goto(&end_label); } else { // First, check if types happen to be equal. This has been shown to give // large speedups. gasm_.GotoIf(gasm_.TaggedEqual(map, rtt), &end_label, BranchHint::kTrue); // Check if map instance type identifies a wasm object. if (is_cast_from_any) { Node* is_wasm_obj = gasm_.IsDataRefMap(map); gasm_.TrapUnless(is_wasm_obj, TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); } Node* type_info = gasm_.LoadWasmTypeInfo(map); DCHECK_GE(rtt_depth, 0); // If the depth of the rtt is known to be less that the minimum supertype // array length, we can access the supertype without bounds-checking the // supertype array. if (static_cast<uint32_t>(rtt_depth) >= wasm::kMinimumSupertypeArraySize) { Node* supertypes_length = gasm_.BuildChangeSmiToIntPtr(gasm_.LoadImmutableFromObject( MachineType::TaggedSigned(), type_info, wasm::ObjectAccess::ToTagged( WasmTypeInfo::kSupertypesLengthOffset))); gasm_.TrapUnless(gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth), supertypes_length), TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); } Node* maybe_match = gasm_.LoadImmutableFromObject( MachineType::TaggedPointer(), type_info, wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset + kTaggedSize * rtt_depth)); gasm_.TrapUnless(gasm_.TaggedEqual(maybe_match, rtt), TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); gasm_.Goto(&end_label); } gasm_.Bind(&end_label); ReplaceWithValue(node, object, gasm_.effect(), gasm_.control()); node->Kill(); return Replace(object); } Reduction WasmGCLowering::ReduceWasmTypeCastAbstract(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmTypeCastAbstract); Node* object = node->InputAt(0); Node* effect_input = NodeProperties::GetEffectInput(node); Node* control_input = NodeProperties::GetControlInput(node); WasmTypeCheckConfig config = OpParameter<WasmTypeCheckConfig>(node->op()); const bool object_can_be_null = config.from.is_nullable(); const bool null_succeeds = config.to.is_nullable(); const bool object_can_be_i31 = wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), config.from, module_) || config.from.heap_representation() == wasm::HeapType::kExtern; gasm_.InitializeEffectControl(effect_input, control_input); auto end_label = gasm_.MakeLabel(); wasm::HeapType::Representation to_rep = config.to.heap_representation(); do { // The none-types only perform a null check. if (to_rep == wasm::HeapType::kNone || to_rep == wasm::HeapType::kNoExtern || to_rep == wasm::HeapType::kNoFunc) { gasm_.TrapUnless(IsNull(object, config.from), TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); break; } // Null checks performed by any other type cast can be skipped if null // fails, because it's covered by the Smi check // or instance type check we'll do later. if (object_can_be_null && null_succeeds && !v8_flags.experimental_wasm_skip_null_checks) { gasm_.GotoIf(IsNull(object, config.from), &end_label, BranchHint::kFalse); } if (to_rep == wasm::HeapType::kI31) { // If earlier optimization passes reached the limit of possible graph // transformations, we could DCHECK(object_can_be_i31) here. Node* success = object_can_be_i31 ? gasm_.IsSmi(object) : gasm_.Int32Constant(0); gasm_.TrapUnless(success, TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); break; } if (to_rep == wasm::HeapType::kEq) { if (object_can_be_i31) { gasm_.GotoIf(gasm_.IsSmi(object), &end_label, BranchHint::kFalse); } gasm_.TrapUnless(gasm_.IsDataRefMap(gasm_.LoadMap(object)), TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); break; } // array, struct, string: i31 fails. if (object_can_be_i31) { gasm_.TrapIf(gasm_.IsSmi(object), TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); } if (to_rep == wasm::HeapType::kArray) { gasm_.TrapUnless(gasm_.HasInstanceType(object, WASM_ARRAY_TYPE), TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); break; } if (to_rep == wasm::HeapType::kStruct) { gasm_.TrapUnless(gasm_.HasInstanceType(object, WASM_STRUCT_TYPE), TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); break; } if (to_rep == wasm::HeapType::kString) { Node* instance_type = gasm_.LoadInstanceType(gasm_.LoadMap(object)); gasm_.TrapUnless( gasm_.Uint32LessThan(instance_type, gasm_.Uint32Constant(FIRST_NONSTRING_TYPE)), TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); break; } UNREACHABLE(); } while (false); if (end_label.IsUsed()) { gasm_.Goto(&end_label); gasm_.Bind(&end_label); } ReplaceWithValue(node, object, gasm_.effect(), gasm_.control()); node->Kill(); return Replace(object); } Reduction WasmGCLowering::ReduceAssertNotNull(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kAssertNotNull); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); Node* object = NodeProperties::GetValueInput(node, 0); gasm_.InitializeEffectControl(effect, control); auto op_parameter = OpParameter<AssertNotNullParameters>(node->op()); // When able, implement a non-null assertion by loading from the object just // after the map word. This will trap for null and be handled by the trap // handler. if (op_parameter.trap_id == TrapId::kTrapNullDereference) { if (!v8_flags.experimental_wasm_skip_null_checks) { // For supertypes of i31ref, we would need to check for i31ref anyway // before loading from the object, so we might as well just check directly // for null. // For subtypes of externref, we use JS null, so we have to check // explicitly. if (null_check_strategy_ == NullCheckStrategy::kExplicit || wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), op_parameter.type, module_) || wasm::IsSubtypeOf(op_parameter.type, wasm::kWasmExternRef, module_)) { gasm_.TrapIf(IsNull(object, op_parameter.type), op_parameter.trap_id); UpdateSourcePosition(gasm_.effect(), node); } else { static_assert(WasmStruct::kHeaderSize > kTaggedSize); static_assert(WasmArray::kHeaderSize > kTaggedSize); static_assert(WasmInternalFunction::kHeaderSize > kTaggedSize); Node* trap_null = gasm_.LoadTrapOnNull( MachineType::Int32(), object, gasm_.IntPtrConstant(wasm::ObjectAccess::ToTagged(kTaggedSize))); UpdateSourcePosition(trap_null, node); } } } else { gasm_.TrapIf(IsNull(object, op_parameter.type), op_parameter.trap_id); UpdateSourcePosition(gasm_.effect(), node); } ReplaceWithValue(node, object, gasm_.effect(), gasm_.control()); node->Kill(); return Replace(object); } Reduction WasmGCLowering::ReduceNull(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kNull); auto type = OpParameter<wasm::ValueType>(node->op()); return Replace(Null(type)); } Reduction WasmGCLowering::ReduceIsNull(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kIsNull); Node* object = NodeProperties::GetValueInput(node, 0); auto type = OpParameter<wasm::ValueType>(node->op()); return Replace(IsNull(object, type)); } Reduction WasmGCLowering::ReduceIsNotNull(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kIsNotNull); Node* object = NodeProperties::GetValueInput(node, 0); auto type = OpParameter<wasm::ValueType>(node->op()); return Replace( gasm_.Word32Equal(IsNull(object, type), gasm_.Int32Constant(0))); } Reduction WasmGCLowering::ReduceRttCanon(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kRttCanon); int type_index = OpParameter<int>(node->op()); Node* instance_node = node->InputAt(0); Node* maps_list = gasm_.LoadImmutable( MachineType::TaggedPointer(), instance_node, WasmInstanceObject::kManagedObjectMapsOffset - kHeapObjectTag); return Replace(gasm_.LoadImmutable( MachineType::TaggedPointer(), maps_list, wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(type_index))); } Reduction WasmGCLowering::ReduceTypeGuard(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kTypeGuard); Node* alias = NodeProperties::GetValueInput(node, 0); ReplaceWithValue(node, alias); node->Kill(); return Replace(alias); } namespace { constexpr int32_t kInt31MaxValue = 0x3fffffff; constexpr int32_t kInt31MinValue = -kInt31MaxValue - 1; } // namespace Reduction WasmGCLowering::ReduceWasmExternInternalize(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmExternInternalize); Node* input = NodeProperties::GetValueInput(node, 0); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); gasm_.InitializeEffectControl(effect, control); auto end_label = gasm_.MakeLabel(MachineRepresentation::kTagged); auto null_label = gasm_.MakeLabel(); auto smi_label = gasm_.MakeLabel(); auto int_to_smi_label = gasm_.MakeLabel(); auto heap_number_label = gasm_.MakeLabel(); gasm_.GotoIf(IsNull(input, wasm::kWasmExternRef), &null_label); gasm_.GotoIf(gasm_.IsSmi(input), &smi_label); Node* is_heap_number = gasm_.HasInstanceType(input, HEAP_NUMBER_TYPE); gasm_.GotoIf(is_heap_number, &heap_number_label); // For anything else, just pass through the value. gasm_.Goto(&end_label, input); gasm_.Bind(&null_label); gasm_.Goto(&end_label, Null(wasm::kWasmNullRef)); // Canonicalize SMI. gasm_.Bind(&smi_label); if constexpr (SmiValuesAre31Bits()) { gasm_.Goto(&end_label, input); } else { auto to_heap_number_label = gasm_.MakeLabel(); Node* int_value = gasm_.BuildChangeSmiToInt32(input); // Convert to heap number if the int32 does not fit into an i31ref. gasm_.GotoIf( gasm_.Int32LessThan(gasm_.Int32Constant(kInt31MaxValue), int_value), &to_heap_number_label); gasm_.GotoIf( gasm_.Int32LessThan(int_value, gasm_.Int32Constant(kInt31MinValue)), &to_heap_number_label); gasm_.Goto(&end_label, input); gasm_.Bind(&to_heap_number_label); Node* heap_number = gasm_.CallBuiltin(Builtin::kWasmInt32ToHeapNumber, Operator::kPure, int_value); gasm_.Goto(&end_label, heap_number); } // Convert HeapNumber to SMI if possible. gasm_.Bind(&heap_number_label); Node* float_value = gasm_.LoadFromObject( MachineType::Float64(), input, wasm::ObjectAccess::ToTagged(HeapNumber::kValueOffset)); // Check range of float value. gasm_.GotoIf( gasm_.Float64LessThan(float_value, gasm_.Float64Constant(kInt31MinValue)), &end_label, input); gasm_.GotoIf( gasm_.Float64LessThan(gasm_.Float64Constant(kInt31MaxValue), float_value), &end_label, input); // Check if value is -0. Node* is_minus_zero = nullptr; if (mcgraph_->machine()->Is64()) { Node* minus_zero = gasm_.Int64Constant(base::bit_cast<int64_t>(-0.0)); Node* float_bits = gasm_.BitcastFloat64ToInt64(float_value); is_minus_zero = gasm_.Word64Equal(float_bits, minus_zero); } else { constexpr int32_t kMinusZeroLoBits = static_cast<int32_t>(0); constexpr int32_t kMinusZeroHiBits = static_cast<int32_t>(1) << 31; auto done = gasm_.MakeLabel(MachineRepresentation::kBit); Node* value_lo = gasm_.Float64ExtractLowWord32(float_value); gasm_.GotoIfNot( gasm_.Word32Equal(value_lo, gasm_.Int32Constant(kMinusZeroLoBits)), &done, gasm_.Int32Constant(0)); Node* value_hi = gasm_.Float64ExtractHighWord32(float_value); gasm_.Goto(&done, gasm_.Word32Equal(value_hi, gasm_.Int32Constant(kMinusZeroHiBits))); gasm_.Bind(&done); is_minus_zero = done.PhiAt(0); } gasm_.GotoIf(is_minus_zero, &end_label, input); // Check if value is integral. Node* int_value = gasm_.ChangeFloat64ToInt32(float_value); gasm_.GotoIf( gasm_.Float64Equal(float_value, gasm_.ChangeInt32ToFloat64(int_value)), &int_to_smi_label); gasm_.Goto(&end_label, input); gasm_.Bind(&int_to_smi_label); gasm_.Goto(&end_label, gasm_.BuildChangeInt32ToSmi(int_value)); gasm_.Bind(&end_label); ReplaceWithValue(node, end_label.PhiAt(0), gasm_.effect(), gasm_.control()); node->Kill(); return Replace(end_label.PhiAt(0)); } Reduction WasmGCLowering::ReduceWasmExternExternalize(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmExternExternalize); Node* object = node->InputAt(0); gasm_.InitializeEffectControl(NodeProperties::GetEffectInput(node), NodeProperties::GetControlInput(node)); auto label = gasm_.MakeLabel(MachineRepresentation::kTagged); gasm_.GotoIfNot(IsNull(object, wasm::kWasmAnyRef), &label, object); gasm_.Goto(&label, Null(wasm::kWasmExternRef)); gasm_.Bind(&label); ReplaceWithValue(node, label.PhiAt(0), gasm_.effect(), gasm_.control()); node->Kill(); return Replace(label.PhiAt(0)); } Reduction WasmGCLowering::ReduceWasmStructGet(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmStructGet); WasmFieldInfo info = OpParameter<WasmFieldInfo>(node->op()); Node* object = NodeProperties::GetValueInput(node, 0); gasm_.InitializeEffectControl(NodeProperties::GetEffectInput(node), NodeProperties::GetControlInput(node)); MachineType type = MachineType::TypeForRepresentation( info.type->field(info.field_index).machine_representation(), info.is_signed); Node* offset = gasm_.FieldOffset(info.type, info.field_index); bool explicit_null_check = info.null_check == kWithNullCheck && (null_check_strategy_ == NullCheckStrategy::kExplicit || info.field_index > wasm::kMaxStructFieldIndexForImplicitNullCheck); bool implicit_null_check = info.null_check == kWithNullCheck && !explicit_null_check; if (explicit_null_check) { gasm_.TrapIf(IsNull(object, wasm::kWasmAnyRef), TrapId::kTrapNullDereference); UpdateSourcePosition(gasm_.effect(), node); } Node* load = implicit_null_check ? gasm_.LoadTrapOnNull(type, object, offset) : info.type->mutability(info.field_index) ? gasm_.LoadFromObject(type, object, offset) : gasm_.LoadImmutableFromObject(type, object, offset); if (implicit_null_check) { UpdateSourcePosition(load, node); } ReplaceWithValue(node, load, gasm_.effect(), gasm_.control()); node->Kill(); return Replace(load); } Reduction WasmGCLowering::ReduceWasmStructSet(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmStructSet); WasmFieldInfo info = OpParameter<WasmFieldInfo>(node->op()); gasm_.InitializeEffectControl(NodeProperties::GetEffectInput(node), NodeProperties::GetControlInput(node)); Node* object = NodeProperties::GetValueInput(node, 0); Node* value = NodeProperties::GetValueInput(node, 1); bool explicit_null_check = info.null_check == kWithNullCheck && (null_check_strategy_ == NullCheckStrategy::kExplicit || info.field_index > wasm::kMaxStructFieldIndexForImplicitNullCheck); bool implicit_null_check = info.null_check == kWithNullCheck && !explicit_null_check; if (explicit_null_check) { gasm_.TrapIf(IsNull(object, wasm::kWasmAnyRef), TrapId::kTrapNullDereference); UpdateSourcePosition(gasm_.effect(), node); } wasm::ValueType field_type = info.type->field(info.field_index); Node* offset = gasm_.FieldOffset(info.type, info.field_index); Node* store = implicit_null_check ? gasm_.StoreTrapOnNull({field_type.machine_representation(), field_type.is_reference() ? kFullWriteBarrier : kNoWriteBarrier}, object, offset, value) : info.type->mutability(info.field_index) ? gasm_.StoreToObject(ObjectAccessForGCStores(field_type), object, offset, value) : gasm_.InitializeImmutableInObject( ObjectAccessForGCStores(field_type), object, offset, value); if (implicit_null_check) { UpdateSourcePosition(store, node); } ReplaceWithValue(node, store, gasm_.effect(), gasm_.control()); node->Kill(); return Replace(store); } Reduction WasmGCLowering::ReduceWasmArrayGet(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmArrayGet); WasmElementInfo info = OpParameter<WasmElementInfo>(node->op()); Node* object = NodeProperties::GetValueInput(node, 0); Node* index = NodeProperties::GetValueInput(node, 1); gasm_.InitializeEffectControl(NodeProperties::GetEffectInput(node), NodeProperties::GetControlInput(node)); Node* offset = gasm_.WasmArrayElementOffset(index, info.type->element_type()); MachineType type = MachineType::TypeForRepresentation( info.type->element_type().machine_representation(), info.is_signed); Node* value = info.type->mutability() ? gasm_.LoadFromObject(type, object, offset) : gasm_.LoadImmutableFromObject(type, object, offset); return Replace(value); } Reduction WasmGCLowering::ReduceWasmArraySet(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmArraySet); const wasm::ArrayType* type = OpParameter<const wasm::ArrayType*>(node->op()); Node* object = NodeProperties::GetValueInput(node, 0); Node* index = NodeProperties::GetValueInput(node, 1); Node* value = NodeProperties::GetValueInput(node, 2); gasm_.InitializeEffectControl(NodeProperties::GetEffectInput(node), NodeProperties::GetControlInput(node)); Node* offset = gasm_.WasmArrayElementOffset(index, type->element_type()); ObjectAccess access = ObjectAccessForGCStores(type->element_type()); Node* store = type->mutability() ? gasm_.StoreToObject(access, object, offset, value) : gasm_.InitializeImmutableInObject(access, object, offset, value); return Replace(store); } Reduction WasmGCLowering::ReduceWasmArrayLength(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmArrayLength); Node* object = NodeProperties::GetValueInput(node, 0); gasm_.InitializeEffectControl(NodeProperties::GetEffectInput(node), NodeProperties::GetControlInput(node)); bool null_check = OpParameter<bool>(node->op()); if (null_check_strategy_ == NullCheckStrategy::kExplicit && null_check == kWithNullCheck) { gasm_.TrapIf(IsNull(object, wasm::kWasmAnyRef), TrapId::kTrapNullDereference); UpdateSourcePosition(gasm_.effect(), node); } bool use_null_trap = null_check_strategy_ == NullCheckStrategy::kTrapHandler && null_check == kWithNullCheck; Node* length = use_null_trap ? gasm_.LoadTrapOnNull( MachineType::Uint32(), object, gasm_.IntPtrConstant( wasm::ObjectAccess::ToTagged(WasmArray::kLengthOffset))) : gasm_.LoadImmutableFromObject( MachineType::Uint32(), object, wasm::ObjectAccess::ToTagged(WasmArray::kLengthOffset)); if (use_null_trap) { UpdateSourcePosition(length, node); } ReplaceWithValue(node, length, gasm_.effect(), gasm_.control()); node->Kill(); return Replace(length); } Reduction WasmGCLowering::ReduceWasmArrayInitializeLength(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kWasmArrayInitializeLength); Node* object = NodeProperties::GetValueInput(node, 0); Node* length = NodeProperties::GetValueInput(node, 1); gasm_.InitializeEffectControl(NodeProperties::GetEffectInput(node), NodeProperties::GetControlInput(node)); Node* set_length = gasm_.InitializeImmutableInObject( ObjectAccess{MachineType::Uint32(), kNoWriteBarrier}, object, wasm::ObjectAccess::ToTagged(WasmArray::kLengthOffset), length); return Replace(set_length); } Reduction WasmGCLowering::ReduceStringAsWtf16(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kStringAsWtf16); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); Node* str = NodeProperties::GetValueInput(node, 0); gasm_.InitializeEffectControl(effect, control); auto done = gasm_.MakeLabel(MachineRepresentation::kTaggedPointer); Node* instance_type = gasm_.LoadInstanceType(gasm_.LoadMap(str)); Node* string_representation = gasm_.Word32And( instance_type, gasm_.Int32Constant(kStringRepresentationMask)); gasm_.GotoIf(gasm_.Word32Equal(string_representation, gasm_.Int32Constant(kSeqStringTag)), &done, str); gasm_.Goto(&done, gasm_.CallBuiltin(Builtin::kWasmStringAsWtf16, Operator::kPure, str)); gasm_.Bind(&done); ReplaceWithValue(node, done.PhiAt(0), gasm_.effect(), gasm_.control()); node->Kill(); return Replace(done.PhiAt(0)); } Reduction WasmGCLowering::ReduceStringPrepareForGetCodeunit(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kStringPrepareForGetCodeunit); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); Node* original_string = NodeProperties::GetValueInput(node, 0); gasm_.InitializeEffectControl(effect, control); auto dispatch = gasm_.MakeLoopLabel(MachineRepresentation::kTaggedPointer, // String. MachineRepresentation::kWord32, // Instance type. MachineRepresentation::kWord32); // Offset. auto next = gasm_.MakeLabel(MachineRepresentation::kTaggedPointer, // String. MachineRepresentation::kWord32, // Instance type. MachineRepresentation::kWord32); // Offset. auto direct_string = gasm_.MakeLabel(MachineRepresentation::kTaggedPointer, // String. MachineRepresentation::kWord32, // Instance type. MachineRepresentation::kWord32); // Offset. // These values will be used to replace the original node's projections. // The first, "string", is either a SeqString or Tagged<Smi>(0) (in case of // external string). Notably this makes it GC-safe: if that string moves, this // pointer will be updated accordingly. The second, "offset", has full // register width so that it can be used to store external pointers: for // external strings, we add up the character backing store's base address and // any slice offset. The third, "character width", is a shift width, i.e. it // is 0 for one-byte strings, 1 for two-byte strings, // kCharWidthBailoutSentinel for uncached external strings (for which // "string"/"offset" are invalid and unusable). auto done = gasm_.MakeLabel(MachineRepresentation::kTagged, // String. MachineType::PointerRepresentation(), // Offset. MachineRepresentation::kWord32); // Character width. Node* original_type = gasm_.LoadInstanceType(gasm_.LoadMap(original_string)); gasm_.Goto(&dispatch, original_string, original_type, gasm_.Int32Constant(0)); gasm_.Bind(&dispatch); { auto thin_string = gasm_.MakeLabel(); auto cons_string = gasm_.MakeLabel(); Node* string = dispatch.PhiAt(0); Node* instance_type = dispatch.PhiAt(1); Node* offset = dispatch.PhiAt(2); static_assert(kIsIndirectStringTag == 1); static constexpr int kIsDirectStringTag = 0; gasm_.GotoIf(gasm_.Word32Equal( gasm_.Word32And(instance_type, gasm_.Int32Constant( kIsIndirectStringMask)), gasm_.Int32Constant(kIsDirectStringTag)), &direct_string, string, instance_type, offset); // Handle indirect strings. Node* string_representation = gasm_.Word32And( instance_type, gasm_.Int32Constant(kStringRepresentationMask)); gasm_.GotoIf(gasm_.Word32Equal(string_representation, gasm_.Int32Constant(kThinStringTag)), &thin_string); gasm_.GotoIf(gasm_.Word32Equal(string_representation, gasm_.Int32Constant(kConsStringTag)), &cons_string); // Sliced string. Node* new_offset = gasm_.Int32Add( offset, gasm_.BuildChangeSmiToInt32(gasm_.LoadImmutableFromObject( MachineType::TaggedSigned(), string, wasm::ObjectAccess::ToTagged(SlicedString::kOffsetOffset)))); Node* parent = gasm_.LoadImmutableFromObject( MachineType::TaggedPointer(), string, wasm::ObjectAccess::ToTagged(SlicedString::kParentOffset)); Node* parent_type = gasm_.LoadInstanceType(gasm_.LoadMap(parent)); gasm_.Goto(&next, parent, parent_type, new_offset); // Thin string. gasm_.Bind(&thin_string); Node* actual = gasm_.LoadImmutableFromObject( MachineType::TaggedPointer(), string, wasm::ObjectAccess::ToTagged(ThinString::kActualOffset)); Node* actual_type = gasm_.LoadInstanceType(gasm_.LoadMap(actual)); // ThinStrings always reference (internalized) direct strings. gasm_.Goto(&direct_string, actual, actual_type, offset); // Flat cons string. (Non-flat cons strings are ruled out by // string.as_wtf16.) gasm_.Bind(&cons_string); Node* first = gasm_.LoadImmutableFromObject( MachineType::TaggedPointer(), string, wasm::ObjectAccess::ToTagged(ConsString::kFirstOffset)); Node* first_type = gasm_.LoadInstanceType(gasm_.LoadMap(first)); gasm_.Goto(&next, first, first_type, offset); gasm_.Bind(&next); gasm_.Goto(&dispatch, next.PhiAt(0), next.PhiAt(1), next.PhiAt(2)); } gasm_.Bind(&direct_string); { Node* string = direct_string.PhiAt(0); Node* instance_type = direct_string.PhiAt(1); Node* offset = direct_string.PhiAt(2); Node* is_onebyte = gasm_.Word32And( instance_type, gasm_.Int32Constant(kStringEncodingMask)); // Char width shift is 1 - (is_onebyte). static_assert(kStringEncodingMask == 1 << 3); Node* charwidth_shift = gasm_.Int32Sub(gasm_.Int32Constant(1), gasm_.Word32Shr(is_onebyte, gasm_.Int32Constant(3))); auto external = gasm_.MakeLabel(); Node* string_representation = gasm_.Word32And( instance_type, gasm_.Int32Constant(kStringRepresentationMask)); gasm_.GotoIf(gasm_.Word32Equal(string_representation, gasm_.Int32Constant(kExternalStringTag)), &external); // Sequential string. static_assert(SeqOneByteString::kCharsOffset == SeqTwoByteString::kCharsOffset); Node* final_offset = gasm_.Int32Add( gasm_.Int32Constant( wasm::ObjectAccess::ToTagged(SeqOneByteString::kCharsOffset)), gasm_.Word32Shl(offset, charwidth_shift)); gasm_.Goto(&done, string, gasm_.BuildChangeInt32ToIntPtr(final_offset), charwidth_shift); // External string. gasm_.Bind(&external); gasm_.GotoIf( gasm_.Word32And(instance_type, gasm_.Int32Constant(kUncachedExternalStringMask)), &done, string, gasm_.IntPtrConstant(0), gasm_.Int32Constant(kCharWidthBailoutSentinel)); Node* resource = gasm_.BuildLoadExternalPointerFromObject( string, ExternalString::kResourceDataOffset, kExternalStringResourceDataTag, gasm_.LoadRootRegister()); Node* shifted_offset = gasm_.Word32Shl(offset, charwidth_shift); final_offset = gasm_.IntPtrAdd( resource, gasm_.BuildChangeInt32ToIntPtr(shifted_offset)); gasm_.Goto(&done, gasm_.SmiConstant(0), final_offset, charwidth_shift); } gasm_.Bind(&done); Node* base = done.PhiAt(0); Node* final_offset = done.PhiAt(1); Node* charwidth_shift = done.PhiAt(2); Node* base_proj = NodeProperties::FindProjection(node, 0); Node* offset_proj = NodeProperties::FindProjection(node, 1); Node* charwidth_proj = NodeProperties::FindProjection(node, 2); if (base_proj) { ReplaceWithValue(base_proj, base, gasm_.effect(), gasm_.control()); base_proj->Kill(); } if (offset_proj) { ReplaceWithValue(offset_proj, final_offset, gasm_.effect(), gasm_.control()); offset_proj->Kill(); } if (charwidth_proj) { ReplaceWithValue(charwidth_proj, charwidth_shift, gasm_.effect(), gasm_.control()); charwidth_proj->Kill(); } // Wire up the dangling end of the new effect chain. ReplaceWithValue(node, node, gasm_.effect(), gasm_.control()); node->Kill(); return Replace(base); } void WasmGCLowering::UpdateSourcePosition(Node* new_node, Node* old_node) { if (source_position_table_) { SourcePosition position = source_position_table_->GetSourcePosition(old_node); DCHECK(position.ScriptOffset() != kNoSourcePosition); source_position_table_->SetSourcePosition(new_node, position); } } } // namespace compiler } // namespace internal } // namespace v8