1 /*
2 * Copyright (c) 2023-2024 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 "compiler/code_info/code_info_builder.h"
17 #include "compiler/optimizer/code_generator/target_info.h"
18 #include "compiler/optimizer/ir/inst.h"
19
20 #include "llvm_ark_interface.h"
21 #include "llvm_logger.h"
22 #include "llvm_options.h"
23 #include "code_info_producer.h"
24
25 static constexpr unsigned LOCATION_STEP = 2U;
26 static constexpr unsigned START_OFFSET = 3U;
27 static constexpr unsigned DWARF_AARCH64_BASE_REG = 19U;
28 [[maybe_unused]] static constexpr unsigned DWARF_MAX_REG = 32U;
29
30 namespace ark::llvmbackend {
31
CodeInfoProducer(Arch arch,LLVMArkInterface * compilation)32 CodeInfoProducer::CodeInfoProducer(Arch arch, LLVMArkInterface *compilation) : arch_(arch), compilation_(compilation) {}
33
34 /// Sets a LLVM stack map if any
SetStackMap(const uint8_t * section,uintptr_t size)35 void CodeInfoProducer::SetStackMap(const uint8_t *section, uintptr_t size)
36 {
37 stackmap_ = std::make_unique<LLVMStackMap>(llvm::makeArrayRef(section, size));
38 if (g_options.IsLlvmDumpStackmaps()) {
39 std::ofstream fout("llvm-stackmaps.txt");
40 LLVM_LOG_IF(!fout.is_open(), FATAL, STACKMAPS) << "Couldn't open llvm-stackmaps.txt";
41 DumpStackMap(stackmap_, fout);
42 fout.close();
43 LLVM_LOG(DEBUG, STACKMAPS) << "LLVM Stackmaps has been dumped into llvm-stackmaps.txt";
44 }
45 }
46
SetFaultMaps(const uint8_t * section,uintptr_t size)47 void CodeInfoProducer::SetFaultMaps(const uint8_t *section, uintptr_t size)
48 {
49 Span span {section, size};
50 faultMapParser_ = std::make_unique<llvm::FaultMapParser>(span.begin(), span.end());
51 }
52
AddSymbol(Method * method,StackMapSymbol symbol)53 void CodeInfoProducer::AddSymbol(Method *method, StackMapSymbol symbol)
54 {
55 symbols_.insert({method, symbol});
56 }
57
AddFaultMapSymbol(Method * method,uint32_t symbol)58 void CodeInfoProducer::AddFaultMapSymbol(Method *method, uint32_t symbol)
59 {
60 faultMapSymbols_.insert({method, symbol});
61 }
62
63 /// Fill a CodeInfoBuilder with proper data for the passed METHOD.
Produce(Method * method,compiler::CodeInfoBuilder * builder) const64 void CodeInfoProducer::Produce(Method *method, compiler::CodeInfoBuilder *builder) const
65 {
66 builder->BeginMethod(0, compilation_->GetVirtualRegistersCount(method));
67
68 // Create empty stackmap for the stack overflow check
69 compiler::SaveStateInst ss(compiler::Opcode::SaveState, 0, nullptr, nullptr, 0);
70 builder->BeginStackMap(0, 0, &ss, false);
71 builder->EndStackMap();
72
73 ConvertStackMaps(method, builder);
74 EncodeNullChecks(method, builder);
75
76 auto xregsMask = GetCalleeRegsMask(arch_, false).GetValue();
77 auto vregsMask = GetCalleeRegsMask(arch_, true).GetValue();
78
79 if (arch_ == Arch::AARCH64) {
80 auto funcName = compilation_->GetUniqMethodName(method);
81 auto regMasks = compilation_->GetCalleeSavedRegistersMask(funcName);
82 xregsMask &= std::get<0>(regMasks);
83 vregsMask &= std::get<1>(regMasks);
84 }
85 builder->SetSavedCalleeRegsMask(xregsMask, vregsMask);
86 builder->EndMethod();
87 }
88
GetDwarfBase(Arch arch) const89 uint16_t CodeInfoProducer::GetDwarfBase(Arch arch) const
90 {
91 switch (arch) {
92 case Arch::AARCH64:
93 return DWARF_AARCH64_BASE_REG;
94 default:
95 LLVM_LOG(FATAL, STACKMAPS) << "Unsupported base register for " << GetIsaName(arch);
96 return std::numeric_limits<uint16_t>::max();
97 }
98 }
99
GetArkFrameSlot(const LLVMStackMap::LocationAccessor & location,uint64_t stackSize,size_t slotSize) const100 size_t CodeInfoProducer::GetArkFrameSlot(const LLVMStackMap::LocationAccessor &location, uint64_t stackSize,
101 size_t slotSize) const
102 {
103 ASSERT(slotSize != 0);
104 int32_t offset = -location.getOffset();
105 auto reg = location.getDwarfRegNum();
106 if (reg == GetDwarfSP(arch_)) {
107 LLVM_LOG(DEBUG, STACKMAPS) << "SP offset, stack size = " << stackSize;
108 offset += stackSize;
109 if (arch_ == Arch::X86_64) {
110 // x86_64 SP points to the last inserted value
111 offset -= slotSize;
112 }
113 if (arch_ == Arch::AARCH64) {
114 // LLVM allocate slots for LR & FP, but we already save
115 // it via INLINEASM, so subtract addition stack size
116 offset -= 2U * slotSize;
117 }
118 } else if (reg == GetDwarfFP(arch_)) {
119 LLVM_LOG(DEBUG, STACKMAPS) << "FP offset";
120 } else if (reg == GetDwarfBase(arch_)) {
121 LLVM_LOG(DEBUG, STACKMAPS) << "Base offset";
122 offset += stackSize;
123 } else {
124 LLVM_LOG(FATAL, STACKMAPS) << "Unsupported location register: " << reg;
125 }
126
127 ASSERT_PRINT(offset % slotSize == 0, "By some reason offset is not an integer number of slots");
128
129 auto slot = offset / slotSize;
130 // Slot is starting from callee regs
131 slot -= CFrameLayout::CALLEE_REGS_START_SLOT;
132 LLVM_LOG(DEBUG, STACKMAPS) << "\tReg: " << location.getDwarfRegNum() << ", offset: " << location.getOffset()
133 << ", LLVM stack offset: 0x" << std::hex << offset << ", ARK stack offset: 0x" << offset
134 << std::dec << ", ARK stack slot: " << slot;
135 return slot;
136 }
137
CollectRoots(const LLVMStackMap::RecordAccessor & record,uint64_t stackSize,ArenaBitVector * stack) const138 unsigned CodeInfoProducer::CollectRoots(const LLVMStackMap::RecordAccessor &record, uint64_t stackSize,
139 ArenaBitVector *stack) const
140 {
141 CFrameLayout fl(arch_, 0);
142 unsigned regMask = 0;
143 auto deoptCount = record.getLocation(LOCATION_DEOPT_COUNT).getSmallConstant();
144 for (uint64_t i = LOCATION_DEOPT_COUNT + deoptCount + 1; i < record.getNumLocations(); i += LOCATION_STEP) {
145 const auto &loc = record.getLocation(i);
146 auto kind = loc.getKind();
147 // We expect spilled references or constant null pointers that may come from "deopt" bundle
148 ASSERT(kind == LLVMStackMap::LocationKind::Indirect || kind == LLVMStackMap::LocationKind::Register ||
149 (kind == LLVMStackMap::LocationKind::Constant && loc.getSmallConstant() == 0));
150 // Spilled value
151 if (kind == LLVMStackMap::LocationKind::Indirect) {
152 const auto callerRegsSlotStart = fl.GetCallerFirstSlot(false);
153 const auto callerRegsCount = fl.GetCallerRegistersCount(false);
154 auto slot = GetArkFrameSlot(loc, stackSize, fl.GetSlotSize());
155 auto callerRegId = slot - callerRegsSlotStart;
156 if (slot < fl.GetFirstSpillSlot()) {
157 regMask |= (1U << (callerRegsCount - 1 - callerRegId));
158 } else {
159 stack->SetBit(slot - fl.GetFirstSpillSlot());
160 }
161 } else if (kind == LLVMStackMap::LocationKind::Register) {
162 unsigned reg = loc.getDwarfRegNum();
163 ASSERT(reg < DWARF_MAX_REG);
164 reg = arch_ == Arch::X86_64 ? LLVMArkInterface::X86RegNumberConvert(reg) : reg;
165 regMask |= (1U << reg);
166 }
167 }
168 LLVM_LOG(DEBUG, STACKMAPS) << "Register mask:" << regMask;
169 ASSERT((regMask & ~(GetCalleeRegsMask(arch_, false).GetValue() | GetCallerRegsMask(arch_, false).GetValue())) == 0);
170 return regMask;
171 }
172
BuildSingleRegMap(compiler::CodeInfoBuilder * builder,const LLVMStackMap::RecordAccessor & record,int32_t methodIdIndex,int32_t vregsCount,uint64_t stackSize) const173 void CodeInfoProducer::BuildSingleRegMap(compiler::CodeInfoBuilder *builder, const LLVMStackMap::RecordAccessor &record,
174 int32_t methodIdIndex, int32_t vregsCount, uint64_t stackSize) const
175 {
176 int32_t vregsTotal = record.getLocation(methodIdIndex + INLINE_VREG_COUNT).getSmallConstant();
177 std::vector<int> ordered;
178 ordered.resize(vregsTotal, -1);
179 for (auto i = 0; i < vregsCount * VREG_RECORD_SIZE; i += VREG_RECORD_SIZE) {
180 int32_t vregIndex = record.getLocation(methodIdIndex + INLINE_VREGS + i + VREG_IDX).getSmallConstant();
181 ordered.at(vregIndex) = i;
182 }
183
184 CFrameLayout fl(arch_, 0);
185 for (auto idx : ordered) {
186 if (idx == -1) {
187 builder->AddVReg(compiler::VRegInfo());
188 continue;
189 }
190 auto typeVal = record.getLocation(methodIdIndex + INLINE_VREGS + idx + VREG_TYPE).getSmallConstant();
191 auto vregType = static_cast<compiler::VRegInfo::Type>(typeVal);
192 const auto &loc = record.getLocation(methodIdIndex + INLINE_VREGS + idx + VREG_VALUE);
193 bool isAcc = ((idx / 3) == (vregsTotal - 1));
194 auto vregVregType = (isAcc ? compiler::VRegInfo::VRegType::ACC : compiler::VRegInfo::VRegType::VREG);
195 if (loc.getKind() == LLVMStackMap::LocationKind::Constant) {
196 int32_t constVal = loc.getSmallConstant(); // sign extend required for VRegInfo::Type::INT64
197 builder->AddConstant(static_cast<int64_t>(constVal), vregType, vregVregType);
198 } else if (loc.getKind() == LLVMStackMap::LocationKind::ConstantIndex) {
199 uint64_t constVal = stackmap_->getConstant(loc.getConstantIndex()).getValue();
200 builder->AddConstant(constVal, vregType, vregVregType);
201 } else if (loc.getKind() == LLVMStackMap::LocationKind::Indirect) {
202 auto slot = GetArkFrameSlot(loc, stackSize, fl.GetSlotSize());
203 compiler::VRegInfo::Location vregLoc;
204 int32_t value;
205 if (slot <= fl.GetCalleeLastSlot(false)) {
206 vregLoc = compiler::VRegInfo::Location::REGISTER;
207 value = GetFirstCalleeReg(arch_, false) + fl.GetCalleeLastSlot(false) - slot;
208 } else if (slot <= fl.GetCalleeLastSlot(true)) {
209 vregLoc = compiler::VRegInfo::Location::FP_REGISTER;
210 value = GetFirstCalleeReg(arch_, true) + fl.GetCalleeLastSlot(true) - slot;
211 } else {
212 vregLoc = compiler::VRegInfo::Location::SLOT;
213 value = slot;
214 }
215 ASSERT(value > 0);
216 builder->AddVReg(compiler::VRegInfo(value, vregLoc, vregType, vregVregType));
217 } else if (loc.getKind() == LLVMStackMap::LocationKind::Register) {
218 unsigned reg = loc.getDwarfRegNum();
219 reg = arch_ == Arch::X86_64 ? LLVMArkInterface::X86RegNumberConvert(reg) : reg;
220 builder->AddVReg(compiler::VRegInfo(reg, compiler::VRegInfo::Location::REGISTER, vregType, vregVregType));
221 } else {
222 UNREACHABLE();
223 }
224 }
225 }
226
BuildRegMap(compiler::CodeInfoBuilder * builder,const LLVMStackMap::RecordAccessor & record,uint64_t stackSize) const227 void CodeInfoProducer::BuildRegMap(compiler::CodeInfoBuilder *builder, const LLVMStackMap::RecordAccessor &record,
228 uint64_t stackSize) const
229 {
230 auto deoptCount = record.getLocation(LOCATION_DEOPT_COUNT).getSmallConstant();
231 if (deoptCount == 0) {
232 return;
233 }
234
235 auto inlineDepth = record.getLocation(LOCATION_INLINE_DEPTH).getSmallConstant();
236
237 auto inlineIndex = 0;
238 auto methodIdIndex = START_OFFSET + record.getLocation(LOCATION_INLINE_TABLE + inlineIndex).getSmallConstant();
239 uint32_t methodId;
240 uint32_t bpc;
241
242 uint32_t nextStart;
243 if (inlineDepth > 1) {
244 nextStart = START_OFFSET + record.getLocation(LOCATION_INLINE_TABLE + inlineIndex + 1).getSmallConstant();
245 } else {
246 nextStart = START_OFFSET + deoptCount;
247 }
248 uint32_t vregSlots = nextStart - methodIdIndex - INLINE_VREGS;
249 ASSERT(vregSlots % VREG_RECORD_SIZE == 0);
250 auto vregs = vregSlots / VREG_RECORD_SIZE;
251
252 if (record.getLocation(methodIdIndex + INLINE_NEED_REGMAP).getSmallConstant() > 0) {
253 BuildSingleRegMap(builder, record, methodIdIndex, vregs, stackSize);
254 }
255 for (uint32_t i = 1; i < inlineDepth; ++i) {
256 inlineIndex++;
257 methodIdIndex = START_OFFSET + record.getLocation(LOCATION_INLINE_TABLE + inlineIndex).getSmallConstant();
258 methodId = record.getLocation(methodIdIndex + INLINE_METHOD_ID).getSmallConstant();
259 bpc = record.getLocation(methodIdIndex + INLINE_BPC).getSmallConstant();
260 auto vregsTotal = record.getLocation(methodIdIndex + INLINE_VREG_COUNT).getSmallConstant();
261 if (i < inlineDepth - 1) {
262 nextStart = START_OFFSET + record.getLocation(LOCATION_INLINE_TABLE + inlineIndex + 1).getSmallConstant();
263 } else {
264 nextStart = START_OFFSET + deoptCount;
265 }
266 vregSlots = nextStart - methodIdIndex - INLINE_VREGS;
267 ASSERT(vregSlots % VREG_RECORD_SIZE == 0);
268 vregs = vregSlots / VREG_RECORD_SIZE;
269
270 builder->BeginInlineInfo(nullptr, methodId, bpc, vregsTotal);
271 if (vregsTotal > 0) {
272 BuildSingleRegMap(builder, record, methodIdIndex, vregs, stackSize);
273 }
274 builder->EndInlineInfo();
275 }
276 }
277
ConvertStackMaps(Method * method,CodeInfoBuilder * builder) const278 void CodeInfoProducer::ConvertStackMaps(Method *method, CodeInfoBuilder *builder) const
279 {
280 if (symbols_.find(method) == symbols_.end()) {
281 return;
282 }
283 ArenaAllocator allocator {SpaceType::SPACE_TYPE_COMPILER};
284 const auto &symbol = symbols_.at(method);
285 ASSERT(stackmap_ != nullptr);
286 int firstRecord = 0;
287 const auto &func = stackmap_->getFunction(symbol.idx);
288 int nRecords = func.getRecordCount();
289 for (uint32_t i = 0; i < symbol.idx; ++i) {
290 firstRecord += stackmap_->getFunction(i).getRecordCount();
291 }
292 LLVM_LOG(DEBUG, STACKMAPS) << "# Method '" << compilation_->GetRuntime()->GetMethodFullName(method, true)
293 << "' covers records in interval [" << firstRecord << ", "
294 << (firstRecord + nRecords - 1) << "] (inclusive)";
295 uint64_t stackSize = compilation_->GetMethodStackSize(method);
296 for (int i = firstRecord; i < firstRecord + nRecords; ++i) {
297 ArenaBitVector stackRoots(&allocator);
298
299 const auto &record = stackmap_->getRecord(i);
300 auto npc = symbol.sectionOffset + record.getInstructionOffset();
301 LLVM_LOG(DEBUG, STACKMAPS) << "Statepoint ID: " << record.getID() << std::hex << ", LLVM instruction offset: 0x"
302 << record.getInstructionOffset() << ", NPC offset: 0x" << npc << std::dec;
303 LLVM_LOG(DEBUG, STACKMAPS) << "Roots:";
304 auto regMap = CollectRoots(record, stackSize, &stackRoots);
305
306 auto hasDeopts = record.getLocation(LOCATION_DEOPT_COUNT).getSmallConstant() > 0;
307 auto needRegmap = false;
308 uint32_t bpc;
309 if (hasDeopts) {
310 auto methodIdIndex = 3 + record.getLocation(LOCATION_INLINE_TABLE).getSmallConstant();
311 bpc = record.getLocation(methodIdIndex + INLINE_BPC).getSmallConstant();
312 needRegmap = record.getLocation(methodIdIndex + INLINE_NEED_REGMAP).getSmallConstant() > 0;
313 } else {
314 bpc = record.getID();
315 }
316 compiler::SaveStateInst ss(compiler::Opcode::SaveState, 0, nullptr, nullptr, 0);
317 ss.SetRootsStackMask(&stackRoots);
318 ss.SetRootsRegsMask(regMap);
319 builder->BeginStackMap(bpc, npc, &ss, needRegmap);
320
321 LLVM_LOG(DEBUG, STACKMAPS) << "RegMap:";
322 BuildRegMap(builder, record, stackSize);
323
324 builder->EndStackMap();
325 }
326 }
327
EncodeNullChecks(Method * method,CodeInfoBuilder * builder) const328 void CodeInfoProducer::EncodeNullChecks(Method *method, CodeInfoBuilder *builder) const
329 {
330 if (faultMapParser_ == nullptr) {
331 return;
332 }
333 auto methodName = compilation_->GetUniqMethodName(method);
334
335 auto symbol = faultMapSymbols_.find(method);
336 if (symbol == faultMapSymbols_.end()) {
337 return;
338 }
339
340 // FunctionInfos are stored in a list, 'symbol->second' is an index in that list
341 ASSERT(faultMapParser_->getNumFunctions() > 0);
342 llvm::FaultMapParser::FunctionInfoAccessor functionInfo = faultMapParser_->getFirstFunctionInfo();
343 for (uint32_t i = 0; i < symbol->second; i++) {
344 functionInfo = functionInfo.getNextFunctionInfo();
345 }
346 // Process selected functionInfo
347 auto faultingPcs = functionInfo.getNumFaultingPCs();
348 for (size_t i = 0; i < faultingPcs; i++) {
349 auto faultInfo = functionInfo.getFunctionFaultInfoAt(i);
350 unsigned instructionNativePc = faultInfo.getFaultingPCOffset();
351 LLVM_LOG(DEBUG, STACKMAPS) << "Encoded implicit null check for '" << methodName << "', instructionNativePc = '"
352 << instructionNativePc << "'";
353 static constexpr uint32_t LLVM_HANDLER_TAG = 1U << 31U;
354 builder->AddImplicitNullCheck(instructionNativePc, LLVM_HANDLER_TAG | faultInfo.getHandlerPCOffset());
355 }
356 }
357 } // namespace ark::llvmbackend
358