| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| |
| // -force:fieldhoist -prejit |
| (function(){ |
| var obj6 = 1; |
| LABEL0: |
| LABEL1: |
| for (var __loopvar0 = 0; obj6.a < (1) && __loopvar0 < 3; obj6.a++ + __loopvar0++) { |
| } |
| })(); |
| |
| // - 'o1.a = 1' kills the hoisted o2.a as being live |
| // - As part of the load in '++o2.a' it reloads field into the hoisted stack sym. This load is int-specialized, making the |
| // hosited stack sym only live as an int. |
| // - As part of the store in '++o2.a' it loads the value to store into the hoisted stack sym and then stores the hoisted stack |
| // sym value into the field. As part of this load, since only the int version of the hoisted stack sym was live, liveness |
| // needs to be updated to say that only the var version of the hoisted stack sym is now live. |
| (function() { |
| var o1 = { a: 0 }; |
| var o2 = o1; |
| for (var i = 0; i < 1; ++i) |
| for (; o2.a < 1; ++o2.a) |
| o1.a = 1; |
| })(); |
| |
| function test0() { |
| function test0a(o) { |
| o.p = "1"; |
| } |
| |
| // - 'o.p' is live around the outer loop, so it's hoisted |
| // - At 'o.p &= 1' the hoisted stack sym is only live as an int32 |
| // - In the prepass of the inner loop, at 'o.q = test0a(o)', 'o.p' is killed. Ideally, here the hoisted int32 stack sym |
| // should also be killed, but it's not since it requires more tracking and computation. |
| // - In the optimization pass of the inner loop, the use at 'o.p | 0' requires a reload since 'o.p' is not live. At this |
| // point, the field should not have been live on entry into the loop either (after merge with prepass), so it should also |
| // kill the hoisted int32 stack sym. |
| // - This prevents compensation to be required on the back-edge of the inner loop in the optimization pass, which would in |
| // this case, cause an unnecessary (and permanent) bail-out even when aggressive int type spec is off (hence the assert). |
| function test0b(o) { |
| var sum = 0; |
| for (var i = 0; i < 10; ++i) { |
| sum += o.p &= 1; |
| for (var j = 0; j < 10; ++j) { |
| sum += o.p | 0; |
| o.q = test0a(o); |
| } |
| sum += o.p | 0; |
| } |
| |
| return sum; |
| } |
| |
| return test0b({ p: 1, q: 1 }); |
| } |
| WScript.Echo("test0: " + test0()); |
| |
| // - 'a' is a slot variable since it's used in a child function |
| // - In 'b = a = c ? 1 : 2', the sym for 'b' is only available as an int32 and 'b' is the sym store for that value |
| // - At 'a = a', the field (slot) load is hoisted. It is found that the field's value number in the landing pad is the same as |
| // the value's sym store's value number. So, instead of loading the field in the landing pad, 'b' is copied instead. |
| // - Since 'b' is only available as an int32, it needs to be converted to var. |
| function test1() { |
| var c = 1; |
| function test1a() { |
| var a; |
| for (var i = 0; i < 1; ++i) { |
| var b = a = c ? 1 : 2; |
| for (var j = 0; j < 1; ++j) |
| a = a; |
| } |
| function test1aa() { a; } |
| return a; |
| } |
| return test1a(); |
| } |
| WScript.Echo("test1: " + test1()); |
| |
| // - 'a' is considered a field since it's used by a child function |
| // - 'a' is hoisted outside the outer loop into a stack sym |
| // - 'a |= 0' makes the hoisted stack sym for 'a' available as var and lossless int32 |
| // - 'test2a()' should kill the hoisted stack syms for 'a' since that function could have changed 'a' |
| // - In this case, by the time of 'c = a' in the inner loop's second prepass, there are hoistable fields. Regardless of whether |
| // the loop has hoistable fields, the call should kill the hoisted stack syms for 'a' (the kill actually happens upon reload |
| // at 'c = a'). |
| function test2() { |
| var a, b, c; |
| for (var i = 0; i < 1; ++i) { |
| a |= 0; |
| b = 0; |
| for (var j = 0; j < 1; ++j) { |
| 0 ? b : 0; |
| null ? b = test2a() : a; |
| c = a; |
| } |
| } |
| function test2a() { a, b; } |
| return c; |
| } |
| WScript.Echo("test2: " + test2()); |
| |
| // - 'b' gets hoisted outside the most outer loop |
| // - 'b = -158506649.9 >> 1' makes the hoisted stack sym for 'b' available as an int32 |
| // - 'test3a()' kills 'b'. However, hoisted stack syms are not killed when fields are killed, rather, they need to be killed on |
| // the next reload. |
| // - Because 'b' is not live coming into the most inner loop, 'b' is hoistable in the prepass |
| // - The use of 'b' in 'b >>= b' and the lack of kills in the most inner loop cause 'b' to be hoisted outside that loop |
| // - Since 'b' was already hoisted, the hoisted stack sym is reused |
| // - Note that the hoisted stack sym from the most outer loop is available an an int32 in the most inner loop's landing pad |
| // - Hoisting 'b' into the most inner loop's landing pad counts as a reload, so it needs to kill specialized versions of the |
| // hoisted stack sym in the landing pad, loop header, and in the on-entry info to prevent forcing compensation |
| function test3() { |
| var a = 0; |
| var b = 0; |
| for (var i = 0; b !== 1 && i < 1; ++i) { |
| b = -158506649.9 >> 1; |
| for (var j = 0; j < 8; ++j) { |
| test3a(); |
| ++a; |
| for (var k = 0; (b >>= b) && k < 1; ++k) |
| a >>>= 1; |
| } |
| } |
| return a; |
| |
| function test3a() { |
| a; |
| ++b; |
| } |
| } |
| WScript.Echo("test3: " + test3()); |
| |
| // - 'a' is a slot variable since it's used by a child function, and is hoisted into a stack sym as a field |
| // - 'a &= 1' makes the hoisted stack sym available as an int |
| // - In 'a = 1', the constant is loaded into a stack sym ('s1 = 1') somewhere before, so the inner loop's last prepass sees |
| // 'a = s1'. Since 's1 = 1' would have already been int-specialized, 's1' would be available as an int and the hoisted stack |
| // sym of 'a' will also be made to be available as an int. |
| // - In the optimization pass, due to constant propagation, 'a = 1' appears as 'a.var = 0x3'. Since the loop prepass made the |
| // hoisted stack sym for 'a' available as an int, the optimization pass should do so as well (prepass must be equally or less |
| // aggressive than the optimization pass). |
| // - By the end, the int version of the hoisted stack sym of 'a' should be live through the inner loop and compensation should |
| // not be necessary |
| function test4() { |
| var a = 0; |
| for (var i = 0; i < 1; ++i) { |
| a &= 1; |
| for (var j = 0; j < 1; ++j) |
| a = 1; |
| } |
| return a; |
| |
| function test4a() { a; } |
| } |
| WScript.Echo("test4: " + test4()); |
| |
| // Same as above but with a field instead of a slot variable |
| function test5() { |
| var o = { a: 0 }; |
| for (var i = 0; i < 1; ++i) { |
| o.a &= 1; |
| for (var j = 0; j < 1; ++j) |
| o.a = 1; |
| } |
| return o.a; |
| } |
| WScript.Echo("test5: " + test5()); |