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