mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 15:17:07 +00:00
[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.... https://github.com/dart-lang/sdk/issues/33967 https://github.com/flutter/flutter/issues/19677 Change-Id: I7d81c9b1c72d99e8c4018f68c0501c7b599e073f Reviewed-on: https://dart-review.googlesource.com/68280 Commit-Queue: Aart Bik <ajcbik@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
This commit is contained in:
parent
92a70946b0
commit
bfa890dcd1
4 changed files with 174 additions and 67 deletions
|
@ -565,7 +565,6 @@ bool AotCallSpecializer::TryOptimizeStaticCallUsingStaticTypes(
|
|||
}
|
||||
break;
|
||||
}
|
||||
// TODO(dartbug.com/30480): Enable 64-bit integer shifts (SHL, SHR).
|
||||
case Token::kBIT_OR:
|
||||
case Token::kBIT_XOR:
|
||||
case Token::kBIT_AND:
|
||||
|
@ -608,7 +607,6 @@ bool AotCallSpecializer::TryOptimizeStaticCallUsingStaticTypes(
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Token::kSHL:
|
||||
case Token::kSHR: {
|
||||
Value* left_value = instr->PushArgumentAt(receiver_index)->value();
|
||||
|
@ -622,7 +620,6 @@ bool AotCallSpecializer::TryOptimizeStaticCallUsingStaticTypes(
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -6076,9 +6076,13 @@ class UnaryUint32OpInstr : public UnaryIntegerOpInstr {
|
|||
|
||||
class UnaryInt64OpInstr : public UnaryIntegerOpInstr {
|
||||
public:
|
||||
UnaryInt64OpInstr(Token::Kind op_kind, Value* value, intptr_t deopt_id)
|
||||
: UnaryIntegerOpInstr(op_kind, value, deopt_id) {
|
||||
ASSERT(op_kind == Token::kBIT_NOT);
|
||||
UnaryInt64OpInstr(Token::Kind op_kind,
|
||||
Value* value,
|
||||
intptr_t deopt_id,
|
||||
SpeculativeMode speculative_mode = kGuardInputs)
|
||||
: UnaryIntegerOpInstr(op_kind, value, deopt_id),
|
||||
speculative_mode_(speculative_mode) {
|
||||
ASSERT(op_kind == Token::kBIT_NOT || op_kind == Token::kNEGATE);
|
||||
}
|
||||
|
||||
virtual bool ComputeCanDeoptimize() const { return false; }
|
||||
|
@ -6092,9 +6096,17 @@ class UnaryInt64OpInstr : public UnaryIntegerOpInstr {
|
|||
return kUnboxedInt64;
|
||||
}
|
||||
|
||||
virtual bool AttributesEqual(Instruction* other) const {
|
||||
return UnaryIntegerOpInstr::AttributesEqual(other) &&
|
||||
(speculative_mode() == other->speculative_mode());
|
||||
}
|
||||
|
||||
virtual SpeculativeMode speculative_mode() const { return speculative_mode_; }
|
||||
|
||||
DECLARE_INSTRUCTION(UnaryInt64Op)
|
||||
|
||||
private:
|
||||
const SpeculativeMode speculative_mode_;
|
||||
DISALLOW_COPY_AND_ASSIGN(UnaryInt64OpInstr);
|
||||
};
|
||||
|
||||
|
|
|
@ -5047,7 +5047,8 @@ class Int64DivideSlowPath : public ThrowErrorSlowPathCode {
|
|||
static const intptr_t kNumberOfArguments = 0;
|
||||
|
||||
Int64DivideSlowPath(BinaryInt64OpInstr* instruction,
|
||||
Register right,
|
||||
Register divisor,
|
||||
Range* divisor_range,
|
||||
Register tmp,
|
||||
Register out,
|
||||
intptr_t try_index)
|
||||
|
@ -5056,36 +5057,63 @@ class Int64DivideSlowPath : public ThrowErrorSlowPathCode {
|
|||
kNumberOfArguments,
|
||||
try_index),
|
||||
is_mod_(instruction->op_kind() == Token::kMOD),
|
||||
right_(right),
|
||||
divisor_(divisor),
|
||||
divisor_range_(divisor_range),
|
||||
tmp_(tmp),
|
||||
out_(out),
|
||||
adjust_sign_label_() {}
|
||||
|
||||
void EmitNativeCode(FlowGraphCompiler* compiler) override {
|
||||
// Main entry throws, use code of superclass.
|
||||
ThrowErrorSlowPathCode::EmitNativeCode(compiler);
|
||||
// Adjust modulo for negative sign.
|
||||
// if (right < 0)
|
||||
// out -= right;
|
||||
// Handle modulo/division by zero, if needed. Use superclass code.
|
||||
if (has_divide_by_zero()) {
|
||||
ThrowErrorSlowPathCode::EmitNativeCode(compiler);
|
||||
} else {
|
||||
__ Bind(entry_label()); // not used, but keeps destructor happy
|
||||
if (Assembler::EmittingComments()) {
|
||||
__ Comment("slow path %s operation (no throw)", name());
|
||||
}
|
||||
}
|
||||
// Adjust modulo for negative sign, optimized for known ranges.
|
||||
// if (divisor < 0)
|
||||
// out -= divisor;
|
||||
// else
|
||||
// out += right;
|
||||
if (is_mod_) {
|
||||
// out += divisor;
|
||||
if (has_adjust_sign()) {
|
||||
__ Bind(adjust_sign_label());
|
||||
__ CompareRegisters(right_, ZR);
|
||||
__ sub(tmp_, out_, Operand(right_));
|
||||
__ add(out_, out_, Operand(right_));
|
||||
__ csel(out_, tmp_, out_, LT);
|
||||
if (RangeUtils::Overlaps(divisor_range_, -1, 1)) {
|
||||
// General case.
|
||||
__ CompareRegisters(divisor_, ZR);
|
||||
__ sub(tmp_, out_, Operand(divisor_));
|
||||
__ add(out_, out_, Operand(divisor_));
|
||||
__ csel(out_, tmp_, out_, LT);
|
||||
} else if (divisor_range_->IsPositive()) {
|
||||
// Always positive.
|
||||
__ add(out_, out_, Operand(divisor_));
|
||||
} else {
|
||||
// Always negative.
|
||||
__ sub(out_, out_, Operand(divisor_));
|
||||
}
|
||||
__ b(exit_label());
|
||||
}
|
||||
}
|
||||
|
||||
const char* name() override { return "int64 divide"; }
|
||||
|
||||
Label* adjust_sign_label() { return &adjust_sign_label_; }
|
||||
bool has_divide_by_zero() { return RangeUtils::CanBeZero(divisor_range_); }
|
||||
|
||||
bool has_adjust_sign() { return is_mod_; }
|
||||
|
||||
bool is_needed() { return has_divide_by_zero() || has_adjust_sign(); }
|
||||
|
||||
Label* adjust_sign_label() {
|
||||
ASSERT(has_adjust_sign());
|
||||
return &adjust_sign_label_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_mod_;
|
||||
Register right_;
|
||||
Register divisor_;
|
||||
Range* divisor_range_;
|
||||
Register tmp_;
|
||||
Register out_;
|
||||
Label adjust_sign_label_;
|
||||
|
@ -5100,14 +5128,16 @@ static void EmitInt64ModTruncDiv(FlowGraphCompiler* compiler,
|
|||
Register out) {
|
||||
ASSERT(op_kind == Token::kMOD || op_kind == Token::kTRUNCDIV);
|
||||
|
||||
// Set up a slow path.
|
||||
// Prepare a slow path.
|
||||
Range* right_range = instruction->right()->definition()->range();
|
||||
Int64DivideSlowPath* slow_path = new (Z) Int64DivideSlowPath(
|
||||
instruction, right, tmp, out, compiler->CurrentTryIndex());
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
instruction, right, right_range, tmp, out, compiler->CurrentTryIndex());
|
||||
|
||||
// Handle modulo/division by zero exception on slow path.
|
||||
__ CompareRegisters(right, ZR);
|
||||
__ b(slow_path->entry_label(), EQ);
|
||||
if (slow_path->has_divide_by_zero()) {
|
||||
__ CompareRegisters(right, ZR);
|
||||
__ b(slow_path->entry_label(), EQ);
|
||||
}
|
||||
|
||||
// Perform actual operation
|
||||
// out = left % right
|
||||
|
@ -5124,7 +5154,10 @@ static void EmitInt64ModTruncDiv(FlowGraphCompiler* compiler,
|
|||
__ sdiv(out, left, right);
|
||||
}
|
||||
|
||||
__ Bind(slow_path->exit_label());
|
||||
if (slow_path->is_needed()) {
|
||||
__ Bind(slow_path->exit_label());
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
}
|
||||
}
|
||||
|
||||
LocationSummary* BinaryInt64OpInstr::MakeLocationSummary(Zone* zone,
|
||||
|
@ -5571,11 +5604,18 @@ LocationSummary* UnaryInt64OpInstr::MakeLocationSummary(Zone* zone,
|
|||
}
|
||||
|
||||
void UnaryInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
ASSERT(op_kind() == Token::kBIT_NOT);
|
||||
const Register left = locs()->in(0).reg();
|
||||
const Register out = locs()->out(0).reg();
|
||||
ASSERT(out == left);
|
||||
__ mvn(out, left);
|
||||
switch (op_kind()) {
|
||||
case Token::kBIT_NOT:
|
||||
__ mvn(out, left);
|
||||
break;
|
||||
case Token::kNEGATE:
|
||||
__ sub(out, ZR, Operand(left));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
CompileType BinaryUint32OpInstr::ComputeType() const {
|
||||
|
|
|
@ -5201,54 +5201,96 @@ class Int64DivideSlowPath : public ThrowErrorSlowPathCode {
|
|||
static const intptr_t kNumberOfArguments = 0;
|
||||
|
||||
Int64DivideSlowPath(BinaryInt64OpInstr* instruction,
|
||||
Register right,
|
||||
Register divisor,
|
||||
Range* divisor_range,
|
||||
intptr_t try_index)
|
||||
: ThrowErrorSlowPathCode(instruction,
|
||||
kIntegerDivisionByZeroExceptionRuntimeEntry,
|
||||
kNumberOfArguments,
|
||||
try_index),
|
||||
is_mod_(instruction->op_kind() == Token::kMOD),
|
||||
right_(right),
|
||||
divisor_(divisor),
|
||||
divisor_range_(divisor_range),
|
||||
div_by_minus_one_label_(),
|
||||
adjust_sign_label_() {}
|
||||
|
||||
void EmitNativeCode(FlowGraphCompiler* compiler) override {
|
||||
// Main entry throws, use code of superclass.
|
||||
ThrowErrorSlowPathCode::EmitNativeCode(compiler);
|
||||
// Handle modulo/division by minus one.
|
||||
__ Bind(div_by_minus_one_label());
|
||||
if (is_mod_) {
|
||||
__ xorq(RDX, RDX); // x % -1 = 0
|
||||
// Handle modulo/division by zero, if needed. Use superclass code.
|
||||
if (has_divide_by_zero()) {
|
||||
ThrowErrorSlowPathCode::EmitNativeCode(compiler);
|
||||
} else {
|
||||
__ negq(RAX); // x / -1 = -x
|
||||
__ Bind(entry_label()); // not used, but keeps destructor happy
|
||||
if (Assembler::EmittingComments()) {
|
||||
__ Comment("slow path %s operation (no throw)", name());
|
||||
}
|
||||
}
|
||||
__ jmp(exit_label());
|
||||
// Adjust modulo for negative sign.
|
||||
// if (right < 0)
|
||||
// out -= right;
|
||||
// else
|
||||
// out += right;
|
||||
if (is_mod_) {
|
||||
Label subtract;
|
||||
__ Bind(adjust_sign_label());
|
||||
__ testq(right_, right_);
|
||||
__ j(LESS, &subtract, Assembler::kNearJump);
|
||||
__ addq(RDX, right_);
|
||||
// Handle modulo/division by minus one, if needed.
|
||||
// Note: an exact -1 divisor is best optimized prior to codegen.
|
||||
if (has_divide_by_minus_one()) {
|
||||
__ Bind(div_by_minus_one_label());
|
||||
if (is_mod_) {
|
||||
__ xorq(RDX, RDX); // x % -1 = 0
|
||||
} else {
|
||||
__ negq(RAX); // x / -1 = -x
|
||||
}
|
||||
__ jmp(exit_label());
|
||||
__ Bind(&subtract);
|
||||
__ subq(RDX, right_);
|
||||
}
|
||||
// Adjust modulo for negative sign, optimized for known ranges.
|
||||
// if (divisor < 0)
|
||||
// out -= divisor;
|
||||
// else
|
||||
// out += divisor;
|
||||
if (has_adjust_sign()) {
|
||||
__ Bind(adjust_sign_label());
|
||||
if (RangeUtils::Overlaps(divisor_range_, -1, 1)) {
|
||||
// General case.
|
||||
Label subtract;
|
||||
__ testq(divisor_, divisor_);
|
||||
__ j(LESS, &subtract, Assembler::kNearJump);
|
||||
__ addq(RDX, divisor_);
|
||||
__ jmp(exit_label());
|
||||
__ Bind(&subtract);
|
||||
__ subq(RDX, divisor_);
|
||||
} else if (divisor_range_->IsPositive()) {
|
||||
// Always positive.
|
||||
__ addq(RDX, divisor_);
|
||||
} else {
|
||||
// Always negative.
|
||||
__ subq(RDX, divisor_);
|
||||
}
|
||||
__ jmp(exit_label());
|
||||
}
|
||||
}
|
||||
|
||||
const char* name() override { return "int64 divide"; }
|
||||
|
||||
Label* div_by_minus_one_label() { return &div_by_minus_one_label_; }
|
||||
Label* adjust_sign_label() { return &adjust_sign_label_; }
|
||||
bool has_divide_by_zero() { return RangeUtils::CanBeZero(divisor_range_); }
|
||||
|
||||
bool has_divide_by_minus_one() {
|
||||
return RangeUtils::Overlaps(divisor_range_, -1, -1);
|
||||
}
|
||||
|
||||
bool has_adjust_sign() { return is_mod_; }
|
||||
|
||||
bool is_needed() {
|
||||
return has_divide_by_zero() || has_divide_by_minus_one() ||
|
||||
has_adjust_sign();
|
||||
}
|
||||
|
||||
Label* div_by_minus_one_label() {
|
||||
ASSERT(has_divide_by_minus_one());
|
||||
return &div_by_minus_one_label_;
|
||||
}
|
||||
|
||||
Label* adjust_sign_label() {
|
||||
ASSERT(has_adjust_sign());
|
||||
return &adjust_sign_label_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_mod_;
|
||||
Register right_;
|
||||
Register divisor_;
|
||||
Range* divisor_range_;
|
||||
Label div_by_minus_one_label_;
|
||||
Label adjust_sign_label_;
|
||||
};
|
||||
|
@ -5262,19 +5304,23 @@ static void EmitInt64ModTruncDiv(FlowGraphCompiler* compiler,
|
|||
Register out) {
|
||||
ASSERT(op_kind == Token::kMOD || op_kind == Token::kTRUNCDIV);
|
||||
|
||||
// Set up a slow path.
|
||||
Int64DivideSlowPath* slow_path = new (Z)
|
||||
Int64DivideSlowPath(instruction, right, compiler->CurrentTryIndex());
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
// Prepare a slow path.
|
||||
Range* right_range = instruction->right()->definition()->range();
|
||||
Int64DivideSlowPath* slow_path = new (Z) Int64DivideSlowPath(
|
||||
instruction, right, right_range, compiler->CurrentTryIndex());
|
||||
|
||||
// Handle modulo/division by zero exception on slow path.
|
||||
__ testq(right, right);
|
||||
__ j(EQUAL, slow_path->entry_label());
|
||||
if (slow_path->has_divide_by_zero()) {
|
||||
__ testq(right, right);
|
||||
__ j(EQUAL, slow_path->entry_label());
|
||||
}
|
||||
|
||||
// Handle modulo/division by minus one explicitly on slow path
|
||||
// (to avoid arithmetic exception on 0x8000000000000000 / -1).
|
||||
__ cmpq(right, Immediate(-1));
|
||||
__ j(EQUAL, slow_path->div_by_minus_one_label());
|
||||
if (slow_path->has_divide_by_minus_one()) {
|
||||
__ cmpq(right, Immediate(-1));
|
||||
__ j(EQUAL, slow_path->div_by_minus_one_label());
|
||||
}
|
||||
|
||||
// Perform actual operation
|
||||
// out = left % right
|
||||
|
@ -5294,7 +5340,11 @@ static void EmitInt64ModTruncDiv(FlowGraphCompiler* compiler,
|
|||
ASSERT(out == RAX);
|
||||
ASSERT(tmp == RDX);
|
||||
}
|
||||
__ Bind(slow_path->exit_label());
|
||||
|
||||
if (slow_path->is_needed()) {
|
||||
__ Bind(slow_path->exit_label());
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OperandType>
|
||||
|
@ -5393,11 +5443,19 @@ LocationSummary* UnaryInt64OpInstr::MakeLocationSummary(Zone* zone,
|
|||
}
|
||||
|
||||
void UnaryInt64OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
ASSERT(op_kind() == Token::kBIT_NOT);
|
||||
const Register left = locs()->in(0).reg();
|
||||
const Register out = locs()->out(0).reg();
|
||||
ASSERT(out == left);
|
||||
__ notq(left);
|
||||
switch (op_kind()) {
|
||||
case Token::kBIT_NOT:
|
||||
__ notq(left);
|
||||
break;
|
||||
case Token::kNEGATE:
|
||||
__ negq(left);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
static void EmitShiftInt64ByConstant(FlowGraphCompiler* compiler,
|
||||
|
|
Loading…
Reference in a new issue