function upsilonReferencingItsPhi(index, input) { // All uses of "outputDouble" are converted to float. // Inside the loop, the Upsilon is referencing its own Phi. This should // not prevent the conversion. let outputDouble = input; while (index) { if (index & 0x4) outputDouble = Math.fround(outputDouble) + Math.PI; index = index >>> 1; } return Math.fround(outputDouble); } noInline(upsilonReferencingItsPhi); let expectedNotTaken = Math.fround(Math.LN2); let expectedTaken = Math.fround(Math.fround(Math.LN2) + Math.PI); for (let i = 0; i < 1e6; ++i) { let branchNotTakenResult = upsilonReferencingItsPhi(3, Math.LN2); if (branchNotTakenResult !== expectedNotTaken) throw "Failed upsilonReferencingItsPhi(3, Math.LN2) at i = " + i + " result = " + branchNotTakenResult; let branchTakenResult = upsilonReferencingItsPhi(7, Math.LN2); if (branchTakenResult !== expectedTaken) throw "Failed upsilonReferencingItsPhi(7, Math.LN2) at i = " + i + " result = " + branchTakenResult; } // Same as above, but this time it is always better to convert the outside Phi-Upsilon. function upsilonReferencingItsPhiAllFloat(index, input) { let outputDouble = Math.fround(input); while (index) { if (index & 0x4) outputDouble = Math.fround(outputDouble) + Math.PI; index = index >>> 1; } return Math.fround(outputDouble); } noInline(upsilonReferencingItsPhiAllFloat); for (let i = 0; i < 1e6; ++i) { let branchNotTakenResult = upsilonReferencingItsPhiAllFloat(3, Math.LN2); if (branchNotTakenResult !== expectedNotTaken) throw "Failed upsilonReferencingItsPhiAllFloat(3, Math.LN2) at i = " + i + " result = " + branchNotTakenResult; let branchTakenResult = upsilonReferencingItsPhiAllFloat(7, Math.LN2); if (branchTakenResult !== expectedTaken) throw "Failed upsilonReferencingItsPhiAllFloat(7, Math.LN2) at i = " + i + " result = " + branchTakenResult; } // This time, converting to float would be a mistake because one of the Phi // is not converted. function upsilonReferencingItsPhiWithoutConversion(index, input) { let outputDouble = input; while (index) { if (index & 0x4) outputDouble = Math.fround(outputDouble) + Math.PI; index = index >>> 1; } return outputDouble; } noInline(upsilonReferencingItsPhiWithoutConversion); let expectedNotTakenWithoutConversion = Math.LN2; let expectedTakenWithoutConversion = Math.fround(Math.LN2) + Math.PI; for (let i = 0; i < 1e6; ++i) { let branchNotTakenResult = upsilonReferencingItsPhiWithoutConversion(3, Math.LN2); if (branchNotTakenResult !== expectedNotTakenWithoutConversion) throw "Failed upsilonReferencingItsPhiWithoutConversion(3, Math.LN2) at i = " + i + " result = " + branchNotTakenResult; let branchTakenResult = upsilonReferencingItsPhiWithoutConversion(7, Math.LN2); if (branchTakenResult !== expectedTakenWithoutConversion) throw "Failed upsilonReferencingItsPhiWithoutConversion(7, Math.LN2) at i = " + i + " result = " + branchTakenResult; } function conversionPropagages(flags, a, b) { let result = 0.5; if (flags & 0x1) { if (flags & 0x2) { if (flags & 0x4) { if (flags & 0x8) { result = Math.fround(a) + Math.fround(b); } else { result = 6.5; } } else { result = 4.5; } } else { result = 2.5; } } else { result = 1.5; } return Math.fround(result); } noInline(conversionPropagages); let conversionPropagageExpectedResult = Math.fround(Math.fround(Math.LN2) + Math.fround(Math.PI)); for (let i = 0; i < 1e6; ++i) { let result = conversionPropagages(0xf, Math.LN2, Math.PI); if (result !== conversionPropagageExpectedResult) throw "Failed conversionPropagages(0xf, Math.LN2, Math.PI)"; } function chainedUpsilonBothConvert(condition1, condition2, a, b) { let firstPhi; if (condition1) firstPhi = Math.fround(a); else firstPhi = Math.fround(b); let secondPhi; if (condition2) secondPhi = firstPhi + 2; else secondPhi = firstPhi + 1; return Math.fround(secondPhi); } noInline(chainedUpsilonBothConvert); let expectedChainedUpsilonBothConvert = Math.fround(Math.fround(Math.PI) + Math.fround(1)); for (let i = 0; i < 1e6; ++i) { if (chainedUpsilonBothConvert(1, 0, Math.PI, Math.LN2) !== expectedChainedUpsilonBothConvert) throw "Failed chainedUpsilonBothConvert(1, 0, Math.PI, Math.LN2)"; } function chainedUpsilonFirstConvert(condition1, condition2, a, b) { // This first phi is trivially simplified by the fround() // of the second if-else. let firstPhi; if (condition1) firstPhi = Math.fround(a); else firstPhi = Math.fround(b); // This second one cannot ever be converted because the // result is not rounded to float. let secondPhi; if (condition2) secondPhi = Math.fround(firstPhi) + Math.fround(1/3); else secondPhi = Math.fround(firstPhi) - Math.fround(1/3); return secondPhi; } noInline(chainedUpsilonFirstConvert); let expectedChainedUpsilonFirstConvert = Math.fround(Math.PI) - Math.fround(1/3); for (let i = 0; i < 1e6; ++i) { if (chainedUpsilonFirstConvert(1, 0, Math.PI, Math.LN2) !== expectedChainedUpsilonFirstConvert) throw "Failed chainedUpsilonFirstConvert(1, 0, Math.PI, Math.LN2)"; }