root/trunk/src/arrayop.c

Revision 753, 19.9 kB (checked in by walter, 2 years ago)

bugzilla 5180 ICE(arrayop.c) in-place array operation on incompatible types

  • Property svn:eol-style set to native
Line 
1 // Copyright (c) 1999-2010 by Digital Mars
2 // All Rights Reserved
3 // written by Walter Bright
4 // http://www.digitalmars.com
5 // License for redistribution is by either the Artistic License
6 // in artistic.txt, or the GNU General Public License in gnu.txt.
7 // See the included readme.txt for details.
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <assert.h>
12
13 #include "rmem.h"
14
15 #include "stringtable.h"
16
17 #include "expression.h"
18 #include "statement.h"
19 #include "mtype.h"
20 #include "declaration.h"
21 #include "scope.h"
22 #include "id.h"
23 #include "module.h"
24 #include "init.h"
25
26 extern int binary(const char *p , const char **tab, int high);
27
28 /**************************************
29  * Hash table of array op functions already generated or known about.
30  */
31
32 StringTable arrayfuncs;
33
34 /**********************************************
35  * Check that there are no uses of arrays without [].
36  */
37 bool isArrayOpValid(Expression *e)
38 {
39     if (e->op == TOKslice)
40         return true;
41     Type *tb = e->type->toBasetype();
42
43     if ( (tb->ty == Tarray) || (tb->ty == Tsarray) )
44     {
45         switch (e->op)
46         {
47             case TOKadd:
48             case TOKmin:
49             case TOKmul:
50             case TOKdiv:
51             case TOKmod:
52             case TOKxor:
53             case TOKand:
54             case TOKor:
55             case TOKassign:
56             case TOKaddass:
57             case TOKminass:
58             case TOKmulass:
59             case TOKdivass:
60             case TOKmodass:
61             case TOKxorass:
62             case TOKandass:
63             case TOKorass:
64 #if DMDV2
65             case TOKpow:
66             case TOKpowass:
67 #endif
68                  return isArrayOpValid(((BinExp *)e)->e1) && isArrayOpValid(((BinExp *)e)->e2);
69
70             case TOKcall:
71                  return false; // TODO: Decide if [] is required after arrayop calls.
72
73             case TOKneg:
74             case TOKtilde:
75                  return isArrayOpValid(((UnaExp *)e)->e1);
76
77             default:
78                 return false;
79         }
80     }
81     return true;
82 }
83
84 /***********************************
85  * Construct the array operation expression.
86  */
87
88 Expression *BinExp::arrayOp(Scope *sc)
89 {
90     //printf("BinExp::arrayOp() %s\n", toChars());
91
92     Type *tb = type->toBasetype();
93     assert(tb->ty == Tarray || tb->ty == Tsarray);
94     if (tb->nextOf()->toBasetype()->ty == Tvoid)
95     {
96         error("Cannot perform array operations on void[] arrays");
97         return new ErrorExp();
98     }
99
100     if (!isArrayOpValid(e2))
101     {
102         e2->error("invalid array operation %s (did you forget a [] ?)", toChars());
103         return new ErrorExp();
104     }
105
106     Expressions *arguments = new Expressions();
107
108     /* The expression to generate an array operation for is mangled
109      * into a name to use as the array operation function name.
110      * Mangle in the operands and operators in RPN order, and type.
111      */
112     OutBuffer buf;
113     buf.writestring("_array");
114     buildArrayIdent(&buf, arguments);
115     buf.writeByte('_');
116
117     /* Append deco of array element type
118      */
119 #if DMDV2
120     buf.writestring(type->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco);
121 #else
122     buf.writestring(type->toBasetype()->nextOf()->toBasetype()->deco);
123 #endif
124
125     size_t namelen = buf.offset;
126     buf.writeByte(0);
127     char *name = (char *)buf.extractData();
128
129     /* Look up name in hash table
130      */
131     StringValue *sv = arrayfuncs.update(name, namelen);
132     FuncDeclaration *fd = (FuncDeclaration *)sv->ptrvalue;
133     if (!fd)
134     {
135         /* Some of the array op functions are written as library functions,
136          * presumably to optimize them with special CPU vector instructions.
137          * List those library functions here, in alpha order.
138          */
139         static const char *libArrayopFuncs[] =
140         {
141             "_arrayExpSliceAddass_a",
142             "_arrayExpSliceAddass_d",           // T[]+=T
143             "_arrayExpSliceAddass_f",           // T[]+=T
144             "_arrayExpSliceAddass_g",
145             "_arrayExpSliceAddass_h",
146             "_arrayExpSliceAddass_i",
147             "_arrayExpSliceAddass_k",
148             "_arrayExpSliceAddass_s",
149             "_arrayExpSliceAddass_t",
150             "_arrayExpSliceAddass_u",
151             "_arrayExpSliceAddass_w",
152
153             "_arrayExpSliceDivass_d",           // T[]/=T
154             "_arrayExpSliceDivass_f",           // T[]/=T
155
156             "_arrayExpSliceMinSliceAssign_a",
157             "_arrayExpSliceMinSliceAssign_d",   // T[]=T-T[]
158             "_arrayExpSliceMinSliceAssign_f",   // T[]=T-T[]
159             "_arrayExpSliceMinSliceAssign_g",
160             "_arrayExpSliceMinSliceAssign_h",
161             "_arrayExpSliceMinSliceAssign_i",
162             "_arrayExpSliceMinSliceAssign_k",
163             "_arrayExpSliceMinSliceAssign_s",
164             "_arrayExpSliceMinSliceAssign_t",
165             "_arrayExpSliceMinSliceAssign_u",
166             "_arrayExpSliceMinSliceAssign_w",
167
168             "_arrayExpSliceMinass_a",
169             "_arrayExpSliceMinass_d",           // T[]-=T
170             "_arrayExpSliceMinass_f",           // T[]-=T
171             "_arrayExpSliceMinass_g",
172             "_arrayExpSliceMinass_h",
173             "_arrayExpSliceMinass_i",
174             "_arrayExpSliceMinass_k",
175             "_arrayExpSliceMinass_s",
176             "_arrayExpSliceMinass_t",
177             "_arrayExpSliceMinass_u",
178             "_arrayExpSliceMinass_w",
179
180             "_arrayExpSliceMulass_d",           // T[]*=T
181             "_arrayExpSliceMulass_f",           // T[]*=T
182             "_arrayExpSliceMulass_i",
183             "_arrayExpSliceMulass_k",
184             "_arrayExpSliceMulass_s",
185             "_arrayExpSliceMulass_t",
186             "_arrayExpSliceMulass_u",
187             "_arrayExpSliceMulass_w",
188
189             "_arraySliceExpAddSliceAssign_a",
190             "_arraySliceExpAddSliceAssign_d",   // T[]=T[]+T
191             "_arraySliceExpAddSliceAssign_f",   // T[]=T[]+T
192             "_arraySliceExpAddSliceAssign_g",
193             "_arraySliceExpAddSliceAssign_h",
194             "_arraySliceExpAddSliceAssign_i",
195             "_arraySliceExpAddSliceAssign_k",
196             "_arraySliceExpAddSliceAssign_s",
197             "_arraySliceExpAddSliceAssign_t",
198             "_arraySliceExpAddSliceAssign_u",
199             "_arraySliceExpAddSliceAssign_w",
200
201             "_arraySliceExpDivSliceAssign_d",   // T[]=T[]/T
202             "_arraySliceExpDivSliceAssign_f",   // T[]=T[]/T
203
204             "_arraySliceExpMinSliceAssign_a",
205             "_arraySliceExpMinSliceAssign_d",   // T[]=T[]-T
206             "_arraySliceExpMinSliceAssign_f",   // T[]=T[]-T
207             "_arraySliceExpMinSliceAssign_g",
208             "_arraySliceExpMinSliceAssign_h",
209             "_arraySliceExpMinSliceAssign_i",
210             "_arraySliceExpMinSliceAssign_k",
211             "_arraySliceExpMinSliceAssign_s",
212             "_arraySliceExpMinSliceAssign_t",
213             "_arraySliceExpMinSliceAssign_u",
214             "_arraySliceExpMinSliceAssign_w",
215
216             "_arraySliceExpMulSliceAddass_d",   // T[] += T[]*T
217             "_arraySliceExpMulSliceAddass_f",
218             "_arraySliceExpMulSliceAddass_r",
219
220             "_arraySliceExpMulSliceAssign_d",   // T[]=T[]*T
221             "_arraySliceExpMulSliceAssign_f",   // T[]=T[]*T
222             "_arraySliceExpMulSliceAssign_i",
223             "_arraySliceExpMulSliceAssign_k",
224             "_arraySliceExpMulSliceAssign_s",
225             "_arraySliceExpMulSliceAssign_t",
226             "_arraySliceExpMulSliceAssign_u",
227             "_arraySliceExpMulSliceAssign_w",
228
229             "_arraySliceExpMulSliceMinass_d",   // T[] -= T[]*T
230             "_arraySliceExpMulSliceMinass_f",
231             "_arraySliceExpMulSliceMinass_r",
232
233             "_arraySliceSliceAddSliceAssign_a",
234             "_arraySliceSliceAddSliceAssign_d", // T[]=T[]+T[]
235             "_arraySliceSliceAddSliceAssign_f", // T[]=T[]+T[]
236             "_arraySliceSliceAddSliceAssign_g",
237             "_arraySliceSliceAddSliceAssign_h",
238             "_arraySliceSliceAddSliceAssign_i",
239             "_arraySliceSliceAddSliceAssign_k",
240             "_arraySliceSliceAddSliceAssign_r", // T[]=T[]+T[]
241             "_arraySliceSliceAddSliceAssign_s",
242             "_arraySliceSliceAddSliceAssign_t",
243             "_arraySliceSliceAddSliceAssign_u",
244             "_arraySliceSliceAddSliceAssign_w",
245
246             "_arraySliceSliceAddass_a",
247             "_arraySliceSliceAddass_d",         // T[]+=T[]
248             "_arraySliceSliceAddass_f",         // T[]+=T[]
249             "_arraySliceSliceAddass_g",
250             "_arraySliceSliceAddass_h",
251             "_arraySliceSliceAddass_i",
252             "_arraySliceSliceAddass_k",
253             "_arraySliceSliceAddass_s",
254             "_arraySliceSliceAddass_t",
255             "_arraySliceSliceAddass_u",
256             "_arraySliceSliceAddass_w",
257
258             "_arraySliceSliceMinSliceAssign_a",
259             "_arraySliceSliceMinSliceAssign_d", // T[]=T[]-T[]
260             "_arraySliceSliceMinSliceAssign_f", // T[]=T[]-T[]
261             "_arraySliceSliceMinSliceAssign_g",
262             "_arraySliceSliceMinSliceAssign_h",
263             "_arraySliceSliceMinSliceAssign_i",
264             "_arraySliceSliceMinSliceAssign_k",
265             "_arraySliceSliceMinSliceAssign_r", // T[]=T[]-T[]
266             "_arraySliceSliceMinSliceAssign_s",
267             "_arraySliceSliceMinSliceAssign_t",
268             "_arraySliceSliceMinSliceAssign_u",
269             "_arraySliceSliceMinSliceAssign_w",
270
271             "_arraySliceSliceMinass_a",
272             "_arraySliceSliceMinass_d",         // T[]-=T[]
273             "_arraySliceSliceMinass_f",         // T[]-=T[]
274             "_arraySliceSliceMinass_g",
275             "_arraySliceSliceMinass_h",
276             "_arraySliceSliceMinass_i",
277             "_arraySliceSliceMinass_k",
278             "_arraySliceSliceMinass_s",
279             "_arraySliceSliceMinass_t",
280             "_arraySliceSliceMinass_u",
281             "_arraySliceSliceMinass_w",
282
283             "_arraySliceSliceMulSliceAssign_d", // T[]=T[]*T[]
284             "_arraySliceSliceMulSliceAssign_f", // T[]=T[]*T[]
285             "_arraySliceSliceMulSliceAssign_i",
286             "_arraySliceSliceMulSliceAssign_k",
287             "_arraySliceSliceMulSliceAssign_s",
288             "_arraySliceSliceMulSliceAssign_t",
289             "_arraySliceSliceMulSliceAssign_u",
290             "_arraySliceSliceMulSliceAssign_w",
291
292             "_arraySliceSliceMulass_d",         // T[]*=T[]
293             "_arraySliceSliceMulass_f",         // T[]*=T[]
294             "_arraySliceSliceMulass_i",
295             "_arraySliceSliceMulass_k",
296             "_arraySliceSliceMulass_s",
297             "_arraySliceSliceMulass_t",
298             "_arraySliceSliceMulass_u",
299             "_arraySliceSliceMulass_w",
300         };
301
302         int i = binary(name, libArrayopFuncs, sizeof(libArrayopFuncs) / sizeof(char *));
303         if (i == -1)
304         {
305 #ifdef DEBUG    // Make sure our array is alphabetized
306             for (i = 0; i < sizeof(libArrayopFuncs) / sizeof(char *); i++)
307             {
308                 if (strcmp(name, libArrayopFuncs[i]) == 0)
309                     assert(0);
310             }
311 #endif
312             /* Not in library, so generate it.
313              * Construct the function body:
314              *  foreach (i; 0 .. p.length)    for (size_t i = 0; i < p.length; i++)
315              *      loopbody;
316              *  return p;
317              */
318
319             Parameters *fparams = new Parameters();
320             Expression *loopbody = buildArrayLoop(fparams);
321             Parameter *p = (Parameter *)fparams->data[0 /*fparams->dim - 1*/];
322 #if DMDV1
323             // for (size_t i = 0; i < p.length; i++)
324             Initializer *init = new ExpInitializer(0, new IntegerExp(0, 0, Type::tsize_t));
325             Dsymbol *d = new VarDeclaration(0, Type::tsize_t, Id::p, init);
326             Statement *s1 = new ForStatement(0,
327                 new DeclarationStatement(0, d),
328                 new CmpExp(TOKlt, 0, new IdentifierExp(0, Id::p), new ArrayLengthExp(0, new IdentifierExp(0, p->ident))),
329                 new PostExp(TOKplusplus, 0, new IdentifierExp(0, Id::p)),
330                 new ExpStatement(0, loopbody));
331 #else
332             // foreach (i; 0 .. p.length)
333             Statement *s1 = new ForeachRangeStatement(0, TOKforeach,
334                 new Parameter(0, NULL, Id::p, NULL),
335                 new IntegerExp(0, 0, Type::tint32),
336                 new ArrayLengthExp(0, new IdentifierExp(0, p->ident)),
337                 new ExpStatement(0, loopbody));
338 #endif
339             Statement *s2 = new ReturnStatement(0, new IdentifierExp(0, p->ident));
340             //printf("s2: %s\n", s2->toChars());
341             Statement *fbody = new CompoundStatement(0, s1, s2);
342
343             /* Construct the function
344              */
345             TypeFunction *ftype = new TypeFunction(fparams, type, 0, LINKc);
346             //printf("ftype: %s\n", ftype->toChars());
347             fd = new FuncDeclaration(0, 0, Lexer::idPool(name), STCundefined, ftype);
348             fd->fbody = fbody;
349             fd->protection = PROTpublic;
350             fd->linkage = LINKc;
351
352             sc->module->importedFrom->members->push(fd);
353
354             sc = sc->push();
355             sc->parent = sc->module->importedFrom;
356             sc->stc = 0;
357             sc->linkage = LINKc;
358             fd->semantic(sc);
359             fd->semantic2(sc);
360             fd->semantic3(sc);
361             sc->pop();
362         }
363         else
364         {   /* In library, refer to it.
365              */
366             fd = FuncDeclaration::genCfunc(type, name);
367         }
368         sv->ptrvalue = fd;      // cache symbol in hash table
369     }
370
371     /* Call the function fd(arguments)
372      */
373     Expression *ec = new VarExp(0, fd);
374     Expression *e = new CallExp(loc, ec, arguments);
375     e->type = type;
376     return e;
377 }
378
379 /******************************************
380  * Construct the identifier for the array operation function,
381  * and build the argument list to pass to it.
382  */
383
384 void Expression::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
385 {
386     buf->writestring("Exp");
387     arguments->shift(this);
388 }
389
390 void CastExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
391 {
392     Type *tb = type->toBasetype();
393     if (tb->ty == Tarray || tb->ty == Tsarray)
394     {
395         e1->buildArrayIdent(buf, arguments);
396     }
397     else
398         Expression::buildArrayIdent(buf, arguments);
399 }
400
401 void SliceExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
402 {
403     buf->writestring("Slice");
404     arguments->shift(this);
405 }
406
407 void AssignExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
408 {
409     /* Evaluate assign expressions right to left
410      */
411     e2->buildArrayIdent(buf, arguments);
412     e1->buildArrayIdent(buf, arguments);
413     buf->writestring("Assign");
414 }
415
416 #define X(Str) \
417 void Str##AssignExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) \
418 {                                                       \
419     /* Evaluate assign expressions right to left        \
420      */                                                 \
421     e2->buildArrayIdent(buf, arguments);                \
422     e1->buildArrayIdent(buf, arguments);                \
423     buf->writestring(#Str);                             \
424     buf->writestring("ass");                            \
425 }
426
427 X(Add)
428 X(Min)
429 X(Mul)
430 X(Div)
431 X(Mod)
432 X(Xor)
433 X(And)
434 X(Or)
435
436 #undef X
437
438 void NegExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
439 {
440     e1->buildArrayIdent(buf, arguments);
441     buf->writestring("Neg");
442 }
443
444 void ComExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
445 {
446     e1->buildArrayIdent(buf, arguments);
447     buf->writestring("Com");
448 }
449
450 #define X(Str) \
451 void Str##Exp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)  \
452 {                                                                       \
453     /* Evaluate assign expressions left to right                        \
454      */                                                                 \
455     e1->buildArrayIdent(buf, arguments);                                \
456     e2->buildArrayIdent(buf, arguments);                                \
457     buf->writestring(#Str);                                             \
458 }
459
460 X(Add)
461 X(Min)
462 X(Mul)
463 X(Div)
464 X(Mod)
465 X(Xor)
466 X(And)
467 X(Or)
468
469 #undef X
470
471 /******************************************
472  * Construct the inner loop for the array operation function,
473  * and build the parameter list.
474  */
475
476 Expression *Expression::buildArrayLoop(Parameters *fparams)
477 {
478     Identifier *id = Identifier::generateId("c", fparams->dim);
479     Parameter *param = new Parameter(0, type, id, NULL);
480     fparams->shift(param);
481     Expression *e = new IdentifierExp(0, id);
482     return e;
483 }
484
485 Expression *CastExp::buildArrayLoop(Parameters *fparams)
486 {
487     Type *tb = type->toBasetype();
488     if (tb->ty == Tarray || tb->ty == Tsarray)
489     {
490         return e1->buildArrayLoop(fparams);
491     }
492     else
493         return Expression::buildArrayLoop(fparams);
494 }
495
496 Expression *SliceExp::buildArrayLoop(Parameters *fparams)
497 {
498     Identifier *id = Identifier::generateId("p", fparams->dim);
499     Parameter *param = new Parameter(STCconst, type, id, NULL);
500     fparams->shift(param);
501     Expression *e = new IdentifierExp(0, id);
502     Expressions *arguments = new Expressions();
503     Expression *index = new IdentifierExp(0, Id::p);
504     arguments->push(index);
505     e = new ArrayExp(0, e, arguments);
506     return e;
507 }
508
509 Expression *AssignExp::buildArrayLoop(Parameters *fparams)
510 {
511     /* Evaluate assign expressions right to left
512      */
513     Expression *ex2 = e2->buildArrayLoop(fparams);
514 #if DMDV2
515     /* Need the cast because:
516      *   b = c + p[i];
517      * where b is a byte fails because (c + p[i]) is an int
518      * which cannot be implicitly cast to byte.
519      */
520     ex2 = new CastExp(0, ex2, e1->type->nextOf());
521 #endif
522     Expression *ex1 = e1->buildArrayLoop(fparams);
523     Parameter *param = (Parameter *)fparams->data[0];
524     param->storageClass = 0;
525     Expression *e = new AssignExp(0, ex1, ex2);
526     return e;
527 }
528
529 #define X(Str) \
530 Expression *Str##AssignExp::buildArrayLoop(Parameters *fparams) \
531 {                                                               \
532     /* Evaluate assign expressions right to left                \
533      */                                                         \
534     Expression *ex2 = e2->buildArrayLoop(fparams);              \
535     Expression *ex1 = e1->buildArrayLoop(fparams);              \
536     Parameter *param = (Parameter *)fparams->data[0];           \
537     param->storageClass = 0;                                    \
538     Expression *e = new Str##AssignExp(0, ex1, ex2);            \
539     return e;                                                   \
540 }
541
542 X(Add)
543 X(Min)
544 X(Mul)
545 X(Div)
546 X(Mod)
547 X(Xor)
548 X(And)
549 X(Or)
550
551 #undef X
552
553 Expression *NegExp::buildArrayLoop(Parameters *fparams)
554 {
555     Expression *ex1 = e1->buildArrayLoop(fparams);
556     Expression *e = new NegExp(0, ex1);
557     return e;
558 }
559
560 Expression *ComExp::buildArrayLoop(Parameters *fparams)
561 {
562     Expression *ex1 = e1->buildArrayLoop(fparams);
563     Expression *e = new ComExp(0, ex1);
564     return e;
565 }
566
567 #define X(Str) \
568 Expression *Str##Exp::buildArrayLoop(Parameters *fparams)       \
569 {                                                               \
570     /* Evaluate assign expressions left to right                \
571      */                                                         \
572     Expression *ex1 = e1->buildArrayLoop(fparams);              \
573     Expression *ex2 = e2->buildArrayLoop(fparams);              \
574     Expression *e = new Str##Exp(0, ex1, ex2);                  \
575     return e;                                                   \
576 }
577
578 X(Add)
579 X(Min)
580 X(Mul)
581 X(Div)
582 X(Mod)
583 X(Xor)
584 X(And)
585 X(Or)
586
587 #undef X
588
589
590 /***********************************************
591  * Test if operand is a valid array op operand.
592  */
593
594 int Expression::isArrayOperand()
595 {
596     //printf("Expression::isArrayOperand() %s\n", toChars());
597     if (op == TOKslice)
598         return 1;
599     if (type->toBasetype()->ty == Tarray)
600     {
601         switch (op)
602         {
603             case TOKadd:
604             case TOKmin:
605             case TOKmul:
606             case TOKdiv:
607             case TOKmod:
608             case TOKxor:
609             case TOKand:
610             case TOKor:
611             case TOKassign:
612             case TOKaddass:
613             case TOKminass:
614             case TOKmulass:
615             case TOKdivass:
616             case TOKmodass:
617             case TOKxorass:
618             case TOKandass:
619             case TOKorass:
620 #if DMDV2
621             case TOKpow:
622             case TOKpowass:
623 #endif
624             case TOKneg:
625             case TOKtilde:
626                 return 1;
627
628             default:
629                 break;
630         }
631     }
632     return 0;
633 }
Note: See TracBrowser for help on using the browser.