| // Copyright (c) 2023, 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. |
| |
| import 'dart:ffi'; |
| |
| import 'package:expect/expect.dart'; |
| |
| void main() { |
| testMultipleIncrement(); |
| reset(); |
| testMultipleCallSites(); |
| reset(); |
| testManyArguments(); |
| reset(); |
| testNonIntArguments(); |
| reset(); |
| testLargeInt(); |
| reset(); |
| testIntArguments(); |
| reset(); |
| testDoubleArguments(); |
| print('done'); |
| } |
| |
| @pragma('vm:force-optimize') |
| void testMultipleIncrement() { |
| int result = 0; |
| final counter = makeCounter(100000); |
| while (counter()) { |
| // We this calls with a cacheable call, |
| // which will lead to the counter no longer being incremented. |
| // Make sure to return the value, so we can see that the boxing and |
| // unboxing works as expected. |
| result = cachedIncrement(/*must be const*/ 3); |
| } |
| // Since this call site is force optimized, we should never recompile and thus |
| // we only ever increment the global counter once. |
| Expect.equals(3, result); |
| } |
| |
| /// A global counter, except for the call sites are being cached. |
| /// |
| /// Arguments passed to this function must be const. |
| /// Call sites should be rewritten to cache using the pool. |
| @pragma('vm:never-inline') |
| @pragma('vm:cachable-idempotent') |
| int cachedIncrement(int amount) { |
| return _globalCounter += amount; |
| } |
| |
| int _globalCounter = 0; |
| |
| void reset() { |
| print('reset'); |
| _globalCounter = 0; |
| } |
| |
| /// Helper for vm:force-optimize for loops without instance calls. |
| /// |
| /// A for loop uses the `operator+` on int. |
| bool Function() makeCounter(int count) { |
| return () => count-- >= 0; |
| } |
| |
| @pragma('vm:force-optimize') |
| void testMultipleCallSites() { |
| int result = 0; |
| final counter = makeCounter(10); |
| result = cachedIncrement(1); |
| while (counter()) { |
| result = cachedIncrement(10); |
| result = cachedIncrement(10); |
| } |
| result = cachedIncrement(100); |
| // All call sites are cached individually. |
| // Even if the arguments are identical. |
| Expect.equals(result, 121); |
| } |
| |
| @pragma('vm:force-optimize') |
| void testManyArguments() { |
| final result = manyArguments(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); |
| Expect.equals(55, result); |
| } |
| |
| @pragma('vm:never-inline') |
| @pragma('vm:cachable-idempotent') |
| int manyArguments( |
| int i1, |
| int i2, |
| int i3, |
| int i4, |
| int i5, |
| int i6, |
| int i7, |
| int i8, |
| int i9, |
| int i10, |
| ) { |
| return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10; |
| } |
| |
| @pragma('vm:force-optimize') |
| void testNonIntArguments() { |
| final result = lotsOfConstArguments( |
| "foo", |
| 3.0, |
| 3, |
| const _MyClass(_MyClass(42)), |
| ); |
| |
| Expect.equals(37, result); |
| } |
| |
| @pragma('vm:never-inline') |
| @pragma('vm:cachable-idempotent') |
| int lotsOfConstArguments(String s, double d, int i, _MyClass m) { |
| return [s, d, i, m].toString().length; |
| } |
| |
| final class _MyClass { |
| final Object i; |
| const _MyClass(this.i); |
| |
| @override |
| String toString() => '_MyClass($i)'; |
| } |
| |
| @pragma('vm:force-optimize') |
| void testLargeInt() { |
| final counter = makeCounter(10); |
| while (counter()) { |
| if (is64bitsArch()) { |
| final result1 = cachedIncrement(0x7FFFFFFFFFFFFFFF); |
| Expect.equals(0x7FFFFFFFFFFFFFFF, result1); |
| _globalCounter = 0; |
| final result2 = cachedIncrement(0x8000000000000000); |
| Expect.equals(0x8000000000000000, result2); |
| _globalCounter = 0; |
| final result3 = cachedIncrement(0xFFFFFFFFFFFFFFFF); |
| Expect.equals(0xFFFFFFFFFFFFFFFF, result3); |
| } else { |
| final result1 = cachedIncrement(0x7FFFFFFF); |
| Expect.equals(0x7FFFFFFF, result1); |
| _globalCounter = 0; |
| final result2 = cachedIncrement(0x80000000); |
| Expect.equals(0x80000000, result2); |
| _globalCounter = 0; |
| final result3 = cachedIncrement(0xFFFFFFFF); |
| Expect.equals(0xFFFFFFFF, result3); |
| } |
| } |
| } |
| |
| bool is64bitsArch() => sizeOf<Pointer>() == 8; |
| |
| @pragma('vm:force-optimize') |
| void testIntArguments() { |
| final result = lotsOfIntArguments(1, 2, 3, 4, 5, 6, 7, 8); |
| Expect.equals(36, result); |
| |
| // Do a second call with different values to prevent the argument values |
| // propagating to the function body in TFA. |
| final result2 = lotsOfIntArguments(101, 102, 103, 104, 105, 106, 107, 108); |
| Expect.equals(836, result2); |
| } |
| |
| @pragma('vm:never-inline') |
| @pragma('vm:cachable-idempotent') |
| int lotsOfIntArguments( |
| int d1, |
| int d2, |
| int d3, |
| int d4, |
| int d5, |
| int d6, |
| int d7, |
| int d8, |
| ) { |
| print([d1, d2, d3, d4, d5, d6, d7, d8]); |
| return (d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8).floor(); |
| } |
| |
| @pragma('vm:force-optimize') |
| void testDoubleArguments() { |
| final result = lotsOfDoubleArguments(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); |
| Expect.equals(36, result); |
| |
| // Do a second call with different values to prevent the argument values |
| // propagating to the function body in TFA. |
| final result2 = lotsOfDoubleArguments( |
| 101.0, |
| 102.0, |
| 103.0, |
| 104.0, |
| 105.0, |
| 106.0, |
| 107.0, |
| 108.0, |
| ); |
| Expect.equals(836, result2); |
| } |
| |
| @pragma('vm:never-inline') |
| @pragma('vm:cachable-idempotent') |
| int lotsOfDoubleArguments( |
| double d1, |
| double d2, |
| double d3, |
| double d4, |
| double d5, |
| double d6, |
| double d7, |
| double d8, |
| ) { |
| print([d1, d2, d3, d4, d5, d6, d7, d8]); |
| return (d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8).floor(); |
| } |