@@ -427,36 +427,91 @@ object Lower {
427
427
def genBinOp (buf : Buffer , n : Local , op : Op .Bin , unwind : Next ): Unit = {
428
428
import buf ._
429
429
430
- op match {
431
- // Detects taking remainder for division by -1 and replaces
432
- // it by division by 1 which can't overflow.
433
- //
434
- // We implement '%' (remainder) with LLVM's 'srem' and it
435
- // can overflow for cases:
436
- //
437
- // - Int.MinValue % -1
438
- // - Long.MinValue % -1
439
- //
440
- // E.g. On x86_64 'srem' might get translated to 'idiv'
441
- // which computes both quotient and remainder at once
442
- // and quotient can overflow.
443
- case sremBin @ Op .Bin (Bin .Srem , intType : Type .I , _, divisor)
444
- if intType.width == 32 || intType.width == 64 =>
445
- val safeDivisor = Val .Local (fresh(), intType)
446
- val thenL, elseL, contL = fresh()
430
+ // LLVM's division by zero is undefined behaviour. We guard
431
+ // the case when the divisor is zero and fail gracefully
432
+ // by throwing an arithmetic exception.
433
+ def checkDivisionByZero (op : Op .Bin ): Unit = {
434
+ val Op .Bin (bin, ty : Type .I , dividend, divisor) = op
435
+
436
+ val thenL, elseL = fresh()
437
+
438
+ val isZero =
439
+ comp(Comp .Ieq , ty, divisor, Val .Zero (ty), unwind)
440
+ branch(isZero, Next (thenL), Next (elseL))
441
+
442
+ label(thenL)
443
+ call(throwDivisionByZeroTy,
444
+ throwDivisionByZeroVal,
445
+ Seq (Val .Null ),
446
+ unwind)
447
+ unreachable
448
+
449
+ label(elseL)
450
+ if (bin == Bin .Srem || bin == Bin .Sdiv ) {
451
+ checkDivisionOverflow(op)
452
+ } else {
453
+ let(n, op, unwind)
454
+ }
455
+ }
447
456
448
- val isPossibleOverflow =
449
- let(Op .Comp (Comp .Ieq , intType, divisor, Val .Int (- 1 )), unwind)
450
- branch(isPossibleOverflow, Next (thenL), Next (elseL))
457
+ // Detects taking remainder for division by -1 and replaces
458
+ // it by division by 1 which can't overflow.
459
+ //
460
+ // We implement '%' (remainder) with LLVM's 'srem' and it
461
+ // can overflow for cases:
462
+ //
463
+ // - Int.MinValue % -1
464
+ // - Long.MinValue % -1
465
+ //
466
+ // E.g. On x86_64 'srem' might get translated to 'idiv'
467
+ // which computes both quotient and remainder at once
468
+ // and quotient can overflow.
469
+ def checkDivisionOverflow (op : Op .Bin ): Unit = {
470
+ val Op .Bin (bin, ty : Type .I , dividend, divisor) = op
471
+
472
+ val mayOverflowL, noOverflowL, didOverflowL, resultL = fresh()
473
+
474
+ val minus1 = ty match {
475
+ case Type .Int => Val .Int (- 1 )
476
+ case Type .Long => Val .Long (- 1L )
477
+ case _ => util.unreachable
478
+ }
479
+ val minValue = ty match {
480
+ case Type .Int => Val .Int (java.lang.Integer .MIN_VALUE )
481
+ case Type .Long => Val .Long (java.lang.Long .MIN_VALUE )
482
+ case _ => util.unreachable
483
+ }
451
484
452
- label(thenL)
453
- jump(contL, Seq (Val .Int (1 )))
485
+ val divisorIsMinus1 =
486
+ let(Op .Comp (Comp .Ieq , ty, divisor, minus1), unwind)
487
+ branch(divisorIsMinus1, Next (mayOverflowL), Next (noOverflowL))
454
488
455
- label(elseL)
456
- jump(contL, Seq (divisor))
489
+ label(mayOverflowL)
490
+ val dividendIsMinValue =
491
+ let(Op .Comp (Comp .Ieq , ty, dividend, minValue), unwind)
492
+ branch(dividendIsMinValue, Next (didOverflowL), Next (noOverflowL))
493
+
494
+ label(didOverflowL)
495
+ val overflowResult = bin match {
496
+ case Bin .Srem => Val .Zero (ty)
497
+ case Bin .Sdiv => minValue
498
+ case _ => util.unreachable
499
+ }
500
+ jump(resultL, Seq (overflowResult))
501
+
502
+ label(noOverflowL)
503
+ val noOverflowResult = let(op, unwind)
504
+ jump(resultL, Seq (noOverflowResult))
457
505
458
- label(contL, Seq (safeDivisor))
459
- let(n, sremBin.copy(r = safeDivisor), unwind)
506
+ label(resultL, Seq (Val .Local (n, ty)))
507
+ }
508
+
509
+ op match {
510
+ case op @ Op .Bin (bin @ (Bin .Srem | Bin .Urem | Bin .Sdiv | Bin .Udiv ),
511
+ ty : Type .I ,
512
+ l,
513
+ r) =>
514
+ checkDivisionByZero(op)
460
515
461
516
case op =>
462
517
let(n, op, unwind)
@@ -792,6 +847,16 @@ object Lower {
792
847
Type .Function (Seq (Type .Ref (Global .Top (" scala.scalanative.runtime.Array" ))),
793
848
Type .Int )
794
849
850
+ val throwDivisionByZeroTy =
851
+ Type .Function (
852
+ Seq (Type .Ref (Global .Top (" scala.scalanative.runtime.package$" ))),
853
+ Type .Nothing )
854
+ val throwDivisionByZero =
855
+ Global .Member (Global .Top (" scala.scalanative.runtime.package$" ),
856
+ Sig .Method (" throwDivisionByZero" , Seq (Type .Nothing )))
857
+ val throwDivisionByZeroVal =
858
+ Val .Global (throwDivisionByZero, Type .Ptr )
859
+
795
860
val injects : Seq [Defn ] = {
796
861
val buf = mutable.UnrolledBuffer .empty[Defn ]
797
862
buf += Defn .Declare (Attrs .None , allocSmallName, allocSig)
@@ -824,6 +889,7 @@ object Lower {
824
889
buf ++= arrayApply.values
825
890
buf ++= arrayUpdateGeneric.values
826
891
buf ++= arrayUpdate.values
892
+ buf += throwDivisionByZero
827
893
buf
828
894
}
829
895
}
0 commit comments