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