isolate.cc中定义的
struct StaticInitializer {
StaticInitializer() {
Isolate::EnsureDefaultIsolate();
}
} static_initializer;
StaticInitializer() {
Isolate::EnsureDefaultIsolate();
}
} static_initializer;
这个变量是一个Global变量,在C++初始化,早于main函数被调用之前就被调用了。
Isolate::EnsureDefaultIsolate()主要初始化Isolate中与Thread相关的一些静态变量
// defined at isolate.h
static Thread::LocalStorageKey per_isolate_thread_data_key_;
static Thread::LocalStorageKey isolate_key_;
static Thread::LocalStorageKey thread_id_key_;
static Isolate* default_isolate_;
static ThreadDataTable* thread_data_table_;
static Thread::LocalStorageKey isolate_key_;
static Thread::LocalStorageKey thread_id_key_;
static Isolate* default_isolate_;
static ThreadDataTable* thread_data_table_;
static inline bool EnsureInitializedForIsolate(i::Isolate* isolate,const char* location)
确保Isolate进行了初始化,如果没有,那么会调用InitializeHelper进行初始化,该函数会调用
bool V8::Initialize(Deserializer* des) {
InitializeOncePerProcess();//1
// The current thread may not yet had entered an isolate to run.
// Note the Isolate::Current() may be non-null because for various
// initialization purposes an initializing thread may be assigned an isolate
// but not actually enter it.
if (i::Isolate::CurrentPerIsolateThreadData() == NULL) {
i::Isolate::EnterDefaultIsolate();//2
}
InitializeOncePerProcess();//1
// The current thread may not yet had entered an isolate to run.
// Note the Isolate::Current() may be non-null because for various
// initialization purposes an initializing thread may be assigned an isolate
// but not actually enter it.
if (i::Isolate::CurrentPerIsolateThreadData() == NULL) {
i::Isolate::EnterDefaultIsolate();//2
}
。。。
return isolate->Init(des);//3
}
该函数主要有三点可说:
1.InitializeOncePerProcess
3.isolate->Init(des)
InitializeOncePerProcess最终会调用
void V8::InitializeOncePerProcessImpl() {
FlagList::EnforceFlagImplications();
if (FLAG_stress_compaction) {
FLAG_force_marking_deque_overflows = true;
FLAG_gc_global = true;
FLAG_max_new_space_size = (1 << (kPageSizeBits - 10)) * 2;
}
if (FLAG_trace_hydrogen) FLAG_parallel_recompilation = false;
OS::SetUp();
CPU::SetUp();
use_crankshaft_ = FLAG_crankshaft
&& !Serializer::enabled()
&& CPU::SupportsCrankshaft();
OS::PostSetUp();
RuntimeProfiler::GlobalSetUp();
ElementsAccessor::InitializeOncePerProcess();
LOperand::SetUpCaches();
SetUpJSCallerSavedCodeData();
SamplerRegistry::SetUp();
ExternalReference::SetUp();
}
FlagList::EnforceFlagImplications();
if (FLAG_stress_compaction) {
FLAG_force_marking_deque_overflows = true;
FLAG_gc_global = true;
FLAG_max_new_space_size = (1 << (kPageSizeBits - 10)) * 2;
}
if (FLAG_trace_hydrogen) FLAG_parallel_recompilation = false;
OS::SetUp();
CPU::SetUp();
use_crankshaft_ = FLAG_crankshaft
&& !Serializer::enabled()
&& CPU::SupportsCrankshaft();
OS::PostSetUp();
RuntimeProfiler::GlobalSetUp();
ElementsAccessor::InitializeOncePerProcess();
LOperand::SetUpCaches();
SetUpJSCallerSavedCodeData();
SamplerRegistry::SetUp();
ExternalReference::SetUp();
}
其中
CPU::SetUp函数会探测当前CPU的一些feature
// The Probe method needs executable memory, so it uses Heap::CreateCode.
// Allocation failure is silent and leads to safe default.
void CpuFeatures::Probe() {
ASSERT(!initialized_);
ASSERT(supported_ == 0);
#ifdef DEBUG
initialized_ = true;
#endif
if (Serializer::enabled()) {
supported_ |= OS::CpuFeaturesImpliedByPlatform();
return; // No features if we might serialize.
}
const int kBufferSize = 4 * KB;
VirtualMemory* memory = new VirtualMemory(kBufferSize);
if (!memory->IsReserved()) {
delete memory;
return;
}
ASSERT(memory->size() >= static_cast<size_t>(kBufferSize));
if (!memory->Commit(memory->address(), kBufferSize, true/*executable*/)) {
delete memory;
return;
}
Assembler assm(NULL, memory->address(), kBufferSize);
Label cpuid, done;
#define __ assm.
// Save old esp, since we are going to modify the stack.
__ push(ebp);
__ pushfd();
__ push(ecx);
__ push(ebx);
__ mov(ebp, esp);
// If we can modify bit 21 of the EFLAGS register, then CPUID is supported.
__ pushfd(); //把EFLAGS寄存器入栈
__ pop(eax);
__ mov(edx, eax);
__ xor_(eax, 0x200000); // Flip bit 21.
__ push(eax);
__ popfd();
__ pushfd();
__ pop(eax);
// Allocation failure is silent and leads to safe default.
void CpuFeatures::Probe() {
ASSERT(!initialized_);
ASSERT(supported_ == 0);
#ifdef DEBUG
initialized_ = true;
#endif
if (Serializer::enabled()) {
supported_ |= OS::CpuFeaturesImpliedByPlatform();
return; // No features if we might serialize.
}
const int kBufferSize = 4 * KB;
VirtualMemory* memory = new VirtualMemory(kBufferSize);
if (!memory->IsReserved()) {
delete memory;
return;
}
ASSERT(memory->size() >= static_cast<size_t>(kBufferSize));
if (!memory->Commit(memory->address(), kBufferSize, true/*executable*/)) {
delete memory;
return;
}
Assembler assm(NULL, memory->address(), kBufferSize);
Label cpuid, done;
#define __ assm.
// Save old esp, since we are going to modify the stack.
__ push(ebp);
__ pushfd();
__ push(ecx);
__ push(ebx);
__ mov(ebp, esp);
// If we can modify bit 21 of the EFLAGS register, then CPUID is supported.
__ pushfd(); //把EFLAGS寄存器入栈
__ pop(eax);
__ mov(edx, eax);
__ xor_(eax, 0x200000); // Flip bit 21.
__ push(eax);
__ popfd();
__ pushfd();
__ pop(eax);
//此时eax为修改后的EFLAGS,edx为修改前的EFLAGS
__ xor_(eax, edx); // Different if CPUID is supported.
__ j(not_zero, &cpuid);
// CPUID not supported. Clear the supported features in edx:eax.
__ xor_(eax, eax);
__ xor_(edx, edx);
__ jmp(&done);
// Invoke CPUID with 1 in eax to get feature information in
// ecx:edx. Temporarily enable CPUID support because we know it's
// safe here.
__ bind(&cpuid);
__ mov(eax, 1);
supported_ = (1 << CPUID);
{ CpuFeatureScope fscope(&assm, CPUID);
__ cpuid();
}
supported_ = 0;
// Move the result from ecx:edx to edx:eax and make sure to mark the
// CPUID feature as supported.
__ mov(eax, edx);
__ or_(eax, 1 << CPUID);
__ mov(edx, ecx);
// Done.
__ bind(&done);
__ mov(esp, ebp);
__ pop(ebx);
__ pop(ecx);
__ popfd();
__ pop(ebp);
__ ret(0);
#undef __
typedef uint64_t (*F0)();
F0 probe = FUNCTION_CAST<F0>(reinterpret_cast<Address>(memory->address()));
uint64_t probed_features = probe(); //5.把返回地址强转为函数指针,执行之。
uint64_t platform_features = OS::CpuFeaturesImpliedByPlatform();
supported_ = probed_features | platform_features;
found_by_runtime_probing_only_ = probed_features & ~platform_features;
delete memory;
}
__ xor_(eax, edx); // Different if CPUID is supported.
__ j(not_zero, &cpuid);
// CPUID not supported. Clear the supported features in edx:eax.
__ xor_(eax, eax);
__ xor_(edx, edx);
__ jmp(&done);
// Invoke CPUID with 1 in eax to get feature information in
// ecx:edx. Temporarily enable CPUID support because we know it's
// safe here.
__ bind(&cpuid);
__ mov(eax, 1);
supported_ = (1 << CPUID);
{ CpuFeatureScope fscope(&assm, CPUID);
__ cpuid();
}
supported_ = 0;
// Move the result from ecx:edx to edx:eax and make sure to mark the
// CPUID feature as supported.
__ mov(eax, edx);
__ or_(eax, 1 << CPUID);
__ mov(edx, ecx);
// Done.
__ bind(&done);
__ mov(esp, ebp);
__ pop(ebx);
__ pop(ecx);
__ popfd();
__ pop(ebp);
__ ret(0);
#undef __
typedef uint64_t (*F0)();
F0 probe = FUNCTION_CAST<F0>(reinterpret_cast<Address>(memory->address()));
uint64_t probed_features = probe(); //5.把返回地址强转为函数指针,执行之。
uint64_t platform_features = OS::CpuFeaturesImpliedByPlatform();
supported_ = probed_features | platform_features;
found_by_runtime_probing_only_ = probed_features & ~platform_features;
delete memory;
}
涉及VirtualMemeory的内容,可以参看--------------------,这里只说明v8是如何探测CPU的,Assembler是一个汇编器,它可以用来编译当前CPU的汇编指令,该函数流程如下:
1.分配一块4k内存,权限为可执行作为Assembler的执行内存
2.修改EFLAGS的21bit,如果可以修改,那么该CPU支持CPUID指令
3.如果支持CPUID指令,根据指令手册说明,输入参数EAX为1的时候,ECX:EDX输出CPU feature,其中ECX作为高32bit,EDX作为低32bit。
4.把ECX:EDX赋值给EDX:EAX,按照Intel的ABI约定,这两个寄存器构成64bit的函数返回值
5.把返回地址强转为函数指针,执行之。
6.释放执行内存
再来看V8::InitializeOncePerProcessImpl函数中的OS::PostSetUp函数
void OS::PostSetUp() {
// Math functions depend on CPU features therefore they are initialized after
// CPU.
MathSetup();
#if defined(V8_TARGET_ARCH_IA32)
memcopy_function = CreateMemCopyFunction();
#endif
}
// Math functions depend on CPU features therefore they are initialized after
// CPU.
MathSetup();
#if defined(V8_TARGET_ARCH_IA32)
memcopy_function = CreateMemCopyFunction();
#endif
}
MathSetup函数和CreateMemCopyFunction函数做了实际上是类似的事情,即使用当前CPU的指令为V8生成可调用的函数。其中MathSetup函数主要生成数学函数,sin,cos,tag等,主要用到了FPU的指令,而CreateMemCopyFunction则是生成了memcpy的替代函数。
OS::MemCopyFunction CreateMemCopyFunction(),该函数定义于codegen-ia32.cc,它的流程如下:
1.分配一块4k的可执行内存,交给MacroAssembler使用
2.使用SSE2指令进行复制
movdqu指令用来复制不对齐的双四字(即16字节)数据。
movdqa指令用来复制对齐的双四字数据
prefetch指令用来预取指定内存地址的数据到各级cache中,加快执行速度
这里贴出主循环部分
__ mov(edx, count);
Register loop_count = ecx;
Register count = edx;
__ shr(loop_count, 5); //除以32,因为每次都会复制32字节
{
// Main copy loop.
Label loop;
__ bind(&loop);
__ prefetch(Operand(src, 0x20), 1); //预取src后32字节的数据,因为每次都是复制32字节
__ movdqa(xmm0, Operand(src, 0x00)); //复制src地址开始的16字节到xmm0寄存器
__ movdqa(xmm1, Operand(src, 0x10)); //复制src+16地址开始的16字节到xmm0寄存器
__ add(src, Immediate(0x20));
__ movdqa(Operand(dst, 0x00), xmm0); //复制mm0寄存器内容到dst地址
__ movdqa(Operand(dst, 0x10), xmm1); //复制mm1寄存器内容到dst+16地址
__ add(dst, Immediate(0x20));
__ dec(loop_count);
__ j(not_zero, &loop);
}
Register loop_count = ecx;
Register count = edx;
__ shr(loop_count, 5); //除以32,因为每次都会复制32字节
{
// Main copy loop.
Label loop;
__ bind(&loop);
__ prefetch(Operand(src, 0x20), 1); //预取src后32字节的数据,因为每次都是复制32字节
__ movdqa(xmm0, Operand(src, 0x00)); //复制src地址开始的16字节到xmm0寄存器
__ movdqa(xmm1, Operand(src, 0x10)); //复制src+16地址开始的16字节到xmm0寄存器
__ add(src, Immediate(0x20));
__ movdqa(Operand(dst, 0x00), xmm0); //复制mm0寄存器内容到dst地址
__ movdqa(Operand(dst, 0x10), xmm1); //复制mm1寄存器内容到dst+16地址
__ add(dst, Immediate(0x20));
__ dec(loop_count);
__ j(not_zero, &loop);
}
该函数其他部分的代码主要用于不对齐部分的处理。
下面来看V8::Initialize函数的
i::Isolate::EnterDefaultIsolate()函数,首先需要弄清几个类之间的关系:
----------------------------------------------------------------------
1| | 1
------------ 1 1 ----------------------------- 1 1 -----------
| Thread | ------------| PerIsolateThreadData | --------------| Isolate |
------------ ----------------------------- -----------
\ | /
\ | /
-------------------------------------------------------
| Isolate::Thread_data_Table |
-------------------------------------------------------
class PerIsolateThreadData定义于isolate.h
// A thread has a PerIsolateThreadData instance for each isolate that it has
// entered. That instance is allocated when the isolate is initially entered
// and reused on subsequent entries.
// entered. That instance is allocated when the isolate is initially entered
// and reused on subsequent entries.
我们看到Thread和PerIsolateThreadData,Isolate三者之间是一对一的关系,
Thread可以通过其存储的Tls key分别得到当前与之关联的PerIsolateThreadData和Isolate
PerIsolateThreadData类的职责就是关联当前Thread和一个Isolate,每个Isolate都有一个PerIsolateThreadData实例。
Isolate有一个静态变量Isolate::Thread_data_Table,它通过Thread_id和isolate可以查询得到PerIsolateThreadData
void Isolate::Enter() {
//Isolate.cc
Isolate* current_isolate = NULL;
PerIsolateThreadData* current_data = CurrentPerIsolateThreadData();
if (current_data != NULL) {
current_isolate = current_data->isolate_;
ASSERT(current_isolate != NULL);
if (current_isolate == this) {
ASSERT(Current() == this);
ASSERT(entry_stack_ != NULL);
ASSERT(entry_stack_->previous_thread_data == NULL ||
entry_stack_->previous_thread_data->thread_id().Equals(
ThreadId::Current()));
// Same thread re-enters the isolate, no need to re-init anything.
entry_stack_->entry_count++;
return;
}
}
// Threads can have default isolate set into TLS as Current but not yet have
// PerIsolateThreadData for it, as it requires more advanced phase of the
// initialization. For example, a thread might be the one that system used for
// static initializers - in this case the default isolate is set in TLS but
// the thread did not yet Enter the isolate. If PerisolateThreadData is not
// there, use the isolate set in TLS.
if (current_isolate == NULL) {
current_isolate = Isolate::UncheckedCurrent();
}
PerIsolateThreadData* data = FindOrAllocatePerThreadDataForThisThread();
ASSERT(data != NULL);
ASSERT(data->isolate_ == this);
EntryStackItem* item = new EntryStackItem(current_data,
current_isolate,
entry_stack_);
entry_stack_ = item;
SetIsolateThreadLocals(this, data);
// In case it's the first time some thread enters the isolate.
set_thread_id(data->thread_id());
}
Isolate* current_isolate = NULL;
PerIsolateThreadData* current_data = CurrentPerIsolateThreadData();
if (current_data != NULL) {
current_isolate = current_data->isolate_;
ASSERT(current_isolate != NULL);
if (current_isolate == this) {
ASSERT(Current() == this);
ASSERT(entry_stack_ != NULL);
ASSERT(entry_stack_->previous_thread_data == NULL ||
entry_stack_->previous_thread_data->thread_id().Equals(
ThreadId::Current()));
// Same thread re-enters the isolate, no need to re-init anything.
entry_stack_->entry_count++;
return;
}
}
// Threads can have default isolate set into TLS as Current but not yet have
// PerIsolateThreadData for it, as it requires more advanced phase of the
// initialization. For example, a thread might be the one that system used for
// static initializers - in this case the default isolate is set in TLS but
// the thread did not yet Enter the isolate. If PerisolateThreadData is not
// there, use the isolate set in TLS.
if (current_isolate == NULL) {
current_isolate = Isolate::UncheckedCurrent();
}
PerIsolateThreadData* data = FindOrAllocatePerThreadDataForThisThread();
ASSERT(data != NULL);
ASSERT(data->isolate_ == this);
EntryStackItem* item = new EntryStackItem(current_data,
current_isolate,
entry_stack_);
entry_stack_ = item;
SetIsolateThreadLocals(this, data);
// In case it's the first time some thread enters the isolate.
set_thread_id(data->thread_id());
}
本函数是Thread在Enter Isolate的时候调用的,其流程如下:
1.通过Thread的tls key得到与之关联的PerIsolateThreadData
2.如果PerIsolateThreadData中存储的isolate与当前isolate相等,说明再次进入了同一个isolate,把当前isolate存储的EntryStackItem加一,返回
如果不相等,说明进入了一个不同的isolate
3.如果从PerIsolateThreadData获取的isolate为空,则使用当前Thread的default isolate。使用这个isolate和当前thread id查询Isolate::Thread_data_Table,看是否存在PerIsolateThreadData。如果不存在的话,就需要创建一个PerIsolateThreadData实例。
4.设置当前Thread,PerIsolateThreadData,和isolate之间的关联
5.EntryStackItem是一个Entry Isolate的Stack,它存储在Isolate中,它在当前的Isolate中形成了一个Stack,栈顶的节点指向当前运行isolate的thread。
// These items form a stack synchronously with threads Enter'ing and Exit'ing
// the Isolate. The top of the stack points to a thread which is currently
// running the Isolate. When the stack is empty, the Isolate is considered
// not entered by any thread and can be Disposed.
// If the same thread enters the Isolate more then once, the entry_count_
// is incremented rather then a new item pushed to the stack.
class EntryStackItem定义于isolate.h
// the Isolate. The top of the stack points to a thread which is currently
// running the Isolate. When the stack is empty, the Isolate is considered
// not entered by any thread and can be Disposed.
// If the same thread enters the Isolate more then once, the entry_count_
// is incremented rather then a new item pushed to the stack.
class EntryStackItem定义于isolate.h
下面来分析isolate->Init(des)
1.stack_guard_.InitThread(lock);
StackGuard类定义于execution.h,它有一个内部类ThreadLocal,它负责记录stack limit,以下是ThreadLocal类的一些注释:
stack limit被分成了Javascript和C++ stack limit,这二者只有一种情况是不同,即运行于模拟器上的时候,此时这两个stack是分开的。每个stack limit都有两个值,有real_前缀的是VM真实的stack limit。没有real_前缀的与带real前缀的stack limit只在一种情况下是不等的,即发生了中断,例如调试断点,或者抢断,这种情况下,它故意低于阀值使得stack check失败。在生成的代码和运行期系统检查中都使用的是不带real_前缀的stack limit。
结合以下的代码会理解的更加清晰点:
bool StackGuard::ThreadLocal::Initialize(Isolate* isolate) {
bool should_set_stack_limits = false;
if (real_climit_ == kIllegalLimit) {
// Takes the address of the limit variable in order to find out where
// the top of stack is right now.
const uintptr_t kLimitSize = FLAG_stack_size * KB;
uintptr_t limit = reinterpret_cast<uintptr_t>(&limit) - kLimitSize; //使用临时变量limit的地址作为栈基地址,计算stack limit,在模拟器上使用的stack size是492kB
ASSERT(reinterpret_cast<uintptr_t>(&limit) > kLimitSize);
real_jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
real_climit_ = limit;
climit_ = limit;
should_set_stack_limits = true;
}
nesting_ = 0;
postpone_interrupts_nesting_ = 0;
interrupt_flags_ = 0;
return should_set_stack_li
bool should_set_stack_limits = false;
if (real_climit_ == kIllegalLimit) {
// Takes the address of the limit variable in order to find out where
// the top of stack is right now.
const uintptr_t kLimitSize = FLAG_stack_size * KB;
uintptr_t limit = reinterpret_cast<uintptr_t>(&limit) - kLimitSize; //使用临时变量limit的地址作为栈基地址,计算stack limit,在模拟器上使用的stack size是492kB
ASSERT(reinterpret_cast<uintptr_t>(&limit) > kLimitSize);
real_jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
real_climit_ = limit;
climit_ = limit;
should_set_stack_limits = true;
}
nesting_ = 0;
postpone_interrupts_nesting_ = 0;
interrupt_flags_ = 0;
return should_set_stack_li
mits;
}
}
2.heap_.SetUp()
请参看
Heap的初始化
4.bootstrapper->Initialize
1)SourceCodeCache初始化
SourceCodeCache定义于bootstrapper.h中,它使用一个FixedArray存储(AsciiString*,JSFunction*),即js文件名称与预编译的函数之间的映射。这里是它的初始化。主要就是从Heap中固定索引empty_fixed_array中获取FixedArray
2)GCExtention::Register
GCExtention是Extention的派生类,Extention定义了name,source和native function,本函数把GCExtention注册给RegisteredExtention链表,它们之间的关系如下所示:
First -------------- next ------------------------>| --+-------->| |-------------- ---------------| |------|------| Extention\/----------------------------| name |----------------------------| source |----------------------------| GetNativeFunction |----------------------------/_\|----------------------------| GCExtention |----------------------------
在Genesis::InstallExtention函数中将编译安装这个链表中所有的Extention,其中在Parse过程中Parser::Declare函数将会调用extention->GetNativeFunction这个虚函数,该函数返回一个FunctionTemplate
ExternalizeStringExtention类与StatisticsExtention类与GCExtention相同,都是继承自Extention类,它们也分别通过extention注册了一些函数。
5.builtin.SetUp
参看
builtins的初始化
6.stub_cache->Initialize