blob: d021a5f705f0f93164eb4f5c63f8e8dff5040104 [file] [log] [blame]
Ben Claytonac07ed82019-03-26 14:17:41 +00001// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2//
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#include "LLVMReactorDebugInfo.hpp"
16
17#ifdef ENABLE_RR_DEBUG_INFO
18
19#include "Reactor.hpp"
20#include "LLVMReactor.hpp"
21
Antonio Maioranof448d8e2019-04-26 16:19:16 -040022#include "boost/stacktrace.hpp"
Ben Claytonac07ed82019-03-26 14:17:41 +000023
24#include "llvm/Demangle/Demangle.h"
25#include "llvm/ExecutionEngine/JITEventListener.h"
26#include "llvm/IR/DIBuilder.h"
27#include "llvm/IR/Intrinsics.h"
28#include "llvm/IR/IRBuilder.h"
29
30#include <cctype>
31#include <fstream>
32#include <regex>
33#include <sstream>
34#include <string>
35
36#if 0
37#define LOG(msg, ...) printf(msg "\n", ##__VA_ARGS__)
38#else
39#define LOG(msg, ...)
40#endif
41
42namespace
43{
44 std::pair<llvm::StringRef, llvm::StringRef> splitPath(const char* path)
45 {
46 return llvm::StringRef(path).rsplit('/');
47 }
48} // anonymous namespaces
49
50namespace rr
51{
52 DebugInfo::DebugInfo(
53 llvm::IRBuilder<> *builder,
54 llvm::LLVMContext *context,
55 llvm::Module *module,
56 llvm::Function *function)
57 : builder(builder), context(context), module(module), function(function)
58 {
59 using namespace ::llvm;
60
61 auto location = getCallerLocation();
62
63 auto fileAndDir = splitPath(location.function.file.c_str());
64 diBuilder = new llvm::DIBuilder(*module);
65 diCU = diBuilder->createCompileUnit(
66 llvm::dwarf::DW_LANG_C,
67 diBuilder->createFile(fileAndDir.first, fileAndDir.second),
68 "Reactor",
69 0, "", 0);
70
71 jitEventListener = llvm::JITEventListener::createGDBRegistrationListener();
72 registerBasicTypes();
73
74 SmallVector<Metadata *, 8> EltTys;
75 auto funcTy = diBuilder->createSubroutineType(diBuilder->getOrCreateTypeArray(EltTys));
76
77 auto file = getOrCreateFile(location.function.file.c_str());
78 auto sp = diBuilder->createFunction(
79 file, // scope
80 "ReactorFunction", // function name
81 "ReactorFunction", // linkage
82 file, // file
83 location.line, // line
84 funcTy, // type
85 false, // internal linkage
86 true, // definition
87 location.line, // scope line
88 DINode::FlagPrototyped, // flags
89 false // is optimized
90 );
91 diSubprogram = sp;
92 function->setSubprogram(sp);
93 diRootLocation = DILocation::get(*context, location.line, 0, sp);
94 builder->SetCurrentDebugLocation(diRootLocation);
95 }
96
97 void DebugInfo::Finalize()
98 {
99 while (diScope.size() > 0)
100 {
101 emitPending(diScope.back(), builder, diBuilder);
102 diScope.pop_back();
103 }
104 diBuilder->finalize();
105 }
106
107 void DebugInfo::EmitLocation()
108 {
109 auto const& backtrace = getCallerBacktrace();
110 syncScope(backtrace);
111 builder->SetCurrentDebugLocation(getLocation(backtrace, backtrace.size() - 1));
Antonio Maioranof448d8e2019-04-26 16:19:16 -0400112
113#ifdef ENABLE_RR_EMIT_PRINT_LOCATION
114 static Location lastLocation;
115 if (backtrace.size() == 0)
116 {
117 return;
118 }
119 Location currLocation = backtrace[backtrace.size() - 1];
120 if (currLocation != lastLocation)
121 {
122 rr::Print("rr> {0} [{1}:{2}]\n", currLocation.function.name.c_str(), currLocation.function.file.c_str(), currLocation.line);
123 lastLocation = std::move(currLocation);
124 }
125#endif // ENABLE_RR_EMIT_PRINT_LOCATION
Ben Claytonac07ed82019-03-26 14:17:41 +0000126 }
127
128 void DebugInfo::Flush()
129 {
130 emitPending(diScope.back(), builder, diBuilder);
131 }
132
133 void DebugInfo::syncScope(Backtrace const& backtrace)
134 {
135 auto shrink = [this](size_t newsize)
136 {
137 while (diScope.size() > newsize)
138 {
139 auto &scope = diScope.back();
140 LOG("- STACK(%d): di: %p, location: %s:%d",
141 int(diScope.size() - 1), scope.di,
142 scope.location.function.file.c_str(),
143 int(scope.location.line));
144 emitPending(scope, builder, diBuilder);
145 diScope.pop_back();
146 }
147 };
148
149 if (backtrace.size() < diScope.size())
150 {
151 shrink(backtrace.size());
152 }
153
154 for (size_t i = 0; i < diScope.size(); i++)
155 {
156 auto &scope = diScope[i];
157 auto const &oldLocation = scope.location;
158 auto const &newLocation = backtrace[i];
159
160 if (oldLocation.function != newLocation.function)
161 {
162 LOG(" STACK(%d): Changed function %s -> %s", int(i),
163 oldLocation.function.name.c_str(), newLocation.function.name.c_str());
164 shrink(i);
165 break;
166 }
167
168 if (oldLocation.line > newLocation.line)
169 {
170 // Create a new di block to shadow all the variables in the loop.
171 auto file = getOrCreateFile(newLocation.function.file.c_str());
172 auto di = diBuilder->createLexicalBlock(scope.di, file, newLocation.line, 0);
173 LOG(" STACK(%d): Jumped backwards %d -> %d. di: %p -> %p", int(i),
174 oldLocation.line, newLocation.line, scope.di, di);
175 emitPending(scope, builder, diBuilder);
176 scope = {newLocation, di};
177 shrink(i+1);
178 break;
179 }
180
181 scope.location = newLocation;
182 }
183
184 while (backtrace.size() > diScope.size())
185 {
186 auto i = diScope.size();
187 auto location = backtrace[i];
188 auto file = getOrCreateFile(location.function.file.c_str());
189 auto funcTy = diBuilder->createSubroutineType(diBuilder->getOrCreateTypeArray({}));
190
191 char buf[1024];
192 size_t size = sizeof(buf);
193 int status = 0;
194 llvm::itaniumDemangle(location.function.name.c_str(), buf, &size, &status);
Antonio Maioranof448d8e2019-04-26 16:19:16 -0400195 auto name = "jit!" + (status == 0 ? std::string(buf) : location.function.name);
Ben Claytonac07ed82019-03-26 14:17:41 +0000196
197 auto func = diBuilder->createFunction(
198 file, // scope
199 name, // function name
200 "", // linkage
201 file, // file
202 location.line, // line
203 funcTy, // type
204 false, // internal linkage
205 true, // definition
206 location.line, // scope line
207 llvm::DINode::FlagPrototyped, // flags
208 false // is optimized
209 );
210 diScope.push_back({location, func});
211 LOG("+ STACK(%d): di: %p, location: %s:%d", int(i), di,
212 location.function.file.c_str(), int(location.line));
213 }
214 }
215
216 llvm::DILocation* DebugInfo::getLocation(const Backtrace &backtrace, size_t i)
217 {
218 if (backtrace.size() == 0) { return nullptr; }
219 assert(backtrace.size() == diScope.size());
220 return llvm::DILocation::get(
221 *context,
222 backtrace[i].line,
223 0,
224 diScope[i].di,
225 i > 0 ? getLocation(backtrace, i - 1) : diRootLocation
226 );
227 }
228
229 void DebugInfo::EmitVariable(Value *variable)
230 {
231 auto const& backtrace = getCallerBacktrace();
232 syncScope(backtrace);
233
234 for (int i = backtrace.size() - 1; i >= 0; i--)
235 {
236 auto const &location = backtrace[i];
237 auto tokens = getOrParseFileTokens(location.function.file.c_str());
238 auto tokIt = tokens->find(location.line);
239 if (tokIt == tokens->end())
240 {
241 break;
242 }
243 auto token = tokIt->second;
244 auto name = token.identifier;
245 if (token.kind == Token::Return)
246 {
247 // This is a:
248 //
249 // return <expr>;
250 //
251 // Emit this expression as two variables -
252 // Once as a synthetic 'return_value' variable at this scope.
253 // Again by bubbling the expression value up the callstack as
254 // Return Value Optimizations (RVOs) are likely to carry across
255 // the value to a local without calling a constructor in
256 // statements like:
257 //
258 // auto val = foo();
259 //
260 name = "return_value";
261 }
262
263 auto &scope = diScope[i];
264 if (scope.pending.location != location)
265 {
266 emitPending(scope, builder, diBuilder);
267 }
268
269 auto value = V(variable);
270 auto block = builder->GetInsertBlock();
271
272 auto insertAfter = block->size() > 0 ? &block->back() : nullptr;
273 while (insertAfter != nullptr && insertAfter->isTerminator())
274 {
275 insertAfter = insertAfter->getPrevNode();
276 }
277
278 scope.pending = Pending{};
279 scope.pending.name = name;
280 scope.pending.location = location;
281 scope.pending.diLocation = getLocation(backtrace, i);
282 scope.pending.value = value;
283 scope.pending.block = block;
284 scope.pending.insertAfter = insertAfter;
285 scope.pending.scope = scope.di;
286
287 if (token.kind == Token::Return)
288 {
289 // Insert a noop instruction so the debugger can inspect the
290 // return value before the function scope closes.
291 scope.pending.addNopOnNextLine = true;
292 }
293 else
294 {
295 break;
296 }
297 }
298 }
299
300 void DebugInfo::emitPending(Scope &scope, IRBuilder *builder, llvm::DIBuilder *diBuilder)
301 {
302 auto const &pending = scope.pending;
303 if (pending.value == nullptr)
304 {
305 return;
306 }
307
308 if (!scope.symbols.emplace(pending.name).second)
309 {
310 return;
311 }
312
313 bool isAlloca = llvm::isa<llvm::AllocaInst>(pending.value);
314
315 LOG(" EMIT(%s): di: %p, location: %s:%d, isAlloca: %s", pending.name.c_str(), scope.di,
316 pending.location.function.file.c_str(), pending.location.line, isAlloca ? "true" : "false");
317
318 auto value = pending.value;
319
320 IRBuilder::InsertPointGuard guard(*builder);
321 if (pending.insertAfter != nullptr)
322 {
323 builder->SetInsertPoint(pending.block, ++pending.insertAfter->getIterator());
324 }
325 else
326 {
327 builder->SetInsertPoint(pending.block);
328 }
329 builder->SetCurrentDebugLocation(pending.diLocation);
330
331 if (!isAlloca)
332 {
333 // While insertDbgValueIntrinsic should be enough to declare a
334 // variable with no storage, variables of RValues can share the same
335 // llvm::Value, and only one can be named. Take for example:
336 //
337 // Int a = 42;
338 // RValue<Int> b = a;
339 // RValue<Int> c = b;
340 //
341 // To handle this, always promote named RValues to an alloca.
342
343 llvm::BasicBlock &entryBlock = function->getEntryBlock();
344 auto alloca = new llvm::AllocaInst(value->getType(), 0, pending.name);
345 entryBlock.getInstList().push_front(alloca);
346 builder->CreateStore(value, alloca);
347 value = alloca;
348 }
349
350 value->setName(pending.name);
351
352 auto diFile = getOrCreateFile(pending.location.function.file.c_str());
353 auto diType = getOrCreateType(value->getType()->getPointerElementType());
354 auto diVar = diBuilder->createAutoVariable(scope.di, pending.name, diFile, pending.location.line, diType);
355
356 auto di = diBuilder->insertDeclare(value, diVar, diBuilder->createExpression(), pending.diLocation, pending.block);
357 if (pending.insertAfter != nullptr) { di->moveAfter(pending.insertAfter); }
358
359 if (pending.addNopOnNextLine)
360 {
361 builder->SetCurrentDebugLocation(llvm::DILocation::get(
362 *context,
363 pending.diLocation->getLine() + 1,
364 0,
365 pending.diLocation->getScope(),
366 pending.diLocation->getInlinedAt()
367 ));
368 Nop();
369 }
370
371 scope.pending = Pending{};
372 }
373
374 void DebugInfo::NotifyObjectEmitted(const llvm::object::ObjectFile &Obj, const llvm::LoadedObjectInfo &L)
375 {
376 jitEventListener->NotifyObjectEmitted(Obj, static_cast<const llvm::RuntimeDyld::LoadedObjectInfo&>(L));
377 }
378
379 void DebugInfo::NotifyFreeingObject(const llvm::object::ObjectFile &Obj)
380 {
381 jitEventListener->NotifyFreeingObject(Obj);
382 }
383
384 void DebugInfo::registerBasicTypes()
385 {
386 using namespace rr;
387 using namespace llvm;
388
389 auto vec4 = diBuilder->getOrCreateArray(diBuilder->getOrCreateSubrange(0, 4));
390 auto vec8 = diBuilder->getOrCreateArray(diBuilder->getOrCreateSubrange(0, 8));
391 auto vec16 = diBuilder->getOrCreateArray(diBuilder->getOrCreateSubrange(0, 16));
392
393 diTypes.emplace(T(Bool::getType()), diBuilder->createBasicType("Bool", sizeof(bool), dwarf::DW_ATE_boolean));
394 diTypes.emplace(T(Byte::getType()), diBuilder->createBasicType("Byte", 8, dwarf::DW_ATE_unsigned_char));
395 diTypes.emplace(T(SByte::getType()), diBuilder->createBasicType("SByte", 8, dwarf::DW_ATE_signed_char));
396 diTypes.emplace(T(Short::getType()), diBuilder->createBasicType("Short", 16, dwarf::DW_ATE_signed));
397 diTypes.emplace(T(UShort::getType()), diBuilder->createBasicType("UShort", 16, dwarf::DW_ATE_unsigned));
398 diTypes.emplace(T(Int::getType()), diBuilder->createBasicType("Int", 32, dwarf::DW_ATE_signed));
399 diTypes.emplace(T(UInt::getType()), diBuilder->createBasicType("UInt", 32, dwarf::DW_ATE_unsigned));
400 diTypes.emplace(T(Long::getType()), diBuilder->createBasicType("Long", 64, dwarf::DW_ATE_signed));
401 diTypes.emplace(T(Half::getType()), diBuilder->createBasicType("Half", 16, dwarf::DW_ATE_float));
402 diTypes.emplace(T(Float::getType()), diBuilder->createBasicType("Float", 32, dwarf::DW_ATE_float));
403
404 diTypes.emplace(T(Byte4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Byte::getType())], {vec16}));
405 diTypes.emplace(T(SByte4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(SByte::getType())], {vec16}));
406 diTypes.emplace(T(Byte8::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Byte::getType())], {vec16}));
407 diTypes.emplace(T(SByte8::getType()), diBuilder->createVectorType(128, 128, diTypes[T(SByte::getType())], {vec16}));
408 diTypes.emplace(T(Byte16::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Byte::getType())], {vec16}));
409 diTypes.emplace(T(SByte16::getType()), diBuilder->createVectorType(128, 128, diTypes[T(SByte::getType())], {vec16}));
410 diTypes.emplace(T(Short2::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Short::getType())], {vec8}));
411 diTypes.emplace(T(UShort2::getType()), diBuilder->createVectorType(128, 128, diTypes[T(UShort::getType())], {vec8}));
412 diTypes.emplace(T(Short4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Short::getType())], {vec8}));
413 diTypes.emplace(T(UShort4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(UShort::getType())], {vec8}));
414 diTypes.emplace(T(Short8::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Short::getType())], {vec8}));
415 diTypes.emplace(T(UShort8::getType()), diBuilder->createVectorType(128, 128, diTypes[T(UShort::getType())], {vec8}));
416 diTypes.emplace(T(Int2::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Int::getType())], {vec4}));
417 diTypes.emplace(T(UInt2::getType()), diBuilder->createVectorType(128, 128, diTypes[T(UInt::getType())], {vec4}));
418 diTypes.emplace(T(Int4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Int::getType())], {vec4}));
419 diTypes.emplace(T(UInt4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(UInt::getType())], {vec4}));
420 diTypes.emplace(T(Float2::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Float::getType())], {vec4}));
421 diTypes.emplace(T(Float4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Float::getType())], {vec4}));
422 }
423
424 DebugInfo::Location DebugInfo::getCallerLocation() const
425 {
426 return getCallerBacktrace(1)[0];
427 }
428
429 DebugInfo::Backtrace DebugInfo::getCallerBacktrace(size_t limit /* = 0 */) const
430 {
Antonio Maioranof448d8e2019-04-26 16:19:16 -0400431 auto shouldSkipFile = [](llvm::StringRef fileSR) {
432 return fileSR.empty() ||
433 fileSR.endswith_lower("ReactorDebugInfo.cpp") ||
434 fileSR.endswith_lower("Reactor.cpp") ||
435 fileSR.endswith_lower("Reactor.hpp") ||
436 fileSR.endswith_lower("stacktrace.hpp");
Ben Claytonac07ed82019-03-26 14:17:41 +0000437 };
438
Antonio Maioranof448d8e2019-04-26 16:19:16 -0400439 std::vector<DebugInfo::Location> locations;
Ben Claytonac07ed82019-03-26 14:17:41 +0000440
Antonio Maioranof448d8e2019-04-26 16:19:16 -0400441 // Note that bs::stacktrace() effectively returns a vector of addresses; bs::frame construction is where
442 // the heavy lifting is done: resolving the function name, file and line number.
443 namespace bs = boost::stacktrace;
444 for (bs::frame frame : bs::stacktrace())
445 {
446 if (shouldSkipFile(frame.source_file()))
447 {
448 continue;
449 }
Ben Claytonac07ed82019-03-26 14:17:41 +0000450
Antonio Maioranof448d8e2019-04-26 16:19:16 -0400451 DebugInfo::Location location;
452 location.function.file = frame.source_file();
453 location.function.name = frame.name();
454 location.line = frame.source_line();
455 locations.push_back(location);
456
457 if (limit > 0 && locations.size() >= limit)
458 {
459 break;
460 }
461 }
462
463 std::reverse(locations.begin(), locations.end());
464
465 return locations;
Ben Claytonac07ed82019-03-26 14:17:41 +0000466 }
467
468 llvm::DIType *DebugInfo::getOrCreateType(llvm::Type* type)
469 {
470 auto it = diTypes.find(type);
471 if (it != diTypes.end()) { return it->second; }
472
473 if(type->isPointerTy())
474 {
475 auto dbgTy = diBuilder->createPointerType(
476 getOrCreateType(type->getPointerElementType()),
477 sizeof(void*)*8, alignof(void*)*8);
478 diTypes.emplace(type, dbgTy);
479 return dbgTy;
480 }
481 llvm::errs() << "Unimplemented debug type: " << type << "\n";
482 assert(false);
Ben Clayton327e4882019-05-17 12:10:12 +0100483 return nullptr;
Ben Claytonac07ed82019-03-26 14:17:41 +0000484 }
485
486 llvm::DIFile *DebugInfo::getOrCreateFile(const char* path)
487 {
488 auto it = diFiles.find(path);
489 if (it != diFiles.end()) { return it->second; }
490 auto dirAndName = splitPath(path);
491 auto file = diBuilder->createFile(dirAndName.second, dirAndName.first);
492 diFiles.emplace(path, file);
493 return file;
494 }
495
496 DebugInfo::LineTokens const *DebugInfo::getOrParseFileTokens(const char* path)
497 {
498 static std::regex reLocalDecl(
499 "^" // line start
500 "\\s*" // initial whitespace
501 "(?:For\\s*\\(\\s*)?" // optional 'For ('
502 "((?:\\w+(?:<[^>]+>)?)(?:::\\w+(?:<[^>]+>)?)*)" // type (match group 1)
503 "\\s+" // whitespace between type and name
504 "(\\w+)" // identifier (match group 2)
505 "\\s*" // whitespace after identifier
506 "(\\[.*\\])?"); // optional array suffix (match group 3)
507
508 auto it = fileTokens.find(path);
509 if (it != fileTokens.end())
510 {
511 return it->second.get();
512 }
513
514 auto tokens = std::unique_ptr<LineTokens>(new LineTokens());
515
516 std::ifstream file(path);
517 std::string line;
518 int lineCount = 0;
519 while (std::getline(file, line))
520 {
521 lineCount++;
522 std::smatch match;
523 if (std::regex_search(line, match, reLocalDecl) && match.size() > 3)
524 {
525 bool isArray = match.str(3) != "";
526 if (!isArray) // Cannot deal with C-arrays of values.
527 {
528 if (match.str(1) == "return")
529 {
530 (*tokens)[lineCount] = Token{Token::Return};
531 }
532 else
533 {
534 (*tokens)[lineCount] = Token{Token::Identifier, match.str(2)};
535 }
536 }
537 }
538 }
539
540 auto out = tokens.get();
541 fileTokens.emplace(path, std::move(tokens));
542 return out;
543 }
544
545} // namespace rr
546
547#endif // ENABLE_RR_DEBUG_INFO