1/*
2 * Copyright (C) 2013 Rob Clark <robdclark@gmail.com>
3 * Copyright (C) 2010-2011 Marcin Kościelnicki <koriakin@0x04.net>
4 * Copyright (C) 2010 Luca Barbieri <luca@luca-barbieri.com>
5 * Copyright (C) 2010 Marcin Slusarz <marcin.slusarz@gmail.com>
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28/* modified version of headergen which uses enums and inline fxns for
29 * type safety.. based on original headergen
30 */
31
32#include "rnn.h"
33#include "util.h"
34#include <stdbool.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <inttypes.h>
38#include <time.h>
39#include <ctype.h>
40#include <unistd.h>
41#include <string.h>
42#include <sys/stat.h>
43#include <sys/wait.h>
44#include <assert.h>
45
46struct rnndelem **elems = NULL;
47int elemsnum = 0;
48int elemsmax = 0;
49
50char **offsetfns = NULL;
51int offsetfnsnum = 0;
52int offsetfnsmax = 0;
53
54int startcol = 64;
55
56struct fout {
57	char *name;
58	FILE *file;
59	char *guard;
60};
61
62struct fout *fouts = 0;
63int foutsnum = 0;
64int foutsmax = 0;
65
66static bool no_asserts = false;
67
68static void seekcol (FILE *f, int src, int dst) {
69	if (dst <= src)
70		fprintf (f, "\t");
71	else {
72		int n = dst/8 - src/8;
73		if (n) {
74			while (n--)
75				fprintf (f, "\t");
76			n = dst&7;
77		} else
78			n = dst-src;
79		while (n--)
80			fprintf (f, " ");
81	}
82}
83
84static FILE *findfout (char *file) {
85	int i;
86	for (i = 0; i < foutsnum; i++)
87		if (!strcmp(fouts[i].name, file))
88			break;
89	if (i == foutsnum) {
90		fprintf (stderr, "AIII, didn't open file %s.\n", file);
91		exit(1);
92	}
93	return fouts[i].file;
94}
95
96static void printdef (char *name, char *suf, int type, uint64_t val, char *file) {
97	FILE *dst = findfout(file);
98	int len;
99	if (suf)
100		fprintf (dst, "#define %s__%s%n", name, suf, &len);
101	else
102		fprintf (dst, "#define %s%n", name, &len);
103	if (type == 0 && val > 0xffffffffull)
104		seekcol (dst, len, startcol-8);
105	else
106		seekcol (dst, len, startcol);
107	switch (type) {
108		case 0:
109			if (val > 0xffffffffull)
110				fprintf (dst, "0x%016"PRIx64"ULL\n", val);
111			else
112				fprintf (dst, "0x%08"PRIx64"\n", val);
113			break;
114		case 1:
115			fprintf (dst, "%"PRIu64"\n", val);
116			break;
117	}
118}
119
120static void printvalue (struct rnnvalue *val, int shift) {
121	if (val->varinfo.dead)
122		return;
123	if (val->valvalid)
124		printdef (val->fullname, 0, 0, val->value << shift, val->file);
125}
126
127static void printbitfield (struct rnnbitfield *bf, int shift);
128
129static void printtypeinfo (struct rnntypeinfo *ti, struct rnnbitfield *bf,
130		char *prefix, char *file) {
131	FILE *dst = findfout(file);
132	enum rnnttype intype = ti->type;
133	char *typename = NULL;
134	uint32_t mask = typeinfo_mask(ti);
135	uint32_t width = 1 + ti->high - ti->low;
136
137	/* for fixed point, input type (arg to fxn) is float: */
138	if ((ti->type == RNN_TTYPE_FIXED) || (ti->type == RNN_TTYPE_UFIXED))
139		intype = RNN_TTYPE_FLOAT;
140
141	/* for toplevel register (ie. not bitfield), only generate accessor
142	 * fxn for special cases (float, shr, min/max, etc):
143	 */
144	if (bf || ti->shr || ti->minvalid || ti->maxvalid || ti->alignvalid ||
145			ti->radixvalid || (intype == RNN_TTYPE_FLOAT)) {
146		switch (intype) {
147		case RNN_TTYPE_HEX:
148		case RNN_TTYPE_UINT:
149		case RNN_TTYPE_A3XX_REGID:
150			typename = "uint32_t";
151			break;
152		case RNN_TTYPE_INT:
153			typename = "int32_t";
154			break;
155		case RNN_TTYPE_FLOAT:
156			typename = "float";
157			break;
158		case RNN_TTYPE_ENUM:
159			asprintf(&typename, "enum %s", ti->name);
160			break;
161		default:
162			break;
163		}
164	}
165
166	/* for boolean, just generate a #define flag.. rather than inline fxn */
167	if (bf && (intype == RNN_TTYPE_BOOLEAN)) {
168		printdef(bf->fullname, 0, 0, mask, file);
169		return;
170	}
171
172	if (typename) {
173		printdef(prefix, "MASK", 0, mask, file);
174		printdef(prefix, "SHIFT", 1, ti->low, file);
175
176		fprintf(dst, "static inline uint32_t %s(%s val)\n", prefix, typename);
177		fprintf(dst, "{\n");
178
179		if ((ti->minvalid || ti->maxvalid || ti->alignvalid) && !no_asserts) {
180			fprintf(dst, "\tassert(1");
181			if (ti->minvalid)
182				fprintf(dst, " && (val >= %"PRIu64")", ti->min);
183			if (ti->maxvalid)
184				fprintf(dst, " && (val <= %"PRIu64")", ti->max);
185			if (ti->alignvalid)
186				fprintf(dst, " && !(val %% %"PRIu64")", ti->align);
187			fprintf(dst, ");\n");
188		}
189
190		if (ti->shr && !no_asserts) {
191			fprintf(dst, "\tassert(!(val & 0x%x));\n", (1 << ti->shr) - 1);
192		}
193
194		fprintf(dst, "\treturn ((");
195
196		if (ti->type == RNN_TTYPE_FIXED) {
197			fprintf(dst, "((int32_t)(val * %d.0))", (1 << ti->radix));
198		} else if (ti->type == RNN_TTYPE_UFIXED) {
199			fprintf(dst, "((uint32_t)(val * %d.0))", (1 << ti->radix));
200		} else if (ti->type == RNN_TTYPE_FLOAT) {
201			if (width == 32)
202				fprintf(dst, "fui(val)");
203			else if (width == 16)
204				fprintf(dst, "_mesa_float_to_half(val)");
205			else
206				assert(!"invalid float size");
207		} else {
208			fprintf(dst, "val");
209		}
210
211		if (ti->shr)
212			fprintf(dst, " >> %d", ti->shr);
213
214		fprintf(dst, ") << %s__SHIFT) & %s__MASK;\n", prefix, prefix);
215		fprintf(dst, "}\n");
216
217		if (intype == RNN_TTYPE_ENUM)
218			free(typename);
219	}
220
221	int i;
222	for (i = 0; i < ti->valsnum; i++)
223		printvalue(ti->vals[i], ti->low);
224	for (i = 0; i < ti->bitfieldsnum; i++)
225		printbitfield(ti->bitfields[i], ti->low);
226}
227
228static void printbitfield (struct rnnbitfield *bf, int shift) {
229	if (bf->varinfo.dead)
230		return;
231	printtypeinfo (&bf->typeinfo, bf, bf->fullname, bf->file);
232}
233
234static void printdelem (struct rnndelem *elem, uint64_t offset, const char *str) {
235	int use_offset_fxn;
236	char *offsetfn = NULL;
237
238	if (elem->varinfo.dead)
239		return;
240
241	use_offset_fxn = elem->offsets || elem->doffset || elem->doffsets;
242	assert((!!elem->offsets + !!elem->doffset + !!elem->doffsets) <= 1);
243
244	if (use_offset_fxn)
245		asprintf(&offsetfn, "__offset_%s", elem->name);
246
247	if (elem->length != 1) {
248		ADDARRAY(elems, elem);
249		ADDARRAY(offsetfns, offsetfn);
250	}
251
252	if (elem->name) {
253		char *regname;
254		if (str) {
255			asprintf(&regname, "REG_%s_%s", elem->fullname, str);
256		} else {
257			asprintf(&regname, "REG_%s", elem->fullname);
258		}
259		if (elemsnum) {
260			int len;
261			FILE *dst = findfout(elem->file);
262			int i;
263
264			if (use_offset_fxn) {
265				fprintf(dst, "static inline uint32_t %s(", offsetfn);
266				if (elem->index)
267					fprintf(dst, "enum %s", elem->index->name);
268				else
269					fprintf(dst, "uint32_t");
270				fprintf(dst, " idx)\n");
271				fprintf(dst, "{\n");
272				if (elem->doffset) {
273					fprintf(dst, "\treturn (%s) + (%#" PRIx64 "*idx);\n", elem->doffset, elem->stride);
274				} else {
275					int valuesnum = elem->doffsets ? elem->doffsetsnum : elem->offsetsnum;
276
277					fprintf(dst, "\tswitch (idx) {\n");
278					for (i = 0; i < valuesnum; i++) {
279						struct rnnvalue *val = NULL;
280						fprintf(dst, "\t\tcase ");
281						if (elem->index) {
282							int j;
283							for (j = 0; j < elem->index->valsnum; j++) {
284								if (elem->index->vals[j]->value == i) {
285									val = elem->index->vals[j];
286									break;
287								}
288							}
289						}
290						if (val) {
291							fprintf(dst, "%s", val->name);
292						} else {
293							fprintf(dst, "%d", i);
294						}
295						if (elem->offsets) {
296							fprintf(dst, ": return 0x%08"PRIx64";\n", elem->offsets[i]);
297						} else {
298							fprintf(dst, ": return (%s);\n", elem->doffsets[i]);
299						}
300					}
301					fprintf(dst, "\t\tdefault: return INVALID_IDX(idx);\n");
302					fprintf(dst, "\t}\n");
303				}
304				fprintf(dst, "}\n");
305			}
306			fprintf (dst, "static inline uint32_t %s(", regname);
307			for (i = 0; i < elemsnum; i++) {
308				if (i)
309					fprintf(dst, ", ");
310				if (elems[i]->index)
311					fprintf(dst, "enum %s ", elems[i]->index->name);
312				else
313					fprintf(dst, "uint32_t ");
314				fprintf (dst, "i%d%n", i, &len);
315			}
316			fprintf (dst, ") { return ");
317			fprintf (dst, "0x%08"PRIx64"", offset + elem->offset);
318			for (i = 0; i < elemsnum; i++) {
319				if (offsetfns[i])
320					fprintf(dst, " + %s(i%d)", offsetfns[i], i);
321				else
322					fprintf (dst, " + %#" PRIx64 "*i%d", elems[i]->stride, i);
323			}
324			fprintf (dst, "; }\n");
325		} else
326			printdef (regname, 0, 0, offset + elem->offset, elem->file);
327
328		free(regname);
329/*
330		if (elem->stride)
331			printdef (elem->fullname, "ESIZE", 0, elem->stride, elem->file);
332		if (elem->length != 1)
333			printdef (elem->fullname, "LEN", 0, elem->length, elem->file);
334*/
335		printtypeinfo (&elem->typeinfo, NULL, elem->fullname, elem->file);
336	}
337	fprintf (findfout(elem->file), "\n");
338	int j;
339	for (j = 0; j < elem->subelemsnum; j++) {
340		printdelem(elem->subelems[j], offset + elem->offset, elem->varinfo.prefixstr);
341	}
342	if (elem->length != 1) {
343		elemsnum--;
344		offsetfnsnum--;
345	}
346	free(offsetfn);
347}
348
349static void print_file_info_(FILE *dst, struct stat* sb, struct tm* tm)
350{
351	char timestr[64];
352	strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", tm);
353	fprintf(dst, "(%7Lu bytes, from %s)\n", (unsigned long long)sb->st_size, timestr);
354}
355
356static void print_file_info(FILE *dst, const char* file)
357{
358	struct stat sb;
359	struct tm tm;
360	stat(file, &sb);
361	gmtime_r(&sb.st_mtime, &tm);
362	print_file_info_(dst, &sb, &tm);
363}
364
365static void printhead(struct fout f, struct rnndb *db) {
366	int i, j;
367	struct stat sb;
368	struct tm tm;
369	stat(f.name, &sb);
370	gmtime_r(&sb.st_mtime, &tm);
371	fprintf (f.file, "#ifndef %s\n", f.guard);
372	fprintf (f.file, "#define %s\n", f.guard);
373	fprintf (f.file, "\n");
374	fprintf(f.file,
375		"/* Autogenerated file, DO NOT EDIT manually!\n"
376		"\n"
377		"This file was generated by the rules-ng-ng headergen tool in this git repository:\n"
378		"http://github.com/freedreno/envytools/\n"
379		"git clone https://github.com/freedreno/envytools.git\n"
380		"\n"
381		"The rules-ng-ng source files this header was generated from are:\n");
382	unsigned maxlen = 0;
383	for(i = 0; i < db->filesnum; ++i) {
384		unsigned len = strlen(db->files[i]);
385		if(len > maxlen)
386			maxlen = len;
387	}
388	for(i = 0; i < db->filesnum; ++i) {
389		unsigned len = strlen(db->files[i]);
390		fprintf(f.file, "- %s%*s ", db->files[i], maxlen - len, "");
391		print_file_info(f.file, db->files[i]);
392	}
393	fprintf(f.file,
394		"\n"
395		"Copyright (C) ");
396	if(db->copyright.firstyear && db->copyright.firstyear < (1900 + tm.tm_year))
397		fprintf(f.file, "%u-", db->copyright.firstyear);
398	fprintf(f.file, "%u", 1900 + tm.tm_year);
399	if(db->copyright.authorsnum) {
400		fprintf(f.file, " by the following authors:");
401		for(i = 0; i < db->copyright.authorsnum; ++i) {
402			fprintf(f.file, "\n- ");
403			if(db->copyright.authors[i]->name)
404				fprintf(f.file, "%s", db->copyright.authors[i]->name);
405			if(db->copyright.authors[i]->email)
406				fprintf(f.file, " <%s>", db->copyright.authors[i]->email);
407			if(db->copyright.authors[i]->nicknamesnum) {
408				for(j = 0; j < db->copyright.authors[i]->nicknamesnum; ++j) {
409					fprintf(f.file, "%s%s", (j ? ", " : " ("), db->copyright.authors[i]->nicknames[j]);
410				}
411				fprintf(f.file, ")");
412			}
413		}
414	}
415	fprintf(f.file, "\n");
416	if(db->copyright.license)
417		fprintf(f.file, "\n%s\n", db->copyright.license);
418	fprintf(f.file, "*/\n\n\n");
419}
420
421int main(int argc, char **argv) {
422	char *file;
423	struct rnndb *db;
424	int i, j;
425
426	if (argc < 2) {
427		fprintf(stderr, "Usage:\n\theadergen database-file\n");
428		exit(1);
429	}
430
431	if ((argc >= 3) && !strcmp(argv[1], "--no-asserts")) {
432		no_asserts = true;
433		file = argv[2];
434	} else {
435		file = argv[1];
436	}
437
438	rnn_init();
439	db = rnn_newdb();
440	rnn_parsefile (db, file);
441	rnn_prepdb (db);
442	for(i = 0; i < db->filesnum; ++i) {
443		char *dstname = malloc(strlen(db->files[i]) + 3);
444		char *pretty;
445		strcpy(dstname, db->files[i]);
446		strcat(dstname, ".h");
447		struct fout f = { db->files[i], fopen(dstname, "w") };
448		if (!f.file) {
449			perror(dstname);
450			exit(1);
451		}
452		free(dstname);
453		pretty = strrchr(f.name, '/');
454		if (pretty)
455			pretty += 1;
456		else
457			pretty = f.name;
458		f.guard = strdup(pretty);
459		for (j = 0; j < strlen(f.guard); j++)
460			if (isalnum(f.guard[j]))
461				f.guard[j] = toupper(f.guard[j]);
462			else
463				f.guard[j] = '_';
464		ADDARRAY(fouts, f);
465		printhead(f, db);
466	}
467
468	for (i = 0; i < db->enumsnum; i++) {
469		FILE *dst = NULL;
470		int j;
471		for (j = 0; j < db->enums[i]->valsnum; j++) {
472			if (!dst) {
473				dst = findfout(db->enums[i]->vals[j]->file);
474				fprintf(dst, "enum %s {\n", db->enums[i]->name);
475			}
476			if (0xffff0000 & db->enums[i]->vals[j]->value)
477				fprintf(dst, "\t%s = 0x%08"PRIx64",\n", db->enums[i]->vals[j]->name,
478						db->enums[i]->vals[j]->value);
479			else
480				fprintf(dst, "\t%s = %"PRIu64",\n", db->enums[i]->vals[j]->name,
481						db->enums[i]->vals[j]->value);
482		}
483		if (dst) {
484			fprintf(dst, "};\n\n");
485		}
486	}
487	for (i = 0; i < db->bitsetsnum; i++) {
488		if (db->bitsets[i]->isinline)
489			continue;
490		int j;
491		for (j = 0; j < db->bitsets[i]->bitfieldsnum; j++)
492			printbitfield (db->bitsets[i]->bitfields[j], 0);
493	}
494	for (i = 0; i < db->domainsnum; i++) {
495		if (db->domains[i]->size)
496			printdef (db->domains[i]->fullname, "SIZE", 0, db->domains[i]->size, db->domains[i]->file);
497		int j;
498		for (j = 0; j < db->domains[i]->subelemsnum; j++) {
499			printdelem(db->domains[i]->subelems[j], 0, NULL);
500		}
501	}
502	for(i = 0; i < foutsnum; ++i) {
503		fprintf (fouts[i].file, "\n#endif /* %s */\n", fouts[i].guard);
504	}
505	return db->estatus;
506}
507