Subversion Repositories tendra.SVN

Rev

Rev 5 | Details | Compare with Previous | 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/move.c,v 1.1.1.1 1998/01/17 15:55:54 release Exp $
39
--------------------------------------------------------------------------
40
$Log: move.c,v $
41
 * Revision 1.1.1.1  1998/01/17  15:55:54  release
42
 * First version to be checked into rolling release.
43
 *
44
 * Revision 1.8  1997/08/23  13:54:10  pwe
45
 * initial ANDF-DE
46
 *
47
 * Revision 1.7  1996/10/03  08:51:12  pwe
48
 * PIC global/large offset, and PIC case guardregs
49
 *
50
 * Revision 1.6  1996/08/22  16:47:05  pwe
51
 * correct accessing for double params
52
 *
53
 * Revision 1.5  1996/02/29  17:39:30  john
54
 * Fix to double load
55
 *
56
 * Revision 1.4  1996/02/27  11:20:28  john
57
 * *** empty log message ***
58
 *
59
 * Revision 1.3  1996/01/24  18:11:05  john
60
 * Removed assertion
61
 *
62
 * Revision 1.2  1995/05/26  12:59:39  john
63
 * Reformatting
64
 *
65
 * Revision 1.1.1.1  1995/03/13  10:18:44  john
66
 * Entered into CVS
67
 *
68
 * Revision 1.7  1994/12/01  13:35:24  djch
69
 * va_alist can generate 4byte aligned doubles on the stack... Always indexed by
70
 * -8, so generated 2 loads for these..
71
 *
72
 * Revision 1.6  1994/07/07  16:11:33  djch
73
 * Jul94 tape
74
 *
75
 * Revision 1.5  1994/06/22  09:52:52  djch
76
 * temporary fix to put temporary bitfields in the bottom of the word - one day
77
 * much of the code could go
78
 *
79
 * Revision 1.4  1994/05/25  14:13:03  djch
80
 * Added CREATE_instore_bits to shut up tcc
81
 *
82
 * Revision 1.3  1994/05/19  08:59:05  djch
83
 * added empty {} to pacify tcc
84
 *
85
 * Revision 1.2  1994/05/13  12:39:32  djch
86
 * Incorporates improvements from expt version
87
 * use new macros from addrtypes.h, added defaults to switchs
88
 *
89
 * Revision 1.1  1994/05/03  14:49:44  djch
90
 * Initial revision
91
 *
92
 * Revision 1.4  93/08/27  11:31:58  11:31:58  ra (Robert Andrews)
93
 * Added a couple of explicit integer casts.
94
 * 
95
 * Revision 1.3  93/07/15  12:32:46  12:32:46  ra (Robert Andrews)
96
 * Reformatted.  Changed critical value for introducing a loop for a large
97
 * move from 8 to 12.
98
 * 
99
 * Revision 1.2  93/07/05  18:22:05  18:22:05  ra (Robert Andrews)
100
 * Reformatted slightly.
101
 * 
102
 * Revision 1.1  93/06/24  14:58:45  14:58:45  ra (Robert Andrews)
103
 * Initial revision
104
 * 
105
--------------------------------------------------------------------------
106
*/
107
 
108
 
109
#define SPARCTRANS_CODE
110
 
111
/*
112
  The procedure move produces code to move a value from a to the
113
  destination dest.  This takes the form of a switch test on the
114
  parameter a (type ans) which is either a reg, freg instore or
115
  bitad value.  In each of the three cases the ans field of the
116
  dest is similarly dealt with to determine the necessary
117
  instructions for the move.  Sizes and alignment are taken from
118
  the ash field of the destination.  The routine returns the
119
  register used if a single word destination is instore, and
120
  NOREG otherwise.
121
*/
122
 
123
#include "config.h"
124
#include "common_types.h"
125
#include "myassert.h"
126
#include "sparcins.h"
127
#include "inst_fmt.h"
128
#include "addrtypes.h"
129
#include "proctypes.h"
130
#include "proc.h"
131
#include "getregs.h"
132
#include "labels.h"
133
#include "comment.h"
134
#include "bitsmacs.h"
135
#include "regmacs.h"
136
#include "maxminmacs.h"
137
#include "makecode.h"
138
#include "flags.h"
139
#include "move.h"
140
 
141
 
142
/*
143
  MAXIMUM SIZE FOR INLINED MOVES
144
*/
145
 
146
#define MAX_STEPS_INLINE_MOVE	12
147
 
148
 
149
/*
150
  BITMASK : NBITMASK ( n ) GIVES n 1'S
151
*/
152
 
153
#define NBITMASK( n )  ((unsigned long)((n)==32 ?~0L:((1<<(n))-1)))
154
 
155
 
156
/*
157
  TYPE REPRESENTING INSTRUCTION PAIRS
158
  Unsigned version, signed version.
159
*/
160
 
161
typedef ins_p ins_sgn_pair [2] ;
162
 
163
 
164
/*
165
  ARRAY OF LOAD INSTRUCTIONS
166
*/
167
 
168
static CONST ins_sgn_pair ld_ins_sz [] = {
169
  { I_NIL, I_NIL },
170
  { i_ldub, i_ldsb },
171
  { i_lduh, i_ldsh },
172
  { I_NIL, I_NIL },
173
  { i_ld, i_ld },
174
  { I_NIL, I_NIL },
175
  { I_NIL, I_NIL },
176
  { I_NIL, I_NIL },
177
  { i_ldd, i_ldd }
178
} ;
179
 
180
 
181
/*
182
  ARRAY OF STORE INSTRUCTIONS
183
*/
184
 
185
static CONST ins_sgn_pair st_ins_sz [] = {
186
  { I_NIL, I_NIL },
187
  { i_stb, i_stb },
188
  { i_sth, i_sth },
189
  { I_NIL, I_NIL },
190
  { i_st, i_st },
191
  { I_NIL, I_NIL },
192
  { I_NIL, I_NIL },
193
  { I_NIL, I_NIL },
194
  { i_std, i_std }
195
} ;
196
 
197
 
198
/*
199
  FIND THE LOAD INSTRUCTION FOR SIZE bits, SIGNEDNESS sgned
200
*/
201
 
202
ins_p i_ld_sz 
203
    PROTO_N ( ( bits, sgned ) )
204
    PROTO_T ( int bits X int sgned ){
205
  return ( ld_ins_sz [ bits / 8 ] [ sgned ] ) ;
206
}
207
 
208
 
209
/*
210
  FIND THE STORE INSTRUCTION FOR SIZE bits (UNSIGNED)
211
*/
212
 
213
ins_p i_st_sz 
214
    PROTO_N ( ( bits ) )
215
    PROTO_T ( int bits ){
216
  return ( st_ins_sz [ bits / 8 ] [0] ) ;
217
}
218
 
219
 
220
/*
221
  LOAD THE ADDRESS REPRESENTED BY is INTO reg
222
*/
223
 
224
void ld_addr 
225
    PROTO_N ( ( is, reg ) )
226
    PROTO_T ( instore is X int reg ){
227
  if ( is.adval ) {
228
    if ( IS_FIXREG ( is.b.base ) ) {
229
      rir_ins ( i_add, is.b.base, is.b.offset, reg ) ;
230
    } else {
231
      set_ins ( is.b, reg ) ;
232
    }
233
  } else {
234
    ld_ins ( i_ld, is.b, reg ) ;
235
  }
236
  return ;
237
}
238
 
239
 
240
/*
241
  LOAD THE ADDRESS REPRESENTED BY is INTO A REGISTER
242
  The register number is returned.  regs gives the registers to 
243
  choose from.
244
*/
245
int addr_reg 
246
    PROTO_N ( ( is, regs ) )
247
    PROTO_T ( instore is X long regs ){
248
  int r ;
249
  if ( is.adval && IS_FIXREG ( is.b.base ) && is.b.offset == 0 ) {
250
    return ( is.b.base ) ;
251
  }
252
  r = getreg ( regs ) ;
253
  ld_addr ( is, r ) ;
254
  return ( r ) ;
255
}
256
 
257
 
258
/*
259
  MOVE a INTO dest
260
  regs gives the free registers, sgned gives the signedness.
261
*/
262
 
263
int move 
264
    PROTO_N ( ( a, dest, regs, sgned ) )
265
    PROTO_T ( ans a X where dest X long regs X bool sgned ){
266
  int al = ( int ) dest.ashwhere.ashalign ;
267
  if ( dest.ashwhere.ashsize == 0 ) return ( NOREG ) ;
268
 
269
  if ( PIC_code && discrim ( dest.answhere ) == notinreg ) {
270
    instore iss;
271
    iss = insalt ( dest.answhere ) ;
272
    if ( !IS_FIXREG (iss.b.base) && !SIMM13_SIZE (iss.b.offset) ) {
273
	/* global with large offset: we have to avoid double use of R_TMP */
274
      int ofs = iss.b.offset ;
275
      int r = getreg ( regs ) ;
276
      regs |= RMASK ( r ) ;
277
      iss.b.offset = 0;
278
      set_ins ( iss.b, r ) ;
279
      iss.b.offset = ofs;
280
      iss.b.base = r;
281
      setinsalt ( dest.answhere, iss ) ;
282
    }
283
  }
284
 
285
  start : switch ( discrim ( a ) ) {
286
 
287
  case insomereg :
288
  case insomefreg : {
289
    /* Source is in some register */
290
    fail ( "move : source register not specified" ) ;
291
    return ( NOREG ) ;
292
  }
293
  case inreg : {
294
    /* Source in fixed point register */
295
    int r = regalt ( a ) ;
296
 
297
    switch ( discrim ( dest.answhere ) ) {
298
 
299
    case inreg : {
300
      /* Register to register move */
301
      int rd = regalt ( dest.answhere ) ;
302
      if ( rd != R_G0 && rd != r ) {
303
	rr_ins ( i_mov, r, rd ) ;
304
      }
305
      return ( NOREG ) ;
306
    }
307
 
308
    case insomereg : {
309
      /* Register to some register move */
310
      int *sr = someregalt ( dest.answhere ) ;
311
      if ( *sr != -1 ) fail ( "Illegal register" ) ;
312
      *sr = r ;
313
      return ( NOREG ) ;
314
    }
315
 
316
    case infreg : {
317
      /* Register to floating register move */
318
      freg fr ;
319
      fr = fregalt ( dest.answhere ) ;
320
      st_ro_ins ( i_st, r, mem_temp ( 0 ) ) ;
321
      ldf_ro_ins ( i_ld, mem_temp ( 0 ), fr.fr << 1 ) ;
322
      if ( fr.dble ) {
323
	st_ro_ins ( i_st, r + 1, mem_temp ( 4 ) ) ;
324
	ldf_ro_ins ( i_ld, mem_temp ( 4 ),
325
		     ( fr.fr << 1 ) + 1 ) ;
326
      }
327
      return ( NOREG ) ;
328
    }
329
 
330
    case notinreg : {
331
      /* Register to store move */
332
      instore is ;
333
      ins_p st = i_st_sz ( al ) ;
334
 
335
      if ( al == 1 ) {
336
	st = i_st_sz ( ( int ) dest.ashwhere.ashsize ) ;
337
      } else {
338
	/*assert ( al == dest.ashwhere.ashsize ) ;*/
339
	st = i_st_sz ( al ) ;
340
      }
341
 
342
      is = insalt ( dest.answhere ) ;
343
      if ( is.adval ) {
344
	st_ins ( st, r, is.b ) ;
345
      } else {
346
	baseoff b ;
347
	b.base = R_TMP ;
348
	b.offset = 0 ;
349
	ld_ins ( i_ld, is.b, b.base ) ;
350
	st_ins ( st, r, b ) ;
351
      }
352
      return ( r ) ;
353
    }
354
    default:
355
      fail("fixed -> wrong dest");
356
    }
357
    /* NOT REACHED */
358
  }
359
  case infreg : {
360
    /* Source in floating point register */
361
    freg fr ;
362
    fr = fregalt ( a ) ;
363
 
364
    switch ( discrim ( dest.answhere ) ) {
365
    case inreg : {
366
      /* Floating register to register move */
367
      int rd = regalt ( dest.answhere ) ;
368
 
369
      if ( rd != 0 ) {
370
	/* store and load to move to fixed reg */
371
	stf_ins ( i_st, fr.fr<<1, mem_temp ( 0 ) ) ;
372
	ld_ro_ins ( i_ld, mem_temp ( 0 ), rd ) ;
373
	if ( fr.dble ) {
374
	  stf_ins ( i_st, ( fr.fr << 1 ) + 1,
375
		    mem_temp ( 4 ) ) ;
376
	  ld_ro_ins ( i_ld, mem_temp ( 4 ), rd + 1 ) ;
377
	}
378
      }
379
      return ( NOREG ) ;
380
    }
381
    case insomereg : {
382
      /* Floating register to some register move */
383
      int *sr = someregalt ( dest.answhere ) ;
384
      if ( *sr != -1 ) fail ( "Illegal register" ) ;
385
      *sr = getreg ( regs ) ;
386
      regs |= RMASK ( *sr ) ;
387
      setregalt ( dest.answhere, *sr ) ;
388
      goto start ;
389
    }
390
    case infreg : {
391
      /* Floating register to floating register move */
392
      freg frd ;
393
      frd = fregalt ( dest.answhere ) ;
394
 
395
      if ( fr.fr != frd.fr ) {
396
	rrf_ins ( i_fmovs, fr.fr << 1, frd.fr << 1 ) ;
397
	if ( frd.dble ) {
398
	  rrf_ins ( i_fmovs, ( fr.fr << 1 ) + 1,
399
		    ( frd.fr << 1 ) + 1 ) ;
400
	}
401
      }
402
      return ( NOREG ) ;
403
    }
404
    case notinreg : {
405
      /* Floating register to store move */
406
      instore is ;
407
      ins_p st = ( fr.dble ? i_std : i_st ) ;
408
 
409
      if ( ( dest.ashwhere.ashsize == 64 && !fr.dble ) ||
410
	   ( dest.ashwhere.ashsize == 32 && fr.dble ) ) {
411
	fail ( "Inconsistent sizes in move" ) ;
412
      }
413
 
414
      is = insalt ( dest.answhere ) ;
415
      if ( is.adval ) {
416
	if ( fr.dble ) {
417
	  if (( ( is.b.offset & 7 ) == 0 ) && 
418
		((is.b.base == R_FP) || (is.b.base == R_SP))) {
419
	    /* double aligned */
420
	    stf_ins ( i_std, fr.fr << 1, is.b ) ;
421
	  } else {
422
				/* not double aligned */
423
	    stf_ins ( i_st, fr.fr << 1, is.b ) ;
424
	    is.b.offset += 4 ;
425
	    stf_ins ( i_st, ( fr.fr << 1 ) + 1, is.b ) ;
426
	  }
427
	} else {
428
	  stf_ins ( i_st, fr.fr << 1, is.b ) ;
429
	}
430
      } else {
431
	baseoff b ;
432
	b.base = getreg ( regs ) ;
433
	b.offset = 0 ;
434
	ld_ins ( i_ld, is.b, b.base ) ;
435
	stf_ro_ins ( st, fr.fr << 1, b ) ;
436
      }
437
 
438
      return ( fr.dble ? -( fr.fr + 32 ) : ( fr.fr + 32 ) ) ;
439
    }
440
    default:
441
      fail("Float to wrong dest");
442
    }
443
    /* NOT REACHED */
444
  }
445
  case notinreg : {
446
    /* Source in store */
447
    instore iss ;
448
    int size = dest.ashwhere.ashsize;
449
    iss = insalt ( a ) ;
450
 
451
    if ( PIC_code && !IS_FIXREG (iss.b.base) && !SIMM13_SIZE (iss.b.offset) ) {
452
	/* global with large offset: we have to avoid double use of R_TMP */
453
      int ofs = iss.b.offset ;
454
      int r = getreg ( regs ) ;
455
      regs |= RMASK ( r ) ;
456
      iss.b.offset = 0;
457
      set_ins ( iss.b, r ) ;
458
      iss.b.offset = ofs;
459
      iss.b.base = r;
460
    }
461
 
462
    if ( iss.adval && iss.b.offset == 0 && IS_FIXREG ( iss.b.base ) ) {
463
      /* address of [ base_reg + 0 ] is base_reg */
464
      setregalt ( a, iss.b.base ) ;
465
      goto start ;
466
    }
467
    if (al == 1){
468
      al = (size<=8)?9: ((size<=16)?16:32);
469
    }
470
    if ( al == 64 ) al = 32 ;
471
 
472
    /* determine which load instruction to use from al and adval */
473
    switch ( discrim ( dest.answhere ) ) {
474
    case insomereg : {
475
      /* Move store to some register */
476
      int *sr = someregalt ( dest.answhere ) ;
477
      if ( *sr != -1 ) fail ( "Illegal register" ) ;
478
      *sr = getreg ( regs ) ;
479
      regs |= RMASK ( *sr ) ;
480
      setregalt ( dest.answhere, *sr ) ;
481
      /* FALL THROUGH */
482
    }
483
    case inreg : {
484
      /* Move store to register */
485
      int rd = regalt ( dest.answhere ) ;
486
      if ( rd != R_G0 ) {
487
	if ( iss.adval ) {
488
	  /* generate address of source */
489
	  if ( IS_FIXREG ( iss.b.base ) ) {
490
	    rir_ins ( i_add, iss.b.base,
491
		      iss.b.offset, rd ) ;
492
	  } else {
493
	    set_ins ( iss.b, rd ) ;
494
	  }
495
	} else {
496
	  /* load source */
497
	  ld_ins ( i_ld_sz ( al, sgned ), iss.b, rd ) ;
498
	}
499
      }
500
      return ( NOREG ) ;
501
    }
502
    case infreg : {
503
      /* Move store to floating register */
504
      freg frd ;
505
      frd = fregalt ( dest.answhere ) ;
506
      assert ( !iss.adval ) ;
507
      if ( frd.dble ) {
508
	/* double precision */
509
	if (( ( iss.b.offset & 7 ) == 0 ) && ( iss.b.offset != -8 ) && 
510
		((iss.b.base == R_FP) || (iss.b.base == R_SP))) {
511
	  /* source is double aligned */
512
	  ldf_ins ( i_ldd, iss.b, frd.fr << 1 ) ;
513
	} else {
514
	  /* source is not double aligned */
515
	  ldf_ins ( i_ld, iss.b, frd.fr << 1 ) ;
516
	  iss.b.offset += 4 ;
517
	  ldf_ins ( i_ld, iss.b, ( frd.fr << 1 ) + 1 ) ;
518
	}
519
      } else {
520
	/* single precision */
521
	ldf_ins ( i_ld, iss.b, frd.fr << 1 ) ;
522
      }
523
      return ( NOREG ) ;
524
    }
525
    case notinreg : {
526
      /* Move store to store address */
527
      int bits ;
528
      int no_steps ;
529
      int bits_per_step ;
530
      int bytes_per_step ;
531
      instore isd ;
532
      ins_p st, ld ;
533
      bool unalign = ( bool ) ( al < 32 ) ;
534
 
535
      /* we are limited by 32 bit regs */
536
      bits_per_step = MIN_OF ( al, 32 ) ;
537
      bytes_per_step = bits_per_step / 8 ;
538
 
539
      /* round up number of bits to be moved */
540
      bits = ( int ) ( ( dest.ashwhere.ashsize +
541
			 bits_per_step - 1 ) &
542
		       ~( bits_per_step - 1 ) ) ;
543
 
544
      no_steps = ( bits + bits_per_step - 1 ) / bits_per_step ;
545
 
546
      if ( ( al % 8 ) != 0 || ( bits % 8 ) != 0 ) {
547
	fail ( "move : misaligned store" ) ;
548
	return ( NOREG ) ;
549
      }
550
      assert ( ( bits % al ) == 0 ) ;
551
      assert ( bytes_per_step > 0 && bytes_per_step <= 4 ) ;
552
      assert ( no_steps > 0 ) ;
553
      assert ( ( no_steps * bytes_per_step ) == ( bits / 8 ) ) ;
554
 
555
      ld = i_ld_sz ( bits_per_step, sgned ) ;
556
      st = i_st_sz ( bits_per_step ) ;
557
 
558
      isd = insalt ( dest.answhere ) ;
559
      if ( no_steps <= MAX_STEPS_INLINE_MOVE ) {
560
	/* expand to a number of loads and stores */
561
	if ( no_steps == 1 ) {
562
	  /* move can be done in one step */
563
	  int r = getreg ( regs ) ;
564
	  regs |= RMASK ( r ) ;
565
	  if ( iss.adval ) {
566
				/* generate address of source */
567
	    if ( IS_FIXREG ( iss.b.base ) ) {
568
	      if ( iss.b.offset == 0 ) {
569
		r = iss.b.base ;
570
	      } else {
571
		rir_ins ( i_add, iss.b.base,
572
			  iss.b.offset, r ) ;
573
	      }
574
	    } else {
575
	      set_ins ( iss.b, r ) ;
576
	    }
577
	  } else {
578
				/* load source */
579
	    ld_ins ( ld, iss.b, r ) ;
580
	  }
581
 
582
	  if ( !isd.adval ) {
583
	    ld_ins ( i_ld, isd.b, R_TMP ) ;
584
	    isd.b.base = R_TMP ;
585
	    isd.b.offset = 0 ;
586
	  }
587
	  st_ins ( st, r, isd.b ) ;
588
	  return ( unalign ? NOREG : r ) ;
589
	} else {
590
	  /* use two registers, ensuring load delay
591
	     slot is not occupied */
592
	  int r1, r2 ;
593
	  int ld_steps = no_steps ;
594
	  int st_steps = no_steps ;
595
 
596
	  assert ( ld_steps >= 2 ) ;
597
	  /*assert ( !iss.adval ) ;*/
598
	  assert ( bits_per_step <= 32 ) ;
599
 
600
	  /* find the registers to be used */
601
	  r1 = getreg ( regs ) ;
602
	  regs |= RMASK ( r1 ) ;
603
	  r2 = getreg ( regs ) ;
604
	  regs |= RMASK ( r2 ) ;
605
 
606
	  if ( !IS_FIXREG ( iss.b.base ) ) {
607
				/* load source ptr in reg */
608
	    int pr = getreg ( regs ) ;
609
	    regs |= RMASK ( pr ) ;
610
	    set_ins ( iss.b, pr ) ;
611
	    iss.b.base = pr ;
612
	    iss.b.offset = 0 ;
613
	  }
614
 
615
	  if ( !isd.adval ) {
616
	    int pr = getreg ( regs ) ;
617
	    regs |= RMASK ( pr ) ;
618
	    ld_ins ( i_ld, isd.b, pr ) ;
619
	    isd.b.base = pr ;
620
	    isd.b.offset = 0 ;
621
	  } else if ( !IS_FIXREG ( isd.b.base ) ) {
622
	    int pr = getreg ( regs ) ;
623
	    regs |= RMASK ( pr ) ;
624
	    set_ins ( isd.b, pr ) ;
625
	    isd.b.base = pr ;
626
	    isd.b.offset = 0 ;
627
	  }
628
 
629
	  /* first pre-load both registers */
630
	  ld_ro_ins ( ld, iss.b, r1 ) ;
631
	  ld_steps-- ;
632
	  iss.b.offset += bytes_per_step ;
633
 
634
	  ld_ro_ins ( ld, iss.b, r2 ) ;
635
	  ld_steps-- ;
636
	  iss.b.offset += bytes_per_step ;
637
 
638
	  /* now generate a sequence of instructions
639
	     of the form :
640
 
641
	     st r1
642
	     ld r1
643
	     st r2
644
	     ld r2
645
	     */
646
	  while ( st_steps > 0 ) {
647
				/* st r1 */
648
	    st_ro_ins ( st, r1, isd.b ) ;
649
	    st_steps-- ;
650
	    isd.b.offset += bytes_per_step ;
651
 
652
				/* ld r1 */
653
	    if ( ld_steps > 0 ) {
654
	      ld_ro_ins ( ld, iss.b, r1 ) ;
655
	      ld_steps-- ;
656
	      iss.b.offset += bytes_per_step ;
657
	    }
658
 
659
				/* st r2 */
660
	    if ( st_steps > 0 ) {
661
	      st_ro_ins ( st, r2, isd.b ) ;
662
	      st_steps-- ;
663
	      isd.b.offset += bytes_per_step ;
664
	    }
665
 
666
				/* ld r2 */
667
	    if ( ld_steps > 0 ) {
668
	      ld_ro_ins ( ld, iss.b, r2 ) ;
669
	      ld_steps-- ;
670
	      iss.b.offset += bytes_per_step ;
671
	    }
672
	  }
673
	  assert ( ld_steps == 0 ) ;
674
	  return ( NOREG ) ;
675
	}
676
      } else {
677
	/* large number of steps - use a loop */
678
	int cnt_reg ;
679
	int copy_reg ;
680
	int srcptr_reg ;
681
	int destptr_reg ;
682
	int loop = new_label () ;
683
 
684
	assert ( !iss.adval ) ;
685
	assert ( bytes_per_step <= 4 ) ;
686
 
687
	/* find control register */
688
	cnt_reg = getreg ( regs ) ;
689
	regs |= RMASK ( cnt_reg ) ;
690
 
691
	/* find source register */
692
	assert ( !iss.adval ) ;
693
	iss.adval = 1 ;
694
	srcptr_reg = addr_reg ( iss, regs ) ;
695
	regs |= RMASK ( srcptr_reg ) ;
696
 
697
	/* find destination register */
698
	destptr_reg = addr_reg ( isd, regs ) ;
699
	regs |= RMASK ( destptr_reg ) ;
700
 
701
	/* find copy register */
702
	copy_reg = R_TMP ;
703
 
704
	/* main loop */
705
	ir_ins ( i_mov, ( long ) ( bytes_per_step * no_steps ),
706
		 cnt_reg ) ;
707
	set_label ( loop ) ;
708
	rir_ins ( i_subcc, cnt_reg, ( long ) bytes_per_step,
709
		  cnt_reg ) ;
710
	ld_rr_ins ( ld, srcptr_reg, cnt_reg, copy_reg ) ;
711
	st_rr_ins ( st, copy_reg, destptr_reg, cnt_reg ) ;
712
	br_ins ( i_bne, loop ) ;
713
	return ( NOREG ) ;
714
      }
715
    }
716
    default:
717
    {
718
      /* fall through to fail */
719
    }
720
    }
721
  }
722
  }
723
  fail ( "Illegal move" ) ;
724
  return ( NOREG ) ;
725
}
726