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 "unit_test.h"
17#include "optimizer/ir_builder/inst_builder-inl.h"
18
19namespace panda::compiler {
20inline bool operator==(const Input &lhs, const Inst *rhs)
21{
22    return lhs.GetInst() == rhs;
23}
24
25class InstTest : public GraphTest {
26};
27
28TEST_F(InstTest, Dataflow)
29{
30    /**
31     * '=' is a definition
32     *
33     *           [2]
34     *            |
35     *    /---------------\
36     *    |               |
37     *   [3]=            [4]=
38     *    |               |
39     *    |          /---------\
40     *   [5]         |         |
41     *    |          |        [6] (need for removing #6)
42     *    |          |         |
43     *    |          |        [7]=
44     *    |          |         |
45     *    \---------[8]--------/
46     *          PHI(1,2,4)
47     *
48     */
49    GRAPH(GetGraph())
50    {
51        CONSTANT(0, 12);
52        CONSTANT(1, 13);
53
54        BASIC_BLOCK(2, 3, 4)
55        {
56            INST(2, Opcode::Add).u64().Inputs(0, 1);
57            INST(8, Opcode::Compare).b().SrcType(DataType::Type::INT64).Inputs(0, 1);
58            INST(9, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(8);
59        }
60        BASIC_BLOCK(3, 5)
61        {
62            INST(3, Opcode::Not).u64().Inputs(0);
63        }
64        BASIC_BLOCK(4, 8, 6)
65        {
66            INST(4, Opcode::Not).u64().Inputs(1);
67            INST(11, Opcode::Compare).b().SrcType(DataType::Type::INT64).Inputs(0, 1);
68            INST(12, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(11);
69        }
70        BASIC_BLOCK(5, 8)
71        {
72            INST(7, Opcode::Sub).u64().Inputs(3, 2);
73        }
74        BASIC_BLOCK(6, 7) {}
75        BASIC_BLOCK(7, 8)
76        {
77            INST(5, Opcode::Not).u64().Inputs(4);
78        }
79        BASIC_BLOCK(8, -1)
80        {
81            INST(6, Opcode::Phi).u64().Inputs({{5, 3}, {4, 4}, {7, 5}});
82            INST(16, Opcode::ReturnVoid);
83        }
84    }
85
86    // Check constructed dataflow
87    ASSERT_TRUE(CheckUsers(INS(0), {2, 3, 8, 11}));
88    ASSERT_TRUE(CheckUsers(INS(1), {2, 4, 8, 11}));
89    ASSERT_TRUE(CheckUsers(INS(2), {7}));
90    ASSERT_TRUE(CheckUsers(INS(3), {6, 7}));
91    ASSERT_TRUE(CheckUsers(INS(4), {5, 6}));
92    ASSERT_TRUE(CheckUsers(INS(5), {6}));
93    ASSERT_TRUE(CheckInputs(INS(2), {0, 1}));
94    ASSERT_TRUE(CheckInputs(INS(3), {0}));
95    ASSERT_TRUE(CheckInputs(INS(7), {3, 2}));
96    ASSERT_TRUE(CheckInputs(INS(4), {1}));
97    ASSERT_TRUE(CheckInputs(INS(5), {4}));
98    ASSERT_TRUE(CheckInputs(INS(6), {3, 4, 5}));
99    ASSERT_EQ(static_cast<PhiInst &>(INS(6)).GetPhiInput(&BB(5)), &INS(3));
100    ASSERT_EQ(static_cast<PhiInst &>(INS(6)).GetPhiInput(&BB(4)), &INS(4));
101    ASSERT_EQ(static_cast<PhiInst &>(INS(6)).GetPhiInput(&BB(7)), &INS(5));
102
103    {  // Test iterating over users of constant instruction
104        const Inst *inst = &INS(2);
105        for (auto &user : inst->GetUsers()) {
106            ASSERT_EQ(inst, user.GetInput());
107        }
108    }
109
110    {  // Test iterating over users of non-constant instruction
111        Inst *inst = &INS(2);
112        for (auto &user : inst->GetUsers()) {
113            user.GetInst()->SetId(user.GetInst()->GetId());
114        }
115    }
116
117    // 1. Remove instruction #3, replace its users by its input
118    INS(3).ReplaceUsers(INS(3).GetInput(0).GetInst());
119    INS(3).GetBasicBlock()->RemoveInst(&INS(3));
120    ASSERT_TRUE(INS(6).GetInput(0).GetInst() == &INS(0));
121    ASSERT_TRUE(INS(3).GetInput(0).GetInst() == nullptr);
122    ASSERT_TRUE(CheckUsers(INS(0), {2, 6, 7, 8, 11}));
123    ASSERT_EQ(static_cast<PhiInst &>(INS(6)).GetPhiInput(&BB(5)), &INS(0));
124    GraphChecker(GetGraph()).Check();
125
126    // TODO(A.Popov): refactor RemovePredsBlocks
127    // 2. Remove basic block #4, phi should be fixed properly
128    // INS(5).RemoveInputs()
129    // INS(5).GetBasicBlock()->EraseInst(&INS(5))
130    // GetGraph()->DisconnectBlock(&BB(7))
131    // ASSERT_TRUE(INS(6).GetInputsCount() == 2)
132    // static_cast<PhiInst&>(INS(6)).GetPhiInput(&BB(5)), &INS(0))
133    // static_cast<PhiInst&>(INS(6)).GetPhiInput(&BB(4)), &INS(4))
134    GraphChecker(GetGraph()).Check();
135
136    // 3. Append additional inputs into PHI, thereby force it to reallocate inputs storage, dataflow is not valid  from
137    // this moment
138    for (int i = 0; i < 4; ++i) {
139        INS(6).AppendInput(&INS(0));
140    }
141}
142
143TEST_F(InstTest, Arithmetics)
144{
145    GRAPH(GetGraph())
146    {
147        CONSTANT(0, 12);
148        CONSTANT(1, 17.23);
149
150        BASIC_BLOCK(2, -1)
151        {
152            INST(2, Opcode::Cast).u64().SrcType(DataType::FLOAT64).Inputs(1);
153            INST(3, Opcode::Add).u64().Inputs(0, 2);
154            INST(4, Opcode::ReturnVoid);
155        }
156    }
157}
158
159TEST_F(InstTest, Memory)
160{
161    GRAPH(GetGraph())
162    {
163        PARAMETER(0, 0).ref();  // array
164        PARAMETER(1, 1).u64();  // index
165        BASIC_BLOCK(2, -1)
166        {
167            INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1});
168            INST(3, Opcode::NullCheck).ref().Inputs(0, 2);
169            INST(4, Opcode::LenArray).s32().Inputs(3);
170            INST(5, Opcode::BoundsCheck).s32().Inputs(4, 1, 2);
171            INST(6, Opcode::LoadArray).u64().Inputs(3, 5);
172            INST(7, Opcode::Add).u64().Inputs(6, 6);
173            INST(8, Opcode::StoreArray).u64().Inputs(3, 5, 7);
174            INST(9, Opcode::ReturnVoid);
175        }
176    }
177}
178
179TEST_F(InstTest, Const)
180{
181    int32_t int32_const[3] = {-5, 0, 5};
182    int64_t int64_const[3] = {-5, 0, 5};
183    GRAPH(GetGraph())
184    {
185        BASIC_BLOCK(2, -1)
186        {
187            INST(0, Opcode::ReturnVoid);
188        }
189    }
190    auto start = GetGraph()->GetStartBlock();
191    for (auto i = 0; i < 3; i++) {
192        int32_t val = int32_const[i];
193        auto const1 = GetGraph()->FindOrCreateConstant(val);
194        ASSERT_EQ(const1->GetType(), DataType::INT64);
195        ASSERT_EQ(const1->GetBasicBlock(), start);
196        uint64_t val1 = int64_const[i];
197        auto const2 = GetGraph()->FindOrCreateConstant(val1);
198        ASSERT_EQ(const2->GetType(), DataType::INT64);
199        ASSERT_EQ(const1, const2);
200        ASSERT_EQ(const1->GetIntValue(), val1);
201    }
202    GraphChecker(GetGraph()).Check();
203    float float_const[3] = {-5.5f, 0.1f, 5.2f};
204    for (auto i = 0; i < 3; i++) {
205        float val = float_const[i];
206        auto const1 = GetGraph()->FindOrCreateConstant(val);
207        ASSERT_EQ(const1->GetType(), DataType::FLOAT32);
208        ASSERT_EQ(const1->GetBasicBlock(), start);
209        auto const2 = GetGraph()->FindOrCreateConstant(val);
210        ASSERT_EQ(const1, const2);
211        ASSERT_EQ(const1->GetFloatValue(), val);
212    }
213    GraphChecker(GetGraph()).Check();
214    double double_const[3] = {-5.5, 0.1, 5.2};
215    for (auto i = 0; i < 3; i++) {
216        double val = double_const[i];
217        auto const1 = GetGraph()->FindOrCreateConstant(val);
218        ASSERT_EQ(const1->GetType(), DataType::FLOAT64);
219        ASSERT_EQ(const1->GetBasicBlock(), start);
220        auto const2 = GetGraph()->FindOrCreateConstant(val);
221        ASSERT_EQ(const1, const2);
222        ASSERT_EQ(const1->GetDoubleValue(), val);
223    }
224    int i = 0;
225    for (auto current_const = GetGraph()->GetFirstConstInst(); current_const != nullptr;
226         current_const = current_const->GetNextConst()) {
227        i++;
228    }
229    ASSERT_EQ(i, 9);
230}
231
232TEST_F(InstTest, Const32)
233{
234    int32_t int32_const[3] = {-5, 0, 5};
235    int64_t int64_const[3] = {-5, 0, 5};
236    auto graph = CreateEmptyBytecodeGraph();
237
238    GRAPH(graph)
239    {
240        BASIC_BLOCK(2, -1)
241        {
242            INST(0, Opcode::ReturnVoid);
243        }
244    }
245    auto start = graph->GetStartBlock();
246    for (auto i = 0; i < 3; i++) {
247        // add first int32 constant
248        int32_t val = int32_const[i];
249        auto const1 = graph->FindOrCreateConstant(val);
250        ASSERT_EQ(const1->GetType(), DataType::INT32);
251        ASSERT_EQ(const1->GetBasicBlock(), start);
252        uint64_t val1 = int64_const[i];
253        // add int64 constant, graph creates new constant
254        auto const2 = graph->FindOrCreateConstant(val1);
255        ASSERT_EQ(const2->GetType(), DataType::INT64);
256        ASSERT_NE(const1, const2);
257        ASSERT_EQ(const2->GetBasicBlock(), start);
258        ASSERT_EQ(const1->GetIntValue(), val1);
259        // add second int32 constant, graph doesn't create new constant
260        int32_t val2 = int32_const[i];
261        auto const3 = graph->FindOrCreateConstant(val2);
262        ASSERT_EQ(const3, const1);
263        ASSERT_EQ(const1->GetInt32Value(), val2);
264    }
265    GraphChecker(graph).Check();
266}
267
268TEST_F(InstTest, ReturnVoid)
269{
270    GRAPH(GetGraph())
271    {
272        BASIC_BLOCK(2, -1)
273        {
274            INST(0, Opcode::ReturnVoid);
275        }
276    }
277}
278
279TEST_F(InstTest, ReturnFloat)
280{
281    GRAPH(GetGraph())
282    {
283        CONSTANT(0, 1.1f);
284        BASIC_BLOCK(2, -1)
285        {
286            INST(1, Opcode::Return).f32().Inputs(0);
287        }
288    }
289}
290
291TEST_F(InstTest, ReturnDouble)
292{
293    GRAPH(GetGraph())
294    {
295        CONSTANT(0, 1.1);
296        BASIC_BLOCK(2, -1)
297        {
298            INST(1, Opcode::Return).f64().Inputs(0);
299        }
300    }
301}
302
303TEST_F(InstTest, ReturnLong)
304{
305    uint64_t i = 1;
306    GRAPH(GetGraph())
307    {
308        CONSTANT(0, i);
309        BASIC_BLOCK(2, -1)
310        {
311            INST(1, Opcode::Return).u64().Inputs(0);
312        }
313    }
314}
315
316TEST_F(InstTest, ReturnInt)
317{
318    int32_t i = 1;
319    GRAPH(GetGraph())
320    {
321        CONSTANT(0, i);
322        BASIC_BLOCK(2, -1)
323        {
324            INST(1, Opcode::Return).u32().Inputs(0);
325        }
326    }
327}
328
329TEST_F(InstTest, ArrayChecks)
330{
331    GRAPH(GetGraph())
332    {
333        PARAMETER(0, 0).ref();  // array
334        PARAMETER(1, 1).u64();  // index
335        BASIC_BLOCK(2, -1)
336        {
337            INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1});
338            INST(3, Opcode::NullCheck).ref().Inputs(0, 2);
339            INST(4, Opcode::LenArray).s32().Inputs(3);
340            INST(5, Opcode::BoundsCheck).s32().Inputs(4, 1, 2);
341            INST(6, Opcode::LoadArray).u64().Inputs(3, 5);
342            INST(7, Opcode::Add).u64().Inputs(6, 6);
343            INST(8, Opcode::StoreArray).u64().Inputs(3, 5, 7);
344            INST(9, Opcode::ReturnVoid);
345        }
346    }
347}
348
349TEST_F(InstTest, ZeroCheck)
350{
351    GRAPH(GetGraph())
352    {
353        PARAMETER(0, 0).u64();
354        PARAMETER(1, 1).u64();
355        BASIC_BLOCK(2, -1)
356        {
357            INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1});
358            INST(3, Opcode::ZeroCheck).u64().Inputs(0, 2);
359            INST(4, Opcode::Div).u64().Inputs(1, 3);
360            INST(5, Opcode::Mod).u64().Inputs(1, 3);
361            INST(6, Opcode::ReturnVoid);
362        }
363    }
364}
365
366TEST_F(InstTest, Parametr)
367{
368    GRAPH(GetGraph())
369    {
370        PARAMETER(0, 0).u64();
371        PARAMETER(1, 1).u32();
372        PARAMETER(2, 4).u16();
373        PARAMETER(3, 5).u8();
374        PARAMETER(4, 8).s64();
375        PARAMETER(5, 10).s32();
376        PARAMETER(6, 11).s16();
377        PARAMETER(7, 24).s8();
378        PARAMETER(8, 27).b();
379        PARAMETER(9, 28).f64();
380        PARAMETER(10, 29).f32();
381        PARAMETER(11, 40).ref();
382        BASIC_BLOCK(2, -1)
383        {
384            INST(12, Opcode::Add).u32().Inputs(1, 5);
385            INST(13, Opcode::ReturnVoid);
386        }
387    }
388}
389
390TEST_F(InstTest, LenArray)
391{
392    GRAPH(GetGraph())
393    {
394        PARAMETER(0, 40).ref();
395        BASIC_BLOCK(2, -1)
396        {
397            INST(1, Opcode::LenArray).s32().Inputs(0);
398            INST(2, Opcode::ReturnVoid);
399        }
400    }
401}
402
403TEST_F(InstTest, Call)
404{
405    GRAPH(GetGraph())
406    {
407        PARAMETER(0, 1).ref();
408        PARAMETER(1, 2).u32();
409        PARAMETER(2, 4).u16();
410        PARAMETER(3, 5).u8();
411        PARAMETER(4, 8).s64();
412        BASIC_BLOCK(2, -1)
413        {
414            using namespace DataType;
415            INST(8, Opcode::SaveState).NoVregs();
416            INST(5, Opcode::CallVirtual).s32().InputsAutoType(0, 2, 4, 8);
417            INST(6, Opcode::CallStatic).b().InputsAutoType(1, 3, 4, 5, 8);
418            INST(7, Opcode::ReturnVoid);
419        }
420    }
421}
422
423TEST_F(InstTest, BinaryImmOperation)
424{
425    GRAPH(GetGraph())
426    {
427        PARAMETER(0, 1).u64();
428        PARAMETER(1, 4).s32();
429        BASIC_BLOCK(2, -1)
430        {
431            INST(2, Opcode::AddI).s32().Imm(10ULL).Inputs(1);
432            INST(3, Opcode::SubI).s32().Imm(15ULL).Inputs(2);
433            INST(4, Opcode::AndI).u64().Imm(15ULL).Inputs(0);
434            INST(5, Opcode::OrI).u64().Imm(1ULL).Inputs(4);
435            INST(6, Opcode::XorI).u64().Imm(10ULL).Inputs(5);
436            INST(7, Opcode::ShlI).u64().Imm(5ULL).Inputs(6);
437            INST(8, Opcode::ShrI).u64().Imm(5ULL).Inputs(7);
438            INST(9, Opcode::AShrI).s32().Imm(4ULL).Inputs(3);
439            INST(10, Opcode::ReturnVoid);
440        }
441    }
442}
443
444TEST_F(InstTest, Fcmp)
445{
446    GRAPH(GetGraph())
447    {
448        PARAMETER(0, 0).f32();
449        PARAMETER(1, 1).f32();
450        PARAMETER(2, 2).f64();
451        PARAMETER(3, 3).f64();
452        BASIC_BLOCK(2, -1)
453        {
454            INST(4, Opcode::Cmp).s32().Inputs(0, 1);
455            INST(5, Opcode::Cmp).s32().Inputs(2, 3);
456            INST(6, Opcode::ReturnVoid);
457        }
458    }
459    GraphChecker(GetGraph()).Check();
460    auto inst4 = static_cast<CmpInst *>(&INS(4));
461    auto inst5 = static_cast<CmpInst *>(&INS(5));
462    inst4->SetFcmpl();
463    ASSERT_EQ(inst4->IsFcmpg(), false);
464    ASSERT_EQ(inst4->IsFcmpl(), true);
465    inst4->SetFcmpl(true);
466    ASSERT_EQ(inst4->IsFcmpg(), false);
467    ASSERT_EQ(inst4->IsFcmpl(), true);
468    inst4->SetFcmpl(false);
469    ASSERT_EQ(inst4->IsFcmpg(), true);
470    ASSERT_EQ(inst4->IsFcmpl(), false);
471    inst5->SetFcmpg();
472    ASSERT_EQ(inst5->IsFcmpg(), true);
473    ASSERT_EQ(inst5->IsFcmpl(), false);
474    inst5->SetFcmpg(true);
475    ASSERT_EQ(inst5->IsFcmpg(), true);
476    ASSERT_EQ(inst5->IsFcmpl(), false);
477    inst5->SetFcmpg(false);
478    ASSERT_EQ(inst5->IsFcmpg(), false);
479    ASSERT_EQ(inst5->IsFcmpl(), true);
480}
481
482TEST_F(InstTest, SpillFill)
483{
484    Register R0 = 0;
485    Register R1 = 1;
486    StackSlot slot0 = 0;
487    StackSlot slot1 = 1;
488
489    auto spill_fill_inst = GetGraph()->CreateInstSpillFill();
490    spill_fill_inst->AddFill(slot0, R0, DataType::UINT64);
491    spill_fill_inst->AddMove(R0, R1, DataType::UINT64);
492    spill_fill_inst->AddSpill(R1, slot1, DataType::UINT64);
493
494    ASSERT_EQ(spill_fill_inst->GetSpillFills().size(), 3U);
495}
496
497TEST_F(InstTest, RemovePhiInput)
498{
499    GRAPH(GetGraph())
500    {
501        CONSTANT(0, 0);
502        CONSTANT(1, 1);
503        CONSTANT(2, 2);
504        BASIC_BLOCK(2, 3, 5)
505        {
506            INST(5, Opcode::Compare).b().SrcType(DataType::Type::INT64).Inputs(0, 1);
507            INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(5);
508        }
509        BASIC_BLOCK(3, 5) {}
510        BASIC_BLOCK(5, -1)
511        {
512            INST(3, Opcode::Phi).u64().Inputs({{2, 0}, {3, 1}});
513            INST(4, Opcode::ReturnVoid);
514        }
515    }
516    auto init_inputs = INS(3).GetInputs();
517    auto init_preds = BB(5).GetPredsBlocks();
518
519    auto pred_bb_idx = INS(3).CastToPhi()->GetPredBlockIndex(&BB(3));
520    BB(5).RemovePred(&BB(3));
521    INS(3).RemoveInput(pred_bb_idx);
522
523    auto curr_inputs = INS(3).GetInputs();
524    auto curr_preds = BB(5).GetPredsBlocks();
525    for (size_t idx = 0; idx < curr_inputs.size(); idx++) {
526        if (idx != pred_bb_idx) {
527            ASSERT_EQ(init_inputs[idx].GetInst(), curr_inputs[idx].GetInst());
528            ASSERT_EQ(init_preds[idx], curr_preds[idx]);
529        } else {
530            ASSERT_EQ(init_inputs.rbegin()->GetInst(), curr_inputs[idx].GetInst());
531            ASSERT_EQ(init_preds.back(), curr_preds[idx]);
532        }
533    }
534}
535
536/**
537 * Test creating instruction with huge dynamic inputs amount
538 */
539TEST_F(InstTest, HugeDynamicOperandsAmount)
540{
541    auto graph = CreateGraphStartEndBlocks();
542    const size_t COUNT = 1000;
543    auto save_state = graph->CreateInstSaveState();
544
545    for (size_t i = 0; i < COUNT; i++) {
546        save_state->AppendInput(graph->FindOrCreateConstant(i));
547        save_state->SetVirtualRegister(i, VirtualRegister(i, false));
548    }
549
550    for (size_t i = 0; i < COUNT; i++) {
551        auto user = graph->FindOrCreateConstant(i)->GetUsers().begin()->GetInst();
552        ASSERT_EQ(user, save_state);
553    }
554}
555
556TEST_F(InstTest, FloatConstants)
557{
558    auto graph = CreateGraphStartEndBlocks();
559    graph->GetStartBlock()->AddSucc(graph->GetEndBlock());
560    auto positiv_zero_float = graph->FindOrCreateConstant(0.0f);
561    auto negativ_zero_float = graph->FindOrCreateConstant(-0.0f);
562    auto positiv_zero_double = graph->FindOrCreateConstant(0.0);
563    auto negativ_zero_double = graph->FindOrCreateConstant(-0.0);
564
565    ASSERT_NE(positiv_zero_float, negativ_zero_float);
566    ASSERT_NE(positiv_zero_double, negativ_zero_double);
567}
568
569TEST_F(InstTest, Flags)
570{
571    auto initial_mask = inst_flags::GetFlagsMask(Opcode::LoadObject);
572    auto inst = GetGraph()->CreateInstLoadObject();
573    ASSERT_EQ(initial_mask, inst->GetFlagsMask());
574    ASSERT_EQ(inst->GetFlagsMask(), initial_mask);
575    ASSERT_TRUE(inst->IsLoad());
576    inst->SetFlag(inst_flags::ALLOC);
577    ASSERT_EQ(inst->GetFlagsMask(), initial_mask | inst_flags::ALLOC);
578    inst->ClearFlag(inst_flags::LOAD);
579    ASSERT_FALSE(inst->IsLoad());
580    ASSERT_EQ(inst->GetFlagsMask(), (initial_mask | inst_flags::ALLOC) & ~inst_flags::LOAD);
581}
582
583TEST_F(InstTest, IntrinsicFlags)
584{
585    ArenaAllocator allocator {SpaceType::SPACE_TYPE_COMPILER};
586#include "intrinsic_flags_test.inl"
587}
588
589}  // namespace panda::compiler
590