当前位置: 首页 > Web前端 > HTML

《Chrome V8 源码》48.弱类型加法之谜,“+”源码分析

时间:2023-03-28 14:28:35 HTML

1简介JavaScript是一种弱类型语言,所以当它的变量、表达式等参与运算时,即使类型不正确,也可以隐式传递转换得到正确的类型,在用户看来似乎所有类型都可以进行所有操作。本文分析V8的加法源码,带领大家了解JavaScript加法运算的细节,看看V8是怎么做的。2ADD_HANDLERV8从字节码执行JavaScript源码,所以我们从加法字节码处理程序入手,源码如下:必须从上面的ADD_HANDLER开始,内部也会调用Builtin::kAdd等函数。在TurboFan中,ADD_HANDLER可能被优化为NewConsString或StringConcat等函数。下面我们跟着代码看看V8是如何组织这些函数的。3Generate_AddWithFeedback1。TNodeBinaryOpAssembler::Generate_AddWithFeedback(){2.标签if_lhsisnotsmi(this,3.rhs_known_smi?Label::kDeferred:Label::kNonDeferred);4.分支(TaggedIsNotSmi(lhs),&if_lhsisnotsmi,&if_lhsissmi);5。绑定(&if_lhsissmi);6。{7.TNodelhs_smi=CAST(lhs);8.如果(!rhs_known_smi){9。标签if_rhsissmi(this),if_rhsisnotsmi(this);10.分支(TaggedIsSmi(rhs),&if_rhsissmi,&if_rhsisnotsmi);11。绑定(&if_rhsisnotsmi);12。{13。TNoderhs_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.转到(&do_fadd);}18.绑定(&if_rhsissmi);}19.{20。TNoderhs_smi=投(右);21。标签if_overflow(this,22.rhs_known_smi?Label::kDeferred:Label::kNonDeferred);23.TNodesmi_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.转到(&结束);}30。绑定(&if_overflow);31。{32。var_fadd_lhs=SmiToFloat64(lhs_smi);33。var_fadd_rhs=SmiToFloat64(rhs_smi);34。转到(&do_fadd);35。}}}36。绑定(&if_lhsisnotsmi);37。{38。TNodelhs_heap_object=CAST(lhs);39.GotoIfNot(IsHeapNumber(lhs_heap_object),&if_lhsisnotnumber);40.如果(!rhs_known_smi){41。标签if_rhsissmi(this),if_rhsisnotsmi(this);42.分支(TaggedIsSmi(rhs),&if_rhsissmi,&if_rhsisnotsmi);43。绑定(&if_rhsisnotsmi);44。{45。TNoderhs_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。转到(&do_fadd);50。}51.绑定(&if_rhsissmi);52。}53.{54。var_fadd_lhs=LoadHeapNumberValue(lhs_heap_object);55。var_fadd_rhs=SmiToFloat64(CAST(rhs));56。转到(&do_fadd);57。}58。}59.绑定(&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值=65。Float64Add(var_fadd_lhs.value(),var_fadd_rhs.value());66.TNode结果=AllocateHeapNumberWithValue(value);67。var_result=结果;68。转到(&结束);69。}70。绑定(&if_lhsisnotnumber);71。{72。TNodelhs_instance_type=LoadInstanceType(CAST(lhs));73。TNodelhs_is_oddball=74。InstanceTypeEqual(lhs_instance_type,ODDBALL_TYPE);75。分支(lhs_is_oddball,&if_lhsisoddball,&if_lhsisnotoddball);76。绑定(&if_lhsisoddball);77。{78。GotoIf(TaggedIsSmi(rhs),&call_with_oddball_feedback);79.分支(IsHeapNumber(CAST(rhs)),&call_with_oddball_feedback,80。&check_rhsisoddball);81。}82。绑定(&if_lhsisnotoddball);83。{84。GotoIf(TaggedIsSmi(rhs),&call_with_any_feedback);85.TNoderhs_heap_object=CAST(rhs);86。GotoIf(IsStringIn姿态类型(lhs_instance_type),&lhs_is_string);87.GotoIf(IsBigIntInstanceType(lhs_instance_type),&lhs_is_bigint);88.转到(&call_with_any_feedback);89。绑定(&lhs_is_bigint);90。分支(IsBigInt(rhs_heap_object),&bigint,&call_with_any_feedback);91。绑定(&lhs_is_string);92。{93。TNoderhs_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.转到(&结束);102。}}}103。BIND(&check_rhsisoddball);104.{105.TNoderhs_instance_type=LoadInstanceType(CAST(rhs));106。TNoderhs_is_oddball=107。InstanceTypeEqual(rhs_instance_type,ODDBALL_TYPE);108。GotoIf(rhs_is_oddball,&call_with_oddball_feedback);109.转到(&call_with_any_feedback);110。}111。绑定(&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.转到(&结束);119。绑定(&bigint_too_big);120。{121。UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny),122.maybe_feedback_vector(),slot_id,update_feedback_mode);123.投掷范围错误(上下文(),MessageTemplate::kBigIntTooBig);124。}}125。绑定(&call_with_oddball_feedback);126。{127。var_type_feedback=SmiConstant(BinaryOperationFeedback::kNumberOrOddball);128。转到(&call_add_stub);}129。&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,_slot_id,136.update_feedback_mode);137.var_result=CallBuiltin(Builtin::kAdd,context(),lhs,rhs);138.转到(&结束);}139.绑定(&结束);140.返回var_result.value();141.}第一部分:左操作数为Smi。第四行代码判断左操作数是否为Smi;最常用于两个值相加,所以先判断左右操作数是否为值第7行将左操作数转换为Smi;第8-10行确定正确操作的类型;第12-17行处理右操作数不是Smi的情况;左右类型不一致,需要进行类型转换13-14将右操作数转换为HeapObject和HeapNumber时,将左操作数(第15行)转换为HeapNumber,跳转到do_fadd标签处(第59-68行)完成浮动加法;第18行处理右操作数在Smi的情况下:第20-23行完成Smi加法;注意TrySmiAdd()在求和的同时还负责判断结果是否越界,Smi是31bit的;第26行更新Feedback,在TurboFan优化的时候用到,下一篇会讲解;当第30-34行加法越界时,使用fload加法(第59行);第二部分:左操作数为HeapNumber,第38-39行代码将左操作数转换为HeapObject,判断是否为HeapNumber,否则跳转到第一行70行代码;40-58行,左操作数为HeapNumber:44-49行判断右操作数为HeapNumber,跳转到59行完成float加法;54-56行判断右操作数为Smi,将右操作数转换为HeapNumber,跳转到59行;第59-67行完成float加法;第三部分:左操作为HeapObject,例如:BigInt,string72-91行取出左右操作数的instance_type类型标记,进一步判断两个操作的类型。第96-100行左右操作数均为String,调用Builtin::kStringAdd_CheckNone完成字符串相加;91-94行,当左操作数为String,右操作数不是String时,跳转到131行;lines131-在135行更新给kAny的反馈后,调用Builtin::kAdd完成“string”和“non-string”的相加。以上源码中其他情况请自行分析。下面简单解释一下Builtin:kAdd的源码4.Builtin:kAdd的源码是用TQ写的。源码如下:1.transitioningbuiltinAdd(implicitcontext:Context)(2.leftArg:JSAny,rightArg:JSAny):JSAny{3.尝试{4。而(真){5。typeswitch(左){6。案例(左:Smi):{7。//省略........8。}9.案例(左:HeapNumber):{10。typeswitch(右){11。//省略...12.}13.案例(左:BigInt):{14。//省略...15.}16.案例(左:字符串):{17。转到StringAddConvertRight(左,右);18。}19.案例(leftReceiver:JSReceiver):{20。//省略...21.}22.案例(堆对象):{23。//省略...24.}}}}25.不可及;26.}上面代码先判断左操作数的类型,再判断右操作数的类型,然后进行类型转换,计算结果。第17行给出了左操作数为字符串,右操作数不是字符串的加法实现,即TQ实现的内置方法StringAddConvertRight,其源码在builtins-string.tq中,将在下一篇文章中解释。5技术总结(1)加法操作(所有操作)从字节码开始,达到热点条件时进入TurboFan;(2)Generate_AddWithFeedback的“Feedback”收集左右操作数的类型信息,在TurboFan中使用(3)Generate_AddWithFeedback做不到的加法由builtin::kAdd完成,kAdd也会进行功能细分一步到位。好了,今天就到这里吧,下次见。个人能力有限,有不足和错误,欢迎批评指正微信:qq9123013备注:v8交流知乎:https://www.zhihu.com/people/...