Subversion Repositories tendra.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 7u83 1
/*
2
    		 Crown Copyright (c) 1997
3
 
4
    This TenDRA(r) Computer Program is subject to Copyright
5
    owned by the United Kingdom Secretary of State for Defence
6
    acting through the Defence Evaluation and Research Agency
7
    (DERA).  It is made available to Recipients with a
8
    royalty-free licence for its use, reproduction, transfer
9
    to other parties and amendment for any purpose not excluding
10
    product development provided that any such use et cetera
11
    shall be deemed to be acceptance of the following conditions:-
12
 
13
        (1) Its Recipients shall ensure that this Notice is
14
        reproduced upon any copies or amended versions of it;
15
 
16
        (2) Any amended version of it shall be clearly marked to
17
        show both the nature of and the organisation responsible
18
        for the relevant amendment or amendments;
19
 
20
        (3) Its onward transfer from a recipient to another
21
        party shall be deemed to be that party's acceptance of
22
        these conditions;
23
 
24
        (4) DERA gives no warranty or assurance as to its
25
        quality or suitability for any purpose and DERA accepts
26
        no liability whatsoever in relation to any use to which
27
        it may be put.
28
*/
29
 
30
 
31
 
32
 
33
/*
34
			    VERSION INFORMATION
35
			    ===================
36
 
37
--------------------------------------------------------------------------
38
$Header: /u/g/release/CVSROOT/Source/src/installers/sparc/common/muldvrem.c,v 1.1.1.1 1998/01/17 15:55:55 release Exp $
39
--------------------------------------------------------------------------
40
$Log: muldvrem.c,v $
41
 * Revision 1.1.1.1  1998/01/17  15:55:55  release
42
 * First version to be checked into rolling release.
43
 *
44
 * Revision 1.11  1997/08/23  13:54:14  pwe
45
 * initial ANDF-DE
46
 *
47
 * Revision 1.10  1996/11/06  15:59:17  pwe
48
 * correct previous multneeds correction
49
 *
50
 * Revision 1.9  1996/11/06  11:18:50  pwe
51
 * multneeds correction in case of error treatment
52
 *
53
 * Revision 1.8  1996/06/20  14:20:59  john
54
 * Fixed special division
55
 *
56
 * Revision 1.7  1996/04/17  08:25:54  john
57
 * Changed div2 trap treatment
58
 *
59
 * Revision 1.6  1995/12/15  10:25:37  john
60
 * Portability changes
61
 *
62
 * Revision 1.5  1995/09/19  14:31:42  john
63
 * Changed treatment of trap error handling for division
64
 *
65
 * Revision 1.4  1995/07/14  16:32:26  john
66
 * Changes for new error handling
67
 *
68
 * Revision 1.3  1995/06/29  08:20:03  john
69
 * Reformatting
70
 *
71
 * Revision 1.2  1995/05/26  12:59:51  john
72
 * Reformatting
73
 *
74
 * Revision 1.1.1.1  1995/03/13  10:18:45  john
75
 * Entered into CVS
76
 *
77
 * Revision 1.5  1994/12/01  13:26:38  djch
78
 * movecont is a function call, so add to is_muldivrem
79
 *
80
 * Revision 1.4  1994/07/07  16:11:33  djch
81
 * Jul94 tape
82
 *
83
 * Revision 1.3  1994/06/17  14:54:05  djch
84
 * removed other_div, since divs can nest.
85
 * added div0, rem0, and the easy case of sra for signed div1.
86
 *
87
 * Revision 1.2  1994/05/13  12:59:22  djch
88
 * Incorporates improvements from expt version
89
 * moved two asserts (which likediv may violate)
90
 *
91
 * Revision 1.1  1994/05/03  14:49:45  djch
92
 * Initial revision
93
 *
94
 * Revision 1.6  93/09/27  14:49:35  14:49:35  ra (Robert Andrews)
95
 * clear_sun_call_divrem_regs is now global rather than static.  Extended
96
 * is_muldivrem_call to deal with those long double operations which
97
 * are implemented using system calls.
98
 * 
99
 * Revision 1.5  93/08/27  11:32:37  11:32:37  ra (Robert Andrews)
100
 * Added a number of explicit integer casts.  Use pnset etc to set
101
 * needs properties.
102
 * 
103
 * Revision 1.4  93/08/18  11:13:26  11:13:26  ra (Robert Andrews)
104
 * Only the whitespace has changed ...
105
 * 
106
 * Revision 1.3  93/08/13  14:41:32  14:41:32  ra (Robert Andrews)
107
 * Reformatted.
108
 * 
109
 * Revision 1.2  93/07/12  15:15:41  15:15:41  ra (Robert Andrews)
110
 * Added partial support for div1 and rem1.  Changed system calls to use
111
 * call_special_routine.
112
 * 
113
 * Revision 1.1  93/06/24  14:58:50  14:58:50  ra (Robert Andrews)
114
 * Initial revision
115
 * 
116
--------------------------------------------------------------------------
117
*/
118
 
119
 
120
#define SPARCTRANS_CODE
121
#include "config.h"
122
#include "common_types.h"
123
#include "myassert.h"
124
#include "needscan.h"
125
#include "addrtypes.h"
126
#include "tags.h"
127
#include "expmacs.h"
128
#include "installtypes.h"
129
#include "exp.h"
130
#include "exptypes.h"
131
#include "maxminmacs.h"
132
#include "shapemacs.h"
133
#include "proctypes.h"
134
#include "eval.h"
135
#include "move.h"
136
#include "oprators.h"
137
#include "comment.h"
138
#include "getregs.h"
139
#include "guard.h"
140
#include "locate.h"
141
#include "codehere.h"
142
#include "inst_fmt.h"
143
#include "sparcins.h"
144
#include "bitsmacs.h"
145
#include "labels.h"
146
#include "regexps.h"
147
#include "special.h"
148
#include "regmacs.h"
149
#include "needscan.h"
150
#include "translat.h"
151
#include "muldvrem.h"
152
#include "makecode.h"
153
#include "externs.h"
154
/*
155
  NUMBER OF BITS IN A WORD
156
*/
157
 
158
#define BITS_PER_WORD		32
159
 
160
 
161
/*
162
  MULTIPLICATION LIMITS
163
  MAX_MUL_POW2_OFFSET is the maximum m such that 2**n +/- m is a 
164
  simple multiplication.  NOT_MUL_CONST_SIMPLE is any value larger 
165
  than this and is used as an error return.
166
*/
167
 
168
#define MAX_MUL_POW2_OFFSET	2
169
#define NOT_MUL_CONST_SIMPLE	( MAX_MUL_POW2_OFFSET + 1 )
170
 
171
 
172
/*
173
  IS c A POWER OF TWO?
174
*/
175
 
176
#define IS_POW2( c )	( ( c ) != 0 && ( ( c ) & ( ( c ) - 1 ) ) == 0 )
177
 
178
 
179
/*
180
  GIVEN A POWER OF TWO, c, FIND n WHERE c = 2**n
181
*/
182
 
183
#define IS_TRAP 1
184
 
185
static int bit_no 
186
    PROTO_N ( ( c ) )
187
    PROTO_T ( long c ){
188
  int n ;
189
  unsigned long m ;
190
  assert ( IS_POW2 ( c ) ) ;
191
  for ( m = 1, n = 0 ; m != ( unsigned long ) c ; m = m << 1 ) n++ ;
192
  return ( n ) ;
193
}	
194
 
195
 
196
/*
197
  VERSION OF rir_ins WITH CAST
198
*/
199
 
200
#define rcr_ins( a, b, c, d )	rir_ins ( a, b, ( long ) ( c ), d )
201
 
202
 
203
#if 0
204
/*
205
  CLEAR REGISTERS USED BY MULTIPLICATION SYSTEM CALL ETC
206
 
207
  According to the System V ABI only registers %o0,...,%o7 are 
208
  clobbered by the system calls .mul, .div etc.  However :
209
  1.  SunOS 4.1.1 does not follow SPARC ABI.
210
  2.  Even if it did, the assembler -O optimiser does not regard
211
  %g1..%g7 as alive after a call and so does not preserve them,
212
  or renumber them as needed after any call, even to .mul.
213
  Note that it does regard float regs alive after a call.
214
*/
215
 
216
static void clear_abi_call_muldivrem_regs 
217
    PROTO_N ( ( sp ) )
218
    PROTO_T ( space sp ){
219
  int r ;
220
  for ( r = R_O0 ; r != R_O7 + 1 ; r++ ) {
221
    /* grab remaining param regs for safety test for bad code */
222
    if ( !( r == R_O0 || r == R_TMP || r == R_SP ) ) {
223
      /* already done or special */
224
      sp = needreg ( r, sp ) ;
225
    }
226
    /* clear regs modified by .mul/.umul */
227
    clear_reg ( r ) ;
228
  }
229
  return ;
230
}
231
#endif
232
 
233
 
234
/*
235
  CLEAR REGISTERS ACTUALLY USED BY MULTIPLICATION SYSTEM CALL ETC
236
 
237
  This is the version of the routine above which reflect reality.
238
  Registers %o0,...,%o7 and %g1,...,%g_reg_max are clobbered.
239
*/
240
 
241
void clear_sun_call_divrem_regs 
242
    PROTO_N ( ( sp ) )
243
    PROTO_T ( space sp ){
244
  int r ;
245
  for ( r = R_G1 ; r != R_O7 + 1 ;
246
	  r = ( ( r == R_G0 + g_reg_max ) ? R_O0 : r + 1 ) ) {
247
	/* grab remaining param regs for safety test for bad code */
248
    if ( !( r == R_O0 || r == R_TMP || r == R_SP ) ) {
249
      /* already done or special */
250
      sp = needreg ( r, sp ) ;
251
    }
252
    /* clear regs modified by .mul/.umul */
253
    clear_reg ( r ) ;
254
  }
255
  return ;
256
}
257
 
258
 
259
/*
260
  CALL A MULTIPLICATION/DIVISION/REMAINDER SYSTEM ROUTINE
261
*/
262
 
263
int call_muldivrem 
264
    PROTO_N ( ( lhs, rhs, sp, proc, err_t ) )
265
    PROTO_T ( exp lhs X exp rhs X space sp X int proc X int err_t ){
266
  int lhs_reg = -1;
267
  int rhs_reg = -1;
268
  if(err_t) {
269
    /* division has error treatment, so check -MAXINT-1/-1 */
270
    if((name(sh(lhs)) == slonghd) &&
271
       ( (name(lhs) !=val_tag) || (no(lhs) == -0x80000000)) &&
272
       ( (name(rhs) != val_tag) || (no(rhs) == -1))) {
273
      int ok_lab = new_label();
274
      lhs_reg = reg_operand(lhs,sp);
275
      rhs_reg = reg_operand(rhs,guardreg(lhs_reg,sp));
276
      if(name(rhs) == val_tag) {
277
	if(no(rhs) != -1) {
278
	  uncond_ins(i_b,ok_lab);
279
	}
280
      }
281
      if(name(lhs) == val_tag) {
282
	if(no(lhs) != -0x80000000) {
283
	  uncond_ins(i_b,ok_lab);
284
	}
285
      }
286
      condri_ins(i_bne,rhs_reg,-1,ok_lab);
287
      condri_ins(i_bne,lhs_reg,-0x80000000,ok_lab);
288
      if(err_t == IS_TRAP){
289
	do_exception(f_overflow);
290
      }
291
      else{
292
	uncond_ins(i_b,-err_t);
293
      }
294
      set_label(ok_lab);
295
    }
296
  }
297
  if (lhs_reg != R_O0) reg_operand_here ( lhs, sp, R_O0 ) ;
298
  sp = needreg ( R_O0, sp ) ;
299
  if (rhs_reg != R_O1) reg_operand_here ( rhs, sp, R_O1 ) ;
300
  call_special_routine ( proc ) ;
301
  clear_sun_call_divrem_regs ( sp ) ;
302
  /* result left in R_O0 */
303
  return ( R_O0 ) ;
304
}
305
 
306
 
307
/*
308
  GENERATE CODE FOR MULTIPLICATION BY A COMPLEX CONSTANT
309
  This algorithm is not optimal, but it's not bad.
310
*/
311
 
312
static void mul_const_complex 
313
    PROTO_N ( ( src, constval, dest, sp, sgned ) )
314
    PROTO_T ( int src X long constval X int dest X space sp X bool sgned ){
315
  struct {
316
    unsigned char bsl ;		/* bit-string of 1's length */
317
    unsigned char shift ;		/* shift from right of word */
318
  } bs_tab [ BITS_PER_WORD / 2 ] ;
319
 
320
  int bs_tab_len = 0 ;
321
  int bsl_1_tab = -1 ;
322
  int max_bsl = 0 ;
323
  /* special case ~0 cannot be handled by the general algorithm */
324
  if ( constval == ~0 ) {
325
    if ( sgned ) {
326
      /* X * -1 => -X */
327
      assert ( constval == -1 ) ;
328
      rr_ins ( i_neg, src, dest ) ;
329
    } 
330
    else {
331
      rr_ins ( i_neg, src, dest ) ;
332
    }
333
    return ;
334
  }
335
  /* set up bs_tab from constval */
336
  {
337
    unsigned long c = ( unsigned long ) constval ;
338
    int bsl = 0, sby ;
339
    for ( sby = 0 ; sby <= BITS_PER_WORD ; sby++, c >>= 1 ) {
340
      if ( c & 1 ) {
341
	bsl++ ;
342
      } 
343
      else if ( bsl != 0 ) {
344
	/* a complete all-1's bit-string */
345
	assert ( bs_tab_len < BITS_PER_WORD / 2 ) ;
346
	bs_tab [ bs_tab_len ].bsl = ( unsigned char ) bsl ;
347
	bs_tab [ bs_tab_len ].shift = ( unsigned char ) ( sby - bsl ) ;
348
	if ( bsl == 1 ) bsl_1_tab = bs_tab_len ;
349
	if ( bsl > max_bsl ) max_bsl = bsl ;
350
	bs_tab_len++ ;
351
	bsl = 0 ;
352
      }
353
    }
354
  }
355
  assert ( bs_tab_len > 0 ) ;	/* shouldn't be here otherwise */
356
  assert ( max_bsl >= 1 ) ;
357
  assert ( max_bsl <= 31 ) ;	/* shifts by 32 don't work */
358
 
359
    /* generate the code */
360
  {
361
    int bsl ;
362
    int bsl_laststep_tab ;
363
    int tmp = R_TMP ;
364
    int accum ;
365
    bool accum_init = 0 ;
366
 
367
    /* allocate regs */
368
    assert ( src != R_TMP ) ;
369
    assert ( dest != R_TMP ) ;
370
 
371
    if ( src != dest ) {
372
      accum = dest ;
373
    } 
374
    else {
375
      accum = getreg ( sp.fixed ) ;
376
    }
377
    assert ( src != accum ) ;
378
 
379
    /* init accum if useful */
380
    if ( bsl_1_tab >= 0 && bs_tab [ bsl_1_tab ].shift != 0 ) {
381
      /* Usefully do one of the 1 bit strings with simple shift to
382
	 accum.  If left to general algorithm 2 instructions, shift
383
	 and move/add, would often be used */
384
      assert ( bs_tab [ bsl_1_tab ].bsl == 1 ) ;
385
      rcr_ins ( i_sll, src, bs_tab [ bsl_1_tab ].shift, accum ) ;
386
      bs_tab [ bsl_1_tab ].bsl = 0 ;
387
      accum_init = 1 ;
388
    }
389
 
390
    /* find last cond generation step, so we can move to dest then */
391
    bsl_laststep_tab = -1 ;
392
    for ( bsl = max_bsl ; bsl > 0 ; bsl-- ) {
393
      int i ;
394
      for ( i = 0 ; i < bs_tab_len ; i++ ) {
395
	if ( bs_tab [i].bsl == (unsigned char)bsl ) bsl_laststep_tab = i ;
396
      }
397
    }
398
    assert ( bsl_laststep_tab != -1 ) ;
399
 
400
    /* accumulate handle all bit strings of same length together, so
401
       'src * ( ( 2 ** bsl ) - 1 )' can be shared */
402
    for ( bsl = max_bsl ; bsl > 0 ; bsl-- ) {
403
      int i ;
404
      int tmp_shifted ;
405
      bool found_bsl = 0 ;
406
      for ( i = 0 ; i < bs_tab_len ; i++ ) {
407
	if ( bs_tab [i].bsl == (unsigned char)bsl ) {
408
	  int to_accum_reg ;
409
	  int step_accum_dest = ( i == bsl_laststep_tab ?
410
				  dest : accum ) ;
411
	  assert ( accum != R_NO_REG ) ;
412
	  /* amount to accum into tmp reg */
413
	  if ( bsl == 1 ) {
414
	    /* accumulate src << shift */
415
	    if ( bs_tab [i].shift == 0 ) {
416
	      /* simple add */
417
	      to_accum_reg = src ;
418
	    } 
419
	    else {
420
	      /* simple shift and add */
421
	      rcr_ins ( i_sll, src, bs_tab [i].shift, tmp ) ;
422
	      to_accum_reg = tmp ;
423
	    }
424
	  } 
425
	  else {
426
	    /* accumulate ( src * ( ( 2**bsl ) - 1 ) ) << shift */
427
	    if ( !found_bsl ) {
428
	      rcr_ins ( i_sll, src, bsl, tmp ) ;
429
	      rrr_ins ( i_sub, tmp, src, tmp ) ;
430
	      tmp_shifted = 0 ;
431
	      found_bsl = 1 ;
432
	    }
433
	    if ( bs_tab [i].shift != (unsigned char)tmp_shifted ) {
434
	      int extra_shift = bs_tab [i].shift - (unsigned char)tmp_shifted ;
435
	      assert ( extra_shift > 0 && extra_shift <= 31 ) ;
436
	      rcr_ins ( i_sll, tmp, extra_shift, tmp ) ;
437
	      tmp_shifted += extra_shift ;
438
	    }
439
	    /* else tmp already shifted to correct position */
440
	    to_accum_reg = tmp ;
441
	  }
442
	  /* accumulate into accum, or on last step to dest */
443
	  if ( accum_init ) {
444
	    rrr_ins ( i_add, accum, to_accum_reg,step_accum_dest ) ;
445
	  } 	
446
	  else {
447
	    rr_ins ( i_mov, to_accum_reg, step_accum_dest ) ;
448
	    accum_init = 1 ;
449
	  }
450
	  if ( i == bsl_laststep_tab ) {
451
	    /* error check */
452
	    accum = R_NO_REG ;
453
	  }
454
	}
455
      }
456
    }
457
    assert ( accum_init ) ;
458
    assert ( accum == R_NO_REG ) ;
459
    /* result in dest, due to step_accum_dest above */
460
  }
461
  return ;
462
}
463
 
464
 
465
/*
466
  IS A CONSTANT SIMPLE FOR MULTIPLICATION?
467
 
468
  A simple constant is one of the form +/- 2**n +/- m where m is at 
469
  most MAX_MUL_POW2_OFFSET.  If constval is of this form, m is 
470
  returned, otherwise NOT_MUL_CONST_SIMPLE is returned.
471
*/
472
 
473
static int offset_mul_const_simple 
474
    PROTO_N ( ( constval, sgned ) )
475
    PROTO_T ( long constval X bool sgned ){
476
  int i ;
477
  if ( constval < 0 ) {
478
    if ( sgned ) {
479
      constval = -constval ;
480
    } 
481
    else {
482
      /* very rare case */
483
      return ( NOT_MUL_CONST_SIMPLE ) ;
484
    }
485
  }
486
  for ( i = 0 ; i <= MAX_MUL_POW2_OFFSET ; i++ ) {
487
    long c ;	/* power of two close to constval */
488
    /* check for add offsets, avoiding overflow confusion */
489
    c = constval - i ;
490
    if ( IS_POW2 ( c ) && c + i == constval ) return ( i ) ;
491
    /* check for sub offset of 1 only, avoiding overflow confusion */
492
    if ( i == 1 ) {
493
      c = constval + i ;
494
      if ( IS_POW2 ( c ) && c - i == constval ) return ( -i ) ;
495
    }
496
  }
497
  return ( NOT_MUL_CONST_SIMPLE ) ;
498
}
499
 
500
 
501
/*
502
  MULTIPLICATION BY A SIMPLE CONSTANT
503
*/
504
 
505
static void mul_const_simple 
506
    PROTO_N ( ( src, constval, dest, sgned ) )
507
    PROTO_T ( int src X long constval X int dest X bool sgned ){
508
  long c ;		/* power of two close to constval */
509
  int add_sub ;	/* difference from power of two */
510
  int shift_const ;
511
 
512
  if ( sgned && constval < 0 ) {
513
    if ( constval == -1 ) {
514
      /* X * -1 => -X */
515
      rr_ins ( i_neg, src, dest ) ;
516
      return ;
517
    }
518
    constval = -constval ;
519
    /* incorrect to modify source */
520
    rr_ins ( i_neg, src, R_TMP ) ;
521
    src = R_TMP ;
522
  }
523
 
524
  if ( constval == 1 ) {
525
    if ( src != dest ){
526
      rr_ins ( i_mov, src, dest ) ;
527
    }
528
    return ;
529
  } 
530
  else if ( constval == 2 ) {
531
    /* use add, which can be peephole optimised to addcc later */
532
    rrr_ins ( i_add, src, src, dest ) ;
533
    return ;
534
  }
535
  add_sub = offset_mul_const_simple ( constval, sgned ) ;
536
  c = constval - add_sub ;
537
 
538
  assert ( constval == c + add_sub ) ;
539
  shift_const = bit_no ( c ) ;
540
  assert ( constval == ( 1 << shift_const ) + add_sub ) ;
541
  if ( add_sub == 0 ) {
542
    rcr_ins ( i_sll, src, shift_const, dest ) ;
543
  } 
544
  else {
545
    /* add_sub != 0 */
546
    int i ;
547
    int n ;		/* number of add_sub instructions */
548
    int inter_reg ;	/* for partial result */
549
    ins_p i_add_sub ;
550
 
551
    if ( add_sub > 0 ) {
552
      i_add_sub = i_add ;
553
      n = add_sub ;
554
    } 
555
    else {
556
      i_add_sub = i_sub ;
557
      n = -add_sub ;
558
    }
559
    if ( src == dest ) {
560
      /* must preserve src for add/sub */
561
      inter_reg = R_TMP ;
562
    } 
563
    else {
564
      inter_reg = dest ;
565
    }
566
    assert ( src != inter_reg ) ;
567
    rcr_ins ( i_sll, src, shift_const, inter_reg ) ;
568
    /* all but final add_sub */
569
    for ( i = 1 ; i < n ; i++ ) {
570
      rrr_ins ( i_add_sub, inter_reg, src, inter_reg ) ;
571
    }
572
 
573
    /* final add_sub to dest reg */
574
    rrr_ins ( i_add_sub, inter_reg, src, dest ) ;
575
  }
576
  return ;
577
}
578
 
579
 
580
/*
581
  CODE GENERATION ROUTINE FOR MULTIPLICATION BY A CONSTANT
582
*/
583
 
584
static void mul_const 
585
    PROTO_N ( ( src, constval, dest, sp, sgned ) )
586
    PROTO_T ( int src X long constval X int dest X space sp X bool sgned ){
587
  if ( constval == 0 ) {
588
    /* rare case not handled by mul_const_X () */
589
    ir_ins ( i_mov, 0, dest ) ;
590
  } 
591
  else if ( offset_mul_const_simple ( constval, sgned ) ==
592
	    NOT_MUL_CONST_SIMPLE ) {
593
    mul_const_complex ( src, constval, dest, sp, sgned ) ;
594
  } 
595
  else {
596
    mul_const_simple ( src, constval, dest, sgned ) ;
597
  }
598
  return ;
599
}
600
 
601
 
602
/*
603
  CODE GENERATION ROUTINE FOR MULTIPLICATION OPERATIONS
604
*/
605
static int do_mul_comm 
606
    PROTO_N ( ( seq, sp, final_reg, sgned ) )
607
    PROTO_T ( exp seq X space sp X int final_reg X bool sgned ){
608
  space nsp ;	
609
  int mul_proc ;
610
  exp arg2 = bro ( seq ) ;
611
  int has_error_treatment = !optop(father(seq));
612
  if ( name ( arg2 ) == val_tag && !has_error_treatment) {
613
    /* const optim */
614
    int lhs_reg = reg_operand ( seq, sp ) ;
615
    sp = guardreg ( lhs_reg, sp ) ;
616
    /* check () & scan () should move const to last */
617
    assert ( last ( arg2 ) ) ;
618
    if ( final_reg == R_NO_REG || final_reg == R_G0) {
619
      /* better code from mul_const if src != dest reg */
620
      final_reg = getreg ( sp.fixed ) ;
621
      sp = guardreg ( final_reg, sp ) ;
622
    }
623
    mul_const ( lhs_reg, ( long ) no ( arg2 ), final_reg, sp, sgned ) ;
624
    return ( final_reg ) ;
625
  }
626
  /* need to call .mul/.umul */
627
  mul_proc = ( sgned ? SPECIAL_MUL : SPECIAL_UMUL ) ;
628
  reg_operand_here ( seq, sp, R_O0 ) ;
629
  nsp = needreg ( R_O0, sp ) ;
630
  for ( ; ; ) {
631
    /* should have break out below by now */
632
    assert ( !last ( seq ) ) ;
633
    seq = bro ( seq ) ;
634
    if ( !has_error_treatment && name ( seq ) == val_tag &&
635
	 offset_mul_const_simple ( ( long ) no ( seq ), sgned ) !=
636
	 NOT_MUL_CONST_SIMPLE) {
637
      /* const optim */
638
      /* check () & scan () should move const to last */
639
      assert ( last ( seq ) ) ;
640
      if ( final_reg == R_NO_REG ) {
641
	/* better code from mul_const if src != dest reg */
642
	final_reg = R_O1 ;
643
      }
644
      mul_const ( R_O0, ( long ) no ( seq ), final_reg, nsp, sgned ) ;
645
      break ;
646
    } 
647
    else {
648
      reg_operand_here ( seq, nsp, R_O1 ) ;
649
      if(has_error_treatment){
650
	rrr_ins(sgned?i_smulcc:i_umulcc,R_O0,R_O1,R_O0);
651
      }
652
      else{
653
	call_special_routine ( mul_proc ) ;
654
      }
655
      clear_sun_call_divrem_regs ( nsp ) ;
656
      if ( last ( seq ) ) {
657
	if ( final_reg == R_NO_REG || final_reg == R_G0 ) {
658
	  final_reg = R_O0 ;
659
	} 
660
	else {
661
	  rr_ins ( i_mov, R_O0, final_reg ) ;
662
	}
663
	break ;
664
      }
665
    }
666
  }
667
  return ( final_reg ) ;
668
}
669
 
670
 
671
/*
672
  FLAG : ALTERNATIVE DIVISION
673
 
674
  There are two division and remainder operations.  In the first the
675
  remainder has the same sign as the denominator, and in the second
676
  the same sign as the numerator.  The second is the default.  This
677
  flag is set to indicate that the first form should be used.
678
*/
679
 
680
/* using a flag is unsafe, lest the lhs itself contains a div. 
681
   Instead recompute otherdiv when needed*/
682
/*static bool other_div = 0 ;*/
683
 
684
 
685
/*
686
  CODE GENERATION ROUTINE FOR DIVISION OPERATIONS
687
*/
688
 
689
static int do_div 
690
    PROTO_N ( ( seq, sp, final_reg, sgned ) )
691
    PROTO_T ( exp seq X space sp X int final_reg X bool sgned ){
692
  int p ;
693
  exp lhs = seq ;
694
  exp rhs = bro ( lhs ) ;
695
  int has_error_treatment = !optop(father(seq)) && !error_treatment_is_trap(father(seq));
696
  int et;
697
  assert ( last ( rhs ) ) ;	/* so bro(rhs) == the div exp  */
698
  if ( !has_error_treatment && name ( rhs ) == val_tag && 
699
       IS_POW2 ( no ( rhs )) && no(rhs) > 0)  {
700
    long constval = no ( rhs ) ;
701
    /* const optim, replace div by 2**n by shift right */
702
    int lhs_reg = reg_operand ( lhs, sp ) ;
703
    int shift_const = bit_no ( constval ) ;
704
    sp = guardreg ( lhs_reg, sp ) ;
705
    if ( final_reg == R_NO_REG ) {
706
      final_reg = getreg ( sp.fixed ) ;
707
      sp = guardreg ( final_reg, sp ) ;
708
    }	
709
    if ( constval == 1 ) {
710
      /* result always lhs */
711
      rr_ins ( i_mov, lhs_reg, final_reg ) ;
712
      return ( final_reg ) ;
713
    }
714
 
715
    if (!sgned) {
716
				/* unsigned, easy, just shift */
717
      rcr_ins ( i_srl, lhs_reg, shift_const, final_reg ) ;
718
      return ( final_reg ) ;
719
    }
720
 
721
    if (name(bro(rhs)) == div2_tag) /* shift and fix up for sgned div2 */
722
    {
723
      /* signed, adjust lhs before shift */
724
      int tmp_reg = R_TMP ;
725
      assert ( shift_const > 0 ) ; /* assumed below */
726
      if ( shift_const - 1 != 0 ) {
727
	rcr_ins ( i_sra, lhs_reg, shift_const - 1, tmp_reg ) ;
728
	rcr_ins ( i_srl, tmp_reg, 32 - shift_const, tmp_reg ) ;
729
      } else {
730
	rcr_ins ( i_srl, lhs_reg, 32 - shift_const, tmp_reg ) ;
731
      }
732
      rrr_ins ( i_add, lhs_reg, tmp_reg, tmp_reg ) ;
733
      rcr_ins ( i_sra, tmp_reg, shift_const, final_reg ) ;
734
      return (final_reg);
735
    } 
736
				/* must be signed div1, a simple shift */
737
    rcr_ins ( i_sra, lhs_reg, shift_const, final_reg ) ;
738
    return ( final_reg ) ;
739
  }
740
  if(0 /*has_error_treatment*/) {
741
    ins_p dop;
742
    reg_operand_here(lhs,sp,R_O0);
743
    reg_operand_here(rhs,sp,R_O1);
744
    if(error_treatment_is_trap(father(seq))){
745
      if(sgned){
746
	dop = i_sdiv;
747
      }
748
      else{
749
	dop = i_udiv;
750
      }
751
    }
752
    else{
753
      if(sgned){
754
	dop = i_sdivcc;
755
      }
756
      else{
757
	dop = i_udivcc;
758
      }
759
    }
760
    rrr_ins(dop,R_O0,R_O1,R_O0);
761
    return R_O0;
762
    /* otherwise need to call .div/.udiv */
763
  }
764
  else if ( sgned && name(bro(rhs)) == div1_tag ) {
765
    p = SPECIAL_DIV1 ;
766
  } 
767
  else {
768
    p = ( sgned ? SPECIAL_DIV2 : SPECIAL_UDIV2 ) ;
769
  }
770
  if(error_treatment_is_trap(father(seq))) {
771
    et = IS_TRAP;
772
  }
773
  else if(has_error_treatment) {
774
    et = -no(son(pt(father(seq))));
775
  }
776
  else {
777
    et = 0;
778
  }
779
  return ( call_muldivrem ( lhs, rhs, sp, p, et ) ) ;
780
}
781
 
782
 
783
/*
784
  CODE GENERATION ROUTINE FOR REMAINDER OPERATIONS
785
*/
786
 
787
static int do_rem 
788
    PROTO_N ( ( seq, sp, final_reg, sgned ) )
789
    PROTO_T ( exp seq X space sp X int final_reg X bool sgned ){
790
  int p ;
791
  exp lhs = seq ;
792
  exp rhs = bro ( lhs ) ;
793
 
794
  assert ( last ( rhs ) ) ;
795
 
796
  if ( name ( rhs ) == val_tag && IS_POW2 ( no ( rhs ) ) && (no(rhs) > 0) ) {
797
    long constval = no ( rhs ) ;
798
 
799
    /* const optim, replace rem by 2**n by and with mask */
800
    int lhs_reg = reg_operand ( lhs, sp ) ;
801
    sp = guardreg ( lhs_reg, sp ) ;
802
 
803
    if ( final_reg == R_NO_REG ) {
804
      final_reg = getreg ( sp.fixed ) ;
805
    }
806
 
807
    if ( constval == 1 ) {
808
      /* result always 0 */
809
      ir_ins ( i_mov, 0, final_reg ) ;
810
      return final_reg;
811
    }
812
    if ( !sgned ) {
813
		/* unsigned by mask */
814
      rcr_ins ( i_and, lhs_reg, constval - 1, final_reg ) ;
815
      return final_reg;
816
    }
817
    if (name(bro(rhs)) == rem2_tag){
818
      /* signed, need to allow for negative lhs. Treat l % c
819
	 as l - ( l / c ) * c */
820
      int tmp_reg = R_TMP ;
821
      int shift_const = bit_no ( constval ) ;
822
      assert ( shift_const > 0 ) ; /* assumed below */
823
      /* do the divide, as in do_div */
824
      if ( shift_const - 1 != 0 ) {
825
	rcr_ins ( i_sra, lhs_reg, shift_const - 1, tmp_reg ) ;
826
	rcr_ins ( i_srl, tmp_reg, 32 - shift_const, tmp_reg ) ;
827
      } 
828
      else {
829
	rcr_ins ( i_srl, lhs_reg, 32 - shift_const, tmp_reg ) ;
830
      }
831
      rrr_ins ( i_add, lhs_reg, tmp_reg, tmp_reg ) ;
832
      rcr_ins ( i_sra, tmp_reg, shift_const, tmp_reg ) ;
833
		/* multiply */
834
      rcr_ins ( i_sll, tmp_reg, shift_const, tmp_reg ) ;
835
      /* subtract */
836
      rrr_ins ( i_sub, lhs_reg, tmp_reg, final_reg ) ;
837
      return final_reg;
838
    }
839
    rcr_ins ( i_and, lhs_reg, constval - 1, final_reg ) ;
840
    return final_reg;
841
  }
842
 
843
  /* otherwise need to call .rem/.urem */
844
  if ( sgned && name(bro(rhs)) == mod_tag) {
845
    p = SPECIAL_REM1 ;
846
  } 
847
  else {
848
    p = ( sgned ? SPECIAL_REM2 : SPECIAL_UREM2 ) ;
849
  }
850
  return ( call_muldivrem ( lhs, rhs, sp, p, 0 ) ) ;
851
}
852
 
853
 
854
/*
855
  FUNCTION TYPE
856
*/
857
 
858
typedef int ( *find_fn ) PROTO_S ( ( exp, space, int, bool ) ) ;
859
 
860
 
861
/*
862
  GENERATE CODE FOR e USING do_fn
863
*/
864
 
865
static int find_reg_and_apply 
866
    PROTO_N ( ( e, sp, dest, sgned, do_fn ) )
867
    PROTO_T ( exp e X space sp X where dest X bool sgned X find_fn do_fn ){
868
  ans a ;
869
  int dest_reg ;
870
  exp seq = son ( e ) ;
871
  /* tidyshort ( dest, sh ( e ) ) ; ??? */
872
  switch ( discrim ( dest.answhere ) ) {
873
    case inreg : {
874
      dest_reg = ( *do_fn ) ( seq, sp, regalt ( dest.answhere ),
875
			    sgned ) ;
876
      break ;
877
    }
878
    case insomereg : {
879
      /* leave ( *do_fn ) () to allocate reg */
880
      int *dr = someregalt ( dest.answhere ) ;
881
      *dr = ( *do_fn ) ( seq, sp, R_NO_REG, sgned ) ;
882
      /* no need for move */
883
      return ( *dr ) ;
884
    }
885
    default : {
886
      /* leave ( *do_fn ) () to allocate reg */
887
      dest_reg = ( *do_fn ) ( seq, sp, R_NO_REG, sgned ) ;
888
      break ;
889
    }
890
  }
891
  assert ( dest_reg != R_NO_REG ) ;
892
  setregalt ( a, dest_reg ) ;
893
  sp = guardreg ( dest_reg, sp ) ;
894
  ( void ) move ( a, dest, sp.fixed, sgned ) ;
895
  return ( dest_reg ) ;
896
}
897
 
898
 
899
/*
900
  GENERATE CODE FOR A MULTIPLICATION OPERATION
901
*/
902
 
903
int do_mul_comm_op 
904
    PROTO_N ( ( e, sp, dest, sgned ) )
905
    PROTO_T ( exp e X space sp X where dest X bool sgned ){
906
  return ( find_reg_and_apply ( e, sp, dest, sgned, do_mul_comm ) ) ;
907
}
908
 
909
 
910
/*
911
  GENERATE CODE FOR A DIVISION OPERATION
912
*/
913
 
914
int do_div_op 
915
    PROTO_N ( ( e, sp, dest, sgned ) )
916
    PROTO_T ( exp e X space sp X where dest X bool sgned ){
917
/*    other_div = ( bool ) ( ( name ( e ) == div1_tag && sgned ) ? 1 : 0 ) ;*/
918
  return ( find_reg_and_apply ( e, sp, dest, sgned, do_div ) ) ;
919
}
920
 
921
 
922
/*
923
  GENERATE CODE FOR A REMAINDER OPERATION
924
*/
925
 
926
int do_rem_op 
927
    PROTO_N ( ( e, sp, dest, sgned ) )
928
    PROTO_T ( exp e X space sp X where dest X bool sgned ){
929
/*    other_div = ( bool ) ( ( name ( e ) == mod_tag && sgned ) ? 1 : 0 ) ;*/
930
  return ( find_reg_and_apply ( e, sp, dest, sgned, do_rem ) ) ;
931
}
932
 
933
 
934
/*
935
  IS AN EXPRESSION IMPLEMENTED BY A SYSTEM CALL?
936
  Multiplications, divisions and remainders, except in simple cases,
937
  are implemented by means of system calls.  This routine checks if
938
  an expression represents one of these calls.
939
*/
940
 
941
bool is_muldivrem_call 
942
    PROTO_N ( ( e ) )
943
    PROTO_T ( exp e ){
944
  switch ( name ( e ) ) {
945
 
946
#if use_long_double
947
    case test_tag:
948
    case chfl_tag:
949
    case round_tag: {
950
      exp s = son ( e ) ;
951
      if ( name ( sh ( s ) ) == doublehd ) return ( 1 ) ;
952
      /* FALL THROUGH */
953
    }
954
    case fplus_tag:
955
    case fminus_tag:
956
    case fmult_tag:
957
    case fdiv_tag:
958
    case fneg_tag:
959
    case fabs_tag:
960
    case float_tag: {
961
      if ( name ( sh ( e ) ) == doublehd ) return ( 1 ) ;
962
      return ( 0 ) ;
963
    }
964
#endif
965
 
966
    case mult_tag:
967
    case offset_mult_tag: {
968
      /*multneeds - simple cases don't need a call */
969
      exp arg2 = bro ( son ( e ) ) ;
970
      if ( last ( arg2 ) && name ( arg2 ) == val_tag && optop(e) ) {
971
	return ( 0 ) ;
972
      }	
973
      return ( 1 ) ;
974
    }
975
    case div0_tag:
976
    case rem0_tag:
977
    case div1_tag:
978
    case mod_tag:
979
    case div2_tag:
980
    case rem2_tag:
981
    case offset_div_tag:
982
    case offset_div_by_int_tag: {
983
      /*remneeds, divneeds - simple cases don't need a call */
984
      exp arg2 = bro ( son ( e ) ) ;
985
      if ( last ( arg2 ) && name ( arg2 ) == val_tag && optop(e) ) {
986
	long constval = no ( arg2 ) ;
987
	if ( constval > 0 && IS_POW2 ( constval ))
988
	  return ( 0 ) ;
989
      }
990
      return ( 1 ) ;
991
    }
992
    case movecont_tag:
993
    return 1;			/* at present */
994
    default: {
995
      return ( 0 ) ;
996
    }
997
  }
998
}
999
 
1000
 
1001
/*
1002
  ESTIMATE NEEDS FOR MULTIPLICATION
1003
*/
1004
 
1005
needs multneeds 
1006
    PROTO_N ( ( e, at ) )
1007
    PROTO_T ( exp * e X exp ** at ){
1008
  needs n ;
1009
  exp arg1 = son ( *e ) ;
1010
  exp arg2 = bro ( arg1 ) ;
1011
  n = likeplus ( e, at ) ;
1012
 
1013
  /* remember that mult may have more than two args after 
1014
     optimisation */
1015
  if ( last ( arg2 ) && name ( arg2 ) == val_tag && optop(*e) ) {
1016
    /* const optim, additional reg only needed where src and dest are
1017
       same reg, in which case it has already been allowed for */
1018
    return ( n ) ;
1019
  }
1020
  /* default, call .mul */
1021
  n.fixneeds = maxfix ;
1022
  pnset ( n, hasproccall ) ;
1023
  return ( n ) ;
1024
}
1025
 
1026
 
1027
/*
1028
  ESTIMATE NEEDS FOR DIVISION
1029
*/
1030
needs divneeds 
1031
    PROTO_N ( ( e, at ) )
1032
    PROTO_T ( exp * e X exp ** at ){
1033
  needs n ;
1034
  exp lhs = son ( *e ) ;
1035
  exp rhs = bro ( lhs ) ;
1036
 
1037
  assert ( last ( rhs ) ) ;	/* after likediv may not be so */
1038
 
1039
  n = likediv ( e, at ) ;
1040
  if ( name ( rhs ) == val_tag && optop(*e) ) {
1041
    long constval = no ( rhs ) ;
1042
    if ( constval > 0 && IS_POW2 ( constval ) ) {
1043
      /* const optim, replace div by shift */
1044
      return ( n ) ;
1045
    }
1046
  }
1047
  /* default, call .div */
1048
  n.fixneeds = maxfix ;
1049
  pnset ( n, hasproccall ) ;
1050
  return ( n ) ;
1051
}
1052
 
1053
 
1054
/*
1055
  ESTIMATE NEEDS FOR REMAINDER
1056
*/
1057
needs remneeds 
1058
    PROTO_N ( ( e, at ) )
1059
    PROTO_T ( exp * e X exp ** at ){
1060
  needs n ;
1061
  exp lhs = son ( *e ) ;
1062
  exp rhs = bro ( lhs ) ;
1063
  assert ( last ( rhs ) ) ;	/* after likediv may not be so */
1064
  n = likediv ( e, at ) ;
1065
  if ( name ( rhs ) == val_tag && optop(*e) ) {
1066
    long constval = no ( rhs ) ;
1067
    if ( constval > 0 && IS_POW2 ( constval ) ) {
1068
      /* const optim of rem by positive, non-zero, 2**n */
1069
      return ( n ) ;
1070
    }
1071
  }
1072
  /* default, call .rem */
1073
  n.fixneeds = maxfix ;
1074
  pnset ( n, hasproccall ) ;
1075
  return ( n ) ;
1076
}