• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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