1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* exprdesc.c */ |
4 | /* */ |
5 | /* Expression descriptor structure */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2002-2010, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | /* common */ |
37 | #include "check.h" |
38 | #include "strbuf.h" |
39 | |
40 | /* cc65 */ |
41 | #include "asmlabel.h" |
42 | #include "datatype.h" |
43 | #include "error.h" |
44 | #include "exprdesc.h" |
45 | #include "stackptr.h" |
46 | #include "symentry.h" |
47 | |
48 | |
49 | |
50 | /*****************************************************************************/ |
51 | /* Code */ |
52 | /*****************************************************************************/ |
53 | |
54 | |
55 | |
56 | ExprDesc* ED_Init (ExprDesc* Expr) |
57 | /* Initialize an ExprDesc */ |
58 | { |
59 | Expr->Sym = 0; |
60 | Expr->Type = 0; |
61 | Expr->Flags = 0; |
62 | Expr->Name = 0; |
63 | Expr->IVal = 0; |
64 | Expr->FVal = FP_D_Make (0.0); |
65 | Expr->LVal = 0; |
66 | Expr->BitOffs = 0; |
67 | Expr->BitWidth = 0; |
68 | return Expr; |
69 | } |
70 | |
71 | |
72 | |
73 | void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth) |
74 | /* Make this expression a bit field expression */ |
75 | { |
76 | Expr->Flags |= E_BITFIELD; |
77 | Expr->BitOffs = BitOffs; |
78 | Expr->BitWidth = BitWidth; |
79 | } |
80 | |
81 | |
82 | |
83 | void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End) |
84 | /* Set the code range for this expression */ |
85 | { |
86 | Expr->Flags |= E_HAVE_MARKS; |
87 | Expr->Start = *Start; |
88 | Expr->End = *End; |
89 | } |
90 | |
91 | |
92 | |
93 | int ED_CodeRangeIsEmpty (const ExprDesc* Expr) |
94 | /* Return true if no code was output for this expression */ |
95 | { |
96 | /* We must have code marks */ |
97 | PRECONDITION (Expr->Flags & E_HAVE_MARKS); |
98 | |
99 | return CodeRangeIsEmpty (&Expr->Start, &Expr->End); |
100 | } |
101 | |
102 | |
103 | |
104 | const char* ED_GetLabelName (const ExprDesc* Expr, long Offs) |
105 | /* Return the assembler label name of the given expression. Beware: This |
106 | ** function may use a static buffer, so the name may get "lost" on the second |
107 | ** call to the function. |
108 | */ |
109 | { |
110 | static StrBuf Buf = STATIC_STRBUF_INITIALIZER; |
111 | |
112 | /* Expr may have it's own offset, adjust Offs accordingly */ |
113 | Offs += Expr->IVal; |
114 | |
115 | /* Generate a label depending on the location */ |
116 | switch (ED_GetLoc (Expr)) { |
117 | |
118 | case E_LOC_ABS: |
119 | /* Absolute: numeric address or const */ |
120 | SB_Printf (&Buf, "$%04X" , (int)(Offs & 0xFFFF)); |
121 | break; |
122 | |
123 | case E_LOC_GLOBAL: |
124 | case E_LOC_STATIC: |
125 | /* Global or static variable */ |
126 | if (Offs) { |
127 | SB_Printf (&Buf, "%s%+ld" , SymGetAsmName (Expr->Sym), Offs); |
128 | } else { |
129 | SB_Printf (&Buf, "%s" , SymGetAsmName (Expr->Sym)); |
130 | } |
131 | break; |
132 | |
133 | case E_LOC_REGISTER: |
134 | /* Register variable */ |
135 | SB_Printf (&Buf, "regbank+%u" , (unsigned)((Offs + Expr->Name) & 0xFFFFU)); |
136 | break; |
137 | |
138 | case E_LOC_LITERAL: |
139 | /* Literal in the literal pool */ |
140 | if (Offs) { |
141 | SB_Printf (&Buf, "%s%+ld" , LocalLabelName (Expr->Name), Offs); |
142 | } else { |
143 | SB_Printf (&Buf, "%s" , LocalLabelName (Expr->Name)); |
144 | } |
145 | break; |
146 | |
147 | default: |
148 | Internal ("Invalid location in ED_GetLabelName: 0x%04X" , ED_GetLoc (Expr)); |
149 | } |
150 | |
151 | /* Return a pointer to the static buffer */ |
152 | return SB_GetConstBuf (&Buf); |
153 | } |
154 | |
155 | |
156 | |
157 | int ED_GetStackOffs (const ExprDesc* Expr, int Offs) |
158 | /* Get the stack offset of an address on the stack in Expr taking into account |
159 | ** an additional offset in Offs. |
160 | */ |
161 | { |
162 | PRECONDITION (ED_IsLocStack (Expr)); |
163 | Offs += ((int) Expr->IVal) - StackPtr; |
164 | CHECK (Offs >= 0); /* Cannot handle negative stack offsets */ |
165 | return Offs; |
166 | } |
167 | |
168 | |
169 | |
170 | ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, Type* Type) |
171 | /* Make Expr an absolute const with the given value and type. */ |
172 | { |
173 | Expr->Sym = 0; |
174 | Expr->Type = Type; |
175 | Expr->Flags = E_LOC_ABS | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); |
176 | Expr->Name = 0; |
177 | Expr->IVal = Value; |
178 | Expr->FVal = FP_D_Make (0.0); |
179 | return Expr; |
180 | } |
181 | |
182 | |
183 | |
184 | ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) |
185 | /* Make Expr a constant integer expression with the given value */ |
186 | { |
187 | Expr->Sym = 0; |
188 | Expr->Type = type_int; |
189 | Expr->Flags = E_LOC_ABS | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); |
190 | Expr->Name = 0; |
191 | Expr->IVal = Value; |
192 | Expr->FVal = FP_D_Make (0.0); |
193 | return Expr; |
194 | } |
195 | |
196 | |
197 | |
198 | ExprDesc* ED_MakeRValExpr (ExprDesc* Expr) |
199 | /* Convert Expr into a rvalue which is in the primary register without an |
200 | ** offset. |
201 | */ |
202 | { |
203 | Expr->Sym = 0; |
204 | Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_NEED_TEST | E_CC_SET); |
205 | Expr->Flags |= (E_LOC_EXPR | E_RTYPE_RVAL); |
206 | Expr->Name = 0; |
207 | Expr->IVal = 0; /* No offset */ |
208 | Expr->FVal = FP_D_Make (0.0); |
209 | return Expr; |
210 | } |
211 | |
212 | |
213 | |
214 | ExprDesc* ED_MakeLValExpr (ExprDesc* Expr) |
215 | /* Convert Expr into a lvalue which is in the primary register without an |
216 | ** offset. |
217 | */ |
218 | { |
219 | Expr->Sym = 0; |
220 | Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_NEED_TEST | E_CC_SET); |
221 | Expr->Flags |= (E_LOC_EXPR | E_RTYPE_LVAL); |
222 | Expr->Name = 0; |
223 | Expr->IVal = 0; /* No offset */ |
224 | Expr->FVal = FP_D_Make (0.0); |
225 | return Expr; |
226 | } |
227 | |
228 | |
229 | |
230 | int ED_IsConst (const ExprDesc* Expr) |
231 | /* Return true if the expression denotes a constant of some sort. This can be a |
232 | ** numeric constant, the address of a global variable (maybe with offset) or |
233 | ** similar. |
234 | */ |
235 | { |
236 | return ED_IsRVal (Expr) && (Expr->Flags & E_LOC_CONST) != 0; |
237 | } |
238 | |
239 | |
240 | |
241 | int ED_IsConstAbsInt (const ExprDesc* Expr) |
242 | /* Return true if the expression is a constant (numeric) integer. */ |
243 | { |
244 | return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == (E_LOC_ABS|E_RTYPE_RVAL) && |
245 | IsClassInt (Expr->Type); |
246 | } |
247 | |
248 | |
249 | |
250 | int ED_IsNullPtr (const ExprDesc* Expr) |
251 | /* Return true if the given expression is a NULL pointer constant */ |
252 | { |
253 | return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE|E_BITFIELD)) == |
254 | (E_LOC_ABS|E_RTYPE_RVAL) && |
255 | Expr->IVal == 0 && |
256 | IsClassInt (Expr->Type); |
257 | } |
258 | |
259 | |
260 | |
261 | int ED_IsBool (const ExprDesc* Expr) |
262 | /* Return true of the expression can be treated as a boolean, that is, it can |
263 | ** be an operand to a compare operation. |
264 | */ |
265 | { |
266 | /* Either ints, floats, or pointers can be used in a boolean context */ |
267 | return IsClassInt (Expr->Type) || |
268 | IsClassFloat (Expr->Type) || |
269 | IsClassPtr (Expr->Type); |
270 | } |
271 | |
272 | |
273 | |
274 | void PrintExprDesc (FILE* F, ExprDesc* E) |
275 | /* Print an ExprDesc */ |
276 | { |
277 | unsigned Flags; |
278 | char Sep; |
279 | |
280 | fprintf (F, "Symbol: %s\n" , E->Sym? E->Sym->Name : "(none)" ); |
281 | if (E->Type) { |
282 | fprintf (F, "Type: " ); |
283 | PrintType (F, E->Type); |
284 | fprintf (F, "\nRaw type: " ); |
285 | PrintRawType (F, E->Type); |
286 | } else { |
287 | fprintf (F, "Type: (unknown)\n" |
288 | "Raw type: (unknown)\n" ); |
289 | } |
290 | fprintf (F, "IVal: 0x%08lX\n" , E->IVal); |
291 | fprintf (F, "FVal: %f\n" , FP_D_ToFloat (E->FVal)); |
292 | |
293 | Flags = E->Flags; |
294 | Sep = '('; |
295 | fprintf (F, "Flags: 0x%04X " , Flags); |
296 | if (Flags & E_LOC_ABS) { |
297 | fprintf (F, "%cE_LOC_ABS" , Sep); |
298 | Flags &= ~E_LOC_ABS; |
299 | Sep = ','; |
300 | } |
301 | if (Flags & E_LOC_GLOBAL) { |
302 | fprintf (F, "%cE_LOC_GLOBAL" , Sep); |
303 | Flags &= ~E_LOC_GLOBAL; |
304 | Sep = ','; |
305 | } |
306 | if (Flags & E_LOC_STATIC) { |
307 | fprintf (F, "%cE_LOC_STATIC" , Sep); |
308 | Flags &= ~E_LOC_STATIC; |
309 | Sep = ','; |
310 | } |
311 | if (Flags & E_LOC_REGISTER) { |
312 | fprintf (F, "%cE_LOC_REGISTER" , Sep); |
313 | Flags &= ~E_LOC_REGISTER; |
314 | Sep = ','; |
315 | } |
316 | if (Flags & E_LOC_STACK) { |
317 | fprintf (F, "%cE_LOC_STACK" , Sep); |
318 | Flags &= ~E_LOC_STACK; |
319 | Sep = ','; |
320 | } |
321 | if (Flags & E_LOC_PRIMARY) { |
322 | fprintf (F, "%cE_LOC_PRIMARY" , Sep); |
323 | Flags &= ~E_LOC_PRIMARY; |
324 | Sep = ','; |
325 | } |
326 | if (Flags & E_LOC_EXPR) { |
327 | fprintf (F, "%cE_LOC_EXPR" , Sep); |
328 | Flags &= ~E_LOC_EXPR; |
329 | Sep = ','; |
330 | } |
331 | if (Flags & E_LOC_LITERAL) { |
332 | fprintf (F, "%cE_LOC_LITERAL" , Sep); |
333 | Flags &= ~E_LOC_LITERAL; |
334 | Sep = ','; |
335 | } |
336 | if (Flags & E_RTYPE_LVAL) { |
337 | fprintf (F, "%cE_RTYPE_LVAL" , Sep); |
338 | Flags &= ~E_RTYPE_LVAL; |
339 | Sep = ','; |
340 | } |
341 | if (Flags & E_BITFIELD) { |
342 | fprintf (F, "%cE_BITFIELD" , Sep); |
343 | Flags &= ~E_BITFIELD; |
344 | Sep = ','; |
345 | } |
346 | if (Flags & E_NEED_TEST) { |
347 | fprintf (F, "%cE_NEED_TEST" , Sep); |
348 | Flags &= ~E_NEED_TEST; |
349 | Sep = ','; |
350 | } |
351 | if (Flags & E_CC_SET) { |
352 | fprintf (F, "%cE_CC_SET" , Sep); |
353 | Flags &= ~E_CC_SET; |
354 | Sep = ','; |
355 | } |
356 | if (Flags) { |
357 | fprintf (F, "%c,0x%04X" , Sep, Flags); |
358 | Sep = ','; |
359 | } |
360 | if (Sep != '(') { |
361 | fputc (')', F); |
362 | } |
363 | fprintf (F, "\nName: 0x%08lX\n" , (unsigned long)E->Name); |
364 | } |
365 | |
366 | |
367 | |
368 | Type* ReplaceType (ExprDesc* Expr, const Type* NewType) |
369 | /* Replace the type of Expr by a copy of Newtype and return the old type string */ |
370 | { |
371 | Type* OldType = Expr->Type; |
372 | Expr->Type = TypeDup (NewType); |
373 | return OldType; |
374 | } |
375 | |