Subversion Repositories tendra.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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