]> git.sur5r.net Git - cc65/blob - src/cc65/stdfunc.c
Rewrote code generation for the strlen standard function. Added code for
[cc65] / src / cc65 / stdfunc.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 stdfunc.c                                 */
4 /*                                                                           */
5 /*         Handle inlining of known functions for the cc65 compiler          */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2004 Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 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 #include <stdlib.h>
37 #include <string.h>
38
39 /* common */
40 #include "attrib.h"
41 #include "check.h"
42
43 /* cc65 */
44 #include "asmlabel.h"
45 #include "codegen.h"
46 #include "error.h"
47 #include "funcdesc.h"
48 #include "global.h"
49 #include "litpool.h"
50 #include "scanner.h"
51 #include "stdfunc.h"
52 #include "stdnames.h"
53 #include "typeconv.h"
54
55
56
57 /*****************************************************************************/
58 /*                             Function forwards                             */
59 /*****************************************************************************/
60
61
62
63 static void StdFunc_memset (FuncDesc*, ExprDesc*);
64 static void StdFunc_strlen (FuncDesc*, ExprDesc*);
65
66
67
68 /*****************************************************************************/
69 /*                                   Data                                    */
70 /*****************************************************************************/
71
72
73
74 /* Table with all known functions and their handlers. Must be sorted
75  * alphabetically!
76  */
77 static struct StdFuncDesc {
78     const char*         Name;
79     void                (*Handler) (FuncDesc*, ExprDesc*);
80 } StdFuncs[] = {
81     {   "memset",       StdFunc_memset          },
82     {   "strlen",       StdFunc_strlen          },
83
84 };
85 #define FUNC_COUNT      (sizeof (StdFuncs) / sizeof (StdFuncs[0]))
86
87
88
89 /*****************************************************************************/
90 /*                             Helper functions                              */
91 /*****************************************************************************/
92
93
94
95 static int CmpFunc (const void* Key, const void* Elem)
96 /* Compare function for bsearch */
97 {
98     return strcmp ((const char*) Key, ((const struct StdFuncDesc*) Elem)->Name);
99 }
100
101
102
103 static unsigned ParseArg (type* Type, ExprDesc* Arg)
104 /* Parse one argument but do not push it onto the stack. Return the code
105  * generator flags needed to do the actual push.
106  */
107 {
108     /* We have a prototype, so chars may be pushed as chars */
109     unsigned Flags = CF_FORCECHAR;
110
111     /* Read the expression we're going to pass to the function */
112     hie1 (Arg);
113
114     /* Convert this expression to the expected type */
115     TypeConversion (Arg, Type);
116
117     /* If the value is a constant, set the flag, otherwise load it into the
118      * primary register.
119      */
120     if (ED_IsConstAbsInt (Arg)) {
121         /* Remember that we have a constant value */
122         Flags |= CF_CONST;
123     } else {
124         /* Load into the primary */
125         ExprLoad (CF_NONE, Arg);
126         ED_MakeRVal (Arg);
127     }
128
129     /* Use the type of the argument for the push */
130     return (Flags | TypeOf (Arg->Type));
131 }
132
133
134
135 /*****************************************************************************/
136 /*                          Handle known functions                           */
137 /*****************************************************************************/
138
139
140
141 static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
142 /* Handle the memset function */
143 {
144     /* Argument types */
145     static type Arg1Type[] = { T_PTR, T_VOID, T_END };  /* void* */
146     static type Arg2Type[] = { T_INT, T_END };          /* int */
147     static type Arg3Type[] = { T_UINT, T_END };         /* size_t */
148
149     unsigned Flags;
150     ExprDesc Arg;
151     int      MemSet    = 1;             /* Use real memset if true */
152     unsigned ParamSize = 0;
153
154     /* Argument #1 */
155     Flags = ParseArg (Arg1Type, &Arg);
156     g_push (Flags, Arg.Val);
157     ParamSize += SizeOf (Arg1Type);
158     ConsumeComma ();
159
160     /* Argument #2. This argument is special in that we will call another
161      * function if it is a constant zero.
162      */
163     Flags = ParseArg (Arg2Type, &Arg);
164     if ((Flags & CF_CONST) != 0 && Arg.Val == 0) {
165         /* Don't call memset, call bzero instead */
166         MemSet = 0;
167     } else {
168         /* Push the argument */
169         g_push (Flags, Arg.Val);
170         ParamSize += SizeOf (Arg2Type);
171     }
172     ConsumeComma ();
173
174     /* Argument #3. Since memset is a fastcall function, we must load the
175      * arg into the primary if it is not already there. This parameter is
176      * also ignored for the calculation of the parameter size, since it is
177      * not passed via the stack.
178      */
179     Flags = ParseArg (Arg3Type, &Arg);
180     if (Flags & CF_CONST) {
181         if (Arg.Val == 0) {
182             Warning ("Call to memset has no effect");
183         }
184         ExprLoad (CF_FORCECHAR, &Arg);
185     }
186
187     /* Emit the actual function call */
188     g_call (CF_NONE, MemSet? Func_memset : Func__bzero, ParamSize);
189
190     /* We expect the closing brace */
191     ConsumeRParen ();
192
193     /* The function result is an rvalue in the primary register */
194     ED_MakeRValExpr (Expr);
195     Expr->Type = GetFuncReturn (Expr->Type);
196 }
197
198
199
200 static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
201 /* Handle the strlen function */
202 {
203     static type ArgType[] = { T_PTR, T_SCHAR, T_END };
204     ExprDesc    Arg;
205     unsigned    L;
206
207
208     /* Setup the argument type string */
209     ArgType[1] = GetDefaultChar () | T_QUAL_CONST;
210
211     /* Evaluate the parameter */
212     hie1 (&Arg);
213
214     /* We can generate special code for several locations */
215     if (ED_IsLocConst (&Arg) && IsTypeArray (Arg.Type)) {
216
217         /* Do type conversion */
218         TypeConversion (&Arg, ArgType);
219
220         /* If the expression is a literal, and if string literals are read
221          * only, we can calculate the length of the string and remove it
222          * from the literal pool. Otherwise we have to calculate the length
223          * at runtime.
224          */
225         if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings)) {
226
227             /* Constant string literal */
228             ED_MakeConstAbs (Expr, strlen (GetLiteral (Arg.Val)), type_size_t);
229             ResetLiteralPoolOffs (Arg.Val);
230
231         } else {
232
233             /* Generate the strlen code */
234             L = GetLocalLabel ();
235             AddCodeLine ("ldy #$FF");
236             g_defcodelabel (L);
237             AddCodeLine ("iny");
238             AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg, 0));
239             AddCodeLine ("bne %s", LocalLabelName (L));
240             AddCodeLine ("tax");
241             AddCodeLine ("tya");
242
243             /* The function result is an rvalue in the primary register */
244             ED_MakeRValExpr (Expr);
245             Expr->Type = type_size_t;
246
247         }
248
249     } else if (ED_IsLocStack (&Arg) && StackPtr >= -255 && IsTypeArray (Arg.Type)) {
250
251         /* Calculate the true stack offset */
252         unsigned Offs = (unsigned) (Arg.Val - StackPtr);
253
254         /* Do type conversion */
255         TypeConversion (&Arg, ArgType);
256
257         /* Generate the strlen code */
258         L = GetLocalLabel ();
259         AddCodeLine ("ldx #$FF");
260         AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
261         g_defcodelabel (L);
262         AddCodeLine ("inx");
263         AddCodeLine ("iny");
264         AddCodeLine ("lda (sp),y");
265         AddCodeLine ("bne %s", LocalLabelName (L));
266         AddCodeLine ("txa");
267         AddCodeLine ("ldx #$00");
268
269         /* The function result is an rvalue in the primary register */
270         ED_MakeRValExpr (Expr);
271         Expr->Type = type_size_t;
272
273     } else if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsTypePtr (Arg.Type)) {
274
275         /* Do type conversion */
276         TypeConversion (&Arg, ArgType);
277
278         /* Generate the strlen code */
279         L = GetLocalLabel ();
280         AddCodeLine ("ldy #$FF");
281         g_defcodelabel (L);
282         AddCodeLine ("iny");
283         AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg, 0));
284         AddCodeLine ("bne %s", LocalLabelName (L));
285         AddCodeLine ("tax");
286         AddCodeLine ("tya");
287
288         /* The function result is an rvalue in the primary register */
289         ED_MakeRValExpr (Expr);
290         Expr->Type = type_size_t;
291
292     } else {
293
294         /* Do type conversion */
295         TypeConversion (&Arg, ArgType);
296
297         /* Load the expression into the primary */
298         ExprLoad (CF_NONE, &Arg);
299
300         /* Call the strlen function */
301         AddCodeLine ("jsr _%s", Func_strlen);
302
303         /* The function result is an rvalue in the primary register */
304         ED_MakeRValExpr (Expr);
305         Expr->Type = type_size_t;
306
307     }
308
309     /* We expect the closing brace */
310     ConsumeRParen ();
311 }
312
313
314
315 /*****************************************************************************/
316 /*                                   Code                                    */
317 /*****************************************************************************/
318
319
320
321 int FindStdFunc (const char* Name)
322 /* Determine if the given function is a known standard function that may be
323  * called in a special way. If so, return the index, otherwise return -1.
324  */
325 {
326     /* Look into the table for known names */
327     struct StdFuncDesc* D =
328         bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
329
330     /* Return the function index or -1 */
331     if (D == 0) {
332         return -1;
333     } else {
334         return D - StdFuncs;
335     }
336 }
337
338
339
340 void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
341 /* Generate code for a known standard function. */
342 {
343     struct StdFuncDesc* D;
344
345     /* Get a pointer to the table entry */
346     CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
347     D = StdFuncs + Index;
348
349     /* Call the handler function */
350     D->Handler (F, lval);
351 }
352
353
354