xref: /csrg-svn/usr.bin/pascal/src/pccaseop.c (revision 17425)
1926Speter /* Copyright (c) 1980 Regents of the University of California */
2763Speter 
315936Smckusick #ifndef lint
4*17425Smckusick static char sccsid[] = "@(#)pccaseop.c 2.2 11/26/84";
515936Smckusick #endif
6763Speter 
7763Speter #include "whoami.h"
8763Speter #ifdef PC
9763Speter     /*
10763Speter      *	and the rest of the file
11763Speter      */
12763Speter #include "0.h"
13763Speter #include "tree.h"
14763Speter #include "objfmt.h"
15763Speter #include "pcops.h"
16763Speter #include "pc.h"
1711329Speter #include "tmps.h"
1815936Smckusick #include "tree_ty.h"
19926Speter 
20763Speter     /*
21926Speter      *	structure for a case:
22926Speter      *	    its constant label, line number (for errors), and location label.
23926Speter      */
24926Speter struct ct {
25926Speter     long	cconst;
26926Speter     int		cline;
27926Speter     int		clabel;
28926Speter };
29926Speter 
30926Speter     /*
31926Speter      *	the P2FORCE operator puts its operand into a register.
32926Speter      *	these to keep from thinking of it as r0 all over.
33926Speter      */
3410378Speter #ifdef vax
3510378Speter #   define	FORCENAME	"r0"
3610378Speter #endif vax
3710378Speter #ifdef mc68000
3810378Speter #   define	FORCENAME	"d0"
3912968Smckusick #   define	ADDRTEMP	"a0"
4010378Speter #endif mc68000
41926Speter 
42926Speter     /*
43926Speter      *	given a tree for a case statement, generate code for it.
44926Speter      *	this computes the expression into a register,
45926Speter      *	puts down the code for each of the cases,
46926Speter      *	and then decides how to do the case switching.
47763Speter      *	tcase	[0]	T_CASE
48763Speter      *		[1]	lineof "case"
49763Speter      *		[2]	expression
50926Speter      *		[3]	list of cased statements:
51763Speter      *			cstat	[0]	T_CSTAT
52763Speter      *				[1]	lineof ":"
53926Speter      *				[2]	list of constant labels
54763Speter      *				[3]	statement
55763Speter      */
56763Speter pccaseop( tcase )
5715936Smckusick     WHI_CAS *tcase;
58926Speter {
59926Speter     struct nl	*exprtype;
603830Speter     struct nl	*exprnlp;
61926Speter     struct nl	*rangetype;
62926Speter     long	low;
63926Speter     long	high;
64926Speter     long	exprctype;
6515936Smckusick     char 	*swlabel;
6615936Smckusick     char	*endlabel;
6715936Smckusick     char	*label;
6815936Smckusick     int		count;
6915936Smckusick     struct tnode *cstatlp;
7015936Smckusick     struct tnode *cstatp;
7115936Smckusick     struct tnode *casep;
72926Speter     struct ct	*ctab;
73926Speter     struct ct	*ctp;
7415936Smckusick     bool	nr;
75926Speter     long	goc;
76926Speter     int		casecmp();
771278Speter     bool	dupcases;
78763Speter 
79926Speter     goc = gocnt;
80926Speter 	/*
81926Speter 	 *  find out the type of the case expression
82926Speter 	 *  even if the expression has errors (exprtype == NIL), continue.
83926Speter 	 */
8415936Smckusick     line = tcase->line_no;
853641Speter     codeoff();
8615936Smckusick     exprtype = rvalue( tcase->expr , NLNIL  , RREQ );
873641Speter     codeon();
8815936Smckusick     if ( exprtype != NLNIL ) {
89926Speter 	if ( isnta( exprtype , "bcsi" ) ) {
90926Speter 	    error("Case selectors cannot be %ss" , nameof( exprtype ) );
9115936Smckusick 	    exprtype = NLNIL;
92926Speter 	} else {
93926Speter 	    if ( exprtype -> class != RANGE ) {
94926Speter 		rangetype = exprtype -> type;
95926Speter 	    } else {
96926Speter 		rangetype = exprtype;
97926Speter 	    }
9815936Smckusick 	    if ( rangetype == NLNIL ) {
9915936Smckusick 		exprtype = NLNIL;
100763Speter 	    } else {
101926Speter 		low = rangetype -> range[0];
102926Speter 		high = rangetype -> range[1];
103763Speter 	    }
104763Speter 	}
105926Speter     }
10615936Smckusick     if ( exprtype != NLNIL ) {
107763Speter 	    /*
1083641Speter 	     *	compute and save the case expression.
1093641Speter 	     *	also, put expression into a register
110926Speter 	     *	save its c-type and jump to the code to do the switch.
111763Speter 	     */
1123641Speter 	exprctype = p2type( exprtype );
11315936Smckusick 	exprnlp = tmpalloc( (long) (sizeof (long)), nl + T4INT , NOREG );
11415936Smckusick 	putRV((char *) 0 , cbn , exprnlp -> value[ NL_OFFS ] ,
1153830Speter 			exprnlp -> extra_flags , P2INT );
11615936Smckusick 	(void) rvalue( tcase->expr , NLNIL , RREQ );
11715936Smckusick 	sconv((int) exprctype, (int) P2INT);
1183641Speter 	putop( P2ASSIGN , P2INT );
119926Speter 	putop( P2FORCE , P2INT );
120926Speter 	putdot( filename , line );
121926Speter 	swlabel = getlab();
12215936Smckusick 	putjbr( (long) swlabel );
123926Speter     }
124926Speter 	/*
125926Speter 	 *  count the number of cases
126926Speter 	 *  and allocate table for cases, lines, and labels
127926Speter 	 *  default case goes in ctab[0].
128926Speter 	 */
129926Speter     count = 1;
13015936Smckusick     for ( cstatlp = tcase->stmnt_list ; cstatlp != TR_NIL ;
13115936Smckusick 		cstatlp = cstatlp->list_node.next ) {
13215936Smckusick 	cstatp = cstatlp->list_node.list;
13315936Smckusick 	if ( cstatp == TR_NIL ) {
134926Speter 	    continue;
135763Speter 	}
13615936Smckusick 	for ( casep = cstatp->c_stmnt.const_list ; casep != TR_NIL ;
13715936Smckusick 			casep = casep->list_node.next ) {
138926Speter 	    count++;
139763Speter 	}
140926Speter     }
141926Speter 	/*
142926Speter 	 */
143926Speter     ctab = (struct ct *) malloc( count * sizeof( struct ct ) );
144926Speter     if ( ctab == (struct ct *) 0 ) {
145926Speter 	error("Ran out of memory (case)");
146926Speter 	pexit( DIED );
147926Speter     }
148926Speter 	/*
149926Speter 	 *  pick up default label and label for after case statement.
150926Speter 	 */
15115936Smckusick     ctab[0].clabel = (int) getlab();
152926Speter     endlabel = getlab();
153926Speter 	/*
154926Speter 	 *  generate code for each case
155926Speter 	 *  filling in ctab for each.
156926Speter 	 *  nr is for error if no case falls out bottom.
157926Speter 	 */
15815936Smckusick     nr = TRUE;;
159926Speter     count = 0;
16015936Smckusick     for ( cstatlp = tcase->stmnt_list ; cstatlp != TR_NIL ;
16115936Smckusick 		cstatlp = cstatlp->list_node.next ) {
16215936Smckusick 	cstatp = cstatlp->list_node.list;
16315936Smckusick 	if ( cstatp == TR_NIL ) {
164926Speter 	    continue;
165926Speter 	}
16615936Smckusick 	line = cstatp->c_stmnt.line_no;
167926Speter 	label = getlab();
16815936Smckusick 	for ( casep = cstatp->c_stmnt.const_list ; casep != TR_NIL ;
16915936Smckusick 			casep = casep->list_node.next ) {
17015936Smckusick 	    gconst( casep->list_node.list );
17115936Smckusick 	    if( exprtype == NLNIL || con.ctype == NIL ) {
172763Speter 		continue;
173763Speter 	    }
17415936Smckusick 	    if ( incompat( con.ctype , exprtype , TR_NIL ) ) {
175926Speter 		cerror("Case label type clashed with case selector expression type");
176926Speter 		continue;
177763Speter 	    }
178926Speter 	    if ( con.crval < low || con.crval > high ) {
179926Speter 		error("Case label out of range");
180926Speter 		continue;
181763Speter 	    }
182926Speter 	    count++;
183926Speter 	    ctab[ count ].cconst = con.crval;
184926Speter 	    ctab[ count ].cline = line;
18515936Smckusick 	    ctab[ count ].clabel = (int) label;
186763Speter 	}
187763Speter 	    /*
188926Speter 	     *	put out the statement
189763Speter 	     */
19015936Smckusick 	(void) putlab( label );
191926Speter 	putcnt();
192926Speter 	level++;
19315936Smckusick 	statement( cstatp->c_stmnt.stmnt );
19415936Smckusick 	nr = (nr && noreach)?TRUE:FALSE;
19515936Smckusick 	noreach = FALSE;
196926Speter 	level--;
197926Speter 	if (gotos[cbn]) {
198926Speter 		ungoto();
199763Speter 	}
20015936Smckusick 	putjbr( (long) endlabel );
201763Speter     }
2021278Speter     noreach = nr;
203926Speter 	/*
204926Speter 	 *	default action is to call error
205926Speter 	 */
20615936Smckusick     (void) putlab( (char *) ctab[0].clabel );
2075663Smckusic     putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR ) , "_CASERNG" );
20815936Smckusick     putRV((char *) 0 , cbn , exprnlp -> value[ NL_OFFS ] ,
2093830Speter 		    exprnlp -> extra_flags , P2INT );
210926Speter     putop( P2CALL , P2INT );
211926Speter     putdot( filename , line );
212926Speter 	/*
213926Speter 	 *  sort the cases
214926Speter 	 */
215926Speter     qsort( &ctab[1] , count , sizeof (struct ct) , casecmp );
216926Speter 	/*
217926Speter 	 *  check for duplicates
218926Speter 	 */
2191278Speter     dupcases = FALSE;
220926Speter     for ( ctp = &ctab[1] ; ctp < &ctab[ count ] ; ctp++ ) {
221926Speter 	if ( ctp[0].cconst == ctp[1].cconst ) {
2221278Speter 	    error("Multiply defined label in case, lines %d and %d" ,
22315936Smckusick 		    (char *) ctp[0].cline , (char *) ctp[1].cline );
2241278Speter 	    dupcases = TRUE;
225926Speter 	}
226926Speter     }
2271278Speter     if ( dupcases ) {
2281278Speter 	return;
2291278Speter     }
230926Speter 	/*
231926Speter 	 *  choose a switch algorithm and implement it:
232926Speter 	 *	direct switch	>= 1/3 full and >= 4 cases.
233926Speter 	 *	binary switch	not direct switch and > 8 cases.
234926Speter 	 *	ifthenelse	not direct or binary switch.
235926Speter 	 */
23615936Smckusick     (void) putlab( swlabel );
237926Speter     if ( ctab[ count ].cconst - ctab[1].cconst < 3 * count && count >= 4 ) {
238926Speter 	directsw( ctab , count );
239926Speter     } else if ( count > 8 ) {
240926Speter 	binarysw( ctab , count );
241926Speter     } else {
242926Speter 	itesw( ctab , count );
243926Speter     }
24415936Smckusick     (void) putlab( endlabel );
245926Speter     if ( goc != gocnt ) {
246926Speter 	    putcnt();
247926Speter     }
248926Speter }
249763Speter 
250926Speter     /*
251926Speter      *	direct switch
252926Speter      */
253926Speter directsw( ctab , count )
254926Speter     struct ct	*ctab;
255926Speter     int		count;
256926Speter {
25715936Smckusick     int		fromlabel = (int) getlab();
258926Speter     long	i;
259926Speter     long	j;
260926Speter 
26110378Speter #   ifdef vax
26212968Smckusick 	if (opt('J')) {
26310378Speter 	    /*
26412968Smckusick 	     *	We have a table of absolute addresses.
26512968Smckusick 	     *
26612968Smckusick 	     *	subl2	to make r0 a 0-origin byte offset.
26710378Speter 	     *	cmpl	check against upper limit.
268*17425Smckusick 	     *	jlssu	error if out of bounds.
26912968Smckusick 	     *	ashl	to make r0 a 0-origin long offset,
27010378Speter 	     *	jmp	and indirect through it.
27110378Speter 	     */
27215936Smckusick 	    putprintf("	subl2	$%d,%s", 0, (int) ctab[1].cconst, (int) FORCENAME);
27312968Smckusick 	    putprintf("	cmpl	$%d,%s", 0,
27415936Smckusick 			(int) (ctab[count].cconst - ctab[1].cconst),
27515936Smckusick 			(int) FORCENAME);
276*17425Smckusick 	    putprintf("	jlssu	%s%d", 0, (int) LABELPREFIX, ctab[0].clabel);
27715936Smckusick 	    putprintf("	ashl	$2,%s,%s", 0, (int) FORCENAME, (int) FORCENAME);
27812968Smckusick 	    putprintf("	jmp	*%s%d(%s)", 0,
27915936Smckusick 		    (int) LABELPREFIX, fromlabel, (int) FORCENAME);
28012968Smckusick 	} else {
28112968Smckusick 	    /*
28212968Smckusick 	     *	We can use the VAX casel instruction with a table
28312968Smckusick 	     *	of short relative offsets.
28412968Smckusick 	     */
28515936Smckusick 	    putprintf("	casel	%s,$%d,$%d" , 0 , (int) FORCENAME ,
28615936Smckusick 		    (int) ctab[1].cconst ,
28715936Smckusick 		    (int) (ctab[ count ].cconst - ctab[1].cconst ));
28812968Smckusick 	}
28912968Smckusick #   endif vax
29012968Smckusick #   ifdef mc68000
29112968Smckusick 	/*
29212968Smckusick 	 *	subl	to make d0 a 0-origin byte offset.
29312968Smckusick 	 *	cmpl	check against upper limit.
29412968Smckusick 	 *	bhi	error if out of bounds.
29512968Smckusick 	 */
29610378Speter 	putprintf("	subl	#%d,%s", 0, ctab[1].cconst, FORCENAME);
29710378Speter 	putprintf("	cmpl	#%d,%s", 0,
29810378Speter 		ctab[count].cconst - ctab[1].cconst, FORCENAME);
29910378Speter 	putprintf("	bhi	%s%d", 0, LABELPREFIX, ctab[0].clabel);
30012968Smckusick 	if (opt('J')) {
30112968Smckusick 	    /*
30212968Smckusick 	     *	We have a table of absolute addresses.
30312968Smckusick 	     *
30412968Smckusick 	     *	asll	to make d0 a 0-origin long offset.
30512968Smckusick 	     *	movl	pick up a jump-table entry
30612968Smckusick 	     *	jmp	and indirect through it.
30712968Smckusick 	     */
30812968Smckusick 	    putprintf("	asll	#2,%s", 0, FORCENAME, FORCENAME);
30912968Smckusick 	    putprintf("	movl	pc@(4,%s:l),%s", 0, FORCENAME, ADDRTEMP);
31012968Smckusick 	    putprintf("	jmp	%s@", 0, ADDRTEMP);
31112968Smckusick 	} else {
31212968Smckusick 	    /*
31312968Smckusick 	     *	We have a table of relative addresses.
31412968Smckusick 	     *
31512968Smckusick 	     *	addw	to make d0 a 0-origin word offset.
31612968Smckusick 	     *	movw	pick up a jump-table entry
31712968Smckusick 	     *	jmp	and indirect through it.
31812968Smckusick 	     */
31912968Smckusick 	    putprintf("	addw	%s,%s", 0, FORCENAME, FORCENAME);
32012968Smckusick 	    putprintf("	movw	pc@(6,%s:w),%s", 0, FORCENAME, FORCENAME);
32112968Smckusick 	    putprintf("	jmp	pc@(2,%s:w)", 0, FORCENAME);
32212968Smckusick 	}
32310378Speter #   endif mc68000
32415936Smckusick     (void) putlab( (char *) fromlabel );
325926Speter     i = 1;
326926Speter     j = ctab[1].cconst;
327926Speter     while ( i <= count ) {
328926Speter 	if ( j == ctab[ i ].cconst ) {
32912968Smckusick 	    if (opt('J')) {
33012968Smckusick 		putprintf( "	.long	" , 1 );
33115936Smckusick 		putprintf( PREFIXFORMAT , 0 , (int) LABELPREFIX , ctab[ i ].clabel );
33212968Smckusick 	    } else {
33312968Smckusick 		putprintf( "	.word	" , 1 );
33415936Smckusick 		putprintf( PREFIXFORMAT , 1 , (int) LABELPREFIX , ctab[ i ].clabel );
33512968Smckusick 		putprintf( "-" , 1 );
33615936Smckusick 		putprintf( PREFIXFORMAT , 0 , (int) LABELPREFIX , fromlabel );
33712968Smckusick 	    }
338926Speter 	    i++;
339926Speter 	} else {
34012968Smckusick 	    if (opt('J')) {
34112968Smckusick 		putprintf( "	.long	" , 1 );
34215936Smckusick 		putprintf( PREFIXFORMAT , 0 , (int) LABELPREFIX , ctab[ 0 ].clabel );
34312968Smckusick 	    } else {
34412968Smckusick 		putprintf( "	.word	" , 1 );
34515936Smckusick 		putprintf( PREFIXFORMAT , 1 , (int) LABELPREFIX , ctab[ 0 ].clabel );
34612968Smckusick 		putprintf( "-" , 1 );
34715936Smckusick 		putprintf( PREFIXFORMAT , 0 , (int) LABELPREFIX , fromlabel );
34812968Smckusick 	    }
349926Speter 	}
350926Speter 	j++;
351926Speter     }
35210378Speter #   ifdef vax
35310378Speter 	    /*
35410378Speter 	     *	execution continues here if value not in range of case.
35510378Speter 	     */
35612968Smckusick 	if (!opt('J'))
35715936Smckusick 	    putjbr( (long) ctab[0].clabel );
35810378Speter #   endif vax
359926Speter }
360926Speter 
361926Speter     /*
362926Speter      *	binary switch
363926Speter      *	special case out default label and start recursion.
364926Speter      */
365926Speter binarysw( ctab , count )
366926Speter     struct ct	*ctab;
367926Speter     int		count;
368926Speter {
369926Speter 
370926Speter     bsrecur( ctab[0].clabel , &ctab[0] , count );
371926Speter }
372926Speter 
373926Speter     /*
374926Speter      *	recursive log( count ) search.
375926Speter      */
376926Speter bsrecur( deflabel , ctab , count )
377926Speter     int		deflabel;
378926Speter     struct ct	*ctab;
379926Speter     int		count;
380926Speter {
381926Speter 
382926Speter     if ( count <= 0 ) {
38315936Smckusick 	putjbr((long) deflabel);
384926Speter 	return;
385926Speter     } else if ( count == 1 ) {
38610378Speter #	ifdef vax
38715936Smckusick 	    putprintf("	cmpl	%s,$%d", 0, (int) FORCENAME, (int) ctab[1].cconst);
38815936Smckusick 	    putprintf("	jeql	%s%d", 0, (int) LABELPREFIX, ctab[1].clabel);
38915936Smckusick 	    putjbr((long) deflabel);
39010378Speter #	endif vax
39110378Speter #	ifdef mc68000
39215936Smckusick 	    putprintf("	cmpl	#%d,%s", 0, ctab[1].cconst, (int) FORCENAME);
39315936Smckusick 	    putprintf("	jeq	L%d", 0, (int) LABELPREFIX, ctab[1].clabel);
39415936Smckusick 	    putjbr((long) deflabel);
39510378Speter #	endif mc68000
396926Speter 	return;
397926Speter     } else {
398926Speter 	int	half = ( count + 1 ) / 2;
39915936Smckusick 	int	gtrlabel = (int) getlab();
400926Speter 
40110378Speter #	ifdef vax
40215936Smckusick 	    putprintf("	cmpl	%s,$%d", 0, (int) FORCENAME, (int) ctab[half].cconst);
40315936Smckusick 	    putprintf("	jgtr	%s%d", 0, (int) LABELPREFIX, gtrlabel);
40415936Smckusick 	    putprintf("	jeql	%s%d", 0, (int) LABELPREFIX, ctab[half].clabel);
40510378Speter #	endif vax
40610378Speter #	ifdef mc68000
40715936Smckusick 	    putprintf("	cmpl	#%d,%s", 0, (int) ctab[half].cconst, (int) FORCENAME);
40815936Smckusick 	    putprintf("	jgt	%s%d", 0, (int) LABELPREFIX, gtrlabel);
40915936Smckusick 	    putprintf("	jeq	%s%d", 0, (int) LABELPREFIX, ctab[half].clabel);
41010378Speter #	endif mc68000
411926Speter 	bsrecur( deflabel , &ctab[0] , half - 1 );
41215936Smckusick 	(void) putlab((char *) gtrlabel);
413926Speter 	bsrecur( deflabel , &ctab[ half ] , count - half );
414926Speter 	return;
415926Speter     }
416926Speter }
417926Speter 
418926Speter itesw( ctab , count )
419926Speter     struct ct	*ctab;
420926Speter     int		count;
421926Speter {
422926Speter     int	i;
423926Speter 
424926Speter     for ( i = 1 ; i <= count ; i++ ) {
42510378Speter #	ifdef vax
42615936Smckusick 	    putprintf("	cmpl	%s,$%d", 0, (int) FORCENAME, (int) ctab[i].cconst);
42715936Smckusick 	    putprintf("	jeql	%s%d", 0, (int) LABELPREFIX, ctab[i].clabel);
42810378Speter #	endif vax
42910378Speter #	ifdef mc68000
43015936Smckusick 	    putprintf("	cmpl	#%d,%s", 0, (int) ctab[i].cconst, (int) FORCENAME);
43115936Smckusick 	    putprintf("	jeq	%s%d", 0, (int) LABELPREFIX, ctab[i].clabel);
43210378Speter #	endif mc68000
433926Speter     }
43415936Smckusick     putjbr((long) ctab[0].clabel);
435926Speter     return;
436926Speter }
437926Speter int
438926Speter casecmp( this , that )
439926Speter     struct ct 	*this;
440926Speter     struct ct 	*that;
441926Speter {
442926Speter     if ( this -> cconst < that -> cconst ) {
443926Speter 	return -1;
444926Speter     } else if ( this -> cconst > that -> cconst ) {
445926Speter 	return 1;
446926Speter     } else {
447926Speter 	return 0;
448926Speter     }
449926Speter }
450763Speter #endif PC
451