cannam@127
|
1 /*
|
cannam@127
|
2 * Copyright (c) 2003, 2007-14 Matteo Frigo
|
cannam@127
|
3 * Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology
|
cannam@127
|
4 *
|
cannam@127
|
5 * This program is free software; you can redistribute it and/or modify
|
cannam@127
|
6 * it under the terms of the GNU General Public License as published by
|
cannam@127
|
7 * the Free Software Foundation; either version 2 of the License, or
|
cannam@127
|
8 * (at your option) any later version.
|
cannam@127
|
9 *
|
cannam@127
|
10 * This program is distributed in the hope that it will be useful,
|
cannam@127
|
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
cannam@127
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
cannam@127
|
13 * GNU General Public License for more details.
|
cannam@127
|
14 *
|
cannam@127
|
15 * You should have received a copy of the GNU General Public License
|
cannam@127
|
16 * along with this program; if not, write to the Free Software
|
cannam@127
|
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
cannam@127
|
18 *
|
cannam@127
|
19 */
|
cannam@127
|
20
|
cannam@127
|
21
|
cannam@127
|
22 /* Twiddle manipulation */
|
cannam@127
|
23
|
cannam@127
|
24 #include "ifftw.h"
|
cannam@127
|
25 #include <math.h>
|
cannam@127
|
26
|
cannam@127
|
27 #define HASHSZ 109
|
cannam@127
|
28
|
cannam@127
|
29 /* hash table of known twiddle factors */
|
cannam@127
|
30 static twid *twlist[HASHSZ];
|
cannam@127
|
31
|
cannam@127
|
32 static INT hash(INT n, INT r)
|
cannam@127
|
33 {
|
cannam@127
|
34 INT h = n * 17 + r;
|
cannam@127
|
35
|
cannam@127
|
36 if (h < 0) h = -h;
|
cannam@127
|
37
|
cannam@127
|
38 return (h % HASHSZ);
|
cannam@127
|
39 }
|
cannam@127
|
40
|
cannam@127
|
41 static int equal_instr(const tw_instr *p, const tw_instr *q)
|
cannam@127
|
42 {
|
cannam@127
|
43 if (p == q)
|
cannam@127
|
44 return 1;
|
cannam@127
|
45
|
cannam@127
|
46 for (;; ++p, ++q) {
|
cannam@127
|
47 if (p->op != q->op)
|
cannam@127
|
48 return 0;
|
cannam@127
|
49
|
cannam@127
|
50 switch (p->op) {
|
cannam@127
|
51 case TW_NEXT:
|
cannam@127
|
52 return (p->v == q->v); /* p->i is ignored */
|
cannam@127
|
53
|
cannam@127
|
54 case TW_FULL:
|
cannam@127
|
55 case TW_HALF:
|
cannam@127
|
56 if (p->v != q->v) return 0; /* p->i is ignored */
|
cannam@127
|
57 break;
|
cannam@127
|
58
|
cannam@127
|
59 default:
|
cannam@127
|
60 if (p->v != q->v || p->i != q->i) return 0;
|
cannam@127
|
61 break;
|
cannam@127
|
62 }
|
cannam@127
|
63 }
|
cannam@127
|
64 A(0 /* can't happen */);
|
cannam@127
|
65 }
|
cannam@127
|
66
|
cannam@127
|
67 static int ok_twid(const twid *t,
|
cannam@127
|
68 enum wakefulness wakefulness,
|
cannam@127
|
69 const tw_instr *q, INT n, INT r, INT m)
|
cannam@127
|
70 {
|
cannam@127
|
71 return (wakefulness == t->wakefulness &&
|
cannam@127
|
72 n == t->n &&
|
cannam@127
|
73 r == t->r &&
|
cannam@127
|
74 m <= t->m &&
|
cannam@127
|
75 equal_instr(t->instr, q));
|
cannam@127
|
76 }
|
cannam@127
|
77
|
cannam@127
|
78 static twid *lookup(enum wakefulness wakefulness,
|
cannam@127
|
79 const tw_instr *q, INT n, INT r, INT m)
|
cannam@127
|
80 {
|
cannam@127
|
81 twid *p;
|
cannam@127
|
82
|
cannam@127
|
83 for (p = twlist[hash(n,r)];
|
cannam@127
|
84 p && !ok_twid(p, wakefulness, q, n, r, m);
|
cannam@127
|
85 p = p->cdr)
|
cannam@127
|
86 ;
|
cannam@127
|
87 return p;
|
cannam@127
|
88 }
|
cannam@127
|
89
|
cannam@127
|
90 static INT twlen0(INT r, const tw_instr *p, INT *vl)
|
cannam@127
|
91 {
|
cannam@127
|
92 INT ntwiddle = 0;
|
cannam@127
|
93
|
cannam@127
|
94 /* compute length of bytecode program */
|
cannam@127
|
95 A(r > 0);
|
cannam@127
|
96 for ( ; p->op != TW_NEXT; ++p) {
|
cannam@127
|
97 switch (p->op) {
|
cannam@127
|
98 case TW_FULL:
|
cannam@127
|
99 ntwiddle += (r - 1) * 2;
|
cannam@127
|
100 break;
|
cannam@127
|
101 case TW_HALF:
|
cannam@127
|
102 ntwiddle += (r - 1);
|
cannam@127
|
103 break;
|
cannam@127
|
104 case TW_CEXP:
|
cannam@127
|
105 ntwiddle += 2;
|
cannam@127
|
106 break;
|
cannam@127
|
107 case TW_COS:
|
cannam@127
|
108 case TW_SIN:
|
cannam@127
|
109 ntwiddle += 1;
|
cannam@127
|
110 break;
|
cannam@127
|
111 }
|
cannam@127
|
112 }
|
cannam@127
|
113
|
cannam@127
|
114 *vl = (INT)p->v;
|
cannam@127
|
115 return ntwiddle;
|
cannam@127
|
116 }
|
cannam@127
|
117
|
cannam@127
|
118 INT X(twiddle_length)(INT r, const tw_instr *p)
|
cannam@127
|
119 {
|
cannam@127
|
120 INT vl;
|
cannam@127
|
121 return twlen0(r, p, &vl);
|
cannam@127
|
122 }
|
cannam@127
|
123
|
cannam@127
|
124 static R *compute(enum wakefulness wakefulness,
|
cannam@127
|
125 const tw_instr *instr, INT n, INT r, INT m)
|
cannam@127
|
126 {
|
cannam@127
|
127 INT ntwiddle, j, vl;
|
cannam@127
|
128 R *W, *W0;
|
cannam@127
|
129 const tw_instr *p;
|
cannam@127
|
130 triggen *t = X(mktriggen)(wakefulness, n);
|
cannam@127
|
131
|
cannam@127
|
132 p = instr;
|
cannam@127
|
133 ntwiddle = twlen0(r, p, &vl);
|
cannam@127
|
134
|
cannam@127
|
135 A(m % vl == 0);
|
cannam@127
|
136
|
cannam@127
|
137 W0 = W = (R *)MALLOC((ntwiddle * (m / vl)) * sizeof(R), TWIDDLES);
|
cannam@127
|
138
|
cannam@127
|
139 for (j = 0; j < m; j += vl) {
|
cannam@127
|
140 for (p = instr; p->op != TW_NEXT; ++p) {
|
cannam@127
|
141 switch (p->op) {
|
cannam@127
|
142 case TW_FULL: {
|
cannam@127
|
143 INT i;
|
cannam@127
|
144 for (i = 1; i < r; ++i) {
|
cannam@127
|
145 A((j + (INT)p->v) * i < n);
|
cannam@127
|
146 A((j + (INT)p->v) * i > -n);
|
cannam@127
|
147 t->cexp(t, (j + (INT)p->v) * i, W);
|
cannam@127
|
148 W += 2;
|
cannam@127
|
149 }
|
cannam@127
|
150 break;
|
cannam@127
|
151 }
|
cannam@127
|
152
|
cannam@127
|
153 case TW_HALF: {
|
cannam@127
|
154 INT i;
|
cannam@127
|
155 A((r % 2) == 1);
|
cannam@127
|
156 for (i = 1; i + i < r; ++i) {
|
cannam@127
|
157 t->cexp(t, MULMOD(i, (j + (INT)p->v), n), W);
|
cannam@127
|
158 W += 2;
|
cannam@127
|
159 }
|
cannam@127
|
160 break;
|
cannam@127
|
161 }
|
cannam@127
|
162
|
cannam@127
|
163 case TW_COS: {
|
cannam@127
|
164 R d[2];
|
cannam@127
|
165
|
cannam@127
|
166 A((j + (INT)p->v) * p->i < n);
|
cannam@127
|
167 A((j + (INT)p->v) * p->i > -n);
|
cannam@127
|
168 t->cexp(t, (j + (INT)p->v) * (INT)p->i, d);
|
cannam@127
|
169 *W++ = d[0];
|
cannam@127
|
170 break;
|
cannam@127
|
171 }
|
cannam@127
|
172
|
cannam@127
|
173 case TW_SIN: {
|
cannam@127
|
174 R d[2];
|
cannam@127
|
175
|
cannam@127
|
176 A((j + (INT)p->v) * p->i < n);
|
cannam@127
|
177 A((j + (INT)p->v) * p->i > -n);
|
cannam@127
|
178 t->cexp(t, (j + (INT)p->v) * (INT)p->i, d);
|
cannam@127
|
179 *W++ = d[1];
|
cannam@127
|
180 break;
|
cannam@127
|
181 }
|
cannam@127
|
182
|
cannam@127
|
183 case TW_CEXP:
|
cannam@127
|
184 A((j + (INT)p->v) * p->i < n);
|
cannam@127
|
185 A((j + (INT)p->v) * p->i > -n);
|
cannam@127
|
186 t->cexp(t, (j + (INT)p->v) * (INT)p->i, W);
|
cannam@127
|
187 W += 2;
|
cannam@127
|
188 break;
|
cannam@127
|
189 }
|
cannam@127
|
190 }
|
cannam@127
|
191 }
|
cannam@127
|
192
|
cannam@127
|
193 X(triggen_destroy)(t);
|
cannam@127
|
194 return W0;
|
cannam@127
|
195 }
|
cannam@127
|
196
|
cannam@127
|
197 static void mktwiddle(enum wakefulness wakefulness,
|
cannam@127
|
198 twid **pp, const tw_instr *instr, INT n, INT r, INT m)
|
cannam@127
|
199 {
|
cannam@127
|
200 twid *p;
|
cannam@127
|
201 INT h;
|
cannam@127
|
202
|
cannam@127
|
203 if ((p = lookup(wakefulness, instr, n, r, m))) {
|
cannam@127
|
204 ++p->refcnt;
|
cannam@127
|
205 } else {
|
cannam@127
|
206 p = (twid *) MALLOC(sizeof(twid), TWIDDLES);
|
cannam@127
|
207 p->n = n;
|
cannam@127
|
208 p->r = r;
|
cannam@127
|
209 p->m = m;
|
cannam@127
|
210 p->instr = instr;
|
cannam@127
|
211 p->refcnt = 1;
|
cannam@127
|
212 p->wakefulness = wakefulness;
|
cannam@127
|
213 p->W = compute(wakefulness, instr, n, r, m);
|
cannam@127
|
214
|
cannam@127
|
215 /* cons! onto twlist */
|
cannam@127
|
216 h = hash(n, r);
|
cannam@127
|
217 p->cdr = twlist[h];
|
cannam@127
|
218 twlist[h] = p;
|
cannam@127
|
219 }
|
cannam@127
|
220
|
cannam@127
|
221 *pp = p;
|
cannam@127
|
222 }
|
cannam@127
|
223
|
cannam@127
|
224 static void twiddle_destroy(twid **pp)
|
cannam@127
|
225 {
|
cannam@127
|
226 twid *p = *pp;
|
cannam@127
|
227 twid **q;
|
cannam@127
|
228
|
cannam@127
|
229 if ((--p->refcnt) == 0) {
|
cannam@127
|
230 /* remove p from twiddle list */
|
cannam@127
|
231 for (q = &twlist[hash(p->n, p->r)]; *q; q = &((*q)->cdr)) {
|
cannam@127
|
232 if (*q == p) {
|
cannam@127
|
233 *q = p->cdr;
|
cannam@127
|
234 X(ifree)(p->W);
|
cannam@127
|
235 X(ifree)(p);
|
cannam@127
|
236 *pp = 0;
|
cannam@127
|
237 return;
|
cannam@127
|
238 }
|
cannam@127
|
239 }
|
cannam@127
|
240 A(0 /* can't happen */ );
|
cannam@127
|
241 }
|
cannam@127
|
242 }
|
cannam@127
|
243
|
cannam@127
|
244
|
cannam@127
|
245 void X(twiddle_awake)(enum wakefulness wakefulness, twid **pp,
|
cannam@127
|
246 const tw_instr *instr, INT n, INT r, INT m)
|
cannam@127
|
247 {
|
cannam@127
|
248 switch (wakefulness) {
|
cannam@127
|
249 case SLEEPY:
|
cannam@127
|
250 twiddle_destroy(pp);
|
cannam@127
|
251 break;
|
cannam@127
|
252 default:
|
cannam@127
|
253 mktwiddle(wakefulness, pp, instr, n, r, m);
|
cannam@127
|
254 break;
|
cannam@127
|
255 }
|
cannam@127
|
256 }
|