SyphonArch / swpp202301-compiler-team1

MIT License
1 stars 0 forks source link

[Sprint 2] [A-VI] Optimize GetElementPtr 도움요청 #26

Closed pzmaus closed 1 year ago

pzmaus commented 1 year ago

현재 backend에 있는 gep_eliminate.cpp를 먼저 실행시키는데 있어 어려움을 겪고 있는데 혹시라도 도움을 주실 수 있으신 분이 계신다면 정말 감사드리겠습니다.

backend에 있는 pass는 혼자 작동되는 것보다도 analysis.c , result.h와 같이 엮여있는 코드들이 많아서 이해하는데 오래걸렸는데, 간단하게 요약하면

  1. 모듈단위로 실행
  2. 함수/ BB/ instruction을 모두 돌면서 getelementptr인지 확인
  3. 맞다면 먼저 getelementptr의 pointer operand(즉 기준점, array[32]에서 array)을 불러오고 타입을 저장
  4. pointer operand를 int64로 형변환 (포인터 자체는 덧셈/뺄셈이 불가능)
  5. index operand(기준점에서 얼마나 움직이는지, array[32]에서 32)를 순회하면서 index값 pointer type 크기를 곱해서 저장. 이후 이를 기준점에 더해준다. 예시로 설명하면 int형 array[10]에서 array[8]위치를 불러오려면 array + 8 sizeof(int)하는 느낌입니다

이렇게 실행되며, 유일하게 const auto size = unwrapOrThrowWithGEP(analysis::tryCalculateSize(curr), *GEPI); 라는 부분만 외부 함수를 필요하는데 결국 curr라는 pointer operand의 type에 알맞는 크기를 가져오는 코드입니다. (i32 이면 4byte, i64 이면 8byte, pointer type이어도 8byte)

일단 이렇게 이해를 완료한 뒤 좀더 간단하게 고쳐서 일단 실행이 되도록 만들고 싶어 도전하고 있는데, 현재 어디서 문제가 나는지 알겠지만 왜 문제가 발생하는지 모르겠는 상황입니다.

namespace sc::opt::gep_elim {
PreservedAnalyses GEPEliminatePass::run(llvm::Module &M, llvm::ModuleAnalysisManager &MAM) {
  llvm::IntegerType *Int64Ty = llvm::Type::getInt64Ty(M.getContext());
  std::set<llvm::GetElementPtrInst *> trashBin;

  for (llvm::Function &F : M) {
    trashBin.clear();
    for (llvm::BasicBlock &BB : F)
      for (llvm::Instruction &I : BB)
        if (llvm::GetElementPtrInst *GEPI =
                llvm::dyn_cast<llvm::GetElementPtrInst>(&I)) {

          llvm::Value *ptrOp = GEPI->getPointerOperand();
          llvm::Type *curr = ptrOp->getType();
          curr = curr->getPointerElementType();

          llvm::Instruction *pti =
              llvm::CastInst::CreateBitOrPointerCast(ptrOp, Int64Ty, "", GEPI);

          std::vector<llvm::Instruction *> v;
          v.push_back(pti);

          int ck = 0;

          for (auto opIt = GEPI->idx_begin(); opIt != GEPI->idx_end(); ++opIt) {
            llvm::Value *op = *opIt;

            uint64_t size;
            if (llvm::isa<llvm::PointerType>(curr)) {
              size = 8UL;
            } else if (llvm::isa<llvm::IntegerType>(curr)) {
              switch (curr->getIntegerBitWidth()) {
              case 1:
              case 8:
                size = 1UL;
                break;
              case 16:
                size = 2UL;
                break;
              case 32:
                size = 4UL;
                break;
              case 64:
                size = 8UL;
                break;
              default:
                ck = 1;
                break;
              } 
              if(ck == 1) break;
            } else {
                ck = 1;
                break;
            }

//            const auto sizeconst = size;

/*************************problem!!!***********************/
            llvm::Instruction *mul = llvm::BinaryOperator::CreateMul(
                op, llvm::ConstantInt::get(Int64Ty, 8UL, true), "", GEPI);
/**********************************************************/

            llvm::Instruction *add =
                llvm::BinaryOperator::CreateAdd(v.back(), mul, "", GEPI);
            v.push_back(add);

          }

          if(ck == 1) continue;

          llvm::Instruction *itp = llvm::CastInst::CreateBitOrPointerCast(
              v.back(), I.getType(), "", GEPI);
          GEPI->replaceAllUsesWith(itp);
          trashBin.insert(GEPI);

        }
    for (llvm::GetElementPtrInst *I : trashBin)
      I->eraseFromParent();
  }

  return llvm::PreservedAnalyses::all();
}

이게 코드의 전문이나 위에 mul 부분에서 문제가 발생하는 것 같습니다. (저기만 주석처리해도 프로그램이 돌아는 갑니다) llvm::ConstantInt::get를 사용할 때 인자로 상수만 넘겨야되는 것 같아 8UL이라고 상수를 넣었음에도 SIGABRT를 띄우고 종료되어 버립니다. 이외에는 backend에 있는 코드랑 완전히 동일한데.... 혹시라도 이유가 생각나시거나 하실때 알려주시면 감사하겠습니다.

계속 노력해보고 있겠습니다.

foxisobese commented 1 year ago

GetElementPtr의 operand op 가 Integer 타입이 아닐 때 버그가 생길 수 있어보입니다.

pzmaus commented 1 year ago

GetElementPtr의 operand op 가 Integer 타입이 아닐 때 버그가 생길 수 있어보입니다.

답변 감사드립니다. 찾아본 결과 GEP의 index는 integer type말고는 불가능하다고 하는 것 같아 예외처리까지는 하지 않아도 괜찮을 것 같습니다. 제가 테스트해본 예제가 아래와 같은데 , 이것마저도 터지고 있어 이유를 찾지 못하고 있습니다.

define i32 @foo(i32* %arr, i32 %i) {
entry:
  %gep = getelementptr i32, i32* %arr, i32 %i
  %val = load i32, i32* %gep
  ret i32 %val
}
heatz123 commented 1 year ago

프로그램의 오류가 컴파일 타임에 발생하나요, 인터프리터를 돌릴 때 발생하나요? 또 오류 메시지가 SIGABRT 이외에 따로 없는건가요?

pzmaus commented 1 year ago

프로그램의 오류가 컴파일 타임에 발생하나요, 인터프리터를 돌릴 때 발생하나요? 또 오류 메시지가 SIGABRT 이외에 따로 없는건가요?

unit_test를 돌리기 위해 ctest --rerun-failed --output-on-failure를 실행시 나는 에러 전문은 다음과 같습니다.

Test project /home/pzmaus/projects/swpp/project/swpp202301-compiler-team1/build
    Start 1: unit_tests
1/1 Test #1: unit_tests .......................***Failed    0.11 sec
== gvn-pass ==
== bias-to-false-branch ==
== add-to-sum ==
== arithmetic-pass ==
== use-async-load ==
== gep_elim ==
Test file:      ./unit_tests/gep_elim_1.ll
        /home/pzmaus/llvm-swpp/bin/opt -load-pass-plugin=./build/libGEPEliminatePass.so -passes=gep_elim ./unit_tests/gep_elim_1.ll -S -o ./tmp/out.gep_elim_1.ll
Traceback (most recent call last):
  File "/home/pzmaus/projects/swpp/project/swpp202301-compiler-team1/unit_tests.py", line 37, in <module>
    subprocess.run(opt_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  File "/usr/lib/python3.10/subprocess.py", line 524, in run
    raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['/home/pzmaus/llvm-swpp/bin/opt', '-load-pass-plugin=./build/libGEPEliminatePass.so', '-passes=gep_elim', './unit_tests/gep_elim_1.ll', '-S', '-o', './tmp/out.gep_elim_1.ll']' died with <Signals.SIGABRT: 6>.

0% tests passed, 1 tests failed out of 1

Total Test time (real) =   0.11 sec

The following tests FAILED:
          1 - unit_tests (Failed)
Errors while running CTest

처음에는 entries.csv에 설정을 잘못 기입한줄 알았는데 createMul만 주석처리하면 최적화만 안될 뿐 정상실행되어

define i32 @foo(i32* %arr, i32 %i) {
entry:
  %0 = ptrtoint i32* %arr to i64
  %1 = inttoptr i64 %0 to i32*
  %val = load i32, i32* %1, align 4
  ret i32 %val
}

이렇게 출력은 되고있습니다.

heatz123 commented 1 year ago

/home/pzmaus/llvm-swpp/bin/opt -load-pass-plugin=./build/libGEPEliminatePass.so -passes=gep_elim ./unit_tests/gep_elim_1.ll -S -o ./tmp/out.gep_elim_1.ll 을 한번 실행해 보시고 나온 출력도 확인해보시면 좋을 것 같습니다.

pzmaus commented 1 year ago

/home/pzmaus/llvm-swpp/bin/opt -load-pass-plugin=./build/libGEPEliminatePass.so -passes=gep_elim ./unit_tests/gep_elim_1.ll -S -o ./tmp/out.gep_elim_1.ll 을 한번 실행해 보시고 나온 출력도 확인해보시면 좋을 것 같습니다.

덕분에 에러를 찾았습니다 감사합니다! 정말 멍청하게도 input test의 문제였습니다....