From 97152cc41ba175cdaef34c457df85d6fc613dae3 Mon Sep 17 00:00:00 2001
From: sergei <gorelkin@nanoreflex.ru>
Date: Mon, 29 Apr 2013 01:12:45 +0000
Subject: [PATCH] * Fixed overflow handling in i386 assembler implementations
 of fpc_div_qword and fpc_mod_qword. Resolves #23963.

git-svn-id: trunk@24362 -
---
 .gitattributes          |  1 +
 rtl/i386/int64p.inc     | 33 ++++++++++++++++++++-------------
 tests/webtbs/tw23963.pp | 14 ++++++++++++++
 3 files changed, 35 insertions(+), 13 deletions(-)
 create mode 100644 tests/webtbs/tw23963.pp

diff --git a/.gitattributes b/.gitattributes
index 7792c9bd4e..4c554b346c 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -13383,6 +13383,7 @@ tests/webtbs/tw2382.pp svneol=native#text/plain
 tests/webtbs/tw2388.pp svneol=native#text/plain
 tests/webtbs/tw23912.pp svneol=native#text/plain
 tests/webtbs/tw23962.pp svneol=native#text/plain
+tests/webtbs/tw23963.pp svneol=native#text/plain
 tests/webtbs/tw2397.pp svneol=native#text/plain
 tests/webtbs/tw24007.pp svneol=native#text/plain
 tests/webtbs/tw2409.pp svneol=native#text/plain
diff --git a/rtl/i386/int64p.inc b/rtl/i386/int64p.inc
index 7627f74288..37c3c7e278 100644
--- a/rtl/i386/int64p.inc
+++ b/rtl/i386/int64p.inc
@@ -18,10 +18,10 @@
 {$define FPC_SYSTEM_HAS_DIV_QWORD}
     function fpc_div_qword(n,z : qword) : qword;assembler;[public,alias: 'FPC_DIV_QWORD']; compilerproc;
       var
-         shift,lzz,lzn : longint;
-         saveebx,saveedi : longint;
+         saveebx,saveedi,saveesi : longint;
       asm
             movl %ebx,saveebx
+            movl %esi,saveesi
             movl %edi,saveedi
             { the following piece of code is taken from the     }
             { AMD Athlon Processor x86 Code Optimization manual }
@@ -68,18 +68,23 @@
             roll $1,%edi
             divl %ebx
             movl z,%ebx
-            movl %eax,%ecx
+            movl %eax,%esi             // save quotient to esi
             imull %eax,%edi
             mull n
             addl %edi,%edx
+            setcb %cl                  // cl:edx:eax = 65 bits quotient*divisor
+
+            movl z+4,%edi              // edi:ebx = dividend
             subl %eax,%ebx
-            movl %ecx,%eax
-            movl z+4,%ecx
-            sbbl %edx,%ecx
-            sbbl $0,%eax
+            movb $0,%al
+            sbbl %edx,%edi
+            sbbb %cl,%al
+            sbbl $0,%esi
             xorl %edx,%edx
+            movl %esi,%eax
 .Lexit:
             movl saveebx,%ebx
+            movl saveesi,%esi
             movl saveedi,%edi
       end;
 
@@ -87,7 +92,6 @@
 {$define FPC_SYSTEM_HAS_MOD_QWORD}
     function fpc_mod_qword(n,z : qword) : qword;assembler;[public,alias: 'FPC_MOD_QWORD']; compilerproc;
       var
-         shift,lzz,lzn : longint;
          saveebx,saveedi : longint;
       asm
             movl %ebx,saveebx
@@ -139,19 +143,22 @@
             roll $1,%edi
             divl %ebx
             movl z,%ebx
-            movl %eax,%ecx
             imull %eax,%edi
             mull n
             addl %edi,%edx
-            subl %eax,%ebx
-            movl z+4,%ecx
+            setcb %cl                  // cl:edx:eax = 65 bits quotient*divisor
+            movl z+4,%edi
+            subl %eax,%ebx             // subtract (quotient*divisor) from dividend
+            movb $0,%al
+            sbbl %edx,%edi
+            sbbb %cl,%al               // if carry is set now, the quotient was off by 1,
+                                       // and we need to add divisor to result
             movl n,%eax
-            sbbl %edx,%ecx
             sbbl %edx,%edx
             andl %edx,%eax
             andl n+4,%edx
             addl %ebx,%eax
-            adcl %ecx,%edx
+            adcl %edi,%edx
 .Lexit:
             movl saveebx,%ebx
             movl saveedi,%edi
diff --git a/tests/webtbs/tw23963.pp b/tests/webtbs/tw23963.pp
new file mode 100644
index 0000000000..f5de32cc68
--- /dev/null
+++ b/tests/webtbs/tw23963.pp
@@ -0,0 +1,14 @@
+{$mode objfpc}
+
+var
+  Dividend, Divisor, Quotient : QWord;
+begin
+  Dividend := QWord( $ffffffffffffffff );
+  Divisor := QWord( $100000003 );
+  Quotient := dividend div divisor;
+  if quotient <> $FFFFFFFD then
+    Halt(1);
+  Quotient := dividend mod divisor;
+  if quotient <> 8 then
+    Halt(2);
+end.