// File: luooofan[P]hblzbd_compiler/src/Pass/global_value_numbering.cpp
// Function: GlobalValueNumbering::CommenExpressionEliminate(SSABasicBlock*)
void GlobalValueNumbering::CommenExpressionEliminate(SSABasicBlock* bb) {
#ifdef DEBUG_GVN_PROCESS
  std::cout << "  BB: " << bb->GetLabel() << std::endl;
#endif

  int src_vec_size = exp_val_vec.size();

  // compute exp and perform eliminate
  for (auto it = bb->GetInstList().begin(); it != bb->GetInstList().end();) {
    auto inst = *it;
    Expression exp(inst);

    auto find_if_cmp = [&exp](std::pair<Expression, Value*>& kv) {
      if (exp.op_ == kv.first.op_) {
        // 满足交换律的加和乘操作 假设最多只有一个操作数是常数 把常数操作数放后面(之前执行SimpleOptimizePass即满足假设)
        if (exp.op_ == Expression::ADD || exp.op_ == Expression::MUL) {
          if (nullptr != dynamic_cast<ConstantInt*>(exp.operands_[0])) {
            auto temp = exp.operands_[1];
            exp.operands_[1] = exp.operands_[0];
            exp.operands_[0] = temp;
          }
          if (nullptr != dynamic_cast<ConstantInt*>(kv.first.operands_[0])) {
            auto temp = kv.first.operands_[1];
            kv.first.operands_[1] = kv.first.operands_[0];
            kv.first.operands_[0] = temp;
          }
        }
        // 比较每个操作数是否一致 对于常数来说比较的是立即数值 对于其他value来说比较的是指针
        for (int i = 0; i < exp.operands_.size(); ++i) {
          if (auto constint = dynamic_cast<ConstantInt*>(exp.operands_[i])) {
            if (auto constint2 = dynamic_cast<ConstantInt*>(kv.first.operands_[i])) {
              if (constint->GetImm() == constint2->GetImm()) continue;
            } else {
              return false;
            }
          }
          if (exp.operands_[i] != kv.first.operands_[i]) return false;
        }
        return true;
      } else {
        return false;
      }
    };
    auto replace = [&bb, &it](Value* src, Value* target) {
      ++kCount;
#ifdef DEBUG_GVN_PROCESS
      std::cout << "  find redundant: ";
      src->Print(std::cout);
      std::cout << "             ==>: ";
      target->Print(std::cout);
      std::cout << std::endl;
#endif
      src->ReplaceAllUseWith(target);
      it = bb->GetInstList().erase(it);
      delete src;
    };
    auto find_and_replace = [&exp, &it, &find_if_cmp, &replace, this](Value* src) {
      auto vec_it = std::find_if(exp_val_vec.begin(), exp_val_vec.end(), find_if_cmp);
      if (vec_it == exp_val_vec.end()) {
        exp_val_vec.push_back({exp, src});
        ++it;
      } else {
        replace(src, (*vec_it).second);
      }
    };

    // 二元运算语句
    if (auto src_inst = dynamic_cast<BinaryOperator*>(inst)) {
      switch (src_inst->op_) {
        case BinaryOperator::ADD:
          exp.op_ = Expression::ADD;
          break;
        case BinaryOperator::SUB:
          exp.op_ = Expression::SUB;
          break;
        case BinaryOperator::MUL:
          exp.op_ = Expression::MUL;
          break;
        case BinaryOperator::DIV:
          exp.op_ = Expression::DIV;
          break;
        case BinaryOperator::MOD:
          exp.op_ = Expression::MOD;
          break;
        default:
          MyAssert(0);
          break;
      }
      exp.operands_.push_back(src_inst->GetOperand(0));
      exp.operands_.push_back(src_inst->GetOperand(1));

      find_and_replace(src_inst);
    }
    // 一元运算语句
    else if (auto src_inst = dynamic_cast<UnaryOperator*>(inst)) {
      switch (src_inst->op_) {
        case UnaryOperator::NEG:
          exp.op_ = Expression::NEG;
          break;
        case UnaryOperator::NOT:
          exp.op_ = Expression::NOT;
          break;
        default:
          MyAssert(0);
          break;
      }
      exp.operands_.push_back(src_inst->GetOperand(0));

      find_and_replace(src_inst);
    }
    // Call语句 需要关注call的函数是否有副作用 有副作用则不可删 kill掉原来的
    else if (auto src_inst = dynamic_cast<CallInst*>(inst)) {
      auto func_val = dynamic_cast<FunctionValue*>(src_inst->GetOperand(0));
      MyAssert(nullptr != func_val);
      // 如果调用了有副作用的函数 不用处理该语句
      if (no_side_effect_funcs.find(func_val->GetFunction()) == no_side_effect_funcs.end()) {
        // TODO: 这里可以考虑把该基本块内的load exp都kill掉
        ++it;
        continue;
      }
      // 调用了void函数也可以算作表达式 虽然没有使用 但是可以删除一条多余的call
      // 如果前面做了死代码消除 不应该出现调用无副作用void函数的call语句

      // Expression exp(Expression::CALL);
      exp.op_ = Expression::CALL;
      for (int i = 0; i < src_inst->GetNumOperands(); ++i) {
        exp.operands_.push_back(src_inst->GetOperand(i));
      }

      find_and_replace(src_inst);
    }
    // Load语句 如果找到一条公共的load语句 需要检查从该语句开始到当前语句的所有执行路径上都没有对target的store语句
    else if (auto src_inst = dynamic_cast<LoadInst*>(inst)) {
      exp.op_ = Expression::LOAD;
      for (int i = 0; i < src_inst->GetNumOperands(); ++i) {
        exp.operands_.push_back(src_inst->GetOperand(i));
      }

      auto check_inst_range = [this](std::list<SSAInstruction*>::iterator begin,
                                     std::list<SSAInstruction*>::iterator end, LoadInst* target) {
        Value* memory = target->GetOperand(0);
        for (; begin != end; ++begin) {
          // 是调用了有副作用函数的call语句
          auto call_inst = dynamic_cast<CallInst*>(*begin);
          if (nullptr != call_inst) {
            auto func_val = dynamic_cast<FunctionValue*>(call_inst->GetOperand(0));
            if (no_side_effect_funcs.find(func_val->GetFunction()) == no_side_effect_funcs.end()) {
              return false;
            }
          }

          // 是对同一块内存区域的store语句
          auto store_inst = dynamic_cast<StoreInst*>(*begin);
          if (nullptr != store_inst && store_inst->GetOperand(1) == memory) {
            // 如果load和store语句的偏移都是常量的话可以比较立即数 如果不同 则跳过这条store语句
            // NOTE: 都有偏移 要么都有 要么都没 不会出现一个有一个没有
            if (target->GetNumOperands() == 2 && store_inst->GetNumOperands() == 3) {
              if (auto constint = dynamic_cast<ConstantInt*>(target->GetOperand(1))) {
                if (auto constint2 = dynamic_cast<ConstantInt*>(store_inst->GetOperand(2))) {
                  if (constint->GetImm() != constint2->GetImm()) continue;
                }
              }
            }
            return false;
          }
        }
        return true;
      };

      auto check = [this, &check_inst_range](SSAInstruction* source, LoadInst* target) {
        // 检查从source指令开始到target指定的所有路径上有没有对同一块内存区域的store语句或者对副作用函数的调用语句
        // source可能是load或store target一定是load
        // source->Print(std::cout);
        // target->Print(std::cout);
        auto source_bb = source->GetParent();
        auto target_bb = target->GetParent();
        // 1 if source and target in the same bb
        if (source_bb == target_bb) {
          auto inst_list = source_bb->GetInstList();
          auto st = ++std::find(inst_list.begin(), inst_list.end(), source),
               ed = std::find(st, inst_list.end(), target);
          return check_inst_range(st, ed, target);
        }
        // 2 in different bbs
        // NOTE: 好像会导致更多的溢出 禁用 等决赛可以取消禁用提交一次
        else {
          // return false;
          // 2.1 check source bb
          auto src_inst_list = source_bb->GetInstList();
          if (!check_inst_range(++std::find(src_inst_list.begin(), src_inst_list.end(), source), src_inst_list.end(),
                                target))
            return false;
          // std::cout << 1 << std::endl;
          // 2.2 check target bb
          auto tar_inst_list = target_bb->GetInstList();
          if (!check_inst_range(tar_inst_list.begin(), std::find(tar_inst_list.begin(), tar_inst_list.end(), target),
                                target))
            return false;
          // std::cout << 2 << std::endl;
          // 2.3 check all reached bb in road
          std::unordered_set<SSABasicBlock*> src_reach;
          std::unordered_set<SSABasicBlock*> tar_reach;
          std::queue<SSABasicBlock*> q;
          std::unordered_set<SSABasicBlock*> vis;
          auto dfs = [&q, &vis](std::unordered_set<SSABasicBlock*>& reach, bool succ = true) {
            while (!q.empty()) {
              auto front = q.front();
              reach.insert(front);
              q.pop();
              for (auto next_bb : (succ ? front->GetSuccBB() : front->GetPredBB())) {
                if (vis.count(next_bb)) continue;
                q.push(next_bb);
                vis.insert(next_bb);
              }
            }
            vis.clear();
          };

          // 2.3.1 fill src_reach
          q.push(source_bb);
          vis.insert(source_bb);
          dfs(src_reach, true);

          // 2.3.2 fill tar_reach
          q.push(target_bb);
          vis.insert(target_bb);
          dfs(tar_reach, false);

          // 2.3.3 compute cross set
          std::unordered_set<SSABasicBlock*> reached_bb;
          for (auto reach_bb : src_reach)
            if (tar_reach.count(reach_bb)) reached_bb.insert(reach_bb);

          // 2.3.4 determine source_bb and target_bb whether in road or not(just end point).
          if (!bbs_in_loop.count(source_bb)) reached_bb.erase(source_bb);
          if (!bbs_in_loop.count(target_bb)) reached_bb.erase(target_bb);

          // 2.3.5 check all bbs in road
          for (auto bb : reached_bb) {
            auto inst_list = bb->GetInstList();
            if (!check_inst_range(inst_list.begin(), inst_list.end(), target)) return false;
          }

          // std::cout << 3 << std::endl;
          return true;
        }
      };

      // NOTE: reverse find 保证找到最新的一个
      auto vec_it = std::find_if(exp_val_vec.rbegin(), exp_val_vec.rend(), find_if_cmp);
      if (vec_it == exp_val_vec.rend()) {
        exp_val_vec.push_back({exp, src_inst});
        ++it;
      } else {
        // check决定能否replace if check true, perform replace. otherwise add exp to exp_val_vec
        if (check((*vec_it).first.inst_, src_inst)) {
          replace(src_inst, (*vec_it).second);
        } else {
          exp_val_vec.push_back({exp, src_inst});
          ++it;
        }
      }
    }
    // Store语句 可以看成一条load语句 同时或许可以kill掉该基本块内的对同一块内存操作的load语句
    else if (auto src_inst = dynamic_cast<StoreInst*>(inst)) {
      exp.op_ = Expression::LOAD;
      for (int i = 1; i < src_inst->GetNumOperands(); ++i) {
        exp.operands_.push_back(src_inst->GetOperand(i));
      }

      // 不管有没有 直接加进去即可 之后再找相同的load exp 保证找到最新(靠后)的一个就行
      exp_val_vec.push_back({exp, src_inst->GetOperand(0)});
      ++it;
    }
    // 其他语句
    else {
      ++it;
    }
  }

  // traverse dom tree
  for (auto dom : bb->GetDoms()) {
    CommenExpressionEliminate(dom);
  }

  // erase now bb exps from exp_val_vec
  exp_val_vec.erase(exp_val_vec.begin() + src_vec_size, exp_val_vec.end());
}

// Assembly:
subq $0x438, %rsp            # imm = 0x438
movq %rdi, 0x430(%rsp)
movq %rsi, 0x428(%rsp)
movq 0x430(%rsp), %rdi
movq %rdi, 0x1d0(%rsp)
addq $0xa8, %rdi
callq 0xbd980
movl %eax, 0x424(%rsp)
movq 0x428(%rsp), %rdi
callq 0x62860
movq %rax, %rdi
callq 0x62870
movq %rax, 0x418(%rsp)
movq 0x428(%rsp), %rdi
callq 0x62860
movq %rax, %rdi
callq 0x628a0
movq %rax, 0x410(%rsp)
leaq 0x418(%rsp), %rdi
leaq 0x410(%rsp), %rsi
callq 0x628d0
testb $0x1, %al
jne 0xba4f2
jmp 0xbb255
leaq 0x418(%rsp), %rdi
callq 0x628f0
movq (%rax), %rax
movq %rax, 0x408(%rsp)
movq 0x408(%rsp), %rsi
leaq 0x3e0(%rsp), %rdi
callq 0xbd9a0
movq 0x1d0(%rsp), %rax
leaq 0x3e0(%rsp), %rcx
movq %rcx, 0x3d8(%rsp)
leaq 0x428(%rsp), %rcx
movq %rcx, 0x3c8(%rsp)
leaq 0x418(%rsp), %rcx
movq %rcx, 0x3d0(%rsp)
leaq 0x3e0(%rsp), %rcx
movq %rcx, 0x3a0(%rsp)
leaq 0x418(%rsp), %rcx
movq %rcx, 0x3a8(%rsp)
leaq 0x3d8(%rsp), %rcx
movq %rcx, 0x3b0(%rsp)
leaq 0x3c8(%rsp), %rcx
movq %rcx, 0x3b8(%rsp)
movq %rax, 0x3c0(%rsp)
movq 0x408(%rsp), %rax
movq %rax, 0x1c8(%rsp)
cmpq $0x0, %rax
je 0xba5de
movq 0x1c8(%rsp), %rdi
leaq 0x90e54(%rip), %rsi     # 0x14b418
leaq 0x91085(%rip), %rdx     # 0x14b650
xorl %eax, %eax
movl %eax, %ecx
callq 0x8410
movq %rax, 0x1c0(%rsp)
jmp 0xba5ea
xorl %eax, %eax
movq %rax, 0x1c0(%rsp)
jmp 0xba5ea
movq 0x1c0(%rsp), %rax
movq %rax, 0x398(%rsp)
cmpq $0x0, 0x398(%rsp)
je 0xba7f7
movq 0x398(%rsp), %rax
movl 0x68(%rax), %eax
movq %rax, 0x1b8(%rsp)
subq $0x4, %rax
ja 0xba68a
movq 0x1b8(%rsp), %rax
leaq 0x433f3(%rip), %rcx     # 0xfda24
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movl $0x0, 0x3e0(%rsp)
jmp 0xba738
movl $0x1, 0x3e0(%rsp)
jmp 0xba738
movl $0x2, 0x3e0(%rsp)
jmp 0xba738
movl $0x3, 0x3e0(%rsp)
jmp 0xba738
movl $0x4, 0x3e0(%rsp)
jmp 0xba738
movq 0x91957(%rip), %rdi     # 0x14bfe8
leaq 0x3fa60(%rip), %rsi     # 0xfa0f8
callq 0x8330
movq %rax, 0x1b0(%rsp)
jmp 0xba6a7
movq 0x1b0(%rsp), %rdi
leaq 0x433b9(%rip), %rsi     # 0xfda6f
callq 0x8330
movq %rax, 0x1a8(%rsp)
jmp 0xba6c5
movq 0x1a8(%rsp), %rdi
leaq 0x40d16(%rip), %rsi     # 0xfb3ea
callq 0x8330
movq %rax, 0x1a0(%rsp)
jmp 0xba6e3
movq 0x1a0(%rsp), %rdi
movl $0xa5, %esi
callq 0x8650
movq %rax, 0x198(%rsp)
jmp 0xba6ff
movq 0x198(%rsp), %rdi
movq 0x91872(%rip), %rsi     # 0x14bf80
callq 0x8380
jmp 0xba715
movl $0xff, %edi
callq 0x8460
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x390(%rsp)
movl %eax, 0x38c(%rsp)
jmp 0xbb243
leaq 0x3e8(%rsp), %rax
movq %rax, 0x188(%rsp)
movq 0x398(%rsp), %rdi
xorl %esi, %esi
callq 0x5c850
movq %rax, 0x190(%rsp)
jmp 0xba761
movq 0x188(%rsp), %rdi
movq 0x190(%rsp), %rax
movq %rax, 0x380(%rsp)
leaq 0x380(%rsp), %rsi
callq 0xbd9e0
jmp 0xba788
leaq 0x3e8(%rsp), %rax
movq %rax, 0x178(%rsp)
movq 0x398(%rsp), %rdi
movl $0x1, %esi
callq 0x5c850
movq %rax, 0x180(%rsp)
jmp 0xba7b4
movq 0x178(%rsp), %rdi
movq 0x180(%rsp), %rax
movq %rax, 0x378(%rsp)
leaq 0x378(%rsp), %rsi
callq 0xbd9e0
jmp 0xba7db
movq 0x398(%rsp), %rsi
leaq 0x3a0(%rsp), %rdi
callq 0xbb3b0
jmp 0xba7f2
jmp 0xbb214
movq 0x408(%rsp), %rax
movq %rax, 0x170(%rsp)
cmpq $0x0, %rax
je 0xba836
movq 0x170(%rsp), %rdi
leaq 0x90bfc(%rip), %rsi     # 0x14b418
leaq 0x90e6d(%rip), %rdx     # 0x14b690
xorl %eax, %eax
movl %eax, %ecx
callq 0x8410
movq %rax, 0x168(%rsp)
jmp 0xba842
xorl %eax, %eax
movq %rax, 0x168(%rsp)
jmp 0xba842
movq 0x168(%rsp), %rax
movq %rax, 0x370(%rsp)
cmpq $0x0, 0x370(%rsp)
je 0xba9a8
movq 0x370(%rsp), %rax
movl 0x68(%rax), %eax
movl %eax, 0x164(%rsp)
testl %eax, %eax
je 0xba887
jmp 0xba879
movl 0x164(%rsp), %eax
subl $0x1, %eax
je 0xba897
jmp 0xba8a7
movl $0x5, 0x3e0(%rsp)
jmp 0xba93c
movl $0x6, 0x3e0(%rsp)
jmp 0xba93c
movq 0x9173a(%rip), %rdi     # 0x14bfe8
leaq 0x3f843(%rip), %rsi     # 0xfa0f8
callq 0x8330
movq %rax, 0x158(%rsp)
jmp 0xba8c4
movq 0x158(%rsp), %rdi
leaq 0x4319c(%rip), %rsi     # 0xfda6f
callq 0x8330
movq %rax, 0x150(%rsp)
jmp 0xba8e2
movq 0x150(%rsp), %rdi
leaq 0x40af9(%rip), %rsi     # 0xfb3ea
callq 0x8330
movq %rax, 0x148(%rsp)
jmp 0xba900
movq 0x148(%rsp), %rdi
movl $0xb7, %esi
callq 0x8650
movq %rax, 0x140(%rsp)
jmp 0xba91c
movq 0x140(%rsp), %rdi
movq 0x91655(%rip), %rsi     # 0x14bf80
callq 0x8380
jmp 0xba932
movl $0xff, %edi
callq 0x8460
leaq 0x3e8(%rsp), %rax
movq %rax, 0x130(%rsp)
movq 0x370(%rsp), %rdi
xorl %esi, %esi
callq 0x5c850
movq %rax, 0x138(%rsp)
jmp 0xba965
movq 0x130(%rsp), %rdi
movq 0x138(%rsp), %rax
movq %rax, 0x368(%rsp)
leaq 0x368(%rsp), %rsi
callq 0xbd9e0
jmp 0xba98c
movq 0x370(%rsp), %rsi
leaq 0x3a0(%rsp), %rdi
callq 0xbb3b0
jmp 0xba9a3
jmp 0xbb212
movq 0x408(%rsp), %rax
movq %rax, 0x128(%rsp)
cmpq $0x0, %rax
je 0xba9e7
movq 0x128(%rsp), %rdi
leaq 0x90a4b(%rip), %rsi     # 0x14b418
leaq 0x90d3c(%rip), %rdx     # 0x14b710
xorl %eax, %eax
movl %eax, %ecx
callq 0x8410
movq %rax, 0x120(%rsp)
jmp 0xba9f3
xorl %eax, %eax
movq %rax, 0x120(%rsp)
jmp 0xba9f3
movq 0x120(%rsp), %rax
movq %rax, 0x360(%rsp)
cmpq $0x0, 0x360(%rsp)
je 0xbacb3
movq 0x360(%rsp), %rdi
xorl %esi, %esi
callq 0x5c850
movq %rax, 0x118(%rsp)
jmp 0xbaa2b
movq 0x118(%rsp), %rax
cmpq $0x0, %rax
je 0xbaa62
movq 0x118(%rsp), %rdi
leaq 0x90a88(%rip), %rsi     # 0x14b4d0
leaq 0x90ad9(%rip), %rdx     # 0x14b528
xorl %eax, %eax
movl %eax, %ecx
callq 0x8410
movq %rax, 0x110(%rsp)
jmp 0xbaa6e
xorl %eax, %eax
movq %rax, 0x110(%rsp)
jmp 0xbaa6e
movq 0x110(%rsp), %rax
movq %rax, 0x358(%rsp)
xorl %eax, %eax
cmpq 0x358(%rsp), %rax
jne 0xbab23
movq 0x91553(%rip), %rdi     # 0x14bfe8
leaq 0x3f65c(%rip), %rsi     # 0xfa0f8
callq 0x8330
movq %rax, 0x108(%rsp)
jmp 0xbaaab
movq 0x108(%rsp), %rdi
leaq 0x42fb5(%rip), %rsi     # 0xfda6f
callq 0x8330
movq %rax, 0x100(%rsp)
jmp 0xbaac9
movq 0x100(%rsp), %rdi
leaq 0x40912(%rip), %rsi     # 0xfb3ea
callq 0x8330
movq %rax, 0xf8(%rsp)
jmp 0xbaae7
movq 0xf8(%rsp), %rdi
movl $0xc1, %esi
callq 0x8650
movq %rax, 0xf0(%rsp)
jmp 0xbab03
movq 0xf0(%rsp), %rdi
movq 0x9146e(%rip), %rsi     # 0x14bf80
callq 0x8380
jmp 0xbab19
movl $0xff, %edi
callq 0x8460
movq 0x1d0(%rsp), %rax
addq $0x38, %rax
movq %rax, 0xe0(%rsp)
movq 0x358(%rsp), %rdi
callq 0x5e2a0
movq %rax, 0xe8(%rsp)
jmp 0xbab4e
movq 0xe0(%rsp), %rdi
movq 0xe8(%rsp), %rax
movq %rax, 0x348(%rsp)
leaq 0x348(%rsp), %rsi
callq 0xa0f50
movq %rax, 0xd8(%rsp)
jmp 0xbab7d
movq 0x1d0(%rsp), %rdi
movq 0xd8(%rsp), %rax
movq %rax, 0x350(%rsp)
addq $0x38, %rdi
callq 0x62a60
movq %rax, 0x340(%rsp)
leaq 0x350(%rsp), %rdi
leaq 0x340(%rsp), %rsi
callq 0xa10f0
testb $0x1, %al
jne 0xbabc1
jmp 0xbabde
leaq 0x418(%rsp), %rdi
callq 0x62960
movl $0x3, 0x33c(%rsp)
jmp 0xbb21f
movl $0x7, 0x3e0(%rsp)
movl $0x0, 0x338(%rsp)
movl 0x338(%rsp), %eax
movl %eax, 0xd0(%rsp)
movq 0x360(%rsp), %rdi
callq 0x5e580
movl %eax, 0xd4(%rsp)
jmp 0xbac18
movl 0xd0(%rsp), %eax
movl 0xd4(%rsp), %ecx
cmpl %ecx, %eax
jae 0xbac97
leaq 0x3e8(%rsp), %rax
movq %rax, 0xc0(%rsp)
movq 0x360(%rsp), %rdi
movl 0x338(%rsp), %esi
callq 0x5c850
movq %rax, 0xc8(%rsp)
jmp 0xbac58
movq 0xc0(%rsp), %rdi
movq 0xc8(%rsp), %rax
movq %rax, 0x330(%rsp)
leaq 0x330(%rsp), %rsi
callq 0xbd9e0
jmp 0xbac7f
jmp 0xbac81
movl 0x338(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x338(%rsp)
jmp 0xbabf4
movq 0x360(%rsp), %rsi
leaq 0x3a0(%rsp), %rdi
callq 0xbb3b0
jmp 0xbacae
jmp 0xbb210
movq 0x408(%rsp), %rax
movq %rax, 0xb8(%rsp)
cmpq $0x0, %rax
je 0xbacf2
movq 0xb8(%rsp), %rdi
leaq 0x90740(%rip), %rsi     # 0x14b418
leaq 0x90af1(%rip), %rdx     # 0x14b7d0
xorl %eax, %eax
movl %eax, %ecx
callq 0x8410
movq %rax, 0xb0(%rsp)
jmp 0xbacfe
xorl %eax, %eax
movq %rax, 0xb0(%rsp)
jmp 0xbacfe
movq 0xb0(%rsp), %rax
movq %rax, 0x328(%rsp)
cmpq $0x0, 0x328(%rsp)
je 0xbb05b
movl $0x8, 0x3e0(%rsp)
movl $0x0, 0x324(%rsp)
movl 0x324(%rsp), %eax
movl %eax, 0xa8(%rsp)
movq 0x328(%rsp), %rdi
callq 0x5e580
movl %eax, 0xac(%rsp)
jmp 0xbad57
movl 0xa8(%rsp), %eax
movl 0xac(%rsp), %ecx
cmpl %ecx, %eax
jae 0xbadd6
leaq 0x3e8(%rsp), %rax
movq %rax, 0x98(%rsp)
movq 0x328(%rsp), %rdi
movl 0x324(%rsp), %esi
callq 0x5c850
movq %rax, 0xa0(%rsp)
jmp 0xbad97
movq 0x98(%rsp), %rdi
movq 0xa0(%rsp), %rax
movq %rax, 0x318(%rsp)
leaq 0x318(%rsp), %rsi
callq 0xbd9e0
jmp 0xbadbe
jmp 0xbadc0
movl 0x324(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x324(%rsp)
jmp 0xbad33
movq 0x1d0(%rsp), %rsi
movq %rsi, 0x310(%rsp)
movq %rsi, 0x300(%rsp)
leaq 0x310(%rsp), %rax
movq %rax, 0x308(%rsp)
addq $0xa8, %rsi
movq %rsi, 0x80(%rsp)
leaq 0x2f0(%rsp), %rdi
movq %rdi, 0x88(%rsp)
callq 0xbda10
movq 0x80(%rsp), %rsi
leaq 0x2e8(%rsp), %rdi
movq %rdi, 0x90(%rsp)
callq 0xbda60
movq 0x88(%rsp), %rsi
movq 0x90(%rsp), %rdx
movq 0x3d8(%rsp), %rax
movq %rax, 0x2e0(%rsp)
movq 0x2e0(%rsp), %rcx
leaq 0x2f8(%rsp), %rdi
callq 0xbb530
jmp 0xbae76
movq 0x1d0(%rsp), %rsi
addq $0xa8, %rsi
leaq 0x2d8(%rsp), %rdi
movq %rdi, 0x70(%rsp)
callq 0xbda60
movq 0x70(%rsp), %rsi
leaq 0x2f8(%rsp), %rdi
callq 0xbdab0
movb %al, 0x7f(%rsp)
jmp 0xbaeaf
movb 0x7f(%rsp), %al
testb $0x1, %al
jne 0xbaebc
jmp 0xbaf47
movq 0x1d0(%rsp), %rax
addq $0xa8, %rax
movq %rax, 0x68(%rsp)
leaq 0x2a8(%rsp), %rdi
leaq 0x3e0(%rsp), %rsi
leaq 0x328(%rsp), %rdx
callq 0xbdb30
jmp 0xbaeee
movq 0x68(%rsp), %rdi
leaq 0x2a8(%rsp), %rsi
callq 0xbdb00
jmp 0xbaf02
leaq 0x2a8(%rsp), %rdi
callq 0xbdb70
leaq 0x418(%rsp), %rdi
callq 0x62960
jmp 0xbb056
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x390(%rsp)
movl %eax, 0x38c(%rsp)
leaq 0x2a8(%rsp), %rdi
callq 0xbdb70
jmp 0xbb243
leaq 0x2f8(%rsp), %rdi
callq 0xbdb80
movq %rax, 0x60(%rsp)
jmp 0xbaf5b
movq 0x60(%rsp), %rax
movq 0x20(%rax), %rsi
movq 0x328(%rsp), %rdx
leaq 0x300(%rsp), %rdi
callq 0xbb5b0
movb %al, 0x5f(%rsp)
jmp 0xbaf7f
movb 0x5f(%rsp), %al
testb $0x1, %al
jne 0xbaf89
jmp 0xbafcc
movq 0x328(%rsp), %rax
movq %rax, 0x48(%rsp)
leaq 0x2f8(%rsp), %rdi
callq 0xbdb80
movq %rax, 0x50(%rsp)
jmp 0xbafaa
movq 0x48(%rsp), %rsi
movq 0x50(%rsp), %rax
movq 0x28(%rax), %rdx
leaq 0x3c8(%rsp), %rdi
callq 0xbbf60
jmp 0xbafc7
jmp 0xbb054
movq 0x1d0(%rsp), %rax
addq $0xa8, %rax
movq %rax, 0x40(%rsp)
leaq 0x278(%rsp), %rdi
leaq 0x3e0(%rsp), %rsi
leaq 0x328(%rsp), %rdx
callq 0xbdb30
jmp 0xbaffe
movq 0x40(%rsp), %rdi
leaq 0x278(%rsp), %rsi
callq 0xbdb00
jmp 0xbb012
leaq 0x278(%rsp), %rdi
callq 0xbdb70
leaq 0x418(%rsp), %rdi
callq 0x62960
jmp 0xbb054
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x390(%rsp)
movl %eax, 0x38c(%rsp)
leaq 0x278(%rsp), %rdi
callq 0xbdb70
jmp 0xbb243
jmp 0xbb056
jmp 0xbb20e
movq 0x408(%rsp), %rax
movq %rax, 0x38(%rsp)
cmpq $0x0, %rax
je 0xbb091
movq 0x38(%rsp), %rdi
leaq 0x9039e(%rip), %rsi     # 0x14b418
leaq 0x9078f(%rip), %rdx     # 0x14b810
xorl %eax, %eax
movl %eax, %ecx
callq 0x8410
movq %rax, 0x30(%rsp)
jmp 0xbb09a
xorl %eax, %eax
movq %rax, 0x30(%rsp)
jmp 0xbb09a
movq 0x30(%rsp), %rax
movq %rax, 0x270(%rsp)
cmpq $0x0, 0x270(%rsp)
je 0xbb1ff
movl $0x8, 0x3e0(%rsp)
movl $0x1, 0x26c(%rsp)
movl 0x26c(%rsp), %eax
movl %eax, 0x28(%rsp)
movq 0x270(%rsp), %rdi
callq 0x5e580
movl %eax, 0x2c(%rsp)
jmp 0xbb0ea
movl 0x28(%rsp), %eax
movl 0x2c(%rsp), %ecx
cmpl %ecx, %eax
jae 0xbb157
leaq 0x3e8(%rsp), %rax
movq %rax, 0x18(%rsp)
movq 0x270(%rsp), %rdi
movl 0x26c(%rsp), %esi
callq 0x5c850
movq %rax, 0x20(%rsp)
jmp 0xbb11e
movq 0x18(%rsp), %rdi
movq 0x20(%rsp), %rax
movq %rax, 0x260(%rsp)
leaq 0x260(%rsp), %rsi
callq 0xbd9e0
jmp 0xbb13f
jmp 0xbb141
movl 0x26c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x26c(%rsp)
jmp 0xbb0cc
movq 0x1d0(%rsp), %rax
addq $0xa8, %rax
movq %rax, 0x8(%rsp)
movq 0x270(%rsp), %rdi
xorl %esi, %esi
callq 0x5c850
movq %rax, 0x10(%rsp)
jmp 0xbb180
movq 0x10(%rsp), %rax
movq %rax, 0x228(%rsp)
leaq 0x230(%rsp), %rdi
leaq 0x3e0(%rsp), %rsi
leaq 0x228(%rsp), %rdx
callq 0xbdbb0
jmp 0xbb1ac
movq 0x8(%rsp), %rdi
leaq 0x230(%rsp), %rsi
callq 0xbdb00
jmp 0xbb1c0
leaq 0x230(%rsp), %rdi
callq 0xbdb70
leaq 0x418(%rsp), %rdi
callq 0x62960
jmp 0xbb20c
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x390(%rsp)
movl %eax, 0x38c(%rsp)
leaq 0x230(%rsp), %rdi
callq 0xbdb70
jmp 0xbb243
leaq 0x418(%rsp), %rdi
callq 0x62960
jmp 0xbb20e
jmp 0xbb210
jmp 0xbb212
jmp 0xbb214
movl $0x0, 0x33c(%rsp)
leaq 0x3e0(%rsp), %rdi
callq 0xbdbf0
movl 0x33c(%rsp), %eax
testl %eax, %eax
je 0xbb23e
jmp 0xbb239
jmp 0xba4b7
jmp 0xba4b7
leaq 0x3e0(%rsp), %rdi
callq 0xbdbf0
jmp 0xbb3a0
movq 0x428(%rsp), %rdi
callq 0xbdc10
movq %rax, 0x220(%rsp)
movq 0x220(%rsp), %rdi
callq 0x90970
movq %rax, 0x218(%rsp)
movq 0x220(%rsp), %rdi
callq 0x907f0
movq %rax, 0x210(%rsp)
leaq 0x218(%rsp), %rdi
leaq 0x210(%rsp), %rsi
callq 0xbdc20
testb $0x1, %al
jne 0xbb2af
jmp 0xbb2eb
leaq 0x218(%rsp), %rdi
callq 0xbdc60
movq 0x1d0(%rsp), %rdi
movq (%rax), %rax
movq %rax, 0x208(%rsp)
movq 0x208(%rsp), %rsi
callq 0xba460
leaq 0x218(%rsp), %rdi
callq 0xbdc70
jmp 0xbb294
movq 0x1d0(%rsp), %rdi
movq %rdi, %rax
addq $0xa8, %rax
movq %rax, (%rsp)
addq $0xa8, %rdi
callq 0xbdd30
movq %rax, 0x1f0(%rsp)
movslq 0x424(%rsp), %rsi
leaq 0x1f0(%rsp), %rdi
callq 0xbdd60
movq %rax, 0x1f8(%rsp)
leaq 0x200(%rsp), %rdi
leaq 0x1f8(%rsp), %rsi
callq 0xbdda0
movq 0x1d0(%rsp), %rdi
addq $0xa8, %rdi
callq 0xbdde0
movq %rax, 0x1e0(%rsp)
leaq 0x1e8(%rsp), %rdi
leaq 0x1e0(%rsp), %rsi
callq 0xbdda0
movq (%rsp), %rdi
movq 0x200(%rsp), %rsi
movq 0x1e8(%rsp), %rdx
callq 0xbdc90
movq %rax, 0x1d8(%rsp)
addq $0x438, %rsp            # imm = 0x438
retq
movq 0x390(%rsp), %rdi
callq 0x8670
nopl (%rax)