Skip to content

Commit bfa890d

Browse files
aartbikcommit-bot@chromium.org
authored andcommitted
[vm/compiler] Various 64-bit operator improvements.
Rationale: Improves the slow path of 64-bit REM/TRUNCDIV on X64 and ARM64. Also introduces 64-bit negate operator, which was needed to expose negative contants (viz. x / -3 was represented as x / - (3) first). The negate operator is not recognized in AOT mode yet, since shifts by out-of-range constants should learn how to throw rather than deopt.... #33967 flutter/flutter#19677 Change-Id: I7d81c9b1c72d99e8c4018f68c0501c7b599e073f Reviewed-on: https://dart-review.googlesource.com/68280 Commit-Queue: Aart Bik <[email protected]> Reviewed-by: Alexander Markov <[email protected]> Reviewed-by: Vyacheslav Egorov <[email protected]>
1 parent 92a7094 commit bfa890d

File tree

4 files changed

+173
-66
lines changed

4 files changed

+173
-66
lines changed

runtime/vm/compiler/aot/aot_call_specializer.cc

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,6 @@ bool AotCallSpecializer::TryOptimizeStaticCallUsingStaticTypes(
565565
}
566566
break;
567567
}
568-
// TODO(dartbug.com/30480): Enable 64-bit integer shifts (SHL, SHR).
569568
case Token::kBIT_OR:
570569
case Token::kBIT_XOR:
571570
case Token::kBIT_AND:
@@ -608,7 +607,6 @@ bool AotCallSpecializer::TryOptimizeStaticCallUsingStaticTypes(
608607
}
609608
break;
610609
}
611-
612610
case Token::kSHL:
613611
case Token::kSHR: {
614612
Value* left_value = instr->PushArgumentAt(receiver_index)->value();
@@ -622,7 +620,6 @@ bool AotCallSpecializer::TryOptimizeStaticCallUsingStaticTypes(
622620
}
623621
break;
624622
}
625-
626623
default:
627624
break;
628625
}

runtime/vm/compiler/backend/il.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6076,9 +6076,13 @@ class UnaryUint32OpInstr : public UnaryIntegerOpInstr {
60766076

60776077
class UnaryInt64OpInstr : public UnaryIntegerOpInstr {
60786078
public:
6079-
UnaryInt64OpInstr(Token::Kind op_kind, Value* value, intptr_t deopt_id)
6080-
: UnaryIntegerOpInstr(op_kind, value, deopt_id) {
6081-
ASSERT(op_kind == Token::kBIT_NOT);
6079+
UnaryInt64OpInstr(Token::Kind op_kind,
6080+
Value* value,
6081+
intptr_t deopt_id,
6082+
SpeculativeMode speculative_mode = kGuardInputs)
6083+
: UnaryIntegerOpInstr(op_kind, value, deopt_id),
6084+
speculative_mode_(speculative_mode) {
6085+
ASSERT(op_kind == Token::kBIT_NOT || op_kind == Token::kNEGATE);
60826086
}
60836087

60846088
virtual bool ComputeCanDeoptimize() const { return false; }
@@ -6092,9 +6096,17 @@ class UnaryInt64OpInstr : public UnaryIntegerOpInstr {
60926096
return kUnboxedInt64;
60936097
}
60946098

6099+
virtual bool AttributesEqual(Instruction* other) const {
6100+
return UnaryIntegerOpInstr::AttributesEqual(other) &&
6101+
(speculative_mode() == other->speculative_mode());
6102+
}
6103+
6104+
virtual SpeculativeMode speculative_mode() const { return speculative_mode_; }
6105+
60956106
DECLARE_INSTRUCTION(UnaryInt64Op)
60966107

60976108
private:
6109+
const SpeculativeMode speculative_mode_;
60986110
DISALLOW_COPY_AND_ASSIGN(UnaryInt64OpInstr);
60996111
};
61006112

runtime/vm/compiler/backend/il_arm64.cc

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5047,7 +5047,8 @@ class Int64DivideSlowPath : public ThrowErrorSlowPathCode {
50475047
static const intptr_t kNumberOfArguments = 0;
50485048

50495049
Int64DivideSlowPath(BinaryInt64OpInstr* instruction,
5050-
Register right,
5050+
Register divisor,
5051+
Range* divisor_range,
50515052
Register tmp,
50525053
Register out,
50535054
intptr_t try_index)
@@ -5056,36 +5057,63 @@ class Int64DivideSlowPath : public ThrowErrorSlowPathCode {
50565057
kNumberOfArguments,
50575058
try_index),
50585059
is_mod_(instruction->op_kind() == Token::kMOD),
5059-
right_(right),
5060+
divisor_(divisor),
5061+
divisor_range_(divisor_range),
50605062
tmp_(tmp),
50615063
out_(out),
50625064
adjust_sign_label_() {}
50635065

50645066
void EmitNativeCode(FlowGraphCompiler* compiler) override {
5065-
// Main entry throws, use code of superclass.
5066-
ThrowErrorSlowPathCode::EmitNativeCode(compiler);
5067-
// Adjust modulo for negative sign.
5068-
// if (right < 0)
5069-
// out -= right;
5067+
// Handle modulo/division by zero, if needed. Use superclass code.
5068+
if (has_divide_by_zero()) {
5069+
ThrowErrorSlowPathCode::EmitNativeCode(compiler);
5070+
} else {
5071+
__ Bind(entry_label()); // not used, but keeps destructor happy
5072+
if (Assembler::EmittingComments()) {
5073+
__ Comment("slow path %s operation (no throw)", name());
5074+
}
5075+
}
5076+
// Adjust modulo for negative sign, optimized for known ranges.
5077+
// if (divisor < 0)
5078+
// out -= divisor;
50705079
// else
5071-
// out += right;
5072-
if (is_mod_) {
5080+
// out += divisor;
5081+
if (has_adjust_sign()) {
50735082
__ Bind(adjust_sign_label());
5074-
__ CompareRegisters(right_, ZR);
5075-
__ sub(tmp_, out_, Operand(right_));
5076-
__ add(out_, out_, Operand(right_));
5077-
__ csel(out_, tmp_, out_, LT);
5083+
if (RangeUtils::Overlaps(divisor_range_, -1, 1)) {
5084+
// General case.
5085+
__ CompareRegisters(divisor_, ZR);
5086+
__ sub(tmp_, out_, Operand(divisor_));
5087+
__ add(out_, out_, Operand(divisor_));
5088+
__ csel(out_, tmp_, out_, LT);
5089+
} else if (divisor_range_->IsPositive()) {
5090+
// Always positive.
5091+
__ add(out_, out_, Operand(divisor_));
5092+
} else {
5093+
// Always negative.
5094+
__ sub(out_, out_, Operand(divisor_));
5095+
}
50785096
__ b(exit_label());
50795097
}
50805098
}
50815099

50825100
const char* name() override { return "int64 divide"; }
50835101

5084-
Label* adjust_sign_label() { return &adjust_sign_label_; }
5102+
bool has_divide_by_zero() { return RangeUtils::CanBeZero(divisor_range_); }
5103+
5104+
bool has_adjust_sign() { return is_mod_; }
5105+
5106+
bool is_needed() { return has_divide_by_zero() || has_adjust_sign(); }
5107+
5108+
Label* adjust_sign_label() {
5109+
ASSERT(has_adjust_sign());
5110+
return &adjust_sign_label_;
5111+
}
50855112

50865113
private:
50875114
bool is_mod_;
5088-
Register right_;
5115+
Register divisor_;
5116+
Range* divisor_range_;
50895117
Register tmp_;
50905118
Register out_;
50915119
Label adjust_sign_label_;
@@ -5100,14 +5128,16 @@ static void EmitInt64ModTruncDiv(FlowGraphCompiler* compiler,
51005128
Register out) {
51015129
ASSERT(op_kind == Token::kMOD || op_kind == Token::kTRUNCDIV);
51025130

5103-
// Set up a slow path.
5131+
// Prepare a slow path.
5132+
Range* right_range = instruction->right()->definition()->range();
51045133
Int64DivideSlowPath* slow_path = new (Z) Int64DivideSlowPath(
5105-
instruction, right, tmp, out, compiler->CurrentTryIndex());
5106-
compiler->AddSlowPathCode(slow_path);
5134+
instruction, right, right_range, tmp, out, compiler->CurrentTryIndex());
51075135

51085136
// Handle modulo/division by zero exception on slow path.
5109-
__ CompareRegisters(right, ZR);
5110-
__ b(slow_path->entry_label(), EQ);
5137+
if (slow_path->has_divide_by_zero()) {
5138+
__ CompareRegisters(right, ZR);
5139+
__ b(slow_path->entry_label(), EQ);
5140+
}
51115141

51125142
// Perform actual operation
51135143
// out = left % right
@@ -5124,7 +5154,10 @@ static void EmitInt64ModTruncDiv(FlowGraphCompiler* compiler,
51245154
__ sdiv(out, left, right);
51255155
}
51265156

5127-
__ Bind(slow_path->exit_label());
5157+
if (slow_path->is_needed()) {
5158+
__ Bind(slow_path->exit_label());
5159+
compiler->AddSlowPathCode(slow_path);
5160+
}
51285161
}
51295162

51305163
LocationSummary* BinaryInt64OpInstr::MakeLocationSummary(Zone* zone,
@@ -5571,11 +5604,18 @@ LocationSummary* UnaryInt64OpInstr::MakeLocationSummary(Zone* zone,
55715604
}
55725605

55735606
void UnaryInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5574-
ASSERT(op_kind() == Token::kBIT_NOT);
55755607
const Register left = locs()->in(0).reg();
55765608
const Register out = locs()->out(0).reg();
5577-
ASSERT(out == left);
5578-
__ mvn(out, left);
5609+
switch (op_kind()) {
5610+
case Token::kBIT_NOT:
5611+
__ mvn(out, left);
5612+
break;
5613+
case Token::kNEGATE:
5614+
__ sub(out, ZR, Operand(left));
5615+
break;
5616+
default:
5617+
UNREACHABLE();
5618+
}
55795619
}
55805620

55815621
CompileType BinaryUint32OpInstr::ComputeType() const {

runtime/vm/compiler/backend/il_x64.cc

Lines changed: 94 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5201,54 +5201,96 @@ class Int64DivideSlowPath : public ThrowErrorSlowPathCode {
52015201
static const intptr_t kNumberOfArguments = 0;
52025202

52035203
Int64DivideSlowPath(BinaryInt64OpInstr* instruction,
5204-
Register right,
5204+
Register divisor,
5205+
Range* divisor_range,
52055206
intptr_t try_index)
52065207
: ThrowErrorSlowPathCode(instruction,
52075208
kIntegerDivisionByZeroExceptionRuntimeEntry,
52085209
kNumberOfArguments,
52095210
try_index),
52105211
is_mod_(instruction->op_kind() == Token::kMOD),
5211-
right_(right),
5212+
divisor_(divisor),
5213+
divisor_range_(divisor_range),
52125214
div_by_minus_one_label_(),
52135215
adjust_sign_label_() {}
52145216

52155217
void EmitNativeCode(FlowGraphCompiler* compiler) override {
5216-
// Main entry throws, use code of superclass.
5217-
ThrowErrorSlowPathCode::EmitNativeCode(compiler);
5218-
// Handle modulo/division by minus one.
5219-
__ Bind(div_by_minus_one_label());
5220-
if (is_mod_) {
5221-
__ xorq(RDX, RDX); // x % -1 = 0
5218+
// Handle modulo/division by zero, if needed. Use superclass code.
5219+
if (has_divide_by_zero()) {
5220+
ThrowErrorSlowPathCode::EmitNativeCode(compiler);
52225221
} else {
5223-
__ negq(RAX); // x / -1 = -x
5222+
__ Bind(entry_label()); // not used, but keeps destructor happy
5223+
if (Assembler::EmittingComments()) {
5224+
__ Comment("slow path %s operation (no throw)", name());
5225+
}
52245226
}
5225-
__ jmp(exit_label());
5226-
// Adjust modulo for negative sign.
5227-
// if (right < 0)
5228-
// out -= right;
5227+
// Handle modulo/division by minus one, if needed.
5228+
// Note: an exact -1 divisor is best optimized prior to codegen.
5229+
if (has_divide_by_minus_one()) {
5230+
__ Bind(div_by_minus_one_label());
5231+
if (is_mod_) {
5232+
__ xorq(RDX, RDX); // x % -1 = 0
5233+
} else {
5234+
__ negq(RAX); // x / -1 = -x
5235+
}
5236+
__ jmp(exit_label());
5237+
}
5238+
// Adjust modulo for negative sign, optimized for known ranges.
5239+
// if (divisor < 0)
5240+
// out -= divisor;
52295241
// else
5230-
// out += right;
5231-
if (is_mod_) {
5232-
Label subtract;
5242+
// out += divisor;
5243+
if (has_adjust_sign()) {
52335244
__ Bind(adjust_sign_label());
5234-
__ testq(right_, right_);
5235-
__ j(LESS, &subtract, Assembler::kNearJump);
5236-
__ addq(RDX, right_);
5237-
__ jmp(exit_label());
5238-
__ Bind(&subtract);
5239-
__ subq(RDX, right_);
5245+
if (RangeUtils::Overlaps(divisor_range_, -1, 1)) {
5246+
// General case.
5247+
Label subtract;
5248+
__ testq(divisor_, divisor_);
5249+
__ j(LESS, &subtract, Assembler::kNearJump);
5250+
__ addq(RDX, divisor_);
5251+
__ jmp(exit_label());
5252+
__ Bind(&subtract);
5253+
__ subq(RDX, divisor_);
5254+
} else if (divisor_range_->IsPositive()) {
5255+
// Always positive.
5256+
__ addq(RDX, divisor_);
5257+
} else {
5258+
// Always negative.
5259+
__ subq(RDX, divisor_);
5260+
}
52405261
__ jmp(exit_label());
52415262
}
52425263
}
52435264

52445265
const char* name() override { return "int64 divide"; }
52455266

5246-
Label* div_by_minus_one_label() { return &div_by_minus_one_label_; }
5247-
Label* adjust_sign_label() { return &adjust_sign_label_; }
5267+
bool has_divide_by_zero() { return RangeUtils::CanBeZero(divisor_range_); }
5268+
5269+
bool has_divide_by_minus_one() {
5270+
return RangeUtils::Overlaps(divisor_range_, -1, -1);
5271+
}
5272+
5273+
bool has_adjust_sign() { return is_mod_; }
5274+
5275+
bool is_needed() {
5276+
return has_divide_by_zero() || has_divide_by_minus_one() ||
5277+
has_adjust_sign();
5278+
}
5279+
5280+
Label* div_by_minus_one_label() {
5281+
ASSERT(has_divide_by_minus_one());
5282+
return &div_by_minus_one_label_;
5283+
}
5284+
5285+
Label* adjust_sign_label() {
5286+
ASSERT(has_adjust_sign());
5287+
return &adjust_sign_label_;
5288+
}
52485289

52495290
private:
52505291
bool is_mod_;
5251-
Register right_;
5292+
Register divisor_;
5293+
Range* divisor_range_;
52525294
Label div_by_minus_one_label_;
52535295
Label adjust_sign_label_;
52545296
};
@@ -5262,19 +5304,23 @@ static void EmitInt64ModTruncDiv(FlowGraphCompiler* compiler,
52625304
Register out) {
52635305
ASSERT(op_kind == Token::kMOD || op_kind == Token::kTRUNCDIV);
52645306

5265-
// Set up a slow path.
5266-
Int64DivideSlowPath* slow_path = new (Z)
5267-
Int64DivideSlowPath(instruction, right, compiler->CurrentTryIndex());
5268-
compiler->AddSlowPathCode(slow_path);
5307+
// Prepare a slow path.
5308+
Range* right_range = instruction->right()->definition()->range();
5309+
Int64DivideSlowPath* slow_path = new (Z) Int64DivideSlowPath(
5310+
instruction, right, right_range, compiler->CurrentTryIndex());
52695311

52705312
// Handle modulo/division by zero exception on slow path.
5271-
__ testq(right, right);
5272-
__ j(EQUAL, slow_path->entry_label());
5313+
if (slow_path->has_divide_by_zero()) {
5314+
__ testq(right, right);
5315+
__ j(EQUAL, slow_path->entry_label());
5316+
}
52735317

52745318
// Handle modulo/division by minus one explicitly on slow path
52755319
// (to avoid arithmetic exception on 0x8000000000000000 / -1).
5276-
__ cmpq(right, Immediate(-1));
5277-
__ j(EQUAL, slow_path->div_by_minus_one_label());
5320+
if (slow_path->has_divide_by_minus_one()) {
5321+
__ cmpq(right, Immediate(-1));
5322+
__ j(EQUAL, slow_path->div_by_minus_one_label());
5323+
}
52785324

52795325
// Perform actual operation
52805326
// out = left % right
@@ -5294,7 +5340,11 @@ static void EmitInt64ModTruncDiv(FlowGraphCompiler* compiler,
52945340
ASSERT(out == RAX);
52955341
ASSERT(tmp == RDX);
52965342
}
5297-
__ Bind(slow_path->exit_label());
5343+
5344+
if (slow_path->is_needed()) {
5345+
__ Bind(slow_path->exit_label());
5346+
compiler->AddSlowPathCode(slow_path);
5347+
}
52985348
}
52995349

53005350
template <typename OperandType>
@@ -5393,11 +5443,19 @@ LocationSummary* UnaryInt64OpInstr::MakeLocationSummary(Zone* zone,
53935443
}
53945444

53955445
void UnaryInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5396-
ASSERT(op_kind() == Token::kBIT_NOT);
53975446
const Register left = locs()->in(0).reg();
53985447
const Register out = locs()->out(0).reg();
53995448
ASSERT(out == left);
5400-
__ notq(left);
5449+
switch (op_kind()) {
5450+
case Token::kBIT_NOT:
5451+
__ notq(left);
5452+
break;
5453+
case Token::kNEGATE:
5454+
__ negq(left);
5455+
break;
5456+
default:
5457+
UNREACHABLE();
5458+
}
54015459
}
54025460

54035461
static void EmitShiftInt64ByConstant(FlowGraphCompiler* compiler,

0 commit comments

Comments
 (0)