// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // VMOptions=--deterministic --enable-inlining-annotations --optimization_counter_threshold=10 import 'dart:typed_data'; import "package:expect/expect.dart"; const String NeverInline = 'NeverInline'; // Tests a few situations in which invariant instructions // can be subject to CSE and LICM. @NeverInline int cse1(Int32List a, int n) { int x = a[0]; for (int i = 0; i < n; i++) { // The a[0] null check, bounds check, and the actual load can be // CSEed with the instructions above even if loop is not taken. x += a[0] * a[i]; } return x; } @NeverInline int cse2(Int32List a, int n) { int x = a[0]; for (int i = 0; i < n; i++) { // The a[0] null check, bounds check, but not the actual load can be // CSEed with the instructions above, since the value of the load // changes in the loop. a[i] = a[0] + 1; } return x; } @NeverInline int licm1(Int32List a, int n) { int x = 0; for (int i = 0; i < n; i++) { // The a[0] null check, bounds check, and the actual load cannot // be LICMed, since the loop may be non-taken. x += a[0] * a[i]; } return x; } @NeverInline int licm2(Int32List a) { int x = 0; for (int i = 0; i < 16; i++) { // The a[0] null check, bounds check, and the actual load can be // LICMed, since the loop is always-taken. x += a[0] * a[i]; } return x; } @NeverInline int licm3(Int32List a, bool cond) { int x = 0; for (int i = 0; i < 16; i++) { // The a[0] null check, bounds check, and the actual load cannot // be LICMed, since the condition may be non-taken (and we don't // hoist invariant conditions). if (cond) x += a[0] * a[i]; } return x; } @NeverInline int licm3_brk(Int32List a, bool cond) { int x = 0; for (int i = 0; i < 16; i++) { // The a[0] null check, bounds check, and the actual load cannot // be LICMed, since the condition may be taken (and we don't // hoist invariant conditions). if (cond) break; x += a[0] * a[i]; } return x; } int global; @NeverInline int licm4(Int32List a) { int x = 0; for (int i = 0; i < 16; i++) { // The a[0] null check, bounds check, and the actual load cannot // be LICMed, since something visible happens before an exception // may be thrown. global++; x += a[0] * a[i]; } return x; } @NeverInline int licm5(Int32List a) { int x = 0; // Anything in the loop header can be LICMed. for (int i = 0; i < a[1]; i++) { x++; } return x; } @NeverInline int licm6(Int32List a, int n) { int x = 0; int i = 0; do { // The a[0] null check, bounds check, and the actual load can be // LICMed, since this "header" is always-taken. x += a[0] * a[i++]; } while (i < n); return x; } @NeverInline int licm7(Int32List a, int n) { int x = 0; int i = 0; while (true) { // The a[0] null check, bounds check, and the actual load can be // LICMed, since this "header" is always-taken. x += a[0] * a[i++]; if (i >= n) break; } return x; } @NeverInline int licm8(Int32List a, int n) { int x = 0; int i = 0; while (true) { if (i >= n) break; // No LICM at this point, loop body may not be taken. x += a[0] * a[i++]; } return x; } @NeverInline int licm9(Int32List a) { int x = 0; int i = 0; while (true) { if (i >= 16) break; // The a[0] null check, bounds check, and the actual load can be // LICMed, since the loop is always-taken. x += a[0] * a[i++]; } return x; } @NeverInline int licm10(Int32List a, bool cond) { int x = 0; int i = 0; while (true) { if (i >= 16) break; // The a[0] null check, bounds check, and the actual load cannot // be LICMed, since the condition may be non-taken (and we don't // hoist invariant conditions). if (cond) x += a[0] * a[i]; i++; } return x; } @NeverInline int licm10_brk(Int32List a, bool cond) { int x = 0; int i = 0; while (true) { if (i >= 16) break; // The a[0] null check, bounds check, and the actual load cannot // be LICMed, since the condition may be taken (and we don't // hoist invariant conditions). if (cond) break; x += a[0] * a[i++]; } return x; } @NeverInline int licm11(Int32List a) { int x = 0; while (true) { // Anything in the loop header can be LICMed. if (x > a[1]) break; x++; } return x; } @NeverInline int foo() { return global--; } @NeverInline int licm12(Int32List a) { int x = 0; int i = 0; // Side-effect loop bound. for (int i = 0; i < foo(); i++) { x += a[0] * a[i++]; } return x; } doTests() { var x = new Int32List(0); var a = new Int32List(16); for (int i = 0; i < 16; i++) { a[i] = i + 1; } Expect.throws(() { cse1(null, 0); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { cse1(null, 1); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { cse1(x, 0); }, (e) { return e is RangeError; }); Expect.throws(() { cse1(x, 1); }, (e) { return e is RangeError; }); Expect.equals(1, cse1(a, 0)); Expect.equals(137, cse1(a, 16)); Expect.throws(() { cse2(null, 0); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { cse2(null, 1); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { cse2(x, 0); }, (e) { return e is RangeError; }); Expect.throws(() { cse2(x, 1); }, (e) { return e is RangeError; }); Expect.equals(1, cse2(a, 0)); Expect.equals(1, cse2(a, 16)); Expect.equals(2, a[0]); for (int i = 1; i < 16; i++) { Expect.equals(3, a[i]); } Expect.equals(0, licm1(null, 0)); Expect.throws(() { licm1(null, 1); }, (e) { return e is NoSuchMethodError; }); Expect.equals(0, licm1(x, 0)); Expect.throws(() { licm1(x, 1); }, (e) { return e is RangeError; }); Expect.equals(0, licm1(a, 0)); Expect.equals(94, licm1(a, 16)); Expect.throws(() { licm2(null); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { licm2(x); }, (e) { return e is RangeError; }); Expect.equals(94, licm2(a)); Expect.equals(0, licm3(null, false)); Expect.throws(() { licm3(null, true); }, (e) { return e is NoSuchMethodError; }); Expect.equals(0, licm3(x, false)); Expect.throws(() { licm3(x, true); }, (e) { return e is RangeError; }); Expect.equals(0, licm3(a, false)); Expect.equals(94, licm3(a, true)); Expect.equals(0, licm3_brk(null, true)); Expect.throws(() { licm3_brk(null, false); }, (e) { return e is NoSuchMethodError; }); Expect.equals(0, licm3_brk(x, true)); Expect.throws(() { licm3_brk(x, false); }, (e) { return e is RangeError; }); Expect.equals(0, licm3_brk(a, true)); Expect.equals(94, licm3_brk(a, false)); global = 0; Expect.throws(() { licm4(null); }, (e) { return e is NoSuchMethodError; }); Expect.equals(1, global); Expect.throws(() { licm4(x); }, (e) { return e is RangeError; }); Expect.equals(2, global); Expect.equals(94, licm4(a)); Expect.equals(18, global); Expect.throws(() { licm5(null); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { licm5(x); }, (e) { return e is RangeError; }); Expect.equals(3, licm5(a)); Expect.throws(() { licm6(null, 0); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { licm6(null, 1); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { licm6(x, 0); }, (e) { return e is RangeError; }); Expect.throws(() { licm6(x, 1); }, (e) { return e is RangeError; }); Expect.equals(4, licm6(a, 0)); Expect.equals(94, licm6(a, 16)); Expect.throws(() { licm7(null, 0); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { licm7(null, 1); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { licm7(x, 0); }, (e) { return e is RangeError; }); Expect.throws(() { licm7(x, 1); }, (e) { return e is RangeError; }); Expect.equals(4, licm7(a, 0)); Expect.equals(94, licm7(a, 16)); Expect.equals(0, licm8(null, 0)); Expect.throws(() { licm8(null, 1); }, (e) { return e is NoSuchMethodError; }); Expect.equals(0, licm8(x, 0)); Expect.throws(() { licm8(x, 1); }, (e) { return e is RangeError; }); Expect.equals(0, licm8(a, 0)); Expect.equals(94, licm8(a, 16)); Expect.throws(() { licm9(null); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { licm9(x); }, (e) { return e is RangeError; }); Expect.equals(94, licm9(a)); Expect.equals(0, licm10(null, false)); Expect.throws(() { licm10(null, true); }, (e) { return e is NoSuchMethodError; }); Expect.equals(0, licm10(x, false)); Expect.throws(() { licm10(x, true); }, (e) { return e is RangeError; }); Expect.equals(0, licm10(a, false)); Expect.equals(94, licm10(a, true)); Expect.equals(0, licm10_brk(null, true)); Expect.throws(() { licm10_brk(null, false); }, (e) { return e is NoSuchMethodError; }); Expect.equals(0, licm10_brk(x, true)); Expect.throws(() { licm10_brk(x, false); }, (e) { return e is RangeError; }); Expect.equals(0, licm10_brk(a, true)); Expect.equals(94, licm10_brk(a, false)); Expect.throws(() { licm11(null); }, (e) { return e is NoSuchMethodError; }); Expect.throws(() { licm11(x); }, (e) { return e is RangeError; }); Expect.equals(4, licm11(a)); global = 0; Expect.equals(0, licm12(null)); Expect.equals(-1, global); Expect.equals(0, licm12(x)); Expect.equals(-2, global); global = 16; Expect.throws(() { licm12(null); }, (e) { return e is NoSuchMethodError; }); Expect.equals(15, global); Expect.throws(() { licm12(x); }, (e) { return e is RangeError; }); Expect.equals(14, global); Expect.equals(28, licm12(a)); Expect.equals(8, global); } main() { // Repeat to enter JIT (when applicable). for (int i = 0; i < 20; i++) { doTests(); } }