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 "lowering.h"
17
18namespace panda::compiler {
19void Lowering::VisitIfImm([[maybe_unused]] GraphVisitor *v, Inst *inst)
20{
21    ASSERT(inst->GetOpcode() == Opcode::IfImm);
22    LowerIf(inst->CastToIfImm());
23}
24
25// Ask encoder whether Constant can be an immediate for Compare
26bool Lowering::ConstantFitsCompareImm(Inst *cst, uint32_t size, ConditionCode cc)
27{
28    ASSERT(cst->GetOpcode() == Opcode::Constant);
29    if (DataType::IsFloatType(cst->GetType())) {
30        return false;
31    }
32    int64_t val = cst->CastToConstant()->GetRawValue();
33    return (size == HALF_SIZE) && (val == 0);
34}
35
36// We'd like to swap only to make second operand immediate
37bool Lowering::BetterToSwapCompareInputs(Inst *cmp)
38{
39    ASSERT(cmp->GetOpcode() == Opcode::Compare);
40    auto in0 = cmp->GetInput(0).GetInst();
41    auto in1 = cmp->GetInput(1).GetInst();
42    if (DataType::IsFloatType(in0->GetType())) {
43        return false;
44    }
45
46    if (in0->IsConst()) {
47        if (in1->IsConst()) {
48            DataType::Type type = cmp->CastToCompare()->GetOperandsType();
49            uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
50            auto cc = cmp->CastToCompare()->GetCc();
51            return ConstantFitsCompareImm(in0, size, cc) && !ConstantFitsCompareImm(in1, size, cc);
52        }
53        return true;
54    }
55    return false;
56}
57
58// Optimize order of input arguments for decreasing using accumulator (Bytecodeoptimizer only).
59void Lowering::OptimizeIfInput(compiler::Inst *if_inst)
60{
61    ASSERT(if_inst->GetOpcode() == compiler::Opcode::If);
62    compiler::Inst *input_0 = if_inst->GetInput(0).GetInst();
63    compiler::Inst *input_1 = if_inst->GetInput(1).GetInst();
64
65    if (input_0->IsDominate(input_1)) {
66        if_inst->SetInput(0, input_1);
67        if_inst->SetInput(1, input_0);
68        // And change CC
69        auto cc = if_inst->CastToIf()->GetCc();
70        cc = SwapOperandsConditionCode(cc);
71        if_inst->CastToIf()->SetCc(cc);
72    }
73}
74
75void Lowering::LowerIf(IfImmInst *inst)
76{
77    auto graph = inst->GetBasicBlock()->GetGraph();
78    ASSERT(inst->GetCc() == ConditionCode::CC_NE || inst->GetCc() == ConditionCode::CC_EQ);
79    ASSERT(inst->GetImm() == 0);
80    if (inst->GetOperandsType() != DataType::BOOL) {
81        ASSERT(!graph->SupportManagedCode() || graph->IsDynamicMethod());
82        return;
83    }
84    auto input = inst->GetInput(0).GetInst();
85    if (input->GetOpcode() != Opcode::Compare) {
86        return;
87    }
88    // Check, that inst have only IfImm user
89    for (auto &user : input->GetUsers()) {
90        if (user.GetInst()->GetOpcode() != Opcode::IfImm) {
91            return;
92        }
93    }
94    // Try put constant in second input
95    if (BetterToSwapCompareInputs(input)) {
96        // Swap inputs
97        auto in0 = input->GetInput(0).GetInst();
98        auto in1 = input->GetInput(1).GetInst();
99        input->SetInput(0, in1);
100        input->SetInput(1, in0);
101        // And change CC
102        auto cc = input->CastToCompare()->GetCc();
103        cc = SwapOperandsConditionCode(cc);
104        input->CastToCompare()->SetCc(cc);
105    }
106    auto cst = input->GetInput(1).GetInst();
107    DataType::Type type = input->CastToCompare()->GetOperandsType();
108    uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
109    auto cc = input->CastToCompare()->GetCc();
110    // IfImm can be inverted
111    if (inst->GetCc() == ConditionCode::CC_EQ && inst->GetImm() == 0) {
112        cc = GetInverseConditionCode(cc);
113    }
114
115    if (cst->IsConst() && ConstantFitsCompareImm(cst, size, cc)) {
116        // In-place change for IfImm
117        InPlaceLowerIfImm(inst, input, cst, cc);
118    } else {
119        // New instruction
120        auto replace = graph->CreateInstIf(DataType::NO_TYPE, inst->GetPc(), cc);
121        replace->SetMethod(inst->GetMethod());
122        replace->SetOperandsType(input->CastToCompare()->GetOperandsType());
123        replace->SetInput(0, input->GetInput(0).GetInst());
124        replace->SetInput(1, input->GetInput(1).GetInst());
125        // Replace IfImm instruction immediately because it's not removable by DCE
126        inst->RemoveInputs();
127        inst->GetBasicBlock()->ReplaceInst(inst, replace);
128        graph->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc());
129        if (graph->IsBytecodeOptimizer()) {
130            OptimizeIfInput(replace);
131        }
132        COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
133    }
134}
135
136void Lowering::InPlaceLowerIfImm(IfImmInst *inst, Inst *input, Inst *cst, ConditionCode cc)
137{
138    inst->SetOperandsType(input->CastToCompare()->GetOperandsType());
139    auto new_input = input->GetInput(0).GetInst();
140    inst->SetInput(0, new_input);
141
142    uint64_t val = cst->CastToConstant()->GetRawValue();
143    inst->SetImm(val);
144    inst->SetCc(cc);
145    inst->GetBasicBlock()->GetGraph()->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(),
146                                                                      inst->GetPc());
147    COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
148}
149
150void Lowering::InvalidateAnalyses()
151{
152}
153
154bool Lowering::RunImpl()
155{
156    VisitGraph();
157    return true;
158}
159}  // namespace panda::compiler
160