Ошибка сегментации GEP LLVM C ++ API

Я уверен, что это действительно просто, но я пытался разобраться в этом больше часа и не могу понять.

Следующий код дает мне ошибку сегментации:

Value *newArray = mBuilder.CreateGEP(alloca, value); // alloca is a `StructType`

но это не

Value *newArray = mBuilder.CreateGEP(alloca, ConstantInt::get(mContext, APInt(32, 0)));

Значение value

%bar1 = load double, double* %bar
%3 = fptoui double %bar1 to i32

Отладка

Когда я отлаживаю его с помощью lldb, я получаю:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x00000001000b9e6e a.out`llvm::PointerType::get(llvm::Type*, unsigned int) + 20
a.out`llvm::PointerType::get:
->  0x1000b9e6e <+20>: movq   (%rdi), %rax

Вопрос

Почему у меня возникает ошибка сегментации и как ее исправить?

Как воспроизвести проблему?

Следующий код воспроизводит проблему:

#include <vector>

#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Value.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"

using namespace llvm;

static LLVMContext mContext;
static IRBuilder<> mBuilder(mContext);
static std::unique_ptr<Module> mModule = make_unique<Module>("example", mContext);
static Module *M = mModule.get();

static Type *dType = Type::getDoubleTy(mContext);
static Type *i32 = IntegerType::get(mContext, 32);

// helper functions
static AllocaInst *entryCreateBlockAllocaType(Function *func, std::string name, Type* type) {
  IRBuilder<> tmpBuilder(&func->getEntryBlock(), func->getEntryBlock().begin());
  return tmpBuilder.CreateAlloca(type, nullptr, name);
}

static ArrayRef<Value *> PrefixZero (Value *index) {
  std::vector<Value *> out;
  out.push_back(ConstantInt::get(mContext, APInt(32, 0)));
  out.push_back(index);
  return ArrayRef<Value *>(out);
}

static AllocaInst *createVariable () {
  auto *func = mBuilder.GetInsertBlock()->getParent();
  auto *initValue = ConstantInt::get(mContext, APInt(32, 0));

  auto *alloca = entryCreateBlockAllocaType(func, "var", initValue->getType());
  mBuilder.CreateStore(initValue, alloca);
  return alloca; 
}

static std::vector<Type *> elementTypes (3, dType);
static AllocaInst *createStruct () {
  auto *func = mBuilder.GetInsertBlock()->getParent();

  auto *mStructType = StructType::get(mContext, elementTypes);
  return entryCreateBlockAllocaType(func, "str", mStructType);
}

int main () {
  // create a main function
  auto *FT = FunctionType::get(i32, std::vector<Type *>(), false);
  auto *f = Function::Create(FT, Function::ExternalLinkage, "main", M);

  // set insert point for out below code
  auto *bb = BasicBlock::Create(mContext, "entry", f);
  mBuilder.SetInsertPoint(bb);

  // Create a variable
  auto *variable = createVariable();
  // create a struct
  auto *mStruct = createStruct();

  // Create a GEP with the loaded index
  auto *loadedVar = mBuilder.CreateLoad(variable, "loaded_index");

  // This is where the problem is.
  // If `PrefixZero` is changed to `ConstantInt::get(mContext, APInt(32, 0))` this works
  auto *elementPtr = mBuilder.CreateGEP(mStruct, PrefixZero(loadedVar)); 

  mBuilder.CreateRet(ConstantInt::get(mContext, APInt(32, 0))); 
  f->print(errs()); // print out the function

  return 1;
}

Код также можно посмотреть здесь.


person zoecarver    schedule 14.06.2018    source источник
comment
alloca - это структура или указатель на структуру? GEP всегда принимает указатель. Если вы еще этого не сделали, попробуйте собрать LLVM с включенными утверждениями. Надеюсь, это даст вам ошибку утверждения с правильным сообщением об ошибке, а не ошибку сегментации. Если это не помогает, опубликуйте MCVE, который я или кто-то другой могу использовать для воспроизведения проблемы.   -  person sepp2k    schedule 15.06.2018
comment
@ sepp2k Спасибо. Это указатель на тип структуры (Builder.CreateAlloca). Я попытаюсь создать llvm с включенными утверждениями и соответствующим образом обновлю свой вопрос.   -  person zoecarver    schedule 15.06.2018
comment
@ sepp2k Я построил llvm с -LLVM_ENABLE_ASSERTIONS=ON, но ничего не изменилось (AFAIK). Я обновил вопрос, добавив ссылку на созданный мной репозиторий, в котором показано, как воспроизвести проблему. github.com/pudility/GEP_Segmentation_fault   -  person zoecarver    schedule 15.06.2018


Ответы (1)


С вашим кодом есть две проблемы:

  1. static ArrayRef<Value *> PrefixZero (Value *index) {
      std::vector<Value *> out;
      out.push_back(ConstantInt::get(mContext, APInt(32, 0)));
      out.push_back(index);
      return ArrayRef<Value *>(out);
    }
    
    # P2 #
    # P3 #
    # P4 #
  2. При использовании getelementptr в структуре индекс, представляющий доступ к члену (то есть второй индекс в вашем случае), должен быть константой. Если задуматься, в противном случае невозможно было бы проверить тип инструкции (имея в виду, что обычно не все члены структуры имеют один и тот же тип). Кроме того, вычисление смещения указателя для данного непостоянного индекса в основном должно было бы создать целую таблицу поиска, и было бы нелогично для инструкции арифметики указателя сгенерировать такой объем кода. Вы можете думать о GEP в структуре как о эквиваленте the_struct.member_name в C, и вы также не можете заменить member_name переменной там.

Обратите внимание: если в вашей сборке LLVM включены утверждения, вторая проблема должна вызвать сбой утверждения «Недопустимые индексы GetElementPtrInst для типа!», Что, хотя и не совсем сообщает вам все, что вам нужно знать (например, каким образом индексы недопустимый), указывает вам в правильном направлении гораздо больше, чем просто "ошибка сегментации". Поэтому, если вы не получили это сообщение, убедитесь, что у вас включены утверждения, чтобы вы могли воспользоваться сообщениями с утверждениями в следующий раз, когда вы столкнетесь с проблемами.

person sepp2k    schedule 15.06.2018
comment
Спасибо - в этом есть смысл. Единственное, что это странно, как это работает с ArrayTypes, а не StructTypes. Как вы посоветуете мне получить элемент по индексу переменной? Спасибо еще раз за помощь! - person zoecarver; 16.06.2018
comment
@pudility Если я изменю createStruct, чтобы вместо этого возвращать массив, я все равно получаю segfault, поэтому я не думаю, что он работает с ArrayTypes, просто StructTypes не является полностью истинным (хотя версии компилятора и настройки оптимизации также могут повлиять на это). Тем не менее, доступ к освобожденной памяти - это UB, поэтому любые изменения, которые вы вносите в код, могут заставить его работать без фактического устранения проблемы. Вы можете использовать valgrind или дезинфицирующие средства, чтобы найти проблемы с памятью даже в программах, которые кажутся работоспособными. Но похоже, что это не единственная проблема в вашем коде. Позже я посмотрю поближе. - person sepp2k; 16.06.2018
comment
@pudility Вторая проблема заключается в том, что структуры можно индексировать только с помощью постоянных индексов, а не переменных. - person sepp2k; 16.06.2018
comment
В этом есть смысл - спасибо за помощь. Еще один вопрос, но я не думаю, что для него стоит открывать еще один выпуск. Вы знаете, почему я не могу преобразовать индекс (%3 = fptoui double %bar1 to i32) в константу int (dyn_cast<ConstantInt>(index))? - person zoecarver; 17.06.2018
comment
@pudility Потому что это не ConstantInt, это FPToUIInst. Если что-то является результатом какой-либо инструкции, а не целочисленным литералом, то это не экземпляр ConstantInt и не может быть приведен как таковой. Вы можете объяснить, что пытаетесь сделать? Почему у вас есть индекс члена структуры, который начинается как двойник? Как выглядит соответствующий код на вашем исходном языке? Если ваш язык допускает динамический доступ к переменным-членам в структурах (хотя я ожидал, что это будет работать со строками, а не с двойниками), вам понадобится более сложное представление в LLVM. - person sepp2k; 17.06.2018
comment
Спасибо, что это объясняет. Я использую двойные числа, чтобы иметь только один тип числа на игрушечном языке, который я делаю (пока). Вот ссылка на код, если вы хотите его посмотреть github.com/pudility/superFlow/blob/master/ast/ast.cpp#L116-L119 еще раз благодарим за помощь! - person zoecarver; 17.06.2018
comment
@pudility Я спрашиваю, почему во внешнем интерфейсе вообще задействованы цифры. Обычно доступ к структуре на поверхностном языке будет выглядеть примерно как my_struct.x, тогда вы должны искать x в таблице членов для данного типа структуры, обнаруживая, что его индекс, скажем, равен 1 (который вы можете сохранить как фактическое целое число независимо от того, какие типы чисел поддерживает ваш язык, потому что в этот момент мы не говорим о коде на вашем языке), а затем сгенерируйте GEP с индексами 0,1. - person sepp2k; 17.06.2018
comment
Я не уверен, что полностью следую. Если, например, у кого-то есть двойник с именем i (на моем игрушечном языке), я хочу иметь возможность somestruct[i], я затем конвертирую i в int и использую GEP для получения элемента. Мой код отлично работает с этим somestruct[1]. Проблемы возникают только с переменной. Другая странность заключается в том, что код, который мне понравился выше, очень похож (массивы вместо структур), но с ним работают как буквальные числа, так и переменные. Извините за такой длинный комментарий и еще раз спасибо за помощь. - person zoecarver; 17.06.2018
comment
На самом деле я думаю, что понимаю то, что вы говорите, и думаю, что делаю большую часть этого. Единственная проблема в том, что я сохраняю индекс как llvm::Value, потому что не уверен, можно ли его использовать для других целей. - person zoecarver; 17.06.2018
comment
@pudility Я думаю, что представление вашего языка о структуре сильно отличается от других языков и от LLVM. В C ++, например, вы должны определить такую ​​структуру: struct Point { double x; double y; }, а затем использовать ее как Point p; p.x = ...; p.y = ...;. Здесь не используются квадратные скобки или числа - они предназначены для массивов. - person sepp2k; 17.06.2018
comment
Согласен - это немного странно. Мне сложно понять, как реализовать массивы. Я действительно задал вопрос об этом (ссылка ниже, если вам интересно), и мне порекомендовали использовать какой-то объект. Первоначально я использовал массивы, но оказалось, что у них возникло несколько проблем (я не буду здесь останавливаться на них, потому что это все в вопросе). В любом случае это проясняет то, что вы говорили о столе - спасибо! Также я очень ценю, что вы нашли время объяснить мне все это :) Ссылка: stackoverflow.com/questions/50842415/ - person zoecarver; 17.06.2018
comment
Позвольте нам продолжить это обсуждение в чате. - person sepp2k; 17.06.2018
comment
Ладно, увидимся в чате. - person zoecarver; 17.06.2018