v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
wasm-interpreter-inl.h
Go to the documentation of this file.
1// Copyright 2024 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
5#ifndef V8_WASM_INTERPRETER_WASM_INTERPRETER_INL_H_
6#define V8_WASM_INTERPRETER_WASM_INTERPRETER_INL_H_
7
8#if !V8_ENABLE_WEBASSEMBLY
9#error This header should only be included if WebAssembly is enabled.
10#endif // !V8_ENABLE_WEBASSEMBLY
11
16
17namespace v8 {
18namespace internal {
19namespace wasm {
20
22 uint32_t function_index) {
23 DCHECK_LT(function_index, interpreter_code_.size());
24 InterpreterCode* code = &interpreter_code_[function_index];
25 if (V8_UNLIKELY(!code->bytecode && code->start)) {
26 Preprocess(function_index);
27 }
28 return code;
29}
30
32 uint32_t func_index) {
33 DCHECK_LT(func_index, interpreter_code_.size());
34
35 // This precompiles the target function.
36 InterpreterCode* code = GetCode(func_index);
37 return code->bytecode.get();
38}
39
41 const uint8_t* code_start,
42 const uint8_t* code_end) {
43 DCHECK_EQ(interpreter_code_.size(), function->func_index);
44 interpreter_code_.emplace_back(function, BodyLocalDecls(),
45 const_cast<uint8_t*>(code_start),
46 const_cast<uint8_t*>(code_end));
47}
48
50 return wasm_runtime_->GetIsolate();
51}
52
55 Address frame_pointer,
56 uint8_t* interpreter_fp,
57 const FrameState& frame_state) {
58 Run();
59 activations_.emplace_back(std::make_unique<Activation>(
60 this, wasm_runtime, frame_pointer, interpreter_fp, frame_state));
61 return activations_.back().get();
62}
63
65 DCHECK(!activations_.empty());
66 activations_.pop_back();
67 if (activations_.empty()) {
68 if (state_ != State::TRAPPED && state_ != State::STOPPED) {
69 Finish();
70 }
71 }
72}
73
76 for (int i = static_cast<int>(activations_.size()) - 1; i >= 0; i--) {
77 if (activations_[i]->GetWasmRuntime() == wasm_runtime) {
78 return &activations_[i]->GetCurrentFrame();
79 }
80 }
81 return nullptr;
82}
83
85 WasmInterpreterThread* thread, uint32_t function_index,
86 Address frame_pointer, uint8_t* interpreter_fp, uint32_t ref_stack_offset,
87 const std::vector<WasmValue>& args) {
88 codemap_.GetCode(function_index);
89 wasm_runtime_->BeginExecution(thread, function_index, frame_pointer,
90 interpreter_fp, ref_stack_offset, &args);
91}
92
94 uint32_t function_index,
95 Address frame_pointer,
96 uint8_t* interpreter_fp) {
97 codemap_.GetCode(function_index);
98 wasm_runtime_->BeginExecution(thread, function_index, frame_pointer,
99 interpreter_fp, thread->NextRefStackOffset());
100}
101
103 return wasm_runtime_->GetReturnValue(index);
104}
105
106inline std::vector<WasmInterpreterStackEntry>
108 return wasm_runtime_->GetInterpretedStack(frame_pointer);
109}
110
112 int index) const {
113 return wasm_runtime_->GetFunctionIndex(frame_pointer, index);
114}
115
116inline void WasmInterpreter::SetTrapFunctionIndex(int32_t func_index) {
117 wasm_runtime_->SetTrapFunctionIndex(func_index);
118}
119
120inline ValueType WasmBytecode::return_type(size_t index) const {
121 DCHECK_LT(index, return_count());
122 return signature_->GetReturn(index);
123}
124
125inline ValueType WasmBytecode::arg_type(size_t index) const {
126 DCHECK_LT(index, args_count());
127 return signature_->GetParam(index);
128}
129
130inline ValueType WasmBytecode::local_type(size_t index) const {
131 DCHECK_LT(index, locals_count());
132 DCHECK_LT(index, interpreter_code_->locals.num_locals);
133 return interpreter_code_->locals.local_types[index];
134}
135
137 switch (kind) {
138 case kI32:
139 return sizeof(int32_t) / kSlotSize;
140 case kI64:
141 return sizeof(int64_t) / kSlotSize;
142 case kF32:
143 return sizeof(float) / kSlotSize;
144 case kF64:
145 return sizeof(double) / kSlotSize;
146 case kS128:
147 return sizeof(Simd128) / kSlotSize;
148 case kRef:
149 case kRefNull:
150 return sizeof(WasmRef) / kSlotSize;
151 default:
152 UNREACHABLE();
153 }
154}
155
157 DCHECK_NOT_NULL(handle_scope_);
158 {
159 HandleScope old(std::move(*handle_scope_));
160 // The HandleScope destructor cleans up the old HandleScope.
161 }
162 // Now that the old HandleScope has been destroyed, make a new one.
163 *handle_scope_ = HandleScope(isolate);
164}
165
167 uint32_t args_slots_size = 0;
168 size_t args_count = sig->parameter_count();
169 for (size_t i = 0; i < args_count; i++) {
170 args_slots_size += GetValueSizeInSlots(sig->GetParam(i).kind());
171 }
172 return args_slots_size;
173}
174
176 uint32_t rets_slots_size = 0;
177 size_t return_count = static_cast<uint32_t>(sig->return_count());
178 for (size_t i = 0; i < return_count; i++) {
179 rets_slots_size += GetValueSizeInSlots(sig->GetReturn(i).kind());
180 }
181 return rets_slots_size;
182}
183
185 uint32_t refs_args_count = 0;
186 size_t args_count = static_cast<uint32_t>(sig->parameter_count());
187 for (size_t i = 0; i < args_count; i++) {
188 ValueKind kind = sig->GetParam(i).kind();
189 if (wasm::is_reference(kind)) refs_args_count++;
190 }
191 return refs_args_count;
192}
193
195 uint32_t refs_rets_count = 0;
196 size_t return_count = static_cast<uint32_t>(sig->return_count());
197 for (size_t i = 0; i < return_count; i++) {
198 ValueKind kind = sig->GetReturn(i).kind();
199 if (wasm::is_reference(kind)) refs_rets_count++;
200 }
201 return refs_rets_count;
202}
203
205 size_t args_count = static_cast<uint32_t>(sig->parameter_count());
206 for (size_t i = 0; i < args_count; i++) {
207 if (sig->GetParam(i).kind() == kS128) return true;
208 }
209
210 size_t return_count = static_cast<uint32_t>(sig->return_count());
211 for (size_t i = 0; i < return_count; i++) {
212 if (sig->GetReturn(i).kind() == kS128) return true;
213 }
214
215 return false;
216}
217
219 size_t args_count = static_cast<uint32_t>(sig->parameter_count());
220 for (size_t i = 0; i < args_count; i++) {
221 ValueKind kind = sig->GetParam(i).kind();
222 if (wasm::is_reference(kind) || kind == kS128) return true;
223 }
224 return false;
225}
226
228 const FunctionSig* sig) {
229 static_assert(kSystemPointerSize == 8);
230
231 uint32_t args_size = 0;
232 size_t args_count = static_cast<uint32_t>(sig->parameter_count());
233 for (size_t i = 0; i < args_count; i++) {
234 switch (sig->GetParam(i).kind()) {
235 case kI32:
236 case kF32:
237 args_size += sizeof(int32_t);
238 break;
239 case kI64:
240 case kF64:
241 args_size += sizeof(int64_t);
242 break;
243 case kS128:
244 args_size += sizeof(Simd128);
245 break;
246 case kRef:
247 case kRefNull:
248 // Make sure Ref slots are 64-bit aligned.
249 args_size += (args_size & 0x04);
250 args_size += sizeof(WasmRef);
251 break;
252 default:
253 UNREACHABLE();
254 }
255 }
256
257 uint32_t rets_size = 0;
258 size_t rets_count = static_cast<uint32_t>(sig->return_count());
259 for (size_t i = 0; i < rets_count; i++) {
260 switch (sig->GetReturn(i).kind()) {
261 case kI32:
262 case kF32:
263 rets_size += sizeof(int32_t);
264 break;
265 case kI64:
266 case kF64:
267 rets_size += sizeof(int64_t);
268 break;
269 case kS128:
270 rets_size += sizeof(Simd128);
271 break;
272 case kRef:
273 case kRefNull:
274 // Make sure Ref slots are 64-bit aligned.
275 rets_size += (rets_size & 0x04);
276 rets_size += sizeof(WasmRef);
277 break;
278 default:
279 UNREACHABLE();
280 }
281 }
282
283 uint32_t size = std::max(args_size, rets_size);
284 // Make sure final size is 64-bit aligned.
285 size += (size & 0x04);
286 return size;
287}
288
289inline uint32_t WasmBytecode::RefLocalsCount(const InterpreterCode* wasm_code) {
290 uint32_t refs_locals_count = 0;
291 size_t locals_count = wasm_code->locals.num_locals;
292 for (size_t i = 0; i < locals_count; i++) {
293 ValueKind kind = wasm_code->locals.local_types[i].kind();
295 refs_locals_count++;
296 }
297 }
298 return refs_locals_count;
299}
300
302 const InterpreterCode* wasm_code) {
303 uint32_t locals_slots_size = 0;
304 size_t locals_count = wasm_code->locals.num_locals;
305 for (size_t i = 0; i < locals_count; i++) {
306 locals_slots_size +=
308 }
309 return locals_slots_size;
310}
311
313 size_t stack_space) const {
314 // Check for overflow
315 if (total_frame_size_in_bytes_ > stack_space) {
316 return false;
317 }
318
319 uint32_t args_slots_size_in_bytes = args_slots_size() * kSlotSize;
320 uint32_t rets_slots_size_in_bytes = rets_slots_size() * kSlotSize;
321 uint32_t const_slots_size_in_bytes = this->const_slots_size_in_bytes();
322
323 uint8_t* start_const_area =
324 sp + args_slots_size_in_bytes + rets_slots_size_in_bytes;
325
326 // Initialize const slots
327 if (const_slots_size_in_bytes) {
328 memcpy(start_const_area, const_slots_values_.data(),
329 const_slots_size_in_bytes);
330 }
331
332 // Initialize local slots
333 memset(start_const_area + const_slots_size_in_bytes, 0,
334 locals_slots_size() * kSlotSize);
335
336 return true;
337}
338
340 const WasmInstruction& instr) {
341 if (!instr.SupportsToRegister()) return false;
342
343 // Even if the instruction is marked as supporting ToRegister, reference
344 // values should not be stored in the register.
345 switch (instr.opcode) {
346 case kExprGlobalGet: {
347 ValueKind kind = GetGlobalType(instr.optional.index);
348 return !wasm::is_reference(kind) && kind != kS128;
349 }
350 case kExprSelect:
351 case kExprSelectWithType: {
352 DCHECK_GE(stack_size(), 2);
353 ValueKind kind = slots_[stack_[stack_size() - 2]].kind();
354 return !wasm::is_reference(kind) && kind != kS128;
355 }
356 default:
357 return true;
358 }
359}
360
361inline void WasmBytecodeGenerator::I32Push(bool emit) {
362 uint32_t slot_index = _PushSlot(kWasmI32);
363 uint32_t slot_offset = slots_[slot_index].slot_offset;
364 if (emit) EmitSlotOffset(slot_offset);
365}
366
367inline void WasmBytecodeGenerator::I64Push(bool emit) {
368 uint32_t slot_index = _PushSlot(kWasmI64);
369 uint32_t slot_offset = slots_[slot_index].slot_offset;
370 if (emit) EmitSlotOffset(slot_offset);
371}
372
373inline void WasmBytecodeGenerator::F32Push(bool emit) {
374 uint32_t slot_index = _PushSlot(kWasmF32);
375 uint32_t slot_offset = slots_[slot_index].slot_offset;
376 if (emit) EmitSlotOffset(slot_offset);
377}
378
379inline void WasmBytecodeGenerator::F64Push(bool emit) {
380 uint32_t slot_index = _PushSlot(kWasmF64);
381 uint32_t slot_offset = slots_[slot_index].slot_offset;
382 if (emit) EmitSlotOffset(slot_offset);
383}
384
385inline void WasmBytecodeGenerator::S128Push(bool emit) {
386 uint32_t slot_index = _PushSlot(kWasmS128);
387 uint32_t slot_offset = slots_[slot_index].slot_offset;
388 if (emit) EmitSlotOffset(slot_offset);
389}
390
391inline void WasmBytecodeGenerator::RefPush(ValueType type, bool emit) {
392 uint32_t slot_index = _PushSlot(type);
393 uint32_t slot_offset = slots_[slot_index].slot_offset;
394 if (emit) {
395 EmitSlotOffset(slot_offset);
396 EmitRefStackIndex(slots_[slot_index].ref_stack_index);
397 }
398}
399
401 switch (type.kind()) {
402 case kI32:
403 I32Push();
404 break;
405 case kI64:
406 I64Push();
407 break;
408 case kF32:
409 F32Push();
410 break;
411 case kF64:
412 F64Push();
413 break;
414 case kS128:
415 S128Push();
416 break;
417 case kRef:
418 case kRefNull:
419 RefPush(type);
420 break;
421 default:
422 UNREACHABLE();
423 }
424}
425
426inline void WasmBytecodeGenerator::PushCopySlot(uint32_t from_stack_index) {
427 DCHECK_LT(from_stack_index, stack_.size());
428 PushSlot(stack_[from_stack_index]);
429
430#ifdef V8_ENABLE_DRUMBRAKE_TRACING
431 TracePushCopySlot(from_stack_index);
432#endif // V8_ENABLE_DRUMBRAKE_TRACING
433}
434
435inline void WasmBytecodeGenerator::PushConstSlot(uint32_t slot_index) {
436 PushSlot(slot_index);
437
438#ifdef V8_ENABLE_DRUMBRAKE_TRACING
439 TracePushConstSlot(slot_index);
440#endif // V8_ENABLE_DRUMBRAKE_TRACING
441}
442
444 const WasmBytecodeGenerator::BlockData& block_data) const {
445 if (block_data.signature_.is_bottom()) {
446 const FunctionSig* sig =
447 module_->signature(block_data.signature_.sig_index);
448 return 0 == (sig->parameter_count() + sig->return_count());
449 } else if (block_data.signature_.value_type() == kWasmVoid) {
450 return true;
451 }
452 return false;
453}
454
456 const WasmBytecodeGenerator::BlockData& block_data) const {
457 if (block_data.signature_.is_bottom()) {
458 const FunctionSig* sig =
459 module_->signature(block_data.signature_.sig_index);
460 return static_cast<uint32_t>(sig->parameter_count());
461 }
462 return 0;
463}
464
466 const WasmBytecodeGenerator::BlockData& block_data, size_t index) const {
467 DCHECK(block_data.signature_.is_bottom());
468 const FunctionSig* sig = module_->signature(block_data.signature_.sig_index);
469 return sig->GetParam(index);
470}
471
473 const WasmBytecodeGenerator::BlockData& block_data) const {
474 if (block_data.signature_.is_bottom()) {
475 const FunctionSig* sig =
476 module_->signature(block_data.signature_.sig_index);
477 return static_cast<uint32_t>(sig->return_count());
478 } else if (block_data.signature_.value_type() == kWasmVoid) {
479 return 0;
480 }
481 return 1;
482}
483
485 const WasmBytecodeGenerator::BlockData& block_data, size_t index) const {
487 if (block_data.signature_.is_bottom()) {
488 const FunctionSig* sig =
489 module_->signature(block_data.signature_.sig_index);
490 return sig->GetReturn(index);
491 }
492 DCHECK_EQ(index, 0);
493 return block_data.signature_.value_type();
494}
495
497 return module_->globals[index].type.kind();
498}
499
501 return !module_->memories.empty() && module_->memories[0].is_memory64();
502}
503
505 return module_->memories.size() > 1;
506}
507
508inline void WasmBytecodeGenerator::EmitGlobalIndex(uint32_t index) {
509 Emit(&index, sizeof(index));
510}
511
513 DCHECK_GE(current_block_index_, 0);
514 int index = blocks_[current_block_index_].parent_block_index_;
515 uint32_t depth = 0;
516 while (index >= 0) {
517 depth++;
518 index = blocks_[index].parent_block_index_;
519 }
520 return depth;
521}
522
523inline int32_t WasmBytecodeGenerator::GetTargetBranch(uint32_t delta) const {
524 int index = current_block_index_;
525 while (delta--) {
526 DCHECK_GE(index, 0);
527 index = blocks_[index].parent_block_index_;
528 }
529 return index;
530}
531
532inline void WasmBytecodeGenerator::EmitBranchOffset(uint32_t delta) {
533 int32_t target_branch_index = GetTargetBranch(delta);
534 DCHECK_GE(target_branch_index, 0);
535 blocks_[target_branch_index].branch_code_offsets_.emplace_back(
536 CurrentCodePos());
537
538 const uint32_t current_code_offset = CurrentCodePos();
539 Emit(&current_code_offset, sizeof(current_code_offset));
540}
541
543 uint32_t code_pos) {
544 int32_t target_branch_index = GetTargetBranch(delta);
545 DCHECK_GE(target_branch_index, 0);
546 blocks_[target_branch_index].branch_code_offsets_.emplace_back(code_pos);
547
548 Emit(&code_pos, sizeof(code_pos));
549}
550
552 // Initially emits offset to jump the end of the 'if' block. If we meet an
553 // 'else' instruction later, this offset needs to be updated with the offset
554 // to the beginning of that 'else' block.
555 blocks_[current_block_index_].branch_code_offsets_.emplace_back(
556 CurrentCodePos());
557
558 const uint32_t current_code_offset = CurrentCodePos();
559 Emit(&current_code_offset, sizeof(current_code_offset));
560}
561
563 // Initially emits offset to jump the end of the 'try/catch' blocks. When we
564 // meet the corresponding 'end' instruction later, this offset needs to be
565 // updated with the offset to the 'end' instruction.
566 blocks_[current_block_index_].branch_code_offsets_.emplace_back(
567 CurrentCodePos());
568
569 const uint32_t current_code_offset = CurrentCodePos();
570 Emit(&current_code_offset, sizeof(current_code_offset));
571}
572
573inline void WasmBytecodeGenerator::BeginElseBlock(uint32_t if_block_index,
574 bool dummy) {
575 EndBlock(kExprElse); // End matching if block.
576 RestoreIfElseParams(if_block_index);
577
578 int32_t else_block_index =
579 BeginBlock(kExprElse, blocks_[if_block_index].signature_);
580 blocks_[if_block_index].if_else_block_index_ = else_block_index;
581 blocks_[else_block_index].if_else_block_index_ = if_block_index;
582 blocks_[else_block_index].first_block_index_ =
583 blocks_[if_block_index].first_block_index_;
584}
585
587 uint32_t function_index) const {
588 return module_->functions[function_index].sig;
589}
590
592 RegMode reg_mode) const {
593 switch (reg_mode) {
594 case RegMode::kNoReg:
595 if (stack_.empty()) return kI32; // not used
596 return slots_[stack_[stack_top_index()]].kind();
597 case RegMode::kI32Reg:
598 return kI32;
599 case RegMode::kI64Reg:
600 return kI64;
601 case RegMode::kF32Reg:
602 return kF32;
603 case RegMode::kF64Reg:
604 return kF64;
605 case RegMode::kAnyReg:
606 default:
607 UNREACHABLE();
608 }
609}
610
611} // namespace wasm
612} // namespace internal
613} // namespace v8
614
615#endif // V8_WASM_INTERPRETER_WASM_INTERPRETER_INL_H_
Builtins::Kind kind
Definition builtins.cc:40
constexpr ValueKind kind() const
Definition value-type.h:631
ValueType GetParamType(const WasmBytecodeGenerator::BlockData &block_data, size_t index) const
void BeginElseBlock(uint32_t if_block_index, bool dummy)
void EmitBranchTableOffset(uint32_t delta, uint32_t code_pos)
const FunctionSig * GetFunctionSignature(uint32_t function_index) const
bool HasVoidSignature(const WasmBytecodeGenerator::BlockData &block_data) const
ValueKind GetTopStackType(RegMode reg_mode) const
void PushCopySlot(uint32_t from_stack_index)
ValueType GetReturnType(const WasmBytecodeGenerator::BlockData &block_data, size_t index) const
uint32_t ReturnsCount(const WasmBytecodeGenerator::BlockData &block_data) const
uint32_t ParamsCount(const WasmBytecodeGenerator::BlockData &block_data) const
ValueKind GetGlobalType(uint32_t index) const
void RefPush(ValueType type, bool emit=true)
int32_t GetTargetBranch(uint32_t delta) const
bool ToRegisterIsAllowed(const WasmInstruction &instr)
static bool HasRefOrSimdArgs(const FunctionSig *sig)
ValueType return_type(size_t index) const
static uint32_t RefLocalsCount(const InterpreterCode *wasm_code)
static uint32_t JSToWasmWrapperPackedArraySize(const FunctionSig *sig)
bool InitializeSlots(uint8_t *sp, size_t stack_space) const
static uint32_t RetsSizeInSlots(const FunctionSig *sig)
static uint32_t ArgsSizeInSlots(const FunctionSig *sig)
ValueType local_type(size_t index) const
static bool ContainsSimd(const FunctionSig *sig)
static uint32_t LocalsSizeInSlots(const InterpreterCode *wasm_code)
ValueType arg_type(size_t index) const
static uint32_t RefArgsCount(const FunctionSig *sig)
static uint32_t RefRetsCount(const FunctionSig *sig)
const FrameState * GetCurrentActivationFor(const WasmInterpreterRuntime *wasm_runtime) const
WasmInterpreterThread::Activation * StartActivation(WasmInterpreterRuntime *wasm_runtime, Address frame_pointer, uint8_t *interpreter_fp, const FrameState &frame_state)
InterpreterCode * GetCode(uint32_t function_index)
WasmBytecode * GetFunctionBytecode(uint32_t func_index)
void AddFunction(const WasmFunction *function, const uint8_t *code_start, const uint8_t *code_end)
ZoneVector< InterpreterCode > interpreter_code_
std::vector< WasmInterpreterStackEntry > GetInterpretedStack(Address frame_pointer)
WasmInterpreterRuntime * GetWasmRuntime()
std::shared_ptr< WasmInterpreterRuntime > wasm_runtime_
int GetFunctionIndex(Address frame_pointer, int index) const
void SetTrapFunctionIndex(int32_t func_index)
void BeginExecution(WasmInterpreterThread *thread, uint32_t function_index, Address frame_pointer, uint8_t *interpreter_fp, uint32_t ref_stack_offset, const std::vector< WasmValue > &argument_values)
WasmValue GetReturnValue(int index) const
Handle< Code > code
enum v8::internal::@1270::DeoptimizableCodeIterator::@67 state_
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
Instruction * instr
WordWithBits< 128 > Simd128
Definition index.h:236
static const size_t kSlotSize
constexpr IndependentValueType kWasmF32
uint32_t WasmInterpreterRuntime * wasm_runtime
uint32_t GetValueSizeInSlots(ValueKind kind)
constexpr IndependentValueType kWasmI32
DirectHandle< Object > WasmRef
constexpr IndependentHeapType kWasmVoid
constexpr IndependentValueType kWasmS128
constexpr IndependentValueType kWasmF64
constexpr bool is_reference(ValueKind kind)
constexpr IndependentValueType kWasmI64
kWasmInternalFunctionIndirectPointerTag kProtectedInstanceDataOffset sig
constexpr int kSystemPointerSize
Definition globals.h:410
Definition c-api.cc:87
std::vector< std::vector< ValueType > > blocks_
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#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
void ResetHandleScope(Isolate *isolate)
#define V8_UNLIKELY(condition)
Definition v8config.h:660
const wasm::WasmModule * module_