1 Introduce
JavaScript It's a weakly typed language , So its variables 、 Expression, etc. when participating in operation , Even if the type is incorrect , You can also get the correct type by implicit conversion , For users , It's like all types can perform all operations . This paper analyzes V8 Addition source code of , Lead you to understand JavaScript Details of the addition operation , have a look V8 How to do it .
2 ADD_HANDLER
V8 Start with bytecode JavaScript Source code , So we start with the byte code processing program of addition , Source code is as follows :
IGNITION_HANDLER(Add, InterpreterBinaryOpAssembler) {
BinaryOpWithFeedback(&BinaryOpAssembler::Generate_AddWithFeedback);
}
JavaScript All addition operations in the source code should start from the above ADD_HANDLER Start , It also internally calls Builtin::kAdd And so on . And in the TurboFan in ,ADD_HANDLER It may be optimized to NewConsString or StringConcat And so on , Let's follow the code to see V8 How to organize these functions .
3 Generate_AddWithFeedback
1. TNode<Object> BinaryOpAssembler::Generate_AddWithFeedback() {
2. Label if_lhsisnotsmi(this,
3. rhs_known_smi ? Label::kDeferred : Label::kNonDeferred);
4. Branch(TaggedIsNotSmi(lhs), &if_lhsisnotsmi, &if_lhsissmi);
5. BIND(&if_lhsissmi);
6. {
7. TNode<Smi> lhs_smi = CAST(lhs);
8. if (!rhs_known_smi) {
9. Label if_rhsissmi(this), if_rhsisnotsmi(this);
10. Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
11. BIND(&if_rhsisnotsmi);
12. {
13. TNode<HeapObject> rhs_heap_object = CAST(rhs);
14. GotoIfNot(IsHeapNumber(rhs_heap_object), &check_rhsisoddball);
15. var_fadd_lhs = SmiToFloat64(lhs_smi);
16. var_fadd_rhs = LoadHeapNumberValue(rhs_heap_object);
17. Goto(&do_fadd); }
18. BIND(&if_rhsissmi); }
19. {
20. TNode<Smi> rhs_smi = CAST(rhs);
21. Label if_overflow(this,
22. rhs_known_smi ? Label::kDeferred : Label::kNonDeferred);
23. TNode<Smi> smi_result = TrySmiAdd(lhs_smi, rhs_smi, &if_overflow);
24. {
25. var_type_feedback = SmiConstant(BinaryOperationFeedback::kSignedSmall);
26. UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(),
27. slot_id, update_feedback_mode);
28. var_result = smi_result;
29. Goto(&end); }
30. BIND(&if_overflow);
31. {
32. var_fadd_lhs = SmiToFloat64(lhs_smi);
33. var_fadd_rhs = SmiToFloat64(rhs_smi);
34. Goto(&do_fadd);
35. } } }
36. BIND(&if_lhsisnotsmi);
37. {
38. TNode<HeapObject> lhs_heap_object = CAST(lhs);
39. GotoIfNot(IsHeapNumber(lhs_heap_object), &if_lhsisnotnumber);
40. if (!rhs_known_smi) {
41. Label if_rhsissmi(this), if_rhsisnotsmi(this);
42. Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
43. BIND(&if_rhsisnotsmi);
44. {
45. TNode<HeapObject> rhs_heap_object = CAST(rhs);
46. GotoIfNot(IsHeapNumber(rhs_heap_object), &check_rhsisoddball);
47. var_fadd_lhs = LoadHeapNumberValue(lhs_heap_object);
48. var_fadd_rhs = LoadHeapNumberValue(rhs_heap_object);
49. Goto(&do_fadd);
50. }
51. BIND(&if_rhsissmi);
52. }
53. {
54. var_fadd_lhs = LoadHeapNumberValue(lhs_heap_object);
55. var_fadd_rhs = SmiToFloat64(CAST(rhs));
56. Goto(&do_fadd);
57. }
58. }
59. BIND(&do_fadd);
60. {
61. var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumber);
62. UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id,
63. update_feedback_mode);
64. TNode<Float64T> value =
65. Float64Add(var_fadd_lhs.value(), var_fadd_rhs.value());
66. TNode<HeapNumber> result = AllocateHeapNumberWithValue(value);
67. var_result = result;
68. Goto(&end);
69. }
70. BIND(&if_lhsisnotnumber);
71. {
72. TNode<Uint16T> lhs_instance_type = LoadInstanceType(CAST(lhs));
73. TNode<BoolT> lhs_is_oddball =
74. InstanceTypeEqual(lhs_instance_type, ODDBALL_TYPE);
75. Branch(lhs_is_oddball, &if_lhsisoddball, &if_lhsisnotoddball);
76. BIND(&if_lhsisoddball);
77. {
78. GotoIf(TaggedIsSmi(rhs), &call_with_oddball_feedback);
79. Branch(IsHeapNumber(CAST(rhs)), &call_with_oddball_feedback,
80. &check_rhsisoddball);
81. }
82. BIND(&if_lhsisnotoddball);
83. {
84. GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback);
85. TNode<HeapObject> rhs_heap_object = CAST(rhs);
86. GotoIf(IsStringInstanceType(lhs_instance_type), &lhs_is_string);
87. GotoIf(IsBigIntInstanceType(lhs_instance_type), &lhs_is_bigint);
88. Goto(&call_with_any_feedback);
89. BIND(&lhs_is_bigint);
90. Branch(IsBigInt(rhs_heap_object), &bigint, &call_with_any_feedback);
91. BIND(&lhs_is_string);
92. {
93. TNode<Uint16T> rhs_instance_type = LoadInstanceType(rhs_heap_object);
94. GotoIfNot(IsStringInstanceType(rhs_instance_type),
95. &call_with_any_feedback);
96. var_type_feedback = SmiConstant(BinaryOperationFeedback::kString);
97. UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(),
98. slot_id, update_feedback_mode);
99. var_result =
100. CallBuiltin(Builtin::kStringAdd_CheckNone, context(), lhs, rhs);
101. Goto(&end);
102. } } }
103. BIND(&check_rhsisoddball);
104. {
105. TNode<Uint16T> rhs_instance_type = LoadInstanceType(CAST(rhs));
106. TNode<BoolT> rhs_is_oddball =
107. InstanceTypeEqual(rhs_instance_type, ODDBALL_TYPE);
108. GotoIf(rhs_is_oddball, &call_with_oddball_feedback);
109. Goto(&call_with_any_feedback);
110. }
111. BIND(&bigint);
112. {
113. var_result = CallBuiltin(Builtin::kBigIntAddNoThrow, context(), lhs, rhs);
114. GotoIf(TaggedIsSmi(var_result.value()), &bigint_too_big);
115. var_type_feedback = SmiConstant(BinaryOperationFeedback::kBigInt);
116. UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id,
117. update_feedback_mode);
118. Goto(&end);
119. BIND(&bigint_too_big);
120. {
121. UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny),
122. maybe_feedback_vector(), slot_id, update_feedback_mode);
123. ThrowRangeError(context(), MessageTemplate::kBigIntTooBig);
124. } }
125. BIND(&call_with_oddball_feedback);
126. {
127. var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumberOrOddball);
128. Goto(&call_add_stub); }
129. BIND(&call_with_any_feedback);
130. {
131. var_type_feedback = SmiConstant(BinaryOperationFeedback::kAny);
132. Goto(&call_add_stub); }
133. BIND(&call_add_stub);
134. {
135. UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id,
136. update_feedback_mode);
137. var_result = CallBuiltin(Builtin::kAdd, context(), lhs, rhs);
138. Goto(&end); }
139. BIND(&end);
140. return var_result.value();
141. } The first part : The left operand is Smi
The first 4 Line of code determines whether the left operand is Smi; If it is Smi In the first 5 Line code , Otherwise enter the 36 Line code ; Tips We write JS The program , The most common addition operation is the addition of two numbers , So first judge left 、 Is the right operand a numeric value .
The first 7 Line of code converts the left operand to Smi;
The first 8-10 The line code determines the right operation type ;
The first 12-17 The row processing right operand is not Smi The situation of ; Left 、 Right type is inconsistent , Type conversion required
The first 13-14 The right operand of row conversion is HeapObject, And for HeapNumber when , The left operand ( The first 15 That's ok ) To HeapNumber, Jump to do_fadd label ( The first 59-68 That's ok ) To complete the float Add ;
The first 18 The row processing right operand is Smi The situation of :
The first 20-23 Line complete Smi Add ; Be careful TrySmiAdd() At the same time, it is also responsible for judging whether the result is out of bounds ,Smi yes 31bit;
The first 26 Row update Feedback,TurboFan Use when optimizing , The next article explains ;
The first 30-34 When the line addition is out of bounds, use fload Add ( The first 59 That's ok );
The second part : The left operation is HeapNumber
The first 38-39 Line of code converts the left operand to HeapObject, And judge whether it is HeapNumber, No, just jump to the 70 Line code ;
The first 40-58 The row left operand is HeapNumber:
The first 44-49 Line determines that the right operand is HeapNumber, Jump to the first place 59 Line to complete float Add ; The first 54-56 Line determines that the right operand is Smi, Convert the right operand to HeapNumber Then jump to 59 That's ok ;
The first 59-67 Line complete float Add ;
The third part : The left operation is HeapObject, for example :BigInt、string
The first 72-91 Row out left 、 Of the right operand instance_type Type marker , And further determine the types of the two operations .
The first 96-100 Row left 、 The right operands are all String、 call Builtin::kStringAdd_CheckNone Complete string addition ;
The first 91-94 The row left operand is String And the right operand is not String Jump to 131 That's ok ;
The first 131-135 Row update feedback by kAny after , call Builtin::kAdd complete “ character string ” and “ The string ” Addition of .
Please analyze other situations in the above source code by yourself , Here is a brief explanation Builtin:kAdd Source code
4 Builtin:kAdd
Its source code is TQ To write , Source code is as follows :
1. transitioning builtin Add(implicit context: Context)(
2. leftArg: JSAny, rightArg: JSAny): JSAny {
3. try {
4. while (true) {
5. typeswitch (left) {
6. case (left: Smi): {
7. // Omit ................
8. }
9. case (left: HeapNumber): {
10. typeswitch (right) {
11. // Omit ................
12. }
13. case (left: BigInt): {
14. // Omit ................
15. }
16. case (left: String): {
17. goto StringAddConvertRight(left, right);
18. }
19. case (leftReceiver: JSReceiver): {
20. // Omit ................
21. }
22. case (HeapObject): {
23. // Omit ................
24. } } } }
25. unreachable;
26. }The above code first determines the type of the left operand 、 Then determine the right operation type , Then type cast and evaluate the result . The first 17 The line representation shows that the left operand is a string 、 Right operand non string addition implementation , namely Builtin Method StringAddConvertRight, This method uses TQ Realization , Its source code is builtins-string.tq in , The next article explains .
5 Technical summary
(1) Addition operation ( All operations ) Start with bytecode , Enter when the hot spot condition is reached TurboFan;
(2) Generate_AddWithFeedback Of “Feedback” Collect left 、 Type information of the right operand , be used for TurboFan Speculative optimization ;
(3) Generate_AddWithFeedback Add what you can't do builtin::kAdd complete , and kAdd We will also further subdivide the functions .
Okay , Come here today , See you next time .
Limited personal ability , There are shortcomings and mistakes , Welcome criticism and correction
WeChat :qq9123013 remarks :v8 communication You know :https://www.zhihu.com/people/...









