[vm/compiler] Cleanup CheckedSmi* instructions
This is a final cleanup of CheckedSmiOpInstr and CheckedSmiComparisonInstr instructions. Their uses were removed in the following changes: https://dart-review.googlesource.com/c/sdk/+/191761 https://dart-review.googlesource.com/c/sdk/+/191760 https://dart-review.googlesource.com/c/sdk/+/193447 TEST=ci Issue: https://github.com/dart-lang/sdk/issues/44852 Change-Id: Ica52ab9e2fcabab1913228818642a27a8d842f92 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193449 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
db0fa84d59
commit
2a14259fd0
@@ -3249,13 +3249,6 @@ class MegamorphicCacheDeserializationCluster : public DeserializationCluster {
|
||||
// In --use-bare-instruction we reduce the extra indirection via the
|
||||
// [Function] object by storing the entry point directly into the hashmap.
|
||||
//
|
||||
// Currently our AOT compiler will emit megamorphic calls in certain
|
||||
// situations (namely in slow-path code of CheckedSmi* instructions).
|
||||
//
|
||||
// TODO(compiler-team): Change the CheckedSmi* slow path code to use
|
||||
// normal switchable calls instead of megamorphic calls. (This is also a
|
||||
// memory balance beause [MegamorphicCache]s are per-selector while
|
||||
// [ICData] are per-callsite.)
|
||||
auto& cache = MegamorphicCache::Handle(d->zone());
|
||||
for (intptr_t i = start_index_; i < stop_index_; ++i) {
|
||||
cache ^= refs.At(i);
|
||||
|
||||
@@ -59,12 +59,12 @@ class AotCallSpecializer : public CallSpecializer {
|
||||
const Function& InterfaceTargetForTableDispatch(InstanceCallBaseInstr* call);
|
||||
|
||||
// Try to replace a call with a more specialized instruction working on
|
||||
// integers (e.g. BinaryInt64OpInstr, CheckedSmiComparisonInstr,
|
||||
// integers (e.g. BinaryInt64OpInstr, EqualityCompareInstr,
|
||||
// RelationalOpInstr)
|
||||
bool TryOptimizeIntegerOperation(TemplateDartCall<0>* call, Token::Kind kind);
|
||||
|
||||
// Try to replace a call with a more specialized instruction working on
|
||||
// doubles (e.g. BinaryDoubleOpInstr, CheckedSmiComparisonInstr,
|
||||
// doubles (e.g. BinaryDoubleOpInstr, EqualityCompareInstr,
|
||||
// RelationalOpInstr)
|
||||
bool TryOptimizeDoubleOperation(TemplateDartCall<0>* call, Token::Kind kind);
|
||||
|
||||
|
||||
@@ -1155,15 +1155,6 @@ void ConstantPropagator::VisitBinaryIntegerOp(BinaryIntegerOpInstr* binary_op) {
|
||||
SetValue(binary_op, non_constant_);
|
||||
}
|
||||
|
||||
void ConstantPropagator::VisitCheckedSmiOp(CheckedSmiOpInstr* instr) {
|
||||
SetValue(instr, non_constant_);
|
||||
}
|
||||
|
||||
void ConstantPropagator::VisitCheckedSmiComparison(
|
||||
CheckedSmiComparisonInstr* instr) {
|
||||
SetValue(instr, non_constant_);
|
||||
}
|
||||
|
||||
void ConstantPropagator::VisitBinarySmiOp(BinarySmiOpInstr* instr) {
|
||||
VisitBinaryIntegerOp(instr);
|
||||
}
|
||||
|
||||
@@ -983,8 +983,6 @@ class FlowGraphCompiler : public ValueObject {
|
||||
friend class StoreIndexedInstr; // For AddPcRelativeCallStubTarget().
|
||||
friend class StoreInstanceFieldInstr; // For AddPcRelativeCallStubTarget().
|
||||
friend class CheckStackOverflowSlowPath; // For pending_deoptimization_env_.
|
||||
friend class CheckedSmiSlowPath; // Same.
|
||||
friend class CheckedSmiComparisonSlowPath; // Same.
|
||||
friend class GraphInstrinsicCodeGenScope; // For optimizing_.
|
||||
|
||||
// Architecture specific implementation of simple native moves.
|
||||
|
||||
@@ -2437,79 +2437,6 @@ BinaryIntegerOpInstr* BinaryIntegerOpInstr::Make(
|
||||
return op;
|
||||
}
|
||||
|
||||
Definition* CheckedSmiOpInstr::Canonicalize(FlowGraph* flow_graph) {
|
||||
if ((left()->Type()->ToCid() == kSmiCid) &&
|
||||
(right()->Type()->ToCid() == kSmiCid)) {
|
||||
Definition* replacement = NULL;
|
||||
// Operations that can't deoptimize are specialized here: These include
|
||||
// bit-wise operators and comparisons. Other arithmetic operations can
|
||||
// overflow or divide by 0 and can't be specialized unless we have extra
|
||||
// range information.
|
||||
switch (op_kind()) {
|
||||
case Token::kBIT_AND:
|
||||
FALL_THROUGH;
|
||||
case Token::kBIT_OR:
|
||||
FALL_THROUGH;
|
||||
case Token::kBIT_XOR:
|
||||
replacement = new BinarySmiOpInstr(
|
||||
op_kind(), new Value(left()->definition()),
|
||||
new Value(right()->definition()), DeoptId::kNone);
|
||||
FALL_THROUGH;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (replacement != NULL) {
|
||||
flow_graph->InsertBefore(this, replacement, env(), FlowGraph::kValue);
|
||||
return replacement;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
ComparisonInstr* CheckedSmiComparisonInstr::CopyWithNewOperands(Value* left,
|
||||
Value* right) {
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Definition* CheckedSmiComparisonInstr::Canonicalize(FlowGraph* flow_graph) {
|
||||
CompileType* left_type = left()->Type();
|
||||
CompileType* right_type = right()->Type();
|
||||
intptr_t op_cid = kIllegalCid;
|
||||
SpeculativeMode speculative_mode = kGuardInputs;
|
||||
|
||||
if ((left_type->ToCid() == kSmiCid) && (right_type->ToCid() == kSmiCid)) {
|
||||
op_cid = kSmiCid;
|
||||
} else if ( // TODO(dartbug.com/30480): handle nullable types here
|
||||
left_type->IsNullableInt() && !left_type->is_nullable() &&
|
||||
right_type->IsNullableInt() && !right_type->is_nullable()) {
|
||||
op_cid = kMintCid;
|
||||
speculative_mode = kNotSpeculative;
|
||||
}
|
||||
|
||||
if (op_cid != kIllegalCid) {
|
||||
Definition* replacement = NULL;
|
||||
if (Token::IsRelationalOperator(kind())) {
|
||||
replacement = new RelationalOpInstr(
|
||||
source(), kind(), left()->CopyWithType(), right()->CopyWithType(),
|
||||
op_cid, DeoptId::kNone, speculative_mode);
|
||||
} else if (Token::IsEqualityOperator(kind())) {
|
||||
replacement = new EqualityCompareInstr(
|
||||
source(), kind(), left()->CopyWithType(), right()->CopyWithType(),
|
||||
op_cid, DeoptId::kNone, /*null_aware=*/false, speculative_mode);
|
||||
}
|
||||
if (replacement != NULL) {
|
||||
if (FLAG_trace_strong_mode_types && (op_cid == kMintCid)) {
|
||||
THR_Print("[Strong mode] Optimization: replacing %s with %s\n",
|
||||
ToCString(), replacement->ToCString());
|
||||
}
|
||||
flow_graph->InsertBefore(this, replacement, env(), FlowGraph::kValue);
|
||||
return replacement;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Definition* BinaryIntegerOpInstr::Canonicalize(FlowGraph* flow_graph) {
|
||||
// If both operands are constants evaluate this expression. Might
|
||||
// occur due to load forwarding after constant propagation pass
|
||||
|
||||
@@ -440,8 +440,6 @@ struct InstrAttrs {
|
||||
M(AllocateUninitializedContext, _) \
|
||||
M(CloneContext, _) \
|
||||
M(BinarySmiOp, kNoGC) \
|
||||
M(CheckedSmiComparison, _) \
|
||||
M(CheckedSmiOp, _) \
|
||||
M(BinaryInt32Op, kNoGC) \
|
||||
M(UnarySmiOp, kNoGC) \
|
||||
M(UnaryDoubleOp, kNoGC) \
|
||||
@@ -7617,100 +7615,6 @@ class UnaryInt64OpInstr : public UnaryIntegerOpInstr {
|
||||
DISALLOW_COPY_AND_ASSIGN(UnaryInt64OpInstr);
|
||||
};
|
||||
|
||||
class CheckedSmiOpInstr : public TemplateDefinition<2, Throws> {
|
||||
public:
|
||||
CheckedSmiOpInstr(Token::Kind op_kind,
|
||||
Value* left,
|
||||
Value* right,
|
||||
TemplateDartCall<0>* call)
|
||||
: TemplateDefinition(call->deopt_id()), call_(call), op_kind_(op_kind) {
|
||||
ASSERT(call->type_args_len() == 0);
|
||||
ASSERT(!call->IsInstanceCallBase() ||
|
||||
call->AsInstanceCallBase()->CanReceiverBeSmiBasedOnInterfaceTarget(
|
||||
Thread::Current()->zone()));
|
||||
|
||||
SetInputAt(0, left);
|
||||
SetInputAt(1, right);
|
||||
}
|
||||
|
||||
TemplateDartCall<0>* call() const { return call_; }
|
||||
Token::Kind op_kind() const { return op_kind_; }
|
||||
Value* left() const { return inputs_[0]; }
|
||||
Value* right() const { return inputs_[1]; }
|
||||
|
||||
virtual bool ComputeCanDeoptimize() const { return false; }
|
||||
|
||||
virtual CompileType ComputeType() const;
|
||||
virtual bool RecomputeType();
|
||||
|
||||
virtual bool HasUnknownSideEffects() const { return true; }
|
||||
virtual bool CanCallDart() const { return true; }
|
||||
|
||||
virtual Definition* Canonicalize(FlowGraph* flow_graph);
|
||||
|
||||
PRINT_OPERANDS_TO_SUPPORT
|
||||
|
||||
DECLARE_INSTRUCTION(CheckedSmiOp)
|
||||
|
||||
private:
|
||||
TemplateDartCall<0>* call_;
|
||||
const Token::Kind op_kind_;
|
||||
DISALLOW_COPY_AND_ASSIGN(CheckedSmiOpInstr);
|
||||
};
|
||||
|
||||
class CheckedSmiComparisonInstr : public TemplateComparison<2, Throws> {
|
||||
public:
|
||||
CheckedSmiComparisonInstr(Token::Kind op_kind,
|
||||
Value* left,
|
||||
Value* right,
|
||||
TemplateDartCall<0>* call)
|
||||
: TemplateComparison(call->source(), op_kind, call->deopt_id()),
|
||||
call_(call),
|
||||
is_negated_(false) {
|
||||
ASSERT(call->type_args_len() == 0);
|
||||
ASSERT(!call->IsInstanceCallBase() ||
|
||||
call->AsInstanceCallBase()->CanReceiverBeSmiBasedOnInterfaceTarget(
|
||||
Thread::Current()->zone()));
|
||||
|
||||
SetInputAt(0, left);
|
||||
SetInputAt(1, right);
|
||||
}
|
||||
|
||||
TemplateDartCall<0>* call() const { return call_; }
|
||||
|
||||
virtual bool ComputeCanDeoptimize() const { return false; }
|
||||
|
||||
virtual CompileType ComputeType() const;
|
||||
|
||||
virtual Definition* Canonicalize(FlowGraph* flow_graph);
|
||||
|
||||
virtual void NegateComparison() {
|
||||
ComparisonInstr::NegateComparison();
|
||||
is_negated_ = !is_negated_;
|
||||
}
|
||||
|
||||
bool is_negated() const { return is_negated_; }
|
||||
|
||||
virtual bool HasUnknownSideEffects() const { return true; }
|
||||
virtual bool CanCallDart() const { return true; }
|
||||
|
||||
PRINT_OPERANDS_TO_SUPPORT
|
||||
|
||||
DECLARE_INSTRUCTION(CheckedSmiComparison)
|
||||
|
||||
virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch);
|
||||
|
||||
virtual Condition EmitComparisonCode(FlowGraphCompiler* compiler,
|
||||
BranchLabels labels);
|
||||
|
||||
virtual ComparisonInstr* CopyWithNewOperands(Value* left, Value* right);
|
||||
|
||||
private:
|
||||
TemplateDartCall<0>* call_;
|
||||
bool is_negated_;
|
||||
DISALLOW_COPY_AND_ASSIGN(CheckedSmiComparisonInstr);
|
||||
};
|
||||
|
||||
class BinaryIntegerOpInstr : public TemplateDefinition<2, NoThrow, Pure> {
|
||||
public:
|
||||
BinaryIntegerOpInstr(Token::Kind op_kind,
|
||||
|
||||
@@ -3897,334 +3897,6 @@ static void EmitSmiShiftLeft(FlowGraphCompiler* compiler,
|
||||
}
|
||||
}
|
||||
|
||||
class CheckedSmiSlowPath : public TemplateSlowPathCode<CheckedSmiOpInstr> {
|
||||
public:
|
||||
CheckedSmiSlowPath(CheckedSmiOpInstr* instruction, intptr_t try_index)
|
||||
: TemplateSlowPathCode(instruction), try_index_(try_index) {}
|
||||
|
||||
static constexpr intptr_t kNumSlowPathArgs = 2;
|
||||
|
||||
virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
if (compiler::Assembler::EmittingComments()) {
|
||||
__ Comment("slow path smi operation");
|
||||
}
|
||||
__ Bind(entry_label());
|
||||
LocationSummary* locs = instruction()->locs();
|
||||
Register result = locs->out(0).reg();
|
||||
locs->live_registers()->Remove(Location::RegisterLocation(result));
|
||||
|
||||
compiler->SaveLiveRegisters(locs);
|
||||
if (instruction()->env() != NULL) {
|
||||
Environment* env =
|
||||
compiler->SlowPathEnvironmentFor(instruction(), kNumSlowPathArgs);
|
||||
compiler->pending_deoptimization_env_ = env;
|
||||
}
|
||||
__ Push(locs->in(0).reg());
|
||||
__ Push(locs->in(1).reg());
|
||||
const auto& selector = String::Handle(instruction()->call()->Selector());
|
||||
const auto& arguments_descriptor =
|
||||
Array::Handle(ArgumentsDescriptor::NewBoxed(
|
||||
/*type_args_len=*/0, /*num_arguments=*/2));
|
||||
compiler->EmitMegamorphicInstanceCall(
|
||||
selector, arguments_descriptor, instruction()->call()->deopt_id(),
|
||||
instruction()->source(), locs, try_index_, kNumSlowPathArgs);
|
||||
__ mov(result, compiler::Operand(R0));
|
||||
compiler->RestoreLiveRegisters(locs);
|
||||
__ b(exit_label());
|
||||
compiler->pending_deoptimization_env_ = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
intptr_t try_index_;
|
||||
};
|
||||
|
||||
LocationSummary* CheckedSmiOpInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 2;
|
||||
const intptr_t kNumTemps = 0;
|
||||
LocationSummary* summary = new (zone) LocationSummary(
|
||||
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
|
||||
summary->set_in(0, Location::RequiresRegister());
|
||||
summary->set_in(1, Location::RequiresRegister());
|
||||
summary->set_out(0, Location::RequiresRegister());
|
||||
return summary;
|
||||
}
|
||||
|
||||
void CheckedSmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
CheckedSmiSlowPath* slow_path =
|
||||
new CheckedSmiSlowPath(this, compiler->CurrentTryIndex());
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
// Test operands if necessary.
|
||||
Register left = locs()->in(0).reg();
|
||||
Register right = locs()->in(1).reg();
|
||||
Register result = locs()->out(0).reg();
|
||||
intptr_t left_cid = this->left()->Type()->ToCid();
|
||||
intptr_t right_cid = this->right()->Type()->ToCid();
|
||||
bool combined_smi_check = false;
|
||||
if (FLAG_use_slow_path) {
|
||||
__ b(slow_path->entry_label());
|
||||
} else if (this->left()->definition() == this->right()->definition()) {
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label());
|
||||
} else if (left_cid == kSmiCid) {
|
||||
__ BranchIfNotSmi(right, slow_path->entry_label());
|
||||
} else if (right_cid == kSmiCid) {
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label());
|
||||
} else {
|
||||
combined_smi_check = true;
|
||||
__ orr(result, left, compiler::Operand(right));
|
||||
__ BranchIfNotSmi(result, slow_path->entry_label());
|
||||
}
|
||||
switch (op_kind()) {
|
||||
case Token::kADD:
|
||||
__ adds(result, left, compiler::Operand(right));
|
||||
__ b(slow_path->entry_label(), VS);
|
||||
break;
|
||||
case Token::kSUB:
|
||||
__ subs(result, left, compiler::Operand(right));
|
||||
__ b(slow_path->entry_label(), VS);
|
||||
break;
|
||||
case Token::kMUL:
|
||||
__ SmiUntag(IP, left);
|
||||
__ smull(result, IP, IP, right);
|
||||
// IP: result bits 32..63.
|
||||
__ cmp(IP, compiler::Operand(result, ASR, 31));
|
||||
__ b(slow_path->entry_label(), NE);
|
||||
break;
|
||||
case Token::kBIT_OR:
|
||||
// Operation may be part of combined smi check.
|
||||
if (!combined_smi_check) {
|
||||
__ orr(result, left, compiler::Operand(right));
|
||||
}
|
||||
break;
|
||||
case Token::kBIT_AND:
|
||||
__ and_(result, left, compiler::Operand(right));
|
||||
break;
|
||||
case Token::kBIT_XOR:
|
||||
__ eor(result, left, compiler::Operand(right));
|
||||
break;
|
||||
case Token::kSHL:
|
||||
ASSERT(result != left);
|
||||
ASSERT(result != right);
|
||||
__ CompareImmediate(
|
||||
right, compiler::target::ToRawSmi(compiler::target::kSmiBits));
|
||||
__ b(slow_path->entry_label(), HI);
|
||||
|
||||
__ SmiUntag(TMP, right);
|
||||
// Check for overflow by shifting left and shifting back arithmetically.
|
||||
// If the result is different from the original, there was overflow.
|
||||
__ Lsl(result, left, TMP);
|
||||
__ cmp(left, compiler::Operand(result, ASR, TMP));
|
||||
__ b(slow_path->entry_label(), NE);
|
||||
break;
|
||||
case Token::kSHR:
|
||||
ASSERT(result != left);
|
||||
ASSERT(result != right);
|
||||
__ CompareImmediate(
|
||||
right, compiler::target::ToRawSmi(compiler::target::kSmiBits));
|
||||
__ b(slow_path->entry_label(), HI);
|
||||
|
||||
__ SmiUntag(result, right);
|
||||
__ SmiUntag(TMP, left);
|
||||
__ Asr(result, TMP, result);
|
||||
__ SmiTag(result);
|
||||
break;
|
||||
case Token::kUSHR: {
|
||||
ASSERT(result != left);
|
||||
ASSERT(result != right);
|
||||
__ CompareImmediate(right, compiler::target::ToRawSmi(kBitsPerInt64));
|
||||
__ b(slow_path->entry_label(), UNSIGNED_GREATER_EQUAL);
|
||||
|
||||
compiler::Label done;
|
||||
__ SmiUntag(result, right);
|
||||
// 64-bit representation of left operand value:
|
||||
//
|
||||
// ss...sssss s s xxxxxxxxxxxxx
|
||||
// | | | | | |
|
||||
// 63 32 31 30 kSmiBits-1 0
|
||||
//
|
||||
// Where 's' is a sign bit.
|
||||
//
|
||||
// If left operand is negative (sign bit is set), then
|
||||
// result will fit into Smi range if and only if
|
||||
// the shift amount >= 64 - kSmiBits.
|
||||
//
|
||||
// If left operand is non-negative, the result always
|
||||
// fits into Smi range.
|
||||
//
|
||||
__ CompareImmediate(result, 64 - compiler::target::kSmiBits);
|
||||
// Shift amount >= 64 - kSmiBits > 32, but < 64.
|
||||
// Result is guaranteed to fit into Smi range.
|
||||
// Low (Smi) part of the left operand is shifted out.
|
||||
// High part is filled with sign bits.
|
||||
__ sub(result, result, compiler::Operand(32), GE);
|
||||
__ Asr(TMP, left, compiler::Operand(31), GE);
|
||||
__ Lsr(result, TMP, result, GE);
|
||||
__ SmiTag(result, GE);
|
||||
__ b(&done, GE);
|
||||
// Shift amount < 64 - kSmiBits.
|
||||
// If left is negative, then result will not fit into Smi range.
|
||||
// Also deopt in case of negative shift amount.
|
||||
__ tst(left, compiler::Operand(left));
|
||||
__ b(slow_path->entry_label(), MI);
|
||||
// At this point left operand is non-negative, so unsigned shift
|
||||
// can't overflow.
|
||||
__ CompareImmediate(result, compiler::target::kSmiBits);
|
||||
// Left operand >= 0, shift amount >= kSmiBits. Result is 0.
|
||||
__ LoadImmediate(result, 0, GE);
|
||||
// Left operand >= 0, shift amount < kSmiBits < 32.
|
||||
__ SmiUntag(TMP, left, LT);
|
||||
__ Lsr(result, TMP, result, LT);
|
||||
__ SmiTag(result, LT);
|
||||
__ Bind(&done);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
__ Bind(slow_path->exit_label());
|
||||
}
|
||||
|
||||
class CheckedSmiComparisonSlowPath
|
||||
: public TemplateSlowPathCode<CheckedSmiComparisonInstr> {
|
||||
public:
|
||||
static constexpr intptr_t kNumSlowPathArgs = 2;
|
||||
|
||||
CheckedSmiComparisonSlowPath(CheckedSmiComparisonInstr* instruction,
|
||||
Environment* env,
|
||||
intptr_t try_index,
|
||||
BranchLabels labels,
|
||||
bool merged)
|
||||
: TemplateSlowPathCode(instruction),
|
||||
try_index_(try_index),
|
||||
labels_(labels),
|
||||
merged_(merged),
|
||||
env_(env) {
|
||||
// The environment must either come from the comparison or the environment
|
||||
// was cleared from the comparison (and moved to a branch).
|
||||
ASSERT(env == instruction->env() ||
|
||||
(merged && instruction->env() == nullptr));
|
||||
}
|
||||
|
||||
virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
if (compiler::Assembler::EmittingComments()) {
|
||||
__ Comment("slow path smi operation");
|
||||
}
|
||||
__ Bind(entry_label());
|
||||
LocationSummary* locs = instruction()->locs();
|
||||
Register result = merged_ ? locs->temp(0).reg() : locs->out(0).reg();
|
||||
locs->live_registers()->Remove(Location::RegisterLocation(result));
|
||||
|
||||
compiler->SaveLiveRegisters(locs);
|
||||
if (env_ != nullptr) {
|
||||
compiler->pending_deoptimization_env_ =
|
||||
compiler->SlowPathEnvironmentFor(env_, locs, kNumSlowPathArgs);
|
||||
}
|
||||
__ Push(locs->in(0).reg());
|
||||
__ Push(locs->in(1).reg());
|
||||
const auto& selector = String::Handle(instruction()->call()->Selector());
|
||||
const auto& arguments_descriptor =
|
||||
Array::Handle(ArgumentsDescriptor::NewBoxed(
|
||||
/*type_args_len=*/0, /*num_arguments=*/2));
|
||||
compiler->EmitMegamorphicInstanceCall(
|
||||
selector, arguments_descriptor, instruction()->call()->deopt_id(),
|
||||
instruction()->source(), locs, try_index_, kNumSlowPathArgs);
|
||||
__ mov(result, compiler::Operand(R0));
|
||||
compiler->RestoreLiveRegisters(locs);
|
||||
compiler->pending_deoptimization_env_ = nullptr;
|
||||
if (merged_) {
|
||||
__ CompareObject(result, Bool::True());
|
||||
__ b(instruction()->is_negated() ? labels_.false_label
|
||||
: labels_.true_label,
|
||||
EQ);
|
||||
__ b(instruction()->is_negated() ? labels_.true_label
|
||||
: labels_.false_label);
|
||||
} else {
|
||||
if (instruction()->is_negated()) {
|
||||
// Need to negate the result of slow path call.
|
||||
__ CompareObject(result, Bool::True());
|
||||
__ LoadObject(result, Bool::True(), NE);
|
||||
__ LoadObject(result, Bool::False(), EQ);
|
||||
}
|
||||
__ b(exit_label());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
intptr_t try_index_;
|
||||
BranchLabels labels_;
|
||||
bool merged_;
|
||||
Environment* env_;
|
||||
};
|
||||
|
||||
LocationSummary* CheckedSmiComparisonInstr::MakeLocationSummary(
|
||||
Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 2;
|
||||
const intptr_t kNumTemps = 1;
|
||||
LocationSummary* summary = new (zone) LocationSummary(
|
||||
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
|
||||
summary->set_in(0, Location::RequiresRegister());
|
||||
summary->set_in(1, Location::RequiresRegister());
|
||||
summary->set_temp(0, Location::RequiresRegister());
|
||||
summary->set_out(0, Location::RequiresRegister());
|
||||
return summary;
|
||||
}
|
||||
|
||||
Condition CheckedSmiComparisonInstr::EmitComparisonCode(
|
||||
FlowGraphCompiler* compiler,
|
||||
BranchLabels labels) {
|
||||
return EmitSmiComparisonOp(compiler, locs(), kind());
|
||||
}
|
||||
|
||||
#define EMIT_SMI_CHECK \
|
||||
Register left = locs()->in(0).reg(); \
|
||||
Register right = locs()->in(1).reg(); \
|
||||
Register temp = locs()->temp(0).reg(); \
|
||||
intptr_t left_cid = this->left()->Type()->ToCid(); \
|
||||
intptr_t right_cid = this->right()->Type()->ToCid(); \
|
||||
if (FLAG_use_slow_path) { \
|
||||
__ b(slow_path->entry_label()); \
|
||||
} else if (this->left()->definition() == this->right()->definition()) { \
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label()); \
|
||||
} else if (left_cid == kSmiCid) { \
|
||||
__ BranchIfNotSmi(right, slow_path->entry_label()); \
|
||||
} else if (right_cid == kSmiCid) { \
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label()); \
|
||||
} else { \
|
||||
__ orr(temp, left, compiler::Operand(right)); \
|
||||
__ BranchIfNotSmi(temp, slow_path->entry_label()); \
|
||||
}
|
||||
|
||||
void CheckedSmiComparisonInstr::EmitBranchCode(FlowGraphCompiler* compiler,
|
||||
BranchInstr* branch) {
|
||||
BranchLabels labels = compiler->CreateBranchLabels(branch);
|
||||
CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
|
||||
this, branch->env(), compiler->CurrentTryIndex(), labels,
|
||||
/* merged = */ true);
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
EMIT_SMI_CHECK;
|
||||
Condition true_condition = EmitComparisonCode(compiler, labels);
|
||||
ASSERT(true_condition != kInvalidCondition);
|
||||
EmitBranchOnCondition(compiler, true_condition, labels);
|
||||
__ Bind(slow_path->exit_label());
|
||||
}
|
||||
|
||||
void CheckedSmiComparisonInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
BranchLabels labels = {NULL, NULL, NULL};
|
||||
CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
|
||||
this, env(), compiler->CurrentTryIndex(), labels,
|
||||
/* merged = */ false);
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
EMIT_SMI_CHECK;
|
||||
Condition true_condition = EmitComparisonCode(compiler, labels);
|
||||
ASSERT(true_condition != kInvalidCondition);
|
||||
Register result = locs()->out(0).reg();
|
||||
__ LoadObject(result, Bool::True(), true_condition);
|
||||
__ LoadObject(result, Bool::False(), InvertCondition(true_condition));
|
||||
__ Bind(slow_path->exit_label());
|
||||
}
|
||||
#undef EMIT_SMI_CHECK
|
||||
|
||||
LocationSummary* BinarySmiOpInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 2;
|
||||
|
||||
@@ -3467,311 +3467,6 @@ static void EmitSmiShiftLeft(FlowGraphCompiler* compiler,
|
||||
}
|
||||
}
|
||||
|
||||
class CheckedSmiSlowPath : public TemplateSlowPathCode<CheckedSmiOpInstr> {
|
||||
public:
|
||||
static constexpr intptr_t kNumSlowPathArgs = 2;
|
||||
|
||||
CheckedSmiSlowPath(CheckedSmiOpInstr* instruction, intptr_t try_index)
|
||||
: TemplateSlowPathCode(instruction), try_index_(try_index) {}
|
||||
|
||||
virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
if (compiler::Assembler::EmittingComments()) {
|
||||
__ Comment("slow path smi operation");
|
||||
}
|
||||
__ Bind(entry_label());
|
||||
LocationSummary* locs = instruction()->locs();
|
||||
Register result = locs->out(0).reg();
|
||||
locs->live_registers()->Remove(Location::RegisterLocation(result));
|
||||
|
||||
compiler->SaveLiveRegisters(locs);
|
||||
if (instruction()->env() != NULL) {
|
||||
Environment* env =
|
||||
compiler->SlowPathEnvironmentFor(instruction(), kNumSlowPathArgs);
|
||||
compiler->pending_deoptimization_env_ = env;
|
||||
}
|
||||
__ PushPair(locs->in(1).reg(), locs->in(0).reg());
|
||||
const auto& selector = String::Handle(instruction()->call()->Selector());
|
||||
const auto& arguments_descriptor =
|
||||
Array::Handle(ArgumentsDescriptor::NewBoxed(
|
||||
/*type_args_len=*/0, /*num_arguments=*/2));
|
||||
compiler->EmitMegamorphicInstanceCall(
|
||||
selector, arguments_descriptor, instruction()->call()->deopt_id(),
|
||||
instruction()->source(), locs, try_index_, kNumSlowPathArgs);
|
||||
__ mov(result, R0);
|
||||
compiler->RestoreLiveRegisters(locs);
|
||||
__ b(exit_label());
|
||||
compiler->pending_deoptimization_env_ = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
intptr_t try_index_;
|
||||
};
|
||||
|
||||
LocationSummary* CheckedSmiOpInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 2;
|
||||
const intptr_t kNumTemps = 0;
|
||||
LocationSummary* summary = new (zone) LocationSummary(
|
||||
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
|
||||
summary->set_in(0, Location::RequiresRegister());
|
||||
summary->set_in(1, Location::RequiresRegister());
|
||||
summary->set_out(0, Location::RequiresRegister());
|
||||
return summary;
|
||||
}
|
||||
|
||||
void CheckedSmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
CheckedSmiSlowPath* slow_path =
|
||||
new CheckedSmiSlowPath(this, compiler->CurrentTryIndex());
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
// Test operands if necessary.
|
||||
Register left = locs()->in(0).reg();
|
||||
Register right = locs()->in(1).reg();
|
||||
Register result = locs()->out(0).reg();
|
||||
intptr_t left_cid = this->left()->Type()->ToCid();
|
||||
intptr_t right_cid = this->right()->Type()->ToCid();
|
||||
bool combined_smi_check = false;
|
||||
if (FLAG_use_slow_path) {
|
||||
__ b(slow_path->entry_label());
|
||||
} else if (this->left()->definition() == this->right()->definition()) {
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label());
|
||||
} else if (left_cid == kSmiCid) {
|
||||
__ BranchIfNotSmi(right, slow_path->entry_label());
|
||||
} else if (right_cid == kSmiCid) {
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label());
|
||||
} else {
|
||||
combined_smi_check = true;
|
||||
__ orr(result, left, compiler::Operand(right));
|
||||
__ BranchIfNotSmi(result, slow_path->entry_label());
|
||||
}
|
||||
|
||||
switch (op_kind()) {
|
||||
case Token::kADD:
|
||||
__ adds(result, left, compiler::Operand(right), compiler::kObjectBytes);
|
||||
__ b(slow_path->entry_label(), VS);
|
||||
break;
|
||||
case Token::kSUB:
|
||||
__ subs(result, left, compiler::Operand(right), compiler::kObjectBytes);
|
||||
__ b(slow_path->entry_label(), VS);
|
||||
break;
|
||||
case Token::kMUL:
|
||||
__ SmiUntag(TMP, left);
|
||||
#if !defined(DART_COMPRESSED_POINTERS)
|
||||
__ mul(result, TMP, right);
|
||||
__ smulh(TMP, TMP, right);
|
||||
// TMP: result bits 64..127.
|
||||
#else
|
||||
__ smull(result, TMP, right);
|
||||
__ AsrImmediate(TMP, result, 31);
|
||||
// TMP: result bits 32..63.
|
||||
#endif
|
||||
__ cmp(TMP, compiler::Operand(result, ASR, 63));
|
||||
__ b(slow_path->entry_label(), NE);
|
||||
break;
|
||||
case Token::kBIT_OR:
|
||||
// Operation may be part of combined smi check.
|
||||
if (!combined_smi_check) {
|
||||
__ orr(result, left, compiler::Operand(right));
|
||||
}
|
||||
break;
|
||||
case Token::kBIT_AND:
|
||||
__ and_(result, left, compiler::Operand(right));
|
||||
break;
|
||||
case Token::kBIT_XOR:
|
||||
__ eor(result, left, compiler::Operand(right));
|
||||
break;
|
||||
case Token::kSHL:
|
||||
ASSERT(result != left);
|
||||
ASSERT(result != right);
|
||||
__ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
|
||||
__ b(slow_path->entry_label(), CS);
|
||||
|
||||
__ SmiUntag(TMP, right);
|
||||
__ lslv(result, left, TMP, compiler::kObjectBytes);
|
||||
__ asrv(TMP2, result, TMP, compiler::kObjectBytes);
|
||||
__ cmp(left, compiler::Operand(TMP2), compiler::kObjectBytes);
|
||||
__ b(slow_path->entry_label(), NE); // Overflow.
|
||||
break;
|
||||
case Token::kSHR:
|
||||
ASSERT(result != left);
|
||||
ASSERT(result != right);
|
||||
__ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
|
||||
__ b(slow_path->entry_label(), CS);
|
||||
|
||||
__ SmiUntag(result, right);
|
||||
__ SmiUntag(TMP, left);
|
||||
__ asrv(result, TMP, result, compiler::kObjectBytes);
|
||||
__ SmiTag(result);
|
||||
break;
|
||||
case Token::kUSHR:
|
||||
ASSERT(result != left);
|
||||
ASSERT(result != right);
|
||||
__ CompareObject(right, Smi::ZoneHandle(Smi::New(kBitsPerInt64)));
|
||||
__ b(slow_path->entry_label(), UNSIGNED_GREATER_EQUAL);
|
||||
|
||||
__ SmiUntag(result, right);
|
||||
__ SmiUntag(TMP, left);
|
||||
__ lsrv(result, TMP, result);
|
||||
__ SmiTagAndBranchIfOverflow(result, slow_path->entry_label());
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
__ Bind(slow_path->exit_label());
|
||||
}
|
||||
|
||||
class CheckedSmiComparisonSlowPath
|
||||
: public TemplateSlowPathCode<CheckedSmiComparisonInstr> {
|
||||
public:
|
||||
static constexpr intptr_t kNumSlowPathArgs = 2;
|
||||
|
||||
CheckedSmiComparisonSlowPath(CheckedSmiComparisonInstr* instruction,
|
||||
Environment* env,
|
||||
intptr_t try_index,
|
||||
BranchLabels labels,
|
||||
bool merged)
|
||||
: TemplateSlowPathCode(instruction),
|
||||
try_index_(try_index),
|
||||
labels_(labels),
|
||||
merged_(merged),
|
||||
env_(env) {
|
||||
// The environment must either come from the comparison or the environment
|
||||
// was cleared from the comparison (and moved to a branch).
|
||||
ASSERT(env == instruction->env() ||
|
||||
(merged && instruction->env() == nullptr));
|
||||
}
|
||||
|
||||
virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
if (compiler::Assembler::EmittingComments()) {
|
||||
__ Comment("slow path smi operation");
|
||||
}
|
||||
__ Bind(entry_label());
|
||||
LocationSummary* locs = instruction()->locs();
|
||||
Register result = merged_ ? locs->temp(0).reg() : locs->out(0).reg();
|
||||
locs->live_registers()->Remove(Location::RegisterLocation(result));
|
||||
|
||||
compiler->SaveLiveRegisters(locs);
|
||||
if (env_ != nullptr) {
|
||||
compiler->pending_deoptimization_env_ =
|
||||
compiler->SlowPathEnvironmentFor(env_, locs, kNumSlowPathArgs);
|
||||
}
|
||||
__ PushPair(locs->in(1).reg(), locs->in(0).reg());
|
||||
const auto& selector = String::Handle(instruction()->call()->Selector());
|
||||
const auto& arguments_descriptor =
|
||||
Array::Handle(ArgumentsDescriptor::NewBoxed(
|
||||
/*type_args_len=*/0, /*num_arguments=*/2));
|
||||
compiler->EmitMegamorphicInstanceCall(
|
||||
selector, arguments_descriptor, instruction()->call()->deopt_id(),
|
||||
instruction()->source(), locs, try_index_, kNumSlowPathArgs);
|
||||
__ mov(result, R0);
|
||||
compiler->RestoreLiveRegisters(locs);
|
||||
compiler->pending_deoptimization_env_ = nullptr;
|
||||
if (merged_) {
|
||||
__ CompareObject(result, Bool::True());
|
||||
__ b(instruction()->is_negated() ? labels_.false_label
|
||||
: labels_.true_label,
|
||||
EQ);
|
||||
__ b(instruction()->is_negated() ? labels_.true_label
|
||||
: labels_.false_label);
|
||||
ASSERT(exit_label()->IsUnused());
|
||||
} else {
|
||||
ASSERT(!instruction()->is_negated());
|
||||
__ b(exit_label());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
intptr_t try_index_;
|
||||
BranchLabels labels_;
|
||||
bool merged_;
|
||||
Environment* env_;
|
||||
};
|
||||
|
||||
LocationSummary* CheckedSmiComparisonInstr::MakeLocationSummary(
|
||||
Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 2;
|
||||
const intptr_t kNumTemps = 1;
|
||||
LocationSummary* summary = new (zone) LocationSummary(
|
||||
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
|
||||
summary->set_in(0, Location::RequiresRegister());
|
||||
summary->set_in(1, Location::RequiresRegister());
|
||||
summary->set_temp(0, Location::RequiresRegister());
|
||||
summary->set_out(0, Location::RequiresRegister());
|
||||
return summary;
|
||||
}
|
||||
|
||||
Condition CheckedSmiComparisonInstr::EmitComparisonCode(
|
||||
FlowGraphCompiler* compiler,
|
||||
BranchLabels labels) {
|
||||
return EmitSmiComparisonOp(compiler, locs(), kind(), labels);
|
||||
}
|
||||
|
||||
#define EMIT_SMI_CHECK \
|
||||
Register left = locs()->in(0).reg(); \
|
||||
Register right = locs()->in(1).reg(); \
|
||||
Register temp = locs()->temp(0).reg(); \
|
||||
intptr_t left_cid = this->left()->Type()->ToCid(); \
|
||||
intptr_t right_cid = this->right()->Type()->ToCid(); \
|
||||
if (FLAG_use_slow_path) { \
|
||||
__ b(slow_path->entry_label()); \
|
||||
} else if (this->left()->definition() == this->right()->definition()) { \
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label()); \
|
||||
} else if (left_cid == kSmiCid) { \
|
||||
__ BranchIfNotSmi(right, slow_path->entry_label()); \
|
||||
} else if (right_cid == kSmiCid) { \
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label()); \
|
||||
} else { \
|
||||
__ orr(temp, left, compiler::Operand(right)); \
|
||||
__ BranchIfNotSmi(temp, slow_path->entry_label()); \
|
||||
}
|
||||
|
||||
void CheckedSmiComparisonInstr::EmitBranchCode(FlowGraphCompiler* compiler,
|
||||
BranchInstr* branch) {
|
||||
BranchLabels labels = compiler->CreateBranchLabels(branch);
|
||||
CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
|
||||
this, branch->env(), compiler->CurrentTryIndex(), labels,
|
||||
/* merged = */ true);
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
EMIT_SMI_CHECK;
|
||||
Condition true_condition = EmitComparisonCode(compiler, labels);
|
||||
if (true_condition != kInvalidCondition) {
|
||||
EmitBranchOnCondition(compiler, true_condition, labels);
|
||||
}
|
||||
// No need to bind slow_path->exit_label() as slow path exits through
|
||||
// true/false branch labels.
|
||||
}
|
||||
|
||||
void CheckedSmiComparisonInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
// Zone-allocate labels to pass them to slow-path which outlives local scope.
|
||||
compiler::Label* true_label = new (Z) compiler::Label();
|
||||
compiler::Label* false_label = new (Z) compiler::Label();
|
||||
compiler::Label done;
|
||||
BranchLabels labels = {true_label, false_label, false_label};
|
||||
// In case of negated comparison result of a slow path call should be negated.
|
||||
// For this purpose, 'merged' slow path is generated: it tests
|
||||
// result of a call and jumps directly to true or false label.
|
||||
CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
|
||||
this, env(), compiler->CurrentTryIndex(), labels,
|
||||
/* merged = */ is_negated());
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
EMIT_SMI_CHECK;
|
||||
Condition true_condition = EmitComparisonCode(compiler, labels);
|
||||
if (true_condition != kInvalidCondition) {
|
||||
EmitBranchOnCondition(compiler, true_condition, labels);
|
||||
}
|
||||
Register result = locs()->out(0).reg();
|
||||
__ Bind(false_label);
|
||||
__ LoadObject(result, Bool::False());
|
||||
__ b(&done);
|
||||
__ Bind(true_label);
|
||||
__ LoadObject(result, Bool::True());
|
||||
__ Bind(&done);
|
||||
// In case of negated comparison slow path exits through true/false labels.
|
||||
if (!is_negated()) {
|
||||
__ Bind(slow_path->exit_label());
|
||||
}
|
||||
}
|
||||
|
||||
LocationSummary* BinarySmiOpInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 2;
|
||||
|
||||
@@ -3127,45 +3127,6 @@ static void EmitSmiShiftLeft(FlowGraphCompiler* compiler,
|
||||
}
|
||||
}
|
||||
|
||||
LocationSummary* CheckedSmiOpInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
// Only for precompiled code, not on ia32 currently.
|
||||
UNIMPLEMENTED();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CheckedSmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
// Only for precompiled code, not on ia32 currently.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
LocationSummary* CheckedSmiComparisonInstr::MakeLocationSummary(
|
||||
Zone* zone,
|
||||
bool opt) const {
|
||||
// Only for precompiled code, not on ia32 currently.
|
||||
UNIMPLEMENTED();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Condition CheckedSmiComparisonInstr::EmitComparisonCode(
|
||||
FlowGraphCompiler* compiler,
|
||||
BranchLabels labels) {
|
||||
// Only for precompiled code, not on ia32 currently.
|
||||
UNIMPLEMENTED();
|
||||
return ZERO;
|
||||
}
|
||||
|
||||
void CheckedSmiComparisonInstr::EmitBranchCode(FlowGraphCompiler* compiler,
|
||||
BranchInstr* instr) {
|
||||
// Only for precompiled code, not on ia32 currently.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void CheckedSmiComparisonInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
// Only for precompiled code, not on ia32 currently.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
static bool IsSmiValue(const Object& constant, intptr_t value) {
|
||||
return constant.IsSmi() && (Smi::Cast(constant).Value() == value);
|
||||
}
|
||||
|
||||
@@ -730,22 +730,6 @@ void UnaryIntegerOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
|
||||
value()->PrintTo(f);
|
||||
}
|
||||
|
||||
void CheckedSmiOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
|
||||
f->Printf("%s", Token::Str(op_kind()));
|
||||
f->AddString(", ");
|
||||
left()->PrintTo(f);
|
||||
f->AddString(", ");
|
||||
right()->PrintTo(f);
|
||||
}
|
||||
|
||||
void CheckedSmiComparisonInstr::PrintOperandsTo(BaseTextBuffer* f) const {
|
||||
f->Printf("%s", Token::Str(kind()));
|
||||
f->AddString(", ");
|
||||
left()->PrintTo(f);
|
||||
f->AddString(", ");
|
||||
right()->PrintTo(f);
|
||||
}
|
||||
|
||||
void BinaryIntegerOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
|
||||
f->Printf("%s", Token::Str(op_kind()));
|
||||
if (is_truncating()) {
|
||||
|
||||
@@ -202,8 +202,7 @@ void TestPipeline::CompileGraphAndAttachFunction() {
|
||||
|
||||
// We expect there to be no deoptimizations.
|
||||
if (mode_ == CompilerPass::kAOT) {
|
||||
// TODO(kustermann): Enable this once we get rid of [CheckedSmiSlowPath]s.
|
||||
// EXPECT(deopt_info_array.IsNull() || deopt_info_array.Length() == 0);
|
||||
EXPECT(deopt_info_array.IsNull() || deopt_info_array.Length() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3554,342 +3554,6 @@ static void EmitSmiShiftLeft(FlowGraphCompiler* compiler,
|
||||
}
|
||||
}
|
||||
|
||||
class CheckedSmiSlowPath : public TemplateSlowPathCode<CheckedSmiOpInstr> {
|
||||
public:
|
||||
CheckedSmiSlowPath(CheckedSmiOpInstr* instruction, intptr_t try_index)
|
||||
: TemplateSlowPathCode(instruction), try_index_(try_index) {}
|
||||
|
||||
static constexpr intptr_t kNumSlowPathArgs = 2;
|
||||
|
||||
virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
if (compiler::Assembler::EmittingComments()) {
|
||||
__ Comment("slow path smi operation");
|
||||
}
|
||||
__ Bind(entry_label());
|
||||
LocationSummary* locs = instruction()->locs();
|
||||
Register result = locs->out(0).reg();
|
||||
locs->live_registers()->Remove(Location::RegisterLocation(result));
|
||||
|
||||
compiler->SaveLiveRegisters(locs);
|
||||
if (instruction()->env() != NULL) {
|
||||
Environment* env =
|
||||
compiler->SlowPathEnvironmentFor(instruction(), kNumSlowPathArgs);
|
||||
compiler->pending_deoptimization_env_ = env;
|
||||
}
|
||||
__ pushq(locs->in(0).reg());
|
||||
__ pushq(locs->in(1).reg());
|
||||
const auto& selector = String::Handle(instruction()->call()->Selector());
|
||||
const auto& arguments_descriptor =
|
||||
Array::Handle(ArgumentsDescriptor::NewBoxed(
|
||||
/*type_args_len=*/0, /*num_arguments=*/2));
|
||||
compiler->EmitMegamorphicInstanceCall(
|
||||
selector, arguments_descriptor, instruction()->call()->deopt_id(),
|
||||
instruction()->source(), locs, try_index_, kNumSlowPathArgs);
|
||||
__ MoveRegister(result, RAX);
|
||||
compiler->RestoreLiveRegisters(locs);
|
||||
__ jmp(exit_label());
|
||||
compiler->pending_deoptimization_env_ = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
intptr_t try_index_;
|
||||
};
|
||||
|
||||
LocationSummary* CheckedSmiOpInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
bool is_shift = (op_kind() == Token::kSHL) || (op_kind() == Token::kSHR) ||
|
||||
(op_kind() == Token::kUSHR);
|
||||
const intptr_t kNumInputs = 2;
|
||||
const intptr_t kNumTemps = is_shift ? 1 : 0;
|
||||
LocationSummary* summary = new (zone) LocationSummary(
|
||||
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
|
||||
summary->set_in(0, Location::RequiresRegister());
|
||||
summary->set_in(1, Location::RequiresRegister());
|
||||
switch (op_kind()) {
|
||||
case Token::kADD:
|
||||
case Token::kSUB:
|
||||
case Token::kMUL:
|
||||
case Token::kSHL:
|
||||
case Token::kSHR:
|
||||
case Token::kUSHR:
|
||||
summary->set_out(0, Location::RequiresRegister());
|
||||
break;
|
||||
case Token::kBIT_OR:
|
||||
case Token::kBIT_AND:
|
||||
case Token::kBIT_XOR:
|
||||
summary->set_out(0, Location::SameAsFirstInput());
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
if (is_shift) {
|
||||
summary->set_temp(0, Location::RegisterLocation(RCX));
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
void CheckedSmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
CheckedSmiSlowPath* slow_path =
|
||||
new CheckedSmiSlowPath(this, compiler->CurrentTryIndex());
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
// Test operands if necessary.
|
||||
|
||||
intptr_t left_cid = left()->Type()->ToCid();
|
||||
intptr_t right_cid = right()->Type()->ToCid();
|
||||
Register left = locs()->in(0).reg();
|
||||
Register right = locs()->in(1).reg();
|
||||
if (FLAG_use_slow_path) {
|
||||
__ jmp(slow_path->entry_label());
|
||||
} else if (this->left()->definition() == this->right()->definition()) {
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label());
|
||||
} else if (left_cid == kSmiCid) {
|
||||
__ BranchIfNotSmi(right, slow_path->entry_label());
|
||||
} else if (right_cid == kSmiCid) {
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label());
|
||||
} else {
|
||||
__ movq(TMP, left);
|
||||
__ orq(TMP, right);
|
||||
__ BranchIfNotSmi(TMP, slow_path->entry_label());
|
||||
}
|
||||
Register result = locs()->out(0).reg();
|
||||
switch (op_kind()) {
|
||||
case Token::kADD:
|
||||
__ movq(result, left);
|
||||
__ OBJ(add)(result, right);
|
||||
__ j(OVERFLOW, slow_path->entry_label());
|
||||
break;
|
||||
case Token::kSUB:
|
||||
__ movq(result, left);
|
||||
__ OBJ(sub)(result, right);
|
||||
__ j(OVERFLOW, slow_path->entry_label());
|
||||
break;
|
||||
case Token::kMUL:
|
||||
__ movq(result, left);
|
||||
__ SmiUntag(result);
|
||||
__ OBJ(imul)(result, right);
|
||||
__ j(OVERFLOW, slow_path->entry_label());
|
||||
break;
|
||||
case Token::kBIT_OR:
|
||||
ASSERT(left == result);
|
||||
__ orq(result, right);
|
||||
break;
|
||||
case Token::kBIT_AND:
|
||||
ASSERT(left == result);
|
||||
__ andq(result, right);
|
||||
break;
|
||||
case Token::kBIT_XOR:
|
||||
ASSERT(left == result);
|
||||
__ xorq(result, right);
|
||||
break;
|
||||
case Token::kSHL:
|
||||
ASSERT(result != right);
|
||||
ASSERT(locs()->temp(0).reg() == RCX);
|
||||
__ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
|
||||
__ j(ABOVE_EQUAL, slow_path->entry_label());
|
||||
|
||||
__ movq(RCX, right);
|
||||
__ SmiUntag(RCX);
|
||||
__ movq(result, left);
|
||||
__ shlq(result, RCX);
|
||||
__ movq(TMP, result);
|
||||
__ OBJ(sar)(TMP, RCX);
|
||||
__ OBJ(cmp)(TMP, left);
|
||||
__ j(NOT_EQUAL, slow_path->entry_label());
|
||||
break;
|
||||
case Token::kSHR:
|
||||
ASSERT(result != right);
|
||||
ASSERT(locs()->temp(0).reg() == RCX);
|
||||
__ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
|
||||
__ j(ABOVE_EQUAL, slow_path->entry_label());
|
||||
|
||||
__ movq(RCX, right);
|
||||
__ SmiUntag(RCX);
|
||||
__ movq(result, left);
|
||||
__ SmiUntag(result);
|
||||
__ OBJ(sar)(result, RCX);
|
||||
__ SmiTag(result);
|
||||
break;
|
||||
case Token::kUSHR: {
|
||||
ASSERT(result != right);
|
||||
ASSERT(locs()->temp(0).reg() == RCX);
|
||||
__ CompareObject(right, Smi::ZoneHandle(Smi::New(kBitsPerInt64)));
|
||||
__ j(ABOVE_EQUAL, slow_path->entry_label());
|
||||
|
||||
__ movq(RCX, right);
|
||||
__ SmiUntag(RCX);
|
||||
__ movq(result, left);
|
||||
__ SmiUntagAndSignExtend(result);
|
||||
__ shrq(result, RCX);
|
||||
__ shlq(result, compiler::Immediate(1)); // SmiTag, keep hi bits.
|
||||
__ j(OVERFLOW, slow_path->entry_label());
|
||||
#if defined(DART_COMPRESSED_POINTERS)
|
||||
const Register temp = locs()->temp(0).reg();
|
||||
__ movsxd(temp, result);
|
||||
__ cmpq(temp, result);
|
||||
__ j(NOT_EQUAL, slow_path->entry_label());
|
||||
#endif // defined(DART_COMPRESSED_POINTERS)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
__ Bind(slow_path->exit_label());
|
||||
}
|
||||
|
||||
class CheckedSmiComparisonSlowPath
|
||||
: public TemplateSlowPathCode<CheckedSmiComparisonInstr> {
|
||||
public:
|
||||
static constexpr intptr_t kNumSlowPathArgs = 2;
|
||||
|
||||
CheckedSmiComparisonSlowPath(CheckedSmiComparisonInstr* instruction,
|
||||
Environment* env,
|
||||
intptr_t try_index,
|
||||
BranchLabels labels,
|
||||
bool merged)
|
||||
: TemplateSlowPathCode(instruction),
|
||||
try_index_(try_index),
|
||||
labels_(labels),
|
||||
merged_(merged),
|
||||
env_(env) {
|
||||
// The environment must either come from the comparison or the environment
|
||||
// was cleared from the comparison (and moved to a branch).
|
||||
ASSERT(env == instruction->env() ||
|
||||
(merged && instruction->env() == nullptr));
|
||||
}
|
||||
|
||||
virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
if (compiler::Assembler::EmittingComments()) {
|
||||
__ Comment("slow path smi comparison");
|
||||
}
|
||||
__ Bind(entry_label());
|
||||
LocationSummary* locs = instruction()->locs();
|
||||
Register result = merged_ ? locs->temp(0).reg() : locs->out(0).reg();
|
||||
locs->live_registers()->Remove(Location::RegisterLocation(result));
|
||||
|
||||
compiler->SaveLiveRegisters(locs);
|
||||
if (env_ != nullptr) {
|
||||
compiler->pending_deoptimization_env_ =
|
||||
compiler->SlowPathEnvironmentFor(env_, locs, kNumSlowPathArgs);
|
||||
}
|
||||
__ pushq(locs->in(0).reg());
|
||||
__ pushq(locs->in(1).reg());
|
||||
|
||||
const auto& selector = String::Handle(instruction()->call()->Selector());
|
||||
const auto& arguments_descriptor =
|
||||
Array::Handle(ArgumentsDescriptor::NewBoxed(
|
||||
/*type_args_len=*/0, /*num_arguments=*/2));
|
||||
|
||||
compiler->EmitMegamorphicInstanceCall(
|
||||
selector, arguments_descriptor, instruction()->call()->deopt_id(),
|
||||
instruction()->source(), locs, try_index_, kNumSlowPathArgs);
|
||||
__ MoveRegister(result, RAX);
|
||||
compiler->RestoreLiveRegisters(locs);
|
||||
compiler->pending_deoptimization_env_ = nullptr;
|
||||
if (merged_) {
|
||||
__ CompareObject(result, Bool::True());
|
||||
__ j(EQUAL, instruction()->is_negated() ? labels_.false_label
|
||||
: labels_.true_label);
|
||||
__ jmp(instruction()->is_negated() ? labels_.true_label
|
||||
: labels_.false_label);
|
||||
ASSERT(exit_label()->IsUnused());
|
||||
} else {
|
||||
ASSERT(!instruction()->is_negated());
|
||||
__ jmp(exit_label());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
intptr_t try_index_;
|
||||
BranchLabels labels_;
|
||||
bool merged_;
|
||||
Environment* env_;
|
||||
};
|
||||
|
||||
LocationSummary* CheckedSmiComparisonInstr::MakeLocationSummary(
|
||||
Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 2;
|
||||
const intptr_t kNumTemps = 1;
|
||||
LocationSummary* summary = new (zone) LocationSummary(
|
||||
zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
|
||||
summary->set_in(0, Location::RequiresRegister());
|
||||
summary->set_in(1, Location::RequiresRegister());
|
||||
summary->set_temp(0, Location::RequiresRegister());
|
||||
summary->set_out(0, Location::RequiresRegister());
|
||||
return summary;
|
||||
}
|
||||
|
||||
Condition CheckedSmiComparisonInstr::EmitComparisonCode(
|
||||
FlowGraphCompiler* compiler,
|
||||
BranchLabels labels) {
|
||||
return EmitSmiComparisonOp(compiler, *locs(), kind());
|
||||
}
|
||||
|
||||
#define EMIT_SMI_CHECK \
|
||||
intptr_t left_cid = left()->Type()->ToCid(); \
|
||||
intptr_t right_cid = right()->Type()->ToCid(); \
|
||||
Register left = locs()->in(0).reg(); \
|
||||
Register right = locs()->in(1).reg(); \
|
||||
if (FLAG_use_slow_path) { \
|
||||
__ jmp(slow_path->entry_label()); \
|
||||
} else if (this->left()->definition() == this->right()->definition()) { \
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label()); \
|
||||
} else if (left_cid == kSmiCid) { \
|
||||
__ BranchIfNotSmi(right, slow_path->entry_label()); \
|
||||
} else if (right_cid == kSmiCid) { \
|
||||
__ BranchIfNotSmi(left, slow_path->entry_label()); \
|
||||
} else { \
|
||||
__ movq(TMP, left); \
|
||||
__ orq(TMP, right); \
|
||||
__ BranchIfNotSmi(TMP, slow_path->entry_label()); \
|
||||
}
|
||||
|
||||
void CheckedSmiComparisonInstr::EmitBranchCode(FlowGraphCompiler* compiler,
|
||||
BranchInstr* branch) {
|
||||
BranchLabels labels = compiler->CreateBranchLabels(branch);
|
||||
CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
|
||||
this, branch->env(), compiler->CurrentTryIndex(), labels,
|
||||
/* merged = */ true);
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
EMIT_SMI_CHECK;
|
||||
Condition true_condition = EmitComparisonCode(compiler, labels);
|
||||
ASSERT(true_condition != kInvalidCondition);
|
||||
EmitBranchOnCondition(compiler, true_condition, labels);
|
||||
// No need to bind slow_path->exit_label() as slow path exits through
|
||||
// true/false branch labels.
|
||||
}
|
||||
|
||||
void CheckedSmiComparisonInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
// Zone-allocate labels to pass them to slow-path which outlives local scope.
|
||||
compiler::Label* true_label = new (Z) compiler::Label();
|
||||
compiler::Label* false_label = new (Z) compiler::Label();
|
||||
compiler::Label done;
|
||||
BranchLabels labels = {true_label, false_label, false_label};
|
||||
// In case of negated comparison result of a slow path call should be negated.
|
||||
// For this purpose, 'merged' slow path is generated: it tests
|
||||
// result of a call and jumps directly to true or false label.
|
||||
CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
|
||||
this, env(), compiler->CurrentTryIndex(), labels,
|
||||
/* merged = */ is_negated());
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
EMIT_SMI_CHECK;
|
||||
Condition true_condition = EmitComparisonCode(compiler, labels);
|
||||
ASSERT(true_condition != kInvalidCondition);
|
||||
EmitBranchOnCondition(compiler, true_condition, labels,
|
||||
compiler::Assembler::kNearJump);
|
||||
Register result = locs()->out(0).reg();
|
||||
__ Bind(false_label);
|
||||
__ LoadObject(result, Bool::False());
|
||||
__ jmp(&done, compiler::Assembler::kNearJump);
|
||||
__ Bind(true_label);
|
||||
__ LoadObject(result, Bool::True());
|
||||
__ Bind(&done);
|
||||
// In case of negated comparison slow path exits through true/false labels.
|
||||
if (!is_negated()) {
|
||||
__ Bind(slow_path->exit_label());
|
||||
}
|
||||
}
|
||||
|
||||
static bool CanBeImmediate(const Object& constant) {
|
||||
return constant.IsSmi() &&
|
||||
compiler::Immediate(static_cast<int64_t>(constant.ptr())).is_int32();
|
||||
|
||||
@@ -1595,30 +1595,6 @@ CompileType UnaryInt64OpInstr::ComputeType() const {
|
||||
return CompileType::Int();
|
||||
}
|
||||
|
||||
CompileType CheckedSmiOpInstr::ComputeType() const {
|
||||
if (left()->Type()->IsNullableInt() && right()->Type()->IsNullableInt()) {
|
||||
const AbstractType& abstract_type =
|
||||
AbstractType::ZoneHandle(Type::IntType());
|
||||
TraceStrongModeType(this, abstract_type);
|
||||
return CompileType::FromAbstractType(abstract_type,
|
||||
CompileType::kNonNullable);
|
||||
} else {
|
||||
CompileType* type = call()->Type();
|
||||
TraceStrongModeType(this, type);
|
||||
return *type;
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckedSmiOpInstr::RecomputeType() {
|
||||
return UpdateType(ComputeType());
|
||||
}
|
||||
|
||||
CompileType CheckedSmiComparisonInstr::ComputeType() const {
|
||||
CompileType* type = call()->Type();
|
||||
TraceStrongModeType(this, type);
|
||||
return *type;
|
||||
}
|
||||
|
||||
CompileType BoxIntegerInstr::ComputeType() const {
|
||||
return ValueFitsSmi() ? CompileType::FromCid(kSmiCid) : CompileType::Int();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user