blob: f4f7918fabca790a66568a2e2328724c244d9c5b [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// 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());