[vm] Turn on entry point checking in JIT mode.

Now that Flutter tests that access entry points from native code
have been annotated[1], we can turn on entry point checking in JIT
mode.

This CL also removes the A flag category from flag_list.h and the
AOT_FLAG_MACRO definitions and uses from flags.[cc,h], as they were
created as a temporary measure until this flag could be unconditionally
defaulted to true.

[1] See the following PRs:
* https://github.com/flutter/engine/pull/57158
* https://github.com/flutter/flutter/pull/160158
* https://github.com/flutter/flutter/pull/160421

TEST=vm/dart/entrypoints_verification_test vm/cc/IRTest
     vm/cc/StreamingFlowGraphBuilder vm/cc/STC vm/cc/TTS

Issue: https://github.com/dart-lang/sdk/issues/50649
Issue: https://github.com/flutter/flutter/issues/118608

Cq-Include-Trybots: luci.dart.try:vm-aot-linux-product-x64-try,vm-aot-linux-debug-x64-try,vm-aot-mac-release-arm64-try,vm-aot-mac-product-arm64-try,vm-aot-dwarf-linux-product-x64-try,vm-linux-debug-x64-try,vm-linux-release-x64-try,vm-appjit-linux-product-x64-try
Change-Id: Ibe5b21bb74f1a6fb88824b71ff87b9e555216dbf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/400301
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
This commit is contained in:
Tess Strickland
2024-12-18 03:42:55 -08:00
committed by Commit Queue
parent 04530d578f
commit 982b9fad44
11 changed files with 97 additions and 36 deletions
@@ -2,7 +2,6 @@
// 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=--verify-entry-points=true
// SharedObjects=entrypoints_verification_test
import 'dart:ffi';
+5
View File
@@ -62,6 +62,7 @@ ISOLATE_UNIT_TEST_CASE(IRTest_EliminateWriteBarrier) {
Container<int> x = Container<int>();
@pragma("vm:entry-point", "call")
foo() {
for (int i = 0; i < 10; ++i) {
x[i] = i;
@@ -689,6 +690,7 @@ ISOLATE_UNIT_TEST_CASE(IRTest_LoadThread) {
auto kScript = R"(
import 'dart:ffi';
@pragma("vm:entry-point", "call")
int myFunction() {
return 100;
}
@@ -780,6 +782,7 @@ ISOLATE_UNIT_TEST_CASE(IRTest_CachableIdempotentCall) {
return increment();
}
@pragma("vm:entry-point", "call")
int multipleIncrement() {
int returnValue = 0;
for(int i = 0; i < 10; i++) {
@@ -943,6 +946,7 @@ ISOLATE_UNIT_TEST_CASE(IRTest_FfiCallInstrLeafDoesntSpill) {
void placeholder() {}
// Will call the "doFfiCall" and exercise its code.
@pragma("vm:entry-point", "call")
bool invokeDoFfiCall() {
final double result = doFfiCall(1, 2, 3, 1.0, 2.0, 3.0);
if (result != (2 + 3 + 4 + 2.0 + 3.0 + 4.0)) {
@@ -971,6 +975,7 @@ ISOLATE_UNIT_TEST_CASE(IRTest_FfiCallInstrLeafDoesntSpill) {
typedef NT = Void Function();
typedef DT = void Function();
Pointer<NativeFunction<NT>> ptr = Pointer.fromAddress(0);
@pragma("vm:entry-point", "call")
DT getFfiTrampolineClosure() => ptr.asFunction(isLeaf:true);
)";
@@ -157,12 +157,14 @@ static void RunMemoryCopyInstrTest(intptr_t src_start,
CStringUniquePtr kScript(OS::SCreate(nullptr, R"(
import 'dart:ffi';
@pragma("vm:entry-point", "call")
void copyConst() {
final pointer = Pointer<Uint8>.fromAddress(%s%p);
final pointer2 = Pointer<Uint8>.fromAddress(%s%p);
noop();
}
@pragma("vm:entry-point", "call")
void callNonConstCopy() {
final pointer = Pointer<Uint8>.fromAddress(%s%p);
final pointer2 = Pointer<Uint8>.fromAddress(%s%p);
@@ -1480,6 +1480,7 @@ ISOLATE_UNIT_TEST_CASE(DelayAllocations_DelayAcrossCalls) {
@pragma("vm:never-inline")
dynamic use(v) {}
@pragma("vm:entry-point", "call")
void test() {
A a = new A(foo(1), foo(2));
use(a);
@@ -1738,10 +1739,12 @@ ISOLATE_UNIT_TEST_CASE(AllocationSinking_NoViewDataMaterialization) {
return x is int;
}
@pragma("vm:entry-point", "call")
bool %s() {
return %s(0xABCC);
}
@pragma("vm:entry-point", "call")
bool %s() {
return %s(1.0);
}
@@ -52,6 +52,7 @@ void RunTestInMode(CompilerPass::PipelineMode mode) {
R"(
import 'dart:async';
@pragma("vm:entry-point", "call")
Future foo() async {
print('pos-0');
await 0;
@@ -81,9 +82,9 @@ void RunTestInMode(CompilerPass::PipelineMode mode) {
auto validate_indices = [](const YieldPoints& yield_points) {
EXPECT_EQ(3, yield_points.length());
EXPECT_EQ(88, yield_points[0].Pos());
EXPECT_EQ(129, yield_points[1].Pos());
EXPECT_EQ(170, yield_points[2].Pos());
EXPECT_EQ(128, yield_points[0].Pos());
EXPECT_EQ(169, yield_points[1].Pos());
EXPECT_EQ(210, yield_points[2].Pos());
};
validate_indices(*GetYieldPointsFromGraph(flow_graph));
@@ -15,6 +15,7 @@ ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_ConstFoldStringConcats) {
// "Adjacent strings are implicitly concatenated to form a single string
// literal."
const char* kScript = R"(
@pragma("vm:entry-point", "call")
test() {
var s = 'aaaa'
'bbbb'
@@ -252,6 +253,7 @@ ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_ConcatStringLits) {
ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_InvariantFlagInListLiterals) {
const char* kScript = R"(
@pragma("vm:entry-point", "call")
test() {
return [...[], 42];
}
@@ -314,6 +316,7 @@ ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_TypedClosureCall) {
//
const char* kScript = R"(
int callClosure(int Function(int) fun, int value) => fun(value);
@pragma("vm:entry-point", "call")
test() => callClosure((int a) => a + 1, 10);
)";
@@ -354,6 +357,7 @@ ISOLATE_UNIT_TEST_CASE(
StreamingFlowGraphBuilder_StaticGetFinalFieldWithTrivialInitializer) {
const char* kScript = R"(
final int x = 0xFEEDFEED;
@pragma("vm:entry-point", "call")
test() {
return x;
}
+2 -5
View File
@@ -69,8 +69,6 @@ constexpr bool FLAG_support_il_printer = false;
// * R elease flags: Generally available flags except when building product.
// * pre C ompile flags: Generally available flags except when building product
// or precompiled runtime.
// * A ot flags: Generally available flags except when building precompiled
// runtime. (Unlike C, these flags are available in product mode.)
// * D ebug flags: Can only be set in debug VMs, which also have C++ assertions
// enabled.
//
@@ -78,9 +76,8 @@ constexpr bool FLAG_support_il_printer = false;
// P(name, type, default_value, comment)
// R(name, product_value, type, default_value, comment)
// C(name, precompiled_value, product_value, type, default_value, comment)
// A(name, precompiled_value, type, default_value, comment)
// D(name, type, default_value, comment)
#define FLAG_LIST(P, R, C, A, D) \
#define FLAG_LIST(P, R, C, D) \
VM_GLOBAL_FLAG_LIST(P, R, C, D) \
DISASSEMBLE_FLAGS(P, R, C, D) \
P(abort_on_oom, bool, false, \
@@ -249,7 +246,7 @@ constexpr bool FLAG_support_il_printer = false;
R(eliminate_type_checks, true, bool, true, \
"Eliminate type checks when allowed by static type analysis.") \
D(support_rr, bool, false, "Support running within RR.") \
A(verify_entry_points, true, bool, false, \
P(verify_entry_points, bool, true, \
"Throw API error on invalid member access through native API. See " \
"entry_point_pragma.md") \
C(branch_coverage, false, false, bool, false, "Enable branch coverage") \
-12
View File
@@ -35,8 +35,6 @@ DEFINE_FLAG(bool,
// Nothing to be done for the precompilation flag definitions.
#define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \
default_value, comment)
// Nothing to be done for the AOT flag definitions.
#define AOT_FLAG_MACRO(name, pre_value, type, default_value, comment)
#elif defined(PRODUCT) // !PRECOMPILED
// Nothing to be done for the product flag definitions.
@@ -44,9 +42,6 @@ DEFINE_FLAG(bool,
// Nothing to be done for the precompilation flag definitions.
#define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \
default_value, comment)
#define AOT_FLAG_MACRO(name, pre_value, type, default_value, comment) \
type FLAG_##name = \
Flags::Register_##type(&FLAG_##name, #name, default_value, comment);
#elif defined(DART_PRECOMPILED_RUNTIME) // !PRODUCT
#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \
@@ -55,8 +50,6 @@ DEFINE_FLAG(bool,
// Nothing to be done for the precompilation flag definitions.
#define PRECOMPILE_FLAG_MACRO(name, pre_value, product_value, type, \
default_value, comment)
// Nothing to be done for the AOT flag definitions.
#define AOT_FLAG_MACRO(name, pre_value, type, default_value, comment)
#else // !PRODUCT && !PRECOMPILED
#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \
@@ -66,22 +59,17 @@ DEFINE_FLAG(bool,
default_value, comment) \
type FLAG_##name = \
Flags::Register_##type(&FLAG_##name, #name, default_value, comment);
#define AOT_FLAG_MACRO(name, pre_value, type, default_value, comment) \
type FLAG_##name = \
Flags::Register_##type(&FLAG_##name, #name, default_value, comment);
#endif
// Define all of the non-product flags here.
FLAG_LIST(PRODUCT_FLAG_MACRO,
RELEASE_FLAG_MACRO,
PRECOMPILE_FLAG_MACRO,
AOT_FLAG_MACRO,
DEBUG_FLAG_MACRO)
#undef PRODUCT_FLAG_MACRO
#undef RELEASE_FLAG_MACRO
#undef PRECOMPILE_FLAG_MACRO
#undef AOT_FLAG_MACRO
#undef DEBUG_FLAG_MACRO
#if defined(DART_PRECOMPILER)
-10
View File
@@ -124,8 +124,6 @@ class Flags {
#define PRECOMPILE_FLAG_MACRO(name, precompiled_value, product_value, type, \
default_value, comment) \
const type FLAG_##name = precompiled_value;
#define AOT_FLAG_MACRO(name, precompiled_value, type, default_value, comment) \
const type FLAG_##name = precompiled_value;
#elif defined(PRODUCT) // !PRECOMPILED
#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \
@@ -133,8 +131,6 @@ class Flags {
#define PRECOMPILE_FLAG_MACRO(name, precompiled_value, product_value, type, \
default_value, comment) \
const type FLAG_##name = product_value;
#define AOT_FLAG_MACRO(name, precompiled_value, type, default_value, comment) \
extern type FLAG_##name;
#elif defined(DART_PRECOMPILED_RUNTIME) // !PRODUCT
#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \
@@ -142,8 +138,6 @@ class Flags {
#define PRECOMPILE_FLAG_MACRO(name, precompiled_value, product_value, type, \
default_value, comment) \
const type FLAG_##name = precompiled_value;
#define AOT_FLAG_MACRO(name, precompiled_value, type, default_value, comment) \
const type FLAG_##name = precompiled_value;
#else // !PRODUCT && !PRECOMPILED
#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment) \
@@ -151,8 +145,6 @@ class Flags {
#define PRECOMPILE_FLAG_MACRO(name, precompiled_value, product_value, type, \
default_value, comment) \
extern type FLAG_##name;
#define AOT_FLAG_MACRO(name, precompiled_value, type, default_value, comment) \
extern type FLAG_##name;
#endif
@@ -160,13 +152,11 @@ class Flags {
FLAG_LIST(PRODUCT_FLAG_MACRO,
RELEASE_FLAG_MACRO,
PRECOMPILE_FLAG_MACRO,
AOT_FLAG_MACRO,
DEBUG_FLAG_MACRO)
#undef RELEASE_FLAG_MACRO
#undef DEBUG_FLAG_MACRO
#undef PRODUCT_FLAG_MACRO
#undef AOT_FLAG_MACRO
#undef PRECOMPILE_FLAG_MACRO
#if defined(DART_PRECOMPILER)
+11 -3
View File
@@ -8436,15 +8436,23 @@ static void SubtypeTestCacheTest(Thread* thread,
intptr_t num_classes,
bool expect_hash) {
TextBuffer buffer(MB);
buffer.AddString("class D {}\n");
buffer.AddString("D createInstanceD() => D();");
buffer.AddString("D Function() createClosureD() => () => D();\n");
buffer.AddString(R"(
class D {}
@pragma('vm:entry-point', 'call')
D createInstanceD() => D();
@pragma('vm:entry-point', 'call')
D Function() createClosureD() => () => D();
)");
for (intptr_t i = 0; i < num_classes; i++) {
buffer.Printf(R"(class C%)" Pd R"( extends D {}
)"
"@pragma('vm:entry-point', 'call')\n"
R"(C%)" Pd R"( createInstanceC%)" Pd R"(() => C%)" Pd
R"(();
)"
"@pragma('vm:entry-point', 'call')\n"
R"(C%)" Pd R"( Function() createClosureC%)" Pd
R"(() => () => C%)" Pd
R"(();
+66 -2
View File
@@ -865,20 +865,35 @@ const char* kSubtypeRangeCheckScript =
genericFun<A, B>() {}
@pragma("vm:entry-point", "call")
createI() => I<int, String>();
@pragma("vm:entry-point", "call")
createI2() => I2();
@pragma("vm:entry-point", "call")
createBaseInt() => Base<int>();
@pragma("vm:entry-point", "call")
createBaseNull() => Base<Null>();
@pragma("vm:entry-point", "call")
createBaseNever() => Base<Never>();
@pragma("vm:entry-point", "call")
createA() => A();
@pragma("vm:entry-point", "call")
createA1() => A1();
@pragma("vm:entry-point", "call")
createA2() => A2<int>();
@pragma("vm:entry-point", "call")
createB() => B();
@pragma("vm:entry-point", "call")
createB1() => B1();
@pragma("vm:entry-point", "call")
createB2() => B2<int>();
@pragma("vm:entry-point", "call")
createBaseIStringDouble() => Base<I<String, double>>();
@pragma("vm:entry-point", "call")
createBaseA2Int() => Base<A2<int>>();
@pragma("vm:entry-point", "call")
createBaseA2A1() => Base<A2<A1>>();
@pragma("vm:entry-point", "call")
createBaseB2Int() => Base<B2<int>>();
)";
@@ -1218,17 +1233,28 @@ const char* kRecordSubtypeRangeCheckScript =
class D<T> {}
getType<T>() => T;
@pragma("vm:entry-point", "call")
getRecordType1() => getType<(int, A)>();
@pragma("vm:entry-point", "call")
getRecordType2() => getType<(A, int, String)>();
@pragma("vm:entry-point", "call")
getRecordType3() => getType<(int, D)>();
@pragma("vm:entry-point", "call")
createObj1() => (1, B());
@pragma("vm:entry-point", "call")
createObj2() => (1, 'bye');
@pragma("vm:entry-point", "call")
createObj3() => (1, foo: B());
@pragma("vm:entry-point", "call")
createObj4() => (1, B(), 2);
@pragma("vm:entry-point", "call")
createObj5() => (C(), 2, 'hi');
@pragma("vm:entry-point", "call")
createObj6() => (D(), 2, 'hi');
@pragma("vm:entry-point", "call")
createObj7() => (3, D<int>());
@pragma("vm:entry-point", "call")
createObj8() => (D<int>(), 3);
)";
@@ -1280,6 +1306,7 @@ ISOLATE_UNIT_TEST_CASE(TTS_Generic_Implements_Instantiated_Interface) {
abstract class I<T> {}
class B<R> implements I<String> {}
@pragma("vm:entry-point", "call")
createBInt() => B<int>();
)";
@@ -1308,8 +1335,11 @@ ISOLATE_UNIT_TEST_CASE(TTS_Future) {
R"(
import "dart:async";
@pragma("vm:entry-point", "call")
Future<int> createFutureInt() async => 3;
@pragma("vm:entry-point", "call")
Future<int Function()> createFutureFunction() async => () => 3;
@pragma("vm:entry-point", "call")
Future<int Function()?> createFutureNullableFunction() async =>
(() => 3) as int Function()?;
)";
@@ -1689,8 +1719,11 @@ ISOLATE_UNIT_TEST_CASE(TTS_Regress40964) {
class B<T> {}
class C<T> {}
@pragma("vm:entry-point", "call")
createACint() => A<C<int>>();
@pragma("vm:entry-point", "call")
createBCint() => B<C<int>>();
@pragma("vm:entry-point", "call")
createBCnum() => B<C<num>>();
)";
@@ -1726,7 +1759,9 @@ ISOLATE_UNIT_TEST_CASE(TTS_TypeParameter) {
}
H genericFun<H>(dynamic x) => x as H;
@pragma("vm:entry-point", "call")
createAInt() => A<int>();
@pragma("vm:entry-point", "call")
createAString() => A<String>();
)";
@@ -1859,11 +1894,16 @@ ISOLATE_UNIT_TEST_CASE(TTS_Function) {
R"(
class A<T> {}
@pragma("vm:entry-point", "call")
createF() => (){};
@pragma("vm:entry-point", "call")
createG() => () => 3;
@pragma("vm:entry-point", "call")
createH() => (int x, String y, {int z = 0}) => x + z;
@pragma("vm:entry-point", "call")
createAInt() => A<int>();
@pragma("vm:entry-point", "call")
createAFunction() => A<Function>();
)";
@@ -1904,9 +1944,13 @@ ISOLATE_UNIT_TEST_CASE(TTS_Partial) {
class E extends D {}
F<A>() {}
@pragma("vm:entry-point", "call")
createBE() => B<E>();
@pragma("vm:entry-point", "call")
createBENullable() => B<E?>();
@pragma("vm:entry-point", "call")
createBNull() => B<Null>();
@pragma("vm:entry-point", "call")
createBNever() => B<Never>();
)";
@@ -2021,6 +2065,7 @@ ISOLATE_UNIT_TEST_CASE(TTS_Partial_Incremental) {
const char* kFirstScript =
R"(
class B<T> {}
@pragma("vm:entry-point", "call")
createB() => B<int>();
)";
@@ -2030,6 +2075,7 @@ ISOLATE_UNIT_TEST_CASE(TTS_Partial_Incremental) {
R"(
import ")" FIRST_PARTIAL_LIBRARY_NAME R"(";
class B2<T> extends B<T> {}
@pragma("vm:entry-point", "call")
createB2() => B2<int>();
)";
@@ -2038,6 +2084,7 @@ ISOLATE_UNIT_TEST_CASE(TTS_Partial_Incremental) {
R"(
import ")" FIRST_PARTIAL_LIBRARY_NAME R"(";
class B3<T> extends B<T> {}
@pragma("vm:entry-point", "call")
createB3() => B3<int>();
)";
@@ -2201,11 +2248,16 @@ static const char* kLoadedScript =
R"(
class A<T> {}
@pragma("vm:entry-point", "call")
createAInt() => A<int>();
@pragma("vm:entry-point", "call")
createAString() => A<String>();
@pragma("vm:entry-point", "call")
(int, int) createRecordIntInt() => (1, 2);
@pragma("vm:entry-point", "call")
(String, int) createRecordStringInt() => ("foo", 2);
@pragma("vm:entry-point", "call")
(int, String) createRecordIntString() => (1, "bar");
)";
@@ -2214,13 +2266,20 @@ static const char* kReloadedScript =
class A<T> {}
class A2<T> extends A<T> {}
@pragma("vm:entry-point", "call")
createAInt() => A<int>();
@pragma("vm:entry-point", "call")
createAString() => A<String>();
@pragma("vm:entry-point", "call")
createA2Int() => A2<int>();
@pragma("vm:entry-point", "call")
createA2String() => A2<String>();
@pragma("vm:entry-point", "call")
(int, int) createRecordIntInt() => (1, 2);
@pragma("vm:entry-point", "call")
(String, int) createRecordStringInt() => ("foo", 2);
@pragma("vm:entry-point", "call")
(int, String) createRecordIntString() => (1, "bar");
)";
@@ -2426,6 +2485,7 @@ ISOLATE_UNIT_TEST_CASE(TTS_Regress_CidRangeChecks) {
final x = 1;
}
@pragma("vm:entry-point", "call")
createI() => I();
)");
@@ -2470,10 +2530,14 @@ struct STCTestResults {
static STCTestResults SubtypeTestCacheTest(Thread* thread,
intptr_t num_classes) {
TextBuffer buffer(MB);
buffer.AddString("class D<S> {}\n");
buffer.AddString("D<int> Function() createClosureD() => () => D<int>();\n");
buffer.AddString(R"(
class D<S> {}
@pragma('vm:entry-point', 'call')
D<int> Function() createClosureD() => () => D<int>();
)");
for (intptr_t i = 0; i < num_classes; i++) {
buffer.Printf(R"(class C%)" Pd R"(<S> extends D<S> {}
@pragma('vm:entry-point', 'call')
C%)" Pd R"(<int> Function() createClosureC%)" Pd R"(() => () => C%)" Pd
R"(<int>();
)",