v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
interpreter-builtins-arm64.cc
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
12
13#if V8_ENABLE_WEBASSEMBLY
17#endif // V8_ENABLE_WEBASSEMBLY
18
19namespace v8 {
20namespace internal {
21
22#define __ ACCESS_MASM(masm)
23
24#if V8_ENABLE_WEBASSEMBLY
25
26namespace {
27// Helper functions for the GenericJSToWasmInterpreterWrapper.
28
29void PrepareForJsToWasmConversionBuiltinCall(MacroAssembler* masm,
30 Register current_param_slot,
31 Register valuetypes_array_ptr,
32 Register wasm_instance,
33 Register function_data) {
34 UseScratchRegisterScope temps(masm);
35 Register GCScanCount = temps.AcquireX();
36 // Pushes and puts the values in order onto the stack before builtin calls for
37 // the GenericJSToWasmInterpreterWrapper.
38 // The last two slots contain tagged objects that need to be visited during
39 // GC.
40 __ Mov(GCScanCount, 2);
41 __ Str(
42 GCScanCount,
44 fp, BuiltinWasmInterpreterWrapperConstants::kGCScanSlotCountOffset));
45 __ Push(current_param_slot, valuetypes_array_ptr, wasm_instance,
46 function_data);
47 // We had to prepare the parameters for the Call: we have to put the context
48 // into x27.
49 Register wasm_trusted_instance = wasm_instance;
50 __ LoadTrustedPointerField(
51 wasm_trusted_instance,
52 FieldMemOperand(wasm_instance, WasmInstanceObject::kTrustedDataOffset),
53 kWasmTrustedInstanceDataIndirectPointerTag);
54 __ LoadTaggedField(
55 kContextRegister, // cp(x27)
56 MemOperand(wasm_trusted_instance,
58 WasmTrustedInstanceData::kNativeContextOffset)));
59}
60
61void RestoreAfterJsToWasmConversionBuiltinCall(MacroAssembler* masm,
62 Register function_data,
63 Register wasm_instance,
64 Register valuetypes_array_ptr,
65 Register current_param_slot) {
66 // Pop and load values from the stack in order into the registers after
67 // builtin calls for the GenericJSToWasmInterpreterWrapper.
68 __ Pop(function_data, wasm_instance, valuetypes_array_ptr,
69 current_param_slot);
70 __ Str(
71 xzr,
73 fp, BuiltinWasmInterpreterWrapperConstants::kGCScanSlotCountOffset));
74}
75
76void PrepareForBuiltinCall(MacroAssembler* masm, Register array_start,
77 Register return_count, Register wasm_instance) {
78 UseScratchRegisterScope temps(masm);
79 Register GCScanCount = temps.AcquireX();
80 // Pushes and puts the values in order onto the stack before builtin calls for
81 // the GenericJSToWasmInterpreterWrapper.
82 __ Mov(GCScanCount, 1);
83 __ Str(
84 GCScanCount,
86 fp, BuiltinWasmInterpreterWrapperConstants::kGCScanSlotCountOffset));
87 // The last slot contains a tagged object that need to be visited during GC.
88 __ Push(array_start, return_count, xzr, wasm_instance);
89 // We had to prepare the parameters for the Call: we have to put the context
90 // into x27.
91 Register wasm_trusted_instance = wasm_instance;
92 __ LoadTrustedPointerField(
93 wasm_trusted_instance,
94 FieldMemOperand(wasm_instance, WasmInstanceObject::kTrustedDataOffset),
95 kWasmTrustedInstanceDataIndirectPointerTag);
96 __ LoadTaggedField(
97 kContextRegister, // cp(x27)
98 MemOperand(wasm_trusted_instance,
100 WasmTrustedInstanceData::kNativeContextOffset)));
101}
102
103void RestoreAfterBuiltinCall(MacroAssembler* masm, Register wasm_instance,
104 Register return_count, Register array_start) {
105 // Pop and load values from the stack in order into the registers after
106 // builtin calls for the GenericJSToWasmInterpreterWrapper.
107 __ Pop(wasm_instance, xzr, return_count, array_start);
108}
109
110void PrepareForWasmToJsConversionBuiltinCall(
111 MacroAssembler* masm, Register return_count, Register result_index,
112 Register current_return_slot, Register valuetypes_array_ptr,
113 Register wasm_instance, Register fixed_array, Register jsarray,
114 bool load_native_context = true) {
115 UseScratchRegisterScope temps(masm);
116 Register GCScanCount = temps.AcquireX();
117 // Pushes and puts the values in order onto the stack before builtin calls
118 // for the GenericJSToWasmInterpreterWrapper.
119 // The last three slots contain tagged objects that need to be visited during
120 // GC.
121 __ Mov(GCScanCount, 3);
122 __ Str(
123 GCScanCount,
125 fp, BuiltinWasmInterpreterWrapperConstants::kGCScanSlotCountOffset));
126 __ Push(return_count, result_index, current_return_slot, valuetypes_array_ptr,
127 xzr, wasm_instance, fixed_array, jsarray);
128 if (load_native_context) {
129 // Put the context into x27.
130 Register wasm_trusted_instance = wasm_instance;
131 __ LoadTrustedPointerField(
132 wasm_trusted_instance,
133 FieldMemOperand(wasm_instance, WasmInstanceObject::kTrustedDataOffset),
134 kWasmTrustedInstanceDataIndirectPointerTag);
135 __ LoadTaggedField(
136 kContextRegister, // cp(x27)
137 MemOperand(wasm_trusted_instance,
139 WasmTrustedInstanceData::kNativeContextOffset)));
140 }
141}
142
143void RestoreAfterWasmToJsConversionBuiltinCall(
144 MacroAssembler* masm, Register jsarray, Register fixed_array,
145 Register wasm_instance, Register valuetypes_array_ptr,
146 Register current_return_slot, Register result_index,
147 Register return_count) {
148 // Pop and load values from the stack in order into the registers after
149 // builtin calls for the GenericJSToWasmInterpreterWrapper.
150 __ Pop(jsarray, fixed_array, wasm_instance, xzr, valuetypes_array_ptr,
151 current_return_slot, result_index, return_count);
152 __ Str(
153 xzr,
155 fp, BuiltinWasmInterpreterWrapperConstants::kGCScanSlotCountOffset));
156}
157
158} // namespace
159
160void Builtins::Generate_WasmInterpreterEntry(MacroAssembler* masm) {
161 // Input registers:
162 // x7 (kWasmImplicitArgRegister): wasm_instance
163 // x12: array_start
164 // w15: function_index
165 Register array_start = x12;
166 Register function_index = x15;
167
168 // Set up the stackframe:
169 //
170 // fp-0x10 wasm_instance
171 // fp-0x08 Marker(StackFrame::WASM_INTERPRETER_ENTRY)
172 // fp Old RBP
173 __ EnterFrame(StackFrame::WASM_INTERPRETER_ENTRY);
174
176 __ Push(function_index, array_start);
178 __ CallRuntime(Runtime::kWasmRunInterpreter, 3);
179
180 // Deconstruct the stack frame.
181 __ LeaveFrame(StackFrame::WASM_INTERPRETER_ENTRY);
182 __ Ret();
183}
184
185void LoadFunctionDataAndWasmInstance(MacroAssembler* masm,
186 Register function_data,
187 Register wasm_instance) {
188 Register closure = function_data;
189 Register shared_function_info = closure;
190 __ LoadTaggedField(
191 shared_function_info,
193 closure,
195 closure = no_reg;
196 __ LoadTrustedPointerField(
197 function_data,
198 FieldMemOperand(shared_function_info,
199 SharedFunctionInfo::kTrustedFunctionDataOffset),
200
202 shared_function_info = no_reg;
203
204 Register trusted_instance_data = wasm_instance;
205#if V8_ENABLE_SANDBOX
206 __ DecompressProtected(
207 trusted_instance_data,
208 MemOperand(function_data,
209 WasmExportedFunctionData::kProtectedInstanceDataOffset -
211#else
212 __ LoadTaggedField(
213 trusted_instance_data,
214 MemOperand(function_data,
215 WasmExportedFunctionData::kProtectedInstanceDataOffset -
217#endif
218 __ LoadTaggedField(
219 wasm_instance,
220 FieldMemOperand(trusted_instance_data,
221 WasmTrustedInstanceData::kInstanceObjectOffset));
222}
223
224void LoadFromSignature(MacroAssembler* masm, Register valuetypes_array_ptr,
225 Register return_count, Register param_count) {
226 Register signature = valuetypes_array_ptr;
227 __ Ldr(return_count,
229 __ Ldr(param_count,
231 valuetypes_array_ptr = signature;
232 __ Ldr(valuetypes_array_ptr,
234}
235
236void LoadValueTypesArray(MacroAssembler* masm, Register function_data,
237 Register valuetypes_array_ptr, Register return_count,
238 Register param_count, Register signature_data) {
239 __ LoadTaggedField(
240 signature_data,
241 FieldMemOperand(function_data,
242 WasmExportedFunctionData::kPackedArgsSizeOffset));
243 __ SmiToInt32(signature_data);
244
245 Register signature = valuetypes_array_ptr;
246 __ Ldr(signature,
247 MemOperand(function_data,
248 WasmExportedFunctionData::kSigOffset - kHeapObjectTag));
249 LoadFromSignature(masm, valuetypes_array_ptr, return_count, param_count);
250}
251
252class RegisterAllocator {
253 public:
254 class Scoped {
255 public:
256 Scoped(RegisterAllocator* allocator, Register* reg)
257 : allocator_(allocator), reg_(reg) {}
258 ~Scoped() { allocator_->Free(reg_); }
259
260 private:
261 RegisterAllocator* allocator_;
262 Register* reg_;
263 };
264
265 explicit RegisterAllocator(const CPURegList& registers)
267 void Ask(Register* reg) {
269 DCHECK(!available_.IsEmpty());
270 *reg = available_.PopLowestIndex().X();
271 allocated_registers_.push_back(reg);
272 }
273
274 void Pinned(const Register& requested, Register* reg) {
275 DCHECK(available_.IncludesAliasOf(requested));
276 *reg = requested;
277 Reserve(requested);
278 allocated_registers_.push_back(reg);
279 }
280
281 void Free(Register* reg) {
283 available_.Combine(*reg);
284 *reg = no_reg;
286 find(allocated_registers_.begin(), allocated_registers_.end(), reg));
287 }
288
289 void Reserve(const Register& reg) {
290 if (reg == NoReg) {
291 return;
292 }
293 DCHECK(available_.IncludesAliasOf(reg));
294 available_.Remove(reg);
295 }
296
297 void Reserve(const Register& reg1, const Register& reg2,
298 const Register& reg3 = NoReg, const Register& reg4 = NoReg,
299 const Register& reg5 = NoReg, const Register& reg6 = NoReg) {
300 Reserve(reg1);
301 Reserve(reg2);
302 Reserve(reg3);
303 Reserve(reg4);
304 Reserve(reg5);
305 Reserve(reg6);
306 }
307
308 bool IsUsed(const Register& reg) {
309 return initial_.IncludesAliasOf(reg) && !available_.IncludesAliasOf(reg);
310 }
311
312 void ResetExcept(const Register& reg1 = NoReg, const Register& reg2 = NoReg,
313 const Register& reg3 = NoReg, const Register& reg4 = NoReg,
314 const Register& reg5 = NoReg, const Register& reg6 = NoReg,
315 const Register& reg7 = NoReg) {
317 if (reg1 != NoReg) {
318 available_.Remove(reg1, reg2, reg3, reg4);
319 }
320 if (reg5 != NoReg) {
321 available_.Remove(reg5, reg6, reg7);
322 }
323 auto it = allocated_registers_.begin();
324 while (it != allocated_registers_.end()) {
325 if (available_.IncludesAliasOf(**it)) {
326 **it = no_reg;
327 allocated_registers_.erase(it);
328 } else {
329 it++;
330 }
331 }
332 }
333
334 static RegisterAllocator WithAllocatableGeneralRegisters() {
335 CPURegList list(kXRegSizeInBits, RegList());
336 // Only use registers x0-x15, which are volatile (caller-saved).
337 // Mksnapshot would fail to compile the GenericJSToWasmInterpreterWrapper
338 // and GenericWasmToJSInterpreterWrapper if they needed more registers.
339 list.set_bits(0xffff); // (The default value is 0x0bf8ffff).
340 return RegisterAllocator(list);
341 }
342
343 private:
344 std::vector<Register*> allocated_registers_;
345 const CPURegList initial_;
346 CPURegList available_;
347};
348
349#define DEFINE_REG(Name) \
350 Register Name = no_reg; \
351 regs.Ask(&Name);
352
353#define DEFINE_REG_W(Name) \
354 DEFINE_REG(Name); \
355 Name = Name.W();
356
357#define ASSIGN_REG(Name) regs.Ask(&Name);
358
359#define ASSIGN_REG_W(Name) \
360 ASSIGN_REG(Name); \
361 Name = Name.W();
362
363#define DEFINE_PINNED(Name, Reg) \
364 Register Name = no_reg; \
365 regs.Pinned(Reg, &Name);
366
367#define DEFINE_SCOPED(Name) \
368 DEFINE_REG(Name) \
369 RegisterAllocator::Scoped scope_##Name(&regs, &Name);
370
371#define FREE_REG(Name) regs.Free(&Name);
372
373// TODO(paolosev@microsoft.com): this should be converted into a Torque builtin,
374// like it was done for GenericJSToWasmWrapper.
375void Builtins::Generate_GenericJSToWasmInterpreterWrapper(
376 MacroAssembler* masm) {
377 auto regs = RegisterAllocator::WithAllocatableGeneralRegisters();
378 // Set up the stackframe.
379 __ EnterFrame(StackFrame::JS_TO_WASM);
380
381 // -------------------------------------------
382 // Compute offsets and prepare for GC.
383 // -------------------------------------------
384 // GenericJSToWasmInterpreterWrapperFrame:
385 // fp-N Args/retvals array for Wasm call
386 // ... ...
387 // fp-0x58 SignatureData
388 // fp-0x50 CurrentIndex
389 // fp-0x48 ArgRetsIsArgs
390 // fp-0x40 ArgRetsAddress
391 // fp-0x38 ValueTypesArray
392 // fp-0x30 SigReps
393 // fp-0x28 ReturnCount
394 // fp-0x20 ParamCount
395 // fp-0x18 InParamCount
396 // fp-0x10 GCScanSlotCount
397 // fp-0x08 Marker(StackFrame::JS_TO_WASM)
398 // fp Old RBP
399
400 constexpr int kMarkerOffset =
401 BuiltinWasmInterpreterWrapperConstants::kGCScanSlotCountOffset +
403 // The number of parameters passed to this function.
404 constexpr int kInParamCountOffset =
405 BuiltinWasmInterpreterWrapperConstants::kGCScanSlotCountOffset -
407 // The number of parameters according to the signature.
408 constexpr int kParamCountOffset =
409 BuiltinWasmInterpreterWrapperConstants::kParamCountOffset;
410 constexpr int kReturnCountOffset =
411 BuiltinWasmInterpreterWrapperConstants::kReturnCountOffset;
412 constexpr int kSigRepsOffset =
413 BuiltinWasmInterpreterWrapperConstants::kSigRepsOffset;
414 constexpr int kValueTypesArrayStartOffset =
415 BuiltinWasmInterpreterWrapperConstants::kValueTypesArrayStartOffset;
416 // Array for arguments and return values. They will be scanned by GC.
417 constexpr int kArgRetsAddressOffset =
418 BuiltinWasmInterpreterWrapperConstants::kArgRetsAddressOffset;
419 // Arg/Return arrays use the same stack address. So, we should keep a flag
420 // whether we are using the array for args or returns. (1 = Args, 0 = Rets)
421 constexpr int kArgRetsIsArgsOffset =
422 BuiltinWasmInterpreterWrapperConstants::kArgRetsIsArgsOffset;
423 // The index of the argument being converted.
424 constexpr int kCurrentIndexOffset =
425 BuiltinWasmInterpreterWrapperConstants::kCurrentIndexOffset;
426 // Precomputed signature data, a uint32_t with the format:
427 // bit 0-14: PackedArgsSize
428 // bit 15: HasRefArgs
429 // bit 16: HasRefRets
430 constexpr int kSignatureDataOffset =
431 BuiltinWasmInterpreterWrapperConstants::kSignatureDataOffset;
432 // We set and use this slot only when moving parameters into the parameter
433 // registers (so no GC scan is needed).
434 constexpr int kNumSpillSlots =
435 (kMarkerOffset - kSignatureDataOffset) / kSystemPointerSize;
436 constexpr int kNum16BytesAlignedSpillSlots = 2 * ((kNumSpillSlots + 1) / 2);
437
438 __ Sub(sp, sp, Immediate(kNum16BytesAlignedSpillSlots * kSystemPointerSize));
439 // Put the in_parameter count on the stack, we only need it at the very end
440 // when we pop the parameters off the stack.
442 __ Str(kJavaScriptCallArgCountRegister, MemOperand(fp, kInParamCountOffset));
443
444 // -------------------------------------------
445 // Load the Wasm exported function data and the Wasm instance.
446 // -------------------------------------------
447 DEFINE_PINNED(function_data, kJSFunctionRegister); // x1
448 DEFINE_PINNED(wasm_instance, kWasmImplicitArgRegister); // x7
449 LoadFunctionDataAndWasmInstance(masm, function_data, wasm_instance);
450
451 regs.ResetExcept(function_data, wasm_instance);
452
453 // -------------------------------------------
454 // Load values from the signature.
455 // -------------------------------------------
456
457 // Param should be x0 for calling Runtime in the conversion loop.
458 DEFINE_PINNED(param, x0);
459 // These registers stays alive until we load params to param registers.
460 // To prevent aliasing assign higher register here.
461 DEFINE_PINNED(valuetypes_array_ptr, x11);
462
463 DEFINE_REG(return_count);
464 DEFINE_REG(param_count);
465 DEFINE_REG(signature_data);
466 DEFINE_REG(scratch);
467
468 // -------------------------------------------
469 // Load values from the signature.
470 // -------------------------------------------
471 LoadValueTypesArray(masm, function_data, valuetypes_array_ptr, return_count,
472 param_count, signature_data);
473 __ Str(signature_data, MemOperand(fp, kSignatureDataOffset));
474 Register array_size = signature_data;
475 __ And(array_size, array_size,
477 // -------------------------------------------
478 // Store signature-related values to the stack.
479 // -------------------------------------------
480 // We store values on the stack to restore them after function calls.
481 // We cannot push values onto the stack right before the wasm call. The wasm
482 // function expects the parameters, that didn't fit into the registers, on the
483 // top of the stack.
484 __ Str(param_count, MemOperand(fp, kParamCountOffset));
485 __ Str(return_count, MemOperand(fp, kReturnCountOffset));
486 __ Str(valuetypes_array_ptr, MemOperand(fp, kSigRepsOffset));
487 __ Str(valuetypes_array_ptr, MemOperand(fp, kValueTypesArrayStartOffset));
488
489 // -------------------------------------------
490 // Allocate array for args and return value.
491 // -------------------------------------------
492
493 // Leave space for WasmInstance.
494 __ Add(array_size, array_size, Immediate(kSystemPointerSize));
495 // Ensure that the array is 16-bytes aligned.
496 __ Add(scratch, array_size, Immediate(8));
497 __ And(array_size, scratch, Immediate(-16));
498
499 DEFINE_PINNED(array_start, x12);
500 __ Sub(array_start, sp, array_size);
501 __ Mov(sp, array_start);
502
503 __ Mov(scratch, 1);
504 __ Str(scratch, MemOperand(fp, kArgRetsIsArgsOffset));
505
506 __ Str(xzr, MemOperand(fp, kCurrentIndexOffset));
507
508 // Set the current_param_slot to point to the start of the section, after the
509 // WasmInstance object.
510 DEFINE_PINNED(current_param_slot, x13);
511 __ Add(current_param_slot, array_start, Immediate(kSystemPointerSize));
512 __ Str(current_param_slot, MemOperand(fp, kArgRetsAddressOffset));
513
514 Label prepare_for_wasm_call;
515 __ Cmp(param_count, 0);
516
517 // IF we have 0 params: jump through parameter handling.
518 __ B(&prepare_for_wasm_call, eq);
519
520 // Create a section on the stack to pass the evaluated parameters to the
521 // interpreter and to receive the results. This section represents the array
522 // expected as argument by the Runtime_WasmRunInterpreter.
523 // Arguments are stored one after the other without holes, starting at the
524 // beginning of the array, and the interpreter puts the returned values in the
525 // same array, also starting at the beginning.
526
527 // Loop through the params starting with the first.
528 // 'fp + kFPOnStackSize + kPCOnStackSize + kReceiverOnStackSize' points to the
529 // first JS parameter we are processing.
530
531 // We have to check the types of the params. The ValueType array contains
532 // first the return then the param types.
533
534 // Set the ValueType array pointer to point to the first parameter.
535 constexpr int kValueTypeSize = sizeof(wasm::ValueType);
536 static_assert(kValueTypeSize == 4);
537 const int32_t kValueTypeSizeLog2 = log2(kValueTypeSize);
538 // Set the ValueType array pointer to point to the first parameter.
539 __ Add(valuetypes_array_ptr, valuetypes_array_ptr,
540 Operand(return_count, LSL, kValueTypeSizeLog2));
541
542 DEFINE_REG(current_index);
543 __ Mov(current_index, xzr);
544
545 // -------------------------------------------
546 // Param evaluation loop.
547 // -------------------------------------------
548 Label loop_through_params;
549 __ bind(&loop_through_params);
550
551 constexpr int kReceiverOnStackSize = kSystemPointerSize;
552 constexpr int kArgsOffset =
553 kFPOnStackSize + kPCOnStackSize + kReceiverOnStackSize;
554 // Read JS argument into 'param'.
555 __ Add(scratch, fp, kArgsOffset);
556 __ Ldr(param,
557 MemOperand(scratch, current_index, LSL, kSystemPointerSizeLog2));
558 __ Str(current_index, MemOperand(fp, kCurrentIndexOffset));
559
560 DEFINE_REG_W(valuetype);
561 __ Ldr(valuetype, MemOperand(valuetypes_array_ptr, 0));
562
563 // -------------------------------------------
564 // Param conversion.
565 // -------------------------------------------
566 // If param is a Smi we can easily convert it. Otherwise we'll call a builtin
567 // for conversion.
568 Label param_conversion_done;
569 Label check_ref_param;
570 Label convert_param;
571 __ Cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
572 __ B(&check_ref_param, ne);
573 __ JumpIfNotSmi(param, &convert_param);
574
575 // Change the param from Smi to int32.
576 __ SmiUntag(param);
577 // Place the param into the proper slot in Integer section.
578 __ Str(param.W(), MemOperand(current_param_slot, 0));
579 __ Add(current_param_slot, current_param_slot, Immediate(sizeof(int32_t)));
580 __ jmp(&param_conversion_done);
581
582 Label handle_ref_param;
583 __ bind(&check_ref_param);
584
585 // wasm::ValueKind::kRefNull is not representable as a cmp immediate operand.
586 __ Tst(valuetype, Immediate(1));
587 __ B(&convert_param, eq);
588
589 // Place the reference param into the proper slot.
590 __ bind(&handle_ref_param);
591 // Make sure slot for ref args are 64-bit aligned.
592 __ And(scratch, current_param_slot, Immediate(0x04));
593 __ Add(current_param_slot, current_param_slot, scratch);
594 __ Str(param, MemOperand(current_param_slot, 0));
595 __ Add(current_param_slot, current_param_slot, Immediate(kSystemPointerSize));
596
597 // -------------------------------------------
598 // Param conversion done.
599 // -------------------------------------------
600 __ bind(&param_conversion_done);
601
602 __ Add(valuetypes_array_ptr, valuetypes_array_ptr, kValueTypeSize);
603
604 __ Ldr(current_index, MemOperand(fp, kCurrentIndexOffset));
605 __ Ldr(scratch, MemOperand(fp, kParamCountOffset));
606 __ Add(current_index, current_index, 1);
607 __ cmp(current_index, scratch);
608 __ B(&loop_through_params, lt);
609 __ Str(current_index, MemOperand(fp, kCurrentIndexOffset));
610 __ jmp(&prepare_for_wasm_call);
611
612 // -------------------------------------------
613 // Param conversion builtins.
614 // -------------------------------------------
615 __ bind(&convert_param);
616 // The order of pushes is important. We want the heap objects, that should be
617 // scanned by GC, to be on the top of the stack.
618 // We have to set the indicating value for the GC to the number of values on
619 // the top of the stack that have to be scanned before calling the builtin
620 // function.
621 // We don't need the JS context for these builtin calls.
622 // The builtin expects the parameter to be in register param = rax.
623
624 PrepareForJsToWasmConversionBuiltinCall(masm, current_param_slot,
625 valuetypes_array_ptr, wasm_instance,
626 function_data);
627
628 Label param_kWasmI32_not_smi, param_kWasmI64, param_kWasmF32, param_kWasmF64,
629 throw_type_error;
630
631 __ Cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
632 __ B(&param_kWasmI32_not_smi, eq);
633 __ Cmp(valuetype, Immediate(wasm::kWasmI64.raw_bit_field()));
634 __ B(&param_kWasmI64, eq);
635 __ Cmp(valuetype, Immediate(wasm::kWasmF32.raw_bit_field()));
636 __ B(&param_kWasmF32, eq);
637 __ Cmp(valuetype, Immediate(wasm::kWasmF64.raw_bit_field()));
638 __ B(&param_kWasmF64, eq);
639
640 __ Cmp(valuetype, Immediate(wasm::kWasmS128.raw_bit_field()));
641 // Simd arguments cannot be passed from JavaScript.
642 __ B(&throw_type_error, eq);
643
644 // Invalid type.
645 __ DebugBreak();
646
647 __ bind(&param_kWasmI32_not_smi);
648 __ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedNonSmiToInt32),
650 // Param is the result of the builtin.
651 RestoreAfterJsToWasmConversionBuiltinCall(masm, function_data, wasm_instance,
652 valuetypes_array_ptr,
653 current_param_slot);
654 __ Str(param.W(), MemOperand(current_param_slot, 0));
655 __ Add(current_param_slot, current_param_slot, Immediate(sizeof(int32_t)));
656 __ jmp(&param_conversion_done);
657
658 __ bind(&param_kWasmI64);
659 __ Call(BUILTIN_CODE(masm->isolate(), BigIntToI64), RelocInfo::CODE_TARGET);
660 RestoreAfterJsToWasmConversionBuiltinCall(masm, function_data, wasm_instance,
661 valuetypes_array_ptr,
662 current_param_slot);
663 __ Str(param, MemOperand(current_param_slot, 0));
664 __ Add(current_param_slot, current_param_slot, Immediate(sizeof(int64_t)));
665 __ jmp(&param_conversion_done);
666
667 __ bind(&param_kWasmF32);
668 __ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedToFloat32),
670 RestoreAfterJsToWasmConversionBuiltinCall(masm, function_data, wasm_instance,
671 valuetypes_array_ptr,
672 current_param_slot);
673 __ Str(s0, MemOperand(current_param_slot, 0));
674 __ Add(current_param_slot, current_param_slot, Immediate(sizeof(float)));
675 __ jmp(&param_conversion_done);
676
677 __ bind(&param_kWasmF64);
678 __ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedToFloat64),
680 RestoreAfterJsToWasmConversionBuiltinCall(masm, function_data, wasm_instance,
681 valuetypes_array_ptr,
682 current_param_slot);
683 __ Str(kFPReturnRegister0, MemOperand(current_param_slot, 0));
684 __ Add(current_param_slot, current_param_slot, Immediate(sizeof(double)));
685 __ jmp(&param_conversion_done);
686
687 __ bind(&throw_type_error);
688 // CallRuntime expects kRootRegister (x26) to contain the root.
689 __ CallRuntime(Runtime::kWasmThrowJSTypeError);
690 __ DebugBreak(); // Should not return.
691
692 // -------------------------------------------
693 // Prepare for the Wasm call.
694 // -------------------------------------------
695
696 regs.ResetExcept(function_data, wasm_instance, array_start, scratch);
697
698 __ bind(&prepare_for_wasm_call);
699
700 // Set thread_in_wasm_flag.
701 DEFINE_REG_W(scratch32);
702 __ Ldr(scratch, MemOperand(kRootRegister,
704 __ Mov(scratch32, 1); // 32 bit.
705 __ Str(scratch32, MemOperand(scratch, 0));
706
707 DEFINE_PINNED(function_index, w15);
708 __ Ldr(
709 function_index,
710 MemOperand(function_data, WasmExportedFunctionData::kFunctionIndexOffset -
712 // We pass function_index as Smi.
713
714 // One tagged object (the wasm_instance) to be visited if there is a GC
715 // during the call.
716 constexpr int kWasmCallGCScanSlotCount = 1;
717 __ Mov(scratch, kWasmCallGCScanSlotCount);
718 __ Str(
719 scratch,
721 fp, BuiltinWasmInterpreterWrapperConstants::kGCScanSlotCountOffset));
722
723 // -------------------------------------------
724 // Call the Wasm function.
725 // -------------------------------------------
726
727 // Here array_start == sp.
728 __ Str(wasm_instance, MemOperand(sp));
729 // Skip wasm_instance.
730 __ Ldr(array_start, MemOperand(fp, kArgRetsAddressOffset));
731 // Here array_start == sp + kSystemPointerSize.
732 __ Call(BUILTIN_CODE(masm->isolate(), WasmInterpreterEntry),
734 __ Ldr(wasm_instance, MemOperand(sp));
735 __ Ldr(array_start, MemOperand(fp, kArgRetsAddressOffset));
736
737 __ Str(xzr, MemOperand(fp, kArgRetsIsArgsOffset));
738
739 // Unset thread_in_wasm_flag.
740 __ Ldr(scratch, MemOperand(kRootRegister,
742 __ Str(wzr, MemOperand(scratch, 0)); // 32 bit.
743
744 regs.ResetExcept(wasm_instance, array_start, scratch);
745
746 // -------------------------------------------
747 // Return handling.
748 // -------------------------------------------
749 DEFINE_PINNED(return_value, kReturnRegister0); // x0
750 ASSIGN_REG(return_count);
751 __ Ldr(return_count, MemOperand(fp, kReturnCountOffset));
752
753 // All return values are already in the packed array.
754 __ Str(return_count,
756 fp, BuiltinWasmInterpreterWrapperConstants::kCurrentIndexOffset));
757
758 DEFINE_PINNED(fixed_array, x14);
759 __ Mov(fixed_array, xzr);
760 DEFINE_PINNED(jsarray, x15);
761 __ Mov(jsarray, xzr);
762
763 Label all_results_conversion_done, start_return_conversion, return_jsarray;
764
765 __ cmp(return_count, 1);
766 __ B(&start_return_conversion, eq);
767 __ B(&return_jsarray, gt);
768
769 // If no return value, load undefined.
770 __ LoadRoot(return_value, RootIndex::kUndefinedValue);
771 __ jmp(&all_results_conversion_done);
772
773 // If we have more than one return value, we need to return a JSArray.
774 __ bind(&return_jsarray);
775 PrepareForBuiltinCall(masm, array_start, return_count, wasm_instance);
776 __ Mov(return_value, return_count);
777 __ SmiTag(return_value);
778
779 // Create JSArray to hold results.
780 __ Call(BUILTIN_CODE(masm->isolate(), WasmAllocateJSArray),
782 __ Mov(jsarray, return_value);
783
784 RestoreAfterBuiltinCall(masm, wasm_instance, return_count, array_start);
785 __ LoadTaggedField(fixed_array, MemOperand(jsarray, JSArray::kElementsOffset -
787
788 __ bind(&start_return_conversion);
789 Register current_return_slot = array_start;
790 __ Ldr(current_return_slot, MemOperand(fp, kArgRetsAddressOffset));
791
792 DEFINE_PINNED(result_index, x13);
793 __ Mov(result_index, xzr);
794
795 ASSIGN_REG(valuetypes_array_ptr);
796 __ Ldr(valuetypes_array_ptr, MemOperand(fp, kValueTypesArrayStartOffset));
797
798 // -------------------------------------------
799 // Return conversions.
800 // -------------------------------------------
801 Label convert_return_value;
802 __ bind(&convert_return_value);
803 // We have to make sure that the kGCScanSlotCount is set correctly when we
804 // call the builtins for conversion. For these builtins it's the same as for
805 // the Wasm call, that is, kGCScanSlotCount = 0, so we don't have to reset it.
806 // We don't need the JS context for these builtin calls.
807
808 // The first valuetype of the array is the return's valuetype.
809 ASSIGN_REG_W(valuetype);
810 __ Ldr(valuetype, MemOperand(valuetypes_array_ptr, 0));
811
812 Label return_kWasmI32, return_kWasmI64, return_kWasmF32, return_kWasmF64,
813 return_kWasmRef;
814
815 __ Cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
816 __ B(&return_kWasmI32, eq);
817 __ Cmp(valuetype, Immediate(wasm::kWasmI64.raw_bit_field()));
818 __ B(&return_kWasmI64, eq);
819 __ Cmp(valuetype, Immediate(wasm::kWasmF32.raw_bit_field()));
820 __ B(&return_kWasmF32, eq);
821 __ Cmp(valuetype, Immediate(wasm::kWasmF64.raw_bit_field()));
822 __ B(&return_kWasmF64, eq);
823
824 {
825 __ Tst(valuetype, Immediate(1));
826 __ B(&return_kWasmRef, ne);
827
828 // Invalid type. Wasm cannot return Simd results to JavaScript.
829 __ DebugBreak();
830 }
831
832 Label return_value_done;
833
834 Label to_heapnumber;
835 {
836 __ bind(&return_kWasmI32);
837 __ Ldr(return_value, MemOperand(current_return_slot, 0));
838 __ Add(current_return_slot, current_return_slot,
839 Immediate(sizeof(int32_t)));
840 // If pointer compression is disabled, we can convert the return to a smi.
841 if (SmiValuesAre32Bits()) {
842 __ SmiTag(return_value);
843 } else {
844 // Double the return value to test if it can be a Smi.
845 __ Adds(wzr, return_value.W(), return_value.W());
846 // If there was overflow, convert the return value to a HeapNumber.
847 __ B(&to_heapnumber, vs);
848 // If there was no overflow, we can convert to Smi.
849 __ SmiTag(return_value);
850 }
851 }
852 __ jmp(&return_value_done);
853
854 // Handle the conversion of the I32 return value to HeapNumber when it cannot
855 // be a smi.
856 __ bind(&to_heapnumber);
857
858 PrepareForWasmToJsConversionBuiltinCall(
859 masm, return_count, result_index, current_return_slot,
860 valuetypes_array_ptr, wasm_instance, fixed_array, jsarray);
861 __ Call(BUILTIN_CODE(masm->isolate(), WasmInt32ToHeapNumber),
863 RestoreAfterWasmToJsConversionBuiltinCall(
864 masm, jsarray, fixed_array, wasm_instance, valuetypes_array_ptr,
865 current_return_slot, result_index, return_count);
866 __ jmp(&return_value_done);
867
868 __ bind(&return_kWasmI64);
869 __ Ldr(return_value, MemOperand(current_return_slot, 0));
870 __ Add(current_return_slot, current_return_slot, Immediate(sizeof(int64_t)));
871 PrepareForWasmToJsConversionBuiltinCall(
872 masm, return_count, result_index, current_return_slot,
873 valuetypes_array_ptr, wasm_instance, fixed_array, jsarray);
874 __ Call(BUILTIN_CODE(masm->isolate(), I64ToBigInt), RelocInfo::CODE_TARGET);
875 RestoreAfterWasmToJsConversionBuiltinCall(
876 masm, jsarray, fixed_array, wasm_instance, valuetypes_array_ptr,
877 current_return_slot, result_index, return_count);
878 __ jmp(&return_value_done);
879
880 __ bind(&return_kWasmF32);
881 __ Ldr(s0, MemOperand(current_return_slot, 0));
882 __ Add(current_return_slot, current_return_slot, Immediate(sizeof(float)));
883 PrepareForWasmToJsConversionBuiltinCall(
884 masm, return_count, result_index, current_return_slot,
885 valuetypes_array_ptr, wasm_instance, fixed_array, jsarray);
886 __ Call(BUILTIN_CODE(masm->isolate(), WasmFloat32ToNumber),
888 RestoreAfterWasmToJsConversionBuiltinCall(
889 masm, jsarray, fixed_array, wasm_instance, valuetypes_array_ptr,
890 current_return_slot, result_index, return_count);
891 __ jmp(&return_value_done);
892
893 __ bind(&return_kWasmF64);
894 __ Ldr(d0, MemOperand(current_return_slot, 0));
895 __ Add(current_return_slot, current_return_slot, Immediate(sizeof(double)));
896 PrepareForWasmToJsConversionBuiltinCall(
897 masm, return_count, result_index, current_return_slot,
898 valuetypes_array_ptr, wasm_instance, fixed_array, jsarray);
899 __ Call(BUILTIN_CODE(masm->isolate(), WasmFloat64ToNumber),
901 RestoreAfterWasmToJsConversionBuiltinCall(
902 masm, jsarray, fixed_array, wasm_instance, valuetypes_array_ptr,
903 current_return_slot, result_index, return_count);
904 __ jmp(&return_value_done);
905
906 __ bind(&return_kWasmRef);
907 // Make sure slot for ref args are 64-bit aligned.
908 __ And(scratch, current_return_slot, Immediate(0x04));
909 __ Add(current_return_slot, current_return_slot, scratch);
910 __ Ldr(return_value, MemOperand(current_return_slot, 0));
911 __ Add(current_return_slot, current_return_slot,
912 Immediate(kSystemPointerSize));
913
914 Label next_return_value;
915
916 __ bind(&return_value_done);
917 __ Add(valuetypes_array_ptr, valuetypes_array_ptr, Immediate(kValueTypeSize));
918 __ cmp(fixed_array, xzr);
919 __ B(&next_return_value, eq);
920
921 // Store result into JSArray.
922 DEFINE_REG(array_items);
923 __ Add(array_items, fixed_array,
925 __ StoreTaggedField(return_value, MemOperand(array_items, result_index, LSL,
927
928 Label skip_write_barrier;
929 // See the arm64 version of LiftoffAssembler::StoreTaggedPointer.
930 __ CheckPageFlag(array_items,
932 &skip_write_barrier);
933 __ JumpIfSmi(return_value, &skip_write_barrier);
934 __ CheckPageFlag(return_value, MemoryChunk::kPointersToHereAreInterestingMask,
935 eq, &skip_write_barrier);
936 PrepareForWasmToJsConversionBuiltinCall(
937 masm, return_count, result_index, current_return_slot,
938 valuetypes_array_ptr, wasm_instance, fixed_array, jsarray, false);
939 Register offset = return_count;
940 __ Mov(offset, Immediate(OFFSET_OF_DATA_START(FixedArray) - kHeapObjectTag));
941 __ Add(offset, offset, Operand(result_index, LSL, kTaggedSizeLog2));
942 __ CallRecordWriteStubSaveRegisters(fixed_array, Operand(offset),
944 RestoreAfterWasmToJsConversionBuiltinCall(
945 masm, jsarray, fixed_array, wasm_instance, valuetypes_array_ptr,
946 current_return_slot, result_index, return_count);
947 __ bind(&skip_write_barrier);
948
949 __ bind(&next_return_value);
950 __ Add(result_index, result_index, 1);
951 __ cmp(result_index, return_count);
952 __ B(&convert_return_value, lt);
953
954 __ bind(&all_results_conversion_done);
955 ASSIGN_REG(param_count);
956 __ Ldr(param_count, MemOperand(fp, kParamCountOffset)); // ???
957
958 Label do_return;
959 __ cmp(fixed_array, xzr);
960 __ B(&do_return, eq);
961 // The result is jsarray.
962 __ Mov(return_value, jsarray);
963
964 __ bind(&do_return);
965 // Calculate the number of parameters we have to pop off the stack. This
966 // number is max(in_param_count, param_count).
967 DEFINE_REG(in_param_count);
968 __ Ldr(in_param_count, MemOperand(fp, kInParamCountOffset));
969 __ cmp(param_count, in_param_count);
970 __ csel(param_count, in_param_count, param_count, lt);
971
972 // -------------------------------------------
973 // Deconstruct the stack frame.
974 // -------------------------------------------
975 __ LeaveFrame(StackFrame::JS_TO_WASM);
976
977 // We have to remove the caller frame slots:
978 // - JS arguments
979 // - the receiver
980 // and transfer the control to the return address (the return address is
981 // expected to be on the top of the stack).
982 // We cannot use just the ret instruction for this, because we cannot pass the
983 // number of slots to remove in a Register as an argument.
984 __ DropArguments(param_count);
985 __ Ret(lr);
986}
987
988void Builtins::Generate_WasmInterpreterCWasmEntry(MacroAssembler* masm) {
989 Label invoke, handler_entry, exit;
990
991 __ EnterFrame(StackFrame::C_WASM_ENTRY);
992
993 // Space to store c_entry_fp and current sp (used by exception handler).
994 __ Push(xzr, xzr);
995
996 {
997 NoRootArrayScope no_root_array(masm);
998
999 // Push callee saved registers.
1000 __ Push(d14, d15);
1001 __ Push(d12, d13);
1002 __ Push(d10, d11);
1003 __ Push(d8, d9);
1004 __ Push(x27, x28);
1005 __ Push(x25, x26);
1006 __ Push(x23, x24);
1007 __ Push(x21, x22);
1008 __ Push(x19, x20);
1009
1010 // Set up the reserved register for 0.0.
1011 __ Fmov(fp_zero, 0.0);
1012
1013 // Initialize the root register.
1014 __ Mov(kRootRegister, x2);
1015
1016#ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE
1017 // Initialize the pointer cage base register.
1018 __ LoadRootRelative(kPtrComprCageBaseRegister,
1019 IsolateData::cage_base_offset());
1020#endif
1021 }
1022
1023 {
1024 UseScratchRegisterScope temps(masm);
1025 Register scratch = temps.AcquireX();
1026 __ Mov(scratch, sp);
1027 __ Str(scratch,
1028 MemOperand(fp, WasmInterpreterCWasmEntryConstants::kSPFPOffset));
1029 }
1030
1031 __ Mov(x11, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress,
1032 masm->isolate()));
1033 __ Ldr(x10, MemOperand(x11)); // x10 = C entry FP.
1034
1035 __ Str(x10,
1036 MemOperand(fp, WasmInterpreterCWasmEntryConstants::kCEntryFPOffset));
1037
1038 // Jump to a faked try block that does the invoke, with a faked catch
1039 // block that sets the pending exception.
1040 __ B(&invoke);
1041
1042 // Prevent the constant pool from being emitted between the record of the
1043 // handler_entry position and the first instruction of the sequence here.
1044 // There is no risk because Assembler::Emit() emits the instruction before
1045 // checking for constant pool emission, but we do not want to depend on
1046 // that.
1047 {
1048 Assembler::BlockPoolsScope block_pools(masm);
1049
1050 __ BindExceptionHandler(&handler_entry);
1051
1052 // Store the current pc as the handler offset. It's used later to create the
1053 // handler table.
1054 masm->isolate()->builtins()->SetCWasmInterpreterEntryHandlerOffset(
1055 handler_entry.pos());
1056 }
1057 __ B(&exit);
1058
1059 // Invoke: Link this frame into the handler chain.
1060 __ Bind(&invoke);
1061
1062 // Link the current handler as the next handler.
1063 __ Mov(x11, ExternalReference::Create(IsolateAddressId::kHandlerAddress,
1064 masm->isolate()));
1065 __ Ldr(x10, MemOperand(x11));
1066 __ Push(padreg, x10);
1067
1068 // Set this new handler as the current one.
1069 {
1070 UseScratchRegisterScope temps(masm);
1071 Register scratch = temps.AcquireX();
1072 __ Mov(scratch, sp);
1073 __ Str(scratch, MemOperand(x11));
1074 }
1075
1076 // Invoke the JS function through the GenericWasmToJSInterpreterWrapper.
1077 __ Call(BUILTIN_CODE(masm->isolate(), GenericWasmToJSInterpreterWrapper),
1079
1080 // Pop the stack handler and unlink this frame from the handler chain.
1082 "Unexpected offset for StackHandlerConstants::kNextOffset");
1083 __ Pop(x10, padreg);
1084 __ Mov(x11, ExternalReference::Create(IsolateAddressId::kHandlerAddress,
1085 masm->isolate()));
1087 __ Str(x10, MemOperand(x11));
1088
1089 __ Bind(&exit);
1090
1091 // Pop callee saved registers.
1092 __ Pop(x20, x19);
1093 __ Pop(x22, x21);
1094 __ Pop(x24, x23);
1095 __ Pop(x26, x25);
1096 __ Pop(x28, x27);
1097 __ Pop(d9, d8);
1098 __ Pop(d11, d10);
1099 __ Pop(d13, d12);
1100 __ Pop(d15, d14);
1101
1102 // Deconstruct the stack frame.
1103 __ LeaveFrame(StackFrame::C_WASM_ENTRY);
1104 __ Ret();
1105}
1106
1107void Builtins::Generate_GenericWasmToJSInterpreterWrapper(
1108 MacroAssembler* masm) {
1109 auto regs = RegisterAllocator::WithAllocatableGeneralRegisters();
1110
1111 DEFINE_PINNED(target_js_function, x0);
1112 DEFINE_PINNED(packed_args, x1);
1113 DEFINE_PINNED(signature, x3);
1114 DEFINE_PINNED(callable, x5);
1115
1116 // Set up the stackframe.
1117 __ EnterFrame(StackFrame::WASM_TO_JS);
1118
1119 // -------------------------------------------
1120 // Compute offsets and prepare for GC.
1121 // -------------------------------------------
1122 // GenericJSToWasmInterpreterWrapperFrame:
1123 // sp = fp-N receiver ^
1124 // ... JS arg 0 |
1125 // ... ... | Tagged
1126 // ... JS arg n-1 | objects
1127 // ... (padding if num args is odd) |
1128 // fp-0x60 context |
1129 // fp-0x58 callable / call result v
1130 // -------------------------------------------
1131 // fp-0x50 current_param_offset/current_result_offset
1132 // fp-0x48 valuetypes_array_ptr
1133 //
1134 // fp-0x40 param_index/return_index
1135 // fp-0x38 signature
1136 //
1137 // fp-0x30 param_count
1138 // fp-0x28 return_count
1139 //
1140 // fp-0x20 packed_array
1141 // fp-0x18 GC_SP (not used on arm64)
1142 //
1143 // fp-0x10 GCScanSlotLimit
1144 // fp-0x08 Marker(StackFrame::WASM_TO_JS)
1145 //
1146 // fp Old fp
1147 // fp+0x08 return address
1148
1149 static_assert(WasmToJSInterpreterFrameConstants::kGCSPOffset ==
1150 WasmToJSInterpreterFrameConstants::kGCScanSlotLimitOffset -
1152 constexpr int kPackedArrayOffset =
1153 WasmToJSInterpreterFrameConstants::kGCSPOffset - kSystemPointerSize;
1154 constexpr int kReturnCountOffset = kPackedArrayOffset - kSystemPointerSize;
1155 constexpr int kParamCountOffset = kReturnCountOffset - kSystemPointerSize;
1156 constexpr int kSignatureOffset = kParamCountOffset - kSystemPointerSize;
1157 constexpr int kParamIndexOffset = kSignatureOffset - kSystemPointerSize;
1158 // Reuse this slot when iterating over return values.
1159 constexpr int kResultIndexOffset = kParamIndexOffset;
1160 constexpr int kValueTypesArrayStartOffset =
1161 kParamIndexOffset - kSystemPointerSize;
1162 constexpr int kCurrentParamOffset =
1163 kValueTypesArrayStartOffset - kSystemPointerSize;
1164 // Reuse this slot when iterating over return values.
1165 constexpr int kCurrentResultOffset = kCurrentParamOffset;
1166 constexpr int kCallableOffset = kCurrentParamOffset - kSystemPointerSize;
1167 constexpr int kContextOffset = kCallableOffset - kSystemPointerSize;
1168 constexpr int kNumSpillSlots =
1169 (WasmToJSInterpreterFrameConstants::kGCScanSlotLimitOffset -
1170 kCurrentParamOffset) /
1172 static_assert((kNumSpillSlots % 2) == 0); // 16-bytes aligned.
1173
1174 __ Sub(sp, sp, Immediate(kNumSpillSlots * kSystemPointerSize));
1175
1176 __ Str(packed_args, MemOperand(fp, kPackedArrayOffset));
1177
1178 // Not used on arm64.
1179 __ Str(xzr, MemOperand(fp, WasmToJSInterpreterFrameConstants::kGCSPOffset));
1180
1181 __ Str(xzr,
1182 MemOperand(fp,
1183 WasmToJSInterpreterFrameConstants::kGCScanSlotLimitOffset));
1184
1185 DEFINE_REG(shared_function_info);
1186 __ LoadTaggedField(
1187 shared_function_info,
1188 MemOperand(
1189 target_js_function,
1191
1192 // Set the context of the function; the call has to run in the function
1193 // context.
1194 DEFINE_REG(context);
1195 __ LoadTaggedField(
1196 context, FieldMemOperand(target_js_function, JSFunction::kContextOffset));
1197 __ Mov(cp, context);
1198 __ Str(cp, MemOperand(fp, kContextOffset));
1199
1200 // Load global receiver if sloppy else use undefined.
1201 Label receiver_undefined;
1202 Label calculate_js_function_arity;
1204 DEFINE_REG(flags);
1205 __ Ldr(flags, FieldMemOperand(shared_function_info,
1206 SharedFunctionInfo::kFlagsOffset));
1207 __ Tst(flags, Immediate(SharedFunctionInfo::IsNativeBit::kMask |
1208 SharedFunctionInfo::IsStrictBit::kMask));
1209 FREE_REG(flags);
1210 __ B(&receiver_undefined, ne);
1211 __ LoadGlobalProxy(receiver);
1212 __ B(&calculate_js_function_arity);
1213
1214 __ bind(&receiver_undefined);
1215 __ LoadRoot(receiver, RootIndex::kUndefinedValue);
1216
1217 __ bind(&calculate_js_function_arity);
1218
1219 // Load values from the signature.
1220 DEFINE_REG(return_count);
1221 DEFINE_REG(param_count);
1222 __ Str(signature, MemOperand(fp, kSignatureOffset));
1223 Register valuetypes_array_ptr = signature;
1224 LoadFromSignature(masm, valuetypes_array_ptr, return_count, param_count);
1225 __ Str(param_count, MemOperand(fp, kParamCountOffset));
1226 FREE_REG(shared_function_info);
1227
1228 // Make room to pass the args and the receiver.
1229 DEFINE_REG(array_size);
1230 DEFINE_REG(scratch);
1231
1232 // Points to the end of the stack frame area that contains tagged objects
1233 // that need to be visited during GC.
1234 __ Mov(scratch, sp);
1235 __ Str(scratch,
1236 MemOperand(fp,
1237 WasmToJSInterpreterFrameConstants::kGCScanSlotLimitOffset));
1238
1239 // Store callable and context.
1240 __ Push(callable, cp);
1241 __ Add(array_size, param_count, Immediate(1));
1242 // Ensure that the array is 16-bytes aligned.
1243 __ Add(scratch, array_size, Immediate(1));
1244 __ And(array_size, scratch, Immediate(-2));
1245 __ Sub(sp, sp, Operand(array_size, LSL, kSystemPointerSizeLog2));
1246
1247 // Make sure that the padding slot (if present) is reset to zero. The other
1248 // slots will be initialized with the arguments.
1249 __ Sub(scratch, array_size, Immediate(1));
1250 __ Str(xzr, MemOperand(sp, scratch, LSL, kSystemPointerSizeLog2));
1251 FREE_REG(array_size);
1252
1253 DEFINE_REG(param_index);
1254 __ Mov(param_index, xzr);
1255
1256 // Store the receiver at the top of the stack.
1257 __ Str(receiver, MemOperand(sp, 0));
1258
1259 // -------------------------------------------
1260 // Store signature-related values to the stack.
1261 // -------------------------------------------
1262 // We store values on the stack to restore them after function calls.
1263 __ Str(return_count, MemOperand(fp, kReturnCountOffset));
1264 __ Str(valuetypes_array_ptr, MemOperand(fp, kValueTypesArrayStartOffset));
1265
1266 Label prepare_for_js_call;
1267 __ Cmp(param_count, 0);
1268 // If we have 0 params: jump through parameter handling.
1269 __ B(&prepare_for_js_call, eq);
1270
1271 // Loop through the params starting with the first.
1272 DEFINE_REG(current_param_slot_offset);
1273 __ Mov(current_param_slot_offset, Immediate(0));
1274
1275 // We have to check the types of the params. The ValueType array contains
1276 // first the return then the param types.
1277
1278 // Set the ValueType array pointer to point to the first parameter.
1279 constexpr int kValueTypeSize = sizeof(wasm::ValueType);
1280 static_assert(kValueTypeSize == 4);
1281 const int32_t kValueTypeSizeLog2 = log2(kValueTypeSize);
1282 __ Add(valuetypes_array_ptr, valuetypes_array_ptr,
1283 Operand(return_count, LSL, kValueTypeSizeLog2));
1284 DEFINE_REG_W(valuetype);
1285
1286 // -------------------------------------------
1287 // Copy reference type params first and initialize the stack for JS arguments.
1288 // -------------------------------------------
1289
1290 // Heap pointers for ref type values in packed_args can be invalidated if GC
1291 // is triggered when converting wasm numbers to JS numbers and allocating
1292 // heap numbers. So, we have to move them to the stack first.
1293 Register param = target_js_function; // x0
1294 {
1295 Label loop_copy_param_ref, load_ref_param, set_and_move;
1296
1297 __ bind(&loop_copy_param_ref);
1298 __ Ldr(valuetype, MemOperand(valuetypes_array_ptr, 0));
1299 __ Tst(valuetype, Immediate(1));
1300 __ B(&load_ref_param, ne);
1301
1302 // Initialize non-ref type slots to zero since they can be visited by GC
1303 // when converting wasm numbers into heap numbers.
1304 __ Mov(param, Smi::zero());
1305
1306 Label inc_param_32bit;
1307 __ Cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
1308 __ B(&inc_param_32bit, eq);
1309 __ Cmp(valuetype, Immediate(wasm::kWasmF32.raw_bit_field()));
1310 __ B(&inc_param_32bit, eq);
1311
1312 Label inc_param_64bit;
1313 __ Cmp(valuetype, Immediate(wasm::kWasmI64.raw_bit_field()));
1314 __ B(&inc_param_64bit, eq);
1315 __ Cmp(valuetype, Immediate(wasm::kWasmF64.raw_bit_field()));
1316 __ B(&inc_param_64bit, eq);
1317
1318 // Invalid type. Wasm cannot pass Simd arguments to JavaScript.
1319 __ DebugBreak();
1320
1321 __ bind(&inc_param_32bit);
1322 __ Add(current_param_slot_offset, current_param_slot_offset,
1323 Immediate(sizeof(int32_t)));
1324 __ B(&set_and_move);
1325
1326 __ bind(&inc_param_64bit);
1327 __ Add(current_param_slot_offset, current_param_slot_offset,
1328 Immediate(sizeof(int64_t)));
1329 __ B(&set_and_move);
1330
1331 __ bind(&load_ref_param);
1332 // No need to align packed_args for ref values in wasm-to-js, because the
1333 // alignment is only required for GC code that visits the stack, and in this
1334 // case we are storing into the stack only heap (or Smi) objects, always
1335 // aligned.
1336 __ Ldr(param, MemOperand(packed_args, current_param_slot_offset));
1337 __ Add(current_param_slot_offset, current_param_slot_offset,
1338 Immediate(kSystemPointerSize));
1339
1340 __ bind(&set_and_move);
1341 __ Add(param_index, param_index, 1);
1342 // Pre-increment param_index to skip receiver slot.
1343 __ Str(param, MemOperand(sp, param_index, LSL, kSystemPointerSizeLog2));
1344 __ Add(valuetypes_array_ptr, valuetypes_array_ptr,
1345 Immediate(kValueTypeSize));
1346 __ Cmp(param_index, param_count);
1347 __ B(&loop_copy_param_ref, lt);
1348 }
1349
1350 // Reset pointers for the second param conversion loop.
1351 __ Ldr(return_count, MemOperand(fp, kReturnCountOffset));
1352 __ Ldr(valuetypes_array_ptr, MemOperand(fp, kValueTypesArrayStartOffset));
1353 __ Add(valuetypes_array_ptr, valuetypes_array_ptr,
1354 Operand(return_count, LSL, kValueTypeSizeLog2));
1355 __ Mov(current_param_slot_offset, xzr);
1356 __ Mov(param_index, xzr);
1357
1358 // -------------------------------------------
1359 // Param evaluation loop.
1360 // -------------------------------------------
1361 Label loop_through_params;
1362 __ bind(&loop_through_params);
1363
1364 __ Ldr(valuetype, MemOperand(valuetypes_array_ptr, 0));
1365
1366 // -------------------------------------------
1367 // Param conversion.
1368 // -------------------------------------------
1369 // If param is a Smi we can easily convert it. Otherwise we'll call a builtin
1370 // for conversion.
1371 Label param_conversion_done, check_ref_param, skip_ref_param, convert_param;
1372 __ Cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
1373 __ B(&check_ref_param, ne);
1374
1375 // I32 param: change to Smi.
1376 __ Ldr(param.W(), MemOperand(packed_args, current_param_slot_offset));
1377
1378 // If pointer compression is disabled, we can convert to a smi.
1379 if (SmiValuesAre32Bits()) {
1380 __ SmiTag(param);
1381 } else {
1382 // Double the return value to test if it can be a Smi.
1383 __ Adds(wzr, param.W(), param.W());
1384 // If there was overflow, convert the return value to a HeapNumber.
1385 __ B(&convert_param, vs);
1386 // If there was no overflow, we can convert to Smi.
1387 __ SmiTag(param);
1388 }
1389
1390 // Place the param into the proper slot.
1391 // Pre-increment param_index to skip the receiver slot.
1392 __ Add(param_index, param_index, 1);
1393 __ Str(param, MemOperand(sp, param_index, LSL, kSystemPointerSizeLog2));
1394 __ Add(current_param_slot_offset, current_param_slot_offset, sizeof(int32_t));
1395
1396 __ B(&param_conversion_done);
1397
1398 // Skip Ref params. We already copied reference params in the first loop.
1399 __ bind(&check_ref_param);
1400 __ Tst(valuetype, Immediate(1));
1401 __ B(&convert_param, eq);
1402
1403 __ bind(&skip_ref_param);
1404 __ Add(param_index, param_index, 1);
1405 __ Add(current_param_slot_offset, current_param_slot_offset,
1406 Immediate(kSystemPointerSize));
1407 __ B(&param_conversion_done);
1408
1409 // -------------------------------------------------
1410 // Param conversion builtins (Wasm type -> JS type).
1411 // -------------------------------------------------
1412 __ bind(&convert_param);
1413
1414 // Prepare for builtin call.
1415
1416 // Need to specify how many heap objects, that should be scanned by GC, are
1417 // on the top of the stack. (Only the context).
1418 // The builtin expects the parameter to be in register param = rax.
1419
1420 __ Str(param_index, MemOperand(fp, kParamIndexOffset));
1421 __ Str(valuetypes_array_ptr, MemOperand(fp, kValueTypesArrayStartOffset));
1422 __ Str(current_param_slot_offset, MemOperand(fp, kCurrentParamOffset));
1423
1424 Label param_kWasmI32_not_smi;
1425 Label param_kWasmI64;
1426 Label param_kWasmF32;
1427 Label param_kWasmF64;
1428 Label finish_param_conversion;
1429
1430 __ Cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
1431 __ B(&param_kWasmI32_not_smi, eq);
1432 __ Cmp(valuetype, Immediate(wasm::kWasmI64.raw_bit_field()));
1433 __ B(&param_kWasmI64, eq);
1434 __ Cmp(valuetype, Immediate(wasm::kWasmF32.raw_bit_field()));
1435 __ B(&param_kWasmF32, eq);
1436 __ Cmp(valuetype, Immediate(wasm::kWasmF64.raw_bit_field()));
1437 __ B(&param_kWasmF64, eq);
1438
1439 // Invalid type. Wasm cannot pass Simd arguments to JavaScript.
1440 __ DebugBreak();
1441
1442 Register increment = scratch;
1443 __ bind(&param_kWasmI32_not_smi);
1444 __ Call(BUILTIN_CODE(masm->isolate(), WasmInt32ToHeapNumber),
1446 // Param is the result of the builtin.
1447 __ Mov(increment, Immediate(sizeof(int32_t)));
1448 __ jmp(&finish_param_conversion);
1449
1450 __ bind(&param_kWasmI64);
1451 __ Ldr(param, MemOperand(packed_args, current_param_slot_offset));
1452 __ Call(BUILTIN_CODE(masm->isolate(), I64ToBigInt), RelocInfo::CODE_TARGET);
1453 __ Mov(increment, Immediate(sizeof(int64_t)));
1454 __ jmp(&finish_param_conversion);
1455
1456 __ bind(&param_kWasmF32);
1457 __ Ldr(v0, MemOperand(packed_args, current_param_slot_offset));
1458 __ Call(BUILTIN_CODE(masm->isolate(), WasmFloat32ToNumber),
1460 __ Mov(increment, Immediate(sizeof(float)));
1461 __ jmp(&finish_param_conversion);
1462
1463 __ bind(&param_kWasmF64);
1464 __ Ldr(d0, MemOperand(packed_args, current_param_slot_offset));
1465 __ Call(BUILTIN_CODE(masm->isolate(), WasmFloat64ToNumber),
1467 __ Mov(increment, Immediate(sizeof(double)));
1468
1469 // Restore after builtin call.
1470 __ bind(&finish_param_conversion);
1471
1472 __ Ldr(current_param_slot_offset, MemOperand(fp, kCurrentParamOffset));
1473 __ Add(current_param_slot_offset, current_param_slot_offset, increment);
1474 __ Ldr(valuetypes_array_ptr, MemOperand(fp, kValueTypesArrayStartOffset));
1475 __ Ldr(param_index, MemOperand(fp, kParamIndexOffset));
1476 __ Ldr(packed_args, MemOperand(fp, kPackedArrayOffset));
1477 __ Ldr(param_count, MemOperand(fp, kParamCountOffset));
1478
1479 __ Add(param_index, param_index, 1);
1480 __ Str(param, MemOperand(sp, param_index, LSL, kSystemPointerSizeLog2));
1481
1482 // -------------------------------------------
1483 // Param conversion done.
1484 // -------------------------------------------
1485 __ bind(&param_conversion_done);
1486
1487 __ Add(valuetypes_array_ptr, valuetypes_array_ptr, Immediate(kValueTypeSize));
1488 __ Cmp(param_index, param_count);
1489 __ B(&loop_through_params, lt);
1490
1491 // -------------------------------------------
1492 // Prepare for the function call.
1493 // -------------------------------------------
1494 __ bind(&prepare_for_js_call);
1495
1496 // Reset thread_in_wasm_flag.
1497 __ Ldr(scratch, MemOperand(kRootRegister,
1499 __ Str(wzr, MemOperand(scratch, 0)); // 32 bit.
1500
1501 regs.ResetExcept(param, packed_args, valuetypes_array_ptr, context,
1502 return_count, valuetype, scratch);
1503
1504 // -------------------------------------------
1505 // Call the JS function.
1506 // -------------------------------------------
1507 // Call_ReceiverIsAny expects the arguments in the stack in this order:
1508 // sp + (n * 0x08) JS arg n-1
1509 // ... ...
1510 // sp + 0x08 JS arg 0
1511 // sp Receiver
1512 //
1513 // It also expects two arguments passed in registers:
1514 // x0: number of arguments + 1 (receiver)
1515 // x1: target (JSFunction|JSBoundFunction|...)
1516
1517 // x0: Receiver.
1518 __ Ldr(x0, MemOperand(fp, kParamCountOffset));
1519 __ Add(x0, x0, 1); // Add 1 to count receiver.
1520
1521 // x1: callable.
1522 __ Ldr(kJSFunctionRegister, MemOperand(fp, kCallableOffset));
1523
1524 __ Call(BUILTIN_CODE(masm->isolate(), Call_ReceiverIsAny),
1526
1527 __ Ldr(cp, MemOperand(fp, kContextOffset));
1528
1529 // The JS function returns its result in register x0.
1530 Register return_reg = kReturnRegister0;
1531
1532 // No slots to visit during GC.
1533 __ Mov(scratch, sp);
1534 __ Str(scratch,
1535 MemOperand(fp,
1536 WasmToJSInterpreterFrameConstants::kGCScanSlotLimitOffset));
1537
1538 // -------------------------------------------
1539 // Return handling.
1540 // -------------------------------------------
1541 __ Ldr(return_count, MemOperand(fp, kReturnCountOffset));
1542 __ Ldr(packed_args, MemOperand(fp, kPackedArrayOffset));
1543 __ Ldr(signature, MemOperand(fp, kSignatureOffset));
1544 __ Ldr(valuetypes_array_ptr,
1546
1547 DEFINE_REG(result_index);
1548 __ Mov(result_index, xzr);
1549 DEFINE_REG(current_result_offset);
1550 __ Mov(current_result_offset, xzr);
1551
1552 // If we have return values, convert them from JS types back to Wasm types.
1553 Label convert_return;
1554 Label return_done;
1555 Label all_done;
1556 Label loop_copy_return_refs;
1557 __ cmp(return_count, Immediate(1));
1558 __ B(&all_done, lt);
1559 __ B(&convert_return, eq);
1560
1561 // We have multiple results. Convert the result into a FixedArray.
1562 DEFINE_REG(fixed_array);
1563 __ Mov(fixed_array, xzr);
1564
1565 // The builtin expects three args:
1566 // x0: object.
1567 // x1: return_count as Smi.
1568 // x27 (cp): context.
1569 __ Ldr(x1, MemOperand(fp, kReturnCountOffset));
1570 __ Add(x1, x1, x1);
1571
1572 // Two tagged objects at the top of the stack (the context and the result,
1573 // which we store at the place of the function object we called).
1574 static_assert(
1575 kCurrentParamOffset == kContextOffset + 0x10,
1576 "Expected two (tagged) slots between 'context' and 'current_param'.");
1577 __ Str(x0, MemOperand(fp, kCallableOffset));
1578 // Here sp points to the Context spilled slot: [fp - kContextOffset].
1579 __ Add(scratch, sp, Immediate(kSystemPointerSize * 2));
1580 __ Str(scratch,
1581 MemOperand(fp,
1582 WasmToJSInterpreterFrameConstants::kGCScanSlotLimitOffset));
1583
1584 // We can have a GC here!
1585 __ Call(BUILTIN_CODE(masm->isolate(), IterableToFixedArrayForWasm),
1587 __ Mov(fixed_array, kReturnRegister0);
1588
1589 // Store fixed_array at the second top of the stack (in place of callable).
1590 __ Str(fixed_array, MemOperand(sp, kSystemPointerSize));
1591 __ Add(scratch, sp, Immediate(2 * kSystemPointerSize));
1592 __ Str(scratch,
1593 MemOperand(fp,
1594 WasmToJSInterpreterFrameConstants::kGCScanSlotLimitOffset));
1595
1596 __ Ldr(return_count, MemOperand(fp, kReturnCountOffset));
1597 __ Ldr(packed_args, MemOperand(fp, kPackedArrayOffset));
1598 __ Ldr(signature, MemOperand(fp, kSignatureOffset));
1599 __ Ldr(valuetypes_array_ptr,
1601 __ Mov(result_index, xzr);
1602 __ Mov(current_result_offset, xzr);
1603
1604 __ Add(scratch, fixed_array,
1605 OFFSET_OF_DATA_START(FixedArray) - kHeapObjectTag);
1606 __ LoadTaggedField(return_reg,
1607 MemOperand(scratch, result_index, LSL, kTaggedSizeLog2));
1608
1609 // -------------------------------------------
1610 // Return conversions (JS type -> Wasm type).
1611 // -------------------------------------------
1612 __ bind(&convert_return);
1613
1614 // Save registers in the stack before the builtin call.
1615 __ Str(current_result_offset, MemOperand(fp, kCurrentResultOffset));
1616 __ Str(result_index, MemOperand(fp, kResultIndexOffset));
1617 __ Str(valuetypes_array_ptr, MemOperand(fp, kValueTypesArrayStartOffset));
1618
1619 // The builtin expects the parameter to be in register param = x0.
1620
1621 // The first valuetype of the array is the return's valuetype.
1622 __ Ldr(valuetype, MemOperand(valuetypes_array_ptr, 0));
1623
1624 Label return_kWasmI32;
1625 Label return_kWasmI32_not_smi;
1626 Label return_kWasmI64;
1627 Label return_kWasmF32;
1628 Label return_kWasmF64;
1629 Label return_kWasmRef;
1630
1631 // Prepare for builtin call.
1632
1633 __ Cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
1634 __ B(&return_kWasmI32, eq);
1635 __ Cmp(valuetype, Immediate(wasm::kWasmI64.raw_bit_field()));
1636 __ B(&return_kWasmI64, eq);
1637 __ Cmp(valuetype, Immediate(wasm::kWasmF32.raw_bit_field()));
1638 __ B(&return_kWasmF32, eq);
1639 __ Cmp(valuetype, Immediate(wasm::kWasmF64.raw_bit_field()));
1640 __ B(&return_kWasmF64, eq);
1641
1642 __ Tst(valuetype, Immediate(1));
1643 __ B(&return_kWasmRef, ne);
1644
1645 // Invalid type. JavaScript cannot return Simd results to WebAssembly.
1646 __ DebugBreak();
1647
1648 __ bind(&return_kWasmI32);
1649 __ JumpIfNotSmi(return_reg, &return_kWasmI32_not_smi);
1650 // Change the param from Smi to int32.
1651 __ SmiUntag(return_reg);
1652 __ AssertZeroExtended(return_reg);
1653 __ Ldr(packed_args, MemOperand(fp, kPackedArrayOffset));
1654 __ Str(return_reg.W(), MemOperand(packed_args, current_result_offset));
1655 __ Add(current_result_offset, current_result_offset,
1656 Immediate(sizeof(int32_t)));
1657 __ jmp(&return_done);
1658
1659 __ bind(&return_kWasmI32_not_smi);
1660 __ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedNonSmiToInt32),
1662 __ AssertZeroExtended(return_reg);
1663 __ Ldr(packed_args, MemOperand(fp, kPackedArrayOffset));
1664 __ Ldr(current_result_offset, MemOperand(fp, kCurrentResultOffset));
1665 __ Str(return_reg.W(), MemOperand(packed_args, current_result_offset));
1666 __ Add(current_result_offset, current_result_offset,
1667 Immediate(sizeof(int32_t)));
1668 __ jmp(&return_done);
1669
1670 __ bind(&return_kWasmI64);
1671 __ Call(BUILTIN_CODE(masm->isolate(), BigIntToI64), RelocInfo::CODE_TARGET);
1672 __ Ldr(packed_args, MemOperand(fp, kPackedArrayOffset));
1673 __ Ldr(current_result_offset, MemOperand(fp, kCurrentResultOffset));
1674 __ Str(return_reg, MemOperand(packed_args, current_result_offset));
1675 __ Add(current_result_offset, current_result_offset,
1676 Immediate(sizeof(int64_t)));
1677 __ jmp(&return_done);
1678
1679 __ bind(&return_kWasmF32);
1680 __ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedToFloat32),
1682 __ Ldr(packed_args, MemOperand(fp, kPackedArrayOffset));
1683 __ Ldr(current_result_offset, MemOperand(fp, kCurrentResultOffset));
1684 __ Str(kFPReturnRegister0, MemOperand(packed_args, current_result_offset));
1685 __ Add(current_result_offset, current_result_offset,
1686 Immediate(sizeof(float)));
1687 __ jmp(&return_done);
1688
1689 __ bind(&return_kWasmF64);
1690 __ Call(BUILTIN_CODE(masm->isolate(), WasmTaggedToFloat64),
1692 __ Ldr(packed_args, MemOperand(fp, kPackedArrayOffset));
1693 __ Ldr(current_result_offset, MemOperand(fp, kCurrentResultOffset));
1694 __ Str(kFPReturnRegister0, MemOperand(packed_args, current_result_offset));
1695 __ Add(current_result_offset, current_result_offset,
1696 Immediate(sizeof(double)));
1697 __ jmp(&return_done);
1698
1699 __ bind(&return_kWasmRef);
1700 __ Ldr(packed_args, MemOperand(fp, kPackedArrayOffset));
1701 __ Str(return_reg,
1702 MemOperand(packed_args, result_index, LSL, kSystemPointerSizeLog2));
1703 __ Add(current_result_offset, current_result_offset,
1704 Immediate(sizeof(double)));
1705
1706 // A result converted.
1707 __ bind(&return_done);
1708
1709 // Restore after builtin call
1710 __ Ldr(cp, MemOperand(fp, kContextOffset));
1711 __ Ldr(fixed_array, MemOperand(sp, kSystemPointerSize));
1712 __ Ldr(valuetypes_array_ptr, MemOperand(fp, kValueTypesArrayStartOffset));
1713 __ Add(valuetypes_array_ptr, valuetypes_array_ptr, Immediate(kValueTypeSize));
1714 __ Ldr(result_index, MemOperand(fp, kResultIndexOffset));
1715 __ Add(result_index, result_index, Immediate(1));
1716 __ Ldr(scratch, MemOperand(fp, kReturnCountOffset));
1717 __ cmp(result_index, scratch); // result_index == return_count?
1718 __ B(&loop_copy_return_refs, ge);
1719
1720 __ Add(scratch, fixed_array,
1721 OFFSET_OF_DATA_START(FixedArray) - kHeapObjectTag);
1722 __ LoadTaggedField(return_reg,
1723 MemOperand(scratch, result_index, LSL, kTaggedSizeLog2));
1724 __ jmp(&convert_return);
1725
1726 // -------------------------------------------
1727 // Update refs after calling all builtins.
1728 // -------------------------------------------
1729
1730 // Some builtin calls for return value conversion may trigger GC, and some
1731 // heap pointers of ref types might become invalid in the conversion loop.
1732 // Thus, copy the ref values again after finishing all the conversions.
1733 __ bind(&loop_copy_return_refs);
1734
1735 // If there is only one return value, there should be no heap pointer in the
1736 // packed_args while calling any builtin. So, we don't need to update refs.
1737 __ Ldr(return_count, MemOperand(fp, kReturnCountOffset));
1738 __ cmp(return_count, Immediate(1));
1739 __ B(&all_done, eq);
1740
1741 Label copy_return_if_ref, copy_return_ref, done_copy_return_ref;
1742 __ Ldr(packed_args, MemOperand(fp, kPackedArrayOffset));
1743 __ Ldr(signature, MemOperand(fp, kSignatureOffset));
1744 __ Ldr(valuetypes_array_ptr,
1746 __ Mov(result_index, xzr);
1747 __ Mov(current_result_offset, xzr);
1748
1749 // Copy if the current return value is a ref type.
1750 __ bind(&copy_return_if_ref);
1751 __ Ldr(valuetype, MemOperand(valuetypes_array_ptr, 0));
1752
1753 __ Tst(valuetype, Immediate(1));
1754 __ B(&copy_return_ref, ne);
1755
1756 Label inc_result_32bit;
1757 __ Cmp(valuetype, Immediate(wasm::kWasmI32.raw_bit_field()));
1758 __ B(&inc_result_32bit, eq);
1759 __ Cmp(valuetype, Immediate(wasm::kWasmF32.raw_bit_field()));
1760 __ B(&inc_result_32bit, eq);
1761
1762 Label inc_result_64bit;
1763 __ Cmp(valuetype, Immediate(wasm::kWasmI64.raw_bit_field()));
1764 __ B(&inc_result_64bit, eq);
1765 __ Cmp(valuetype, Immediate(wasm::kWasmF64.raw_bit_field()));
1766 __ B(&inc_result_64bit, eq);
1767
1768 // Invalid type. JavaScript cannot return Simd values to WebAssembly.
1769 __ DebugBreak();
1770
1771 __ bind(&inc_result_32bit);
1772 __ Add(current_result_offset, current_result_offset,
1773 Immediate(sizeof(int32_t)));
1774 __ jmp(&done_copy_return_ref);
1775
1776 __ bind(&inc_result_64bit);
1777 __ Add(current_result_offset, current_result_offset,
1778 Immediate(sizeof(int64_t)));
1779 __ jmp(&done_copy_return_ref);
1780
1781 __ bind(&copy_return_ref);
1782 __ Add(scratch, fixed_array,
1783 OFFSET_OF_DATA_START(FixedArray) - kHeapObjectTag);
1784 __ LoadTaggedField(return_reg,
1785 MemOperand(scratch, result_index, LSL, kTaggedSizeLog2));
1786 __ Str(return_reg, MemOperand(packed_args, current_result_offset));
1787 __ Add(current_result_offset, current_result_offset,
1788 Immediate(kSystemPointerSize));
1789
1790 // Move pointers.
1791 __ bind(&done_copy_return_ref);
1792 __ Add(valuetypes_array_ptr, valuetypes_array_ptr, Immediate(kValueTypeSize));
1793 __ Add(result_index, result_index, Immediate(1));
1794 __ cmp(result_index, return_count);
1795 __ B(&copy_return_if_ref, lt);
1796
1797 // -------------------------------------------
1798 // All done.
1799 // -------------------------------------------
1800
1801 __ bind(&all_done);
1802 // Set thread_in_wasm_flag.
1803 DEFINE_REG_W(scratch32);
1804 __ Ldr(scratch, MemOperand(kRootRegister,
1806 __ Mov(scratch32, Immediate(1));
1807 __ Str(scratch32, MemOperand(scratch, 0)); // 32 bit.
1808
1809 // Deconstruct the stack frame.
1810 __ LeaveFrame(StackFrame::WASM_TO_JS);
1811
1812 __ Mov(x0, Immediate(WasmToJSInterpreterFrameConstants::kSuccess));
1813 __ Ret(lr);
1814}
1815
1816#endif // V8_ENABLE_WEBASSEMBLY
1817
1818#undef __
1819
1820} // namespace internal
1821} // namespace v8
const RegList initial_
RegList available_
#define ASSIGN_REG(Name)
RegisterAllocator * allocator_
std::vector< Register * > allocated_registers_
#define DEFINE_PINNED(Name, Reg)
Register * reg_
#define FREE_REG(Name)
#define DEFINE_REG(Name)
#define BUILTIN_CODE(isolate, name)
Definition builtins.h:45
static constexpr U kMask
Definition bit-field.h:41
static constexpr Builtin Call(ConvertReceiverMode=ConvertReceiverMode::kAny)
static ExternalReference Create(const SCTableReference &table_ref)
static constexpr uint32_t thread_in_wasm_flag_address_offset()
Definition isolate.h:1338
static constexpr MainThreadFlags kPointersToHereAreInterestingMask
static constexpr MainThreadFlags kPointersFromHereAreInterestingMask
static constexpr size_t kReturnCountOffset
Definition signature.h:140
static constexpr size_t kRepsOffset
Definition signature.h:143
static constexpr size_t kParameterCountOffset
Definition signature.h:141
static constexpr Tagged< Smi > zero()
Definition smi.h:99
static constexpr int SharedFunctionInfoOffsetInTaggedJSFunction()
static constexpr int ToTagged(int offset)
int32_t offset
TNode< Object > receiver
double increment
LiftoffRegister reg
RegListBase< RegisterT > registers
int int32_t
Definition unicode.cc:40
double log2(double x)
Definition ieee754.cc:1973
void Free(void *memory)
Definition memory.h:63
void Add(RWDigits Z, Digits X, Digits Y)
void And(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void Sub(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
constexpr IndependentValueType kWasmF32
constexpr IndependentValueType kWasmI32
constexpr IndependentValueType kWasmS128
constexpr IndependentValueType kWasmF64
constexpr IndependentValueType kWasmI64
constexpr Register no_reg
constexpr Register kRootRegister
RegListBase< Register > RegList
Definition reglist-arm.h:14
constexpr int kPCOnStackSize
Definition globals.h:412
constexpr ShiftOp LSL
constexpr int B
constexpr Register kJavaScriptCallArgCountRegister
constexpr int kSystemPointerSizeLog2
Definition globals.h:494
constexpr int kFPOnStackSize
Definition globals.h:413
MemOperand FieldMemOperand(Register object, int offset)
constexpr int kSystemPointerSize
Definition globals.h:410
constexpr DoubleRegister kFPReturnRegister0
constexpr int kTaggedSizeLog2
Definition globals.h:543
constexpr Register kReturnRegister0
constexpr Register kWasmImplicitArgRegister
constexpr Register kContextRegister
const int kHeapObjectTag
Definition v8-internal.h:72
constexpr bool SmiValuesAre32Bits()
constexpr Register kPtrComprCageBaseRegister
constexpr int kXRegSizeInBits
constexpr Register cp
constexpr Register NoReg
constexpr Register kJSFunctionRegister
constexpr Register padreg
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define OFFSET_OF_DATA_START(Type)