• 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 "optimizer/code_generator/codegen.h"
17 #include "optimizer/code_generator/encode.h"
18 #include "optimizer/ir/graph.h"
19 #include "optimizer/code_generator/spill_fill_encoder.h"
20 
21 namespace panda::compiler {
22 
AreConsecutiveOps(const SpillFillData & pred,const SpillFillData & succ)23 bool SpillFillEncoder::AreConsecutiveOps(const SpillFillData &pred, const SpillFillData &succ)
24 {
25     bool same_src_type = pred.SrcType() == succ.SrcType();
26     bool same_dst_type = pred.DstType() == succ.DstType();
27     bool same_argument_type = pred.GetCommonType() == succ.GetCommonType();
28     if (!same_src_type || !same_dst_type || !same_argument_type) {
29         return false;
30     }
31 
32     // Slots should be neighboring, note that offset from SP is decreasing when slot number is increasing,
33     // so succ's slot number should be lower than pred's slot number.
34     if (pred.SrcType() == LocationType::STACK && pred.SrcValue() != succ.SrcValue() + 1U) {
35         return false;
36     }
37     if (pred.DstType() == LocationType::STACK && pred.DstValue() != succ.DstValue() + 1U) {
38         return false;
39     }
40     return true;
41 }
42 
CanCombineSpillFills(SpillFillData pred,SpillFillData succ,const Graph * graph)43 bool SpillFillEncoder::CanCombineSpillFills(SpillFillData pred, SpillFillData succ, const Graph *graph)
44 {
45     if (!IsCombiningEnabled(graph)) {
46         return false;
47     }
48     // Stack slot is 64-bit wide, so we can only combine types that could be widened up to
49     // 64 bit (i.e. we can' combine two floats).
50     if (!DataType::Is64Bits(pred.GetCommonType(), graph->GetArch())) {
51         return false;
52     }
53 
54     return AreConsecutiveOps(pred, succ);
55 }
56 
SortSpillFillData(ArenaVector<SpillFillData> * spill_fills)57 void SpillFillEncoder::SortSpillFillData(ArenaVector<SpillFillData> *spill_fills)
58 {
59     constexpr size_t MAX_VECTOR_LEN = MAX_NUM_REGS + MAX_NUM_VREGS;
60     // Don't sort vectors that are too large in order to reduce compilation duration.
61     if (spill_fills->size() > MAX_VECTOR_LEN) {
62         COMPILER_LOG(DEBUG, CODEGEN) << "Bypass spill fills sorting because corresponding vector is too large: "
63                                      << spill_fills->size();
64         return;
65     }
66     auto it = spill_fills->begin();
67     while (it != spill_fills->end()) {
68         // Sort spill fills only within group of consecutive SpillFillData elements sharing the same spill-fill type.
69         // SpillFillData elements could not be reordered within whole spill_fills array, because some of these elements
70         // may be inserted by SpillFillResolver to break cyclic dependency.
71         bool is_fill = it->SrcType() == LocationType::STACK && it->GetDst().IsAnyRegister();
72         bool is_spill = it->GetSrc().IsAnyRegister() && it->DstType() == LocationType::STACK;
73         if (!is_spill && !is_fill) {
74             ++it;
75             continue;
76         }
77         auto next = std::next(it);
78         while (next != spill_fills->end() && it->SrcType() == next->SrcType() && it->DstType() == next->DstType()) {
79             ++next;
80         }
81 
82         if (is_spill) {
83             std::sort(it, next, [](auto sf1, auto sf2) { return sf1.DstValue() > sf2.DstValue(); });
84         } else {
85             ASSERT(is_fill);
86             std::sort(it, next, [](auto sf1, auto sf2) { return sf1.SrcValue() > sf2.SrcValue(); });
87         }
88 
89         it = next;
90     }
91 }
92 
SpillFillEncoder(Codegen * codegen,Inst * inst)93 SpillFillEncoder::SpillFillEncoder(Codegen *codegen, Inst *inst)
94     : inst_(inst->CastToSpillFill()),
95       graph_(codegen->GetGraph()),
96       codegen_(codegen),
97       encoder_(codegen->GetEncoder()),
98       fl_(codegen->GetFrameLayout())
99 {
100     sp_reg_ = codegen->GetTarget().GetStackReg();
101 }
102 
EncodeSpillFill()103 void SpillFillEncoder::EncodeSpillFill()
104 {
105     if (IsCombiningEnabled(graph_)) {
106         SortSpillFillData(&(inst_->GetSpillFills()));
107     }
108 
109     // hint on how many consecutive ops current group contain
110     int consecutive_ops_hint = 0;
111     for (auto it = inst_->GetSpillFills().begin(), end = inst_->GetSpillFills().end(); it != end;) {
112         auto sf = *it;
113         auto next_it = std::next(it);
114         SpillFillData *next = next_it == end ? nullptr : &(*next_it);
115 
116         // new group started
117         if (consecutive_ops_hint <= 0) {
118             consecutive_ops_hint = 1;
119             // find how many consecutive SpillFillData have the same type, source and destination type
120             // and perform read or write from consecutive stack slots.
121             for (auto group_it = it, next_group_it = std::next(it);
122                  next_group_it != end && AreConsecutiveOps(*group_it, *next_group_it); ++next_group_it) {
123                 consecutive_ops_hint++;
124                 group_it = next_group_it;
125             }
126         }
127 
128         size_t adv = 0;
129         switch (sf.SrcType()) {
130             case LocationType::IMMEDIATE: {
131                 adv = EncodeImmToX(sf);
132                 break;
133             }
134             case LocationType::FP_REGISTER:
135             case LocationType::REGISTER: {
136                 adv = EncodeRegisterToX(sf, next, consecutive_ops_hint);
137                 break;
138             }
139             case LocationType::STACK_PARAMETER:
140             case LocationType::STACK: {
141                 adv = EncodeStackToX(sf, next, consecutive_ops_hint);
142                 break;
143             }
144             default:
145                 UNREACHABLE();
146         }
147         consecutive_ops_hint -= adv;
148         std::advance(it, adv);
149     }
150 }
151 
EncodeImmToX(const SpillFillData & sf)152 size_t SpillFillEncoder::EncodeImmToX(const SpillFillData &sf)
153 {
154     auto const_inst = graph_->GetSpilledConstant(sf.SrcValue());
155     ASSERT(const_inst->IsConst());
156 
157     if (sf.GetDst().IsAnyRegister()) {  // imm -> register
158         auto type = sf.GetType();
159         if (graph_->IsDynamicMethod() && const_inst->GetType() == DataType::INT64) {
160             type = DataType::UINT32;
161         }
162         auto imm = codegen_->ConvertImm(const_inst, type);
163         auto dst_reg = GetDstReg(sf.GetDst(), imm.GetType());
164         encoder_->EncodeMov(dst_reg, imm);
165         return 1U;
166     }
167 
168     ASSERT(sf.GetDst().IsAnyStack());  // imm -> stack
169     auto dst_mem = codegen_->GetMemRefForSlot(sf.GetDst());
170     auto imm = codegen_->ConvertImm(const_inst, sf.GetCommonType());
171     encoder_->EncodeSti(imm, dst_mem);
172     return 1U;
173 }
174 
EncodeRegisterToX(const SpillFillData & sf,const SpillFillData * next,int consecutive_ops_hint)175 size_t SpillFillEncoder::EncodeRegisterToX(const SpillFillData &sf, const SpillFillData *next, int consecutive_ops_hint)
176 {
177     if (sf.GetDst().IsAnyRegister()) {  // register -> register
178         auto src_reg = codegen_->ConvertRegister(sf.SrcValue(), sf.GetType());
179         auto dst_reg = GetDstReg(sf.GetDst(), src_reg.GetType());
180         encoder_->EncodeMov(dst_reg, src_reg);
181         return 1U;
182     }
183 
184     ASSERT(sf.GetDst().IsAnyStack());
185     auto offset = codegen_->GetStackOffset(sf.GetDst());
186     auto mem_ref = MemRef(sp_reg_, offset);
187 
188     if (sf.GetDst().IsStackArgument()) {  // register -> stack_arg
189         auto src_reg = codegen_->ConvertRegister(sf.SrcValue(), sf.GetType());
190         // There is possible to have sequence to intrinsics with no getter/setter in interpreter:
191         // compiled_code->c2i(push to frame)->interpreter(HandleCallVirtShort)->i2c(move to stack)->intrinsic
192         // To do not fix it in interpreter, it is better to store 64-bits
193         if (src_reg.GetSize() < DOUBLE_WORD_SIZE && !src_reg.GetType().IsFloat()) {
194             src_reg = src_reg.As(Codegen::ConvertDataType(DataType::REFERENCE, codegen_->GetArch()));
195         }
196         encoder_->EncodeStrz(src_reg, mem_ref);
197         return 1U;
198     }
199 
200     // register -> stack
201     auto src_reg = codegen_->ConvertRegister(sf.SrcValue(), sf.GetCommonType());
202     // If address is no qword aligned and current group consist of even number of consecutive slots
203     // then we can skip current operation.
204     constexpr int COALESCE_OPS_LIMIT = 2;
205     auto skip_coalescing = (consecutive_ops_hint % COALESCE_OPS_LIMIT == 1) && (offset % QUAD_WORD_SIZE_BYTE != 0);
206     if (next != nullptr && CanCombineSpillFills(sf, *next, graph_) && !skip_coalescing) {
207         auto next_reg = codegen_->ConvertRegister(next->SrcValue(), next->GetCommonType());
208         encoder_->EncodeStp(src_reg, next_reg, mem_ref);
209         return 2U;
210     }
211     encoder_->EncodeStr(src_reg, mem_ref);
212     return 1U;
213 }
214 
EncodeStackToX(const SpillFillData & sf,const SpillFillData * next,int consecutive_ops_hint)215 size_t SpillFillEncoder::EncodeStackToX(const SpillFillData &sf, const SpillFillData *next, int consecutive_ops_hint)
216 {
217     auto offset = codegen_->GetStackOffset(sf.GetSrc());
218     auto src_mem = MemRef(sp_reg_, offset);
219     auto type_info = Codegen::ConvertDataType(sf.GetType(), codegen_->GetArch());
220 
221     if (sf.GetDst().IsAnyRegister()) {  // stack -> register
222         // If address is no qword aligned and current group consist of even number of consecutive slots
223         // then we can skip current operation.
224         constexpr int COALESCE_OPS_LIMIT = 2;
225         auto skip_coalescing = (consecutive_ops_hint % COALESCE_OPS_LIMIT == 1) && (offset % QUAD_WORD_SIZE_BYTE != 0);
226         if (next != nullptr && CanCombineSpillFills(sf, *next, graph_) && !skip_coalescing) {
227             auto cur_reg = codegen_->ConvertRegister(sf.DstValue(), sf.GetCommonType());
228             auto next_reg = codegen_->ConvertRegister(next->DstValue(), next->GetCommonType());
229             encoder_->EncodeLdp(cur_reg, next_reg, false, src_mem);
230             return 2U;
231         }
232         auto dst_reg = GetDstReg(sf.GetDst(), type_info);
233         encoder_->EncodeLdr(dst_reg, false, src_mem);
234         return 1U;
235     }
236 
237     // stack -> stack
238     ASSERT(sf.GetDst().IsAnyStack());
239     auto dst_mem = codegen_->GetMemRefForSlot(sf.GetDst());
240     encoder_->EncodeMemCopy(src_mem, dst_mem, DOUBLE_WORD_SIZE);  // Stack slot is 64-bit wide
241     return 1U;
242 }
243 }  // namespace panda::compiler
244