dart-sdk/tests/language_2/inv_cse_licm.dart
Aart Bik a10b0d8763 [vm/compiler] LICM and CSE improvements
Several improvements
(1) makes implicit control dependence of a null check
    explicit by means of data dependence (already
    done for bounds checks); also see the doc in
    runtime/docs/compiler/data_dep_for_control_dep.md
(2) improves CSE properties of various IL nodes
(3) allows LICM on invariant code that may-throw
    as long as the visible behavior is preserved

https://github.com/dart-lang/sdk/issues/35323
https://github.com/dart-lang/sdk/issues/34684

Change-Id: Icb4520a649da38eddc3d7c85af21427d3c64d22e
Reviewed-on: https://dart-review.googlesource.com/c/93822
Commit-Queue: Aart Bik <ajcbik@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2019-02-28 19:29:06 +00:00

501 lines
10 KiB
Dart

// 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();
}
}