一,instructions指令(Library/namespace->Field/Class/Function->Code->instructions)
二,PC指令计数器是用于存放下一条指令所在单元的地址的地方。
为了保证程序(在操作系统中理解为进程)能够连续地执行下去,CPU必须具有某些手段来确定下一条指令的地址。而程序计数器正是起到这种作用,所以通常又称为指令计数器。在程序开始执行前,必须将它的起始地址,即程序的一条指令所在的内存单元地址送入PC,因此程序计数器(PC)的内容即是从内存提取的第一条指令的地址。当执行指令时,CPU将自动修改PC的内容,即每执行一条指令PC增加一个量,这个量等于指令所含的字节数,以便使其保持的总是将要执行的下一条指令的地址。由于大多数指令都是按顺序来执行的,所以修改的过程通常只是简单的对PC加1。
PC的维数一般和存储器地址寄存器MAR的维数一样。当程序转移时,转移指令执行的最终结果就是要改变PC的值,此PC值就是转去的地址,以此实现转移。有些机器中也称PC为指令指针IP(Instruction Pointer)。
enum CallKind {
kPcRelativeCall = 1,
kPcRelativeTTSCall = 2,
kPcRelativeTailCall = 3,
kCallViaCode = 4,
};
LDR (PC-relative)指令详解----加载PC相对标号所代表的地址处的内存内容到寄存器中
ADDR(s) | main节在实际的可执行文件中,位于内存的实际地址 |
ADDR(r.symbol) | func函数在实际可执行文件中,位于内存的实际地址 |
r.offset | 上文提到的offset,计算出需要修改的引用的操作数相对main节的偏移 |
r.addend | 上文提到的addend,计算实际callq语句操作数时需要用到的计算偏移 |
三,object pool对象池,PayloadStart装载器
ObjectPool包含由(生成的代码和非优化信息引用)的常量、立即数指令和地址.每个入口(entry)都有一个与其关联的类型,该类型存储在所有入口(entry)之后的inlined中。
PayloadStart装载器,之后不需要知道汇编指令,通过code类就能对应.
四,state_bits_,unchecked_offset_
state_bits_是具有三个字段的位字段: 优化位、活动位和指针偏移量的计数。 活动:如果为true,则将在GC期间访问底层对象指针
unchecked_offset_缓存the unchecked entry point指令,以防我们需要将active_instructions_重置为instructions_。
五, 栈帧
1, entry_point_,unchecked_entry_point_,monomorphic_entry_point_,monomorphic_unchecked_entry_point_(入口点)
2,stub code:存根, 占位代码,占坑代码,桩代码,粘合代码,残存代码, 指满足形式要求但没有实现具体功能的占坑/代理代码。stub code 给出的实现是临时性的/待编辑的。它使得程序在结构上能够符合标准,又能够使程序员可以暂时不编辑这段代码。举个例子,我写了一个类文件,里面只有类、方法、变量的声明部分,没有具体实现的部分,那么这个类文件就可以称之为 stub 文件
stubCall
void Code::SetStubCallTargetCodeAt(uword pc, const Code& code) const {
…
const intptr_t i = BinarySearchInSCallTable(pc);
const Array& array = Array::Handle(raw_ptr()->static_calls_target_table_);
StaticCallsTable entries(array);
…
return entries[i].Set<kSCallTableCodeOrTypeTarget>(code);//找到第i块入口
}
六,VisitPointers
使用pointer_offset遍历指令中的所有底层对象。
七,New()
New是一个私有方法,因为RawInstruction和RawCode对象只能使用Code::FinalizeCode方法创建。New方法创建RawInstruction和RawCode对象,设置指针偏移,并以GC安全的方式将两者链接起来
八,内联缓存(参考博客V8中inline cache 实现)
Inline缓存是一项加速技术,此设计是为了利用程序中局部(local)类别的方法。若要程序化的属性存取,产生一个指令串来搜寻隐藏类列表。此代码称为premonomorphic stub。此stub是为了在函数存取属性。Premonomorphic stub拥有两个信息:搜寻用的隐藏类,以及取自隐藏的位移。最后会产生新代码以缓存此信息。
在伪代码(pseudocode)中的premonomorphic stub 从隐藏类中取得属性位移
premonomorphic stub呼叫存取函数中的属性时会呼叫premonomorphic stub在搜寻表格之前,带有属性的对象之隐藏类会与缓存隐藏类比较。如果相符就不需要再搜寻,且可以使用缓存的位移来存取属性。如果隐藏类不相符,就透过隐藏类哈希表以一般方式判断位移。
新产生的代码被称为monomorphic stub。「Inline」这个字的意思是查询隐藏类所需的位移,是以立即可用的形式嵌入在所产生的代码中。当第一次叫出monomorphic stub时,它会将功能从pre-monomorphic stub位址中所叫出的第一个位址重写成monomorphic stub位址 。自此,使用高速的monomorphic stub,单靠类比较和数组存取就可以处理属性存取
monomorphic stub呼叫 当呼叫monomorphic stub时,它会将功能从premonomorphic stub位址中叫出的第一个位址,重写成monomorphic stub位址。
如果只有一个具有属性的对象,monomorphic stub的效率就会很高。然而,如果类型愈多,缓存失误就会更频繁,进而降低monomorphic stub的效率。
当缓存失误时, 产生另一个称为megamorphic stub的代码来解决 。与个别类对应的monomorphic stub都写在哈希表中,其在执行时搜寻和叫出stub。如果没有类型对应的monomorphic stub时,就会从类型哈希表中搜寻位移
Megamorphic stub处理与类型对应的monomorphic stub事先储存在哈希表中,并在执行时被搜寻和叫出。如果无法找到对应的monomorphic stub,就会在类型哈希表中搜寻位移。
当monomorphic stub发生缓存失误时,monomorphic stub会将功能从monomorphic stub位址叫出的第一个位址以megamorphic stub位址重写。在代码搜寻方面,megamorphic stub的性能比monomorphic stub低,但是megamorphic代码却比使用缓存更新、代码生成及其他辅助处理的premonomorphic stubs快许多。
涵盖多种类的内嵌缓存称为多型态内嵌缓存(polymorphic inline cache)。 Inline缓存系统被用来呼叫方法以及存取属性。
九,查找
RawCode* Code::FindCode(uword pc, int64_t timestamp) {
…
SlowFindRawCodeVisitor visitor(pc);
RawObject* needle = isolate->heap()->FindOldObject(&visitor);
Code& code = Code::Handle(needle);
if (!code.IsNull() && (code.compile_timestamp() == timestamp) &&
(code.PayloadStart() == pc)) {//PayloadStart()入口地址
// Found code in isolate.
return code.raw();
}
code = Code::LookupCodeInVmIsolate(pc);
if (!code.IsNull() && (code.compile_timestamp() == timestamp) &&
(code.PayloadStart() == pc)) {
// Found code in VM isolate.
return code.raw();
}
return Code::null();
}
十,通过生成各种元数据(例如。堆栈映射、pc描述符等),并将它们附加到新生成的[Code]对象。如果Code::PoolAttachment::kAttachPool指定了为[pool_attachment],则新的[ObjectPool]也将附加到代码对象。否则,调用方负责通过“Object::set_Object_pool()”执行此操作。
static RawCode* FinalizeCode(FlowGraphCompiler* compiler,
compiler::Assembler* assembler,
PoolAttachment pool_attachment,
bool optimized,
CodeStatistics* stats);
十一,GetInlinedFunctionsAtInstruction
以给定的pc偏移量提供调用堆栈,堆栈顶部位于最后一个元素,root函数(this)作为第一个元素,以及相应的源位置。请注意,除了堆栈顶部之外,每个函数的标记位置都是调用下一个函数的位置。如果我们缺少元数据来生成堆栈,那么堆栈将是空的,这对于存根代码来说会发生。pc偏移量被解释为指令地址(根据反汇编程序或分析器示例的顶部框架的需要)
void Code::GetInlinedFunctionsAtInstruction(
intptr_t pc_offset,
GrowableArray<const Function*>* functions,
GrowableArray<TokenPosition>* token_positions) const {
//CodeSourceMap encodes a mapping from code PC ranges to source token positions and the stack of inlined functions
const CodeSourceMap& map = CodeSourceMap::Handle(code_source_map());
if (map.IsNull()) {
ASSERT(!IsFunctionCode() ||
(Isolate::Current()->object_store()->megamorphic_call_miss_code() ==
this->raw()));
return; // VM stub, allocation stub, or megamorphic call miss function.
}
const Array& id_map = Array::Handle(inlined_id_to_function());//id_map就是Function对象数组
const Function& root = Function::Handle(function());
CodeSourceMapReader reader(map, id_map, root);
reader.GetInlinedFunctionsAt(pc_offset, functions, token_positions);
}
版权声明:内容来源于互联网和用户投稿 如有侵权请联系删除