@@ -5196,6 +5196,107 @@ void CheckArrayBoundInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5196
5196
}
5197
5197
}
5198
5198
5199
+ class Int64DivideSlowPath : public ThrowErrorSlowPathCode {
5200
+ public:
5201
+ static const intptr_t kNumberOfArguments = 0 ;
5202
+
5203
+ Int64DivideSlowPath (BinaryInt64OpInstr* instruction,
5204
+ Register right,
5205
+ intptr_t try_index)
5206
+ : ThrowErrorSlowPathCode(instruction,
5207
+ kIntegerDivisionByZeroExceptionRuntimeEntry ,
5208
+ kNumberOfArguments ,
5209
+ try_index),
5210
+ is_mod_ (instruction->op_kind () == Token::kMOD),
5211
+ right_(right),
5212
+ div_by_minus_one_label_(),
5213
+ adjust_sign_label_() {}
5214
+
5215
+ 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
5222
+ } else {
5223
+ __ negq (RAX); // x / -1 = -x
5224
+ }
5225
+ __ jmp (exit_label ());
5226
+ // Adjust modulo for negative sign.
5227
+ // if (right < 0)
5228
+ // out -= right;
5229
+ // else
5230
+ // out += right;
5231
+ if (is_mod_) {
5232
+ Label subtract;
5233
+ __ 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_);
5240
+ __ jmp (exit_label ());
5241
+ }
5242
+ }
5243
+
5244
+ const char * name () override { return " int64 divide" ; }
5245
+
5246
+ Label* div_by_minus_one_label () { return &div_by_minus_one_label_; }
5247
+ Label* adjust_sign_label () { return &adjust_sign_label_; }
5248
+
5249
+ private:
5250
+ bool is_mod_;
5251
+ Register right_;
5252
+ Label div_by_minus_one_label_;
5253
+ Label adjust_sign_label_;
5254
+ };
5255
+
5256
+ static void EmitInt64ModTruncDiv (FlowGraphCompiler* compiler,
5257
+ BinaryInt64OpInstr* instruction,
5258
+ Token::Kind op_kind,
5259
+ Register left,
5260
+ Register right,
5261
+ Register tmp,
5262
+ Register out) {
5263
+ ASSERT (op_kind == Token::kMOD || op_kind == Token::kTRUNCDIV );
5264
+
5265
+ // Set up a slow path.
5266
+ Int64DivideSlowPath* slow_path = new (Z)
5267
+ Int64DivideSlowPath (instruction, right, compiler->CurrentTryIndex ());
5268
+ compiler->AddSlowPathCode (slow_path);
5269
+
5270
+ // Handle modulo/division by zero exception on slow path.
5271
+ __ testq (right, right);
5272
+ __ j (EQUAL, slow_path->entry_label ());
5273
+
5274
+ // Handle modulo/division by minus one explicitly on slow path
5275
+ // (to avoid arithmetic exception on 0x8000000000000000 / -1).
5276
+ __ cmpq (right, Immediate (-1 ));
5277
+ __ j (EQUAL, slow_path->div_by_minus_one_label ());
5278
+
5279
+ // Perform actual operation
5280
+ // out = left % right
5281
+ // or
5282
+ // out = left / right.
5283
+ ASSERT (left == RAX);
5284
+ __ cqo (); // sign-ext rax into rdx:rax
5285
+ __ idivq (right); // quotient rax, remainder rdx
5286
+ if (op_kind == Token::kMOD ) {
5287
+ ASSERT (out == RDX);
5288
+ ASSERT (tmp == RAX);
5289
+ // For the % operator, again the idiv instruction does
5290
+ // not quite do what we want. Adjust for sign on slow path.
5291
+ __ testq (out, out);
5292
+ __ j (LESS, slow_path->adjust_sign_label ());
5293
+ } else {
5294
+ ASSERT (out == RAX);
5295
+ ASSERT (tmp == RDX);
5296
+ }
5297
+ __ Bind (slow_path->exit_label ());
5298
+ }
5299
+
5199
5300
template <typename OperandType>
5200
5301
static void EmitInt64Arithmetic (FlowGraphCompiler* compiler,
5201
5302
Token::Kind op_kind,
@@ -5227,30 +5328,55 @@ static void EmitInt64Arithmetic(FlowGraphCompiler* compiler,
5227
5328
5228
5329
LocationSummary* BinaryInt64OpInstr::MakeLocationSummary (Zone* zone,
5229
5330
bool opt) const {
5230
- const intptr_t kNumInputs = 2 ;
5231
- const intptr_t kNumTemps = 0 ;
5232
- LocationSummary* summary = new (zone)
5233
- LocationSummary (zone, kNumInputs , kNumTemps , LocationSummary::kNoCall );
5234
- summary->set_in (0 , Location::RequiresRegister ());
5235
- summary->set_in (1 , Location::RegisterOrConstant (right ()));
5236
- summary->set_out (0 , Location::SameAsFirstInput ());
5237
- return summary;
5331
+ switch (op_kind ()) {
5332
+ case Token::kMOD :
5333
+ case Token::kTRUNCDIV : {
5334
+ const intptr_t kNumInputs = 2 ;
5335
+ const intptr_t kNumTemps = 1 ;
5336
+ LocationSummary* summary = new (zone) LocationSummary (
5337
+ zone, kNumInputs , kNumTemps , LocationSummary::kCallOnSlowPath );
5338
+ summary->set_in (0 , Location::RegisterLocation (RAX));
5339
+ summary->set_in (1 , Location::RequiresRegister ());
5340
+ // Intel uses rdx:rax with quotient rax and remainder rdx. Pick the
5341
+ // appropriate one for output and reserve the other as temp.
5342
+ summary->set_out (
5343
+ 0 , Location::RegisterLocation (op_kind () == Token::kMOD ? RDX : RAX));
5344
+ summary->set_temp (
5345
+ 0 , Location::RegisterLocation (op_kind () == Token::kMOD ? RAX : RDX));
5346
+ return summary;
5347
+ }
5348
+ default : {
5349
+ const intptr_t kNumInputs = 2 ;
5350
+ const intptr_t kNumTemps = 0 ;
5351
+ LocationSummary* summary = new (zone) LocationSummary (
5352
+ zone, kNumInputs , kNumTemps , LocationSummary::kNoCall );
5353
+ summary->set_in (0 , Location::RequiresRegister ());
5354
+ summary->set_in (1 , Location::RegisterOrConstant (right ()));
5355
+ summary->set_out (0 , Location::SameAsFirstInput ());
5356
+ return summary;
5357
+ }
5358
+ }
5238
5359
}
5239
5360
5240
5361
void BinaryInt64OpInstr::EmitNativeCode (FlowGraphCompiler* compiler) {
5241
5362
const Location left = locs ()->in (0 );
5242
5363
const Location right = locs ()->in (1 );
5243
5364
const Location out = locs ()->out (0 );
5244
- ASSERT (out.reg () == left.reg ());
5245
5365
ASSERT (!can_overflow ());
5246
5366
ASSERT (!CanDeoptimize ());
5247
5367
5248
- if (right.IsConstant ()) {
5368
+ if (op_kind () == Token::kMOD || op_kind () == Token::kTRUNCDIV ) {
5369
+ const Location temp = locs ()->temp (0 );
5370
+ EmitInt64ModTruncDiv (compiler, this , op_kind (), left.reg (), right.reg (),
5371
+ temp.reg (), out.reg ());
5372
+ } else if (right.IsConstant ()) {
5373
+ ASSERT (out.reg () == left.reg ());
5249
5374
ConstantInstr* constant_instr = right.constant_instruction ();
5250
5375
const int64_t value =
5251
5376
constant_instr->GetUnboxedSignedIntegerConstantValue ();
5252
5377
EmitInt64Arithmetic (compiler, op_kind (), left.reg (), Immediate (value));
5253
5378
} else {
5379
+ ASSERT (out.reg () == left.reg ());
5254
5380
EmitInt64Arithmetic (compiler, op_kind (), left.reg (), right.reg ());
5255
5381
}
5256
5382
}
0 commit comments