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