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 }