mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 14:43:32 +00:00
[vm/compiler] Handle far-ranches in TypeTestingStub generation.
Issue https://github.com/dart-lang/sdk/issues/45898 TEST=vm/dart{,_2}/regress_45898_test Change-Id: I5c7dfff0f7d4832e9c41f1aa2adaa332e163dfe6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/198040 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
This commit is contained in:
parent
c3683c79a2
commit
5a28a719f7
98
runtime/tests/vm/dart/regress_45898_test.dart
Normal file
98
runtime/tests/vm/dart/regress_45898_test.dart
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2021, 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:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'use_flag_test_helper.dart' show withTempDir, run;
|
||||
|
||||
final buildDir = path.dirname(Platform.executable);
|
||||
final platformDill = path.join(buildDir, 'vm_platform_strong.dill');
|
||||
final genSnapshot1 = path.join(buildDir, 'gen_snapshot');
|
||||
final genSnapshot2 = path.join('${buildDir}_X64', 'gen_snapshot');
|
||||
final genSnapshot =
|
||||
File(genSnapshot1).existsSync() ? genSnapshot1 : genSnapshot2;
|
||||
|
||||
const classCount = 10000;
|
||||
const subclassCount = 5000;
|
||||
|
||||
// Generates an example that causes generation of a TypeTestingStub checking for
|
||||
// 10000 classes - thereby making the TTS larger than 32 KB.
|
||||
//
|
||||
// We alternate classes to be subclasses of I0 and I1 to ensure that subclasses
|
||||
// of I0 do not have consecutive class ids.
|
||||
String generateExample() {
|
||||
final sb = StringBuffer()..writeln('''
|
||||
class I0 {}
|
||||
class I1 {}
|
||||
''');
|
||||
for (int i = 0; i < classCount; ++i) {
|
||||
sb.writeln('class S$i extends I${i % 2} {}');
|
||||
}
|
||||
sb.writeln('final all = <Object>[');
|
||||
for (int i = 0; i < classCount; ++i) {
|
||||
sb.writeln(' S$i(),');
|
||||
}
|
||||
sb.writeln('];');
|
||||
sb.writeln('''
|
||||
main() {
|
||||
int succeeded = 0;
|
||||
int failed = 0;
|
||||
for (dynamic obj in all) {
|
||||
try {
|
||||
obj as I0;
|
||||
succeeded++;
|
||||
} on TypeError catch (e, s) {
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
if (succeeded != $subclassCount ||
|
||||
failed != $subclassCount) {
|
||||
throw 'Error: succeeded: \$succeeded, failed: \$failed';
|
||||
}
|
||||
}
|
||||
''');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
void main(List<String> args) async {
|
||||
if (!Platform.isLinux) {
|
||||
// We want this test to run in (sim)arm, (sim)arm64 on Linux in JIT/AOT.
|
||||
// As written it wouldn't run on Windows / Android due to testing setup.
|
||||
return;
|
||||
}
|
||||
|
||||
final bool isAot = Platform.executable.contains('dart_precompiled_runtime');
|
||||
|
||||
await withTempDir('tts', (String temp) async {
|
||||
final script = path.join(temp, 'script.dart');
|
||||
await File(script).writeAsString(generateExample());
|
||||
|
||||
// We always compile to .dill file because simarm/simarm64 runs really slow
|
||||
// from source (and this dart2kernel compilation happens with checked-in
|
||||
// binaries).
|
||||
final scriptDill = path.join(temp, 'script.dart.dill');
|
||||
await run('pkg/vm/tool/gen_kernel', <String>[
|
||||
isAot ? '--aot' : '--no-aot',
|
||||
'--platform=$platformDill',
|
||||
'-o',
|
||||
scriptDill,
|
||||
script,
|
||||
]);
|
||||
|
||||
String mainFile = scriptDill;
|
||||
if (isAot) {
|
||||
final elfFile = path.join(temp, 'script.dart.dill.elf');
|
||||
await run(genSnapshot, <String>[
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$elfFile',
|
||||
scriptDill,
|
||||
]);
|
||||
mainFile = elfFile;
|
||||
}
|
||||
await run(Platform.executable, [mainFile]);
|
||||
});
|
||||
}
|
98
runtime/tests/vm/dart_2/regress_45898_test.dart
Normal file
98
runtime/tests/vm/dart_2/regress_45898_test.dart
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2021, 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:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'use_flag_test_helper.dart' show withTempDir, run;
|
||||
|
||||
final buildDir = path.dirname(Platform.executable);
|
||||
final platformDill = path.join(buildDir, 'vm_platform_strong.dill');
|
||||
final genSnapshot1 = path.join(buildDir, 'gen_snapshot');
|
||||
final genSnapshot2 = path.join('${buildDir}_X64', 'gen_snapshot');
|
||||
final genSnapshot =
|
||||
File(genSnapshot1).existsSync() ? genSnapshot1 : genSnapshot2;
|
||||
|
||||
const classCount = 10000;
|
||||
const subclassCount = 5000;
|
||||
|
||||
// Generates an example that causes generation of a TypeTestingStub checking for
|
||||
// 10000 classes - thereby making the TTS larger than 32 KB.
|
||||
//
|
||||
// We alternate classes to be subclasses of I0 and I1 to ensure that subclasses
|
||||
// of I0 do not have consecutive class ids.
|
||||
String generateExample() {
|
||||
final sb = StringBuffer()..writeln('''
|
||||
class I0 {}
|
||||
class I1 {}
|
||||
''');
|
||||
for (int i = 0; i < classCount; ++i) {
|
||||
sb.writeln('class S$i extends I${i % 2} {}');
|
||||
}
|
||||
sb.writeln('final all = <Object>[');
|
||||
for (int i = 0; i < classCount; ++i) {
|
||||
sb.writeln(' S$i(),');
|
||||
}
|
||||
sb.writeln('];');
|
||||
sb.writeln('''
|
||||
main() {
|
||||
int succeeded = 0;
|
||||
int failed = 0;
|
||||
for (dynamic obj in all) {
|
||||
try {
|
||||
obj as I0;
|
||||
succeeded++;
|
||||
} on TypeError catch (e, s) {
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
if (succeeded != $subclassCount ||
|
||||
failed != $subclassCount) {
|
||||
throw 'Error: succeeded: \$succeeded, failed: \$failed';
|
||||
}
|
||||
}
|
||||
''');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
void main(List<String> args) async {
|
||||
if (!Platform.isLinux) {
|
||||
// We want this test to run in (sim)arm, (sim)arm64 on Linux in JIT/AOT.
|
||||
// As written it wouldn't run on Windows / Android due to testing setup.
|
||||
return;
|
||||
}
|
||||
|
||||
final bool isAot = Platform.executable.contains('dart_precompiled_runtime');
|
||||
|
||||
await withTempDir('tts', (String temp) async {
|
||||
final script = path.join(temp, 'script.dart');
|
||||
await File(script).writeAsString(generateExample());
|
||||
|
||||
// We always compile to .dill file because simarm/simarm64 runs really slow
|
||||
// from source (and this dart2kernel compilation happens with checked-in
|
||||
// binaries).
|
||||
final scriptDill = path.join(temp, 'script.dart.dill');
|
||||
await run('pkg/vm/tool/gen_kernel', <String>[
|
||||
isAot ? '--aot' : '--no-aot',
|
||||
'--platform=$platformDill',
|
||||
'-o',
|
||||
scriptDill,
|
||||
script,
|
||||
]);
|
||||
|
||||
String mainFile = scriptDill;
|
||||
if (isAot) {
|
||||
final elfFile = path.join(temp, 'script.dart.dill.elf');
|
||||
await run(genSnapshot, <String>[
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$elfFile',
|
||||
scriptDill,
|
||||
]);
|
||||
mainFile = elfFile;
|
||||
}
|
||||
await run(Platform.executable, [mainFile]);
|
||||
});
|
||||
}
|
|
@ -284,7 +284,9 @@ dart_2/snapshot_depfile_test: SkipByDesign # Test needs to run from source
|
|||
|
||||
[ $compiler == dartkp && ($arch == simarm || $arch == simarm64 || $arch == simarm64c) ]
|
||||
dart/causal_stacks/async_throws_stack_lazy_non_symbolic_test: Pass, Slow
|
||||
dart/regress_45898_test: Pass, Slow
|
||||
dart_2/causal_stacks/async_throws_stack_lazy_non_symbolic_test: Pass, Slow
|
||||
dart_2/regress_45898_test: Pass, Slow
|
||||
|
||||
[ $compiler == dartkp && ($runtime == dart_precompiled || $runtime == vm) ]
|
||||
dart/redirection_type_shuffling_test: SkipByDesign # Includes dart:mirrors.
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
// 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.
|
||||
|
||||
#include "vm/type_testing_stubs.h"
|
||||
#include <functional>
|
||||
|
||||
#include "vm/compiler/assembler/disassembler.h"
|
||||
#include "vm/longjump.h"
|
||||
#include "vm/object_store.h"
|
||||
#include "vm/stub_code.h"
|
||||
#include "vm/timeline.h"
|
||||
#include "vm/type_testing_stubs.h"
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#include "vm/compiler/backend/flow_graph_compiler.h"
|
||||
|
@ -195,6 +198,37 @@ CodePtr TypeTestingStubGenerator::OptimizedCodeForType(
|
|||
#if !defined(TARGET_ARCH_IA32)
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_ARM64)
|
||||
#define ONLY_ON_ARM(...) __VA_ARGS__
|
||||
#else
|
||||
#define ONLY_ON_ARM(...)
|
||||
#endif
|
||||
|
||||
static CodePtr RetryCompilationWithFarBranches(
|
||||
Thread* thread,
|
||||
std::function<CodePtr(compiler::Assembler&)> fun) {
|
||||
bool use_far_branches = false;
|
||||
while (true) {
|
||||
LongJumpScope jump;
|
||||
if (setjmp(*jump.Set()) == 0) {
|
||||
// To use the already-defined __ Macro !
|
||||
compiler::Assembler assembler(nullptr ONLY_ON_ARM(, use_far_branches));
|
||||
return fun(assembler);
|
||||
} else {
|
||||
// We bailed out or we encountered an error.
|
||||
const Error& error = Error::Handle(thread->StealStickyError());
|
||||
if (error.ptr() == Object::branch_offset_error().ptr()) {
|
||||
ASSERT(!use_far_branches);
|
||||
use_far_branches = true;
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef ONLY_ON_ARM
|
||||
|
||||
CodePtr TypeTestingStubGenerator::BuildCodeForType(const Type& type) {
|
||||
auto thread = Thread::Current();
|
||||
auto zone = thread->zone();
|
||||
|
@ -214,55 +248,64 @@ CodePtr TypeTestingStubGenerator::BuildCodeForType(const Type& type) {
|
|||
slow_tts_stub = thread->isolate_group()->object_store()->slow_tts_stub();
|
||||
}
|
||||
|
||||
// To use the already-defined __ Macro !
|
||||
compiler::Assembler assembler(nullptr);
|
||||
compiler::UnresolvedPcRelativeCalls unresolved_calls;
|
||||
BuildOptimizedTypeTestStub(&assembler, &unresolved_calls, slow_tts_stub, hi,
|
||||
type, type_class);
|
||||
const Code& code = Code::Handle(
|
||||
thread->zone(),
|
||||
RetryCompilationWithFarBranches(
|
||||
thread, [&](compiler::Assembler& assembler) {
|
||||
compiler::UnresolvedPcRelativeCalls unresolved_calls;
|
||||
BuildOptimizedTypeTestStub(&assembler, &unresolved_calls,
|
||||
slow_tts_stub, hi, type, type_class);
|
||||
|
||||
const auto& static_calls_table =
|
||||
Array::Handle(zone, compiler::StubCodeCompiler::BuildStaticCallsTable(
|
||||
zone, &unresolved_calls));
|
||||
const auto& static_calls_table = Array::Handle(
|
||||
zone, compiler::StubCodeCompiler::BuildStaticCallsTable(
|
||||
zone, &unresolved_calls));
|
||||
|
||||
const char* name = namer_.StubNameForType(type);
|
||||
const auto pool_attachment = FLAG_use_bare_instructions
|
||||
? Code::PoolAttachment::kNotAttachPool
|
||||
: Code::PoolAttachment::kAttachPool;
|
||||
const char* name = namer_.StubNameForType(type);
|
||||
const auto pool_attachment =
|
||||
FLAG_use_bare_instructions
|
||||
? Code::PoolAttachment::kNotAttachPool
|
||||
: Code::PoolAttachment::kAttachPool;
|
||||
|
||||
Code& code = Code::Handle(thread->zone());
|
||||
auto install_code_fun = [&]() {
|
||||
code = Code::FinalizeCode(nullptr, &assembler, pool_attachment,
|
||||
/*optimized=*/false, /*stats=*/nullptr);
|
||||
if (!static_calls_table.IsNull()) {
|
||||
code.set_static_calls_target_table(static_calls_table);
|
||||
}
|
||||
};
|
||||
Code& code = Code::Handle(thread->zone());
|
||||
auto install_code_fun = [&]() {
|
||||
code = Code::FinalizeCode(nullptr, &assembler, pool_attachment,
|
||||
/*optimized=*/false, /*stats=*/nullptr);
|
||||
if (!static_calls_table.IsNull()) {
|
||||
code.set_static_calls_target_table(static_calls_table);
|
||||
}
|
||||
};
|
||||
|
||||
// We have to ensure no mutators are running, because:
|
||||
//
|
||||
// a) We allocate an instructions object, which might cause us to
|
||||
// temporarily flip page protections from (RX -> RW -> RX).
|
||||
//
|
||||
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
|
||||
thread->isolate_group()->RunWithStoppedMutators(install_code_fun,
|
||||
/*use_force_growth=*/true);
|
||||
// We have to ensure no mutators are running, because:
|
||||
//
|
||||
// a) We allocate an instructions object, which might cause us to
|
||||
// temporarily flip page protections from (RX -> RW -> RX).
|
||||
//
|
||||
SafepointWriteRwLocker ml(thread,
|
||||
thread->isolate_group()->program_lock());
|
||||
thread->isolate_group()->RunWithStoppedMutators(
|
||||
install_code_fun,
|
||||
/*use_force_growth=*/true);
|
||||
|
||||
Code::NotifyCodeObservers(name, code, /*optimized=*/false);
|
||||
Code::NotifyCodeObservers(name, code, /*optimized=*/false);
|
||||
|
||||
code.set_owner(type);
|
||||
code.set_owner(type);
|
||||
#ifndef PRODUCT
|
||||
if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
|
||||
LogBlock lb;
|
||||
THR_Print("Code for stub '%s' (type = %s): {\n", name, type.ToCString());
|
||||
DisassembleToStdout formatter;
|
||||
code.Disassemble(&formatter);
|
||||
THR_Print("}\n");
|
||||
const ObjectPool& object_pool = ObjectPool::Handle(code.object_pool());
|
||||
if (!object_pool.IsNull()) {
|
||||
object_pool.DebugPrint();
|
||||
}
|
||||
}
|
||||
if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
|
||||
LogBlock lb;
|
||||
THR_Print("Code for stub '%s' (type = %s): {\n", name,
|
||||
type.ToCString());
|
||||
DisassembleToStdout formatter;
|
||||
code.Disassemble(&formatter);
|
||||
THR_Print("}\n");
|
||||
const ObjectPool& object_pool =
|
||||
ObjectPool::Handle(code.object_pool());
|
||||
if (!object_pool.IsNull()) {
|
||||
object_pool.DebugPrint();
|
||||
}
|
||||
}
|
||||
#endif // !PRODUCT
|
||||
return code.ptr();
|
||||
}));
|
||||
|
||||
return code.ptr();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue