• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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/assembler/aarch64/macro_assembler_aarch64.h"
17 #include "ecmascript/js_function.h"
18 
19 namespace panda::ecmascript::kungfu {
20 using namespace panda::ecmascript;
21 constexpr uint32_t k4BitSize = 4;
22 constexpr uint32_t k16BitSize = 16;
23 constexpr uint32_t k32BitSize = 32;
24 constexpr uint32_t k48BitSize = 48;
25 constexpr uint32_t k64BitSize = 64;
26 
27 const std::set<uint64_t> ValidBitmaskImmSet = {
28 #include "valid_bitmask_imm.txt"
29 };
30 constexpr uint32_t kMaxBitTableSize = 5;
31 constexpr std::array<uint64_t, kMaxBitTableSize> kBitmaskImmMultTable = {
32     0x0000000100000001UL, 0x0001000100010001UL, 0x0101010101010101UL, 0x1111111111111111UL, 0x5555555555555555UL,
33 };
34 
Move(const StackSlotOperand & dstStackSlot,Immediate value)35 void MacroAssemblerAArch64::Move(const StackSlotOperand &dstStackSlot, Immediate value)
36 {
37     aarch64::Register baseReg = (dstStackSlot.IsFrameBase()) ? aarch64::Register(aarch64::FP) :
38                                                                aarch64::Register(aarch64::SP);
39     aarch64::MemoryOperand dstOpnd(baseReg, static_cast<int64_t>(dstStackSlot.GetOffset()));
40     assembler.Mov(LOCAL_SCOPE_REGISTER, aarch64::Immediate(value.GetValue()));
41     PickLoadStoreInsn(LOCAL_SCOPE_REGISTER, dstOpnd, false);
42 }
43 
Move(const StackSlotOperand & dstStackSlot,const StackSlotOperand & srcStackSlot)44 void MacroAssemblerAArch64::Move(const StackSlotOperand &dstStackSlot,
45                                  const StackSlotOperand &srcStackSlot)
46 {
47     aarch64::Register dstBaseReg = (dstStackSlot.IsFrameBase()) ? aarch64::Register(aarch64::FP) :
48                                                                   aarch64::Register(aarch64::SP);
49     aarch64::MemoryOperand dstOpnd(dstBaseReg, static_cast<int64_t>(dstStackSlot.GetOffset()));
50     aarch64::Register srcBaseReg = (srcStackSlot.IsFrameBase()) ? aarch64::Register(aarch64::FP) :
51                                                                   aarch64::Register(aarch64::SP);
52     aarch64::MemoryOperand srcOpnd(srcBaseReg, static_cast<int64_t>(srcStackSlot.GetOffset()));
53     PickLoadStoreInsn(LOCAL_SCOPE_REGISTER, srcOpnd);
54     PickLoadStoreInsn(LOCAL_SCOPE_REGISTER, dstOpnd, false);
55 }
56 
Cmp(const StackSlotOperand & stackSlot,Immediate value)57 void MacroAssemblerAArch64::Cmp(const StackSlotOperand &stackSlot, Immediate value)
58 {
59     aarch64::Register baseReg = (stackSlot.IsFrameBase()) ? aarch64::Register(aarch64::FP) :
60                                                             aarch64::Register(aarch64::SP);
61     aarch64::MemoryOperand opnd(baseReg, static_cast<int64_t>(stackSlot.GetOffset()));
62     aarch64::Operand immOpnd = aarch64::Immediate(value.GetValue());
63     PickLoadStoreInsn(LOCAL_SCOPE_REGISTER, opnd);
64     assembler.Cmp(LOCAL_SCOPE_REGISTER, immOpnd);
65 }
66 
Bind(JumpLabel & label)67 void MacroAssemblerAArch64::Bind(JumpLabel &label)
68 {
69     assembler.Bind(&label);
70 }
71 
Jz(JumpLabel & label)72 void MacroAssemblerAArch64::Jz(JumpLabel &label)
73 {
74     assembler.B(aarch64::EQ, &label);
75 }
76 
Jnz(JumpLabel & label)77 void MacroAssemblerAArch64::Jnz(JumpLabel &label)
78 {
79     assembler.B(aarch64::NE, &label);
80 }
81 
Jump(JumpLabel & label)82 void MacroAssemblerAArch64::Jump(JumpLabel &label)
83 {
84     assembler.B(&label);
85 }
86 
CallBuiltin(Address funcAddress,const std::vector<MacroParameter> & parameters)87 void MacroAssemblerAArch64::CallBuiltin(Address funcAddress,
88                                         const std::vector<MacroParameter> &parameters)
89 {
90     for (size_t i = 0; i < parameters.size(); ++i) {
91         auto param = parameters[i];
92         if (i == PARAM_REGISTER_COUNT) {
93             std::cout << "not support aarch64 baseline stack parameter " << std::endl;
94             std::abort();
95             break;
96         }
97         MovParameterIntoParamReg(param, registerParamVec[i]);
98     }
99     size_t startPc = assembler.GetCurPos();
100     assembler.Mov(LOCAL_SCOPE_REGISTER, aarch64::Immediate(funcAddress));
101     assembler.Blr(LOCAL_SCOPE_REGISTER);
102     size_t endPc = assembler.GetCurPos() - INSTRUCT_SIZE;
103     assembler.RecordRelocInfo(startPc, endPc, funcAddress);
104 }
105 
SaveReturnRegister(const StackSlotOperand & dstStackSlot)106 void MacroAssemblerAArch64::SaveReturnRegister(const StackSlotOperand &dstStackSlot)
107 {
108     aarch64::Register baseReg = (dstStackSlot.IsFrameBase()) ? aarch64::Register(aarch64::FP) :
109                                                                aarch64::Register(aarch64::SP);
110     aarch64::MemoryOperand dstOpnd(baseReg, static_cast<int64_t>(dstStackSlot.GetOffset()));
111     PickLoadStoreInsn(RETURN_REGISTER, dstOpnd, false);
112 }
113 
MovParameterIntoParamReg(MacroParameter param,aarch64::Register paramReg)114 void MacroAssemblerAArch64::MovParameterIntoParamReg(MacroParameter param, aarch64::Register paramReg)
115 {
116     if (std::holds_alternative<BaselineSpecialParameter>(param)) {
117         auto specialParam = std::get<BaselineSpecialParameter>(param);
118         switch (specialParam) {
119             case BaselineSpecialParameter::GLUE: {
120                 assembler.Mov(paramReg, GLUE_REGISTER);
121                 break;
122             }
123             case BaselineSpecialParameter::PROFILE_TYPE_INFO: {
124                 assembler.Ldur(LOCAL_SCOPE_REGISTER,
125                                aarch64::MemoryOperand(aarch64::Register(aarch64::X29),
126                                                       static_cast<int64_t>(FUNCTION_OFFSET_FROM_SP)));
127                 assembler.Ldr(LOCAL_SCOPE_REGISTER,
128                               aarch64::MemoryOperand(LOCAL_SCOPE_REGISTER, JSFunction::RAW_PROFILE_TYPE_INFO_OFFSET));
129                 assembler.Ldr(paramReg,
130                               aarch64::MemoryOperand(LOCAL_SCOPE_REGISTER, ProfileTypeInfoCell::VALUE_OFFSET));
131                 break;
132             }
133             case BaselineSpecialParameter::SP: {
134                 assembler.Mov(paramReg, aarch64::Register(aarch64::X29));
135                 break;
136             }
137             case BaselineSpecialParameter::HOTNESS_COUNTER: {
138                 assembler.Ldur(LOCAL_SCOPE_REGISTER, aarch64::MemoryOperand(aarch64::Register(aarch64::X29),
139                     static_cast<int64_t>(FUNCTION_OFFSET_FROM_SP)));
140                 assembler.Ldr(LOCAL_SCOPE_REGISTER,
141                               aarch64::MemoryOperand(LOCAL_SCOPE_REGISTER, JSFunctionBase::METHOD_OFFSET));
142                 assembler.Ldr(paramReg,
143                               aarch64::MemoryOperand(LOCAL_SCOPE_REGISTER, Method::LITERAL_INFO_OFFSET));
144                 break;
145             }
146             default: {
147                 std::cout << "not supported other BaselineSpecialParameter currently" << std::endl;
148                 std::abort();
149             }
150         }
151         return;
152     }
153     if (std::holds_alternative<int8_t>(param)) {
154         int16_t num = std::get<int8_t>(param);
155         assembler.Mov(paramReg, aarch64::Immediate(static_cast<int64_t>(num)));
156         return;
157     }
158     if (std::holds_alternative<int16_t>(param)) {
159         int16_t num = std::get<int16_t>(param);
160         assembler.Mov(paramReg, aarch64::Immediate(static_cast<int64_t>(num)));
161         return;
162     }
163     if (std::holds_alternative<int32_t>(param)) {
164         int32_t num = std::get<int32_t>(param);
165         assembler.Mov(paramReg, aarch64::Immediate(static_cast<int64_t>(num)));
166         return;
167     }
168     if (std::holds_alternative<int64_t>(param)) {
169         int64_t num = std::get<int64_t>(param);
170         CopyImm(paramReg, num, k64BitSize);
171         return;
172     }
173     if (std::holds_alternative<StackSlotOperand>(param)) {
174         StackSlotOperand stackSlotOpnd = std::get<StackSlotOperand>(param);
175         aarch64::Register dstBaseReg = (stackSlotOpnd.IsFrameBase()) ? aarch64::Register(aarch64::FP) :
176                                                                        aarch64::Register(aarch64::SP);
177         aarch64::MemoryOperand paramOpnd(dstBaseReg, static_cast<int64_t>(stackSlotOpnd.GetOffset()));
178         PickLoadStoreInsn(paramReg, paramOpnd);
179         return;
180     }
181     std::cout << "not supported other type of aarch64 baseline parameters currently" << std::endl;
182     std::abort();
183 }
184 
PickLoadStoreInsn(aarch64::Register reg,aarch64::MemoryOperand memOpnd,bool isLoad)185 void MacroAssemblerAArch64::PickLoadStoreInsn(aarch64::Register reg, aarch64::MemoryOperand memOpnd, bool isLoad)
186 {
187     int64_t maxNineBitsSignedValue = 255;
188     int64_t minNineBitsSignedValue = -256;
189     int64_t value = memOpnd.GetImmediate().Value();
190     if (value < minNineBitsSignedValue) {
191         std::cout << "not support aarch64 offset in memory operand is less than -256!" << std::endl;
192         std::abort();
193     }
194     if (value > maxNineBitsSignedValue && isLoad) {
195         if (value % 8 != 0) { // 8: offset in memory operand must be a multiple of 8 for ldr insn
196             std::cout << "not support offset in memory operand is not a multiple of 8 for ldr insn!" << std::endl;
197             std::abort();
198         }
199         assembler.Ldr(reg, memOpnd);
200     }
201     if (value > maxNineBitsSignedValue && !isLoad) {
202         if (value % 8 != 0) { // 8: offset in memory operand must be a multiple of 8 for str insn
203             std::cout << "not support offset in memory operand is not a multiple of 8 for str insn!" << std::endl;
204             std::abort();
205         }
206         assembler.Str(reg, memOpnd);
207     }
208     if (value <= maxNineBitsSignedValue && isLoad) {
209         assembler.Ldur(reg, memOpnd);
210     }
211     if (value <= maxNineBitsSignedValue && !isLoad) {
212         assembler.Stur(reg, memOpnd);
213     }
214 }
215 
IsMoveWidableImmediate(uint64_t val,uint32_t bitLen)216 bool MacroAssemblerAArch64::IsMoveWidableImmediate(uint64_t val, uint32_t bitLen)
217 {
218     if (bitLen == k64BitSize) {
219         /* 0xHHHH000000000000 or 0x0000HHHH00000000, return true */
220         if (((val & ((static_cast<uint64_t>(0xffff)) << k48BitSize)) == val) ||
221             ((val & ((static_cast<uint64_t>(0xffff)) << k32BitSize)) == val)) {
222             return true;
223         }
224     } else {
225         /* get lower 32 bits */
226         val &= static_cast<uint64_t>(0xffffffff);
227     }
228     /* 0x00000000HHHH0000 or 0x000000000000HHHH, return true */
229     return ((val & ((static_cast<uint64_t>(0xffff)) << k16BitSize)) == val ||
230             (val & ((static_cast<uint64_t>(0xffff)) << 0)) == val);
231 }
232 
IsBitmaskImmediate(uint64_t val,uint32_t bitLen)233 bool MacroAssemblerAArch64::IsBitmaskImmediate(uint64_t val, uint32_t bitLen)
234 {
235     if (static_cast<int64_t>(val) == -1 || val == 0) {
236         std::cout << "IsBitmaskImmediate() don't accept 0 or -1!" << std::endl;
237         std::abort();
238     }
239     if ((bitLen == k32BitSize) && (static_cast<int32_t>(val) == -1)) {
240         return false;
241     }
242     uint64_t val2 = val;
243     if (bitLen == k32BitSize) {
244         val2 = (val2 << k32BitSize) | (val2 & ((1ULL << k32BitSize) - 1));
245     }
246     bool expectedOutcome = (ValidBitmaskImmSet.find(val2) != ValidBitmaskImmSet.end());
247 
248     if ((val & 0x1) != 0) {
249         /*
250          * we want to work with
251          * 0000000000000000000000000000000000000000000001100000000000000000
252          * instead of
253          * 1111111111111111111111111111111111111111111110011111111111111111
254          */
255         val = ~val;
256     }
257 
258     if (bitLen == k32BitSize) {
259         val = (val << k32BitSize) | (val & ((1ULL << k32BitSize) - 1));
260     }
261 
262     /* get the least significant bit set and add it to 'val' */
263     uint64_t tmpVal = val + (val & static_cast<uint64_t>(UINT64_MAX - val + 1));
264 
265     /* now check if tmp is a power of 2 or tmpVal==0. */
266     if (tmpVal < 1 || tmpVal > UINT64_MAX) {
267         std::cout << "tmpVal value overflow!" << std::endl;
268         std::abort();
269     }
270     tmpVal = tmpVal & (tmpVal - 1);
271     if (tmpVal == 0) {
272         if (!expectedOutcome) {
273             return false;
274         }
275         /* power of two or zero ; return true */
276         return true;
277     }
278 
279     int32_t p0 = __builtin_ctzll(val);
280     int32_t p1 = __builtin_ctzll(tmpVal);
281     int64_t diff = p1 - p0;
282 
283     /* check if diff is a power of two; return false if not. */
284     if (static_cast<uint64_t>(diff) < 1 || static_cast<uint64_t>(diff) > UINT64_MAX) {
285         std::cout << "diff value overflow!" << std::endl;
286         std::abort();
287     }
288     if ((static_cast<uint64_t>(diff) & (static_cast<uint64_t>(diff) - 1)) != 0) {
289         return false;
290     }
291 
292     uint32_t logDiff = static_cast<uint32_t>(__builtin_ctzll(static_cast<uint64_t>(diff)));
293     uint64_t pattern = val & ((1ULL << static_cast<uint64_t>(diff)) - 1);
294     return val == pattern * kBitmaskImmMultTable[kMaxBitTableSize - logDiff];
295 }
296 
IsSingleInstructionMovable(uint64_t val,uint32_t size)297 bool MacroAssemblerAArch64::IsSingleInstructionMovable(uint64_t val, uint32_t size)
298 {
299     return (IsMoveWidableImmediate(val, size) ||
300            IsMoveWidableImmediate(~val, size) || IsBitmaskImmediate(val, size));
301 }
302 
BetterUseMOVZ(uint64_t val)303 bool MacroAssemblerAArch64::BetterUseMOVZ(uint64_t val)
304 {
305     int32_t n16zerosChunks = 0;
306     int32_t n16onesChunks = 0;
307     uint64_t sa = 0;
308     /* a 64 bits number is split 4 chunks, each chunk has 16 bits. check each chunk whether is all 1 or is all 0 */
309     for (uint64_t i = 0; i < k4BitSize; ++i, sa += k16BitSize) {
310         uint64_t chunkVal = (val >> (static_cast<uint64_t>(sa))) & 0x0000FFFFUL;
311         if (chunkVal == 0) {
312             ++n16zerosChunks;
313         } else if (chunkVal == 0xFFFFUL) {
314             ++n16onesChunks;
315         }
316     }
317     return (n16zerosChunks >= n16onesChunks);
318 }
319 
CopyImm(aarch64::Register destReg,int64_t imm,uint32_t size)320 void MacroAssemblerAArch64::CopyImm(aarch64::Register destReg, int64_t imm, uint32_t size)
321 {
322     uint64_t srcVal = static_cast<uint64_t>(imm);
323 
324     if (IsSingleInstructionMovable(srcVal, size)) {
325         assembler.Mov(destReg, aarch64::Immediate(imm));
326         return;
327     }
328 
329     if (size != k32BitSize && size != k64BitSize) {
330         std::cout << "only support 32 and 64 bits size!" << std::endl;
331         std::abort();
332     }
333 
334     if (size == k32BitSize) {
335         /* check lower 16 bits and higher 16 bits respectively */
336         if ((srcVal & 0x0000FFFFULL) == 0 || (srcVal & 0x0000FFFFULL) == 0xFFFFULL) {
337             std::cout << "unexpected val!" << std::endl;
338             std::abort();
339         }
340         if (((srcVal >> k16BitSize) & 0x0000FFFFULL) == 0 || ((srcVal >> k16BitSize) & 0x0000FFFFULL) == 0xFFFFULL) {
341             std::cout << "unexpected val" << std::endl;
342             std::abort();
343         }
344         /* create an imm opereand which represents lower 16 bits of the immediate */
345         int64_t srcLower = static_cast<int64_t>(srcVal & 0x0000FFFFULL);
346         assembler.Mov(destReg, aarch64::Immediate(srcLower));
347         /* create an imm opereand which represents upper 16 bits of the immediate */
348         int64_t srcUpper = static_cast<int64_t>((srcVal >> k16BitSize) & 0x0000FFFFULL);
349         assembler.Movk(destReg, srcUpper, k16BitSize);
350     } else {
351         CopyImmSize64(destReg, srcVal);
352     }
353 }
354 
CopyImmSize64(aarch64::Register destReg,uint64_t srcVal)355 void MacroAssemblerAArch64::CopyImmSize64(aarch64::Register destReg, uint64_t srcVal)
356 {
357     /*
358      * partition it into 4 16-bit chunks
359      * if more 0's than 0xFFFF's, use movz as the initial instruction.
360      * otherwise, movn.
361      */
362     bool useMovz = BetterUseMOVZ(srcVal);
363     bool useMovk = false;
364     /* get lower 32 bits of the immediate */
365     uint64_t chunkLval = srcVal & 0xFFFFFFFFULL;
366     /* get upper 32 bits of the immediate */
367     uint64_t chunkHval = (srcVal >> k32BitSize) & 0xFFFFFFFFULL;
368     int32_t maxLoopTime = 4;
369 
370     if (chunkLval == chunkHval) {
371         /* compute lower 32 bits, and then copy to higher 32 bits, so only 2 chunks need be processed */
372         maxLoopTime = 2;
373     }
374 
375     uint64_t sa = 0;
376     for (int64_t i = 0; i < maxLoopTime; ++i, sa += k16BitSize) {
377         /* create an imm opereand which represents the i-th 16-bit chunk of the immediate */
378         uint64_t chunkVal = (srcVal >> (static_cast<uint64_t>(sa))) & 0x0000FFFFULL;
379         if (useMovz ? (chunkVal == 0) : (chunkVal == 0x0000FFFFULL)) {
380             continue;
381         }
382         int64_t src16 = static_cast<int64_t>(chunkVal);
383         if (!useMovk) {
384             /* use movz or movn */
385             if (!useMovz) {
386                 src16 = ~(static_cast<uint64_t>(src16)) & ((1ULL << k16BitSize) - 1UL);
387                 assembler.Movn(destReg, src16, sa);
388             } else {
389                 assembler.Movz(destReg, src16, sa);
390             }
391             useMovk = true;
392         } else {
393             assembler.Movk(destReg, src16, sa);
394         }
395     }
396 
397     if (maxLoopTime == 2) { /* as described above, only 2 chunks need be processed */
398         /* copy lower 32 bits to higher 32 bits */
399         uint32_t immr = -k32BitSize % k64BitSize; // immr = -shift % size
400         uint32_t imms = k32BitSize - 1; // imms = width - 1
401         assembler.Bfm(destReg, destReg, immr, imms);
402     }
403 }
404 
405 }  // namespace panda::ecmascript::kungfu
406