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