1926Speter /* Copyright (c) 1980 Regents of the University of California */
2763Speter 
3*5663Smckusic static	char sccsid[] = "@(#)pccaseop.c 1.8 02/02/82";
4763Speter 
5763Speter #include "whoami.h"
6763Speter #ifdef PC
7763Speter     /*
8763Speter      *	and the rest of the file
9763Speter      */
10763Speter #include "0.h"
11763Speter #include "tree.h"
12763Speter #include "objfmt.h"
13763Speter #include "pcops.h"
14763Speter #include "pc.h"
15926Speter 
16763Speter     /*
17926Speter      *	structure for a case:
18926Speter      *	    its constant label, line number (for errors), and location label.
19926Speter      */
20926Speter struct ct {
21926Speter     long	cconst;
22926Speter     int		cline;
23926Speter     int		clabel;
24926Speter };
25926Speter 
26926Speter     /*
27926Speter      *	the P2FORCE operator puts its operand into a register.
28926Speter      *	these to keep from thinking of it as r0 all over.
29926Speter      */
30926Speter #define	FORCENAME	"r0"
31926Speter 
32926Speter     /*
33926Speter      *	given a tree for a case statement, generate code for it.
34926Speter      *	this computes the expression into a register,
35926Speter      *	puts down the code for each of the cases,
36926Speter      *	and then decides how to do the case switching.
37763Speter      *	tcase	[0]	T_CASE
38763Speter      *		[1]	lineof "case"
39763Speter      *		[2]	expression
40926Speter      *		[3]	list of cased statements:
41763Speter      *			cstat	[0]	T_CSTAT
42763Speter      *				[1]	lineof ":"
43926Speter      *				[2]	list of constant labels
44763Speter      *				[3]	statement
45763Speter      */
46763Speter pccaseop( tcase )
47763Speter     int	*tcase;
48926Speter {
49926Speter     struct nl	*exprtype;
503830Speter     struct nl	*exprnlp;
51926Speter     struct nl	*rangetype;
52926Speter     long	low;
53926Speter     long	high;
54926Speter     long	exprctype;
55926Speter     long	swlabel;
56926Speter     long	endlabel;
57926Speter     long	label;
58926Speter     long	count;
59926Speter     long	*cstatlp;
60926Speter     long	*cstatp;
61926Speter     long	*casep;
62926Speter     struct ct	*ctab;
63926Speter     struct ct	*ctp;
64926Speter     long	i;
65926Speter     long	nr;
66926Speter     long	goc;
67926Speter     int		casecmp();
681278Speter     bool	dupcases;
69763Speter 
70926Speter     goc = gocnt;
71926Speter 	/*
72926Speter 	 *  find out the type of the case expression
73926Speter 	 *  even if the expression has errors (exprtype == NIL), continue.
74926Speter 	 */
75926Speter     line = tcase[1];
763641Speter     codeoff();
77926Speter     exprtype = rvalue( (int *) tcase[2] , NIL  , RREQ );
783641Speter     codeon();
79926Speter     if ( exprtype != NIL ) {
80926Speter 	if ( isnta( exprtype , "bcsi" ) ) {
81926Speter 	    error("Case selectors cannot be %ss" , nameof( exprtype ) );
82926Speter 	    exprtype = NIL;
83926Speter 	} else {
84926Speter 	    if ( exprtype -> class != RANGE ) {
85926Speter 		rangetype = exprtype -> type;
86926Speter 	    } else {
87926Speter 		rangetype = exprtype;
88926Speter 	    }
89926Speter 	    if ( rangetype == NIL ) {
90763Speter 		exprtype = NIL;
91763Speter 	    } else {
92926Speter 		low = rangetype -> range[0];
93926Speter 		high = rangetype -> range[1];
94763Speter 	    }
95763Speter 	}
96926Speter     }
97926Speter     if ( exprtype != NIL ) {
98763Speter 	    /*
993641Speter 	     *	compute and save the case expression.
1003641Speter 	     *	also, put expression into a register
101926Speter 	     *	save its c-type and jump to the code to do the switch.
102763Speter 	     */
1033641Speter 	exprctype = p2type( exprtype );
1043830Speter 	exprnlp = tmpalloc( sizeof (long) , nl + T4INT , NOREG );
1053830Speter 	putRV( 0 , cbn , exprnlp -> value[ NL_OFFS ] ,
1063830Speter 			exprnlp -> extra_flags , P2INT );
1073641Speter 	(void) rvalue( (int *) tcase[2] , NIL , RREQ );
1083641Speter 	putop( P2ASSIGN , P2INT );
109926Speter 	putop( P2FORCE , P2INT );
110926Speter 	putdot( filename , line );
111926Speter 	swlabel = getlab();
112926Speter 	putjbr( swlabel );
113926Speter     }
114926Speter 	/*
115926Speter 	 *  count the number of cases
116926Speter 	 *  and allocate table for cases, lines, and labels
117926Speter 	 *  default case goes in ctab[0].
118926Speter 	 */
119926Speter     count = 1;
120926Speter     for ( cstatlp = tcase[3] ; cstatlp != NIL ; cstatlp = cstatlp[2] ) {
121926Speter 	cstatp = cstatlp[1];
122926Speter 	if ( cstatp == NIL ) {
123926Speter 	    continue;
124763Speter 	}
125926Speter 	for ( casep = cstatp[2] ; casep != NIL ; casep = casep[2] ) {
126926Speter 	    count++;
127763Speter 	}
128926Speter     }
129926Speter 	/*
130926Speter 	 */
131926Speter     ctab = (struct ct *) malloc( count * sizeof( struct ct ) );
132926Speter     if ( ctab == (struct ct *) 0 ) {
133926Speter 	error("Ran out of memory (case)");
134926Speter 	pexit( DIED );
135926Speter     }
136926Speter 	/*
137926Speter 	 *  pick up default label and label for after case statement.
138926Speter 	 */
139926Speter     ctab[0].clabel = getlab();
140926Speter     endlabel = getlab();
141926Speter 	/*
142926Speter 	 *  generate code for each case
143926Speter 	 *  filling in ctab for each.
144926Speter 	 *  nr is for error if no case falls out bottom.
145926Speter 	 */
146926Speter     nr = 1;
147926Speter     count = 0;
148926Speter     for ( cstatlp = tcase[3] ; cstatlp != NIL ; cstatlp = cstatlp[2] ) {
149926Speter 	cstatp = cstatlp[1];
150926Speter 	if ( cstatp == NIL ) {
151926Speter 	    continue;
152926Speter 	}
153926Speter 	line = cstatp[1];
154926Speter 	label = getlab();
155926Speter 	for ( casep = cstatp[2] ; casep != NIL ; casep = casep[2] ) {
156926Speter 	    gconst( casep[1] );
157926Speter 	    if( exprtype == NIL || con.ctype == NIL ) {
158763Speter 		continue;
159763Speter 	    }
160926Speter 	    if ( incompat( con.ctype , exprtype , NIL ) ) {
161926Speter 		cerror("Case label type clashed with case selector expression type");
162926Speter 		continue;
163763Speter 	    }
164926Speter 	    if ( con.crval < low || con.crval > high ) {
165926Speter 		error("Case label out of range");
166926Speter 		continue;
167763Speter 	    }
168926Speter 	    count++;
169926Speter 	    ctab[ count ].cconst = con.crval;
170926Speter 	    ctab[ count ].cline = line;
171926Speter 	    ctab[ count ].clabel = label;
172763Speter 	}
173763Speter 	    /*
174926Speter 	     *	put out the statement
175763Speter 	     */
176926Speter 	putlab( label );
177926Speter 	putcnt();
178926Speter 	level++;
179926Speter 	statement( cstatp[3] );
1803139Smckusic 	nr = (nr && noreach);
181926Speter 	noreach = 0;
182926Speter 	level--;
183926Speter 	if (gotos[cbn]) {
184926Speter 		ungoto();
185763Speter 	}
186926Speter 	putjbr( endlabel );
187763Speter     }
1881278Speter     noreach = nr;
189926Speter 	/*
190926Speter 	 *	default action is to call error
191926Speter 	 */
192926Speter     putlab( ctab[0].clabel );
193*5663Smckusic     putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) , "_CASERNG" );
1943830Speter     putRV( 0 , cbn , exprnlp -> value[ NL_OFFS ] ,
1953830Speter 		    exprnlp -> extra_flags , P2INT );
196926Speter     putop( P2CALL , P2INT );
197926Speter     putdot( filename , line );
198926Speter 	/*
199926Speter 	 *  sort the cases
200926Speter 	 */
201926Speter     qsort( &ctab[1] , count , sizeof (struct ct) , casecmp );
202926Speter 	/*
203926Speter 	 *  check for duplicates
204926Speter 	 */
2051278Speter     dupcases = FALSE;
206926Speter     for ( ctp = &ctab[1] ; ctp < &ctab[ count ] ; ctp++ ) {
207926Speter 	if ( ctp[0].cconst == ctp[1].cconst ) {
2081278Speter 	    error("Multiply defined label in case, lines %d and %d" ,
209926Speter 		    ctp[0].cline , ctp[1].cline );
2101278Speter 	    dupcases = TRUE;
211926Speter 	}
212926Speter     }
2131278Speter     if ( dupcases ) {
2141278Speter 	return;
2151278Speter     }
216926Speter 	/*
217926Speter 	 *  choose a switch algorithm and implement it:
218926Speter 	 *	direct switch	>= 1/3 full and >= 4 cases.
219926Speter 	 *	binary switch	not direct switch and > 8 cases.
220926Speter 	 *	ifthenelse	not direct or binary switch.
221926Speter 	 */
222926Speter     putlab( swlabel );
223926Speter     if ( ctab[ count ].cconst - ctab[1].cconst < 3 * count && count >= 4 ) {
224926Speter 	directsw( ctab , count );
225926Speter     } else if ( count > 8 ) {
226926Speter 	binarysw( ctab , count );
227926Speter     } else {
228926Speter 	itesw( ctab , count );
229926Speter     }
230926Speter     putlab( endlabel );
231926Speter     if ( goc != gocnt ) {
232926Speter 	    putcnt();
233926Speter     }
234926Speter }
235763Speter 
236926Speter     /*
237926Speter      *	direct switch
238926Speter      */
239926Speter directsw( ctab , count )
240926Speter     struct ct	*ctab;
241926Speter     int		count;
242926Speter {
243926Speter     int		fromlabel = getlab();
244926Speter     long	i;
245926Speter     long	j;
246926Speter 
247926Speter     putprintf( "	casel	%s,$%d,$%d" , 0 , FORCENAME ,
248926Speter 	    ctab[1].cconst , ctab[ count ].cconst - ctab[1].cconst );
249926Speter     putlab( fromlabel );
250926Speter     i = 1;
251926Speter     j = ctab[1].cconst;
252926Speter     while ( i <= count ) {
253926Speter 	if ( j == ctab[ i ].cconst ) {
254926Speter 	    putprintf( "	.word	" , 1 );
255926Speter 	    putprintf( PREFIXFORMAT , 1 , LABELPREFIX , ctab[ i ].clabel );
256926Speter 	    putprintf( "-" , 1 );
257926Speter 	    putprintf( PREFIXFORMAT , 0 , LABELPREFIX , fromlabel );
258926Speter 	    i++;
259926Speter 	} else {
260926Speter 	    putprintf( "	.word	" , 1 );
261926Speter 	    putprintf( PREFIXFORMAT , 1 , LABELPREFIX , ctab[ 0 ].clabel );
262926Speter 	    putprintf( "-" , 1 );
263926Speter 	    putprintf( PREFIXFORMAT , 0 , LABELPREFIX , fromlabel );
264926Speter 	}
265926Speter 	j++;
266926Speter     }
267926Speter     putjbr( ctab[0].clabel );
268926Speter }
269926Speter 
270926Speter     /*
271926Speter      *	binary switch
272926Speter      *	special case out default label and start recursion.
273926Speter      */
274926Speter binarysw( ctab , count )
275926Speter     struct ct	*ctab;
276926Speter     int		count;
277926Speter {
278926Speter 
279926Speter     bsrecur( ctab[0].clabel , &ctab[0] , count );
280926Speter }
281926Speter 
282926Speter     /*
283926Speter      *	recursive log( count ) search.
284926Speter      */
285926Speter bsrecur( deflabel , ctab , count )
286926Speter     int		deflabel;
287926Speter     struct ct	*ctab;
288926Speter     int		count;
289926Speter {
290926Speter 
291926Speter     if ( count <= 0 ) {
292926Speter 	putprintf( "	jbr	L%d" , 0 , deflabel );
293926Speter 	return;
294926Speter     } else if ( count == 1 ) {
295926Speter 	putprintf( "	cmpl	%s,$%d" , 0 , FORCENAME , ctab[1].cconst );
296926Speter 	putprintf( "	jeql	L%d" , 0 , ctab[1].clabel );
297926Speter 	putprintf( "	jbr	L%d" , 0 , deflabel );
298926Speter 	return;
299926Speter     } else {
300926Speter 	int	half = ( count + 1 ) / 2;
301926Speter 	int	gtrlabel = getlab();
302926Speter 
303926Speter 	putprintf( "	cmpl	%s,$%d" , 0 , FORCENAME , ctab[ half ].cconst );
304929Speter 	putprintf( "	jgtr	L%d" , 0 , gtrlabel );
305926Speter 	putprintf( "	jeql	L%d" , 0 , ctab[ half ].clabel );
306926Speter 	bsrecur( deflabel , &ctab[0] , half - 1 );
307926Speter 	putprintf( "L%d:" , 0 , gtrlabel );
308926Speter 	bsrecur( deflabel , &ctab[ half ] , count - half );
309926Speter 	return;
310926Speter     }
311926Speter }
312926Speter 
313926Speter itesw( ctab , count )
314926Speter     struct ct	*ctab;
315926Speter     int		count;
316926Speter {
317926Speter     int	i;
318926Speter 
319926Speter     for ( i = 1 ; i <= count ; i++ ) {
320926Speter 	putprintf( "	cmpl	%s,$%d" , 0 , FORCENAME , ctab[ i ].cconst );
321926Speter 	putprintf( "	jeql	L%d" , 0 , ctab[ i ].clabel );
322926Speter     }
323926Speter     putprintf( "	jbr	L%d" , 0 , ctab[0].clabel );
324926Speter     return;
325926Speter }
326926Speter int
327926Speter casecmp( this , that )
328926Speter     struct ct 	*this;
329926Speter     struct ct 	*that;
330926Speter {
331926Speter     if ( this -> cconst < that -> cconst ) {
332926Speter 	return -1;
333926Speter     } else if ( this -> cconst > that -> cconst ) {
334926Speter 	return 1;
335926Speter     } else {
336926Speter 	return 0;
337926Speter     }
338926Speter }
339763Speter #endif PC
340