1 /*
2 	opcodes.c
3 
4 	opcode searching
5 
6 	Copyright (C) 2002 Bill Currie <bill@taniwha.org>
7 
8 	Author: Bill Currie <bill@taniwha.org>
9 	Date: 2002/06/01
10 
11 	This program is free software; you can redistribute it and/or
12 	modify it under the terms of the GNU General Public License
13 	as published by the Free Software Foundation; either version 2
14 	of the License, or (at your option) any later version.
15 
16 	This program is distributed in the hope that it will be useful,
17 	but WITHOUT ANY WARRANTY; without even the implied warranty of
18 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 
20 	See the GNU General Public License for more details.
21 
22 	You should have received a copy of the GNU General Public License
23 	along with this program; if not, write to:
24 
25 		Free Software Foundation, Inc.
26 		59 Temple Place - Suite 330
27 		Boston, MA  02111-1307, USA
28 
29 */
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33 
34 #ifdef HAVE_STRING_H
35 # include <string.h>
36 #endif
37 #ifdef HAVE_STRINGS_H
38 # include <strings.h>
39 #endif
40 #include <stdlib.h>
41 
42 #include <QF/hash.h>
43 
44 #include "opcodes.h"
45 #include "options.h"
46 #include "qfcc.h"
47 #include "statements.h"
48 #include "type.h"
49 
50 hashtab_t  *opcode_type_table;
51 hashtab_t  *opcode_void_table;
52 
53 #define ROTL(x,n) ((((unsigned)(x))<<(n))|((unsigned)(x))>>(32-n))
54 
55 static uintptr_t
get_hash(const void * _op,void * _tab)56 get_hash (const void *_op, void *_tab)
57 {
58 	opcode_t   *op = (opcode_t *) _op;
59 	uintptr_t   hash;
60 
61 	hash = ROTL (~op->type_a, 8) + ROTL (~op->type_b, 16)
62 		+ ROTL (~op->type_c, 24);
63 	return hash + Hash_String (op->name);
64 }
65 
66 static int
compare(const void * _opa,const void * _opb,void * unused)67 compare (const void *_opa, const void *_opb, void *unused)
68 {
69 	opcode_t   *opa = (opcode_t *) _opa;
70 	opcode_t   *opb = (opcode_t *) _opb;
71 	int         cmp;
72 
73 	cmp = (opa->type_a == opb->type_a)
74 		  && (opa->type_b == opb->type_b)
75 		  && (opa->type_c == opb->type_c);
76 	return cmp && !strcmp (opa->name, opb->name);
77 }
78 
79 static const char *
get_key(const void * op,void * unused)80 get_key (const void *op, void *unused)
81 {
82 	return ((opcode_t *) op)->name;
83 }
84 
85 static int
check_operand_type(etype_t ot1,etype_t ot2)86 check_operand_type (etype_t ot1, etype_t ot2)
87 {
88 	if ((ot1 == ev_void && ot2 != ev_invalid)
89 		|| ot1 == ot2)
90 		return 1;
91 	return 0;
92 }
93 
94 opcode_t *
opcode_find(const char * name,operand_t * op_a,operand_t * op_b,operand_t * op_c)95 opcode_find (const char *name, operand_t *op_a, operand_t *op_b,
96 			 operand_t *op_c)
97 {
98 	opcode_t    search_op;
99 	opcode_t   *op;
100 	opcode_t   *sop;
101 	void      **op_list;
102 	int         i;
103 
104 	search_op.name = name;
105 	search_op.type_a = op_a ? op_a->type : ev_invalid;
106 	search_op.type_b = op_b ? op_b->type : ev_invalid;
107 	search_op.type_c = op_c ? op_c->type : ev_invalid;
108 	op = Hash_FindElement (opcode_type_table, &search_op);
109 	if (op)
110 		return op;
111 	op_list = Hash_FindList (opcode_void_table, name);
112 	if (!op_list)
113 		return op;
114 	for (i = 0; !op && op_list[i]; i++) {
115 		sop = op_list[i];
116 		if (check_operand_type (sop->type_a, search_op.type_a)
117 			&& check_operand_type (sop->type_b, search_op.type_b)
118 			&& check_operand_type (sop->type_c, search_op.type_c))
119 			op = sop;
120 	}
121 	free (op_list);
122 	return op;
123 }
124 
125 static void
opcode_free(void * _op,void * unused)126 opcode_free (void *_op, void *unused)
127 {
128 	free (_op);
129 }
130 
131 void
opcode_init(void)132 opcode_init (void)
133 {
134 	opcode_t   *op, *mop;
135 
136 	if (opcode_type_table) {
137 		Hash_FlushTable (opcode_void_table);
138 		Hash_FlushTable (opcode_type_table);
139 	} else {
140 		PR_Opcode_Init ();
141 		opcode_type_table = Hash_NewTable (1021, 0, opcode_free, 0);
142 		Hash_SetHashCompare (opcode_type_table, get_hash, compare);
143 		opcode_void_table = Hash_NewTable (1021, get_key, 0, 0);
144 	}
145 	for (op = pr_opcodes; op->name; op++) {
146 		if (op->min_version > options.code.progsversion)
147 			continue;
148 		mop = malloc (sizeof (opcode_t));
149 		*mop = *op;
150 		if (options.code.progsversion == PROG_ID_VERSION) {
151 			// v6 progs have no concept of integer, but the QF engine
152 			// treats the operands of certain operands as integers
153 			// irrespective the progs version, so convert the engine's
154 			// view of the operands to the prog's view.
155 			if (mop->type_a == ev_integer)
156 				mop->type_a = ev_float;
157 			if (mop->type_b == ev_integer)
158 				mop->type_b = ev_float;
159 			if (mop->type_c == ev_integer)
160 				mop->type_c = ev_float;
161 		}
162 		Hash_AddElement (opcode_type_table, mop);
163 		if (mop->type_a == ev_void || mop->type_b == ev_void
164 			|| mop->type_c == ev_void)
165 			Hash_Add (opcode_void_table, mop);
166 	}
167 }
168