v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
wasm-load-elimination.cc
Go to the documentation of this file.
1// Copyright 2023 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
15
16namespace v8::internal::compiler {
17
18/**** Helpers ****/
19
20namespace {
21bool TypesUnrelated(Node* lhs, Node* rhs) {
22 wasm::TypeInModule type1 = NodeProperties::GetType(lhs).AsWasm();
23 wasm::TypeInModule type2 = NodeProperties::GetType(rhs).AsWasm();
24 return wasm::TypesUnrelated(type1.type, type2.type, type1.module,
25 type2.module);
26}
27
28bool IsFresh(Node* node) {
29 return node->opcode() == IrOpcode::kAllocate ||
30 node->opcode() == IrOpcode::kAllocateRaw;
31}
32
33bool IsConstant(Node* node) {
34 return node->opcode() == IrOpcode::kParameter ||
35 node->opcode() == IrOpcode::kHeapConstant;
36}
37
38bool MayAlias(Node* lhs, Node* rhs) {
39 if (lhs == rhs) return true;
40 if (TypesUnrelated(lhs, rhs) || (IsFresh(lhs) && IsFresh(rhs)) ||
41 (IsFresh(lhs) && IsConstant(rhs)) || (IsConstant(lhs) && IsFresh(rhs))) {
42 return false;
43 }
44 return true;
45}
46
47Node* ResolveAliases(Node* node) {
48 while (node->opcode() == IrOpcode::kWasmTypeCast ||
49 node->opcode() == IrOpcode::kWasmTypeCastAbstract ||
50 node->opcode() == IrOpcode::kAssertNotNull ||
51 node->opcode() == IrOpcode::kTypeGuard) {
52 node = NodeProperties::GetValueInput(node, 0);
53 }
54 return node;
55}
56
57// We model array length and string canonicalization as fields at negative
58// indices.
59constexpr int kArrayLengthFieldIndex = -1;
60constexpr int kStringPrepareForGetCodeunitIndex = -2;
61constexpr int kStringAsWtf16Index = -3;
62constexpr int kAnyConvertExternIndex = -4;
63} // namespace
64
66 AbstractState const* state) {
67 AbstractState const* original = node_states_.Get(node);
68 // Only signal that the {node} has Changed, if the information about {state}
69 // has changed wrt. the {original}.
70 if (state != original) {
71 if (original == nullptr || !state->Equals(original)) {
72 node_states_.Set(node, state);
73 return Changed(node);
74 }
75 }
76 return NoChange();
77}
78
80 Node* value, Node* effect, Node* control, wasm::ValueType field_type,
81 bool is_signed) {
82 if (field_type == wasm::kWasmI8 || field_type == wasm::kWasmI16) {
83 Node* ret = nullptr;
84 if (is_signed) {
85 int shift = 32 - 8 * field_type.value_kind_size();
86 ret = graph()->NewNode(machine()->Word32Sar(),
87 graph()->NewNode(machine()->Word32Shl(), value,
88 jsgraph()->Int32Constant(shift)),
89 jsgraph()->Int32Constant(shift));
90 } else {
91 int mask = (1 << 8 * field_type.value_kind_size()) - 1;
92 ret = graph()->NewNode(machine()->Word32And(), value,
93 jsgraph()->Int32Constant(mask));
94 }
95
97 return {ret, effect};
98 }
99
100 // The value might be untyped in case of wasm inlined into JS if the value
101 // comes from a JS node.
102 if (!NodeProperties::IsTyped(value)) {
103 return {value, effect};
104 }
105
106 Type value_type = NodeProperties::GetType(value);
107 if (!value_type.IsWasm()) {
108 return {value, effect};
109 }
110
111 wasm::TypeInModule node_type = value_type.AsWasm();
112
113 // TODO(12166): Adapt this if cross-module inlining is allowed.
114 if (wasm::TypesUnrelated(node_type.type, field_type, node_type.module,
115 node_type.module)) {
116 // Unrelated types can occur as a result of unreachable code.
117 // Example: Storing a value x of type A in a struct, then casting the struct
118 // to a different struct type to then load type B from the same offset
119 // results in trying to replace the load with value x.
120 return {dead(), dead()};
121 }
122 if (!wasm::IsSubtypeOf(node_type.type, field_type, node_type.module)) {
123 Type type = Type::Wasm({field_type, node_type.module}, graph()->zone());
124 Node* ret =
125 graph()->NewNode(common()->TypeGuard(type), value, effect, control);
126 NodeProperties::SetType(ret, type);
127 return {ret, ret};
128 }
129
130 return {value, effect};
131}
132
133/***** Reductions *****/
134
136 if (v8_flags.trace_turbo_load_elimination) {
137 // TODO(manoskouk): Add some tracing.
138 }
139 switch (node->opcode()) {
140 case IrOpcode::kWasmStructGet:
141 return ReduceWasmStructGet(node);
142 case IrOpcode::kWasmStructSet:
143 return ReduceWasmStructSet(node);
144 case IrOpcode::kWasmArrayLength:
145 return ReduceWasmArrayLength(node);
146 case IrOpcode::kWasmArrayInitializeLength:
148 case IrOpcode::kStringPrepareForGetCodeunit:
150 case IrOpcode::kStringAsWtf16:
151 return ReduceStringAsWtf16(node);
152 case IrOpcode::kWasmAnyConvertExtern:
153 return ReduceAnyConvertExtern(node);
154 case IrOpcode::kEffectPhi:
155 return ReduceEffectPhi(node);
156 case IrOpcode::kDead:
157 return NoChange();
158 case IrOpcode::kStart:
159 return ReduceStart(node);
160 default:
161 return ReduceOtherNode(node);
162 }
163}
164
166 DCHECK_EQ(node->opcode(), IrOpcode::kWasmStructGet);
167 Node* input_struct = NodeProperties::GetValueInput(node, 0);
168 Node* object = ResolveAliases(input_struct);
169 Node* effect = NodeProperties::GetEffectInput(node);
170 Node* control = NodeProperties::GetControlInput(node);
171
172 if (object->opcode() == IrOpcode::kDead) return NoChange();
173 AbstractState const* state = node_states_.Get(effect);
174 if (state == nullptr) return NoChange();
175
176 const WasmFieldInfo& field_info = OpParameter<WasmFieldInfo>(node->op());
177 bool is_mutable = field_info.type->mutability(field_info.field_index);
178
179 if (!NodeProperties::IsTyped(input_struct) ||
180 !NodeProperties::GetType(input_struct).IsWasm()) {
181 // The input should always be typed. https://crbug.com/1507106 reported
182 // that we can end up with Type None here instead of a wasm type.
183 // In the worst case this only means that we miss a potential optimization,
184 // still the assumption is that all inputs into StructGet should be typed.
185 return NoChange();
186 }
187 // Skip reduction if the input type is nullref. in this case, the struct get
188 // will always trap.
189 wasm::ValueType struct_type =
190 NodeProperties::GetType(input_struct).AsWasm().type;
191 if (struct_type == wasm::kWasmNullRef) {
192 return NoChange();
193 }
194 // The node is in unreachable code if its input is uninhabitable (bottom or
195 // ref none type). It can also be treated as unreachable if the field index is
196 // in the wrong half state. This can happen if an object gets cast to two
197 // unrelated types subsequently (as the state only tracks the field index)
198 // independent of the underlying type.
199 if (struct_type.is_uninhabited() ||
200 !(is_mutable ? state->immutable_state : state->mutable_state)
201 .LookupField(field_info.field_index, object)
202 .IsEmpty()) {
203 ReplaceWithValue(node, dead(), dead(), dead());
204 MergeControlToEnd(graph(), common(),
205 graph()->NewNode(common()->Throw(), effect, control));
206 node->Kill();
207 return Replace(dead());
208 }
209 // If the input type is not (ref null? none) or bottom and we don't have type
210 // inconsistencies, then the result type must be valid.
211 DCHECK(!NodeProperties::GetType(node).AsWasm().type.is_bottom());
212
213 HalfState const* half_state =
214 is_mutable ? &state->mutable_state : &state->immutable_state;
215
216 FieldOrElementValue lookup_result =
217 half_state->LookupField(field_info.field_index, object);
218
219 if (!lookup_result.IsEmpty() && !lookup_result.value->IsDead()) {
220 std::tuple<Node*, Node*> replacement = TruncateAndExtendOrType(
221 lookup_result.value, effect, control,
222 field_info.type->field(field_info.field_index), field_info.is_signed);
223 if (std::get<0>(replacement) == dead()) {
224 // If the value is dead (unreachable), this whole code path is unreachable
225 // and we can mark this control flow path as dead.
226 ReplaceWithValue(node, dead(), dead(), dead());
227 MergeControlToEnd(graph(), common(),
228 graph()->NewNode(common()->Throw(), effect, control));
229 node->Kill();
230 return Replace(dead());
231 }
232 ReplaceWithValue(node, std::get<0>(replacement), std::get<1>(replacement),
233 control);
234 node->Kill();
235 return Replace(std::get<0>(replacement));
236 }
237
238 half_state = half_state->AddField(field_info.field_index, object, node);
239
240 AbstractState const* new_state =
241 is_mutable
242 ? zone()->New<AbstractState>(*half_state, state->immutable_state)
243 : zone()->New<AbstractState>(state->mutable_state, *half_state);
244
245 return UpdateState(node, new_state);
246}
247
249 DCHECK_EQ(node->opcode(), IrOpcode::kWasmStructSet);
250 Node* input_struct = NodeProperties::GetValueInput(node, 0);
251 Node* object = ResolveAliases(input_struct);
252 Node* value = NodeProperties::GetValueInput(node, 1);
253 Node* effect = NodeProperties::GetEffectInput(node);
254 Node* control = NodeProperties::GetControlInput(node);
255
256 if (object->opcode() == IrOpcode::kDead) return NoChange();
257 AbstractState const* state = node_states_.Get(effect);
258 if (state == nullptr) return NoChange();
259
260 if (!NodeProperties::IsTyped(input_struct) ||
261 !NodeProperties::GetType(input_struct).IsWasm()) {
262 // Also see the same pattern in ReduceWasmStructGet. Note that this is
263 // reached for cases where the StructSet has a value input that is
264 // DeadValue(). Above we check for `object->opcode() == IrOpcode::kDead.
265 // As an alternative that check could be extended to also check for
266 // ... || object->opcode() == IrOpcode::kDeadValue.
267 // It seems that the DeadValue may be caused by
268 // DeadCodeElimination::ReducePureNode. If that finds any input that is a
269 // Dead() node, it will replace that input with a DeadValue().
270 return NoChange();
271 }
272
273 // Skip reduction if the input type is nullref. in this case, the struct get
274 // will always trap.
275 wasm::ValueType struct_type =
276 NodeProperties::GetType(input_struct).AsWasm().type;
277 if (struct_type == wasm::kWasmNullRef) {
278 return NoChange();
279 }
280
281 const WasmFieldInfo& field_info = OpParameter<WasmFieldInfo>(node->op());
282 bool is_mutable = field_info.type->mutability(field_info.field_index);
283
284 // The struct.set is unreachable if its input struct is an uninhabitable type.
285 // It can also be treated as unreachable if the field index is in the wrong
286 // half state. This can happen if an object gets cast to two unrelated types
287 // subsequently (as the state only tracks the field index) independent of the
288 // underlying type.
289 if (struct_type.is_uninhabited() ||
290 !(is_mutable ? state->immutable_state : state->mutable_state)
291 .LookupField(field_info.field_index, object)
292 .IsEmpty()) {
293 ReplaceWithValue(node, dead(), dead(), dead());
294 MergeControlToEnd(graph(), common(),
295 graph()->NewNode(common()->Throw(), effect, control));
296 node->Kill();
297 return Replace(dead());
298 }
299
300 if (is_mutable) {
301 HalfState const* mutable_state =
302 state->mutable_state.KillField(field_info.field_index, object);
303 mutable_state =
304 mutable_state->AddField(field_info.field_index, object, value);
305 AbstractState const* new_state =
306 zone()->New<AbstractState>(*mutable_state, state->immutable_state);
307 return UpdateState(node, new_state);
308 } else {
309 // We should not initialize the same immutable field twice.
310 DCHECK(state->immutable_state.LookupField(field_info.field_index, object)
311 .IsEmpty());
312 HalfState const* immutable_state =
313 state->immutable_state.AddField(field_info.field_index, object, value);
314 AbstractState const* new_state =
315 zone()->New<AbstractState>(state->mutable_state, *immutable_state);
316 return UpdateState(node, new_state);
317 }
318}
319
321 int index) {
322 // The index must be negative as it is not a real load, to not confuse it with
323 // actual loads.
324 DCHECK_LT(index, 0);
325 Node* object = ResolveAliases(NodeProperties::GetValueInput(node, 0));
326 Node* effect = NodeProperties::GetEffectInput(node);
327 Node* control = NodeProperties::GetControlInput(node);
328
329 if (object->opcode() == IrOpcode::kDead) return NoChange();
330 AbstractState const* state = node_states_.Get(effect);
331 if (state == nullptr) return NoChange();
332
333 HalfState const* immutable_state = &state->immutable_state;
334
335 FieldOrElementValue lookup_result =
336 immutable_state->LookupField(index, object);
337
338 if (!lookup_result.IsEmpty() && !lookup_result.value->IsDead()) {
339 ReplaceWithValue(node, lookup_result.value, effect, control);
340 node->Kill();
341 return Replace(lookup_result.value);
342 }
343
344 immutable_state = immutable_state->AddField(index, object, node);
345
346 AbstractState const* new_state =
347 zone()->New<AbstractState>(state->mutable_state, *immutable_state);
348
349 return UpdateState(node, new_state);
350}
351
353 DCHECK_EQ(node->opcode(), IrOpcode::kWasmArrayLength);
354 return ReduceLoadLikeFromImmutable(node, kArrayLengthFieldIndex);
355}
356
358 DCHECK_EQ(node->opcode(), IrOpcode::kWasmArrayInitializeLength);
359 Node* object = ResolveAliases(NodeProperties::GetValueInput(node, 0));
360 Node* value = NodeProperties::GetValueInput(node, 1);
361 Node* effect = NodeProperties::GetEffectInput(node);
362
363 if (object->opcode() == IrOpcode::kDead) return NoChange();
364 AbstractState const* state = node_states_.Get(effect);
365 if (state == nullptr) return NoChange();
366
367 // We should not initialize the length twice.
368 DCHECK(state->immutable_state.LookupField(kArrayLengthFieldIndex, object)
369 .IsEmpty());
370 HalfState const* immutable_state =
371 state->immutable_state.AddField(kArrayLengthFieldIndex, object, value);
372 AbstractState const* new_state =
373 zone()->New<AbstractState>(state->mutable_state, *immutable_state);
374 return UpdateState(node, new_state);
375}
376
378 DCHECK_EQ(node->opcode(), IrOpcode::kStringPrepareForGetCodeunit);
379 Node* object = ResolveAliases(NodeProperties::GetValueInput(node, 0));
380 Node* effect = NodeProperties::GetEffectInput(node);
381 Node* control = NodeProperties::GetControlInput(node);
382
383 if (object->opcode() == IrOpcode::kDead) return NoChange();
384 AbstractState const* state = node_states_.Get(effect);
385 if (state == nullptr) return NoChange();
386
387 HalfState const* mutable_state = &state->mutable_state;
388
389 FieldOrElementValue lookup_result =
390 mutable_state->LookupField(kStringPrepareForGetCodeunitIndex, object);
391
392 if (!lookup_result.IsEmpty() && !lookup_result.value->IsDead()) {
393 for (size_t i : {0, 1, 2}) {
394 Node* proj_to_replace = NodeProperties::FindProjection(node, i);
395 ReplaceWithValue(proj_to_replace,
396 NodeProperties::FindProjection(lookup_result.value, i));
397 proj_to_replace->Kill();
398 }
399 ReplaceWithValue(node, lookup_result.value, effect, control);
400 node->Kill();
401 return Replace(lookup_result.value);
402 }
403
404 mutable_state =
405 mutable_state->AddField(kStringPrepareForGetCodeunitIndex, object, node);
406
407 AbstractState const* new_state =
408 zone()->New<AbstractState>(*mutable_state, state->immutable_state);
409
410 return UpdateState(node, new_state);
411}
412
414 DCHECK_EQ(node->opcode(), IrOpcode::kStringAsWtf16);
415 return ReduceLoadLikeFromImmutable(node, kStringAsWtf16Index);
416}
417
419 DCHECK_EQ(node->opcode(), IrOpcode::kWasmAnyConvertExtern);
420 // An externref is not immutable meaning it could change. However, the values
421 // relevant for any.convert_extern (null, HeapNumber, Smi) are immutable, so
422 // we can treat the externref as immutable.
423 return ReduceLoadLikeFromImmutable(node, kAnyConvertExternIndex);
424}
425
427 if (node->op()->EffectOutputCount() == 0) return NoChange();
428 DCHECK_EQ(node->op()->EffectInputCount(), 1);
429 Node* const effect = NodeProperties::GetEffectInput(node);
430 AbstractState const* state = node_states_.Get(effect);
431 // If we do not know anything about the predecessor, do not propagate just
432 // yet because we will have to recompute anyway once we compute the
433 // predecessor.
434 if (state == nullptr) return NoChange();
435 // If this {node} has some uncontrolled side effects (i.e. it is a call
436 // without {kNoWrite}), set its state to the immutable half-state of its
437 // input state, otherwise to its input state.
438 // Any cached StringPrepareForGetCodeUnit nodes must be killed at any point
439 // that can cause internalization of strings (i.e. that can turn sequential
440 // strings into thin strings). Currently, that can only happen in JS, so
441 // from Wasm's point of view only in calls.
442 return UpdateState(node, node->opcode() == IrOpcode::kCall &&
443 !node->op()->HasProperty(Operator::kNoWrite)
444 ? zone()->New<AbstractState>(
445 HalfState(zone()), state->immutable_state)
446 : state);
447}
448
452
454 DCHECK_EQ(node->opcode(), IrOpcode::kEffectPhi);
455 Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
456 Node* const control = NodeProperties::GetControlInput(node);
457 AbstractState const* state0 = node_states_.Get(effect0);
458 if (state0 == nullptr) return NoChange();
459 if (control->opcode() == IrOpcode::kLoop) {
460 // Here we rely on having only reducible loops:
461 // The loop entry edge always dominates the header, so we can just take
462 // the state from the first input, and compute the loop state based on it.
463 AbstractState const* state = ComputeLoopState(node, state0);
464 return UpdateState(node, state);
465 }
466 DCHECK_EQ(IrOpcode::kMerge, control->opcode());
467
468 // Shortcut for the case when we do not know anything about some input.
469 int const input_count = node->op()->EffectInputCount();
470 for (int i = 1; i < input_count; ++i) {
471 Node* const effect = NodeProperties::GetEffectInput(node, i);
472 if (node_states_.Get(effect) == nullptr) return NoChange();
473 }
474
475 // Make a copy of the first input's state and intersect it with the state
476 // from other inputs.
477 // TODO(manoskouk): Consider computing phis for at least a subset of the
478 // state.
479 AbstractState* state = zone()->New<AbstractState>(*state0);
480 for (int i = 1; i < input_count; ++i) {
481 Node* const input = NodeProperties::GetEffectInput(node, i);
482 state->IntersectWith(node_states_.Get(input));
483 }
484 return UpdateState(node, state);
485}
486
487/***** AbstractState implementation *****/
488
491 Node* object) const {
492 return fields_.Get(field_index).Get(object);
493}
494
496 int field_index, Node* object, Node* value) const {
497 HalfState* new_state = zone_->New<HalfState>(*this);
498 Update(new_state->fields_, field_index, object, FieldOrElementValue(value));
499 return new_state;
500}
501
503 int field_index, Node* object) const {
504 const InnerMap& same_index_map = fields_.Get(field_index);
505 InnerMap new_map(same_index_map);
506 for (std::pair<Node*, FieldOrElementValue> pair : same_index_map) {
507 if (MayAlias(pair.first, object)) {
508 new_map.Set(pair.first, FieldOrElementValue());
509 }
510 }
511 HalfState* result = zone_->New<HalfState>(*this);
512 result->fields_.Set(field_index, new_map);
513 return result;
514}
515
517 Node* node, AbstractState const* state) const {
518 DCHECK_EQ(node->opcode(), IrOpcode::kEffectPhi);
519 if (state->mutable_state.IsEmpty()) return state;
520 std::queue<Node*> queue;
521 AccountingAllocator allocator;
522 Zone temp_set_zone(&allocator, ZONE_NAME);
523 ZoneUnorderedSet<Node*> visited(&temp_set_zone);
524 visited.insert(node);
525 for (int i = 1; i < node->InputCount() - 1; ++i) {
526 queue.push(node->InputAt(i));
527 }
528 while (!queue.empty()) {
529 Node* const current = queue.front();
530 queue.pop();
531 if (visited.insert(current).second) {
532 if (current->opcode() == IrOpcode::kWasmStructSet) {
533 Node* object = NodeProperties::GetValueInput(current, 0);
534 if (object->opcode() == IrOpcode::kDead ||
535 object->opcode() == IrOpcode::kDeadValue) {
536 // We are in dead code. Bail out with no mutable state.
537 return zone()->New<AbstractState>(HalfState(zone()),
538 state->immutable_state);
539 }
540 WasmFieldInfo field_info = OpParameter<WasmFieldInfo>(current->op());
541 bool is_mutable = field_info.type->mutability(field_info.field_index);
542 if (is_mutable) {
543 const HalfState* new_mutable_state =
544 state->mutable_state.KillField(field_info.field_index, object);
545 state = zone()->New<AbstractState>(*new_mutable_state,
546 state->immutable_state);
547 } else {
548 // TODO(manoskouk): DCHECK
549 }
550 } else if (current->opcode() == IrOpcode::kCall &&
551 !current->op()->HasProperty(Operator::kNoWrite)) {
552 return zone()->New<AbstractState>(HalfState(zone()),
553 state->immutable_state);
554 }
555 for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
556 queue.push(NodeProperties::GetEffectInput(current, i));
557 }
558 }
559 }
560 return state;
561}
562
565 for (const std::pair<int, InnerMap> to_map : fields_) {
566 InnerMap to_map_copy(to_map.second);
567 int key = to_map.first;
568 const InnerMap& current_map = that->fields_.Get(key);
569 for (std::pair<Node*, FieldOrElementValue> value : to_map.second) {
570 if (current_map.Get(value.first) != value.second) {
571 to_map_copy.Set(value.first, empty);
572 }
573 }
574 fields_.Set(key, to_map_copy);
575 }
576}
577
578/***** Constructor/ trivial accessors *****/
580 Zone* zone)
581 : AdvancedReducer(editor),
583 node_states_(jsgraph->graph()->NodeCount(), zone),
585 dead_(jsgraph->Dead()),
586 zone_(zone) {}
587
591
595
597
599
600} // namespace v8::internal::compiler
T * New(Args &&... args)
Definition zone.h:114
Isolate * isolate() const
Definition js-graph.h:106
CommonOperatorBuilder * common() const
MachineOperatorBuilder * machine() const
static Type GetType(const Node *node)
static bool IsTyped(const Node *node)
static Node * GetEffectInput(Node *node, int index=0)
static Node * GetValueInput(Node *node, int index)
static void SetType(Node *node, Type type)
static Node * FindProjection(Node *node, size_t projection_index)
static Node * GetControlInput(Node *node, int index=0)
constexpr IrOpcode::Value opcode() const
Definition node.h:52
const Value & Get(const Key &key) const
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
HalfState const * AddField(int field_index, Node *object, Node *value) const
HalfState const * KillField(int field_index, Node *object) const
FieldOrElementValue LookupField(int field_index, Node *object) const
NodeAuxData< AbstractState const * > node_states_
WasmLoadElimination(Editor *editor, JSGraph *jsgraph, Zone *zone)
AbstractState const * ComputeLoopState(Node *node, AbstractState const *state) const
Reduction ReduceLoadLikeFromImmutable(Node *node, int index)
Reduction UpdateState(Node *node, AbstractState const *state)
std::tuple< Node *, Node * > TruncateAndExtendOrType(Node *value, Node *effect, Node *control, wasm::ValueType field_type, bool is_signed)
constexpr int value_kind_size() const
Definition value-type.h:485
constexpr bool is_uninhabited() const
Definition value-type.h:455
Node * node
ZoneVector< RpoNumber > & result
LiftoffAssembler::CacheState state
uint32_t const mask
T const & OpParameter(const Operator *op)
Definition operator.h:214
constexpr IndependentHeapType kWasmNullRef
constexpr IndependentValueType kWasmI8
V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype, const WasmModule *sub_module, const WasmModule *super_module)
V8_INLINE bool TypesUnrelated(ValueType type1, ValueType type2, const WasmModule *module1, const WasmModule *module2)
constexpr IndependentValueType kWasmI16
V8_EXPORT_PRIVATE FlagValues v8_flags
return value
Definition map-inl.h:893
bool is_signed(Condition cond)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define ZONE_NAME
Definition zone.h:22