Skip to content

Commit

Permalink
[vm] Use multiple entrypoints to remove unnecessary checks on calls a…
Browse files Browse the repository at this point in the history
…gainst "this".

Test Plan:

Behavioral correctness should be ensured by existing tests. Tests in vm/dart/entrypoints
ensure that the unchecked entrypoint is used in cases where the optimization should trigger.

Bug: #31798

Change-Id: I5b880b2dfa6343b4bb0a96ad23562facff73e41f
Cq-Include-Trybots: luci.dart.try:vm-kernel-win-release-x64-try,vm-kernel-optcounter-threshold-linux-release-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-release-simarm-try,vm-kernel-precomp-linux-release-simarm64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/69741
Commit-Queue: Samir Jindel <[email protected]>
Reviewed-by: Vyacheslav Egorov <[email protected]>
  • Loading branch information
sjindel-google authored and [email protected] committed Aug 20, 2018
1 parent c082761 commit dea7de2
Show file tree
Hide file tree
Showing 34 changed files with 623 additions and 120 deletions.
19 changes: 19 additions & 0 deletions runtime/docs/pragmas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# VM-Specific Pragma Annotations

## Pragmas for general use

These pragmas are part of the VM's API and are safe for use in external code.

- **vm:entry-point**

[Defining entry-points into Dart code for an embedder or native methods]
(file://../vm/compiler/aot/entry_points_pragma.md)

## Pragmas for internal testing

These pragmas are used for inspecting or modifying internal VM state and should be used exclusively by SDK tests. They must be enabled with the `--enable-testing-pragmas` flag. The names of these pragmas are prefixed with "testing". Additionally, they are categorized into "safe" and "unsafe" forms: "safe" pragmas should not affect the behavior of the program and can be safely added anywhere. "unsafe" pragmas may change the code's behavior or may cause the VM to crash if used improperly.

- **vm:testing.unsafe.trace-entrypoints-fn**

[Observing which flow-graph-level entry-point was used when a function was called]
(file://../vm/compiler/frontend/entrypoints_pragma.md)
52 changes: 52 additions & 0 deletions runtime/tests/vm/dart/entrypoints/common.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2018, 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 "package:expect/expect.dart";

// We want to run each test with and without inlining of the target functions.
// We accomplish this by using VM options in the yes-inlining variant to set the
// "enable_inlining" constant variable to true. This maximizes code sharing
// between the two variants, which are otherwise identical.
const String NeverInline =
const bool.fromEnvironment("enable_inlining") ? "" : "NeverInline";

// All these tests can be run in test mode or in benchmark mode. In benchmark
// mode, there is introspection is omitted and the tests runs for many more
// iterations.
const bool benchmarkMode = const bool.fromEnvironment("benchmark_mode");

int expectedEntryPoint = -1;
int expectedTearoffEntryPoint = -1;

// We check that this is true at the end of the test to ensure that the
// introspection machinery is operational.
bool validateRan = false;

_validateHelper(int expected, int ep) {
validateRan = true;
if (ep < 0 || ep > 2) {
Expect.fail("ERROR: invalid entry-point ($ep) passed by VM.");
}
if (expected < -1 || expected > 2) {
Expect.fail("ERROR: invalid expected entry-point set ($expected)");
}
if (expected == -1) return;
Expect.equals(expected, ep);
}

void _validateFn(String _, int ep) => _validateHelper(expectedEntryPoint, ep);

// Invocation of tearoffs go through a tearoff wrapper. We want to independently
// test which entrypoint was used for the tearoff wrapper vs. which was used for
// actual target.
_validateTearoffFn(String name, int entryPoint) {
_validateHelper(
name.endsWith("#tearoff")
? expectedTearoffEntryPoint
: expectedEntryPoint,
entryPoint);
}

const validate = benchmarkMode ? null : _validateFn;
const validateTearoff = benchmarkMode ? null : _validateTearoffFn;
65 changes: 65 additions & 0 deletions runtime/tests/vm/dart/entrypoints/polymorphic_optional_this.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) 2016, 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.

// Test that 'PolymorphicInstanceCall's against "this" go through the unchecked
// entrypoint. The use of optional arguments here encourages prologue sharing
// between the entrypoints.

import "common.dart";
import "package:expect/expect.dart";

abstract class C<T> {
@NeverInline
void samir1(T x) {
samir2(x, y: "hi");
}

void samir2(T x, {String y});
}

class D<T> extends C<T> {
@NeverInline
@pragma("vm:testing.unsafe.trace-entrypoints-fn", validate)
void samir2(T x, {String y}) {
Expect.notEquals(x, -1);
Expect.equals(y, "hi");
}
}

class E<T> extends C<T> {
@NeverInline
@pragma("vm:testing.unsafe.trace-entrypoints-fn", validate)
void samir2(T x, {String y}) {
Expect.notEquals(x, -1);
Expect.equals(y, "hi");
}
}

int j = 0;

C getC() {
if (j % 2 == 0) {
++j;
return new D<int>();
} else {
++j;
return new E<int>();
}
}

test(List<String> args) {
// Warmup.
expectedEntryPoint = -1;
for (int i = 0; i < 100; ++i) {
getC().samir1(i);
}

expectedEntryPoint = 2;
const int iterations = benchmarkMode ? 100000000 : 100;
for (int i = 0; i < iterations; ++i) {
getC().samir1(i);
}

Expect.isTrue(validateRan);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) 2016, 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=--enable-testing-pragmas --no-background-compilation --enable-inlining-annotations --optimization-counter-threshold=10 -Denable_inlining=true

import "polymorphic_optional_this.dart";

main(args) => test(args);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) 2016, 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=--enable-testing-pragmas --no-background-compilation --enable-inlining-annotations --optimization-counter-threshold=10

import "polymorphic_optional_this.dart";

main(args) => test(args);
69 changes: 69 additions & 0 deletions runtime/tests/vm/dart/entrypoints/polymorphic_this.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) 2016, 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.

// Test that 'PolymorphicInstanceCall's against "this" go through the unchecked
// entrypoint.

import "common.dart";
import "package:expect/expect.dart";

abstract class C<T> {
@NeverInline
void target1(T x) {
target2(x);
}

void target2(T x);
}

class D<T> extends C<T> {
@NeverInline
@pragma("vm:testing.unsafe.trace-entrypoints-fn", validate)
void target2(T x) {
Expect.notEquals(x, -1);
}
}

class E<T> extends C<T> {
@pragma("vm:testing.unsafe.trace-entrypoints-fn", validate)
@NeverInline
void target2(T x) {
Expect.notEquals(x, -1);
}
}

int j = 0;

C getC() {
if (j % 2 == 0) {
++j;
return new D<int>();
} else {
++j;
return new E<int>();
}
}

test(List<String> args) {
// Warmup.
expectedEntryPoint = -1;
for (int i = 0; i < 100; ++i) {
getC().target1(0);
}

expectedEntryPoint = 1;
const int iterations = benchmarkMode ? 200000000 : 100;
for (int i = 0; i < iterations; ++i) {
getC().target1(i);
}

// Once for D and once for E.
expectedEntryPoint = 0;
dynamic f = getC().target2;
f(0);
f = getC().target2;
f(0);

Expect.isTrue(validateRan);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) 2016, 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=--enable-testing-pragmas --no-background-compilation --enable-inlining-annotations --optimization-counter-threshold=10 -Denable_inlining=true

import "polymorphic_this.dart";

main(args) => test(args);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) 2016, 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=--enable-testing-pragmas --no-background-compilation --enable-inlining-annotations --optimization-counter-threshold=10

import "polymorphic_this.dart";

main(args) => test(args);
44 changes: 44 additions & 0 deletions runtime/tests/vm/dart/entrypoints/static_this.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2016, 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.

// Test that 'StaticCall's against "this" go through the unchecked entry-point.

import "common.dart";
import "package:expect/expect.dart";

class C<T> {
@pragma("vm:testing.unsafe.trace-entrypoints-fn", validate)
@NeverInline
void target2(T x) {
Expect.notEquals(x, -1);
}

@NeverInline
void target1(T x) {
target2(x);
}
}

test(List<String> args) {
// Make sure the precise runtime-type of C is not known below.
C c = args.length == 0 ? C<int>() : C<String>();

// Warmup.
expectedEntryPoint = -1;
for (int i = 0; i < 100; ++i) {
c.target1(i);
}

expectedEntryPoint = 1;
const int iterations = benchmarkMode ? 400000000 : 100;
for (int i = 0; i < iterations; ++i) {
c.target1(i);
}

expectedEntryPoint = 0;
dynamic f = c.target2;
f(0);

Expect.isTrue(validateRan);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) 2016, 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=--enable-testing-pragmas --no-background-compilation --enable-inlining-annotations --optimization-counter-threshold=10 -Denable_inlining=true

import "static_this.dart";

main(args) => test(args);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) 2016, 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=--enable-testing-pragmas --no-background-compilation --enable-inlining-annotations --optimization-counter-threshold=10

import "static_this.dart";

main(args) => test(args);
3 changes: 3 additions & 0 deletions runtime/tests/vm/vm.status
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ dart/simd128float32_test: Skip # compilers not aware of Simd128
dart/truncating_ints_test: SkipByDesign # The test requires int64.
dart/wrap_around_in_range_analysis_test: SkipByDesign # The test requires int64.

[ $compiler != dartk || ($arch != x64 && $arch != simarm && $arch != arm)]
dart/entrypoints/*: Skip # Only supported in Dart 2 JIT.

[ ($compiler == dartk || $compiler == dartkb) ]
cc/DartAPI_New: Fail # Issue #33041
dart/redirection_type_shuffling_test/00: RuntimeError, Pass
Expand Down
14 changes: 7 additions & 7 deletions runtime/vm/code_patcher_x64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ class UnoptimizedCall : public ValueObject {

bool IsValid() const {
static int16_t pattern[kCallPatternSize] = {
0x49, 0x8b, 0x9f, -1, -1, -1, -1, // movq RBX, [PP + offs]
0x4d, 0x8b, 0xa7, -1, -1, -1, -1, // movq CR, [PP + offs]
0x4d, 0x8b, 0x5c, 0x24, 0x07, // movq TMP, [CR + entry_point_offs]
0x41, 0xff, 0xd3 // callq TMP
0x49, 0x8b, 0x9f, -1, -1, -1, -1, // movq RBX, [PP + offs]
0x4d, 0x8b, 0xa7, -1, -1, -1, -1, // movq CR, [PP + offs]
0x4d, 0x8b, 0x5c, 0x24, -1, // movq TMP, [CR + entry_point_offs]
0x41, 0xff, 0xd3 // callq TMP
};
return MatchesPattern(start_, pattern, kCallPatternSize);
}
Expand Down Expand Up @@ -134,9 +134,9 @@ class PoolPointerCall : public ValueObject {

bool IsValid() const {
static int16_t pattern[kCallPatternSize] = {
0x4d, 0x8b, 0xa7, -1, -1, -1, -1, // movq CR, [PP + offs]
0x4d, 0x8b, 0x5c, 0x24, 0x07, // movq TMP, [CR + entry_point_off]
0x41, 0xff, 0xd3 // callq TMP
0x4d, 0x8b, 0xa7, -1, -1, -1, -1, // movq CR, [PP + offs]
0x4d, 0x8b, 0x5c, 0x24, -1, // movq TMP, [CR + entry_point_off]
0x41, 0xff, 0xd3 // callq TMP
};
return MatchesPattern(start_, pattern, kCallPatternSize);
}
Expand Down
Loading

0 comments on commit dea7de2

Please sign in to comment.