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