当前位置:网站首页>Chrome V8 source code 48 The secret of weak type addition,'+'source code analysis

Chrome V8 source code 48 The secret of weak type addition,'+'source code analysis

2022-06-11 20:02:00 Grey bean

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/...

原网站

版权声明
本文为[Grey bean]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/03/202203011805557275.html