diff --git a/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart b/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart new file mode 100644 index 000000000000..9baf000cd85e --- /dev/null +++ b/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart @@ -0,0 +1,18 @@ +// 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. + +// Test that we emit EqualityCompare rather than StrictCompare+BoxInt64 +// when comparing non-nullable integer to a Smi. + +// MatchIL[AOT]=factorial +// __ GraphEntry +// __ FunctionEntry +// __ CheckStackOverflow +// __ Branch(EqualityCompare) +@pragma('vm:never-inline') +int factorial(int value) => value == 1 ? value : value * factorial(value - 1); + +void main() { + print(factorial(4)); +} diff --git a/runtime/tests/vm/dart_2/aot_prefer_equality_comparison_il_test.dart b/runtime/tests/vm/dart_2/aot_prefer_equality_comparison_il_test.dart new file mode 100644 index 000000000000..9baf000cd85e --- /dev/null +++ b/runtime/tests/vm/dart_2/aot_prefer_equality_comparison_il_test.dart @@ -0,0 +1,18 @@ +// 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. + +// Test that we emit EqualityCompare rather than StrictCompare+BoxInt64 +// when comparing non-nullable integer to a Smi. + +// MatchIL[AOT]=factorial +// __ GraphEntry +// __ FunctionEntry +// __ CheckStackOverflow +// __ Branch(EqualityCompare) +@pragma('vm:never-inline') +int factorial(int value) => value == 1 ? value : value * factorial(value - 1); + +void main() { + print(factorial(4)); +} diff --git a/runtime/vm/compiler/aot/aot_call_specializer.cc b/runtime/vm/compiler/aot/aot_call_specializer.cc index 6fa1d0180bcb..a013cb2cd8f5 100644 --- a/runtime/vm/compiler/aot/aot_call_specializer.cc +++ b/runtime/vm/compiler/aot/aot_call_specializer.cc @@ -465,23 +465,35 @@ bool AotCallSpecializer::TryOptimizeIntegerOperation(TemplateDartCall<0>* instr, switch (op_kind) { case Token::kEQ: - case Token::kNE: - if (left_type->IsNull() || left_type->IsNullableSmi() || - right_type->IsNull() || right_type->IsNullableSmi()) { + case Token::kNE: { + // If both arguments are nullable Smi or one of the arguments is + // a null or Smi and the other argument is nullable then emit + // StrictCompare (all arguments are going to be boxed anyway). + // Otherwise prefer EqualityCompare to avoid redundant boxing. + const bool left_is_null_or_smi = + left_type->IsNull() || left_type->IsNullableSmi(); + const bool right_is_null_or_smi = + right_type->IsNull() || right_type->IsNullableSmi(); + const bool both_are_nullable_smis = + left_type->IsNullableSmi() && right_type->IsNullableSmi(); + const bool either_can_be_null = + left_type->is_nullable() || right_type->is_nullable(); + if (both_are_nullable_smis || + ((left_is_null_or_smi || right_is_null_or_smi) && + either_can_be_null)) { replacement = new (Z) StrictCompareInstr( instr->source(), (op_kind == Token::kEQ) ? Token::kEQ_STRICT : Token::kNE_STRICT, left_value->CopyWithType(Z), right_value->CopyWithType(Z), /*needs_number_check=*/false, DeoptId::kNone); } else { - const bool null_aware = - left_type->is_nullable() || right_type->is_nullable(); replacement = new (Z) EqualityCompareInstr( instr->source(), op_kind, left_value->CopyWithType(Z), right_value->CopyWithType(Z), kMintCid, DeoptId::kNone, - null_aware, Instruction::kNotSpeculative); + /*null_aware=*/either_can_be_null, Instruction::kNotSpeculative); } break; + } case Token::kLT: case Token::kLTE: case Token::kGT: