• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 <functional>
17 #include <string>
18 #include <unordered_map>
19 
20 #include <regex>
21 #include "optimizer/optimizations/regalloc/reg_alloc.h"
22 #include "optimizer/optimizations/regalloc/reg_alloc_linear_scan.h"
23 #include "optimizer/code_generator/codegen.h"
24 #include "optimizer/code_generator/codegen_native.h"
25 #include "optimizer/code_generator/method_properties.h"
26 #include "aarch64/decoder-aarch64.h"
27 #include "aarch64/disasm-aarch64.h"
28 #include "aarch64/operands-aarch64.h"
29 #include "aarch64/disasm-aarch64.h"
30 #include "tests/unit_test.h"
31 
32 namespace panda::compiler {
33 
34 class VixlDisasmTest : public GraphTest {
35 public:
VixlDisasmTest()36     VixlDisasmTest() : decoder_(GetAllocator()) {}
37 
GetDecoder()38     auto &GetDecoder()
39     {
40         return decoder_;
41     }
42 
CreateDisassembler()43     auto CreateDisassembler()
44     {
45         return vixl::aarch64::Disassembler(asm_buf_, sizeof(asm_buf_));
46     }
47 
48 private:
49     vixl::aarch64::Decoder decoder_;
50     char asm_buf_[vixl::aarch64::Disassembler::GetDefaultBufferSize()];
51 };
52 
53 class CodegenCallerSavedRegistersTest : public VixlDisasmTest {
54 };
55 
56 class DecoderVisitor : public vixl::aarch64::DecoderVisitor {
57 public:
58 #define DECLARE(A) \
59     virtual void Visit##A([[maybe_unused]] const vixl::aarch64::Instruction *instr) {}
VISITOR_LIST(DECLARE)60     VISITOR_LIST(DECLARE)
61 #undef DECLARE
62 
63     void Visit(vixl::aarch64::Metadata *metadata, const vixl::aarch64::Instruction *instr) final
64     {
65         using FormToVisitorFnMap =
66             std::unordered_map<std::string, std::function<void(DecoderVisitor *, const vixl::aarch64::Instruction *)>>;
67         static const FormToVisitorFnMap form_to_visitor {DEFAULT_FORM_TO_VISITOR_MAP(DecoderVisitor)};
68 
69         auto visitor_it {form_to_visitor.find((*metadata)["form"])};
70         ASSERT(visitor_it != std::end(form_to_visitor));
71 
72         const auto &visitor {visitor_it->second};
73         ASSERT(visitor != nullptr);
74         visitor(this, instr);
75     }
76 };
77 
78 class LoadStoreRegistersCollector : public DecoderVisitor {
79 public:
80     // use the same body for all VisitXXX methods to simplify visitor's implementation
81 #define DECLARE(A)                                                                                             \
82     void Visit##A(const vixl::aarch64::Instruction *instr) override                                            \
83     {                                                                                                          \
84         if (std::string(#A) == "LoadStorePairOffset") {                                                        \
85             if (instr->Mask(vixl::aarch64::LoadStorePairOp::LDP_x) == vixl::aarch64::LoadStorePairOp::LDP_x || \
86                 instr->Mask(vixl::aarch64::LoadStorePairOp::STP_x) == vixl::aarch64::LoadStorePairOp::STP_x) { \
87                 regs.set(instr->GetRt());                                                                      \
88                 regs.set(instr->GetRt2());                                                                     \
89                 return;                                                                                        \
90             }                                                                                                  \
91             if (instr->Mask(vixl::aarch64::LoadStorePairOp::LDP_d) == vixl::aarch64::LoadStorePairOp::LDP_d || \
92                 instr->Mask(vixl::aarch64::LoadStorePairOp::STP_d) == vixl::aarch64::LoadStorePairOp::STP_d) { \
93                 vregs.set(instr->GetRt());                                                                     \
94                 vregs.set(instr->GetRt2());                                                                    \
95                 return;                                                                                        \
96             }                                                                                                  \
97         }                                                                                                      \
98         if (std::string(#A) == "LoadStoreUnscaledOffset" || std::string(#A) == "LoadStoreUnsignedOffset" ||    \
99             std::string(#A) == "LoadStoreRegisterOffset") {                                                    \
100             if (instr->Mask(vixl::aarch64::LoadStoreOp::LDR_x) == vixl::aarch64::LoadStoreOp::LDR_x ||         \
101                 instr->Mask(vixl::aarch64::LoadStoreOp::STR_x) == vixl::aarch64::LoadStoreOp::STR_x) {         \
102                 regs.set(instr->GetRt());                                                                      \
103                 return;                                                                                        \
104             }                                                                                                  \
105             if (instr->Mask(vixl::aarch64::LoadStoreOp::LDR_d) == vixl::aarch64::LoadStoreOp::LDR_d ||         \
106                 instr->Mask(vixl::aarch64::LoadStoreOp::STR_d) == vixl::aarch64::LoadStoreOp::STR_d) {         \
107                 vregs.set(instr->GetRt());                                                                     \
108                 return;                                                                                        \
109             }                                                                                                  \
110         }                                                                                                      \
111     }
112 
VISITOR_LIST(DECLARE)113     VISITOR_LIST(DECLARE)
114 #undef DECLARE
115     RegMask GetAccessedRegisters()
116     {
117         return regs;
118     }
119 
GetAccessedVRegisters()120     VRegMask GetAccessedVRegisters()
121     {
122         return vregs;
123     }
124 
125 private:
126     RegMask regs;
127     VRegMask vregs;
128 };
129 
130 class LoadStoreInstCollector : public DecoderVisitor {
131 public:
132 #define DECLARE(A)                                                                                                    \
133     void Visit##A(const vixl::aarch64::Instruction *instr) override                                                   \
134     {                                                                                                                 \
135         if (std::string(#A) == "LoadStorePairOffset") {                                                               \
136             if (instr->Mask(vixl::aarch64::LoadStorePairOp::LDP_x) == vixl::aarch64::LoadStorePairOp::LDP_x) {        \
137                 ldp_++;                                                                                               \
138                 regs.set(instr->GetRt());                                                                             \
139                 regs.set(instr->GetRt2());                                                                            \
140             } else if (instr->Mask(vixl::aarch64::LoadStorePairOp::STP_x) == vixl::aarch64::LoadStorePairOp::STP_x) { \
141                 stp_++;                                                                                               \
142                 regs.set(instr->GetRt());                                                                             \
143                 regs.set(instr->GetRt2());                                                                            \
144             } else if (instr->Mask(vixl::aarch64::LoadStorePairOp::LDP_d) == vixl::aarch64::LoadStorePairOp::LDP_d) { \
145                 ldp_v_++;                                                                                             \
146                 vregs.set(instr->GetRt());                                                                            \
147                 vregs.set(instr->GetRt2());                                                                           \
148             } else if (instr->Mask(vixl::aarch64::LoadStorePairOp::STP_d) == vixl::aarch64::LoadStorePairOp::STP_d) { \
149                 stp_v_++;                                                                                             \
150                 vregs.set(instr->GetRt());                                                                            \
151                 vregs.set(instr->GetRt2());                                                                           \
152             }                                                                                                         \
153         }                                                                                                             \
154     }
155 
VISITOR_LIST(DECLARE)156     VISITOR_LIST(DECLARE)
157 #undef DECLARE
158     auto GetLdpX()
159     {
160         return ldp_;
161     }
162 
GetStpX()163     auto GetStpX()
164     {
165         return stp_;
166     }
167 
GetLdpD()168     auto GetLdpD()
169     {
170         return ldp_v_;
171     }
172 
GetStpD()173     auto GetStpD()
174     {
175         return stp_v_;
176     }
177 
GetAccessedPairRegisters()178     RegMask GetAccessedPairRegisters()
179     {
180         return regs;
181     }
182 
GetAccessedPairVRegisters()183     VRegMask GetAccessedPairVRegisters()
184     {
185         return vregs;
186     }
187 
188 private:
189     size_t ldp_ {0};
190     size_t stp_ {0};
191     size_t ldp_v_ {0};
192     size_t stp_v_ {0};
193     RegMask regs;
194     VRegMask vregs;
195 };
196 
TEST_F(CodegenCallerSavedRegistersTest,SaveOnlyLiveRegisters)197 TEST_F(CodegenCallerSavedRegistersTest, SaveOnlyLiveRegisters)
198 {
199     options.SetCompilerSaveOnlyLiveRegisters(true);
200     constexpr auto ARGS_COUNT = 8;
201     GRAPH(GetGraph())
202     {
203         for (int i = 0; i < ARGS_COUNT; i++) {
204             PARAMETER(i, i).u64();
205         }
206         for (int i = 0; i < ARGS_COUNT; i++) {
207             PARAMETER(i + ARGS_COUNT, i + ARGS_COUNT).f64();
208         }
209 
210         BASIC_BLOCK(2, -1)
211         {
212             INST(16, Opcode::Add).u64().Inputs(0, 1);
213             INST(17, Opcode::Add).u64().Inputs(16, 2);
214             INST(18, Opcode::Add).u64().Inputs(17, 3);
215             INST(19, Opcode::Add).u64().Inputs(18, 4);
216             INST(20, Opcode::Add).u64().Inputs(19, 5);
217             INST(21, Opcode::Add).u64().Inputs(20, 6);
218             INST(22, Opcode::Add).u64().Inputs(21, 7);
219             INST(23, Opcode::Add).f64().Inputs(8, 9);
220             INST(24, Opcode::Add).f64().Inputs(23, 10);
221             INST(25, Opcode::Add).f64().Inputs(24, 11);
222             INST(26, Opcode::Add).f64().Inputs(25, 12);
223             INST(27, Opcode::Add).f64().Inputs(26, 13);
224             INST(28, Opcode::Add).f64().Inputs(27, 14);
225             INST(29, Opcode::Add).f64().Inputs(28, 15);
226             INST(30, Opcode::Cast).u64().SrcType(DataType::FLOAT64).Inputs(29);
227             INST(31, Opcode::Add).u64().Inputs(30, 22);
228 
229             INST(44, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68);
230             INST(32, Opcode::NewArray).ref().TypeId(8).Inputs(44, 31);
231             INST(33, Opcode::Return).ref().Inputs(32);
232         }
233     }
234 
235     SetNumArgs(ARGS_COUNT * 2);
236     SetNumVirtRegs(0);
237     GraphChecker(GetGraph()).Check();
238     RegAlloc(GetGraph());
239     ASSERT_TRUE(GetGraph()->RunPass<Codegen>());
240 
241     auto code_entry = reinterpret_cast<vixl::aarch64::Instruction *>(GetGraph()->GetData().Data());
242     auto code_exit = code_entry + GetGraph()->GetData().Size();
243     ASSERT(code_entry != nullptr && code_exit != nullptr);
244     auto &decoder {GetDecoder()};
245     LoadStoreRegistersCollector visitor;
246     vixl::aarch64::Decoder::ScopedVisitors sv(decoder, {&visitor});
247     for (auto instr = code_entry; instr < code_exit; instr += vixl::aarch64::kInstructionSize) {
248         decoder.Decode(instr);
249     }
250     // not using reg lists from vixl::aarch64 to check only r0-r7
251     constexpr auto CALLER_REGS = RegMask((1 << ARGS_COUNT) - 1);
252     EXPECT_EQ(visitor.GetAccessedRegisters() & CALLER_REGS, RegMask {0});
253     EXPECT_TRUE((visitor.GetAccessedVRegisters() & CALLER_REGS).none());
254 }
255 
256 class CodegenSpillFillCoalescingTest : public VixlDisasmTest {
257 public:
CodegenSpillFillCoalescingTest()258     CodegenSpillFillCoalescingTest()
259     {
260         options.SetCompilerSpillFillPair(true);
261         options.SetCompilerVerifyRegalloc(false);
262     }
263 
CheckSpillFillCoalescingForEvenRegsNumber(bool aligned)264     void CheckSpillFillCoalescingForEvenRegsNumber(bool aligned)
265     {
266         GRAPH(GetGraph())
267         {
268             BASIC_BLOCK(2, -1)
269             {
270                 INST(0, Opcode::SpillFill);
271                 INST(1, Opcode::ReturnVoid);
272             }
273         }
274 
275         int alignment_offset = aligned ? 1 : 0;
276 
277         auto sf_inst = INS(0).CastToSpillFill();
278         sf_inst->AddSpill(0, 0 + alignment_offset, DataType::Type::INT64);
279         sf_inst->AddSpill(1, 1 + alignment_offset, DataType::Type::INT64);
280         sf_inst->AddSpill(0, 2 + alignment_offset, DataType::Type::FLOAT64);
281         sf_inst->AddSpill(1, 3 + alignment_offset, DataType::Type::FLOAT64);
282         sf_inst->AddFill(4 + alignment_offset, 3, DataType::Type::INT64);
283         sf_inst->AddFill(5 + alignment_offset, 2, DataType::Type::INT64);
284         sf_inst->AddFill(6 + alignment_offset, 3, DataType::Type::FLOAT64);
285         sf_inst->AddFill(7 + alignment_offset, 2, DataType::Type::FLOAT64);
286 
287         SetNumArgs(0);
288         SetNumVirtRegs(0);
289         GraphChecker(GetGraph()).Check();
290         GetGraph()->SetStackSlotsCount(8U + alignment_offset);
291         RegAlloc(GetGraph());
292         ASSERT_TRUE(GetGraph()->RunPass<Codegen>());
293 
294         auto code_entry = reinterpret_cast<vixl::aarch64::Instruction *>(GetGraph()->GetData().Data());
295         auto code_exit = code_entry + GetGraph()->GetData().Size();
296         ASSERT(code_entry != nullptr && code_exit != nullptr);
297         auto &decoder {GetDecoder()};
298         LoadStoreInstCollector visitor;
299         vixl::aarch64::Decoder::ScopedVisitors sv(decoder, {&visitor});
300         for (auto instr = code_entry; instr < code_exit; instr += vixl::aarch64::kInstructionSize) {
301             decoder.Decode(instr);
302         }
303         EXPECT_EQ(visitor.GetStpX(), 1 /* 1 use pre increment and not counted */);
304         EXPECT_EQ(visitor.GetLdpX(), 1 /* 1 use post increment and not counted */);
305         EXPECT_EQ(visitor.GetLdpD(), 1);
306         EXPECT_EQ(visitor.GetStpD(), 1);
307 
308         constexpr auto TEST_REGS = RegMask(0xF);
309         EXPECT_EQ(visitor.GetAccessedPairRegisters() & TEST_REGS, RegMask {0xF});
310         EXPECT_EQ(visitor.GetAccessedPairVRegisters() & TEST_REGS, RegMask {0xF});
311     }
312 
CheckSpillFillCoalescingForOddRegsNumber(bool aligned)313     void CheckSpillFillCoalescingForOddRegsNumber(bool aligned)
314     {
315         GRAPH(GetGraph())
316         {
317             BASIC_BLOCK(2, -1)
318             {
319                 INST(0, Opcode::SpillFill);
320                 INST(1, Opcode::ReturnVoid);
321             }
322         }
323 
324         int alignment_offset = aligned ? 1 : 0;
325 
326         auto sf_inst = INS(0).CastToSpillFill();
327         sf_inst->AddSpill(0, 0 + alignment_offset, DataType::Type::INT64);
328         sf_inst->AddSpill(1, 1 + alignment_offset, DataType::Type::INT64);
329         sf_inst->AddSpill(2, 2 + alignment_offset, DataType::Type::INT64);
330         sf_inst->AddSpill(0, 3 + alignment_offset, DataType::Type::FLOAT64);
331         sf_inst->AddSpill(1, 4 + alignment_offset, DataType::Type::FLOAT64);
332         sf_inst->AddSpill(2, 5 + alignment_offset, DataType::Type::FLOAT64);
333         sf_inst->AddFill(6 + alignment_offset, 3, DataType::Type::INT64);
334         sf_inst->AddFill(7 + alignment_offset, 4, DataType::Type::INT64);
335         sf_inst->AddFill(8 + alignment_offset, 5, DataType::Type::INT64);
336         sf_inst->AddFill(9 + alignment_offset, 3, DataType::Type::FLOAT64);
337         sf_inst->AddFill(10 + alignment_offset, 4, DataType::Type::FLOAT64);
338         sf_inst->AddFill(11 + alignment_offset, 5, DataType::Type::FLOAT64);
339 
340         SetNumArgs(0);
341         SetNumVirtRegs(0);
342         GraphChecker(GetGraph()).Check();
343         GetGraph()->SetStackSlotsCount(12U + alignment_offset);
344         RegAlloc(GetGraph());
345         ASSERT_TRUE(GetGraph()->RunPass<Codegen>());
346 
347         auto code_entry = reinterpret_cast<vixl::aarch64::Instruction *>(GetGraph()->GetData().Data());
348         auto code_exit = code_entry + GetGraph()->GetData().Size();
349         ASSERT(code_entry != nullptr && code_exit != nullptr);
350         auto &decoder {GetDecoder()};
351         LoadStoreInstCollector visitor;
352         vixl::aarch64::Decoder::ScopedVisitors sv(decoder, {&visitor});
353         for (auto instr = code_entry; instr < code_exit; instr += vixl::aarch64::kInstructionSize) {
354             decoder.Decode(instr);
355         }
356         EXPECT_EQ(visitor.GetStpX(), 1 /* 1 use pre increment and not counted */);
357         EXPECT_EQ(visitor.GetLdpX(), 1 /* 1 use post increment and not counted */);
358         EXPECT_EQ(visitor.GetLdpD(), 1);
359         EXPECT_EQ(visitor.GetStpD(), 1);
360 
361         constexpr auto TEST_REGS = RegMask(0x3F);
362         if (aligned) {
363             EXPECT_EQ(visitor.GetAccessedPairRegisters() & TEST_REGS, RegMask {0b11011});
364             EXPECT_EQ(visitor.GetAccessedPairVRegisters() & TEST_REGS, RegMask {0b110110});
365         } else {
366             EXPECT_EQ(visitor.GetAccessedPairRegisters() & TEST_REGS, RegMask {0b110110});
367             EXPECT_EQ(visitor.GetAccessedPairVRegisters() & TEST_REGS, RegMask {0b11011});
368         }
369     }
370 };
371 
TEST_F(CodegenSpillFillCoalescingTest,CoalesceAccessToUnalignedNeighborSlotsEvenRegsNumber)372 TEST_F(CodegenSpillFillCoalescingTest, CoalesceAccessToUnalignedNeighborSlotsEvenRegsNumber)
373 {
374     CheckSpillFillCoalescingForEvenRegsNumber(false);
375 }
376 
TEST_F(CodegenSpillFillCoalescingTest,CoalesceAccessToAlignedNeighborSlotsEvenRegsNumber)377 TEST_F(CodegenSpillFillCoalescingTest, CoalesceAccessToAlignedNeighborSlotsEvenRegsNumber)
378 {
379     CheckSpillFillCoalescingForEvenRegsNumber(true);
380 }
381 
TEST_F(CodegenSpillFillCoalescingTest,CoalesceAccessToUnalignedNeighborSlotsOddRegsNumber)382 TEST_F(CodegenSpillFillCoalescingTest, CoalesceAccessToUnalignedNeighborSlotsOddRegsNumber)
383 {
384     CheckSpillFillCoalescingForOddRegsNumber(false);
385 }
386 
TEST_F(CodegenSpillFillCoalescingTest,CoalesceAccessToAlignedNeighborSlotsOddRegsNumber)387 TEST_F(CodegenSpillFillCoalescingTest, CoalesceAccessToAlignedNeighborSlotsOddRegsNumber)
388 {
389     CheckSpillFillCoalescingForOddRegsNumber(true);
390 }
391 
392 // clang-format off
393 class CodegenLeafPrologueTest : public VixlDisasmTest {
394 public:
CodegenLeafPrologueTest()395     CodegenLeafPrologueTest()
396     {
397         options.SetCompilerVerifyRegalloc(false);
398 #ifndef NDEBUG
399         graph_->SetLowLevelInstructionsEnabled();
400 #endif
401     }
402 
CheckLeafPrologue()403     void CheckLeafPrologue()
404     {
405         // RedundantOps::inc()
406         RuntimeInterface::FieldPtr i = (void *)(0xDEADBEEF);
407         GRAPH(GetGraph())
408         {
409             PARAMETER(0, 0).ref();
410             BASIC_BLOCK(2, 3)
411             {
412                 INST(1, Opcode::LoadObject).s64().Inputs(0).TypeId(208U).ObjField(i);
413                 INST(2, Opcode::AddI).s64().Inputs(1).Imm(1);
414                 INST(3, Opcode::StoreObject).s64().Inputs(0, 2).TypeId(208U).ObjField(i);
415             }
416             BASIC_BLOCK(3, -1)
417             {
418                 INST(4, Opcode::ReturnVoid);
419             }
420         }
421         SetNumArgs(1);
422 
423         std::vector<std::string> expected_asm = {
424 #ifdef PANDA_COMPILER_CFI
425             "stp x29, x30, [sp, #-16]!",  // prolog save FP and LR
426             "mov x29, sp",                // prolog set FP
427             "stp x19, x20, [sp, #-80]",   // prolog save callee-saved
428 #else
429             "stp x19, x20, [sp, #-96]",   // prolog save callee-saved
430 #endif
431             "ldr x19, [x1]",
432             "add x19, x19, #0x1 // (1)",
433             "str x19, [x1]",
434 #ifdef PANDA_COMPILER_CFI
435             "ldp x19, x20, [sp, #-80]",   // epilog restore callee-saved
436             "ldp x29, x30, [sp], #16",    // epilog restore FP and LR
437 #else
438             "ldp x19, x20, [sp, #-96]",   // epilog restore callee-saved
439 #endif
440             "ret"};
441 
442         GraphChecker(GetGraph()).Check();
443         GetGraph()->RunPass<RegAllocLinearScan>();
444         bool setup_frame = GetGraph()->GetMethodProperties().GetRequireFrameSetup();
445         ASSERT_TRUE(setup_frame ? GetGraph()->RunPass<Codegen>() : GetGraph()->RunPass<CodegenNative>());
446         ASSERT_TRUE(GetGraph()->GetData().Size() == expected_asm.size() * vixl::aarch64::kInstructionSize);
447         auto code_entry = reinterpret_cast<vixl::aarch64::Instruction *>(GetGraph()->GetData().Data());
448         auto code_exit = code_entry + GetGraph()->GetData().Size();
449         size_t code_items = (code_exit - code_entry) / vixl::aarch64::kInstructionSize;
450         ASSERT_TRUE(code_items == expected_asm.size());
451 
452         auto& decoder {GetDecoder()};
453         auto disasm(CreateDisassembler());
454         vixl::aarch64::Decoder::ScopedVisitors sv(decoder, {&disasm});
455         for (size_t item = 0; item < code_items; ++item) {
456             decoder.Decode(code_entry + item * vixl::aarch64::kInstructionSize);
457             EXPECT_EQ(expected_asm.at(item), disasm.GetOutput());
458         }
459     }
460 
CheckLeafWithParamsOnStackPrologue()461     void CheckLeafWithParamsOnStackPrologue()
462     {
463         // RedundantOps::sum()
464         RuntimeInterface::FieldPtr i = (void *)(0xDEADBEEF);
465         GRAPH(GetGraph())
466         {
467             PARAMETER(0, 0).ref();
468             PARAMETER(1, 1).s64();
469             PARAMETER(2, 2).s64();
470             PARAMETER(3, 3).s64();
471             PARAMETER(4, 4).s64();
472             PARAMETER(5, 5).s64();
473             PARAMETER(6, 6).s64();
474             PARAMETER(7, 7).s64();
475             PARAMETER(8, 8).s64();
476 
477             BASIC_BLOCK(2, -1)
478             {
479                 INST(10, Opcode::Add).s64().Inputs(1, 2);
480                 INST(11, Opcode::Add).s64().Inputs(3, 4);
481                 INST(12, Opcode::Add).s64().Inputs(5, 6);
482                 INST(13, Opcode::Add).s64().Inputs(7, 8);
483                 INST(14, Opcode::Add).s64().Inputs(10, 11);
484                 INST(15, Opcode::Add).s64().Inputs(12, 13);
485                 INST(16, Opcode::Add).s64().Inputs(14, 15);
486                 INST(17, Opcode::StoreObject).s64().Inputs(0, 16).TypeId(301U).ObjField(i);
487                 INST(18, Opcode::ReturnVoid);
488             }
489         }
490         SetNumArgs(9);
491 
492         // In this case two parameters are passed on stack,
493         // thus to address them SP needs to be adjusted in prolog/epilog.
494         std::vector<std::string> expected_asm = {
495 #ifdef PANDA_COMPILER_CFI
496             "stp x29, x30, [sp, #-16]!",    // prolog save FP and LR
497             "mov x29, sp",                  // prolog set FP
498             "stp x19, x20, [sp, #-112]",    // prolog callee-saved
499             "stp x21, x22, [sp, #-96]",     // prolog callee-saved
500             "stp x23, x24, [sp, #-80]",     // prolog callee-saved
501             "sub sp, sp, #0x230 // (560)",  // prolog adjust SP
502 #else
503             "stp x19, x20, [sp, #-128]",    // prolog callee-saved
504             "stp x21, x22, [sp, #-112]",    // prolog callee-saved
505             "stp x23, x24, [sp, #-96]",     // prolog callee-saved
506             "sub sp, sp, #0x240 // (576)",  // prolog adjust SP
507 #endif
508             "add",                          // "add x19, x2, x3"
509             "add",                          // "add x21, x4, x5"
510             "add",                          // "add x22, x6, x7"
511             "add x16, sp, #0x240 // (576)", // load params from stack
512             "ldp x23, x24, [x16]",          // load params from stack
513             "add",                          // "add x23, x23, x24"
514             "add",                          // "add x19, x19, x21"
515             "add",                          // "add x21, x22, x23"
516             "add",                          // "add x19, x19, x21"
517             "str x19, [x1]",
518             "ldp x19, x20, [sp, #448]",     // restore callee-saved
519             "ldp x21, x22, [sp, #464]",     // restore callee-saved
520             "ldp x23, x24, [sp, #480]",     // restore callee-saved
521 #ifdef PANDA_COMPILER_CFI
522             "add sp, sp, #0x230 // (560)",  // epilog adjust SP
523             "ldp x29, x30, [sp], #16",      // epilog restore FP and LR
524 #else
525             "add sp, sp, #0x240 // (576)",  // epilog adjust SP
526 #endif
527             "ret"};
528 
529         std::regex add_regex("^add[[:blank:]]+x[0-9]+,[[:blank:]]+x[0-9]+,[[:blank:]]+x[0-9]+",
530                              std::regex::egrep | std::regex::icase);
531 
532         GraphChecker(GetGraph()).Check();
533         GetGraph()->RunPass<RegAllocLinearScan>();
534         bool setup_frame = GetGraph()->GetMethodProperties().GetRequireFrameSetup();
535         ASSERT_TRUE(setup_frame ? GetGraph()->RunPass<Codegen>() : GetGraph()->RunPass<CodegenNative>());
536         ASSERT_TRUE(GetGraph()->GetData().Size() == expected_asm.size() * vixl::aarch64::kInstructionSize);
537         auto code_entry = reinterpret_cast<vixl::aarch64::Instruction *>(GetGraph()->GetData().Data());
538         auto code_exit = code_entry + GetGraph()->GetData().Size();
539         size_t code_items = (code_exit - code_entry) / vixl::aarch64::kInstructionSize;
540         ASSERT_TRUE(code_items == expected_asm.size());
541 
542         auto& decoder {GetDecoder()};
543         auto disasm(CreateDisassembler());
544         vixl::aarch64::Decoder::ScopedVisitors sv(decoder, {&disasm});
545         for (size_t item = 0; item < code_items; ++item) {
546             decoder.Decode(code_entry + item * vixl::aarch64::kInstructionSize);
547             // replace 'add rx, ry, rz' with 'add' to make comparison independent of regalloc
548             std::string s = std::regex_replace(disasm.GetOutput(), add_regex, "add");
549             EXPECT_EQ(expected_asm.at(item), s);
550         }
551     }
552 };
553 // clang-format on
554 
TEST_F(CodegenLeafPrologueTest,LeafPrologueGeneration)555 TEST_F(CodegenLeafPrologueTest, LeafPrologueGeneration)
556 {
557     CheckLeafPrologue();
558 }
559 
TEST_F(CodegenLeafPrologueTest,LeafWithParamsOnStackPrologueGeneration)560 TEST_F(CodegenLeafPrologueTest, LeafWithParamsOnStackPrologueGeneration)
561 {
562     CheckLeafWithParamsOnStackPrologue();
563 }
564 
565 class CodegenTest : public VixlDisasmTest {
566 public:
567     template <typename T, size_t len>
AssertCode(const T (& expected_code)[len])568     void AssertCode(const T (&expected_code)[len])
569     {
570         auto code_entry = reinterpret_cast<vixl::aarch64::Instruction *>(GetGraph()->GetData().Data());
571         auto code_exit = code_entry + GetGraph()->GetData().Size();
572         ASSERT(code_entry != nullptr && code_exit != nullptr);
573         auto &decoder {GetDecoder()};
574         auto disasm(CreateDisassembler());
575         vixl::aarch64::Decoder::ScopedVisitors sv(decoder, {&disasm});
576 
577         size_t index = 0;
578         for (auto instr = code_entry; instr < code_exit; instr += vixl::aarch64::kInstructionSize) {
579             decoder.Decode(instr);
580             auto output = disasm.GetOutput();
581             if (index == 0) {
582                 if (std::strncmp(output, expected_code[index], std::strlen(expected_code[index])) == 0) {
583                     index++;
584                 }
585                 continue;
586             }
587             if (index >= len) {
588                 break;
589             }
590             ASSERT_TRUE(std::strncmp(output, expected_code[index], std::strlen(expected_code[index])) == 0);
591             index++;
592         }
593         ASSERT_EQ(index, len);
594     }
595 };
596 
TEST_F(CodegenTest,CallVirtual)597 TEST_F(CodegenTest, CallVirtual)
598 {
599     auto graph = GetGraph();
600     GRAPH(graph)
601     {
602         PARAMETER(0, 0).ref();
603         PARAMETER(1, 1).i32();
604         BASIC_BLOCK(2, -1)
605         {
606             INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1});
607             INST(3, Opcode::CallVirtual).v0id().InputsAutoType(0, 1, 2);
608             INST(4, Opcode::ReturnVoid).v0id();
609         }
610     }
611     EXPECT_TRUE(RegAlloc(graph));
612     EXPECT_TRUE(graph->RunPass<Codegen>());
613     // exclude offset from verification to avoid test modifications
614     const char *expected_code[] = {"ldr w0, [x1, #", "ldr x0, [x0, #", "ldr x30, [x0, #",
615                                    "blr x30"};  // CallVirtual is encoded without tmp reg
616     AssertCode(expected_code);
617 }
618 
TEST_F(CodegenTest,EncodeMemCopy)619 TEST_F(CodegenTest, EncodeMemCopy)
620 {
621     auto graph = GetGraph();
622     GRAPH(graph)
623     {
624         CONSTANT(0, 0).i32().DstReg(0U);
625         BASIC_BLOCK(2, -1)
626         {
627             INST(2, Opcode::SpillFill);
628             INST(3, Opcode::Return).i32().Inputs(0).DstReg(0U);
629         }
630     }
631     auto spill_fill = INS(2).CastToSpillFill();
632     // Add moves chain: R0 -> S0 -> S1 -> R0 [u32]
633     spill_fill->AddSpillFill(Location::MakeRegister(0), Location::MakeStackSlot(0), DataType::INT32);
634     spill_fill->AddSpillFill(Location::MakeStackSlot(0), Location::MakeStackSlot(1), DataType::INT32);
635     spill_fill->AddSpillFill(Location::MakeStackSlot(1), Location::MakeRegister(0), DataType::INT32);
636 
637     graph->SetStackSlotsCount(2U);
638 #ifndef NDEBUG
639     graph->SetRegAllocApplied();
640 #endif
641     EXPECT_TRUE(graph->RunPass<Codegen>());
642 
643     // Check that stack slots are 64-bit wide
644     const char *expected_code[] = {"str x0, [sp, #16]", "ldr x16, [sp, #16]", "str x16, [sp, #8]", "ldr w0, [sp, #8]"};
645     AssertCode(expected_code);
646 }
647 
TEST_F(CodegenTest,EncodeWithZeroReg)648 TEST_F(CodegenTest, EncodeWithZeroReg)
649 {
650     // MAdd a, b, c <=> c + a * b
651 
652     // a = 0
653     {
654         auto graph = GetGraph();
655         GRAPH(graph)
656         {
657             CONSTANT(0, 0).i64();
658             PARAMETER(1, 0).i64();
659             PARAMETER(2, 1).i64();
660 
661             BASIC_BLOCK(2, -1)
662             {
663                 INST(3, Opcode::MAdd).i64().Inputs(0, 1, 2);
664                 INST(4, Opcode::Return).i64().Inputs(3);
665             }
666         }
667 
668         EXPECT_TRUE(RegAlloc(graph));
669         EXPECT_TRUE(graph->RunPass<Codegen>());
670 
671         const char *expected_code[] = {"mov x0, x2"};
672         AssertCode(expected_code);
673         ResetGraph();
674     }
675 
676     // b = 0
677     {
678         auto graph = GetGraph();
679         GRAPH(graph)
680         {
681             CONSTANT(0, 0).i64();
682             PARAMETER(1, 0).i64();
683             PARAMETER(2, 1).i64();
684 
685             BASIC_BLOCK(2, -1)
686             {
687                 INST(3, Opcode::MAdd).i64().Inputs(1, 0, 2);
688                 INST(4, Opcode::Return).i64().Inputs(3);
689             }
690         }
691 
692         EXPECT_TRUE(RegAlloc(graph));
693         EXPECT_TRUE(graph->RunPass<Codegen>());
694 
695         const char *expected_code[] = {"mov x0, x2"};
696         AssertCode(expected_code);
697         ResetGraph();
698     }
699 
700     // c = 0
701     {
702         auto graph = GetGraph();
703         GRAPH(graph)
704         {
705             CONSTANT(0, 0).i64();
706             PARAMETER(1, 0).i64();
707             PARAMETER(2, 1).i64();
708 
709             BASIC_BLOCK(2, -1)
710             {
711                 INST(3, Opcode::MAdd).i64().Inputs(1, 2, 0);
712                 INST(4, Opcode::Return).i64().Inputs(3);
713             }
714         }
715 
716         EXPECT_TRUE(RegAlloc(graph));
717         EXPECT_TRUE(graph->RunPass<Codegen>());
718 
719         const char *expected_code[] = {"mul x0, x1, x2"};
720         AssertCode(expected_code);
721         ResetGraph();
722     }
723 
724     // MSub a, b, c <=> c - a * b
725 
726     // a = 0
727     {
728         auto graph = GetGraph();
729         GRAPH(graph)
730         {
731             CONSTANT(0, 0).i64();
732             PARAMETER(1, 0).i64();
733             PARAMETER(2, 1).i64();
734 
735             BASIC_BLOCK(2, -1)
736             {
737                 INST(3, Opcode::MSub).i64().Inputs(0, 1, 2);
738                 INST(4, Opcode::Return).i64().Inputs(3);
739             }
740         }
741 
742         EXPECT_TRUE(RegAlloc(graph));
743         EXPECT_TRUE(graph->RunPass<Codegen>());
744 
745         const char *expected_code[] = {"mov x0, x2"};
746         AssertCode(expected_code);
747         ResetGraph();
748     }
749 
750     // b = 0
751     {
752         auto graph = GetGraph();
753         GRAPH(graph)
754         {
755             CONSTANT(0, 0).i64();
756             PARAMETER(1, 0).i64();
757             PARAMETER(2, 1).i64();
758 
759             BASIC_BLOCK(2, -1)
760             {
761                 INST(3, Opcode::MSub).i64().Inputs(1, 0, 2);
762                 INST(4, Opcode::Return).i64().Inputs(3);
763             }
764         }
765 
766         EXPECT_TRUE(RegAlloc(graph));
767         EXPECT_TRUE(graph->RunPass<Codegen>());
768 
769         const char *expected_code[] = {"mov x0, x2"};
770         AssertCode(expected_code);
771         ResetGraph();
772     }
773 
774     // c = 0
775     {
776         auto graph = GetGraph();
777         GRAPH(graph)
778         {
779             CONSTANT(0, 0).i64();
780             PARAMETER(1, 0).i64();
781             PARAMETER(2, 1).i64();
782 
783             BASIC_BLOCK(2, -1)
784             {
785                 INST(3, Opcode::MSub).i64().Inputs(1, 2, 0);
786                 INST(4, Opcode::Return).i64().Inputs(3);
787             }
788         }
789 
790         EXPECT_TRUE(RegAlloc(graph));
791         EXPECT_TRUE(graph->RunPass<Codegen>());
792 
793         const char *expected_code[] = {"mneg x0, x1, x2"};
794         AssertCode(expected_code);
795         ResetGraph();
796     }
797 
798     // MNeg a, b <=> -(a * b)
799 
800     // a = 0
801     {
802         auto graph = GetGraph();
803         GRAPH(graph)
804         {
805             CONSTANT(0, 0).i64();
806             PARAMETER(1, 0).i64();
807 
808             BASIC_BLOCK(2, -1)
809             {
810                 INST(3, Opcode::MNeg).i64().Inputs(0, 1);
811                 INST(4, Opcode::Return).i64().Inputs(3);
812             }
813         }
814 
815         EXPECT_TRUE(RegAlloc(graph));
816         EXPECT_TRUE(graph->RunPass<Codegen>());
817 
818         const char *expected_code[] = {"mov x0, #0"};
819         AssertCode(expected_code);
820         ResetGraph();
821     }
822 
823     // b = 0
824     {
825         auto graph = GetGraph();
826         GRAPH(graph)
827         {
828             CONSTANT(0, 0).i64();
829             PARAMETER(1, 0).i64();
830 
831             BASIC_BLOCK(2, -1)
832             {
833                 INST(3, Opcode::MNeg).i64().Inputs(1, 0);
834                 INST(4, Opcode::Return).i64().Inputs(3);
835             }
836         }
837 
838         EXPECT_TRUE(RegAlloc(graph));
839         EXPECT_TRUE(graph->RunPass<Codegen>());
840 
841         const char *expected_code[] = {"mov x0, #0"};
842         AssertCode(expected_code);
843         ResetGraph();
844     }
845 }
846 
847 }  // namespace panda::compiler
848