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 }