1/*
2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/compiler/trampoline/aarch64/common_call.h"
17
18#include "ecmascript/compiler/assembler/assembler.h"
19#include "ecmascript/compiler/argument_accessor.h"
20#include "ecmascript/compiler/rt_call_signature.h"
21#include "ecmascript/ecma_runtime_call_info.h"
22#include "ecmascript/frames.h"
23#include "ecmascript/js_function.h"
24#include "ecmascript/js_thread.h"
25#include "ecmascript/js_generator_object.h"
26#include "ecmascript/message_string.h"
27#include "ecmascript/method.h"
28#include "ecmascript/runtime_call_id.h"
29
30namespace panda::ecmascript::aarch64 {
31using Label = panda::ecmascript::Label;
32#define __ assembler->
33
34void CommonCall::PushAsmInterpBridgeFrame(ExtendedAssembler *assembler)
35{
36    Register fp(X29);
37    Register sp(SP);
38
39    [[maybe_unused]] TempRegister1Scope scope1(assembler);
40    Register frameTypeRegister = __ TempRegister1();
41
42    __ Mov(frameTypeRegister, Immediate(static_cast<int64_t>(FrameType::ASM_INTERPRETER_BRIDGE_FRAME)));
43    // 2 : return addr & frame type
44    __ Stp(frameTypeRegister, Register(X30), MemoryOperand(sp, -2 * FRAME_SLOT_SIZE, AddrMode::PREINDEX));
45    // 2 : prevSp & pc
46    __ Stp(Register(Zero), Register(FP), MemoryOperand(sp, -2 * FRAME_SLOT_SIZE, AddrMode::PREINDEX));
47    __ Add(fp, sp, Immediate(24));  // 24: skip frame type, prevSp, pc
48
49    if (!assembler->FromInterpreterHandler()) {
50        __ CalleeSave();
51    }
52}
53
54void CommonCall::PopAsmInterpBridgeFrame(ExtendedAssembler *assembler)
55{
56    Register sp(SP);
57
58    if (!assembler->FromInterpreterHandler()) {
59        __ CalleeRestore();
60    }
61    // 2: prevSp & pc
62    __ Ldp(Register(Zero), Register(FP), MemoryOperand(sp, 2 * FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
63    // 2: return addr & frame type
64    __ Ldp(Register(Zero), Register(X30), MemoryOperand(sp, 2 * FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
65}
66
67
68void CommonCall::PushLeaveFrame(ExtendedAssembler *assembler, Register glue)
69{
70    TempRegister2Scope temp2Scope(assembler);
71    Register frameType = __ TempRegister2();
72    Register currentSp(X6);
73    Register sp(SP);
74
75    // construct leave frame
76    __ Mov(frameType, Immediate(static_cast<int64_t>(FrameType::LEAVE_FRAME)));
77    __ PushFpAndLr();
78    // 2 : 2 means pairs
79    __ Stp(Register(X19), frameType, MemoryOperand(sp, -2 * FRAME_SLOT_SIZE, AddrMode::PREINDEX));
80    __ Add(Register(FP), sp, Immediate(DOUBLE_SLOT_SIZE));
81    // save to thread currentLeaveFrame_;
82    __ Str(Register(FP), MemoryOperand(glue, JSThread::GlueData::GetLeaveFrameOffset(false)));
83}
84
85
86void CommonCall::PopLeaveFrame(ExtendedAssembler *assembler)
87{
88    Register sp(SP);
89    Register currentSp(X6);
90    TempRegister2Scope temp2Scope(assembler);
91    Register frameType = __ TempRegister2();
92    // 2 : 2 means pairs
93    __ Ldp(Register(X19), frameType, MemoryOperand(sp, 2 * FRAME_SLOT_SIZE, AddrMode::POSTINDEX));
94    __ RestoreFpAndLr();
95}
96
97void CommonCall::PushArgsWithArgv(ExtendedAssembler *assembler, Register glue, Register argc,
98    Register argv, Register op, Register currentSlot, Label *next, Label *stackOverflow)
99{
100    Label loopBeginning;
101    if (next != nullptr) {
102        __ Cmp(argc.W(), Immediate(0));
103        __ B(Condition::LS, next);
104    }
105    if (stackOverflow != nullptr) {
106        StackOverflowCheck(assembler, glue, currentSlot, argc, op, stackOverflow);
107    }
108    __ Add(argv, argv, Operand(argc.W(), UXTW, 3));  // 3: argc * 8
109    __ Bind(&loopBeginning);
110    __ Ldr(op, MemoryOperand(argv, -FRAME_SLOT_SIZE, PREINDEX));  // -8: 8 bytes
111    __ Str(op, MemoryOperand(currentSlot, -FRAME_SLOT_SIZE, PREINDEX));  // -8: 8 bytes
112    __ Sub(argc.W(), argc.W(), Immediate(1));
113    __ Cbnz(argc.W(), &loopBeginning);
114}
115
116void CommonCall::PushArgsWithArgvInPair(ExtendedAssembler *assembler, Register argc,
117    Register argv, Register padding, Register op1, Register op2, Label *next)
118{
119    Register sp(SP);
120    if (next != nullptr) {
121        __ Cmp(argc.W(), Immediate(0));
122        __ B(Condition::LS, next);
123    }
124
125    Label copyArgs;
126    __ Tbnz(argc, 0, &copyArgs);
127    {
128        __ Add(argv, argv, Operand(argc.W(), UXTW, 3)); // 3: argc * 8
129        __ Ldr(op1, MemoryOperand(argv, -FRAME_SLOT_SIZE, PREINDEX));
130        __ Stp(op1, Register(Zero), MemoryOperand(sp, -DOUBLE_SLOT_SIZE, AddrMode::PREINDEX));
131        __ Sub(argc.W(), argc.W(), Immediate(1)); // 1: push the top arg already
132        __ Sub(argv, argv, Operand(argc.W(), UXTW, 3)); // 3: argc * 8
133        __ B(&copyArgs);
134    }
135    __ Bind(&copyArgs);
136    {
137        Label loopBeginning;
138        Label pushPadding;
139        __ Add(argv, argv, Operand(argc.W(), UXTW, 3));  // 3: argc * 8
140
141        __ Cmp(argc.W(), Immediate(1));  // 1: argc is odd number in copyArgs
142        __ B(Condition::LS, &pushPadding);
143
144        __ Bind(&loopBeginning);
145        __ Ldp(op1, op2, MemoryOperand(argv, -DOUBLE_SLOT_SIZE, PREINDEX));
146        __ Stp(op1, op2, MemoryOperand(sp, -DOUBLE_SLOT_SIZE, AddrMode::PREINDEX));
147        __ Sub(argc.W(), argc.W(), Immediate(2));  // 2: pair
148        __ Cmp(argc.W(), Immediate(1));  // 1: argc is odd number in copyArgs
149        __ B(Condition::HI, &loopBeginning);
150
151        __ Bind(&pushPadding);
152        __ Ldr(op2, MemoryOperand(argv, -FRAME_SLOT_SIZE, PREINDEX));
153        __ Stp(padding, op2, MemoryOperand(sp, -DOUBLE_SLOT_SIZE, AddrMode::PREINDEX));
154        if (next != nullptr) {
155            __ B(next);
156        }
157    }
158}
159
160void CommonCall::PushUndefinedWithArgc(ExtendedAssembler *assembler, Register glue, Register argc, Register temp,
161    Register currentSlot, Label *next, Label *stackOverflow)
162{
163    if (next != nullptr) {
164        __ Cmp(argc.W(), Immediate(0));
165        __ B(Condition::LE, next);
166    }
167    if (stackOverflow != nullptr) {
168        StackOverflowCheck(assembler, glue, currentSlot, argc, temp, stackOverflow);
169    }
170    Label loopBeginning;
171    __ Mov(temp, Immediate(JSTaggedValue::VALUE_UNDEFINED));
172    __ Bind(&loopBeginning);
173    __ Str(temp, MemoryOperand(currentSlot, -FRAME_SLOT_SIZE, AddrMode::PREINDEX));
174    __ Sub(argc.W(), argc.W(), Immediate(1));
175    __ Cbnz(argc.W(), &loopBeginning);
176}
177
178void CommonCall::StackOverflowCheck(ExtendedAssembler *assembler, Register glue, Register currentSlot,
179    Register numArgs, Register op, Label *stackOverflow)
180{
181    __ Ldr(op, MemoryOperand(glue, JSThread::GlueData::GetStackLimitOffset(false)));
182    Label skipThrow;
183    __ Sub(op, currentSlot, Operand(op, UXTX, 0));
184    __ Cmp(op, Operand(numArgs, LSL, 3));  // 3: each args occupies 8 bytes
185    __ B(Condition::GT, &skipThrow);
186    __ Ldr(op, MemoryOperand(glue, JSThread::GlueData::GetAllowCrossThreadExecutionOffset(false)));
187    __ Cbz(op, stackOverflow);
188    __ Bind(&skipThrow);
189}
190
191void CommonCall::PushAsmBridgeFrame(ExtendedAssembler *assembler)
192{
193    Register sp(SP);
194    TempRegister2Scope temp2Scope(assembler);
195    Register frameType = __ TempRegister2();
196    __ PushFpAndLr();
197    // construct frame
198    __ Mov(frameType, Immediate(static_cast<int64_t>(FrameType::ASM_BRIDGE_FRAME)));
199    // 2 : 2 means pairs. X19 means calleesave and 16bytes align
200    __ Stp(Register(X19), frameType, MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX));
201    __ Add(Register(FP), sp, Immediate(DOUBLE_SLOT_SIZE));
202}
203
204void CommonCall::PopAsmBridgeFrame(ExtendedAssembler *assembler)
205{
206    TempRegister2Scope temp2Scope(assembler);
207    Register sp(SP);
208    Register frameType = __ TempRegister2();
209    // 2 : 2 means pop call site sp and type
210    __ Ldp(Register(X19), frameType, MemoryOperand(sp, FRAME_SLOT_SIZE * 2, AddrMode::POSTINDEX));
211    __ RestoreFpAndLr();
212}
213#undef __
214}  // panda::ecmascript::aarch64