步骤2: 计算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,还真的不知道……