1/*****************************************************************************/
2/* */
3/* stdfunc.c */
4/* */
5/* Handle inlining of known functions for the cc65 compiler */
6/* */
7/* */
8/* */
9/* (C) 1998-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#include <stdlib.h>
37#include <string.h>
38
39/* common */
40#include "attrib.h"
41#include "check.h"
42
43/* cc65 */
44#include "asmcode.h"
45#include "asmlabel.h"
46#include "codegen.h"
47#include "error.h"
48#include "funcdesc.h"
49#include "global.h"
50#include "litpool.h"
51#include "loadexpr.h"
52#include "scanner.h"
53#include "stackptr.h"
54#include "stdfunc.h"
55#include "stdnames.h"
56#include "typeconv.h"
57
58
59
60/*****************************************************************************/
61/* Function forwards */
62/*****************************************************************************/
63
64
65
66static void StdFunc_memcpy (FuncDesc*, ExprDesc*);
67static void StdFunc_memset (FuncDesc*, ExprDesc*);
68static void StdFunc_strcmp (FuncDesc*, ExprDesc*);
69static void StdFunc_strcpy (FuncDesc*, ExprDesc*);
70static void StdFunc_strlen (FuncDesc*, ExprDesc*);
71
72
73
74/*****************************************************************************/
75/* Data */
76/*****************************************************************************/
77
78
79
80/* Table with all known functions and their handlers. Must be sorted
81** alphabetically!
82*/
83static struct StdFuncDesc {
84 const char* Name;
85 void (*Handler) (FuncDesc*, ExprDesc*);
86} StdFuncs[] = {
87 { "memcpy", StdFunc_memcpy },
88 { "memset", StdFunc_memset },
89 { "strcmp", StdFunc_strcmp },
90 { "strcpy", StdFunc_strcpy },
91 { "strlen", StdFunc_strlen },
92
93};
94#define FUNC_COUNT (sizeof (StdFuncs) / sizeof (StdFuncs[0]))
95
96typedef struct ArgDesc ArgDesc;
97struct ArgDesc {
98 const Type* ArgType; /* Required argument type */
99 ExprDesc Expr; /* Argument expression */
100 const Type* Type; /* The original type before conversion */
101 CodeMark Load; /* Start of argument load code */
102 CodeMark Push; /* Start of argument push code */
103 CodeMark End; /* End of the code for calculation+push */
104 unsigned Flags; /* Code generation flags */
105};
106
107
108
109/*****************************************************************************/
110/* Helper functions */
111/*****************************************************************************/
112
113
114
115static int CmpFunc (const void* Key, const void* Elem)
116/* Compare function for bsearch */
117{
118 return strcmp ((const char*) Key, ((const struct StdFuncDesc*) Elem)->Name);
119}
120
121
122
123static long ArrayElementCount (const ArgDesc* Arg)
124/* Check if the type of the given argument is an array. If so, and if the
125** element count is known, return it. In all other cases, return UNSPECIFIED.
126*/
127{
128 long Count;
129
130 if (IsTypeArray (Arg->Type)) {
131 Count = GetElementCount (Arg->Type);
132 if (Count == FLEXIBLE) {
133 /* Treat as unknown */
134 Count = UNSPECIFIED;
135 }
136 } else {
137 Count = UNSPECIFIED;
138 }
139 return Count;
140}
141
142
143
144static void ParseArg (ArgDesc* Arg, Type* Type)
145/* Parse one argument but do not push it onto the stack. Make all fields in
146** Arg valid.
147*/
148{
149 /* We have a prototype, so chars may be pushed as chars */
150 Arg->Flags = CF_FORCECHAR;
151
152 /* Remember the required argument type */
153 Arg->ArgType = Type;
154
155 /* Read the expression we're going to pass to the function */
156 MarkedExprWithCheck (hie1, &Arg->Expr);
157
158 /* Remember the actual argument type */
159 Arg->Type = Arg->Expr.Type;
160
161 /* Convert this expression to the expected type */
162 TypeConversion (&Arg->Expr, Type);
163
164 /* Remember the following code position */
165 GetCodePos (&Arg->Load);
166
167 /* If the value is a constant, set the flag, otherwise load it into the
168 ** primary register.
169 */
170 if (ED_IsConstAbsInt (&Arg->Expr) && ED_CodeRangeIsEmpty (&Arg->Expr)) {
171 /* Remember that we have a constant value */
172 Arg->Flags |= CF_CONST;
173 } else {
174 /* Load into the primary */
175 LoadExpr (CF_NONE, &Arg->Expr);
176 }
177
178 /* Remember the following code position */
179 GetCodePos (&Arg->Push);
180 GetCodePos (&Arg->End);
181
182 /* Use the type of the argument for the push */
183 Arg->Flags |= TypeOf (Arg->Expr.Type);
184}
185
186
187
188void AddCmpCodeIfSizeNot256 (const char* Code, long Size)
189/* Add a line of Assembly code that compares an index register
190** only if it isn't comparing to #<256. (If the next line
191** is "bne", then this will avoid a redundant line.)
192*/
193{
194 if (Size != 256) {
195 AddCodeLine (Code, (unsigned int)Size);
196 }
197}
198
199
200
201/*****************************************************************************/
202/* memcpy */
203/*****************************************************************************/
204
205
206
207static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
208/* Handle the memcpy function */
209{
210 /* Argument types: (void*, const void*, size_t) */
211 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
212 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_VOID|T_QUAL_CONST), TYPE(T_END) };
213 static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
214
215 ArgDesc Arg1, Arg2, Arg3;
216 unsigned ParamSize = 0;
217 unsigned Label;
218 int Offs;
219
220 /* Argument #1 */
221 ParseArg (&Arg1, Arg1Type);
222 g_push (Arg1.Flags, Arg1.Expr.IVal);
223 GetCodePos (&Arg1.End);
224 ParamSize += SizeOf (Arg1Type);
225 ConsumeComma ();
226
227 /* Argument #2 */
228 ParseArg (&Arg2, Arg2Type);
229 g_push (Arg2.Flags, Arg2.Expr.IVal);
230 GetCodePos (&Arg2.End);
231 ParamSize += SizeOf (Arg2Type);
232 ConsumeComma ();
233
234 /* Argument #3. Since memcpy is a fastcall function, we must load the
235 ** arg into the primary if it is not already there. This parameter is
236 ** also ignored for the calculation of the parameter size, since it is
237 ** not passed via the stack.
238 */
239 ParseArg (&Arg3, Arg3Type);
240 if (Arg3.Flags & CF_CONST) {
241 LoadExpr (CF_NONE, &Arg3.Expr);
242 }
243
244 /* Emit the actual function call. This will also cleanup the stack. */
245 g_call (CF_FIXARGC, Func_memcpy, ParamSize);
246
247 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
248
249 /* memcpy has been called with a count argument of zero */
250 Warning ("Call to memcpy has no effect");
251
252 /* Remove all of the generated code but the load of the first
253 ** argument, which is what memcpy returns.
254 */
255 RemoveCode (&Arg1.Push);
256
257 /* Set the function result to the first argument */
258 *Expr = Arg1.Expr;
259
260 /* Bail out, no need for further improvements */
261 goto ExitPoint;
262 }
263
264 if (IS_Get (&InlineStdFuncs)) {
265
266 /* We've generated the complete code for the function now and know the
267 ** types of all parameters. Check for situations where better code can
268 ** be generated. If such a situation is detected, throw away the
269 ** generated, and emit better code.
270 */
271 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
272 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
273 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
274 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
275 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
276
277 int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
278 int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
279
280 /* Drop the generated code */
281 RemoveCode (&Arg1.Expr.Start);
282
283 /* We need a label */
284 Label = GetLocalLabel ();
285
286 /* Generate memcpy code */
287 if (Arg3.Expr.IVal <= 129) {
288
289 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
290 g_defcodelabel (Label);
291 if (Reg2) {
292 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
293 } else {
294 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
295 }
296 if (Reg1) {
297 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
298 } else {
299 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
300 }
301 AddCodeLine ("dey");
302 AddCodeLine ("bpl %s", LocalLabelName (Label));
303
304 } else {
305
306 AddCodeLine ("ldy #$00");
307 g_defcodelabel (Label);
308 if (Reg2) {
309 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
310 } else {
311 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
312 }
313 if (Reg1) {
314 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
315 } else {
316 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
317 }
318 AddCodeLine ("iny");
319 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
320 AddCodeLine ("bne %s", LocalLabelName (Label));
321
322 }
323
324 /* memcpy returns the address, so the result is actually identical
325 ** to the first argument.
326 */
327 *Expr = Arg1.Expr;
328
329 /* Bail out, no need for further processing */
330 goto ExitPoint;
331 }
332
333 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
334 ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
335 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
336 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
337
338 /* It is possible to just use one index register even if the stack
339 ** offset is not zero, by adjusting the offset to the constant
340 ** address accordingly. But we cannot do this if the data in
341 ** question is in the register space or at an absolute address less
342 ** than 256. Register space is zero page, which means that the
343 ** address calculation could overflow in the linker.
344 */
345 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
346 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
347
348 /* Calculate the real stack offset */
349 Offs = ED_GetStackOffs (&Arg1.Expr, 0);
350
351 /* Drop the generated code */
352 RemoveCode (&Arg1.Expr.Start);
353
354 /* We need a label */
355 Label = GetLocalLabel ();
356
357 /* Generate memcpy code */
358 if (Arg3.Expr.IVal <= 129 && !AllowOneIndex) {
359
360 if (Offs == 0) {
361 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
362 g_defcodelabel (Label);
363 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
364 AddCodeLine ("sta (sp),y");
365 AddCodeLine ("dey");
366 AddCodeLine ("bpl %s", LocalLabelName (Label));
367 } else {
368 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
369 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
370 g_defcodelabel (Label);
371 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
372 AddCodeLine ("sta (sp),y");
373 AddCodeLine ("dey");
374 AddCodeLine ("dex");
375 AddCodeLine ("bpl %s", LocalLabelName (Label));
376 }
377
378 } else {
379
380 if (Offs == 0 || AllowOneIndex) {
381 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
382 g_defcodelabel (Label);
383 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
384 AddCodeLine ("sta (sp),y");
385 AddCodeLine ("iny");
386 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
387 AddCodeLine ("bne %s", LocalLabelName (Label));
388 } else {
389 AddCodeLine ("ldx #$00");
390 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
391 g_defcodelabel (Label);
392 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
393 AddCodeLine ("sta (sp),y");
394 AddCodeLine ("iny");
395 AddCodeLine ("inx");
396 AddCmpCodeIfSizeNot256 ("cpx #$%02X", Arg3.Expr.IVal);
397 AddCodeLine ("bne %s", LocalLabelName (Label));
398 }
399
400 }
401
402 /* memcpy returns the address, so the result is actually identical
403 ** to the first argument.
404 */
405 *Expr = Arg1.Expr;
406
407 /* Bail out, no need for further processing */
408 goto ExitPoint;
409 }
410
411 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
412 ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
413 (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 &&
414 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
415
416 /* It is possible to just use one index register even if the stack
417 ** offset is not zero, by adjusting the offset to the constant
418 ** address accordingly. But we cannot do this if the data in
419 ** question is in the register space or at an absolute address less
420 ** than 256. Register space is zero page, which means that the
421 ** address calculation could overflow in the linker.
422 */
423 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
424 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
425
426 /* Calculate the real stack offset */
427 Offs = ED_GetStackOffs (&Arg2.Expr, 0);
428
429 /* Drop the generated code */
430 RemoveCode (&Arg1.Expr.Start);
431
432 /* We need a label */
433 Label = GetLocalLabel ();
434
435 /* Generate memcpy code */
436 if (Arg3.Expr.IVal <= 129 && !AllowOneIndex) {
437
438 if (Offs == 0) {
439 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
440 g_defcodelabel (Label);
441 AddCodeLine ("lda (sp),y");
442 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
443 AddCodeLine ("dey");
444 AddCodeLine ("bpl %s", LocalLabelName (Label));
445 } else {
446 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
447 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
448 g_defcodelabel (Label);
449 AddCodeLine ("lda (sp),y");
450 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
451 AddCodeLine ("dey");
452 AddCodeLine ("dex");
453 AddCodeLine ("bpl %s", LocalLabelName (Label));
454 }
455
456 } else {
457
458 if (Offs == 0 || AllowOneIndex) {
459 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
460 g_defcodelabel (Label);
461 AddCodeLine ("lda (sp),y");
462 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
463 AddCodeLine ("iny");
464 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
465 AddCodeLine ("bne %s", LocalLabelName (Label));
466 } else {
467 AddCodeLine ("ldx #$00");
468 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
469 g_defcodelabel (Label);
470 AddCodeLine ("lda (sp),y");
471 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
472 AddCodeLine ("iny");
473 AddCodeLine ("inx");
474 AddCmpCodeIfSizeNot256 ("cpx #$%02X", Arg3.Expr.IVal);
475 AddCodeLine ("bne %s", LocalLabelName (Label));
476 }
477
478 }
479
480 /* memcpy returns the address, so the result is actually identical
481 ** to the first argument.
482 */
483 *Expr = Arg1.Expr;
484
485 /* Bail out, no need for further processing */
486 goto ExitPoint;
487 }
488
489 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
490 ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
491 (Offs = ED_GetStackOffs (&Arg2.Expr, 0)) == 0) {
492
493 /* Drop the generated code but leave the load of the first argument*/
494 RemoveCode (&Arg1.Push);
495
496 /* We need a label */
497 Label = GetLocalLabel ();
498
499 /* Generate memcpy code */
500 AddCodeLine ("sta ptr1");
501 AddCodeLine ("stx ptr1+1");
502 if (Arg3.Expr.IVal <= 129) {
503 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
504 g_defcodelabel (Label);
505 AddCodeLine ("lda (sp),y");
506 AddCodeLine ("sta (ptr1),y");
507 AddCodeLine ("dey");
508 AddCodeLine ("bpl %s", LocalLabelName (Label));
509 } else {
510 AddCodeLine ("ldy #$00");
511 g_defcodelabel (Label);
512 AddCodeLine ("lda (sp),y");
513 AddCodeLine ("sta (ptr1),y");
514 AddCodeLine ("iny");
515 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
516 AddCodeLine ("bne %s", LocalLabelName (Label));
517 }
518
519 /* Reload result - X hasn't changed by the code above */
520 AddCodeLine ("lda ptr1");
521
522 /* The function result is an rvalue in the primary register */
523 ED_MakeRValExpr (Expr);
524 Expr->Type = GetFuncReturn (Expr->Type);
525
526 /* Bail out, no need for further processing */
527 goto ExitPoint;
528 }
529 }
530
531 /* The function result is an rvalue in the primary register */
532 ED_MakeRValExpr (Expr);
533 Expr->Type = GetFuncReturn (Expr->Type);
534
535ExitPoint:
536 /* We expect the closing brace */
537 ConsumeRParen ();
538}
539
540
541
542/*****************************************************************************/
543/* memset */
544/*****************************************************************************/
545
546
547
548static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
549/* Handle the memset function */
550{
551 /* Argument types: (void*, int, size_t) */
552 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
553 static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) };
554 static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
555
556 ArgDesc Arg1, Arg2, Arg3;
557 int MemSet = 1; /* Use real memset if true */
558 unsigned ParamSize = 0;
559 unsigned Label;
560
561 /* Argument #1 */
562 ParseArg (&Arg1, Arg1Type);
563 g_push (Arg1.Flags, Arg1.Expr.IVal);
564 GetCodePos (&Arg1.End);
565 ParamSize += SizeOf (Arg1Type);
566 ConsumeComma ();
567
568 /* Argument #2. This argument is special in that we will call another
569 ** function if it is a constant zero.
570 */
571 ParseArg (&Arg2, Arg2Type);
572 if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
573 /* Don't call memset, call bzero instead */
574 MemSet = 0;
575 } else {
576 /* Push the argument */
577 g_push (Arg2.Flags, Arg2.Expr.IVal);
578 GetCodePos (&Arg2.End);
579 ParamSize += SizeOf (Arg2Type);
580 }
581 ConsumeComma ();
582
583 /* Argument #3. Since memset is a fastcall function, we must load the
584 ** arg into the primary if it is not already there. This parameter is
585 ** also ignored for the calculation of the parameter size, since it is
586 ** not passed via the stack.
587 */
588 ParseArg (&Arg3, Arg3Type);
589 if (Arg3.Flags & CF_CONST) {
590 LoadExpr (CF_NONE, &Arg3.Expr);
591 }
592
593 /* Emit the actual function call. This will also cleanup the stack. */
594 g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
595
596 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
597
598 /* memset has been called with a count argument of zero */
599 Warning ("Call to memset has no effect");
600
601 /* Remove all of the generated code but the load of the first
602 ** argument, which is what memset returns.
603 */
604 RemoveCode (&Arg1.Push);
605
606 /* Set the function result to the first argument */
607 *Expr = Arg1.Expr;
608
609 /* Bail out, no need for further improvements */
610 goto ExitPoint;
611 }
612
613 if (IS_Get (&InlineStdFuncs)) {
614
615 /* We've generated the complete code for the function now and know the
616 ** types of all parameters. Check for situations where better code can
617 ** be generated. If such a situation is detected, throw away the
618 ** generated, and emit better code.
619 ** Note: Lots of improvements would be possible here, but I will
620 ** concentrate on the most common case: memset with arguments 2 and 3
621 ** being constant numerical values. Some checks have shown that this
622 ** covers nearly 90% of all memset calls.
623 */
624 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
625 ED_IsConstAbsInt (&Arg2.Expr) &&
626 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
627 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
628
629 int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
630
631 /* Drop the generated code */
632 RemoveCode (&Arg1.Expr.Start);
633
634 /* We need a label */
635 Label = GetLocalLabel ();
636
637 /* Generate memset code */
638 if (Arg3.Expr.IVal <= 129) {
639
640 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
641 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
642 g_defcodelabel (Label);
643 if (Reg) {
644 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
645 } else {
646 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
647 }
648 AddCodeLine ("dey");
649 AddCodeLine ("bpl %s", LocalLabelName (Label));
650
651 } else {
652
653 AddCodeLine ("ldy #$00");
654 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
655 g_defcodelabel (Label);
656 if (Reg) {
657 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
658 } else {
659 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
660 }
661 AddCodeLine ("iny");
662 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
663 AddCodeLine ("bne %s", LocalLabelName (Label));
664
665 }
666
667 /* memset returns the address, so the result is actually identical
668 ** to the first argument.
669 */
670 *Expr = Arg1.Expr;
671
672 /* Bail out, no need for further processing */
673 goto ExitPoint;
674 }
675
676 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
677 ED_IsConstAbsInt (&Arg2.Expr) &&
678 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
679 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
680
681 /* Calculate the real stack offset */
682 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
683
684 /* Drop the generated code */
685 RemoveCode (&Arg1.Expr.Start);
686
687 /* We need a label */
688 Label = GetLocalLabel ();
689
690 /* Generate memset code */
691 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
692 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
693 g_defcodelabel (Label);
694 AddCodeLine ("sta (sp),y");
695 AddCodeLine ("iny");
696 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
697 AddCodeLine ("bne %s", LocalLabelName (Label));
698
699 /* memset returns the address, so the result is actually identical
700 ** to the first argument.
701 */
702 *Expr = Arg1.Expr;
703
704 /* Bail out, no need for further processing */
705 goto ExitPoint;
706 }
707
708 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
709 ED_IsConstAbsInt (&Arg2.Expr) &&
710 (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) {
711
712 /* Remove all of the generated code but the load of the first
713 ** argument.
714 */
715 RemoveCode (&Arg1.Push);
716
717 /* We need a label */
718 Label = GetLocalLabel ();
719
720 /* Generate code */
721 AddCodeLine ("sta ptr1");
722 AddCodeLine ("stx ptr1+1");
723 if (Arg3.Expr.IVal <= 129) {
724 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
725 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
726 g_defcodelabel (Label);
727 AddCodeLine ("sta (ptr1),y");
728 AddCodeLine ("dey");
729 AddCodeLine ("bpl %s", LocalLabelName (Label));
730 } else {
731 AddCodeLine ("ldy #$00");
732 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
733 g_defcodelabel (Label);
734 AddCodeLine ("sta (ptr1),y");
735 AddCodeLine ("iny");
736 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
737 AddCodeLine ("bne %s", LocalLabelName (Label));
738 }
739
740 /* Load the function result pointer into a/x (x is still valid). This
741 ** code will get removed by the optimizer if it is not used later.
742 */
743 AddCodeLine ("lda ptr1");
744
745 /* The function result is an rvalue in the primary register */
746 ED_MakeRValExpr (Expr);
747 Expr->Type = GetFuncReturn (Expr->Type);
748
749 /* Bail out, no need for further processing */
750 goto ExitPoint;
751 }
752 }
753
754 /* The function result is an rvalue in the primary register */
755 ED_MakeRValExpr (Expr);
756 Expr->Type = GetFuncReturn (Expr->Type);
757
758ExitPoint:
759 /* We expect the closing brace */
760 ConsumeRParen ();
761}
762
763
764
765/*****************************************************************************/
766/* strcmp */
767/*****************************************************************************/
768
769
770
771static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
772/* Handle the strcmp function */
773{
774 /* Argument types: (const char*, const char*) */
775 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
776 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
777
778 ArgDesc Arg1, Arg2;
779 unsigned ParamSize = 0;
780 long ECount1;
781 long ECount2;
782 int IsArray;
783 int Offs;
784
785 /* Setup the argument type string */
786 Arg1Type[1].C = GetDefaultChar () | T_QUAL_CONST;
787 Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
788
789 /* Argument #1 */
790 ParseArg (&Arg1, Arg1Type);
791 g_push (Arg1.Flags, Arg1.Expr.IVal);
792 ParamSize += SizeOf (Arg1Type);
793 ConsumeComma ();
794
795 /* Argument #2. */
796 ParseArg (&Arg2, Arg2Type);
797
798 /* Since strcmp is a fastcall function, we must load the
799 ** arg into the primary if it is not already there. This parameter is
800 ** also ignored for the calculation of the parameter size, since it is
801 ** not passed via the stack.
802 */
803 if (Arg2.Flags & CF_CONST) {
804 LoadExpr (CF_NONE, &Arg2.Expr);
805 }
806
807 /* Emit the actual function call. This will also cleanup the stack. */
808 g_call (CF_FIXARGC, Func_strcmp, ParamSize);
809
810 /* Get the element counts of the arguments. Then get the larger of the
811 ** two into ECount1. This removes FLEXIBLE and UNSPECIFIED automatically
812 */
813 ECount1 = ArrayElementCount (&Arg1);
814 ECount2 = ArrayElementCount (&Arg2);
815 if (ECount2 > ECount1) {
816 ECount1 = ECount2;
817 }
818
819 if (IS_Get (&InlineStdFuncs)) {
820
821 /* If the second argument is the empty string literal, we can generate
822 ** more efficient code.
823 */
824 if (ED_IsLocLiteral (&Arg2.Expr) &&
825 IS_Get (&WritableStrings) == 0 &&
826 GetLiteralSize (Arg2.Expr.LVal) == 1 &&
827 GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') {
828
829 /* Drop the generated code so we have the first argument in the
830 ** primary
831 */
832 RemoveCode (&Arg1.Push);
833
834 /* We don't need the literal any longer */
835 ReleaseLiteral (Arg2.Expr.LVal);
836
837 /* We do now have Arg1 in the primary. Load the first character from
838 ** this string and cast to int. This is the function result.
839 */
840 IsArray = IsTypeArray (Arg1.Type) && ED_IsRVal (&Arg1.Expr);
841 if (IsArray && ED_IsLocStack (&Arg1.Expr) &&
842 (Offs = ED_GetStackOffs (&Arg1.Expr, 0) < 256)) {
843 /* Drop the generated code */
844 RemoveCode (&Arg1.Load);
845
846 /* Generate code */
847 AddCodeLine ("ldy #$%02X", Offs);
848 AddCodeLine ("ldx #$00");
849 AddCodeLine ("lda (sp),y");
850 } else if (IsArray && ED_IsLocConst (&Arg1.Expr)) {
851 /* Drop the generated code */
852 RemoveCode (&Arg1.Load);
853
854 /* Generate code */
855 AddCodeLine ("ldx #$00");
856 AddCodeLine ("lda %s", ED_GetLabelName (&Arg1.Expr, 0));
857 } else {
858 /* Drop part of the generated code so we have the first argument
859 ** in the primary
860 */
861 RemoveCode (&Arg1.Push);
862
863 /* Fetch the first char */
864 g_getind (CF_CHAR | CF_UNSIGNED, 0);
865 }
866
867 } else if ((IS_Get (&CodeSizeFactor) >= 165) &&
868 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
869 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
870 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
871 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
872 (IS_Get (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) {
873
874 unsigned Entry, Loop, Fin; /* Labels */
875 const char* Load;
876 const char* Compare;
877
878 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
879 Load = "lda (%s),y";
880 } else {
881 Load = "lda %s,y";
882 }
883 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
884 Compare = "cmp (%s),y";
885 } else {
886 Compare = "cmp %s,y";
887 }
888
889 /* Drop the generated code */
890 RemoveCode (&Arg1.Expr.Start);
891
892 /* We need labels */
893 Entry = GetLocalLabel ();
894 Loop = GetLocalLabel ();
895 Fin = GetLocalLabel ();
896
897 /* Generate strcmp code */
898 AddCodeLine ("ldy #$00");
899 AddCodeLine ("beq %s", LocalLabelName (Entry));
900 g_defcodelabel (Loop);
901 AddCodeLine ("tax");
902 AddCodeLine ("beq %s", LocalLabelName (Fin));
903 AddCodeLine ("iny");
904 g_defcodelabel (Entry);
905 AddCodeLine (Load, ED_GetLabelName (&Arg1.Expr, 0));
906 AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
907 AddCodeLine ("beq %s", LocalLabelName (Loop));
908 AddCodeLine ("ldx #$01");
909 AddCodeLine ("bcs %s", LocalLabelName (Fin));
910 AddCodeLine ("ldx #$FF");
911 g_defcodelabel (Fin);
912
913 } else if ((IS_Get (&CodeSizeFactor) > 190) &&
914 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
915 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
916 (IS_Get (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) {
917
918 unsigned Entry, Loop, Fin; /* Labels */
919 const char* Compare;
920
921 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
922 Compare = "cmp (%s),y";
923 } else {
924 Compare = "cmp %s,y";
925 }
926
927 /* Drop the generated code */
928 RemoveCode (&Arg1.Push);
929
930 /* We need labels */
931 Entry = GetLocalLabel ();
932 Loop = GetLocalLabel ();
933 Fin = GetLocalLabel ();
934
935 /* Store Arg1 into ptr1 */
936 AddCodeLine ("sta ptr1");
937 AddCodeLine ("stx ptr1+1");
938
939 /* Generate strcmp code */
940 AddCodeLine ("ldy #$00");
941 AddCodeLine ("beq %s", LocalLabelName (Entry));
942 g_defcodelabel (Loop);
943 AddCodeLine ("tax");
944 AddCodeLine ("beq %s", LocalLabelName (Fin));
945 AddCodeLine ("iny");
946 g_defcodelabel (Entry);
947 AddCodeLine ("lda (ptr1),y");
948 AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
949 AddCodeLine ("beq %s", LocalLabelName (Loop));
950 AddCodeLine ("ldx #$01");
951 AddCodeLine ("bcs %s", LocalLabelName (Fin));
952 AddCodeLine ("ldx #$FF");
953 g_defcodelabel (Fin);
954 }
955 }
956
957 /* The function result is an rvalue in the primary register */
958 ED_MakeRValExpr (Expr);
959 Expr->Type = GetFuncReturn (Expr->Type);
960
961 /* We expect the closing brace */
962 ConsumeRParen ();
963}
964
965
966
967/*****************************************************************************/
968/* strcpy */
969/*****************************************************************************/
970
971
972
973static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
974/* Handle the strcpy function */
975{
976 /* Argument types: (char*, const char*) */
977 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) };
978 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
979
980 ArgDesc Arg1, Arg2;
981 unsigned ParamSize = 0;
982 long ECount;
983 unsigned L1;
984
985 /* Setup the argument type string */
986 Arg1Type[1].C = GetDefaultChar ();
987 Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
988
989 /* Argument #1 */
990 ParseArg (&Arg1, Arg1Type);
991 g_push (Arg1.Flags, Arg1.Expr.IVal);
992 GetCodePos (&Arg1.End);
993 ParamSize += SizeOf (Arg1Type);
994 ConsumeComma ();
995
996 /* Argument #2. Since strcpy is a fastcall function, we must load the
997 ** arg into the primary if it is not already there. This parameter is
998 ** also ignored for the calculation of the parameter size, since it is
999 ** not passed via the stack.
1000 */
1001 ParseArg (&Arg2, Arg2Type);
1002 if (Arg2.Flags & CF_CONST) {
1003 LoadExpr (CF_NONE, &Arg2.Expr);
1004 }
1005
1006 /* Emit the actual function call. This will also cleanup the stack. */
1007 g_call (CF_FIXARGC, Func_strcpy, ParamSize);
1008
1009 /* Get the element count of argument 1 if it is an array */
1010 ECount = ArrayElementCount (&Arg1);
1011
1012 if (IS_Get (&InlineStdFuncs)) {
1013
1014 /* We've generated the complete code for the function now and know the
1015 ** types of all parameters. Check for situations where better code can
1016 ** be generated. If such a situation is detected, throw away the
1017 ** generated, and emit better code.
1018 */
1019 if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
1020 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
1021 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
1022 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
1023 (IS_Get (&EagerlyInlineFuncs) ||
1024 (ECount != UNSPECIFIED && ECount < 256))) {
1025
1026 const char* Load;
1027 const char* Store;
1028 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
1029 Load = "lda (%s),y";
1030 } else {
1031 Load = "lda %s,y";
1032 }
1033 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
1034 Store = "sta (%s),y";
1035 } else {
1036 Store = "sta %s,y";
1037 }
1038
1039 /* Drop the generated code */
1040 RemoveCode (&Arg1.Expr.Start);
1041
1042 /* We need labels */
1043 L1 = GetLocalLabel ();
1044
1045 /* Generate strcpy code */
1046 AddCodeLine ("ldy #$FF");
1047 g_defcodelabel (L1);
1048 AddCodeLine ("iny");
1049 AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0));
1050 AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0));
1051 AddCodeLine ("bne %s", LocalLabelName (L1));
1052
1053 /* strcpy returns argument #1 */
1054 *Expr = Arg1.Expr;
1055
1056 /* Bail out, no need for further processing */
1057 goto ExitPoint;
1058 }
1059
1060 if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
1061 StackPtr >= -255 &&
1062 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
1063
1064 /* It is possible to just use one index register even if the stack
1065 ** offset is not zero, by adjusting the offset to the constant
1066 ** address accordingly. But we cannot do this if the data in
1067 ** question is in the register space or at an absolute address less
1068 ** than 256. Register space is zero page, which means that the
1069 ** address calculation could overflow in the linker.
1070 */
1071 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
1072 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
1073
1074 /* Calculate the real stack offset */
1075 int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
1076
1077 /* Drop the generated code */
1078 RemoveCode (&Arg1.Expr.Start);
1079
1080 /* We need labels */
1081 L1 = GetLocalLabel ();
1082
1083 /* Generate strcpy code */
1084 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
1085 if (Offs == 0 || AllowOneIndex) {
1086 g_defcodelabel (L1);
1087 AddCodeLine ("iny");
1088 AddCodeLine ("lda (sp),y");
1089 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
1090 } else {
1091 AddCodeLine ("ldx #$FF");
1092 g_defcodelabel (L1);
1093 AddCodeLine ("iny");
1094 AddCodeLine ("inx");
1095 AddCodeLine ("lda (sp),y");
1096 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
1097 }
1098 AddCodeLine ("bne %s", LocalLabelName (L1));
1099
1100 /* strcpy returns argument #1 */
1101 *Expr = Arg1.Expr;
1102
1103 /* Bail out, no need for further processing */
1104 goto ExitPoint;
1105 }
1106
1107 if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
1108 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
1109 StackPtr >= -255) {
1110
1111 /* It is possible to just use one index register even if the stack
1112 ** offset is not zero, by adjusting the offset to the constant
1113 ** address accordingly. But we cannot do this if the data in
1114 ** question is in the register space or at an absolute address less
1115 ** than 256. Register space is zero page, which means that the
1116 ** address calculation could overflow in the linker.
1117 */
1118 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
1119 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
1120
1121 /* Calculate the real stack offset */
1122 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
1123
1124 /* Drop the generated code */
1125 RemoveCode (&Arg1.Expr.Start);
1126
1127 /* We need labels */
1128 L1 = GetLocalLabel ();
1129
1130 /* Generate strcpy code */
1131 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
1132 if (Offs == 0 || AllowOneIndex) {
1133 g_defcodelabel (L1);
1134 AddCodeLine ("iny");
1135 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
1136 AddCodeLine ("sta (sp),y");
1137 } else {
1138 AddCodeLine ("ldx #$FF");
1139 g_defcodelabel (L1);
1140 AddCodeLine ("iny");
1141 AddCodeLine ("inx");
1142 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
1143 AddCodeLine ("sta (sp),y");
1144 }
1145 AddCodeLine ("bne %s", LocalLabelName (L1));
1146
1147 /* strcpy returns argument #1 */
1148 *Expr = Arg1.Expr;
1149
1150 /* Bail out, no need for further processing */
1151 goto ExitPoint;
1152 }
1153 }
1154
1155 /* The function result is an rvalue in the primary register */
1156 ED_MakeRValExpr (Expr);
1157 Expr->Type = GetFuncReturn (Expr->Type);
1158
1159ExitPoint:
1160 /* We expect the closing brace */
1161 ConsumeRParen ();
1162}
1163
1164
1165
1166/*****************************************************************************/
1167/* strlen */
1168/*****************************************************************************/
1169
1170
1171
1172static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
1173/* Handle the strlen function */
1174{
1175 static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
1176 ExprDesc Arg;
1177 int IsArray;
1178 int IsPtr;
1179 int IsByteIndex;
1180 long ECount;
1181 unsigned L;
1182
1183 /* Setup the argument type string */
1184 ArgType[1].C = GetDefaultChar () | T_QUAL_CONST;
1185
1186 /* Evaluate the parameter */
1187 hie1 (&Arg);
1188
1189 /* Check if the argument is an array. If so, remember the element count.
1190 ** Otherwise set the element count to undefined.
1191 */
1192 IsArray = IsTypeArray (Arg.Type);
1193 if (IsArray) {
1194 ECount = GetElementCount (Arg.Type);
1195 if (ECount == FLEXIBLE) {
1196 /* Treat as unknown */
1197 ECount = UNSPECIFIED;
1198 }
1199 IsPtr = 0;
1200 } else {
1201 ECount = UNSPECIFIED;
1202 IsPtr = IsTypePtr (Arg.Type);
1203 }
1204
1205 /* Check if the elements of an array can be addressed by a byte sized
1206 ** index. This is true if the size of the array is known and less than
1207 ** 256.
1208 */
1209 IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);
1210
1211 /* Do type conversion */
1212 TypeConversion (&Arg, ArgType);
1213
1214 if (IS_Get (&Optimize)) {
1215
1216 /* If the expression is a literal, and if string literals are read
1217 ** only, we can calculate the length of the string and remove it
1218 ** from the literal pool. Otherwise we have to calculate the length
1219 ** at runtime.
1220 */
1221 if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
1222
1223 /* Constant string literal */
1224 ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t);
1225
1226 /* We don't need the literal any longer */
1227 ReleaseLiteral (Arg.LVal);
1228
1229 /* Bail out, no need for further improvements */
1230 goto ExitPoint;
1231 }
1232 }
1233
1234 if (IS_Get (&InlineStdFuncs)) {
1235
1236 /* We will inline strlen for arrays with constant addresses, if either
1237 ** requested on the command line, or the array is smaller than 256,
1238 ** so the inlining is considered safe.
1239 */
1240 if (ED_IsLocConst (&Arg) && IsArray &&
1241 (IS_Get (&EagerlyInlineFuncs) || IsByteIndex)) {
1242
1243 /* Generate the strlen code */
1244 L = GetLocalLabel ();
1245 AddCodeLine ("ldy #$FF");
1246 g_defcodelabel (L);
1247 AddCodeLine ("iny");
1248 AddCodeLine ("ldx %s,y", ED_GetLabelName (&Arg, 0));
1249 AddCodeLine ("bne %s", LocalLabelName (L));
1250 AddCodeLine ("tya");
1251
1252 /* The function result is an rvalue in the primary register */
1253 ED_MakeRValExpr (Expr);
1254 Expr->Type = type_size_t;
1255
1256 /* Bail out, no need for further processing */
1257 goto ExitPoint;
1258 }
1259
1260 /* We will inline strlen for arrays on the stack, if the array is
1261 ** completely within the reach of a byte sized index register.
1262 */
1263 if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
1264 (Arg.IVal - StackPtr) + ECount < 256) {
1265
1266 /* Calculate the true stack offset */
1267 int Offs = ED_GetStackOffs (&Arg, 0);
1268
1269 /* Generate the strlen code */
1270 L = GetLocalLabel ();
1271 AddCodeLine ("ldx #$FF");
1272 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
1273 g_defcodelabel (L);
1274 AddCodeLine ("inx");
1275 AddCodeLine ("iny");
1276 AddCodeLine ("lda (sp),y");
1277 AddCodeLine ("bne %s", LocalLabelName (L));
1278 AddCodeLine ("txa");
1279 AddCodeLine ("ldx #$00");
1280
1281 /* The function result is an rvalue in the primary register */
1282 ED_MakeRValExpr (Expr);
1283 Expr->Type = type_size_t;
1284
1285 /* Bail out, no need for further processing */
1286 goto ExitPoint;
1287 }
1288
1289 /* strlen for a string that is pointed to by a register variable will only
1290 ** get inlined if requested on the command line, since we cannot know how
1291 ** big the buffer actually is, so inlining is not always safe.
1292 */
1293 if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
1294 IS_Get (&EagerlyInlineFuncs)) {
1295
1296 /* Generate the strlen code */
1297 L = GetLocalLabel ();
1298 AddCodeLine ("ldy #$FF");
1299 g_defcodelabel (L);
1300 AddCodeLine ("iny");
1301 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg, 0));
1302 AddCodeLine ("bne %s", LocalLabelName (L));
1303 AddCodeLine ("tax");
1304 AddCodeLine ("tya");
1305
1306 /* The function result is an rvalue in the primary register */
1307 ED_MakeRValExpr (Expr);
1308 Expr->Type = type_size_t;
1309
1310 /* Bail out, no need for further processing */
1311 goto ExitPoint;
1312 }
1313
1314 /* Last check: We will inline a generic strlen routine if inlining was
1315 ** requested on the command line, and the code size factor is more than
1316 ** 400 (code is 13 bytes vs. 3 for a jsr call).
1317 */
1318 if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&EagerlyInlineFuncs)) {
1319
1320 /* Load the expression into the primary */
1321 LoadExpr (CF_NONE, &Arg);
1322
1323 /* Inline the function */
1324 L = GetLocalLabel ();
1325 AddCodeLine ("sta ptr1");
1326 AddCodeLine ("stx ptr1+1");
1327 AddCodeLine ("ldy #$FF");
1328 g_defcodelabel (L);
1329 AddCodeLine ("iny");
1330 AddCodeLine ("lda (ptr1),y");
1331 AddCodeLine ("bne %s", LocalLabelName (L));
1332 AddCodeLine ("tax");
1333 AddCodeLine ("tya");
1334
1335 /* The function result is an rvalue in the primary register */
1336 ED_MakeRValExpr (Expr);
1337 Expr->Type = type_size_t;
1338
1339 /* Bail out, no need for further processing */
1340 goto ExitPoint;
1341 }
1342 }
1343
1344 /* Load the expression into the primary */
1345 LoadExpr (CF_NONE, &Arg);
1346
1347 /* Call the strlen function */
1348 AddCodeLine ("jsr _%s", Func_strlen);
1349
1350 /* The function result is an rvalue in the primary register */
1351 ED_MakeRValExpr (Expr);
1352 Expr->Type = type_size_t;
1353
1354ExitPoint:
1355 /* We expect the closing brace */
1356 ConsumeRParen ();
1357}
1358
1359
1360
1361/*****************************************************************************/
1362/* Code */
1363/*****************************************************************************/
1364
1365
1366
1367int FindStdFunc (const char* Name)
1368/* Determine if the given function is a known standard function that may be
1369** called in a special way. If so, return the index, otherwise return -1.
1370*/
1371{
1372 /* Look into the table for known names */
1373 struct StdFuncDesc* D =
1374 bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
1375
1376 /* Return the function index or -1 */
1377 if (D == 0) {
1378 return -1;
1379 } else {
1380 return D - StdFuncs;
1381 }
1382}
1383
1384
1385
1386void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
1387/* Generate code for a known standard function. */
1388{
1389 struct StdFuncDesc* D;
1390
1391 /* Get a pointer to the table entry */
1392 CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
1393 D = StdFuncs + Index;
1394
1395 /* Call the handler function */
1396 D->Handler (F, lval);
1397}
1398