[vm/compiler] Introduce 64-bit NEGATE - all archs.

Rationale:
This improves JIT and AOT performance of unary minus
and also improves constant folding and range analysis
on negative constant (viz. x / -3 is often x / - (3)).
The SHIFT operator needed some special treatment, since
we have to avoid converting a NON-speculative shifts
back into a deopt.

https://github.com/dart-lang/sdk/issues/34072

Change-Id: I230c9cfda98297f683bbba53688e57c2cc659360
Reviewed-on: https://dart-review.googlesource.com/68434
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
Commit-Queue: Aart Bik <ajcbik@google.com>
This commit is contained in:
Aart Bik 2018-08-09 16:43:18 +00:00 committed by commit-bot@chromium.org
parent 5013a2ccc4
commit 71d96019d1
7 changed files with 130 additions and 19 deletions

View file

@ -607,6 +607,16 @@ bool AotCallSpecializer::TryOptimizeStaticCallUsingStaticTypes(
}
break;
}
#ifndef TARGET_ARCH_DBC
case Token::kNEGATE: {
Value* left_value = instr->PushArgumentAt(receiver_index)->value();
left_value = PrepareReceiverOfDevirtualizedCall(left_value, kMintCid);
replacement = new (Z)
UnaryInt64OpInstr(Token::kNEGATE, left_value, Thread::kNoDeoptId,
Instruction::kNotSpeculative);
break;
}
#endif
case Token::kSHL:
case Token::kSHR: {
Value* left_value = instr->PushArgumentAt(receiver_index)->value();

View file

@ -2437,9 +2437,17 @@ Definition* BinaryIntegerOpInstr::Canonicalize(FlowGraph* flow_graph) {
if (rhs == 0) {
return left()->definition();
} else if (rhs < 0) {
// Instruction will always throw on negative rhs operand.
if (!CanDeoptimize()) {
// For non-speculative operations (no deopt), let
// the code generator deal with throw on slowpath.
break;
}
ASSERT(GetDeoptId() != Thread::kNoDeoptId);
DeoptimizeInstr* deopt =
new DeoptimizeInstr(ICData::kDeoptBinarySmiOp, GetDeoptId());
flow_graph->InsertBefore(this, deopt, env(), FlowGraph::kEffect);
// Replace with zero since it always throws.
return CreateConstantResult(flow_graph, Integer::Handle(Smi::New(0)));
}
break;
@ -2450,10 +2458,18 @@ Definition* BinaryIntegerOpInstr::Canonicalize(FlowGraph* flow_graph) {
return left()->definition();
} else if ((rhs < 0) || (rhs >= kMaxShift)) {
if ((rhs < 0) || !is_truncating()) {
// Instruction will always throw on negative rhs operand.
if (!CanDeoptimize()) {
// For non-speculative operations (no deopt), let
// the code generator deal with throw on slowpath.
break;
}
ASSERT(GetDeoptId() != Thread::kNoDeoptId);
DeoptimizeInstr* deopt =
new DeoptimizeInstr(ICData::kDeoptBinarySmiOp, GetDeoptId());
flow_graph->InsertBefore(this, deopt, env(), FlowGraph::kEffect);
}
// Replace with zero since it overshifted or always throws.
return CreateConstantResult(flow_graph, Integer::Handle(Smi::New(0)));
}
break;

View file

@ -6090,7 +6090,9 @@ LocationSummary* ShiftInt64OpInstr::MakeLocationSummary(Zone* zone,
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
summary->set_in(0, Location::Pair(Location::RequiresRegister(),
Location::RequiresRegister()));
if (ConstantInstr* constant = right()->definition()->AsConstant()) {
if (RangeUtils::IsPositive(shift_range()) &&
right()->definition()->IsConstant()) {
ConstantInstr* constant = right()->definition()->AsConstant();
summary->set_in(1, Location::Constant(constant));
} else {
summary->set_in(1, Location::Pair(Location::RequiresRegister(),
@ -6114,7 +6116,7 @@ void ShiftInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
EmitShiftInt64ByConstant(compiler, op_kind(), out_lo, out_hi, left_lo,
left_hi, locs()->in(1).constant());
} else {
// Code for a variable shift amount.
// Code for a variable shift amount (or constant that throws).
PairLocation* right_pair = locs()->in(1).AsPairLocation();
Register right_lo = right_pair->At(0).reg();
Register right_hi = right_pair->At(1).reg();
@ -6228,7 +6230,9 @@ LocationSummary* ShiftUint32OpInstr::MakeLocationSummary(Zone* zone,
LocationSummary* summary = new (zone) LocationSummary(
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
summary->set_in(0, Location::RequiresRegister());
if (ConstantInstr* constant = right()->definition()->AsConstant()) {
if (RangeUtils::IsPositive(shift_range()) &&
right()->definition()->IsConstant()) {
ConstantInstr* constant = right()->definition()->AsConstant();
summary->set_in(1, Location::Constant(constant));
} else {
summary->set_in(1, Location::Pair(Location::RequiresRegister(),
@ -6248,6 +6252,7 @@ void ShiftUint32OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
EmitShiftUint32ByConstant(compiler, op_kind(), out, left,
locs()->in(1).constant());
} else {
// Code for a variable shift amount (or constant that throws).
PairLocation* right_pair = locs()->in(1).AsPairLocation();
Register right_lo = right_pair->At(0).reg();
Register right_hi = right_pair->At(1).reg();
@ -6338,7 +6343,6 @@ LocationSummary* UnaryInt64OpInstr::MakeLocationSummary(Zone* zone,
}
void UnaryInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(op_kind() == Token::kBIT_NOT);
PairLocation* left_pair = locs()->in(0).AsPairLocation();
Register left_lo = left_pair->At(0).reg();
Register left_hi = left_pair->At(1).reg();
@ -6346,8 +6350,19 @@ void UnaryInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
PairLocation* out_pair = locs()->out(0).AsPairLocation();
Register out_lo = out_pair->At(0).reg();
Register out_hi = out_pair->At(1).reg();
__ mvn(out_lo, Operand(left_lo));
__ mvn(out_hi, Operand(left_hi));
switch (op_kind()) {
case Token::kBIT_NOT:
__ mvn(out_lo, Operand(left_lo));
__ mvn(out_hi, Operand(left_hi));
break;
case Token::kNEGATE:
__ rsbs(out_lo, left_lo, Operand(0));
__ sbc(out_hi, out_hi, Operand(out_hi));
__ sub(out_hi, out_hi, Operand(left_hi));
default:
UNREACHABLE();
}
}
CompileType BinaryUint32OpInstr::ComputeType() const {

View file

@ -5398,7 +5398,9 @@ LocationSummary* ShiftInt64OpInstr::MakeLocationSummary(Zone* zone,
LocationSummary* summary = new (zone) LocationSummary(
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::RegisterOrConstant(right()));
summary->set_in(1, RangeUtils::IsPositive(shift_range())
? Location::RegisterOrConstant(right())
: Location::RequiresRegister());
summary->set_out(0, Location::RequiresRegister());
return summary;
}
@ -5412,7 +5414,7 @@ void ShiftInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
EmitShiftInt64ByConstant(compiler, op_kind(), out, left,
locs()->in(1).constant());
} else {
// Code for a variable shift amount.
// Code for a variable shift amount (or constant that throws).
Register shift = locs()->in(1).reg();
// Jump to a slow path if shift is larger than 63 or less than 0.
@ -5507,7 +5509,9 @@ LocationSummary* ShiftUint32OpInstr::MakeLocationSummary(Zone* zone,
LocationSummary* summary = new (zone) LocationSummary(
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::RegisterOrConstant(right()));
summary->set_in(1, RangeUtils::IsPositive(shift_range())
? Location::RegisterOrConstant(right())
: Location::RequiresRegister());
summary->set_out(0, Location::RequiresRegister());
return summary;
}
@ -5520,6 +5524,7 @@ void ShiftUint32OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
EmitShiftUint32ByConstant(compiler, op_kind(), out, left,
locs()->in(1).constant());
} else {
// Code for a variable shift amount (or constant that throws).
const Register right = locs()->in(1).reg();
const bool shift_count_in_range =
IsShiftCountInRange(kUint32ShiftCountLimit);

View file

@ -5497,7 +5497,9 @@ LocationSummary* ShiftInt64OpInstr::MakeLocationSummary(Zone* zone,
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
summary->set_in(0, Location::Pair(Location::RequiresRegister(),
Location::RequiresRegister()));
if (ConstantInstr* constant = right()->definition()->AsConstant()) {
if (RangeUtils::IsPositive(shift_range()) &&
right()->definition()->IsConstant()) {
ConstantInstr* constant = right()->definition()->AsConstant();
summary->set_in(1, Location::Constant(constant));
} else {
summary->set_in(1, Location::Pair(Location::RegisterLocation(ECX),
@ -5522,7 +5524,7 @@ void ShiftInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
EmitShiftInt64ByConstant(compiler, op_kind(), left_lo, left_hi,
locs()->in(1).constant());
} else {
// Code for a variable shift amount.
// Code for a variable shift amount (or constant that throws).
ASSERT(locs()->in(1).AsPairLocation()->At(0).reg() == ECX);
Register right_hi = locs()->in(1).AsPairLocation()->At(1).reg();
@ -5638,7 +5640,9 @@ LocationSummary* ShiftUint32OpInstr::MakeLocationSummary(Zone* zone,
LocationSummary* summary = new (zone) LocationSummary(
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
summary->set_in(0, Location::RequiresRegister());
if (ConstantInstr* constant = right()->definition()->AsConstant()) {
if (RangeUtils::IsPositive(shift_range()) &&
right()->definition()->IsConstant()) {
ConstantInstr* constant = right()->definition()->AsConstant();
summary->set_in(1, Location::Constant(constant));
} else {
summary->set_in(1, Location::Pair(Location::RegisterLocation(ECX),
@ -5657,7 +5661,7 @@ void ShiftUint32OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
EmitShiftUint32ByConstant(compiler, op_kind(), left,
locs()->in(1).constant());
} else {
// Code for a variable shift amount.
// Code for a variable shift amount (or constant that throws).
ASSERT(locs()->in(1).AsPairLocation()->At(0).reg() == ECX);
Register right_hi = locs()->in(1).AsPairLocation()->At(1).reg();
@ -5745,7 +5749,6 @@ LocationSummary* UnaryInt64OpInstr::MakeLocationSummary(Zone* zone,
}
void UnaryInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(op_kind() == Token::kBIT_NOT);
PairLocation* left_pair = locs()->in(0).AsPairLocation();
Register left_lo = left_pair->At(0).reg();
Register left_hi = left_pair->At(1).reg();
@ -5754,8 +5757,19 @@ void UnaryInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
Register out_hi = out_pair->At(1).reg();
ASSERT(out_lo == left_lo);
ASSERT(out_hi == left_hi);
__ notl(left_lo);
__ notl(left_hi);
switch (op_kind()) {
case Token::kBIT_NOT:
__ notl(left_lo);
__ notl(left_hi);
break;
case Token::kNEGATE:
__ negl(left_lo);
__ adcl(left_hi, Immediate(0));
__ negl(left_hi);
break;
default:
UNREACHABLE();
}
}
CompileType BinaryUint32OpInstr::ComputeType() const {

View file

@ -5587,7 +5587,9 @@ LocationSummary* ShiftInt64OpInstr::MakeLocationSummary(Zone* zone,
LocationSummary* summary = new (zone) LocationSummary(
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::FixedRegisterOrConstant(right(), RCX));
summary->set_in(1, RangeUtils::IsPositive(shift_range())
? Location::FixedRegisterOrConstant(right(), RCX)
: Location::RegisterLocation(RCX));
summary->set_out(0, Location::SameAsFirstInput());
return summary;
}
@ -5602,7 +5604,7 @@ void ShiftInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
EmitShiftInt64ByConstant(compiler, op_kind(), left,
locs()->in(1).constant());
} else {
// Code for a variable shift amount.
// Code for a variable shift amount (or constant that throws).
ASSERT(locs()->in(1).reg() == RCX);
// Jump to a slow path if shift count is > 63 or negative.
@ -5705,7 +5707,9 @@ LocationSummary* ShiftUint32OpInstr::MakeLocationSummary(Zone* zone,
LocationSummary* summary = new (zone) LocationSummary(
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::FixedRegisterOrConstant(right(), RCX));
summary->set_in(1, RangeUtils::IsPositive(shift_range())
? Location::FixedRegisterOrConstant(right(), RCX)
: Location::RegisterLocation(RCX));
summary->set_out(0, Location::SameAsFirstInput());
return summary;
}
@ -5719,6 +5723,7 @@ void ShiftUint32OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
EmitShiftUint32ByConstant(compiler, op_kind(), left,
locs()->in(1).constant());
} else {
// Code for a variable shift amount (or constant that throws).
ASSERT(locs()->in(1).reg() == RCX);
// Jump to a slow path if shift count is > 31 or negative.

View file

@ -0,0 +1,46 @@
// 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.
// VMOptions=--no_background_compilation --optimization_counter_threshold=10
import "package:expect/expect.dart";
// Tests for long negations under
// 64-bit arithmetic wrap-around semantics.
final int maxInt32 = 2147483647;
final int minInt32 = -2147483648;
final int maxInt64 = 0x7fffffffffffffff;
final int minInt64 = 0x8000000000000000;
int negate(int x) {
return -x;
}
doConstant() {
Expect.equals(1, negate(-1));
Expect.equals(0, negate(0));
Expect.equals(-1, negate(1));
Expect.equals(-maxInt32, negate(maxInt32));
Expect.equals(-minInt32, negate(minInt32));
Expect.equals(-maxInt64, negate(maxInt64));
Expect.equals(minInt64, negate(minInt64)); // sic!
}
doVar() {
int d = 0;
for (int i = -88; i < 10; i++) {
d += negate(i);
}
Expect.equals(3871, d);
}
main() {
// Repeat tests to enter JIT (when applicable).
for (int i = 0; i < 20; i++) {
doConstant();
doVar();
}
}