Version 3.3.0-88.0.dev
Merge 1afa57fc484cb35b10609e2e95e92c14f9ab3d3f into dev
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index 79a54ee..96b0017 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -2816,15 +2816,29 @@
}
}
}
- if (formals == null &&
- declarationContext == DeclarationContext.ExtensionType &&
- kind == MemberKind.PrimaryConstructor) {
+ if (declarationContext == DeclarationContext.ExtensionType &&
+ kind == MemberKind.PrimaryConstructor &&
+ formals == null) {
// In case of primary constructors of extension types, an error is
// reported by the parser if the formals together with the parentheses
// around them are missing. To distinguish that case from the case of the
// formal parameters present, but lacking the representation field, we
// pass the empty list further along instead of `null`.
formals = const [];
+ } else if ((declarationContext == DeclarationContext.ExtensionType &&
+ kind == MemberKind.PrimaryConstructor ||
+ declarationContext ==
+ DeclarationContext.ExtensionTypeConstructor) &&
+ formals != null) {
+ for (FormalParameterBuilder formal in formals) {
+ if (formal.isSuperInitializingFormal) {
+ libraryBuilder.addProblem(
+ messageExtensionTypeConstructorWithSuperFormalParameter,
+ formal.charOffset,
+ formal.name.length,
+ formal.fileUri);
+ }
+ }
}
push(beginToken.charOffset);
push(formals ?? NullValues.FormalParameters);
diff --git a/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart b/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart
index efebf90..66b36a6 100644
--- a/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart
@@ -35,7 +35,6 @@
show
LocatedMessage,
Message,
- messageExtensionTypeConstructorWithSuperFormalParameter,
messageMoreThanOneSuperInitializer,
messageRedirectingConstructorWithAnotherInitializer,
messageRedirectingConstructorWithMultipleRedirectInitializers,
@@ -1176,11 +1175,6 @@
if (formal.isSuperInitializingFormal) {
TypeBuilder formalTypeBuilder = formal.type;
if (formalTypeBuilder is InferableTypeBuilder) {
- libraryBuilder.addProblem(
- messageExtensionTypeConstructorWithSuperFormalParameter,
- formal.charOffset,
- formal.name.length,
- formal.fileUri);
formalTypeBuilder.registerType(const InvalidType());
}
}
diff --git a/pkg/front_end/testcases/extension_types/issue53212.dart b/pkg/front_end/testcases/extension_types/issue53212.dart
index 67e465d..008a0b9 100644
--- a/pkg/front_end/testcases/extension_types/issue53212.dart
+++ b/pkg/front_end/testcases/extension_types/issue53212.dart
@@ -13,3 +13,5 @@
extension type E3(int foo) {
E3.named(this.foo, [super.bar = null]);
}
+
+extension type E4(super.foo) {} // Error.
diff --git a/pkg/front_end/testcases/extension_types/issue53212.dart.strong.expect b/pkg/front_end/testcases/extension_types/issue53212.dart.strong.expect
index 88f11ab..16b5101 100644
--- a/pkg/front_end/testcases/extension_types/issue53212.dart.strong.expect
+++ b/pkg/front_end/testcases/extension_types/issue53212.dart.strong.expect
@@ -14,6 +14,14 @@
// E3.named(this.foo, [super.bar = null]);
// ^^^
//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Extension type constructors can't declare super formal parameters.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Expected a representation type.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
import self as self;
import "dart:core" as core;
@@ -38,6 +46,11 @@
constructor named = self::E3|constructor#named;
constructor tearoff named = self::E3|constructor#_#named#tearOff;
}
+extension type E4(dynamic foo) {
+ abstract inline-class-member representation-field get foo() → dynamic;
+ constructor • = self::E4|constructor#;
+ constructor tearoff • = self::E4|constructor#_#new#tearOff;
+}
static inline-class-member method E1|constructor#(core::int foo) → self::E1 /* = core::int */ {
lowered final self::E1 /* = core::int */ #this = foo;
return #this;
@@ -74,6 +87,12 @@
}
static inline-class-member method E3|constructor#_#named#tearOff(core::int foo, [has-declared-initializer invalid-type bar]) → self::E3 /* = core::int */
return self::E3|constructor#named(foo, bar);
+static inline-class-member method E4|constructor#(dynamic foo) → self::E4 /* = dynamic */ {
+ lowered final self::E4 /* = dynamic */ #this = foo;
+ return #this;
+}
+static inline-class-member method E4|constructor#_#new#tearOff(dynamic foo) → self::E4 /* = dynamic */
+ return self::E4|constructor#(foo);
constants {
#C1 = null
diff --git a/pkg/front_end/testcases/extension_types/issue53212.dart.strong.transformed.expect b/pkg/front_end/testcases/extension_types/issue53212.dart.strong.transformed.expect
index 88f11ab..16b5101 100644
--- a/pkg/front_end/testcases/extension_types/issue53212.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/extension_types/issue53212.dart.strong.transformed.expect
@@ -14,6 +14,14 @@
// E3.named(this.foo, [super.bar = null]);
// ^^^
//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Extension type constructors can't declare super formal parameters.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Expected a representation type.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
import self as self;
import "dart:core" as core;
@@ -38,6 +46,11 @@
constructor named = self::E3|constructor#named;
constructor tearoff named = self::E3|constructor#_#named#tearOff;
}
+extension type E4(dynamic foo) {
+ abstract inline-class-member representation-field get foo() → dynamic;
+ constructor • = self::E4|constructor#;
+ constructor tearoff • = self::E4|constructor#_#new#tearOff;
+}
static inline-class-member method E1|constructor#(core::int foo) → self::E1 /* = core::int */ {
lowered final self::E1 /* = core::int */ #this = foo;
return #this;
@@ -74,6 +87,12 @@
}
static inline-class-member method E3|constructor#_#named#tearOff(core::int foo, [has-declared-initializer invalid-type bar]) → self::E3 /* = core::int */
return self::E3|constructor#named(foo, bar);
+static inline-class-member method E4|constructor#(dynamic foo) → self::E4 /* = dynamic */ {
+ lowered final self::E4 /* = dynamic */ #this = foo;
+ return #this;
+}
+static inline-class-member method E4|constructor#_#new#tearOff(dynamic foo) → self::E4 /* = dynamic */
+ return self::E4|constructor#(foo);
constants {
#C1 = null
diff --git a/pkg/front_end/testcases/extension_types/issue53212.dart.textual_outline.expect b/pkg/front_end/testcases/extension_types/issue53212.dart.textual_outline.expect
index 4097d32..962d9bd 100644
--- a/pkg/front_end/testcases/extension_types/issue53212.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/extension_types/issue53212.dart.textual_outline.expect
@@ -7,3 +7,4 @@
extension type E3(int foo) {
E3.named(this.foo, [super.bar = null]);
}
+extension type E4(super.foo) {}
diff --git a/pkg/front_end/testcases/extension_types/issue53212.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/extension_types/issue53212.dart.textual_outline_modelled.expect
index 4097d32..962d9bd 100644
--- a/pkg/front_end/testcases/extension_types/issue53212.dart.textual_outline_modelled.expect
+++ b/pkg/front_end/testcases/extension_types/issue53212.dart.textual_outline_modelled.expect
@@ -7,3 +7,4 @@
extension type E3(int foo) {
E3.named(this.foo, [super.bar = null]);
}
+extension type E4(super.foo) {}
diff --git a/pkg/front_end/testcases/extension_types/issue53212.dart.weak.expect b/pkg/front_end/testcases/extension_types/issue53212.dart.weak.expect
index 88f11ab..16b5101 100644
--- a/pkg/front_end/testcases/extension_types/issue53212.dart.weak.expect
+++ b/pkg/front_end/testcases/extension_types/issue53212.dart.weak.expect
@@ -14,6 +14,14 @@
// E3.named(this.foo, [super.bar = null]);
// ^^^
//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Extension type constructors can't declare super formal parameters.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Expected a representation type.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
import self as self;
import "dart:core" as core;
@@ -38,6 +46,11 @@
constructor named = self::E3|constructor#named;
constructor tearoff named = self::E3|constructor#_#named#tearOff;
}
+extension type E4(dynamic foo) {
+ abstract inline-class-member representation-field get foo() → dynamic;
+ constructor • = self::E4|constructor#;
+ constructor tearoff • = self::E4|constructor#_#new#tearOff;
+}
static inline-class-member method E1|constructor#(core::int foo) → self::E1 /* = core::int */ {
lowered final self::E1 /* = core::int */ #this = foo;
return #this;
@@ -74,6 +87,12 @@
}
static inline-class-member method E3|constructor#_#named#tearOff(core::int foo, [has-declared-initializer invalid-type bar]) → self::E3 /* = core::int */
return self::E3|constructor#named(foo, bar);
+static inline-class-member method E4|constructor#(dynamic foo) → self::E4 /* = dynamic */ {
+ lowered final self::E4 /* = dynamic */ #this = foo;
+ return #this;
+}
+static inline-class-member method E4|constructor#_#new#tearOff(dynamic foo) → self::E4 /* = dynamic */
+ return self::E4|constructor#(foo);
constants {
#C1 = null
diff --git a/pkg/front_end/testcases/extension_types/issue53212.dart.weak.modular.expect b/pkg/front_end/testcases/extension_types/issue53212.dart.weak.modular.expect
index 88f11ab..16b5101 100644
--- a/pkg/front_end/testcases/extension_types/issue53212.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/extension_types/issue53212.dart.weak.modular.expect
@@ -14,6 +14,14 @@
// E3.named(this.foo, [super.bar = null]);
// ^^^
//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Extension type constructors can't declare super formal parameters.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Expected a representation type.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
import self as self;
import "dart:core" as core;
@@ -38,6 +46,11 @@
constructor named = self::E3|constructor#named;
constructor tearoff named = self::E3|constructor#_#named#tearOff;
}
+extension type E4(dynamic foo) {
+ abstract inline-class-member representation-field get foo() → dynamic;
+ constructor • = self::E4|constructor#;
+ constructor tearoff • = self::E4|constructor#_#new#tearOff;
+}
static inline-class-member method E1|constructor#(core::int foo) → self::E1 /* = core::int */ {
lowered final self::E1 /* = core::int */ #this = foo;
return #this;
@@ -74,6 +87,12 @@
}
static inline-class-member method E3|constructor#_#named#tearOff(core::int foo, [has-declared-initializer invalid-type bar]) → self::E3 /* = core::int */
return self::E3|constructor#named(foo, bar);
+static inline-class-member method E4|constructor#(dynamic foo) → self::E4 /* = dynamic */ {
+ lowered final self::E4 /* = dynamic */ #this = foo;
+ return #this;
+}
+static inline-class-member method E4|constructor#_#new#tearOff(dynamic foo) → self::E4 /* = dynamic */
+ return self::E4|constructor#(foo);
constants {
#C1 = null
diff --git a/pkg/front_end/testcases/extension_types/issue53212.dart.weak.outline.expect b/pkg/front_end/testcases/extension_types/issue53212.dart.weak.outline.expect
index 292f458..afb8f67 100644
--- a/pkg/front_end/testcases/extension_types/issue53212.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/extension_types/issue53212.dart.weak.outline.expect
@@ -14,6 +14,14 @@
// E3.named(this.foo, [super.bar = null]);
// ^^^
//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Extension type constructors can't declare super formal parameters.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Expected a representation type.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
import self as self;
import "dart:core" as core;
@@ -38,6 +46,11 @@
constructor named = self::E3|constructor#named;
constructor tearoff named = self::E3|constructor#_#named#tearOff;
}
+extension type E4(dynamic foo) {
+ abstract inline-class-member representation-field get foo() → dynamic;
+ constructor • = self::E4|constructor#;
+ constructor tearoff • = self::E4|constructor#_#new#tearOff;
+}
static inline-class-member method E1|constructor#(core::int foo) → self::E1 /* = core::int */
;
static inline-class-member method E1|constructor#_#new#tearOff(core::int foo) → self::E1 /* = core::int */
@@ -62,3 +75,7 @@
;
static inline-class-member method E3|constructor#_#named#tearOff(core::int foo, [has-declared-initializer invalid-type bar]) → self::E3 /* = core::int */
return self::E3|constructor#named(foo, bar);
+static inline-class-member method E4|constructor#(dynamic foo) → self::E4 /* = dynamic */
+ ;
+static inline-class-member method E4|constructor#_#new#tearOff(dynamic foo) → self::E4 /* = dynamic */
+ return self::E4|constructor#(foo);
diff --git a/pkg/front_end/testcases/extension_types/issue53212.dart.weak.transformed.expect b/pkg/front_end/testcases/extension_types/issue53212.dart.weak.transformed.expect
index 88f11ab..16b5101 100644
--- a/pkg/front_end/testcases/extension_types/issue53212.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/extension_types/issue53212.dart.weak.transformed.expect
@@ -14,6 +14,14 @@
// E3.named(this.foo, [super.bar = null]);
// ^^^
//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Extension type constructors can't declare super formal parameters.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
+// pkg/front_end/testcases/extension_types/issue53212.dart:17:25: Error: Expected a representation type.
+// extension type E4(super.foo) {} // Error.
+// ^^^
+//
import self as self;
import "dart:core" as core;
@@ -38,6 +46,11 @@
constructor named = self::E3|constructor#named;
constructor tearoff named = self::E3|constructor#_#named#tearOff;
}
+extension type E4(dynamic foo) {
+ abstract inline-class-member representation-field get foo() → dynamic;
+ constructor • = self::E4|constructor#;
+ constructor tearoff • = self::E4|constructor#_#new#tearOff;
+}
static inline-class-member method E1|constructor#(core::int foo) → self::E1 /* = core::int */ {
lowered final self::E1 /* = core::int */ #this = foo;
return #this;
@@ -74,6 +87,12 @@
}
static inline-class-member method E3|constructor#_#named#tearOff(core::int foo, [has-declared-initializer invalid-type bar]) → self::E3 /* = core::int */
return self::E3|constructor#named(foo, bar);
+static inline-class-member method E4|constructor#(dynamic foo) → self::E4 /* = dynamic */ {
+ lowered final self::E4 /* = dynamic */ #this = foo;
+ return #this;
+}
+static inline-class-member method E4|constructor#_#new#tearOff(dynamic foo) → self::E4 /* = dynamic */
+ return self::E4|constructor#(foo);
constants {
#C1 = null
diff --git a/runtime/tests/vm/dart/address_local_pointer_il_test.dart b/runtime/tests/vm/dart/address_local_pointer_il_test.dart
index 0842756..eb290f5 100644
--- a/runtime/tests/vm/dart/address_local_pointer_il_test.dart
+++ b/runtime/tests/vm/dart/address_local_pointer_il_test.dart
@@ -17,32 +17,21 @@
int identity(int address) => Pointer<Void>.fromAddress(address).address;
void matchIL$identity(FlowGraph graph) {
- graph.dump();
- if (is32BitConfiguration) {
- // The Dart int address is truncated before being returned.
- graph.match([
- match.block('Graph'),
- match.block('Function', [
- 'address' << match.Parameter(index: 0),
- 'int32' <<
- match.IntConverter('address',
- from: 'int64', to: 'int32', is_truncating: true),
+ final retval = is32BitConfiguration ? 'retval' : 'address';
+ graph.match([
+ match.block('Graph'),
+ match.block('Function', [
+ 'address' << match.Parameter(index: 0),
+ if (is32BitConfiguration) ...[
+ // The Dart int address is truncated before being returned.
'uint32' <<
- match.IntConverter('int32',
- from: 'int32', to: 'uint32', is_truncating: true),
+ match.IntConverter('address',
+ from: 'int64', to: 'uint32', is_truncating: true),
'retval' << match.IntConverter('uint32', from: 'uint32', to: 'int64'),
- match.Return('retval'),
- ]),
- ]);
- } else {
- graph.match([
- match.block('Graph'),
- match.block('Function', [
- 'address' << match.Parameter(index: 0),
- match.Return('address'),
- ]),
- ]);
- }
+ ],
+ match.Return(retval),
+ ]),
+ ]);
}
void main(List<String> args) {
diff --git a/runtime/tests/vm/dart/regress_306327173_il_test.dart b/runtime/tests/vm/dart/regress_306327173_il_test.dart
index a9206d3e..e3c22ff 100644
--- a/runtime/tests/vm/dart/regress_306327173_il_test.dart
+++ b/runtime/tests/vm/dart/regress_306327173_il_test.dart
@@ -30,12 +30,7 @@
// and int64 on 64-bit arches.
if (is32BitConfiguration) ...[
// 'unboxed' needs to be converted to int64 before returning.
- //
- // Note: The first two conversions here should be fixed once all
- // kUnboxedIntPtr uses are appropriately converted to kUnboxedFfiIntPtr.
- 'extra1' << match.IntConverter('unboxed', from: 'uint32', to: 'int32'),
- 'extra2' << match.IntConverter('extra1', from: 'int32', to: 'uint32'),
- 'address' << match.IntConverter('extra2', from: 'uint32', to: 'int64'),
+ 'address' << match.IntConverter('unboxed', from: 'uint32', to: 'int64'),
],
match.Return(retvalName),
]),
diff --git a/runtime/tests/vm/dart/unsigned_truncated_division_il_test.dart b/runtime/tests/vm/dart/unsigned_truncated_division_il_test.dart
new file mode 100644
index 0000000..e1c1154
--- /dev/null
+++ b/runtime/tests/vm/dart/unsigned_truncated_division_il_test.dart
@@ -0,0 +1,60 @@
+// 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.
+
+// Verify that we don't leave in binary int operations that do not throw and
+// have a calculated range of a single value, but instead replace the operation
+// with that constant value.
+
+import 'package:expect/expect.dart';
+import 'package:vm/testing/il_matchers.dart';
+
+@pragma('vm:never-inline')
+@pragma('vm:testing:print-flow-graph')
+int testUnsignedTruncatingDivision(Iterable i) => i.length ~/ 32;
+
+void matchIL$testUnsignedTruncatingDivision(FlowGraph graph) {
+ graph.match([
+ match.block('Graph', [
+ if (is32BitConfiguration) ...[
+ 'c5' << match.UnboxedConstant(value: 5, representation: 'int32'),
+ ] else ...[
+ 'c32' << match.UnboxedConstant(value: 32, representation: 'int64'),
+ ],
+ ]),
+ match.block('Function', [
+ 'it' << match.Parameter(index: 0),
+ 'len' << match.LoadField('it', slot: 'GrowableObjectArray.length'),
+ if (is32BitConfiguration) ...[
+ // 32-bit architectures don't handle 64-bit truncated division natively.
+ // However, for powers of two, the runtime call
+ // m ~/ 2^n
+ // gets replaced with
+ // (m + ((m >> 63) & (2^n - 1))) >> n
+ // which works for both signed and unsigned values.
+ //
+ // In this specific case, with m = len and 2^n = 32,
+ // (len + ((len >> 63) & 31)) >> 5
+ // However, the numerator len has a non-negative range since it's
+ // retrieved from the length slot of an Iterable. This means (len >> 63)
+ // is guaranteed to be 0, and the compiler should simplify this to
+ // len >> 5
+ 'unboxed_len' << match.UnboxInt32('len'),
+ 'retval_32' << match.BinaryInt32Op('unboxed_len', 'c5', op_kind: '>>'),
+ 'retval' << match.IntConverter('retval_32', from: 'int32', to: 'int64'),
+ ] else ...[
+ // 64-bit architectures do handle 64-bit truncated division natively,
+ // so it's just a single operation there.
+ 'unboxed_len' << match.UnboxInt64('len'),
+ 'retval' << match.BinaryInt64Op('unboxed_len', 'c32', op_kind: '~/'),
+ ],
+ match.Return('retval'),
+ ]),
+ ]);
+}
+
+void main(List<String> args) {
+ final len = args.isEmpty ? 100 : int.parse(args.first);
+ final list = List.generate(len, (i) => len - i);
+ Expect.equals(len ~/ 32, testUnsignedTruncatingDivision(list));
+}
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index a99cef7..c2c1bca 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -1521,6 +1521,12 @@
other->env()->DeepCopyTo(zone, this);
}
+bool Instruction::CanEliminate(const BlockEntryInstr* block) const {
+ ASSERT(const_cast<Instruction*>(this)->GetBlock() == block);
+ return !MayHaveVisibleEffect() && !CanDeoptimize() &&
+ this != block->last_instruction();
+}
+
bool Instruction::IsDominatedBy(Instruction* dom) {
BlockEntryInstr* block = GetBlock();
BlockEntryInstr* dom_block = dom->GetBlock();
@@ -1980,41 +1986,25 @@
RangeBoundary::kRangeBoundaryInt32);
}
-bool UnboxInt32Instr::ComputeCanDeoptimize() const {
+bool UnboxIntegerInstr::ComputeCanDeoptimize() const {
if (SpeculativeModeOfInputs() == kNotSpeculative) {
return false;
}
- const intptr_t value_cid = value()->Type()->ToCid();
- if (value_cid == kSmiCid) {
- return (compiler::target::kSmiBits > 32) && !is_truncating() &&
- !RangeUtils::Fits(value()->definition()->range(),
- RangeBoundary::kRangeBoundaryInt32);
- } else if (value_cid == kMintCid) {
- return !is_truncating() &&
- !RangeUtils::Fits(value()->definition()->range(),
- RangeBoundary::kRangeBoundaryInt32);
- } else if (is_truncating() && value()->definition()->IsBoxInteger()) {
- return false;
- } else if ((compiler::target::kSmiBits < 32) && value()->Type()->IsInt()) {
- return !RangeUtils::Fits(value()->definition()->range(),
- RangeBoundary::kRangeBoundaryInt32);
- } else {
+ if (!value()->Type()->IsInt()) {
return true;
}
-}
-
-bool UnboxUint32Instr::ComputeCanDeoptimize() const {
- ASSERT(is_truncating());
- if (SpeculativeModeOfInputs() == kNotSpeculative) {
+ if (representation() == kUnboxedInt64 || is_truncating()) {
return false;
}
- if ((value()->Type()->ToCid() == kSmiCid) ||
- (value()->Type()->ToCid() == kMintCid)) {
+ const intptr_t rep_bitsize =
+ RepresentationUtils::ValueSize(representation()) * kBitsPerByte;
+ if (value()->Type()->ToCid() == kSmiCid &&
+ compiler::target::kSmiBits <= rep_bitsize) {
return false;
}
- // Check input value's range.
- Range* value_range = value()->definition()->range();
- return !RangeUtils::Fits(value_range, RangeBoundary::kRangeBoundaryInt64);
+ return !RangeUtils::IsWithin(value()->definition()->range(),
+ RepresentationUtils::MinValue(representation()),
+ RepresentationUtils::MaxValue(representation()));
}
bool BinaryInt32OpInstr::ComputeCanDeoptimize() const {
@@ -2072,9 +2062,18 @@
return RangeUtils::IsWithin(shift_range(), 0, max);
}
+bool BinaryIntegerOpInstr::RightIsNonZero() const {
+ if (right()->BindsToConstant()) {
+ const auto& constant = right()->BoundConstant();
+ if (!constant.IsInteger()) return false;
+ return Integer::Cast(constant).AsInt64Value() != 0;
+ }
+ return !RangeUtils::CanBeZero(right()->definition()->range());
+}
+
bool BinaryIntegerOpInstr::RightIsPowerOfTwoConstant() const {
- if (!right()->definition()->IsConstant()) return false;
- const Object& constant = right()->definition()->AsConstant()->value();
+ if (!right()->BindsToConstant()) return false;
+ const Object& constant = right()->BoundConstant();
if (!constant.IsSmi()) return false;
const intptr_t int_value = Smi::Cast(constant).Value();
ASSERT(int_value != kIntptrMin);
@@ -2340,7 +2339,35 @@
return op;
}
+Definition* UnaryIntegerOpInstr::Canonicalize(FlowGraph* flow_graph) {
+ // If range analysis has already determined a single possible value for
+ // this operation, then replace it if possible.
+ if (RangeUtils::IsSingleton(range()) && CanReplaceWithConstant()) {
+ const auto& value =
+ Integer::Handle(Integer::NewCanonical(range()->Singleton()));
+ auto* const replacement =
+ flow_graph->TryCreateConstantReplacementFor(this, value);
+ if (replacement != this) {
+ return replacement;
+ }
+ }
+
+ return this;
+}
+
Definition* BinaryIntegerOpInstr::Canonicalize(FlowGraph* flow_graph) {
+ // If range analysis has already determined a single possible value for
+ // this operation, then replace it if possible.
+ if (RangeUtils::IsSingleton(range()) && CanReplaceWithConstant()) {
+ const auto& value =
+ Integer::Handle(Integer::NewCanonical(range()->Singleton()));
+ auto* const replacement =
+ flow_graph->TryCreateConstantReplacementFor(this, value);
+ if (replacement != this) {
+ return replacement;
+ }
+ }
+
// If both operands are constants evaluate this expression. Might
// occur due to load forwarding after constant propagation pass
// have already been run.
@@ -3306,40 +3333,24 @@
set_speculative_mode(kNotSpeculative);
}
- return this;
-}
-
-Definition* UnboxInt32Instr::Canonicalize(FlowGraph* flow_graph) {
- Definition* replacement = UnboxIntegerInstr::Canonicalize(flow_graph);
- if (replacement != this) {
- return replacement;
- }
-
- ConstantInstr* c = value()->definition()->AsConstant();
- if ((c != nullptr) && c->value().IsInteger()) {
- if (!is_truncating()) {
- // Check that constant fits into 32-bit integer.
- const int64_t value = Integer::Cast(c->value()).AsInt64Value();
- if (!Utils::IsInt(32, value)) {
- return this;
+ if (value()->BindsToConstant()) {
+ const auto& obj = value()->BoundConstant();
+ if (obj.IsInteger()) {
+ if (representation() == kUnboxedInt64) {
+ return flow_graph->GetConstant(obj, representation());
+ }
+ const int64_t intval = Integer::Cast(obj).AsInt64Value();
+ if (RepresentationUtils::IsRepresentable(representation(), intval)) {
+ return flow_graph->GetConstant(obj, representation());
+ }
+ if (is_truncating()) {
+ const int64_t result = Evaluator::TruncateTo(intval, representation());
+ return flow_graph->GetConstant(
+ Integer::ZoneHandle(flow_graph->zone(),
+ Integer::NewCanonical(result)),
+ representation());
}
}
-
- return flow_graph->GetConstant(c->value(), kUnboxedInt32);
- }
-
- return this;
-}
-
-Definition* UnboxInt64Instr::Canonicalize(FlowGraph* flow_graph) {
- Definition* replacement = UnboxIntegerInstr::Canonicalize(flow_graph);
- if (replacement != this) {
- return replacement;
- }
-
- ConstantInstr* c = value()->definition()->AsConstant();
- if (c != nullptr && c->value().IsInteger()) {
- return flow_graph->GetConstant(c->value(), kUnboxedInt64);
}
return this;
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index a825904..fa72095 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -1334,6 +1334,17 @@
virtual bool MayThrow() const = 0;
+ // Returns true if instruction may have a "visible" effect,
+ virtual bool MayHaveVisibleEffect() const {
+ return HasUnknownSideEffects() || MayThrow();
+ }
+
+ // Returns true if this instruction can be eliminated if its result is not
+ // used without changing the behavior of the program. For Definitions,
+ // overwrite CanReplaceWithConstant() instead.
+ virtual bool CanEliminate(const BlockEntryInstr* block) const;
+ bool CanEliminate() { return CanEliminate(GetBlock()); }
+
bool IsDominatedBy(Instruction* dom);
void ClearEnv() { env_ = nullptr; }
@@ -2546,6 +2557,18 @@
void AddInputUse(Value* value) { Value::AddToList(value, &input_use_list_); }
void AddEnvUse(Value* value) { Value::AddToList(value, &env_use_list_); }
+ // Returns true if the definition can be replaced with a constant without
+ // changing the behavior of the program.
+ virtual bool CanReplaceWithConstant() const {
+ return !MayHaveVisibleEffect() && !CanDeoptimize();
+ }
+
+ virtual bool CanEliminate(const BlockEntryInstr* block) const {
+ // Basic blocks should not end in a definition, so treat this as replacing
+ // the definition with a constant (that is then unused).
+ return CanReplaceWithConstant();
+ }
+
// Replace uses of this definition with uses of other definition or value.
// Precondition: use lists must be properly calculated.
// Postcondition: use lists and use values are still valid.
@@ -2998,6 +3021,7 @@
}
virtual bool ComputeCanDeoptimize() const { return false; }
virtual bool HasUnknownSideEffects() const { return false; }
+ virtual bool MayHaveVisibleEffect() const { return true; }
virtual bool AttributesEqual(const Instruction& other) const {
return other.AsStoreIndexedUnsafe()->offset() == offset();
@@ -4053,6 +4077,10 @@
virtual bool ComputeCanDeoptimize() const { return false; }
virtual bool HasUnknownSideEffects() const { return false; }
+ virtual bool CanEliminate(const BlockEntryInstr* block) const {
+ return false;
+ }
+
PRINT_OPERANDS_TO_SUPPORT
DECLARE_EMPTY_SERIALIZATION(ReachabilityFenceInstr, TemplateInstruction)
@@ -4184,6 +4212,9 @@
DECLARE_INSTRUCTION(UnboxedConstant)
DECLARE_CUSTOM_SERIALIZATION(UnboxedConstantInstr)
+ DECLARE_ATTRIBUTES_NAMED(("value", "representation"),
+ (&value(), representation()))
+
private:
const Representation representation_;
uword
@@ -6160,6 +6191,10 @@
virtual bool ComputeCanDeoptimize() const { return false; }
virtual bool HasUnknownSideEffects() const { return false; }
+ virtual bool CanEliminate(const BlockEntryInstr* block) const {
+ return false;
+ }
+
#define FIELD_LIST(F) F(const int32_t, offset_)
DECLARE_INSTRUCTION_SERIALIZABLE_FIELDS(RawStoreFieldInstr,
@@ -6379,6 +6414,8 @@
// are marked as having no side-effects.
virtual bool HasUnknownSideEffects() const { return false; }
+ virtual bool MayHaveVisibleEffect() const { return true; }
+
virtual Representation RequiredInputRepresentation(intptr_t index) const;
virtual Instruction* Canonicalize(FlowGraph* flow_graph);
@@ -6643,6 +6680,8 @@
// are marked as having no side-effects.
virtual bool HasUnknownSideEffects() const { return false; }
+ virtual bool MayHaveVisibleEffect() const { return true; }
+
virtual TokenPosition token_pos() const { return token_pos_; }
PRINT_OPERANDS_TO_SUPPORT
@@ -7033,6 +7072,8 @@
virtual bool HasUnknownSideEffects() const { return false; }
+ virtual bool MayHaveVisibleEffect() const { return true; }
+
void PrintOperandsTo(BaseTextBuffer* f) const;
virtual Instruction* Canonicalize(FlowGraph* flow_graph);
@@ -7597,6 +7638,7 @@
virtual bool ComputeCanDeoptimize() const { return false; }
virtual bool HasUnknownSideEffects() const { return false; }
+ virtual bool CanReplaceWithConstant() const { return false; }
Location* locations() { return locations_; }
void set_locations(Location* locations) { locations_ = locations; }
@@ -8511,6 +8553,8 @@
void mark_truncating() { is_truncating_ = true; }
+ virtual bool ComputeCanDeoptimize() const;
+
virtual CompileType ComputeType() const;
virtual bool AttributesEqual(const Instruction& other) const {
@@ -8521,6 +8565,8 @@
virtual Definition* Canonicalize(FlowGraph* flow_graph);
+ virtual void InferRange(RangeAnalysis* analysis, Range* range);
+
DECLARE_ABSTRACT_INSTRUCTION(UnboxInteger)
PRINT_OPERANDS_TO_SUPPORT
@@ -8570,10 +8616,6 @@
ASSERT(is_truncating());
}
- virtual bool ComputeCanDeoptimize() const;
-
- virtual void InferRange(RangeAnalysis* analysis, Range* range);
-
DECLARE_INSTRUCTION_NO_BACKEND(UnboxUint32)
DECLARE_EMPTY_SERIALIZATION(UnboxUint32Instr, UnboxInteger32Instr)
@@ -8594,12 +8636,6 @@
deopt_id,
speculative_mode) {}
- virtual bool ComputeCanDeoptimize() const;
-
- virtual void InferRange(RangeAnalysis* analysis, Range* range);
-
- virtual Definition* Canonicalize(FlowGraph* flow_graph);
-
DECLARE_INSTRUCTION_NO_BACKEND(UnboxInt32)
DECLARE_EMPTY_SERIALIZATION(UnboxInt32Instr, UnboxInteger32Instr)
@@ -8619,18 +8655,6 @@
deopt_id,
speculative_mode) {}
- virtual void InferRange(RangeAnalysis* analysis, Range* range);
-
- virtual Definition* Canonicalize(FlowGraph* flow_graph);
-
- virtual bool ComputeCanDeoptimize() const {
- if (SpeculativeModeOfInputs() == kNotSpeculative) {
- return false;
- }
-
- return !value()->Type()->IsInt();
- }
-
DECLARE_INSTRUCTION_NO_BACKEND(UnboxInt64)
DECLARE_EMPTY_SERIALIZATION(UnboxInt64Instr, UnboxIntegerInstr)
@@ -8817,6 +8841,8 @@
return GetDeoptId();
}
+ DECLARE_ATTRIBUTE(op_kind())
+
PRINT_OPERANDS_TO_SUPPORT
DECLARE_INSTRUCTION(BinaryDoubleOp)
@@ -8998,6 +9024,8 @@
Value* value() const { return inputs_[0]; }
Token::Kind op_kind() const { return op_kind_; }
+ virtual Definition* Canonicalize(FlowGraph* flow_graph);
+
virtual bool AttributesEqual(const Instruction& other) const {
return other.AsUnaryIntegerOp()->op_kind() == op_kind();
}
@@ -9012,6 +9040,8 @@
DECLARE_ABSTRACT_INSTRUCTION(UnaryIntegerOp)
+ DECLARE_ATTRIBUTE(op_kind())
+
#define FIELD_LIST(F) F(const Token::Kind, op_kind_)
DECLARE_INSTRUCTION_SERIALIZABLE_FIELDS(UnaryIntegerOpInstr,
@@ -9167,6 +9197,10 @@
set_can_overflow(false);
}
+ // Returns true if right is either a non-zero Integer constant or has a range
+ // that does not include the possibility of being zero.
+ bool RightIsNonZero() const;
+
// Returns true if right is a non-zero Smi constant which absolute value is
// a power of two.
bool RightIsPowerOfTwoConstant() const;
@@ -9183,6 +9217,8 @@
DECLARE_ABSTRACT_INSTRUCTION(BinaryIntegerOp)
+ DECLARE_ATTRIBUTE(op_kind())
+
#define FIELD_LIST(F) \
F(const Token::Kind, op_kind_) \
F(bool, can_overflow_) \
@@ -9355,7 +9391,8 @@
}
virtual bool MayThrow() const {
- return op_kind() == Token::kMOD || op_kind() == Token::kTRUNCDIV;
+ return (op_kind() == Token::kMOD || op_kind() == Token::kTRUNCDIV) &&
+ !RightIsNonZero();
}
virtual Representation representation() const { return kUnboxedInt64; }
@@ -9616,6 +9653,8 @@
(representation_ == other_op->representation_);
}
+ DECLARE_ATTRIBUTE(op_kind())
+
PRINT_OPERANDS_TO_SUPPORT
#define FIELD_LIST(F) \
@@ -9674,6 +9713,10 @@
virtual bool HasUnknownSideEffects() const { return false; }
+ virtual bool CanEliminate(const BlockEntryInstr* block) const {
+ return false;
+ }
+
virtual bool UseSharedSlowPathStub(bool is_optimizing) const {
return SlowPathSharingSupported(is_optimizing);
}
@@ -9978,6 +10021,8 @@
DECLARE_INSTRUCTION(FloatCompare)
+ DECLARE_ATTRIBUTE(op_kind())
+
virtual CompileType ComputeType() const;
virtual bool ComputeCanDeoptimize() const { return false; }
diff --git a/runtime/vm/compiler/backend/locations.cc b/runtime/vm/compiler/backend/locations.cc
index 77cd5d0..4054c3d 100644
--- a/runtime/vm/compiler/backend/locations.cc
+++ b/runtime/vm/compiler/backend/locations.cc
@@ -121,6 +121,12 @@
}
#undef REP_MAX_VALUE_CLAUSE
+bool RepresentationUtils::IsRepresentable(Representation rep, int64_t value) {
+ const intptr_t bit_size = ValueSize(rep) * kBitsPerByte;
+ return IsUnsigned(rep) ? Utils::IsUint(bit_size, value)
+ : Utils::IsInt(bit_size, value);
+}
+
const char* Location::RepresentationToCString(Representation repr) {
switch (repr) {
#define REPR_CASE(Name, __, ___) \
diff --git a/runtime/vm/compiler/backend/locations.h b/runtime/vm/compiler/backend/locations.h
index 2489b02..1a9d0ea 100644
--- a/runtime/vm/compiler/backend/locations.h
+++ b/runtime/vm/compiler/backend/locations.h
@@ -97,12 +97,16 @@
static compiler::OperandSize OperandSize(Representation rep);
// The minimum integral value that can be represented.
- // Assumes that [rep] is a unboxed integer.
+ // Assumes that [rep] is an unboxed integer.
static int64_t MinValue(Representation rep);
// The maximum integral value that can be represented.
- // Assumes that [rep] is a unboxed integer.
+ // Assumes that [rep] is an unboxed integer.
static int64_t MaxValue(Representation rep);
+
+ // Whether the given value is representable in the given representation.
+ // Assumes that [rep] is an unboxed integer.
+ static bool IsRepresentable(Representation rep, int64_t value);
};
// The representation for word-sized unboxed fields.
diff --git a/runtime/vm/compiler/backend/range_analysis.cc b/runtime/vm/compiler/backend/range_analysis.cc
index d57cc36..90c4bac 100644
--- a/runtime/vm/compiler/backend/range_analysis.cc
+++ b/runtime/vm/compiler/backend/range_analysis.cc
@@ -24,6 +24,35 @@
// Quick access to the locally defined zone() method.
#define Z (zone())
+#if defined(DEBUG)
+static void CheckRangeForRepresentation(const Assert& assert,
+ const Instruction* instr,
+ const Range* range,
+ Representation rep) {
+ const Range other = Range::Full(rep);
+ if (!RangeUtils::IsWithin(range, &other)) {
+ assert.Fail(
+ "During range analysis for:\n %s\n"
+ "expected range containing only %s-representable values, but got %s",
+ instr->ToCString(), RepresentationToCString(rep),
+ Range::ToCString(range));
+ }
+}
+
+#define ASSERT_VALID_RANGE_FOR_REPRESENTATION(instr, range, representation) \
+ do { \
+ CheckRangeForRepresentation(dart::Assert(__FILE__, __LINE__), instr, \
+ range, representation); \
+ } while (false)
+#else
+#define ASSERT_VALID_RANGE_FOR_REPRESENTATION(instr, range, representation) \
+ do { \
+ USE(instr); \
+ USE(range); \
+ USE(representation); \
+ } while (false)
+#endif
+
void RangeAnalysis::Analyze() {
CollectValues();
InsertConstraints();
@@ -2121,6 +2150,19 @@
return OnlyGreaterThanOrEqualTo(min_int) && OnlyLessThanOrEqualTo(max_int);
}
+bool Range::IsWithin(const Range* other) const {
+ auto const lower_bound = other->min().LowerBound();
+ auto const upper_bound = other->max().UpperBound();
+ if (lower_bound.IsNegativeInfinity()) {
+ if (upper_bound.IsPositiveInfinity()) return true;
+ return OnlyLessThanOrEqualTo(other->max().ConstantValue());
+ } else if (upper_bound.IsPositiveInfinity()) {
+ return OnlyGreaterThanOrEqualTo(other->min().ConstantValue());
+ } else {
+ return IsWithin(other->min().ConstantValue(), other->max().ConstantValue());
+ }
+}
+
bool Range::Overlaps(int64_t min_int, int64_t max_int) const {
RangeBoundary lower = min().LowerBound();
RangeBoundary upper = max().UpperBound();
@@ -2601,6 +2643,12 @@
// Only Smi and Mint supported.
FATAL("Unsupported type in: %s", ToCString());
}
+
+ // If the representation also gives us range information, then refine
+ // the range from the type by using the intersection of the two.
+ if (RepresentationUtils::IsUnboxedInteger(representation())) {
+ *range = Range::Full(representation()).Intersect(range);
+ }
}
static bool DependsOnSymbol(const RangeBoundary& a, Definition* symbol) {
@@ -3079,60 +3127,35 @@
void BoxIntegerInstr::InferRange(RangeAnalysis* analysis, Range* range) {
const Range* value_range = value()->definition()->range();
- if (!Range::IsUnknown(value_range)) {
+ if (Range::IsUnknown(value_range)) {
+ *range = Range::Full(from_representation());
+ } else {
+ ASSERT_VALID_RANGE_FOR_REPRESENTATION(value()->definition(), value_range,
+ from_representation());
*range = *value_range;
}
}
-void UnboxInt32Instr::InferRange(RangeAnalysis* analysis, Range* range) {
- if (value()->Type()->ToCid() == kSmiCid) {
- const Range* value_range = analysis->GetSmiRange(value());
- if (!Range::IsUnknown(value_range)) {
- *range = *value_range;
- }
- } else if (RangeAnalysis::IsIntegerDefinition(value()->definition())) {
- const Range* value_range = analysis->GetIntRange(value());
- if (!Range::IsUnknown(value_range)) {
- *range = *value_range;
- }
- } else if (value()->Type()->ToCid() == kSmiCid) {
- *range = Range::Full(RangeBoundary::kRangeBoundarySmi);
- } else {
- *range = Range::Full(RangeBoundary::kRangeBoundaryInt32);
- }
-}
+void UnboxIntegerInstr::InferRange(RangeAnalysis* analysis, Range* range) {
+ auto* const value_range = value()->Type()->ToCid() == kSmiCid
+ ? analysis->GetSmiRange(value())
+ : value()->definition()->range();
+ const Range to_range = Range::Full(representation());
-void UnboxUint32Instr::InferRange(RangeAnalysis* analysis, Range* range) {
- const Range* value_range = nullptr;
-
- if (value()->Type()->ToCid() == kSmiCid) {
- value_range = analysis->GetSmiRange(value());
- } else if (RangeAnalysis::IsIntegerDefinition(value()->definition())) {
- value_range = analysis->GetIntRange(value());
- } else {
- *range = Range(RangeBoundary::FromConstant(0),
- RangeBoundary::FromConstant(kMaxUint32));
- return;
- }
-
- if (!Range::IsUnknown(value_range)) {
- if (value_range->IsPositive()) {
- *range = *value_range;
- } else {
- *range = Range(RangeBoundary::FromConstant(0),
- RangeBoundary::FromConstant(kMaxUint32));
- }
- }
-}
-
-void UnboxInt64Instr::InferRange(RangeAnalysis* analysis, Range* range) {
- const Range* value_range = value()->definition()->range();
- if (value_range != nullptr) {
+ if (Range::IsUnknown(value_range)) {
+ *range = to_range;
+ } else if (value_range->IsWithin(&to_range)) {
*range = *value_range;
- } else if (!value()->definition()->IsInt64Definition() &&
- (value()->definition()->Type()->ToCid() != kSmiCid)) {
- *range = Range::Full(RangeBoundary::kRangeBoundaryInt64);
+ } else if (is_truncating()) {
+ // If truncating, then in most cases any non-representable values means
+ // no assumption can be made about the truncated value.
+ *range = to_range;
+ } else {
+ // When not truncating, then unboxing deoptimizes if the value is outside
+ // the range representation.
+ *range = value_range->Intersect(&to_range);
}
+ ASSERT_VALID_RANGE_FOR_REPRESENTATION(this, range, representation());
}
void IntConverterInstr::InferRange(RangeAnalysis* analysis, Range* range) {
@@ -3140,13 +3163,14 @@
ASSERT(RepresentationUtils::IsUnboxedInteger(to()));
ASSERT(from() == kUntagged || RepresentationUtils::IsUnboxedInteger(from()));
- auto* const value_range = value()->definition()->range();
+ const Range* const value_range = value()->definition()->range();
+ const Range to_range = Range::Full(to());
if (from() == kUntagged) {
ASSERT(value_range == nullptr); // Not an integer-valued definition.
- *range = Range::Full(to());
+ *range = to_range;
} else if (Range::IsUnknown(value_range)) {
- *range = Range::Full(to());
+ *range = to_range;
} else if (RepresentationUtils::ValueSize(to()) >
RepresentationUtils::ValueSize(from()) &&
(!RepresentationUtils::IsUnsigned(to()) ||
@@ -3161,29 +3185,21 @@
// are the same size) or a larger value is being truncated. That means
// we need to determine whether or not the value range lies within the
// range of numbers that have the same representation (modulo truncation).
- const int64_t min_overlap =
- Utils::Maximum(RepresentationUtils::MinValue(from()),
- RepresentationUtils::MinValue(to()));
- const int64_t max_overlap =
- Utils::Minimum(RepresentationUtils::MaxValue(from()),
- RepresentationUtils::MaxValue(to()));
-
- if (value_range->IsWithin(min_overlap, max_overlap)) {
+ const Range common_range = Range::Full(from()).Intersect(&to_range);
+ if (value_range->IsWithin(&common_range)) {
*range = *value_range;
} else {
// In most cases, if there are non-representable values, then no
// assumptions can be made about the converted value.
- *range = Range::Full(to());
+ *range = to_range;
}
} else {
// The conversion deoptimizes if the value is outside the range represented
// by to(), so we can just take the intersection.
- const auto& to_range = Range::Full(to());
*range = value_range->Intersect(&to_range);
}
- ASSERT(RangeUtils::IsWithin(range, RepresentationUtils::MinValue(to()),
- RepresentationUtils::MaxValue(to())));
+ ASSERT_VALID_RANGE_FOR_REPRESENTATION(this, range, to());
}
void AssertAssignableInstr::InferRange(RangeAnalysis* analysis, Range* range) {
diff --git a/runtime/vm/compiler/backend/range_analysis.h b/runtime/vm/compiler/backend/range_analysis.h
index e3260e8..d37cb7b 100644
--- a/runtime/vm/compiler/backend/range_analysis.h
+++ b/runtime/vm/compiler/backend/range_analysis.h
@@ -429,12 +429,25 @@
bool IsWithin(int64_t min_int, int64_t max_int) const;
// Inclusive.
+ bool IsWithin(const Range* other) const;
+
+ // Inclusive.
bool Overlaps(int64_t min_int, int64_t max_int) const;
bool IsUnsatisfiable() const;
bool IsFinite() const { return !min_.IsInfinity() && !max_.IsInfinity(); }
+ bool IsSingleton() const {
+ return min_.IsConstant() && max_.IsConstant() &&
+ min_.ConstantValue() == max_.ConstantValue();
+ }
+
+ int64_t Singleton() const {
+ ASSERT(IsSingleton());
+ return min_.ConstantValue();
+ }
+
Range Intersect(const Range* other) const {
return Range(RangeBoundary::IntersectionMin(min(), other->min()),
RangeBoundary::IntersectionMax(max(), other->max()));
@@ -560,10 +573,14 @@
return !Range::IsUnknown(range) && range->Fits(size);
}
- static bool IsWithin(Range* range, int64_t min, int64_t max) {
+ static bool IsWithin(const Range* range, int64_t min, int64_t max) {
return !Range::IsUnknown(range) && range->IsWithin(min, max);
}
+ static bool IsWithin(const Range* range, const Range* other) {
+ return !Range::IsUnknown(range) && range->IsWithin(other);
+ }
+
static bool IsPositive(Range* range) {
return !Range::IsUnknown(range) && range->IsPositive();
}
@@ -580,6 +597,10 @@
static bool OnlyLessThanOrEqualTo(Range* range, intptr_t value) {
return !Range::IsUnknown(range) && range->OnlyLessThanOrEqualTo(value);
}
+
+ static bool IsSingleton(Range* range) {
+ return !Range::IsUnknown(range) && range->IsSingleton();
+ }
};
// Range analysis for integer values.
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index 60538db..9588bde 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -1534,19 +1534,6 @@
}
}
-// Returns true if instruction may have a "visible" effect,
-static bool MayHaveVisibleEffect(Instruction* instr) {
- switch (instr->tag()) {
- case Instruction::kStoreField:
- case Instruction::kStoreStaticField:
- case Instruction::kStoreIndexed:
- case Instruction::kStoreIndexedUnsafe:
- return true;
- default:
- return instr->HasUnknownSideEffects() || instr->MayThrow();
- }
-}
-
void LICM::Optimize() {
if (flow_graph()->function().ProhibitsInstructionHoisting()) {
// Do not hoist any.
@@ -1624,7 +1611,7 @@
// effect invalidates the first "visible" effect flag.
if (is_loop_invariant) {
Hoist(&it, pre_header, current);
- } else if (!seen_visible_effect && MayHaveVisibleEffect(current)) {
+ } else if (!seen_visible_effect && current->MayHaveVisibleEffect()) {
seen_visible_effect = true;
}
}
@@ -4441,20 +4428,6 @@
}
}
-// Returns true if [current] instruction can be possibly eliminated
-// (if its result is not used).
-static bool CanEliminateInstruction(Instruction* current,
- BlockEntryInstr* block) {
- ASSERT(current->GetBlock() == block);
- if (MayHaveVisibleEffect(current) || current->CanDeoptimize() ||
- current == block->last_instruction() || current->IsMaterializeObject() ||
- current->IsCheckStackOverflow() || current->IsReachabilityFence() ||
- current->IsRawStoreField()) {
- return false;
- }
- return true;
-}
-
void DeadCodeElimination::EliminateDeadCode(FlowGraph* flow_graph) {
GrowableArray<Instruction*> worklist;
BitVector live(flow_graph->zone(), flow_graph->current_ssa_temp_index());
@@ -4468,7 +4441,7 @@
ASSERT(!current->IsMoveArgument());
// TODO(alexmarkov): take control dependencies into account and
// eliminate dead branches/conditions.
- if (!CanEliminateInstruction(current, block)) {
+ if (!current->CanEliminate(block)) {
worklist.Add(current);
if (Definition* def = current->AsDefinition()) {
if (def->HasSSATemp()) {
@@ -4525,7 +4498,7 @@
}
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
Instruction* current = it.Current();
- if (!CanEliminateInstruction(current, block)) {
+ if (!current->CanEliminate(block)) {
continue;
}
ASSERT(!current->IsMoveArgument());
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 17893dd..16c3edf 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -1384,8 +1384,8 @@
LocalVariable* pointer = MakeTemporary();
body += LoadLocal(pointer);
body += LoadLocal(address);
- body += UnboxTruncate(kUnboxedIntPtr);
- body += ConvertUnboxedToUntagged(kUnboxedIntPtr);
+ body += UnboxTruncate(kUnboxedFfiIntPtr);
+ body += ConvertUnboxedToUntagged(kUnboxedFfiIntPtr);
body += StoreNativeField(Slot::PointerBase_data(),
InnerPointerAccess::kCannotBeInnerPointer,
StoreFieldInstr::Kind::kInitializing);
@@ -1467,8 +1467,8 @@
body += LoadLocal(MakeTemporary()); // Duplicate Pointer.
body += LoadLocal(parsed_function_->RawParameterVariable(0)); // Address.
body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
- body += UnboxTruncate(kUnboxedIntPtr);
- body += ConvertUnboxedToUntagged(kUnboxedIntPtr);
+ body += UnboxTruncate(kUnboxedFfiIntPtr);
+ body += ConvertUnboxedToUntagged(kUnboxedFfiIntPtr);
body += StoreNativeField(Slot::PointerBase_data(),
InnerPointerAccess::kCannotBeInnerPointer,
StoreFieldInstr::Kind::kInitializing);
@@ -1800,11 +1800,12 @@
body += LoadLocal(typed_data);
body += LoadNativeField(Slot::PointerBase_data(),
InnerPointerAccess::kMayBeInnerPointer);
- body += ConvertUntaggedToUnboxed(kUnboxedIntPtr);
+ body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
body += LoadLocal(offset_in_bytes);
- body += UnboxTruncate(kUnboxedIntPtr);
- body += BinaryIntegerOp(Token::kADD, kUnboxedIntPtr, /*is_truncating=*/true);
- body += ConvertUnboxedToUntagged(kUnboxedIntPtr);
+ body += UnboxTruncate(kUnboxedFfiIntPtr);
+ body +=
+ BinaryIntegerOp(Token::kADD, kUnboxedFfiIntPtr, /*is_truncating=*/true);
+ body += ConvertUnboxedToUntagged(kUnboxedFfiIntPtr);
body += StoreNativeField(Slot::PointerBase_data(),
InnerPointerAccess::kMayBeInnerPointer,
StoreFieldInstr::Kind::kInitializing);
@@ -1865,27 +1866,27 @@
call_memmove += LoadLocal(arg_to);
call_memmove += LoadNativeField(Slot::PointerBase_data(),
InnerPointerAccess::kMayBeInnerPointer);
- call_memmove += ConvertUntaggedToUnboxed(kUnboxedIntPtr);
+ call_memmove += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
call_memmove += LoadLocal(arg_to_start);
call_memmove += IntConstant(element_size);
call_memmove += SmiBinaryOp(Token::kMUL, /*is_truncating=*/true);
- call_memmove += UnboxTruncate(kUnboxedIntPtr);
+ call_memmove += UnboxTruncate(kUnboxedFfiIntPtr);
call_memmove +=
- BinaryIntegerOp(Token::kADD, kUnboxedIntPtr, /*is_truncating=*/true);
+ BinaryIntegerOp(Token::kADD, kUnboxedFfiIntPtr, /*is_truncating=*/true);
call_memmove += LoadLocal(arg_from);
call_memmove += LoadNativeField(Slot::PointerBase_data(),
InnerPointerAccess::kMayBeInnerPointer);
- call_memmove += ConvertUntaggedToUnboxed(kUnboxedIntPtr);
+ call_memmove += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
call_memmove += LoadLocal(arg_from_start);
call_memmove += IntConstant(element_size);
call_memmove += SmiBinaryOp(Token::kMUL, /*is_truncating=*/true);
- call_memmove += UnboxTruncate(kUnboxedIntPtr);
+ call_memmove += UnboxTruncate(kUnboxedFfiIntPtr);
call_memmove +=
- BinaryIntegerOp(Token::kADD, kUnboxedIntPtr, /*is_truncating=*/true);
+ BinaryIntegerOp(Token::kADD, kUnboxedFfiIntPtr, /*is_truncating=*/true);
call_memmove += LoadLocal(arg_count);
call_memmove += IntConstant(element_size);
call_memmove += SmiBinaryOp(Token::kMUL, /*is_truncating=*/true);
- call_memmove += UnboxTruncate(kUnboxedIntPtr);
+ call_memmove += UnboxTruncate(kUnboxedFfiIntPtr);
call_memmove += LoadThread();
call_memmove += LoadUntagged(
compiler::target::Thread::OffsetFromThread(&kMemoryMoveRuntimeEntry));
@@ -4529,8 +4530,8 @@
LocalVariable* pointer = MakeTemporary();
code += LoadLocal(pointer);
code += LoadLocal(address);
- code += UnboxTruncate(kUnboxedIntPtr);
- code += ConvertUnboxedToUntagged(kUnboxedIntPtr);
+ code += UnboxTruncate(kUnboxedFfiIntPtr);
+ code += ConvertUnboxedToUntagged(kUnboxedFfiIntPtr);
code += StoreNativeField(Slot::PointerBase_data(),
InnerPointerAccess::kCannotBeInnerPointer,
StoreFieldInstr::Kind::kInitializing);
diff --git a/tools/VERSION b/tools/VERSION
index da7c5d7..175bfe4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 3
MINOR 3
PATCH 0
-PRERELEASE 87
+PRERELEASE 88
PRERELEASE_PATCH 0