1 /** 2 * Wrapper for long integer multiplication / division opcodes 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.math.longmul; 15 16 import std.traits; 17 18 import ae.utils.math; 19 20 struct LongInt(uint bits, bool signed) 21 { 22 TypeForBits!bits low; 23 static if (signed) 24 Signed!(TypeForBits!bits) high; 25 else 26 TypeForBits!bits high; 27 } 28 29 alias LongInt(T) = LongInt!(T.sizeof * 8, isSigned!T); 30 31 alias Cent = LongInt!long; 32 alias UCent = LongInt!ulong; 33 34 version (X86) 35 version = Intel; 36 else 37 version (X86_64) 38 version = Intel; 39 40 version (Intel) 41 { 42 version (DigitalMars) 43 enum x86RegSizePrefix(T) = 44 T.sizeof == 2 ? "" : 45 T.sizeof == 4 ? "E" : 46 T.sizeof == 8 ? "R" : 47 "?"; // force syntax error 48 else 49 { 50 enum x86RegSizePrefix(T) = 51 T.sizeof == 2 ? "" : 52 T.sizeof == 4 ? "e" : 53 T.sizeof == 8 ? "r" : 54 "?"; // force syntax error 55 enum x86SizeOpSuffix(T) = 56 T.sizeof == 2 ? "w" : 57 T.sizeof == 4 ? "l" : 58 T.sizeof == 8 ? "q" : 59 "?"; // force syntax error 60 } 61 62 enum x86SignedOpPrefix(T) = isSigned!T ? "i" : ""; 63 } 64 65 LongInt!T longMul(T)(T a, T b) 66 if (is(T : long) && T.sizeof >= 2) 67 { 68 version (Intel) 69 { 70 version (LDC) 71 { 72 import ldc.llvmasm; 73 auto t = __asmtuple!(T, T)( 74 x86SignedOpPrefix!T~`mul`~x86SizeOpSuffix!T~` $3`, 75 // Technically, the last one should be "rm", but that generates suboptimal code in many cases 76 `={`~x86RegSizePrefix!T~`ax},={`~x86RegSizePrefix!T~`dx},{`~x86RegSizePrefix!T~`ax},r`, 77 a, b 78 ); 79 return typeof(return)(t.v[0], t.v[1]); 80 } 81 else 82 version (GNU) 83 { 84 T low = void, high = void; 85 mixin(` 86 asm 87 { 88 "`~x86SignedOpPrefix!T~`mul`~x86SizeOpSuffix!T~` %3" 89 : "=a" low, "=d" high 90 : "a" a, "rm" b; 91 } 92 `); 93 return typeof(return)(low, high); 94 } 95 else 96 { 97 T low = void, high = void; 98 mixin(` 99 asm 100 { 101 mov `~x86RegSizePrefix!T~`AX, a; 102 `~x86SignedOpPrefix!T~`mul b; 103 mov low, `~x86RegSizePrefix!T~`AX; 104 mov high, `~x86RegSizePrefix!T~`DX; 105 } 106 `); 107 return typeof(return)(low, high); 108 } 109 } 110 else 111 static assert(false, "Not implemented on this architecture"); 112 } 113 114 unittest 115 { 116 assert(longMul(1, 1) == LongInt!int(1, 0)); 117 assert(longMul(1, 2) == LongInt!int(2, 0)); 118 assert(longMul(0x1_0000, 0x1_0000) == LongInt!int(0, 1)); 119 120 assert(longMul(short(1), short(1)) == LongInt!short(1, 0)); 121 assert(longMul(short(0x100), short(0x100)) == LongInt!short(0, 1)); 122 123 assert(longMul(short(1), short(-1)) == LongInt!short(cast(ushort)-1, -1)); 124 assert(longMul(ushort(1), cast(ushort)-1) == LongInt!ushort(cast(ushort)-1, 0)); 125 126 version(X86_64) 127 { 128 assert(longMul(1L, 1L) == LongInt!long(1, 0)); 129 assert(longMul(0x1_0000_0000L, 0x1_0000_0000L) == LongInt!long(0, 1)); 130 } 131 } 132 133 struct DivResult(T) { T quotient, remainder; } 134 135 DivResult!T longDiv(T, L)(L a, T b) 136 if (is(T : long) && T.sizeof >= 2 && is(L == LongInt!T)) 137 { 138 version (Intel) 139 { 140 version (LDC) 141 { 142 import ldc.llvmasm; 143 auto t = __asmtuple!(T, T)( 144 x86SignedOpPrefix!T~`div`~x86SizeOpSuffix!T~` $4`, 145 // Technically, the last one should be "rm", but that generates suboptimal code in many cases 146 `={`~x86RegSizePrefix!T~`ax},={`~x86RegSizePrefix!T~`dx},{`~x86RegSizePrefix!T~`ax},{`~x86RegSizePrefix!T~`dx},r`, 147 a.low, a.high, b 148 ); 149 return typeof(return)(t.v[0], t.v[1]); 150 } 151 else 152 version (GNU) 153 { 154 T low = a.low, high = a.high; 155 T quotient = void; 156 T remainder = void; 157 mixin(` 158 asm 159 { 160 "`~x86SignedOpPrefix!T~`div`~x86SizeOpSuffix!T~` %4" 161 : "=a" quotient, "=d" remainder 162 : "a" low, "d" high, "rm" b; 163 } 164 `); 165 return typeof(return)(quotient, remainder); 166 } 167 else 168 { 169 auto low = a.low; 170 auto high = a.high; 171 T quotient = void; 172 T remainder = void; 173 mixin(` 174 asm 175 { 176 mov `~x86RegSizePrefix!T~`AX, low; 177 mov `~x86RegSizePrefix!T~`DX, high; 178 `~x86SignedOpPrefix!T~`div b; 179 mov quotient, `~x86RegSizePrefix!T~`AX; 180 mov remainder, `~x86RegSizePrefix!T~`DX; 181 } 182 `); 183 return typeof(return)(quotient, remainder); 184 } 185 } 186 else 187 static assert(false, "Not implemented on this architecture"); 188 } 189 190 unittest 191 { 192 assert(longDiv(LongInt!int(1, 0), 1) == DivResult!int(1, 0)); 193 assert(longDiv(LongInt!int(5, 0), 2) == DivResult!int(2, 1)); 194 assert(longDiv(LongInt!int(0, 1), 0x1_0000) == DivResult!int(0x1_0000, 0)); 195 196 assert(longDiv(LongInt!short(1, 0), short(1)) == DivResult!short(1, 0)); 197 assert(longDiv(LongInt!short(0, 1), short(0x100)) == DivResult!short(0x100, 0)); 198 199 assert(longDiv(LongInt!short(cast(ushort)-1, -1), short(-1)) == DivResult!short(1)); 200 assert(longDiv(LongInt!ushort(cast(ushort)-1, 0), cast(ushort)-1) == DivResult!ushort(1)); 201 202 version(X86_64) 203 { 204 assert(longDiv(LongInt!long(1, 0), 1L) == DivResult!long(1)); 205 assert(longDiv(LongInt!long(0, 1), 0x1_0000_0000L) == DivResult!long(0x1_0000_0000)); 206 } 207 }