• 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 <fstream>
17 #include "unit_test.h"
18 #include "code_info/code_info.h"
19 #include "code_info/code_info_builder.h"
20 #include "mem/pool_manager.h"
21 
22 using panda::panda_file::File;
23 
24 namespace panda::compiler {
25 
26 class CodeInfoTest : public ::testing::Test {
27 public:
CodeInfoTest()28     CodeInfoTest()
29     {
30         panda::mem::MemConfig::Initialize(0, 64_MB, 256_MB, 32_MB);
31         PoolManager::Initialize();
32         allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER);
33     }
34 
~CodeInfoTest()35     virtual ~CodeInfoTest()
36     {
37         delete allocator_;
38         PoolManager::Finalize();
39         panda::mem::MemConfig::Finalize();
40     }
41 
GetAllocator()42     ArenaAllocator *GetAllocator()
43     {
44         return allocator_;
45     }
46 
EmitCode(CodeInfoBuilder & builder)47     auto EmitCode(CodeInfoBuilder &builder)
48     {
49         static constexpr size_t DUMMY_CODE_SIZE = 16;
50         ArenaVector<uint8_t> data(GetAllocator()->Adapter());
51         size_t code_offset = CodeInfo::GetCodeOffset(RUNTIME_ARCH) + DUMMY_CODE_SIZE;
52         data.resize(code_offset);
53         builder.Encode(&data, data.size() * BITS_PER_BYTE);
54         CodePrefix *prefix = new (data.data()) CodePrefix;
55         prefix->code_size = data.size();
56         prefix->code_info_offset = code_offset;
57         prefix->code_info_size = data.size() - code_offset;
58         return data;
59     }
60 
61     template <typename Callback>
EnumerateVRegs(const CodeInfo & code_info,const StackMap & stack_map,int inline_depth,Callback callback)62     void EnumerateVRegs(const CodeInfo &code_info, const StackMap &stack_map, int inline_depth, Callback callback)
63     {
64         auto list = code_info.GetVRegList(stack_map, inline_depth, GetAllocator());
65         for (auto vreg : list) {
66             callback(vreg);
67         }
68     }
69 
70     template <size_t N>
CompareVRegs(CodeInfo & code_info,StackMap stack_map,int inline_info_index,std::array<VRegInfo,N> vregs)71     void CompareVRegs(CodeInfo &code_info, StackMap stack_map, int inline_info_index, std::array<VRegInfo, N> vregs)
72     {
73         std::vector<VRegInfo> vregs_in_map;
74         if (inline_info_index == -1) {
75             EnumerateVRegs(code_info, stack_map, -1, [&vregs_in_map](auto vreg) { vregs_in_map.push_back(vreg); });
76         } else {
77             EnumerateVRegs(code_info, stack_map, inline_info_index,
78                            [&vregs_in_map](auto vreg) { vregs_in_map.push_back(vreg); });
79         }
80         ASSERT_EQ(vregs_in_map.size(), vregs.size());
81         for (size_t i = 0; i < vregs.size(); i++) {
82             vregs_in_map[i].SetIndex(0);
83             ASSERT_EQ(vregs_in_map[i], vregs[i]);
84         }
85     }
86 
87 private:
88     ArenaAllocator *allocator_ {nullptr};
89 };
90 
TEST_F(CodeInfoTest,SingleStackmap)91 TEST_F(CodeInfoTest, SingleStackmap)
92 {
93     auto regs_count = GetCalleeRegsCount(RUNTIME_ARCH, false) + GetCalleeRegsCount(RUNTIME_ARCH, true) +
94                       GetCallerRegsCount(RUNTIME_ARCH, false) + GetCallerRegsCount(RUNTIME_ARCH, true);
95     std::array vregs = {VRegInfo(1, VRegInfo::Location::FP_REGISTER, VRegInfo::Type::INT64, false),
96                         VRegInfo(2, VRegInfo::Location::SLOT, VRegInfo::Type::OBJECT, false),
97                         VRegInfo(12, VRegInfo::Location::REGISTER, VRegInfo::Type::OBJECT, false)};
98     if constexpr (!ArchTraits<RUNTIME_ARCH>::IS_64_BITS) {  // NOLINT
99         vregs[1].SetValue((vregs[1].GetValue() << 1) + 1 + regs_count);
100     } else {  // NOLINT
101         vregs[1].SetValue(vregs[1].GetValue() + regs_count);
102     }
103     CodeInfoBuilder builder(RUNTIME_ARCH, GetAllocator());
104     builder.BeginMethod(1, 3);
105     ArenaBitVector stack_roots(GetAllocator());
106     stack_roots.resize(3);
107     stack_roots.SetBit(2);
108     std::bitset<32> reg_roots(0);
109     reg_roots.set(12);
110     builder.BeginStackMap(10, 20, &stack_roots, reg_roots.to_ullong(), true, false);
111     builder.AddVReg(vregs[0]);
112     builder.AddVReg(vregs[1]);
113     builder.AddVReg(vregs[2]);
114     builder.EndStackMap();
115     builder.EndMethod();
116 
117     ArenaVector<uint8_t> data = EmitCode(builder);
118 
119     BitMemoryStreamIn in(data.data(), 0, data.size() * BITS_PER_BYTE);
120     CodeInfo code_info(data.data());
121     ASSERT_EQ(code_info.GetHeader().GetFrameSize(), 1);
122     ASSERT_EQ(code_info.GetStackMaps().GetRowsCount(), 1);
123     auto stack_map = code_info.GetStackMaps().GetRow(0);
124     ASSERT_EQ(stack_map.GetBytecodePc(), 10);
125     ASSERT_EQ(stack_map.GetNativePcUnpacked(), 20);
126     ASSERT_FALSE(stack_map.HasInlineInfoIndex());
127     ASSERT_TRUE(stack_map.HasRootsRegMaskIndex());
128     ASSERT_TRUE(stack_map.HasRootsStackMaskIndex());
129     ASSERT_TRUE(stack_map.HasVRegMaskIndex());
130     ASSERT_TRUE(stack_map.HasVRegMapIndex());
131 
132     ASSERT_EQ(Popcount(code_info.GetRootsRegMask(stack_map)), 1);
133     ASSERT_EQ(code_info.GetRootsRegMask(stack_map), 1 << 12);
134 
135     ASSERT_EQ(code_info.GetRootsStackMask(stack_map).Popcount(), 1);
136     auto roots_region = code_info.GetRootsStackMask(stack_map);
137     ASSERT_EQ(roots_region.Size(), 3);
138     ASSERT_EQ(code_info.GetRootsStackMask(stack_map).Read(0, 3), 1 << 2);
139 
140     ASSERT_EQ(code_info.GetVRegMask(stack_map).Popcount(), 3);
141     ASSERT_EQ(code_info.GetVRegMask(stack_map).Size(), 3);
142 
143     size_t index = 0;
144     EnumerateVRegs(code_info, stack_map, -1, [&vregs, &index](auto vreg) {
145         vreg.SetIndex(0);
146         ASSERT_EQ(vreg, vregs[index++]);
147     });
148 
149     std::bitset<vregs.size()> mask(0);
150     code_info.EnumerateStaticRoots(stack_map, [&mask, &vregs](auto vreg) -> bool {
151         auto it = std::find(vregs.begin(), vregs.end(), vreg);
152         if (it != vregs.end()) {
153             ASSERT(std::distance(vregs.begin(), it) < helpers::ToSigned(mask.size()));
154             mask.set(std::distance(vregs.begin(), it));
155         }
156         return true;
157     });
158     ASSERT_EQ(Popcount(mask.to_ullong()), 2);
159     ASSERT_TRUE(mask.test(1));
160     ASSERT_TRUE(mask.test(2));
161 }
162 
TEST_F(CodeInfoTest,MultipleStackmaps)163 TEST_F(CodeInfoTest, MultipleStackmaps)
164 {
165     uintptr_t method_stub;
166     std::array vregs = {
167         VRegInfo(1, VRegInfo::Location::REGISTER, VRegInfo::Type::INT64, false),
168         VRegInfo(2, VRegInfo::Location::SLOT, VRegInfo::Type::OBJECT, false),
169         VRegInfo(3, VRegInfo::Location::SLOT, VRegInfo::Type::OBJECT, false),
170         VRegInfo(10, VRegInfo::Location::FP_REGISTER, VRegInfo::Type::FLOAT64, false),
171         VRegInfo(20, VRegInfo::Location::SLOT, VRegInfo::Type::BOOL, false),
172         VRegInfo(30, VRegInfo::Location::REGISTER, VRegInfo::Type::OBJECT, false),
173     };
174     ArenaBitVector stack_roots(GetAllocator());
175     stack_roots.resize(8);
176     std::bitset<32> reg_roots(0);
177 
178     CodeInfoBuilder builder(RUNTIME_ARCH, GetAllocator());
179     builder.BeginMethod(12, 3);
180 
181     stack_roots.SetBit(1);
182     stack_roots.SetBit(2);
183     builder.BeginStackMap(10, 20, &stack_roots, reg_roots.to_ullong(), true, false);
184     builder.AddVReg(vregs[0]);
185     builder.AddVReg(vregs[1]);
186     builder.AddVReg(VRegInfo());
187     builder.BeginInlineInfo(&method_stub, 0, 1, 2);
188     builder.AddVReg(vregs[2]);
189     builder.AddVReg(vregs[3]);
190     builder.EndInlineInfo();
191     builder.BeginInlineInfo(nullptr, 0x123456, 2, 1);
192     builder.AddVReg(vregs[3]);
193     builder.EndInlineInfo();
194     builder.EndStackMap();
195 
196     stack_roots.Reset();
197     reg_roots.reset();
198     reg_roots.set(5);
199     builder.BeginStackMap(30, 40, &stack_roots, reg_roots.to_ullong(), true, false);
200     builder.AddVReg(vregs[3]);
201     builder.AddVReg(vregs[5]);
202     builder.AddVReg(vregs[4]);
203     builder.BeginInlineInfo(nullptr, 0xabcdef, 3, 2);
204     builder.AddVReg(vregs[1]);
205     builder.AddVReg(VRegInfo());
206     builder.EndInlineInfo();
207     builder.EndStackMap();
208 
209     stack_roots.Reset();
210     reg_roots.reset();
211     reg_roots.set(1);
212     builder.BeginStackMap(50, 60, &stack_roots, reg_roots.to_ullong(), true, false);
213     builder.AddVReg(VRegInfo());
214     builder.AddVReg(VRegInfo());
215     builder.AddVReg(VRegInfo());
216     builder.EndStackMap();
217 
218     builder.EndMethod();
219 
220     ArenaVector<uint8_t> data = EmitCode(builder);
221 
222     CodeInfo code_info(data.data());
223 
224     {
225         auto stack_map = code_info.GetStackMaps().GetRow(0);
226         ASSERT_EQ(stack_map.GetBytecodePc(), 10);
227         ASSERT_EQ(stack_map.GetNativePcUnpacked(), 20);
228         ASSERT_TRUE(stack_map.HasInlineInfoIndex());
229         CompareVRegs(code_info, stack_map, -1, std::array {vregs[0], vregs[1], VRegInfo()});
230 
231         ASSERT_TRUE(stack_map.HasInlineInfoIndex());
232         auto inline_infos = code_info.GetInlineInfos(stack_map);
233         ASSERT_EQ(std::distance(inline_infos.begin(), inline_infos.end()), 2);
234         auto it = inline_infos.begin();
235         ASSERT_EQ(std::get<void *>(code_info.GetMethod(stack_map, 0)), &method_stub);
236         ASSERT_TRUE(it->Get(InlineInfo::COLUMN_IS_LAST));
237         CompareVRegs(code_info, stack_map, it->GetRow() - stack_map.GetInlineInfoIndex(), std::array {vregs[3]});
238         ++it;
239         ASSERT_FALSE(it->Get(InlineInfo::COLUMN_IS_LAST));
240         ASSERT_EQ(std::get<uint32_t>(code_info.GetMethod(stack_map, 1)), 0x123456);
241         CompareVRegs(code_info, stack_map, it->GetRow() - stack_map.GetInlineInfoIndex(),
242                      std::array {vregs[2], vregs[3]});
243     }
244     {
245         auto stack_map = code_info.GetStackMaps().GetRow(1);
246         ASSERT_EQ(stack_map.GetBytecodePc(), 30);
247         ASSERT_EQ(stack_map.GetNativePcUnpacked(), 40);
248         CompareVRegs(code_info, stack_map, -1, std::array {vregs[3], vregs[5], vregs[4]});
249 
250         ASSERT_TRUE(stack_map.HasInlineInfoIndex());
251         auto inline_infos = code_info.GetInlineInfos(stack_map);
252         ASSERT_EQ(std::distance(inline_infos.begin(), inline_infos.end()), 1);
253         ASSERT_EQ(std::get<uint32_t>(code_info.GetMethod(stack_map, 0)), 0xabcdef);
254         ASSERT_TRUE(inline_infos[0].Get(InlineInfo::COLUMN_IS_LAST));
255         CompareVRegs(code_info, stack_map, inline_infos[0].GetRow() - stack_map.GetInlineInfoIndex(),
256                      std::array {vregs[1], VRegInfo()});
257     }
258 
259     {
260         auto stack_map = code_info.GetStackMaps().GetRow(2);
261         ASSERT_EQ(stack_map.GetBytecodePc(), 50);
262         ASSERT_EQ(stack_map.GetNativePcUnpacked(), 60);
263         CompareVRegs(code_info, stack_map, -1, std::array {VRegInfo(), VRegInfo(), VRegInfo()});
264 
265         ASSERT_FALSE(stack_map.HasInlineInfoIndex());
266         auto inline_infos = code_info.GetInlineInfos(stack_map);
267         ASSERT_EQ(std::distance(inline_infos.begin(), inline_infos.end()), 0);
268     }
269 
270     {
271         auto stackmap = code_info.FindStackMapForNativePc(20);
272         ASSERT_TRUE(stackmap.IsValid());
273         ASSERT_EQ(stackmap.GetNativePcUnpacked(), 20);
274         ASSERT_EQ(stackmap.GetBytecodePc(), 10);
275         stackmap = code_info.FindStackMapForNativePc(40);
276         ASSERT_TRUE(stackmap.IsValid());
277         ASSERT_EQ(stackmap.GetNativePcUnpacked(), 40);
278         ASSERT_EQ(stackmap.GetBytecodePc(), 30);
279         stackmap = code_info.FindStackMapForNativePc(60);
280         ASSERT_TRUE(stackmap.IsValid());
281         ASSERT_EQ(stackmap.GetNativePcUnpacked(), 60);
282         ASSERT_EQ(stackmap.GetBytecodePc(), 50);
283         stackmap = code_info.FindStackMapForNativePc(90);
284         ASSERT_FALSE(stackmap.IsValid());
285     }
286 }
287 
TEST_F(CodeInfoTest,Constants)288 TEST_F(CodeInfoTest, Constants)
289 {
290     CodeInfoBuilder builder(RUNTIME_ARCH, GetAllocator());
291     builder.BeginMethod(12, 3);
292     builder.BeginStackMap(1, 4, nullptr, 0, true, false);
293     builder.AddConstant(0, VRegInfo::Type::INT64, false);
294     builder.AddConstant(0x1234567890abcdef, VRegInfo::Type::INT64, true);
295     builder.AddConstant(0x12345678, VRegInfo::Type::INT32, false);
296     builder.EndStackMap();
297     builder.EndMethod();
298 
299     ArenaVector<uint8_t> data = EmitCode(builder);
300 
301     CodeInfo code_info(data.data());
302 
303     auto stack_map = code_info.GetStackMaps().GetRow(0);
304 
305     auto vreg_list = code_info.GetVRegList(stack_map, GetAllocator());
306     ASSERT_EQ(code_info.GetConstant(vreg_list[0]), 0);
307     ASSERT_EQ(code_info.GetConstant(vreg_list[1]), 0x1234567890abcdef);
308     ASSERT_EQ(code_info.GetConstant(vreg_list[2]), 0x12345678);
309     // 0 and 0x12345678 should be deduplicated
310     ASSERT_EQ(code_info.GetConstantTable().GetRowsCount(), 3);
311 }
312 }  // namespace panda::compiler
313