Assembler如何把跳转汇编变成机器码的(三)

步骤2: 计算target

计算属于label1的target
计算属于label2的target

1.Bind(Label1)计算出属于label1跳转的target

Bind函数完成了两个工作,第一个计算出Bind之前,该label的所有跳转的target,第二个是记录此时Bind(label)的地址,在接下来跳转到该label的时候可以直接计算出target。来看源码:

void Riscv64Assembler::Bind(Riscv64Label* label) {
  CHECK(!label->IsBound());
  uint32_t bound_pc = buffer_.Size(); //获得buffer的大小

  // Walk the list of branches referring to and preceding this label.
  // Store the previously unknown target addresses in them.
  while (label->IsLinked()) { 
    uint32_t branch_id = label->Position();  
    Branch* branch = GetBranch(branch_id);  
    branch->Resolve(bound_pc);              // 入参:获得buffer的大小,其实就是bind的地址
    uint32_t branch_location = branch->GetLocation();  
    // Extract the location of the previous branch in the list (walking the list backwards;
    // the previous branch ID was stored in the space reserved for this branch).
    uint32_t prev = buffer_.Load<uint32_t>(branch_location); // 这个时候我已经获得了beq3的id beq1在容器中的位置

    // On to the previous branch in the list...
    label->position_ = prev; 
  }
  // Now make the label object contain its own location (relative to the end of the preceding
  // branch, if any; it will be used by the branches referring to and following this label).
  label->prev_branch_id_plus_one_ = branches_.size(); // 获得容器中目前有几个  
  if (label->prev_branch_id_plus_one_) {
    uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
    const Branch* branch = GetBranch(branch_id); 
    bound_pc -= branch->GetEndLocation();       //   计算bind距离beq2的偏移量  buffer的大小- beq2的结束地址   表示bind到容器中最后一个元素的距离

  }
  label->BindTo(bound_pc);  // pos = -pos - 8 = -8  1:的相对地址已知,不知绝对地址
}

while (label->IsLinked())如果pos>0的话,就一直循环。这一步的作用就是向前遍历属于该label的跳转,顺序是由后向前,直到属于该label的首位元素,其pos为0。

branch_id = label->Position(); return position_ - sizeof(void*) label1的pos此时为2+8,-8之后为2,branch此时通过pos,获取容器中branches[2]即属于label1的对象,设置beq2的target为此时buffer的大小,因为此时buffer的大小就是Bind(label1)的地址,此时beq3的target已经设置完成,接下来要做的就是设置同属于label1的beq1的target。

branch_location = branch->GetLocation()获得了beq3的location,prev = buffer_.Load<uint32_t>(branch_location) return *reinterpret_cast<T*>(contents_ + position);buffer起始位置+beq3在buffer的位置,从中对指针进行解引用得到beq3在之前占位时存放的四字节内容,得到pos = 0 + 8,再次进入循环,对pos进行处理-8之后就获得了beq1在容器中的位置,再次设置branches[0]的target为当前buffer的地址。

1.1 计算出Bind(label1)的相对位置

label->prev_branch_id_plus_one_ = branches_.size()取容器中最后一个元素的id
bound_pc -= branch->GetEndLocation(); GetEndLocation(){ return GetLocation() + GetSize();}用buffer的大小减去容器中最后一个元素的endlocation,其实就是获得了beq4结束到bind(label1)的偏移量。
label->BindTo(bound_pc); position_ = -position - sizeof(void*);设置label1->pos为-偏移量-8,此时pos被设置为负数。当跳转指令向再次跳转到该label时,如果pos为负数就说明为向前跳转,可以根据此时记录的Bind(label1)的地址直接计算出target。

此时可以发现label->pos的神奇用法:当pos为负数的时候,说明为向前跳转,pos表示label1的相对地址。当pos为非负数时,表示为向后跳转,pos表示跳转生成的对象在容器中的位置。

2.Bind(label2)计算出属于label2跳转的target

流程与Bind(label1)相似,当调用Bind函数时,label2->pos为3+8,处理之后3表示beq4在容器中的位置。设定beq4的target后循环处理beq2的target。处理结束之后,设置label->pos为label2的相对beq4的偏移量。设置偏移量而不是直接将buffer.size给pos的目的是因为无论是label1还是label2,本身是不占据空间的,如果直接将buffer.size给label2,其实代表的是add的内容,容易给人以歧义,但给定了偏移量进行处理就可以直观一点。除了这个原因,为什么不直接给pos赋值buffer.size,还真的不知道……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值