LLVM IR codegen segfaults во время выхода только в том случае, если объявления методов имеют параметры

#c #segmentation-fault #bison #flex-lexer #llvm-c -api

Вопрос:

Объяснение

Я создаю компилятор для C-подобного языка, используя yacc/bison, flex и набор инструментов LLVM (LLVM 12), используя API LLVM C . Я разрабатывал и тестировал Ubuntu версии 20.04.3 LTS (Фокальная ямка) и macOS 11.6 Big Sur. В настоящее время проблема заключается в том, что программа выполняет сегментацию при выходе из программы, когда объявление метода содержит параметры метода, такие как просто:

 func test(x int) void {}  

ИК-адрес LLVM будет напечатан правильно, как

 ; ModuleID = 'Test' source_filename = "Test"  define void @test(i32 %x) { entry:  %x1 = alloca i32, align 4  store i32 %x, i32* %x1, align 4  ret void }  

И сразу же после этого сделает сегфолт.

Объявление метода, например

 func test() int {  var x int;  x = 5;  return (x); }   

Не будет сегфолта.

GDB сообщает, что segfault происходит во llvm::LLVMContextImpl::~LLVMContextImpl() время . Valgrind сообщает ~LLVMContextImpl() о недопустимом чтении размера 8.

Изменить: Вывод Valgrind, относящийся к недопустимому чтению

 ==10254== Invalid read of size 8 ==10254== at 0x5553C30: llvm::LLVMContextImpl::~LLVMContextImpl() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1) ==10254== by 0x5552130: llvm::LLVMContext::~LLVMContext() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1) ==10254== by 0xA44AA26: __run_exit_handlers (exit.c:108) ==10254== by 0xA44ABDF: exit (exit.c:139) ==10254== by 0xA4280B9: (below main) (libc-start.c:342) ==10254== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==10254==  ==10254==  ==10254== Process terminating with default action of signal 11 (SIGSEGV) ==10254== Access not within mapped region at address 0x0 ==10254== at 0x5553C30: llvm::LLVMContextImpl::~LLVMContextImpl() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1) ==10254== by 0x5552130: llvm::LLVMContext::~LLVMContext() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1) ==10254== by 0xA44AA26: __run_exit_handlers (exit.c:108) ==10254== by 0xA44ABDF: exit (exit.c:139) ==10254== by 0xA4280B9: (below main) (libc-start.c:342) ==10254== If you believe this happened as a result of a stack ==10254== overflow in your program's main thread (unlikely but ==10254== possible), you can try to increase the size of the ==10254== main thread stack using the --main-stacksize= flag. ==10254== The main thread stack size used in this run was 8388608.  

I’m hoping that by asking here I can get some kind of hint for how to work towards solving this issue. I’ve been stuck on this for days.

Source Code Fragments

The sections of my code relating to method declarations and method parameters are as follow, I apologize for the length:

Bison grammar rule for program

 program: extern_list decafpackage  {   ProgramAST *prog = new ProgramAST((decafStmtList*)$1, (PackageAST*)$2);   if (printAST) {  cout lt;lt; getString(prog) lt;lt; endl;  }  prog-gt;Codegen();  delete prog;  }  ;  

Bison grammar rule for method declaration

 method_decl: T_FUNC T_ID T_LPAREN params T_RPAREN method_type method_block   {  $ = new Method(*$2, $6-gt;str(), $4, $7);  delete $2;   delete $6;  }  

Bison grammar rule for method parameter

 param: T_ID type { $ = new VarDef(*$1, $2-gt;str()); delete $1; delete $2; }  ;  

C Method::Codegen() handling of parameters

 llvm::Function *func = llvm::Function::Create(  llvm::FunctionType::get(returnTy, args, false),  llvm::Function::ExternalLinkage,  name,  TheModule  );  llvm::BasicBlock *BB = llvm::BasicBlock::Create(TheContext, "entry", func); Builder.SetInsertPoint(BB);  . . .  for (auto amp;Arg : func-gt;args()) {  llvm::AllocaInst* Alloca = CreateEntryBlockAlloca(func, Arg.getName().str());  Builder.CreateStore(amp;Arg, Alloca);  sTStack-gt;enter_symtbl(Arg.getName().str(), Alloca);  }  

C VarDef::Codegen()

 llvm::Value *Codegen() {  llvm::Type* ty = getLLVMType(type);  llvm::AllocaInst* V = Builder.CreateAlloca(ty, 0, name);  V-gt;setName(name);  sTStack-gt;enter_symtbl(name, V);  return V;  return nullptr;  }  

Зубр главный

 int main() {  // Setup  llvm::LLVMContext amp;Context = TheContext;  TheModule = new llvm::Module("Test", Context);  FPM = std::make_uniquelt;llvm::legacy::FunctionPassManagergt;(TheModule);  FPM-gt;add(llvm::createPromoteMemoryToRegisterPass());  FPM-gt;add(llvm::createInstructionCombiningPass());  FPM-gt;add(llvm::createReassociatePass());  FPM-gt;add(llvm::createGVNPass());  FPM-gt;add(llvm::createCFGSimplificationPass());  FPM-gt;doInitialization();   int retval = yyparse();   TheModule-gt;print(llvm::errs(), nullptr);  return(retval gt;= 1 ? EXIT_FAILURE : EXIT_SUCCESS); }  

Комментарии:

1. При использовании valgrind (или, лучше, AddressSanitizer) и в нем говорится, что вы выполняете недопустимое чтение, важно посмотреть, где находится это недопустимое чтение. Находится ли он в конце допустимого объекта? Является ли это частью недавно освобожденного объекта? Если вы передадите правильные параметры, он будет отслеживать, где был выделен/освобожден близлежащий объект.

2. @o11c Я добавил вывод Valgrind, относящийся к недопустимому чтению. Я попробую еще немного покопаться в Valgrind/GDB/отладчиках, чтобы посмотреть, смогу ли я выяснить, где происходит недопустимое чтение.

3. Это «вероятно» не является источником сбоя (хотя это может быть), но вы никогда не удаляете свой модуль.

4. @o11c: Считываемый адрес является нулевым указателем.

5. Если вы можете либо войти в деструктор llvm::LLVMContextImpl::~LLVMContextImpl() (с версией библиотеки, содержащей отладочные символы), либо разобрать его и проверить регистры/память в момент сбоя, вы, возможно, сможете выяснить, откуда у него нулевой указатель, который он разыменовывает. Поместите контрольную точку записи на этот объект указателя и снова запустите программу. Тогда вы сможете увидеть, где это было аннулировано, и выяснить, должно ли это было произойти.

Ответ №1:

Решение:

Проблема заключалась в том, что строки кода не были включены. llvm::Function::Create требуется llvm::FunctionType , который может быть предоставлен путем заполнения вектора llvm::Type* объектами. Я написал функцию для этого:

 void getLLVMTypes(vectorlt;llvm::Type*gt;* v) {  for (auto* i : stmts) {  llvm::Type* type = getLLVMType(i-gt;getType());  ((llvm::Value*)(type))-gt;setName(i-gt;getName()); // Problem  v-gt;push_back(type);  } }  

Проблема заключалась в приведении каждого llvm::Type* объекта к llvm::Value* и использовании llvm::Value::setName для установки его имени. Я сделал это, чтобы решить проблему, с которой я столкнулся ранее, когда имена параметров не были заданы. Я не совсем уверен, в чем была проблема, у меня возникли проблемы с компиляцией LLVM из исходного кода с флагами отладки, но это грубая строка кода, и ее удаление, а также использование альтернативного способа сохранения имен параметров метода, решили проблему.