Chrome V8을 이해해 봅시다 — 9장: 내장
52718 단어 v8chromejavascriptcpp
Let’s Understand Chrome V8의 다른 장에 오신 것을 환영합니다.
V8의 대부분의 기능은 builtin에서 구현됩니다. 이 백서에서는 내장 및 일반 내장 유형의 초기화에 대해 설명합니다.
1. 초기화
아래는 모든 빌트인을 관리하는 코드 클래스입니다.
1. class Code : public HeapObject {
2. public:
3. NEVER_READ_ONLY_SPACE
4. // Opaque data type for encapsulating code flags like kind, inline
5. // cache state, and arguments count.
6. using Flags = uint32_t;
7. #define CODE_KIND_LIST(V) \
8. V(OPTIMIZED_FUNCTION) \
9. V(BYTECODE_HANDLER) \
10. V(STUB) \
11. V(BUILTIN) \
12. V(REGEXP) \
13. V(WASM_FUNCTION) \
14. V(WASM_TO_CAPI_FUNCTION) \
15. V(WASM_TO_JS_FUNCTION) \
16. V(JS_TO_WASM_FUNCTION) \
17. V(JS_TO_JS_FUNCTION) \
18. V(WASM_INTERPRETER_ENTRY) \
19. V(C_WASM_ENTRY)
20. enum Kind {
21. #define DEFINE_CODE_KIND_ENUM(name) name,
22. CODE_KIND_LIST(DEFINE_CODE_KIND_ENUM)
23. #undef DEFINE_CODE_KIND_ENUM
24. NUMBER_OF_KINDS
25. };
26. static const char* Kind2String(Kind kind);
27. // Layout description.
28. #define CODE_FIELDS(V) \
29. V(kRelocationInfoOffset, kTaggedSize) \
30. V(kDeoptimizationDataOffset, kTaggedSize) \
31. V(kSourcePositionTableOffset, kTaggedSize) \
32. V(kCodeDataContainerOffset, kTaggedSize) \
33. /* Data or code not directly visited by GC directly starts here. */ \
34. /* The serializer needs to copy bytes starting from here verbatim. */ \
35. /* Objects embedded into code is visited via reloc info. */ \
36. V(kDataStart, 0) \
37. V(kInstructionSizeOffset, kIntSize) \
38. V(kFlagsOffset, kIntSize) \
39. V(kSafepointTableOffsetOffset, kIntSize) \
40. V(kHandlerTableOffsetOffset, kIntSize) \
41. V(kConstantPoolOffsetOffset, \
42. FLAG_enable_embedded_constant_pool ? kIntSize : 0) \
43. V(kCodeCommentsOffsetOffset, kIntSize) \
44. V(kBuiltinIndexOffset, kIntSize) \
45. V(kUnalignedHeaderSize, 0) \
46. /* Add padding to align the instruction start following right after */ \
47. /* the Code object header. */ \
48. V(kOptionalPaddingOffset, CODE_POINTER_PADDING(kOptionalPaddingOffset)) \
49. V(kHeaderSize, 0)
50. DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, CODE_FIELDS)
51. //omit...........................
52. };
7행은 코드 유형을 정의하고 내장 정의는 builtins-definitions.h에 제공됩니다. 코드와 빌트인의 역할은 다르지만 초기화는 void Isolate::Initialize(Isolate* isolate,const v8::Isolate::CreateParams& params)에 의해 균일하게 수행됩니다. 아래는 초기화입니다.
1. void Isolate::Initialize(Isolate* isolate,
2. const v8::Isolate::CreateParams& params) {
3. i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
4. CHECK_NOT_NULL(params.array_buffer_allocator);
5. i_isolate->set_array_buffer_allocator(params.array_buffer_allocator);
6. if (params.snapshot_blob != nullptr) {
7. i_isolate->set_snapshot_blob(params.snapshot_blob);
8. } else {
9. i_isolate->set_snapshot_blob(i::Snapshot::DefaultSnapshotBlob());
10. }
11. auto code_event_handler = params.code_event_handler;
12. //.....................omit....................
13. if (!i::Snapshot::Initialize(i_isolate)) {
14. // If snapshot data was provided and we failed to deserialize it must
15. // have been corrupted.
16. if (i_isolate->snapshot_blob() != nullptr) {
17. FATAL(
18. "Failed to deserialize the V8 snapshot blob. This can mean that the "
19. "snapshot blob file is corrupted or missing.");
20. }
21. base::ElapsedTimer timer;
22. if (i::FLAG_profile_deserialization) timer.Start();
23. i_isolate->InitWithoutSnapshot();
24. if (i::FLAG_profile_deserialization) {
25. double ms = timer.Elapsed().InMillisecondsF();
26. i::PrintF("[Initializing isolate from scratch took %0.3f ms]\n", ms);
27. }
28. }
29. i_isolate->set_only_terminate_in_safe_scope(
30. params.only_terminate_in_safe_scope);
31. }
초기화하는 동안 코드의 22번째 줄이 실행되고 아래 함수를 호출합니다.
1. void SetupIsolateDelegate::SetupBuiltinsInternal(Isolate* isolate) {
2. //..................omit
3. //..................omit
4. int index = 0;
5. Code code;
6. #define BUILD_CPP(Name) \
7. code = BuildAdaptor(isolate, index, FUNCTION_ADDR(Builtin_##Name), #Name); \
8. AddBuiltin(builtins, index++, code);
9. #define BUILD_TFJ(Name, Argc, ...) \
10. code = BuildWithCodeStubAssemblerJS( \
11. isolate, index, &Builtins::Generate_##Name, Argc, #Name); \
12. AddBuiltin(builtins, index++, code);
13. #define BUILD_TFC(Name, InterfaceDescriptor) \
14. /* Return size is from the provided CallInterfaceDescriptor. */ \
15. code = BuildWithCodeStubAssemblerCS( \
16. isolate, index, &Builtins::Generate_##Name, \
17. CallDescriptors::InterfaceDescriptor, #Name); \
18. AddBuiltin(builtins, index++, code);
19. #define BUILD_TFS(Name, ...) \
20. /* Return size for generic TF builtins (stub linkage) is always 1. */ \
21. code = \
22. BuildWithCodeStubAssemblerCS(isolate, index, &Builtins::Generate_##Name, \
23. CallDescriptors::Name, #Name); \
24. AddBuiltin(builtins, index++, code);
25. #define BUILD_TFH(Name, InterfaceDescriptor) \
26. /* Return size for IC builtins/handlers is always 1. */ \
27. code = BuildWithCodeStubAssemblerCS( \
28. isolate, index, &Builtins::Generate_##Name, \
29. CallDescriptors::InterfaceDescriptor, #Name); \
30. AddBuiltin(builtins, index++, code);
31. #define BUILD_BCH(Name, OperandScale, Bytecode) \
32. code = GenerateBytecodeHandler(isolate, index, OperandScale, Bytecode); \
33. AddBuiltin(builtins, index++, code);
34. #define BUILD_ASM(Name, InterfaceDescriptor) \
35. code = BuildWithMacroAssembler(isolate, index, Builtins::Generate_##Name, \
36. #Name); \
37. AddBuiltin(builtins, index++, code);
38. BUILTIN_LIST(BUILD_CPP, BUILD_TFJ, BUILD_TFC, BUILD_TFS, BUILD_TFH, BUILD_BCH,
39. BUILD_ASM);
40//omit...........................
41. }
초기화의 주요 작업은 내장 코드를 생성 및 컴파일하고 i::isolate에 마운트하는 것입니다. BuildWithCodeStubAssemblerCS에 대해 자세히 설명합니다.
첫 번째 매개변수는 내장을 유지하는 데 사용됩니다. 두 번째 매개변수는 내장 배열의 인덱스입니다. 세 번째 매개변수는 내장 함수의 생성된 함수를 가리키는 함수 포인터입니다. 네 번째 매개변수는 호출 설명자입니다. 마지막 매개변수는 이름입니다.
1. // Builder for builtins implemented in TurboFan with CallStub linkage.
2. Code BuildWithCodeStubAssemblerCS(Isolate* isolate, int32_t builtin_index,
3. CodeAssemblerGenerator generator,
4. CallDescriptors::Key interface_descriptor,
5. const char* name) {
6. HandleScope scope(isolate);
7. // Canonicalize handles, so that we can share constant pool entries pointing
8. // to code targets without dereferencing their handles.
9. CanonicalHandleScope canonical(isolate);
10. Zone zone(isolate->allocator(), ZONE_NAME);
11. // The interface descriptor with given key must be initialized at this point
12. // and this construction just queries the details from the descriptors table.
13. CallInterfaceDescriptor descriptor(interface_descriptor);
14. // Ensure descriptor is already initialized.
15. DCHECK_LE(0, descriptor.GetRegisterParameterCount());
16. compiler::CodeAssemblerState state(
17. isolate, &zone, descriptor, Code::BUILTIN, name,
18. PoisoningMitigationLevel::kDontPoison, builtin_index);
19. generator(&state);
20. Handle<Code> code = compiler::CodeAssembler::GenerateCode(
21. &state, BuiltinAssemblerOptions(isolate, builtin_index));
22. return *code;
23. }
위 코드의 19행은 제너레이터 함수를 호출합니다. TF_BUILTIN(RecordWrite, RecordWriteCodeStubAssembler)을 예로 들어 설명하겠습니다.
1. TF_BUILTIN(RecordWrite, RecordWriteCodeStubAssembler) {
2. Label generational_wb(this);
3. Label incremental_wb(this);
4. Label exit(this);
5. Node* remembered_set = Parameter(Descriptor::kRememberedSet);
6. Branch(ShouldEmitRememberSet(remembered_set), &generational_wb,
7. &incremental_wb);
8. BIND(&generational_wb);
9. {
10. Label test_old_to_young_flags(this);
11. Label store_buffer_exit(this), store_buffer_incremental_wb(this);
12. TNode<IntPtrT> slot = UncheckedCast<IntPtrT>(Parameter(Descriptor::kSlot));
13. Branch(IsMarking(), &test_old_to_young_flags, &store_buffer_exit);
14. BIND(&test_old_to_young_flags);
15. {
16. TNode<IntPtrT> value =
17. BitcastTaggedToWord(Load(MachineType::TaggedPointer(), slot));
18. TNode<BoolT> value_is_young =
19. IsPageFlagSet(value, MemoryChunk::kIsInYoungGenerationMask);
20. GotoIfNot(value_is_young, &incremental_wb);
21. TNode<IntPtrT> object =
22. BitcastTaggedToWord(Parameter(Descriptor::kObject));
23. TNode<BoolT> object_is_young =
24. IsPageFlagSet(object, MemoryChunk::kIsInYoungGenerationMask);
25. Branch(object_is_young, &incremental_wb, &store_buffer_incremental_wb);
26. }
27. BIND(&store_buffer_exit);
28. {
29. TNode<ExternalReference> isolate_constant =
30. ExternalConstant(ExternalReference::isolate_address(isolate()));
31. Node* fp_mode = Parameter(Descriptor::kFPMode);
32. InsertToStoreBufferAndGoto(isolate_constant, slot, fp_mode, &exit);
33. }
34. BIND(&store_buffer_incremental_wb);
35. {
36. TNode<ExternalReference> isolate_constant =
37. ExternalConstant(ExternalReference::isolate_address(isolate()));
38. Node* fp_mode = Parameter(Descriptor::kFPMode);
39. InsertToStoreBufferAndGoto(isolate_constant, slot, fp_mode,
40. &incremental_wb);
41. }
42. } //........................omit......................................
43. BIND(&exit);
44. IncrementCounter(isolate()->counters()->write_barriers(), 1);
45. Return(TrueConstant());
46. }
TF_BUILTIN(RecordWrite, RecordWriteCodeStubAssembler)은 RecordWrite의 소스를 생성합니다. TF_BUILTIN은 매크로 템플릿입니다. 확장 후 생성된 코드를 저장하는 멤버 CodeAssemblerState* 상태를 확인할 수 있습니다. 생성된 RecordWrite 소스 코드는 코드 구조에 저장됩니다.
class Code : public HeapObject {
public:
NEVER_READ_ONLY_SPACE
// Opaque data type for encapsulating code flags like kind, inline
// cache state, and arguments count.
using Flags = uint32_t;
#define CODE_KIND_LIST(V) \
V(OPTIMIZED_FUNCTION) \
V(BYTECODE_HANDLER) \
V(STUB) \
V(BUILTIN) \
V(REGEXP) \
V(WASM_FUNCTION) \
V(WASM_TO_CAPI_FUNCTION) \
V(WASM_TO_JS_FUNCTION) \
V(JS_TO_WASM_FUNCTION) \
V(JS_TO_JS_FUNCTION) \
V(WASM_INTERPRETER_ENTRY) \
V(C_WASM_ENTRY)
enum Kind {
#define DEFINE_CODE_KIND_ENUM(name) name,
CODE_KIND_LIST(DEFINE_CODE_KIND_ENUM)
#undef DEFINE_CODE_KIND_ENUM
NUMBER_OF_KINDS
};
//..................omit
빌트인이 코드 클래스로 분류되어 있음을 알 수 있습니다. 그런데 코드는 V8 힙에 의해 관리되는 힙 객체입니다. 앞으로 V8 힙 개체에 대해 설명할 것입니다. 그림 1은 호출 스택을 보여줍니다.
SetupBuiltinsInternal()에서 아래와 같이 AddBuiltin()이 i::isolate에 코드를 추가하는 것을 볼 수 있습니다.
void SetupIsolateDelegate::AddBuiltin(Builtins* builtins, int index,
Code code) {
DCHECK_EQ(index, code.builtin_index());
builtins->set_builtin(index, code);
}
//..............separation......................
void Builtins::set_builtin(int index, Code builtin) {
isolate_->heap()->set_builtin(index, builtin);
}
생성된 builtins는 builtins_[Builtins::builtin_count] 배열에 삽입되고 마지막으로 배열은 i::isolate에 마운트됩니다.
2. 빌트인
기능적 관점에서 빌트인은 점화, 바이트코드 및 ECMA 사양 기능과 같은 많은 커널 기능을 다룹니다. BUILTIN_LIST에 자세한 설명이 있습니다. 빌트인에는 7가지 유형이 있습니다.
#define BUILD_CPP(Name)
#define BUILD_TFJ(Name, Argc, ...)
#define BUILD_TFC(Name, InterfaceDescriptor)
#define BUILD_TFS(Name, ...)
#define BUILD_TFH(Name, InterfaceDescriptor)
#define BUILD_BCH(Name, OperandScale, Bytecode)
#define BUILD_ASM(Name, InterfaceDescriptor)
BuildAdaptor에 대해 이야기해 봅시다.
1. Code BuildAdaptor(Isolate* isolate, int32_t builtin_index,
2. Address builtin_address, const char* name) {
3. HandleScope scope(isolate);
4. // Canonicalize handles, so that we can share constant pool entries pointing
5. // to code targets without dereferencing their handles.
6. CanonicalHandleScope canonical(isolate);
7. constexpr int kBufferSize = 32 * KB;
8. byte buffer[kBufferSize];
9. MacroAssembler masm(isolate, BuiltinAssemblerOptions(isolate, builtin_index),
10. CodeObjectRequired::kYes,
11. ExternalAssemblerBuffer(buffer, kBufferSize));
12. masm.set_builtin_index(builtin_index);
13. DCHECK(!masm.has_frame());
14. Builtins::Generate_Adaptor(&masm, builtin_address);
15. CodeDesc desc;
16. masm.GetCode(isolate, &desc);
17. Handle<Code> code = Factory::CodeBuilder(isolate, desc, Code::BUILTIN)
18. .set_self_reference(masm.CodeObject())
19. .set_builtin_index(builtin_index)
20. .Build();
21. return *code;
22. }
코드의 14번째 줄은 BuildAdaptor 생성을 담당하고 마지막으로 다음 코드를 호출합니다.
void Builtins::Generate_Adaptor(MacroAssembler* masm, Address address) {
__ LoadAddress(kJavaScriptCallExtraArg1Register,
ExternalReference::Create(address));
__ Jump(BUILTIN_CODE(masm->isolate(), AdaptorWithBuiltinExitFrame),
RelocInfo::CODE_TARGET);
}
}
그림 2는 char로 생성된 소스 코드를 보여줍니다.
요약: 각 빌트인의 생성은 다르지만 코드를 분석하고 디버깅하는 방법은 동일합니다.
자, 이것으로 이번 공유를 마치겠습니다. 다음에 뵙겠습니다 여러분 건강하세요!
문제가 있으면 저에게 연락해 주세요. 위챗: qq9123013 이메일: [email protected]
Reference
이 문제에 관하여(Chrome V8을 이해해 봅시다 — 9장: 내장), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/v8blink/lets-understand-chrome-v8-chapter-9-builtin-3opl텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)