v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
wasm-gc-lowering.cc
Go to the documentation of this file.
1// Copyright 2022 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include "src/base/logging.h"
17#include "src/objects/string.h"
23
24namespace v8 {
25namespace internal {
26namespace compiler {
27
28namespace {
29int TaggedOffset(FieldAccess access) {
30 DCHECK(access.base_is_tagged);
31 return wasm::ObjectAccess::ToTagged(access.offset);
32}
33} // namespace
34
36 const wasm::WasmModule* module,
37 bool disable_trap_handler,
38 SourcePositionTable* source_position_table)
39 : AdvancedReducer(editor),
40 null_check_strategy_(trap_handler::IsTrapHandlerEnabled() &&
41 V8_STATIC_ROOTS_BOOL && !disable_trap_handler
44 gasm_(mcgraph, mcgraph->zone()),
45 module_(module),
46 dead_(mcgraph->Dead()),
47 mcgraph_(mcgraph),
48 source_position_table_(source_position_table) {}
49
51 switch (node->opcode()) {
52 case IrOpcode::kWasmTypeCheck:
53 return ReduceWasmTypeCheck(node);
54 case IrOpcode::kWasmTypeCheckAbstract:
55 return ReduceWasmTypeCheckAbstract(node);
56 case IrOpcode::kWasmTypeCast:
57 return ReduceWasmTypeCast(node);
58 case IrOpcode::kWasmTypeCastAbstract:
59 return ReduceWasmTypeCastAbstract(node);
60 case IrOpcode::kAssertNotNull:
61 return ReduceAssertNotNull(node);
62 case IrOpcode::kNull:
63 return ReduceNull(node);
64 case IrOpcode::kIsNull:
65 return ReduceIsNull(node);
66 case IrOpcode::kIsNotNull:
67 return ReduceIsNotNull(node);
68 case IrOpcode::kRttCanon:
69 return ReduceRttCanon(node);
70 case IrOpcode::kTypeGuard:
71 return ReduceTypeGuard(node);
72 case IrOpcode::kWasmAnyConvertExtern:
73 return ReduceWasmAnyConvertExtern(node);
74 case IrOpcode::kWasmExternConvertAny:
75 return ReduceWasmExternConvertAny(node);
76 case IrOpcode::kWasmStructGet:
77 return ReduceWasmStructGet(node);
78 case IrOpcode::kWasmStructSet:
79 return ReduceWasmStructSet(node);
80 case IrOpcode::kWasmArrayGet:
81 return ReduceWasmArrayGet(node);
82 case IrOpcode::kWasmArraySet:
83 return ReduceWasmArraySet(node);
84 case IrOpcode::kWasmArrayLength:
85 return ReduceWasmArrayLength(node);
86 case IrOpcode::kWasmArrayInitializeLength:
88 case IrOpcode::kStringAsWtf16:
89 return ReduceStringAsWtf16(node);
90 case IrOpcode::kStringPrepareForGetCodeunit:
92 default:
93 return NoChange();
94 }
95}
96
98 RootIndex index =
99 type.use_wasm_null() ? RootIndex::kWasmNull : RootIndex::kNullValue;
102}
103
105#if V8_STATIC_ROOTS_BOOL
106 Node* null_value = gasm_.UintPtrConstant(
107 type.use_wasm_null() ? StaticReadOnlyRoot::kWasmNull
108 : StaticReadOnlyRoot::kNullValue);
109#else
110 Node* null_value = Null(type);
111#endif
112 return gasm_.TaggedEqual(object, null_value);
113}
114
115// TODO(manoskouk): Use the Callbacks infrastructure from wasm-compiler.h to
116// unify all check/cast implementations.
117// TODO(manoskouk): Find a way to optimize branches on typechecks.
119 DCHECK_EQ(node->opcode(), IrOpcode::kWasmTypeCheck);
120
121 Node* object = node->InputAt(0);
122 Node* rtt = node->InputAt(1);
123 Node* effect_input = NodeProperties::GetEffectInput(node);
124 Node* control_input = NodeProperties::GetControlInput(node);
125 auto config = OpParameter<WasmTypeCheckConfig>(node->op());
126 int rtt_depth = wasm::GetSubtypingDepth(module_, config.to.ref_index());
127 bool object_can_be_null = config.from.is_nullable();
128 bool object_can_be_i31 =
129 wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), config.from, module_);
130
131 gasm_.InitializeEffectControl(effect_input, control_input);
132
134 bool is_cast_from_any = config.from.is_reference_to(wasm::HeapType::kAny);
135
136 // If we are casting from any and null results in check failure, then the
137 // {IsDataRefMap} check below subsumes the null check. Otherwise, perform
138 // an explicit null check now.
139 if (object_can_be_null && (!is_cast_from_any || config.to.is_nullable())) {
140 const int kResult = config.to.is_nullable() ? 1 : 0;
141 gasm_.GotoIf(IsNull(object, wasm::kWasmAnyRef), &end_label,
143 }
144
145 if (object_can_be_i31) {
146 gasm_.GotoIf(gasm_.IsSmi(object), &end_label, gasm_.Int32Constant(0));
147 }
148
149 Node* map = gasm_.LoadMap(object);
150
151 DCHECK_IMPLIES(module_->type(config.to.ref_index()).is_final,
152 config.exactness == kExactMatchOnly);
153
154 if (config.exactness == kExactMatchOnly) {
155 gasm_.Goto(&end_label, gasm_.TaggedEqual(map, rtt));
156 } else {
157 // First, check if types happen to be equal. This has been shown to give
158 // large speedups.
159 gasm_.GotoIf(gasm_.TaggedEqual(map, rtt), &end_label, BranchHint::kTrue,
161
162 // Check if map instance type identifies a wasm object.
163 if (is_cast_from_any) {
164 Node* is_wasm_obj = gasm_.IsDataRefMap(map);
165 gasm_.GotoIfNot(is_wasm_obj, &end_label, BranchHint::kTrue,
167 }
168
169 Node* type_info = gasm_.LoadWasmTypeInfo(map);
170 DCHECK_GE(rtt_depth, 0);
171 // If the depth of the rtt is known to be less that the minimum supertype
172 // array length, we can access the supertype without bounds-checking the
173 // supertype array.
174 if (static_cast<uint32_t>(rtt_depth) >= wasm::kMinimumSupertypeArraySize) {
175 Node* supertypes_length =
177 MachineType::TaggedSigned(), type_info,
179 WasmTypeInfo::kSupertypesLengthOffset)));
180 gasm_.GotoIfNot(gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth),
181 supertypes_length),
182 &end_label, BranchHint::kTrue, gasm_.Int32Constant(0));
183 }
184
185 Node* maybe_match = gasm_.LoadImmutableFromObject(
186 MachineType::TaggedPointer(), type_info,
187 wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset +
188 kTaggedSize * rtt_depth));
189
190 gasm_.Goto(&end_label, gasm_.TaggedEqual(maybe_match, rtt));
191 }
192
193 gasm_.Bind(&end_label);
194
195 ReplaceWithValue(node, end_label.PhiAt(0), gasm_.effect(), gasm_.control());
196 node->Kill();
197 return Replace(end_label.PhiAt(0)); // Meaningless argument.
198}
199
201 DCHECK_EQ(node->opcode(), IrOpcode::kWasmTypeCheckAbstract);
202
203 Node* object = node->InputAt(0);
204 Node* effect_input = NodeProperties::GetEffectInput(node);
205 Node* control_input = NodeProperties::GetControlInput(node);
207 const bool object_can_be_null = config.from.is_nullable();
208 const bool null_succeeds = config.to.is_nullable();
209 const bool object_can_be_i31 =
210 wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), config.from, module_) ||
212
213 gasm_.InitializeEffectControl(effect_input, control_input);
214
215 Node* result = nullptr;
217
218 wasm::HeapType::Representation to_rep = config.to.heap_representation();
219 do {
220 // The none-types only perform a null check. They need no control flow.
221 if (to_rep == wasm::HeapType::kNone ||
222 to_rep == wasm::HeapType::kNoExtern ||
223 to_rep == wasm::HeapType::kNoFunc || to_rep == wasm::HeapType::kNoExn) {
224 result = IsNull(object, config.from);
225 break;
226 }
227 // Null checks performed by any other type check need control flow. We can
228 // skip the null check if null fails, because it's covered by the Smi check
229 // or instance type check we'll do later.
230 if (object_can_be_null && null_succeeds) {
231 const int kResult = null_succeeds ? 1 : 0;
232 gasm_.GotoIf(IsNull(object, wasm::kWasmAnyRef), &end_label,
234 }
235 // i31 is special in that the Smi check is the last thing to do.
236 if (to_rep == wasm::HeapType::kI31) {
237 // If earlier optimization passes reached the limit of possible graph
238 // transformations, we could DCHECK(object_can_be_i31) here.
239 result = object_can_be_i31 ? gasm_.IsSmi(object) : gasm_.Int32Constant(0);
240 break;
241 }
242 if (to_rep == wasm::HeapType::kEq) {
243 if (object_can_be_i31) {
244 gasm_.GotoIf(gasm_.IsSmi(object), &end_label, BranchHint::kFalse,
246 }
248 break;
249 }
250 // array, struct, string: i31 fails.
251 if (object_can_be_i31) {
252 gasm_.GotoIf(gasm_.IsSmi(object), &end_label, BranchHint::kFalse,
254 }
255 if (to_rep == wasm::HeapType::kArray) {
256 result = gasm_.HasInstanceType(object, WASM_ARRAY_TYPE);
257 break;
258 }
259 if (to_rep == wasm::HeapType::kStruct) {
260 result = gasm_.HasInstanceType(object, WASM_STRUCT_TYPE);
261 break;
262 }
263 if (to_rep == wasm::HeapType::kString ||
265 Node* instance_type = gasm_.LoadInstanceType(gasm_.LoadMap(object));
266 result = gasm_.Uint32LessThan(instance_type,
268 break;
269 }
270 UNREACHABLE();
271 } while (false);
272
274 if (end_label.IsUsed()) {
275 gasm_.Goto(&end_label, result);
276 gasm_.Bind(&end_label);
277 result = end_label.PhiAt(0);
278 }
279
281 node->Kill();
282 return Replace(result); // Meaningless argument.
283}
284
286 DCHECK_EQ(node->opcode(), IrOpcode::kWasmTypeCast);
287
288 Node* object = node->InputAt(0);
289 Node* rtt = node->InputAt(1);
290 Node* effect_input = NodeProperties::GetEffectInput(node);
291 Node* control_input = NodeProperties::GetControlInput(node);
292 auto config = OpParameter<WasmTypeCheckConfig>(node->op());
293 int rtt_depth = wasm::GetSubtypingDepth(module_, config.to.ref_index());
294 bool object_can_be_null = config.from.is_nullable();
295 bool object_can_be_i31 =
296 wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), config.from, module_);
297
298 gasm_.InitializeEffectControl(effect_input, control_input);
299
300 auto end_label = gasm_.MakeLabel();
301 bool is_cast_from_any = config.from.is_reference_to(wasm::HeapType::kAny);
302
303 // If we are casting from any and null results in check failure, then the
304 // {IsDataRefMap} check below subsumes the null check. Otherwise, perform
305 // an explicit null check now.
306 if (object_can_be_null && (!is_cast_from_any || config.to.is_nullable())) {
307 Node* is_null = IsNull(object, wasm::kWasmAnyRef);
308 if (config.to.is_nullable()) {
309 gasm_.GotoIf(is_null, &end_label, BranchHint::kFalse);
310 } else if (!v8_flags.experimental_wasm_skip_null_checks) {
311 gasm_.TrapIf(is_null, TrapId::kTrapIllegalCast);
313 }
314 }
315
316 if (object_can_be_i31) {
317 gasm_.TrapIf(gasm_.IsSmi(object), TrapId::kTrapIllegalCast);
319 }
320
321 Node* map = gasm_.LoadMap(object);
322
323 DCHECK_IMPLIES(module_->type(config.to.ref_index()).is_final,
324 config.exactness == kExactMatchOnly);
325
326 if (config.exactness == kExactMatchOnly) {
327 gasm_.TrapUnless(gasm_.TaggedEqual(map, rtt), TrapId::kTrapIllegalCast);
329 gasm_.Goto(&end_label);
330 } else {
331 // First, check if types happen to be equal. This has been shown to give
332 // large speedups.
333 gasm_.GotoIf(gasm_.TaggedEqual(map, rtt), &end_label, BranchHint::kTrue);
334
335 // Check if map instance type identifies a wasm object.
336 if (is_cast_from_any) {
337 Node* is_wasm_obj = gasm_.IsDataRefMap(map);
338 gasm_.TrapUnless(is_wasm_obj, TrapId::kTrapIllegalCast);
340 }
341
342 Node* type_info = gasm_.LoadWasmTypeInfo(map);
343 DCHECK_GE(rtt_depth, 0);
344 // If the depth of the rtt is known to be less that the minimum supertype
345 // array length, we can access the supertype without bounds-checking the
346 // supertype array.
347 if (static_cast<uint32_t>(rtt_depth) >= wasm::kMinimumSupertypeArraySize) {
348 Node* supertypes_length =
350 MachineType::TaggedSigned(), type_info,
352 WasmTypeInfo::kSupertypesLengthOffset)));
353 gasm_.TrapUnless(gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth),
354 supertypes_length),
355 TrapId::kTrapIllegalCast);
357 }
358
359 Node* maybe_match = gasm_.LoadImmutableFromObject(
360 MachineType::TaggedPointer(), type_info,
361 wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset +
362 kTaggedSize * rtt_depth));
363
364 gasm_.TrapUnless(gasm_.TaggedEqual(maybe_match, rtt),
365 TrapId::kTrapIllegalCast);
367 gasm_.Goto(&end_label);
368 }
369
370 gasm_.Bind(&end_label);
371
372 ReplaceWithValue(node, object, gasm_.effect(), gasm_.control());
373 node->Kill();
374 return Replace(object);
375}
376
378 DCHECK_EQ(node->opcode(), IrOpcode::kWasmTypeCastAbstract);
379
380 Node* object = node->InputAt(0);
381 Node* effect_input = NodeProperties::GetEffectInput(node);
382 Node* control_input = NodeProperties::GetControlInput(node);
384 const bool object_can_be_null = config.from.is_nullable();
385 const bool null_succeeds = config.to.is_nullable();
386 const bool object_can_be_i31 =
387 wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), config.from, module_) ||
389
390 gasm_.InitializeEffectControl(effect_input, control_input);
391
392 auto end_label = gasm_.MakeLabel();
393
394 wasm::HeapType::Representation to_rep = config.to.heap_representation();
395
396 do {
397 // The none-types only perform a null check.
398 if (to_rep == wasm::HeapType::kNone ||
399 to_rep == wasm::HeapType::kNoExtern ||
400 to_rep == wasm::HeapType::kNoFunc || to_rep == wasm::HeapType::kNoExn) {
401 gasm_.TrapUnless(IsNull(object, config.from), TrapId::kTrapIllegalCast);
403 break;
404 }
405 // Null checks performed by any other type cast can be skipped if null
406 // fails, because it's covered by the Smi check
407 // or instance type check we'll do later.
408 if (object_can_be_null && null_succeeds &&
409 !v8_flags.experimental_wasm_skip_null_checks) {
410 gasm_.GotoIf(IsNull(object, config.from), &end_label, BranchHint::kFalse);
411 }
412 if (to_rep == wasm::HeapType::kI31) {
413 // If earlier optimization passes reached the limit of possible graph
414 // transformations, we could DCHECK(object_can_be_i31) here.
415 Node* success =
416 object_can_be_i31 ? gasm_.IsSmi(object) : gasm_.Int32Constant(0);
417 gasm_.TrapUnless(success, TrapId::kTrapIllegalCast);
419 break;
420 }
421 if (to_rep == wasm::HeapType::kEq) {
422 if (object_can_be_i31) {
423 gasm_.GotoIf(gasm_.IsSmi(object), &end_label, BranchHint::kFalse);
424 }
426 TrapId::kTrapIllegalCast);
428 break;
429 }
430 // array, struct, string: i31 fails.
431 if (object_can_be_i31) {
432 gasm_.TrapIf(gasm_.IsSmi(object), TrapId::kTrapIllegalCast);
434 }
435 if (to_rep == wasm::HeapType::kArray) {
436 gasm_.TrapUnless(gasm_.HasInstanceType(object, WASM_ARRAY_TYPE),
437 TrapId::kTrapIllegalCast);
439 break;
440 }
441 if (to_rep == wasm::HeapType::kStruct) {
442 gasm_.TrapUnless(gasm_.HasInstanceType(object, WASM_STRUCT_TYPE),
443 TrapId::kTrapIllegalCast);
445 break;
446 }
447 if (to_rep == wasm::HeapType::kString ||
449 Node* instance_type = gasm_.LoadInstanceType(gasm_.LoadMap(object));
451 gasm_.Uint32LessThan(instance_type,
453 TrapId::kTrapIllegalCast);
455 break;
456 }
457 UNREACHABLE();
458 } while (false);
459
460 if (end_label.IsUsed()) {
461 gasm_.Goto(&end_label);
462 gasm_.Bind(&end_label);
463 }
464
465 ReplaceWithValue(node, object, gasm_.effect(), gasm_.control());
466 node->Kill();
467 return Replace(object);
468}
469
471 DCHECK_EQ(node->opcode(), IrOpcode::kAssertNotNull);
472 Node* effect = NodeProperties::GetEffectInput(node);
473 Node* control = NodeProperties::GetControlInput(node);
474 Node* object = NodeProperties::GetValueInput(node, 0);
475 gasm_.InitializeEffectControl(effect, control);
476 auto op_parameter = OpParameter<AssertNotNullParameters>(node->op());
477 // When able, implement a non-null assertion by loading from the object just
478 // after the map word. This will trap for null and be handled by the trap
479 // handler.
480 if (op_parameter.trap_id == TrapId::kTrapNullDereference) {
481 if (!v8_flags.experimental_wasm_skip_null_checks) {
482 // For supertypes of i31ref, we would need to check for i31ref anyway
483 // before loading from the object, so we might as well just check directly
484 // for null.
485 // For subtypes of externref, we use JS null, so we have to check
486 // explicitly.
488 wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), op_parameter.type,
489 module_) ||
490 !op_parameter.type.use_wasm_null()) {
491 gasm_.TrapIf(IsNull(object, op_parameter.type), op_parameter.trap_id);
493 } else {
494 static_assert(WasmStruct::kHeaderSize > kTaggedSize);
495 static_assert(WasmArray::kHeaderSize > kTaggedSize);
496 static_assert(WasmInternalFunction::kHeaderSize > kTaggedSize);
497 Node* trap_null = gasm_.LoadTrapOnNull(
498 MachineType::Int32(), object,
500 UpdateSourcePosition(trap_null, node);
501 }
502 }
503 } else {
504 gasm_.TrapIf(IsNull(object, op_parameter.type), op_parameter.trap_id);
506 }
507
508 ReplaceWithValue(node, object, gasm_.effect(), gasm_.control());
509 node->Kill();
510 return Replace(object);
511}
512
514 DCHECK_EQ(node->opcode(), IrOpcode::kNull);
515 auto type = OpParameter<wasm::ValueType>(node->op());
516 return Replace(Null(type));
517}
518
520 DCHECK_EQ(node->opcode(), IrOpcode::kIsNull);
521 Node* object = NodeProperties::GetValueInput(node, 0);
522 auto type = OpParameter<wasm::ValueType>(node->op());
523 return Replace(IsNull(object, type));
524}
525
527 DCHECK_EQ(node->opcode(), IrOpcode::kIsNotNull);
528 Node* object = NodeProperties::GetValueInput(node, 0);
529 auto type = OpParameter<wasm::ValueType>(node->op());
530 return Replace(
531 gasm_.Word32Equal(IsNull(object, type), gasm_.Int32Constant(0)));
532}
533
535 DCHECK_EQ(node->opcode(), IrOpcode::kRttCanon);
536 int type_index = OpParameter<int>(node->op());
537 Node* instance_node = node->InputAt(0);
538 Node* maps_list = gasm_.LoadImmutable(
539 MachineType::TaggedPointer(), instance_node,
540 WasmTrustedInstanceData::kManagedObjectMapsOffset - kHeapObjectTag);
542 MachineType::TaggedPointer(), maps_list,
544}
545
547 DCHECK_EQ(node->opcode(), IrOpcode::kTypeGuard);
548 Node* alias = NodeProperties::GetValueInput(node, 0);
549 ReplaceWithValue(node, alias);
550 node->Kill();
551 return Replace(alias);
552}
553
554namespace {
555constexpr int32_t kInt31MaxValue = 0x3fffffff;
556constexpr int32_t kInt31MinValue = -kInt31MaxValue - 1;
557} // namespace
558
560 DCHECK_EQ(node->opcode(), IrOpcode::kWasmAnyConvertExtern);
561 Node* input = NodeProperties::GetValueInput(node, 0);
562 Node* effect = NodeProperties::GetEffectInput(node);
563 Node* control = NodeProperties::GetControlInput(node);
564 gasm_.InitializeEffectControl(effect, control);
565
567 auto null_label = gasm_.MakeLabel();
568 auto smi_label = gasm_.MakeLabel();
569 auto int_to_smi_label = gasm_.MakeLabel();
570 auto heap_number_label = gasm_.MakeLabel();
571
572 gasm_.GotoIf(IsNull(input, wasm::kWasmExternRef), &null_label);
573 gasm_.GotoIf(gasm_.IsSmi(input), &smi_label);
574 Node* is_heap_number = gasm_.HasInstanceType(input, HEAP_NUMBER_TYPE);
575 gasm_.GotoIf(is_heap_number, &heap_number_label);
576 // For anything else, just pass through the value.
577 gasm_.Goto(&end_label, input);
578
579 gasm_.Bind(&null_label);
580 gasm_.Goto(&end_label, Null(wasm::kWasmNullRef));
581
582 // Canonicalize SMI.
583 gasm_.Bind(&smi_label);
584 if constexpr (SmiValuesAre31Bits()) {
585 gasm_.Goto(&end_label, input);
586 } else {
587 auto to_heap_number_label = gasm_.MakeLabel();
588 Node* int_value = gasm_.BuildChangeSmiToInt32(input);
589
590 // Convert to heap number if the int32 does not fit into an i31ref.
592 gasm_.Int32LessThan(gasm_.Int32Constant(kInt31MaxValue), int_value),
593 &to_heap_number_label);
595 gasm_.Int32LessThan(int_value, gasm_.Int32Constant(kInt31MinValue)),
596 &to_heap_number_label);
597 gasm_.Goto(&end_label, input);
598
599 gasm_.Bind(&to_heap_number_label);
600 Node* heap_number = gasm_.CallBuiltin(Builtin::kWasmInt32ToHeapNumber,
601 Operator::kPure, int_value);
602 gasm_.Goto(&end_label, heap_number);
603 }
604
605 // Convert HeapNumber to SMI if possible.
606 gasm_.Bind(&heap_number_label);
607 Node* float_value = gasm_.LoadFromObject(
608 MachineType::Float64(), input,
610 // Check range of float value.
612 gasm_.Float64LessThan(float_value, gasm_.Float64Constant(kInt31MinValue)),
613 &end_label, input);
615 gasm_.Float64LessThan(gasm_.Float64Constant(kInt31MaxValue), float_value),
616 &end_label, input);
617 // Check if value is -0.
618 Node* is_minus_zero = nullptr;
619 if (mcgraph_->machine()->Is64()) {
620 Node* minus_zero = gasm_.Int64Constant(base::bit_cast<int64_t>(-0.0));
621 Node* float_bits = gasm_.BitcastFloat64ToInt64(float_value);
622 is_minus_zero = gasm_.Word64Equal(float_bits, minus_zero);
623 } else {
624 constexpr int32_t kMinusZeroLoBits = static_cast<int32_t>(0);
625 constexpr int32_t kMinusZeroHiBits = static_cast<int32_t>(1) << 31;
627
628 Node* value_lo = gasm_.Float64ExtractLowWord32(float_value);
630 gasm_.Word32Equal(value_lo, gasm_.Int32Constant(kMinusZeroLoBits)),
631 &done, gasm_.Int32Constant(0));
632 Node* value_hi = gasm_.Float64ExtractHighWord32(float_value);
633 gasm_.Goto(&done, gasm_.Word32Equal(value_hi,
635 gasm_.Bind(&done);
636 is_minus_zero = done.PhiAt(0);
637 }
638 gasm_.GotoIf(is_minus_zero, &end_label, input);
639 // Check if value is integral.
640 Node* int_value = gasm_.ChangeFloat64ToInt32(float_value);
642 gasm_.Float64Equal(float_value, gasm_.ChangeInt32ToFloat64(int_value)),
643 &int_to_smi_label);
644 gasm_.Goto(&end_label, input);
645
646 gasm_.Bind(&int_to_smi_label);
647 gasm_.Goto(&end_label, gasm_.BuildChangeInt32ToSmi(int_value));
648
649 gasm_.Bind(&end_label);
650 ReplaceWithValue(node, end_label.PhiAt(0), gasm_.effect(), gasm_.control());
651 node->Kill();
652 return Replace(end_label.PhiAt(0));
653}
654
656 DCHECK_EQ(node->opcode(), IrOpcode::kWasmExternConvertAny);
657 Node* object = node->InputAt(0);
661 gasm_.GotoIfNot(IsNull(object, wasm::kWasmAnyRef), &label, object);
663 gasm_.Bind(&label);
664 ReplaceWithValue(node, label.PhiAt(0), gasm_.effect(), gasm_.control());
665 node->Kill();
666 return Replace(label.PhiAt(0));
667}
668
670 DCHECK_EQ(node->opcode(), IrOpcode::kWasmStructGet);
671 WasmFieldInfo info = OpParameter<WasmFieldInfo>(node->op());
672
673 Node* object = NodeProperties::GetValueInput(node, 0);
674
677
679 info.type->field(info.field_index).machine_representation(),
680 info.is_signed);
681
682 Node* offset = gasm_.FieldOffset(info.type, info.field_index);
683
684 bool explicit_null_check =
685 info.null_check == kWithNullCheck &&
688 bool implicit_null_check =
689 info.null_check == kWithNullCheck && !explicit_null_check;
690
691 if (explicit_null_check) {
693 TrapId::kTrapNullDereference);
695 }
696
697 Node* load = implicit_null_check ? gasm_.LoadTrapOnNull(type, object, offset)
698 : info.type->mutability(info.field_index)
699 ? gasm_.LoadFromObject(type, object, offset)
700 : gasm_.LoadImmutableFromObject(type, object, offset);
701 if (implicit_null_check) {
702 UpdateSourcePosition(load, node);
703 }
704
705 ReplaceWithValue(node, load, gasm_.effect(), gasm_.control());
706 node->Kill();
707 return Replace(load);
708}
709
711 DCHECK_EQ(node->opcode(), IrOpcode::kWasmStructSet);
712 WasmFieldInfo info = OpParameter<WasmFieldInfo>(node->op());
713
716
717 Node* object = NodeProperties::GetValueInput(node, 0);
718 Node* value = NodeProperties::GetValueInput(node, 1);
719
720 bool explicit_null_check =
721 info.null_check == kWithNullCheck &&
724 bool implicit_null_check =
725 info.null_check == kWithNullCheck && !explicit_null_check;
726
727 if (explicit_null_check) {
729 TrapId::kTrapNullDereference);
731 }
732
733 wasm::ValueType field_type = info.type->field(info.field_index);
734 Node* offset = gasm_.FieldOffset(info.type, info.field_index);
735
736 Node* store =
737 implicit_null_check
739 field_type.is_reference() ? kFullWriteBarrier
741 object, offset, value)
742 : info.type->mutability(info.field_index)
743 ? gasm_.StoreToObject(ObjectAccessForGCStores(field_type), object,
744 offset, value)
746 ObjectAccessForGCStores(field_type), object, offset, value);
747 if (implicit_null_check) {
748 UpdateSourcePosition(store, node);
749 }
750
751 ReplaceWithValue(node, store, gasm_.effect(), gasm_.control());
752 node->Kill();
753 return Replace(store);
754}
755
757 DCHECK_EQ(node->opcode(), IrOpcode::kWasmArrayGet);
758 WasmElementInfo info = OpParameter<WasmElementInfo>(node->op());
759
760 Node* object = NodeProperties::GetValueInput(node, 0);
761 Node* index = NodeProperties::GetValueInput(node, 1);
762
765
766 Node* offset = gasm_.WasmArrayElementOffset(index, info.type->element_type());
767
769 info.type->element_type().machine_representation(), info.is_signed);
770
771 Node* value = info.type->mutability()
772 ? gasm_.LoadFromObject(type, object, offset)
773 : gasm_.LoadImmutableFromObject(type, object, offset);
774
775 return Replace(value);
776}
777
779 DCHECK_EQ(node->opcode(), IrOpcode::kWasmArraySet);
781
782 Node* object = NodeProperties::GetValueInput(node, 0);
783 Node* index = NodeProperties::GetValueInput(node, 1);
784 Node* value = NodeProperties::GetValueInput(node, 2);
785
788
789 Node* offset = gasm_.WasmArrayElementOffset(index, type->element_type());
790
791 ObjectAccess access = ObjectAccessForGCStores(type->element_type());
792
793 Node* store =
794 type->mutability()
795 ? gasm_.StoreToObject(access, object, offset, value)
796 : gasm_.InitializeImmutableInObject(access, object, offset, value);
797
798 return Replace(store);
799}
800
802 DCHECK_EQ(node->opcode(), IrOpcode::kWasmArrayLength);
803 Node* object = NodeProperties::GetValueInput(node, 0);
804
807
808 bool null_check = OpParameter<bool>(node->op());
809
811 null_check == kWithNullCheck) {
813 TrapId::kTrapNullDereference);
815 }
816
817 bool use_null_trap =
819 null_check == kWithNullCheck;
820 Node* length =
821 use_null_trap
823 MachineType::Uint32(), object,
825 wasm::ObjectAccess::ToTagged(WasmArray::kLengthOffset)))
827 MachineType::Uint32(), object,
828 wasm::ObjectAccess::ToTagged(WasmArray::kLengthOffset));
829 if (use_null_trap) {
830 UpdateSourcePosition(length, node);
831 }
832
833 ReplaceWithValue(node, length, gasm_.effect(), gasm_.control());
834 node->Kill();
835 return Replace(length);
836}
837
839 DCHECK_EQ(node->opcode(), IrOpcode::kWasmArrayInitializeLength);
840 Node* object = NodeProperties::GetValueInput(node, 0);
841 Node* length = NodeProperties::GetValueInput(node, 1);
842
845
848 wasm::ObjectAccess::ToTagged(WasmArray::kLengthOffset), length);
849
850 return Replace(set_length);
851}
852
854 DCHECK_EQ(node->opcode(), IrOpcode::kStringAsWtf16);
855 Node* effect = NodeProperties::GetEffectInput(node);
856 Node* control = NodeProperties::GetControlInput(node);
857 Node* str = NodeProperties::GetValueInput(node, 0);
858
859 gasm_.InitializeEffectControl(effect, control);
860
862 Node* instance_type = gasm_.LoadInstanceType(gasm_.LoadMap(str));
863 Node* string_representation = gasm_.Word32And(
865 gasm_.GotoIf(gasm_.Word32Equal(string_representation,
867 &done, str);
868 gasm_.Goto(&done, gasm_.CallBuiltin(Builtin::kWasmStringAsWtf16,
870 gasm_.Bind(&done);
871 ReplaceWithValue(node, done.PhiAt(0), gasm_.effect(), gasm_.control());
872 node->Kill();
873 return Replace(done.PhiAt(0));
874}
875
877 DCHECK_EQ(node->opcode(), IrOpcode::kStringPrepareForGetCodeunit);
878 Node* effect = NodeProperties::GetEffectInput(node);
879 Node* control = NodeProperties::GetControlInput(node);
880 Node* original_string = NodeProperties::GetValueInput(node, 0);
881
882 gasm_.InitializeEffectControl(effect, control);
883
884 auto dispatch =
886 MachineRepresentation::kWord32, // Instance type.
889 MachineRepresentation::kWord32, // Instance type.
891 auto direct_string =
893 MachineRepresentation::kWord32, // Instance type.
895
896 // These values will be used to replace the original node's projections.
897 // The first, "string", is either a SeqString or Tagged<Smi>(0) (in case of
898 // external string). Notably this makes it GC-safe: if that string moves, this
899 // pointer will be updated accordingly. The second, "offset", has full
900 // register width so that it can be used to store external pointers: for
901 // external strings, we add up the character backing store's base address and
902 // any slice offset. The third, "character width", is a shift width, i.e. it
903 // is 0 for one-byte strings, 1 for two-byte strings,
904 // kCharWidthBailoutSentinel for uncached external strings (for which
905 // "string"/"offset" are invalid and unusable).
906 auto done =
909 MachineRepresentation::kWord32); // Character width.
910
911 Node* original_type = gasm_.LoadInstanceType(gasm_.LoadMap(original_string));
912 gasm_.Goto(&dispatch, original_string, original_type, gasm_.Int32Constant(0));
913
914 gasm_.Bind(&dispatch);
915 {
916 auto thin_string = gasm_.MakeLabel();
917 auto cons_string = gasm_.MakeLabel();
918
919 Node* string = dispatch.PhiAt(0);
920 Node* instance_type = dispatch.PhiAt(1);
921 Node* offset = dispatch.PhiAt(2);
922 static_assert(kIsIndirectStringTag == 1);
923 static constexpr int kIsDirectStringTag = 0;
924 gasm_.GotoIf(gasm_.Word32Equal(
925 gasm_.Word32And(instance_type, gasm_.Int32Constant(
927 gasm_.Int32Constant(kIsDirectStringTag)),
928 &direct_string, string, instance_type, offset);
929
930 // Handle indirect strings.
931 Node* string_representation = gasm_.Word32And(
933 gasm_.GotoIf(gasm_.Word32Equal(string_representation,
935 &thin_string);
936 gasm_.GotoIf(gasm_.Word32Equal(string_representation,
938 &cons_string);
939
940 // Sliced string.
941 Node* new_offset = gasm_.Int32Add(
944 TaggedOffset(AccessBuilder::ForSlicedStringOffset()))));
948 Node* parent_type = gasm_.LoadInstanceType(gasm_.LoadMap(parent));
949 gasm_.Goto(&next, parent, parent_type, new_offset);
950
951 // Thin string.
952 gasm_.Bind(&thin_string);
955 TaggedOffset(AccessBuilder::ForThinStringActual()));
956 Node* actual_type = gasm_.LoadInstanceType(gasm_.LoadMap(actual));
957 // ThinStrings always reference (internalized) direct strings.
958 gasm_.Goto(&direct_string, actual, actual_type, offset);
959
960 // Flat cons string. (Non-flat cons strings are ruled out by
961 // string.as_wtf16.)
962 gasm_.Bind(&cons_string);
965 TaggedOffset(AccessBuilder::ForConsStringFirst()));
966 Node* first_type = gasm_.LoadInstanceType(gasm_.LoadMap(first));
967 gasm_.Goto(&next, first, first_type, offset);
968
969 gasm_.Bind(&next);
970 gasm_.Goto(&dispatch, next.PhiAt(0), next.PhiAt(1), next.PhiAt(2));
971 }
972
973 gasm_.Bind(&direct_string);
974 {
975 Node* string = direct_string.PhiAt(0);
976 Node* instance_type = direct_string.PhiAt(1);
977 Node* offset = direct_string.PhiAt(2);
978
979 Node* is_onebyte = gasm_.Word32And(
980 instance_type, gasm_.Int32Constant(kStringEncodingMask));
981 // Char width shift is 1 - (is_onebyte).
982 static_assert(kStringEncodingMask == 1 << 3);
983 Node* charwidth_shift =
984 gasm_.Int32Sub(gasm_.Int32Constant(1),
985 gasm_.Word32Shr(is_onebyte, gasm_.Int32Constant(3)));
986
987 auto external = gasm_.MakeLabel();
988 Node* string_representation = gasm_.Word32And(
990 gasm_.GotoIf(gasm_.Word32Equal(string_representation,
992 &external);
993
994 // Sequential string.
997 const int chars_start_offset =
999 Node* final_offset = gasm_.Int32Add(
1001 gasm_.Word32Shl(offset, charwidth_shift));
1002 gasm_.Goto(&done, string, gasm_.BuildChangeInt32ToIntPtr(final_offset),
1003 charwidth_shift);
1004
1005 // External string.
1006 gasm_.Bind(&external);
1007 gasm_.GotoIf(
1008 gasm_.Word32And(instance_type,
1010 &done, string, gasm_.IntPtrConstant(0),
1015 Node* shifted_offset = gasm_.Word32Shl(offset, charwidth_shift);
1016 final_offset = gasm_.IntPtrAdd(
1017 resource, gasm_.BuildChangeInt32ToIntPtr(shifted_offset));
1018 gasm_.Goto(&done, gasm_.SmiConstant(0), final_offset, charwidth_shift);
1019 }
1020
1021 gasm_.Bind(&done);
1022 Node* base = done.PhiAt(0);
1023 Node* final_offset = done.PhiAt(1);
1024 Node* charwidth_shift = done.PhiAt(2);
1025
1026 Node* base_proj = NodeProperties::FindProjection(node, 0);
1027 Node* offset_proj = NodeProperties::FindProjection(node, 1);
1028 Node* charwidth_proj = NodeProperties::FindProjection(node, 2);
1029 if (base_proj) {
1030 ReplaceWithValue(base_proj, base, gasm_.effect(), gasm_.control());
1031 base_proj->Kill();
1032 }
1033 if (offset_proj) {
1034 ReplaceWithValue(offset_proj, final_offset, gasm_.effect(),
1035 gasm_.control());
1036 offset_proj->Kill();
1037 }
1038 if (charwidth_proj) {
1039 ReplaceWithValue(charwidth_proj, charwidth_shift, gasm_.effect(),
1040 gasm_.control());
1041 charwidth_proj->Kill();
1042 }
1043
1044 // Wire up the dangling end of the new effect chain.
1045 ReplaceWithValue(node, node, gasm_.effect(), gasm_.control());
1046
1047 node->Kill();
1048 return Replace(base);
1049}
1050
1059
1060} // namespace compiler
1061} // namespace internal
1062} // namespace v8
static constexpr int root_slot_offset(RootIndex root_index)
static MachineType TypeForRepresentation(const MachineRepresentation &rep, bool isSigned=true)
static constexpr MachineType Float64()
static constexpr MachineType Pointer()
static constexpr MachineType Int32()
static constexpr MachineType Uint32()
static constexpr MachineType TaggedSigned()
static constexpr MachineType TaggedPointer()
static constexpr MachineRepresentation PointerRepresentation()
static ElementAccess ForSeqTwoByteStringCharacter()
static ElementAccess ForSeqOneByteStringCharacter()
static FieldAccess ForExternalStringResourceData()
void ReplaceWithValue(Node *node, Node *value, Node *effect=nullptr, Node *control=nullptr)
static Reduction Replace(Node *node)
detail::GraphAssemblerLabelForReps< Reps... > MakeLabel(Reps... reps)
TNode< Uint32T > Uint32Constant(uint32_t value)
Node * TaggedEqual(Node *left, Node *right)
TNode< UintPtrT > UintPtrConstant(uintptr_t value)
Node * LoadTrapOnNull(MachineType type, Node *object, Node *offset)
detail::GraphAssemblerLabelForReps< Reps... > MakeLoopLabel(Reps... reps)
void InitializeEffectControl(Node *effect, Node *control)
void GotoIf(Node *condition, detail::GraphAssemblerLabelForVars< Vars... > *label, BranchHint hint, Vars...)
Node * StoreTrapOnNull(StoreRepresentation rep, Node *object, Node *offset, Node *value)
void Bind(GraphAssemblerLabel< VarCount > *label)
void GotoIfNot(Node *condition, detail::GraphAssemblerLabelForVars< Vars... > *label, BranchHint hint, Vars...)
void Goto(detail::GraphAssemblerLabelForVars< Vars... > *label, Vars...)
static Node * GetEffectInput(Node *node, int index=0)
static Node * GetValueInput(Node *node, int index)
static Node * FindProjection(Node *node, size_t projection_index)
static Node * GetControlInput(Node *node, int index=0)
Node * InputAt(int index) const
Definition node.h:70
void SetSourcePosition(Node *node, SourcePosition position)
Node * Null(wasm::ValueType type)
WasmGCLowering(Editor *editor, MachineGraph *mcgraph, const wasm::WasmModule *module, bool disable_trap_handler, SourcePositionTable *source_position_table)
void UpdateSourcePosition(Node *new_node, Node *old_node)
Reduction ReduceWasmTypeCheckAbstract(Node *node)
Reduction ReduceStringPrepareForGetCodeunit(Node *node)
Node * IsNull(Node *object, wasm::ValueType type)
Reduction ReduceWasmArrayInitializeLength(Node *node)
Node * LoadImmutableFromObject(MachineType type, Node *base, Node *offset)
Node * FieldOffset(const wasm::StructType *type, uint32_t field_index)
Node * BuildLoadExternalPointerFromObject(Node *object, int offset, ExternalPointerTagRange tag_range, Node *isolate_root)
Node * CallBuiltin(Builtin name, Operator::Properties properties, Args... args)
void TrapIf(Node *condition, TrapId reason)
void TrapUnless(Node *condition, TrapId reason)
Node * LoadFromObject(MachineType type, Node *base, Node *offset)
Node * StoreToObject(ObjectAccess access, Node *base, Node *offset, Node *value)
Node * LoadImmutable(LoadRepresentation rep, Node *base, Node *offset)
Node * InitializeImmutableInObject(ObjectAccess access, Node *base, Node *offset, Node *value)
Node * HasInstanceType(Node *heap_object, InstanceType type)
Node * WasmArrayElementOffset(Node *index, wasm::ValueType element_type)
static constexpr int ElementOffsetInTaggedFixedArray(int index)
static constexpr int ToTagged(int offset)
constexpr bool is_reference() const
Definition value-type.h:600
constexpr MachineRepresentation machine_representation() const
Definition value-type.h:520
constexpr bool is_nullable() const
Definition value-type.h:393
constexpr HeapType::Representation heap_representation() const
Definition value-type.h:963
constexpr int32_t kMinusZeroLoBits
Definition globals.h:185
constexpr int32_t kMinusZeroHiBits
Definition globals.h:186
Label label
int32_t offset
ZoneVector< RpoNumber > & result
bool null_succeeds
const compiler::NullCheckStrategy null_check_strategy_
int position
Definition liveedit.cc:290
V8_INLINE Dest bit_cast(Source const &source)
Definition macros.h:95
ObjectAccess ObjectAccessForGCStores(wasm::ValueType type)
T const & OpParameter(const Operator *op)
Definition operator.h:214
static constexpr int kCharWidthBailoutSentinel
int GetSubtypingDepth(const WasmModule *module, ModuleTypeIndex type_index)
constexpr IndependentHeapType kWasmNullRef
constexpr uint32_t kMinimumSupertypeArraySize
constexpr IndependentHeapType kWasmAnyRef
constexpr IndependentHeapType kWasmExternRef
constexpr IndependentHeapType kWasmI31Ref
constexpr int kMaxStructFieldIndexForImplicitNullCheck
V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype, const WasmModule *sub_module, const WasmModule *super_module)
const uint32_t kStringEncodingMask
constexpr int kTaggedSize
Definition globals.h:542
constexpr int kNoSourcePosition
Definition globals.h:850
const uint32_t kUncachedExternalStringMask
constexpr bool SmiValuesAre31Bits()
const uint32_t kStringRepresentationMask
const int kHeapObjectTag
Definition v8-internal.h:72
@ kExternalStringResourceDataTag
const uint32_t kIsIndirectStringTag
V8_EXPORT_PRIVATE FlagValues v8_flags
return value
Definition map-inl.h:893
const uint32_t kIsIndirectStringMask
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define V8_STATIC_ROOTS_BOOL
Definition v8config.h:1001
MachineGraph * mcgraph_
SourcePositionTable * source_position_table_
WasmGraphAssembler gasm_
const wasm::WasmModule * module_