Browse code

script engine: string switch optimization and fixup

- fixup and optimize (if constant) string switches

Andrei Pelinescu-Onciul authored on 19/02/2009 22:47:10
Showing 1 changed files
... ...
@@ -22,6 +22,7 @@
22 22
  * History:
23 23
  * --------
24 24
  *  2009-02-02  initial version (andrei)
25
+ *  2009-02-19  string and RE switch support added (andrei)
25 26
 */
26 27
 
27 28
 #include "switch.h"
... ...
@@ -84,6 +85,32 @@ static struct switch_jmp_table* mk_switch_jmp_table(int jmp_size, int rest)
84 84
 
85 85
 
86 86
 
87
+/** create a match cond table structure (pkg_malloc'ed).
88
+ * @return 0 on error, pointer on success
89
+ */
90
+static struct match_cond_table* mk_match_cond_table(int n)
91
+{
92
+	struct match_cond_table* mct;
93
+	
94
+	/* allocate everything in a single block, better for cache locality */
95
+	mct=pkg_malloc(ROUND_POINTER(sizeof(*mct))+
96
+					ROUND_POINTER(n*sizeof(mct->match[0]))+
97
+					n*sizeof(mct->jump[0]));
98
+	if (mct==0) return 0;
99
+	mct->n=n;
100
+	mct->match=(struct match_str*)((char*)mct+ROUND_POINTER(sizeof(*mct)));
101
+	mct->jump=(struct action**)
102
+				((char*)mct->match+ROUND_POINTER(n*sizeof(mct->match[0])));
103
+	mct->def=0;
104
+	return mct;
105
+}
106
+
107
+
108
+
109
+static int fix_match(struct action* t);
110
+
111
+
112
+
87 113
 void destroy_case_stms(struct case_stms *lst)
88 114
 {
89 115
 	struct case_stms* l;
... ...
@@ -138,6 +165,11 @@ int fix_switch(struct action* t)
138 138
 	def_jmp_bm=0;
139 139
 	labels_no=0;
140 140
 	default_found=0;
141
+	/* check if string switch (first case is string or RE) */
142
+	for (c=(struct case_stms*)t->val[1].u.data; c && c->is_default; c=c->next);
143
+	if (c && (c->type==MATCH_STR || c->type==MATCH_RE))
144
+		return fix_match(t);
145
+	
141 146
 	sw_rve=(struct rval_expr*)t->val[0].u.data;
142 147
 	/*  handle null actions: optimize away if no
143 148
 	   sideffects */
... ...
@@ -159,12 +191,17 @@ int fix_switch(struct action* t)
159 159
 	n=0;
160 160
 	for (c=(struct case_stms*)t->val[1].u.data; c; c=c->next){
161 161
 		if (c->ct_rve){
162
+			if (c->type!=MATCH_INT){
163
+				LOG(L_ERR, "ERROR: fix_switch: wrong case type %d (int"
164
+							"expected)\n", c->type);
165
+				return E_UNSPEC;
166
+			}
162 167
 			if (!rve_is_constant(c->ct_rve)){
163 168
 				LOG(L_ERR, "ERROR: fix_switch: non constant "
164 169
 						"expression in case\n");
165 170
 				return E_BUG;
166 171
 			}
167
-			if (rval_expr_eval_int(0, 0,  &c->int_label, c->ct_rve)
172
+			if (rval_expr_eval_int(0, 0,  &c->label.match_int, c->ct_rve)
168 173
 					<0){
169 174
 				LOG(L_ERR, "ERROR: fix_switch: case expression"
170 175
 						" (%d,%d) has non-interger type\n",
... ...
@@ -183,7 +220,7 @@ int fix_switch(struct action* t)
183 183
 				return E_UNSPEC;
184 184
 			}
185 185
 			default_found=1;
186
-			c->int_label=-1;
186
+			c->label.match_int=-1;
187 187
 			c->is_default=1;
188 188
 			def_a=c->actions;
189 189
 		}
... ...
@@ -241,14 +278,14 @@ int fix_switch(struct action* t)
241 241
 			def_jmp_bm=tail;
242 242
 		} else {
243 243
 			for (j=0; j<n; j++){
244
-				if (cond[j]==c->int_label){
244
+				if (cond[j]==c->label.match_int){
245 245
 					LOG(L_ERR, "ERROR: fix_switch: duplicate case (%d,%d)\n",
246 246
 							c->ct_rve->fpos.s_line, c->ct_rve->fpos.s_col);
247 247
 					ret=E_UNSPEC;
248 248
 					goto error;
249 249
 				}
250 250
 			}
251
-			cond[n]=c->int_label;
251
+			cond[n]=c->label.match_int;
252 252
 			jmp_bm[n]=tail;
253 253
 			n++;
254 254
 		}
... ...
@@ -371,4 +408,300 @@ error:
371 371
 	return ret;
372 372
 }
373 373
 
374
+
375
+
376
+/** fixup function for MATCH_T actions.
377
+ * can produce 3 different action types:
378
+ *  - BLOCK_T (actions) - actions grouped in a block, break ends the block
379
+ *    execution.
380
+ *  - EVAL_T (cond)  - null switch block, but the condition has to be
381
+ *                       evaluated due to possible side-effects.
382
+ *  - MATCH_COND_T(cond, jumps) - condition table
383
+ */
384
+static int fix_match(struct action* t)
385
+{
386
+	struct case_stms* c;
387
+	int n, i, j, ret;
388
+	struct action* a;
389
+	struct action* block;
390
+	struct action* def_a;
391
+	struct action* action_lst;
392
+	struct action** tail;
393
+	struct match_cond_table* mct;
394
+	int labels_no;
395
+	struct action** def_jmp_bm;
396
+	struct match_str* match;
397
+	struct action*** jmp_bm;
398
+	int default_found;
399
+	struct rval_expr* m_rve;
400
+	struct rvalue* rv;
401
+	regex_t* regex;
402
+	str s;
403
+	
404
+	ret=E_BUG;
405
+	match=0;
406
+	jmp_bm=0;
407
+	def_jmp_bm=0;
408
+	labels_no=0;
409
+	default_found=0;
410
+	rv=0;
411
+	s.s=0;
412
+	s.len=0;
413
+	m_rve=(struct rval_expr*)t->val[0].u.data;
414
+	/*  handle null actions: optimize away if no
415
+	   sideffects */
416
+	if (t->val[1].u.data==0){
417
+		if (!rve_has_side_effects(m_rve)){
418
+			t->type=BLOCK_T;
419
+			rve_destroy(m_rve);
420
+			t->val[0].type=BLOCK_ST;
421
+			t->val[0].u.data=0;
422
+			DBG("MATCH: null switch optimized away\n");
423
+		}else{
424
+			t->type=EVAL_T;
425
+			t->val[0].type=RVE_ST;
426
+			DBG("MATCH: null switch turned to EVAL_T\n");
427
+		}
428
+		return 0;
429
+	}
430
+	def_a=0;
431
+	n=0;
432
+	for (c=(struct case_stms*)t->val[1].u.data; c; c=c->next){
433
+		if (c->ct_rve){
434
+			if (c->type!=MATCH_STR && c->type!=MATCH_RE){
435
+				LOG(L_ERR, "ERROR: fix_match: wrong case type %d (string"
436
+							"or RE expected)\n", c->type);
437
+				return E_UNSPEC;
438
+			}
439
+			if (!rve_is_constant(c->ct_rve)){
440
+				LOG(L_ERR, "ERROR: fix_match: non constant "
441
+						"expression in case\n");
442
+				ret=E_BUG;
443
+				goto error;
444
+			}
445
+			if ((rv=rval_expr_eval(0, 0, c->ct_rve)) == 0 ){
446
+				LOG(L_ERR, "ERROR: fix_match: bad case expression"
447
+						" (%d,%d)\n",
448
+						c->ct_rve->fpos.s_line,
449
+						c->ct_rve->fpos.s_col);
450
+				ret=E_BUG;
451
+				goto error;
452
+			}
453
+			if (rval_get_str(0, 0, &s, rv, 0)<0){
454
+				LOG(L_ERR, "ERROR: fix_match (%d,%d): out of memory?\n",
455
+						c->ct_rve->fpos.s_line,
456
+						c->ct_rve->fpos.s_col);
457
+				ret=E_BUG;
458
+				goto error;
459
+			}
460
+			if (c->type==MATCH_RE){
461
+				if ((regex=pkg_malloc(sizeof(regex_t))) == 0){
462
+					LOG(L_ERR, "ERROR: fix_match: out of memory\n");
463
+					ret=E_OUT_OF_MEM;
464
+					goto error;
465
+				}
466
+				if (regcomp(regex, s.s, 
467
+							REG_EXTENDED | REG_NOSUB | c->re_flags) !=0){
468
+					pkg_free(regex);
469
+					regex=0;
470
+					LOG(L_ERR, "ERROR: fix_match (%d, %d): bad regular"
471
+								" expression %.*s\n",
472
+							c->ct_rve->fpos.s_line,
473
+							c->ct_rve->fpos.s_col,
474
+							s.len, ZSW(s.s));
475
+					ret=E_UNSPEC;
476
+					goto error;
477
+				}
478
+				c->label.match_re=regex;
479
+				regex=0;
480
+			}else if (c->type==MATCH_STR){
481
+				c->label.match_str=s;
482
+				s.s=0;
483
+				s.len=0;
484
+			}else{
485
+				LOG(L_CRIT, "BUG: fix_match (%d,%d): wrong case type %d\n",
486
+						c->ct_rve->fpos.s_line, c->ct_rve->fpos.s_col,
487
+						c->type);
488
+				ret=E_BUG;
489
+				goto error;
490
+			}
491
+			c->is_default=0;
492
+			n++; /* count only non-default cases */
493
+			/* cleanup */
494
+			rval_destroy(rv);
495
+			rv=0;
496
+			if (s.s){
497
+				pkg_free(s.s);
498
+				s.s=0;
499
+				s.len=0;
500
+			}
501
+		}else{
502
+			if (default_found){
503
+				LOG(L_ERR, "ERROR: fix_match: more then one \"default\""
504
+						" label found (%d, %d)\n",
505
+						c->ct_rve->fpos.s_line,
506
+						c->ct_rve->fpos.s_col);
507
+				ret=E_UNSPEC;
508
+				goto error;
509
+			}
510
+			default_found=1;
511
+			c->is_default=1;
512
+			def_a=c->actions;
513
+		}
514
+		if ( c->actions && ((ret=fix_actions(c->actions))<0))
515
+			goto error;
516
+	}
517
+	DBG("MATCH: %d cases, %d default\n", n, default_found);
518
+	/*: handle n==0 (no case only a default:) */
519
+	if (n==0){
520
+		if (default_found){
521
+			if (!rve_has_side_effects(m_rve)){
522
+				t->type=BLOCK_T;
523
+				rve_destroy(m_rve);
524
+				destroy_case_stms(t->val[1].u.data);
525
+				t->val[0].type=BLOCK_ST;
526
+				t->val[0].u.data=def_a;
527
+				t->val[1].type=0;
528
+				t->val[1].u.data=0;
529
+				DBG("MATCH: default only switch optimized away (BLOCK_T)\n");
530
+				return 0;
531
+			}
532
+			DBG("MATCH: default only switch with side-effect...\n");
533
+		}else{
534
+			LOG(L_CRIT, "BUG: fix_match: empty switch not expected at this"
535
+					" point\n");
536
+			ret=E_BUG;
537
+			goto error;
538
+		}
539
+	}
540
+	labels_no=n;
541
+	match=pkg_malloc(sizeof(match[0])*n);
542
+	jmp_bm=pkg_malloc(sizeof(jmp_bm[0])*n);
543
+	if (match==0 || jmp_bm==0){
544
+		LOG(L_ERR, "ERROR: fix_match: memory allocation failure\n");
545
+		ret=E_OUT_OF_MEM;
546
+		goto error;
547
+	}
548
+	
549
+	/* fill condition table and jump point bookmarks and "flatten" the action 
550
+	   lists (transform them into a single list for the entire switch, rather
551
+	    then one block per case ) */
552
+	n=0;
553
+	action_lst=0;
554
+	tail=&action_lst;
555
+	for (c=(struct case_stms*)t->val[1].u.data; c; c=c->next){
556
+		a=c->actions;
557
+		if (a){
558
+			for (; a->next; a=a->next);
559
+			if (action_lst==0)
560
+				action_lst=c->actions;
561
+			else
562
+				*tail=c->actions;
563
+		}
564
+		if (c->is_default){
565
+			def_jmp_bm=tail;
566
+		} else{
567
+			match[n].type=c->type;
568
+			if (match[n].type == MATCH_STR){
569
+				for (j=0; j<n; j++){
570
+					if ( match[j].type == c->type &&
571
+						 match[j].l.s.len ==  c->label.match_str.len &&
572
+						 memcmp(match[j].l.s.s, c->label.match_str.s,
573
+							 		match[j].l.s.len) == 0 ){
574
+						LOG(L_ERR, "ERROR: fix_match: duplicate case"
575
+								" (%d,%d)\n", c->ct_rve->fpos.s_line,
576
+								c->ct_rve->fpos.s_col);
577
+						ret=E_UNSPEC;
578
+						goto error;
579
+					}
580
+				}
581
+				match[n].flags=0;
582
+				match[n].l.s=c->label.match_str;
583
+				c->label.match_str.s=0; /* prevent s being freed */
584
+				c->label.match_str.len=0;
585
+			} else {
586
+				match[n].flags=c->re_flags | REG_EXTENDED | REG_NOSUB;
587
+				match[n].l.regex=c->label.match_re;
588
+				c->label.match_re=0;
589
+			}
590
+			jmp_bm[n]=tail;
591
+			n++;
592
+		}
593
+		if (c->actions)
594
+			tail=&a->next;
595
+	}
596
+	/* handle constant rve w/ no side-effects: replace the whole case 
597
+	   with the case rve block */
598
+	if ( (scr_opt_lev>=2) &&
599
+			!rve_has_side_effects(m_rve) && rve_is_constant(m_rve)){
600
+		if ((rv=rval_expr_eval(0, 0, m_rve)) == 0){
601
+			LOG(L_ERR, "ERROR: fix_match: bad expression (%d,%d)\n", 
602
+					m_rve->fpos.s_line, m_rve->fpos.s_col);
603
+			ret=E_UNSPEC;
604
+			goto error;
605
+		}
606
+		if (rval_get_str(0, 0, &s, rv, 0) < 0 ){
607
+				LOG(L_ERR, "ERROR: fix_match (%d,%d): bad string expression\n",
608
+						m_rve->fpos.s_line,
609
+						m_rve->fpos.s_col);
610
+			ret=E_UNSPEC;
611
+			goto error;
612
+		}
613
+		/* start with the "default:" value in case nothing is found */
614
+		block=def_jmp_bm?*def_jmp_bm:0;
615
+		for (i=0; i<n; i++){
616
+			if (((match[i].type == MATCH_STR) && (match[i].l.s.len == s.len) &&
617
+					(memcmp(match[i].l.s.s, s.s, s.len) == 0)) ||
618
+				((match[i].type == MATCH_RE) && 
619
+					regexec(match[i].l.regex, s.s, 0, 0, 0) == 0) ) {
620
+				block=*jmp_bm[i];
621
+				break;
622
+			}
623
+		}
624
+		DBG("MATCH: constant switch(\"%.*s\") with %d cases optimized away"
625
+				" to case no. %d\n", s.len, ZSW(s.s), n, i);
626
+		/* cleanup */
627
+		rval_destroy(rv);
628
+		rv=0;
629
+		pkg_free(s.s);
630
+		s.s=0;
631
+		s.len=0;
632
+		ret=0;
633
+		/* replace with BLOCK_ST */
634
+		rve_destroy(m_rve);
635
+		destroy_case_stms(t->val[1].u.data);
636
+		t->type=BLOCK_T;
637
+		t->val[0].type=BLOCK_ST;
638
+		t->val[0].u.data=block;
639
+		t->val[1].type=0;
640
+		t->val[1].u.data=0;
641
+		goto end;
642
+	}
643
+	mct=mk_match_cond_table(n);
644
+	if (mct==0){
645
+		LOG(L_ERR, "ERROR: fix_match: memory allocation error\n");
646
+		ret=E_OUT_OF_MEM;
647
+		goto error;
648
+	}
649
+	mct->n=n;
650
+	for (i=0; i<n; i++){
651
+		mct->match[i]=match[i];
652
+		mct->jump[i]=*jmp_bm[i];
653
+	}
654
+	mct->def=def_jmp_bm?*def_jmp_bm:0;
655
+	t->type=MATCH_COND_T;
656
+	t->val[1].type=MATCH_CONDTABLE_ST;
657
+	t->val[1].u.data=mct;
658
+	DBG("MATCH: optimized to match condtable (%d) default: %s\n ",
659
+				mct->n, mct->def?"yes":"no");
660
+		ret=0;
661
+end:
662
+error:
663
+	if (match) pkg_free(match);
664
+	if (jmp_bm) pkg_free(jmp_bm);
665
+	/* cleanup rv & s*/
666
+	if (rv) rval_destroy(rv);
667
+	if (s.s) pkg_free(s.s);
668
+	return ret;
669
+}
374 670
 /* vi: set ts=4 sw=4 tw=79:ai:cindent: */