%{

  /* ARM back-end for lcc 4.0
   *
   * version 1.12q Fri 20th March 1998
   *
   * use lburg to convert to a C file.
   *
   *
   * NOTES:
   *
   * 1 Supports all necessary constructs in C. Also conforms, as far as I can tell, to
   *   the APCS.
   *
   * 2 Shorts are implemented as a two-byte size data item (rather than simply as ints),
   *   but loads/stores must be synthesized using several instructions, which is slow.
   *   However, shorts may occasionally hold more than a 16-bit value if they are held
   *   in integer registers. They are truncated on load/store. Similarly for chars. Use
   *   ints for preference unless space is tight.
   *
   * 3 lr and ip are used as a temporary registers. (Allowed by APCS).
   *
   * 4 No debug information is output.
   *
   * 5 a1-a4, f0-f7 and ip may be (both senses) destroyed by functions, unless
   *   -msave-fps is used, in which case f4-f7 will not be changed, and assumed
   *   not changed.
   *
   * 6 8 bytes below sp (frame) may be corrupted by double <-> integer conversions for
   *   calling convention - take note if writing routines (eg fast alloca) which
   *   alter/use fp directly).
   *
   * 7 Variadic routines: use ... (preferred) or args called va_alist or
   *   __builtin_va_alist.
   *
   * 8 -Wo switches (-Wo or -Wono-) (non-defaults) are:-
   *
   *	-Woversion		Print version number
   *
   *	-Wono-check-stack	Don't perform stack checking, use for speed or
   *				 (eg in UnixLib:signal/coredump.c).
   *	-Wono-fast-locals	Turn off extra rules for faster local access; prevents
   *				 error when framesize > 4094 (or sometimes when
   *				 framesize > 1020). Will produce slower and bigger code.
   *	-Wouse-fpincall		Pass first few FP args in FREGs
   *
   *	-Wosave-fps		Save FP registers f4-f7
   *
   * 9 Order registers are loaded for shorts _is_ important; we use
   *	ldrb	lr, [%0+1]
   *	ldrb	%c, [%0]
   *	add	%c, %c, lr, lsl#8
   *   rather than loading %c before lr, since %c may be the same register
   *   as (reg_of) %0.
   */

#define	ARMVERS	"1.12q"

enum {ARM_A1 = 0, ARM_A2 = 1, ARM_A3 = 2, ARM_A4 = 3,
      ARM_V1 = 4, ARM_V2 = 5, ARM_V3 = 6, ARM_V4 = 7, ARM_V5 = 8, ARM_V6 = 9,
      ARM_SL = 10, ARM_FP = 11, ARM_IP = 12, ARM_SP = 13, ARM_LR = 14, ARM_PC = 15};

#define	INTTMP	((1<<ARM_V1) | (1<<ARM_V2) | (1<<ARM_V3) | \
                 (1<<ARM_V4) | (1<<ARM_V5) | (1<<ARM_V6) | \
                 (1<<ARM_A2) | (1<<ARM_A3) | (1<<ARM_A4) | \
                 (1<<ARM_IP))

#define	INTVAR	((1<<ARM_V4) | (1<<ARM_V5) | (1<<ARM_V6))

#define FLTTMP 0x000000fe	/* f1-f7 */
#define FLTVAR 0x00000000

#define INTRET 0x00000001
#define FLTRET 0x00000001

#define INTCLOBBER	0x100e	/* a2-a4, ip */
#define FLTCLOBBER	0xfe	/* f1-f7 */
#define FLSCLOBBER	0x0e	/* f1-f3 */

#define readsreg(p) \
        (generic((p)->op)==INDIR && (p)->kids[0]->op==VREG+P)
#define setsrc(d) ((d) && (d)->x.regnode && \
        (d)->x.regnode->set == src->x.regnode->set && \
        (d)->x.regnode->mask&src->x.regnode->mask)

#define relink(a, b) ((b)->x.prev = (a), (a)->x.next = (b))

#include "c.h"
#define NODEPTR_TYPE Node
#define OP_LABEL(p) ((p)->op)
#define LEFT_CHILD(p) ((p)->kids[0])
#define RIGHT_CHILD(p) ((p)->kids[1])
#define STATE_LABEL(p) ((p)->x.state)

static void address(Symbol, Symbol, int);
static void blkfetch(int, int, int, int);
static void blkloop(int, int, int, int, int, int[]);
static void blkstore(int, int, int, int);
static void defaddress(Symbol);
static void defconst(int, int, Value);
static void defstring(int, char *);
static void defsymbol(Symbol);
static void doarg(Node);
static void emit2(Node);
static void export(Symbol);
static void clobber(Node);
static void function(Symbol, Symbol [], Symbol [], int);
static void global(Symbol);
static void import(Symbol);
static void local(Symbol);
static void progbeg(int, char **);
static void progend(void);
static void segment(int);
static void space(int);
static void target(Node);
static int  bitcount(unsigned);
static Symbol argreg(int, int);

static Symbol ireg[32], freg[32], a1a2, a2a3, a3a4, splitdr;
static Symbol iregw, fregw;
static int tmpregs[] = {ARM_LR, ARM_V2, ARM_V3};
static Symbol blkreg;

static int fourstore, splitdouble, varargs;
static int cseg, infunc, check_stack;
static int fast_locals, use_fpincall, save_fps;

/*
 * ARM nonterminals
 *
 * NAME		WHAT IT MATCHES
 *------------------------------
 * reg		computations that yield a result in a register
 * stmt		computations done for side-efect
 * con		constants
 * con12	constants which can be used by datatrans instructions (12 bits)
 * con10	constants which can be used by fp datatrans instructions (10 bits)
 * acon		address constants
 * addr		address calculations for instructions that read and write memory
 * addrf	address calculations for fp instructions that read and write memory
 * addri	address calculations for shorts
 * rc		registers and constants
 * rc5		registers and constants that fit in 5 bits
 * arc		registers, constants, register shifts for datatrans instructions
 * alab		labels
 */

%}
%start stmt
%term CNSTF4=4113
%term CNSTF8=8209
%term CNSTF16=16401
%term CNSTI1=1045
%term CNSTI2=2069
%term CNSTI4=4117
%term CNSTI8=8213
%term CNSTP4=4119
%term CNSTP8=8215
%term CNSTU1=1046
%term CNSTU2=2070
%term CNSTU4=4118
%term CNSTU8=8214

%term ARGB=41
%term ARGF4=4129
%term ARGF8=8225
%term ARGF16=16417
%term ARGI4=4133
%term ARGI8=8229
%term ARGP4=4135
%term ARGP8=8231
%term ARGU4=4134
%term ARGU8=8230

%term ASGNB=57
%term ASGNF4=4145
%term ASGNF8=8241
%term ASGNF16=16433
%term ASGNI1=1077
%term ASGNI2=2101
%term ASGNI4=4149
%term ASGNI8=8245
%term ASGNP4=4151
%term ASGNP8=8247
%term ASGNU1=1078
%term ASGNU2=2102
%term ASGNU4=4150
%term ASGNU8=8246

%term INDIRB=73
%term INDIRF4=4161
%term INDIRF8=8257
%term INDIRF16=16449
%term INDIRI1=1093
%term INDIRI2=2117
%term INDIRI4=4165
%term INDIRI8=8261
%term INDIRP4=4167
%term INDIRP8=8263
%term INDIRU1=1094
%term INDIRU2=2118
%term INDIRU4=4166
%term INDIRU8=8262

%term CVFF4=4209
%term CVFF8=8305
%term CVFF16=16497
%term CVFI4=4213
%term CVFI8=8309

%term CVIF4=4225
%term CVIF8=8321
%term CVIF16=16513
%term CVII1=1157
%term CVII2=2181
%term CVII4=4229
%term CVII8=8325
%term CVIU1=1158
%term CVIU2=2182
%term CVIU4=4230
%term CVIU8=8326

%term CVPP4=4247
%term CVPP8=8343
%term CVPP16=16535
%term CVPU4=4246
%term CVPU8=8342

%term CVUI1=1205
%term CVUI2=2229
%term CVUI4=4277
%term CVUI8=8373
%term CVUP4=4279
%term CVUP8=8375
%term CVUP16=16567
%term CVUU1=1206
%term CVUU2=2230
%term CVUU4=4278
%term CVUU8=8374

%term NEGF4=4289
%term NEGF8=8385
%term NEGF16=16577
%term NEGI4=4293
%term NEGI8=8389

%term CALLB=217
%term CALLF4=4305
%term CALLF8=8401
%term CALLF16=16593
%term CALLI4=4309
%term CALLI8=8405
%term CALLP4=4311
%term CALLP8=8407
%term CALLU4=4310
%term CALLU8=8406
%term CALLV=216

%term RETF4=4337
%term RETF8=8433
%term RETF16=16625
%term RETI4=4341
%term RETI8=8437
%term RETP4=4343
%term RETP8=8439
%term RETU4=4342
%term RETU8=8438
%term RETV=248

%term ADDRGP4=4359
%term ADDRGP8=8455

%term ADDRFP4=4375
%term ADDRFP8=8471

%term ADDRLP4=4391
%term ADDRLP8=8487

%term ADDF4=4401
%term ADDF8=8497
%term ADDF16=16689
%term ADDI4=4405
%term ADDI8=8501
%term ADDP4=4407
%term ADDP8=8503
%term ADDU4=4406
%term ADDU8=8502

%term SUBF4=4417
%term SUBF8=8513
%term SUBF16=16705
%term SUBI4=4421
%term SUBI8=8517
%term SUBP4=4423
%term SUBP8=8519
%term SUBU4=4422
%term SUBU8=8518

%term LSHI4=4437
%term LSHI8=8533
%term LSHU4=4438
%term LSHU8=8534

%term MODI4=4453
%term MODI8=8549
%term MODU4=4454
%term MODU8=8550

%term RSHI4=4469
%term RSHI8=8565
%term RSHU4=4470
%term RSHU8=8566

%term BANDI4=4485
%term BANDI8=8581
%term BANDU4=4486
%term BANDU8=8582

%term BCOMI4=4501
%term BCOMI8=8597
%term BCOMU4=4502
%term BCOMU8=8598

%term BORI4=4517
%term BORI8=8613
%term BORU4=4518
%term BORU8=8614

%term BXORI4=4533
%term BXORI8=8629
%term BXORU4=4534
%term BXORU8=8630

%term DIVF4=4545
%term DIVF8=8641
%term DIVF16=16833
%term DIVI4=4549
%term DIVI8=8645
%term DIVU4=4550
%term DIVU8=8646

%term MULF4=4561
%term MULF8=8657
%term MULF16=16849
%term MULI4=4565
%term MULI8=8661
%term MULU4=4566
%term MULU8=8662

%term EQF4=4577
%term EQF8=8673
%term EQF16=16865
%term EQI4=4581
%term EQI8=8677
%term EQU4=4582
%term EQU8=8678

%term GEF4=4593
%term GEF8=8689
%term GEI4=4597
%term GEI8=8693
%term GEI16=16885
%term GEU4=4598
%term GEU8=8694

%term GTF4=4609
%term GTF8=8705
%term GTF16=16897
%term GTI4=4613
%term GTI8=8709
%term GTU4=4614
%term GTU8=8710

%term LEF4=4625
%term LEF8=8721
%term LEF16=16913
%term LEI4=4629
%term LEI8=8725
%term LEU4=4630
%term LEU8=8726

%term LTF4=4641
%term LTF8=8737
%term LTF16=16929
%term LTI4=4645
%term LTI8=8741
%term LTU4=4646
%term LTU8=8742

%term NEF4=4657
%term NEF8=8753
%term NEF16=16945
%term NEI4=4661
%term NEI8=8757
%term NEU4=4662
%term NEU8=8758

%term JUMPV=584

%term LABELV=600

%term LOADB=233
%term LOADF4=4321
%term LOADF8=8417
%term LOADF16=16609
%term LOADI1=1253
%term LOADI2=2277
%term LOADI4=4325
%term LOADI8=8421
%term LOADP4=4327
%term LOADP8=8423
%term LOADU1=1254
%term LOADU2=2278
%term LOADU4=4326
%term LOADU8=8422

%term VREGP=711
%%
reg: INDIRI1(VREGP)	"# read register\n"
reg: INDIRU1(VREGP)	"# read register\n"

reg: INDIRI2(VREGP)	"# read register\n"
reg: INDIRU2(VREGP)	"# read register\n"

reg: INDIRF4(VREGP)	"# read register\n"
reg: INDIRI4(VREGP)	"# read register\n"
reg: INDIRP4(VREGP)	"# read register\n"
reg: INDIRU4(VREGP)	"# read register\n"

reg: INDIRF8(VREGP)	"# read register\n"
reg: INDIRI8(VREGP)	"# read register\n"
reg: INDIRP8(VREGP)	"# read register\n"
reg: INDIRU8(VREGP)	"# read register\n"

stmt: ASGNI1(VREGP,reg)	"# write register\n"
stmt: ASGNU1(VREGP,reg)	"# write register\n"

stmt: ASGNI2(VREGP,reg)	"# write register\n"
stmt: ASGNU2(VREGP,reg)	"# write register\n"

stmt: ASGNF4(VREGP,reg)	"# write register\n"
stmt: ASGNI4(VREGP,reg)	"# write register\n"
stmt: ASGNP4(VREGP,reg)	"# write register\n"
stmt: ASGNU4(VREGP,reg)	"# write register\n"

stmt: ASGNF8(VREGP,reg)	"# write register\n"
stmt: ASGNI8(VREGP,reg)	"# write register\n"
stmt: ASGNP8(VREGP,reg)	"# write register\n"
stmt: ASGNU8(VREGP,reg)	"# write register\n"

con: CNSTI1		"%a"
con: CNSTU1		"%a"

con: CNSTI2		"%a"
con: CNSTU2		"%a"

con: CNSTI4		"%a"
con: CNSTU4		"%a"
con: CNSTP4		"%a"

con: CNSTI8		"%a"
con: CNSTU8		"%a"
con: CNSTP8		"%a"

con12: CNSTI1		"%a"	range(a, -4095, 4094)
con12: CNSTU1		"%a"	range(a, -4095, 4094)

con12: CNSTI2		"%a"	range(a, -4095, 4094)
con12: CNSTU2		"%a"	range(a, -4095, 4094)

con12: CNSTI4		"%a"	range(a, -4095, 4094)
con12: CNSTU4		"%a"	range(a, -4095, 4094)
con12: CNSTP4		"%a"	range(a, -4095, 4094)

con12: CNSTI8		"%a"	range(a, -4095, 4094)
con12: CNSTU8		"%a"	range(a, -4095, 4094)
con12: CNSTP8		"%a"	range(a, -4095, 4094)

con10: CNSTI1		"%a"	range(a, -1020, 1020)
con10: CNSTU1		"%a"	range(a, -1020, 1020)

con10: CNSTI2		"%a"	range(a, -1020, 1020)
con10: CNSTU2		"%a"	range(a, -1020, 1020)

con10: CNSTI4		"%a"	range(a, -1020, 1020)
con10: CNSTU4		"%a"	range(a, -1020, 1020)
con10: CNSTP4		"%a"	range(a, -1020, 1020)

con10: CNSTI8		"%a"	range(a, -1020, 1020)
con10: CNSTU8		"%a"	range(a, -1020, 1020)
con10: CNSTP8		"%a"	range(a, -1020, 1020)

stmt: reg		""

acon: ADDRGP4		"%a"

addri: reg		"%0, #0"
addri: ADDI4(reg,con12)	"%0, #%1"
addri: ADDU4(reg,con12)	"%0, #%1"
addri: ADDP4(reg,con12)	"%0, #%1"
addri: ADDI4(con12,reg)	"%1, #%0"
addri: ADDU4(con12,reg)	"%1, #%0"
addri: ADDP4(con12,reg)	"%1, #%0"
addri: ADDRFP4		"# address of formal at %a"
addri: ADDRLP4		"sp, #%a+%F"	(fast_locals ? 0 : LBURG_MAX)

arc: reg		" %0"
arc: con12		" #%0"
arc: LSHI4(reg,rc5)	" %0, asl%1"
arc: LSHU4(reg,rc5)	" %0, lsl%1"
arc: RSHI4(reg,rc5)	" %0, asr%1"
arc: RSHU4(reg,rc5)	" %0, lsr%1"

addr: ADDI4(reg,arc)	"%0,%1"
addr: ADDU4(reg,arc)	"%0,%1"
addr: ADDP4(reg,arc)	"%0,%1"
addr: ADDI4(arc,reg)	"%1,%0"
addr: ADDU4(arc,reg)	"%1,%0"
addr: ADDP4(arc,reg)	"%1,%0"
addr: addri		"%0"

addrf: reg		"%0, #0"
addrf: ADDI4(reg,con10)	"%0, #%1"
addrf: ADDU4(reg,con10)	"%0, #%1"
addrf: ADDP4(reg,con10)	"%0, #%1"
addrf: ADDI4(con10,reg)	"%1, #%0"
addrf: ADDU4(con10,reg)	"%1, #%0"
addrf: ADDP4(con10,reg)	"%1, #%0"
addrf: ADDRFP4		"# address of formal at %a"
addrf: ADDRLP4		"sp, #%a+%F"	(fast_locals ? 0 : LBURG_MAX)

reg: ADDRLP4		"	add	%c, sp, #%a+%F\n"	1
reg: addr		"	add	%c, %0\n"	1
reg: acon		"	adr	%c, %0\n"	3
reg: con		"	mov	%c, #%0\n"	1
stmt: ASGNI1(addr,reg)	"	strb	%1, [%0]\n"	4
stmt: ASGNU1(addr,reg)	"	strb	%1, [%0]\n"	4

stmt: ASGNI2(addri,reg)	"	strb	%1, [%0]\n	mov	lr, %1, asr #8\n	strb	lr, [%0+1]\n"	9
stmt: ASGNU2(addri,reg)	"	strb	%1, [%0]\n	mov	lr, %1, lsr #8\n	strb	lr, [%0+1]\n"	9

stmt: ASGNI4(addr,reg)	"	str	%1, [%0]\n"	4
stmt: ASGNU4(addr,reg)	"	str	%1, [%0]\n"	4
stmt: ASGNP4(addr,reg)	"	str	%1, [%0]\n"	4
reg: INDIRI1(addr)	"	ldrb	%c, [%0]\n"	4
reg: INDIRU1(addr)	"	ldrb	%c, [%0]\n"	4
reg: INDIRI2(addri)	"	ldrb	lr, [%0+1]\n	ldrb	%c, [%0]\n	orr	%c, %c, lr, lsl#8\n"	9
reg: INDIRU2(addri)	"	ldrb	lr, [%0+1]\n	ldrb	%c, [%0]\n	orr	%c, %c, lr, lsl#8\n"	9

reg: INDIRI4(addr)	"	ldr	%c, [%0]\n"	4
reg: INDIRU4(addr)	"	ldr	%c, [%0]\n"	4
reg: INDIRP4(addr)	"	ldr	%c, [%0]\n"	4

reg: INDIRF4(addrf)	"	ldfs	%c, [%0]\n"	10
reg: INDIRF8(addrf)	"	ldfd	%c, [%0]\n"	10
stmt: ASGNF4(addrf,reg)	"	stfs	%1, [%0]\n"	10
stmt: ASGNF8(addrf,reg)	"	stfd	%1, [%0]\n"	10

reg: CVUU4(INDIRU1(addr))	"	ldrb	%c, [%0]\n"	4
reg: CVUU2(INDIRU1(addr))	"	ldrb	%c, [%0]\n"	4
reg: CVUI4(INDIRU1(addr))	"	ldrb	%c, [%0]\n"	4
reg: CVUI2(INDIRU1(addr))	"	ldrb	%c, [%0]\n"	4
reg: CVUU4(INDIRU2(addri))	"	ldrb	lr, [%0+1]\n	ldrb	%c, [%0]\n	orr	%c, %c, lr, lsl#8\n"	9
reg: CVUI4(INDIRU2(addri))	"	ldrb	lr, [%0+1]\n	ldrb	%c, [%0]\n	orr	%c, %c, lr, lsl#8\n"	9

reg: DIVI4(reg,reg)	"	bl	x$divide\n"	20
reg: DIVU4(reg,reg)	"	bl	x$udivide\n"	20
reg: MODI4(reg,reg)	"	bl	x$remainder\n"	20
reg: MODU4(reg,reg)	"	bl	x$uremainder\n"	20
reg: MULI4(reg,reg)	"	mul	%c, %0, %1\n"	10
reg: MULU4(reg,reg)	"	mul	%c, %0, %1\n"	10

rc: reg			" %0"
rc: LSHI4(reg,rc5)	" %0, asl%1"
rc: LSHU4(reg,rc5)	" %0, lsl%1"
rc: RSHI4(reg,rc5)	" %0, asr%1"
rc: RSHU4(reg,rc5)	" %0, lsr%1"
rc: con			" #%0"

rc5: CNSTI1		" #%a"	range(a, 1, 31)
rc5: CNSTU1		" #%a"	range(a, 1, 31)
rc5: CNSTI2		" #%a"	range(a, 1, 31)
rc5: CNSTU2		" #%a"	range(a, 1, 31)
rc5: CNSTI4		" #%a"	range(a, 1, 31)
rc5: CNSTU4		" #%a"	range(a, 1, 31)
rc5: reg		" %0"	1

reg: ADDI4(reg,rc)	"	add	%c, %0,%1\n"	1
reg: ADDP4(reg,rc)	"	add	%c, %0,%1\n"	1
reg: ADDU4(reg,rc)	"	add	%c, %0,%1\n"	1
reg: BANDI4(reg,rc)	"	and	%c, %0,%1\n"	1
reg: BORI4(reg,rc)	"	orr	%c, %0,%1\n"	1
reg: BXORI4(reg,rc)	"	eor	%c, %0,%1\n"	1
reg: BANDU4(reg,rc)	"	and	%c, %0,%1\n"	1
reg: BORU4(reg,rc)	"	orr	%c, %0,%1\n"	1
reg: BXORU4(reg,rc)	"	eor	%c, %0,%1\n"	1
reg: SUBI4(reg,rc)	"	sub	%c, %0,%1\n"	1
reg: SUBP4(reg,rc)	"	sub	%c, %0,%1\n"	1
reg: SUBU4(reg,rc)	"	sub	%c, %0,%1\n"	1

reg: ADDI4(rc,reg)	"	add	%c, %1,%0\n"	1
reg: ADDP4(rc,reg)	"	add	%c, %1,%0\n"	1
reg: ADDU4(rc,reg)	"	add	%c, %1,%0\n"	1
reg: BANDI4(rc,reg)	"	and	%c, %1,%0\n"	1
reg: BORI4(rc,reg)	"	orr	%c, %1,%0\n"	1
reg: BXORI4(rc,reg)	"	eor	%c, %1,%0\n"	1
reg: BANDU4(rc,reg)	"	and	%c, %1,%0\n"	1
reg: BORU4(rc,reg)	"	orr	%c, %1,%0\n"	1
reg: BXORU4(rc,reg)	"	eor	%c, %1,%0\n"	1

reg: LSHI4(reg,rc5)	"	mov	%c, %0, asl%1\n"	1
reg: LSHU4(reg,rc5)	"	mov	%c, %0, lsl%1\n"	1
reg: RSHI4(reg,rc5)	"	mov	%c, %0, asr%1\n"	1
reg: RSHU4(reg,rc5)	"	mov	%c, %0, lsr%1\n"	1
reg: BCOMI4(reg)	"	mvn	%c, %0\n"	1
reg: BCOMU4(reg)	"	mvn	%c, %0\n"	1
reg: NEGI4(reg)		"	rsb	%c, %0, #0\n"	1
reg: NEGF4(reg)		"	mnfs	%c, %0\n"	10
reg: NEGF8(reg)		"	mnfd	%c, %0\n"	10

reg: LOADI1(reg)	"	mov	%c, %0\n"	move(a)
reg: LOADU1(reg)	"	mov	%c, %0\n"	move(a)
reg: LOADI2(reg)	"	mov	%c, %0\n"	move(a)
reg: LOADU2(reg)	"	mov	%c, %0\n"	move(a)
reg: LOADI4(reg)	"	mov	%c, %0\n"	move(a)
reg: LOADP4(reg)	"	mov	%c, %0\n"	move(a)
reg: LOADU4(reg)	"	mov	%c, %0\n"	move(a)
reg: ADDF4(reg,reg)	"	adfs	%c, %0, %1\n"	10
reg: ADDF8(reg,reg)	"	adfd	%c, %0, %1\n"	10
reg: DIVF4(reg,reg)	"	dvfs	%c, %0, %1\n"	10
reg: DIVF8(reg,reg)	"	dvfd	%c, %0, %1\n"	10
reg: MULF4(reg,reg)	"	mufs	%c, %0, %1\n"	10
reg: MULF8(reg,reg)	"	mufd	%c, %0, %1\n"	10
reg: SUBF4(reg,reg)	"	sufs	%c, %0, %1\n"	10
reg: SUBF8(reg,reg)	"	sufd	%c, %0, %1\n"	10
reg: LOADF4(reg)	"	mvfs	%c, %0\n"	(move(a)*10)
reg: LOADF8(reg)	"	mvfd	%c, %0\n"	(move(a)*10)
reg: NEGF4(reg)		"	mnfs	%c, %0\n"	10
reg: NEGF8(reg)		"	mnfd	%c, %0\n"	10
reg: CVII4(reg)		"	mov	%c, %0, asl #8*(4-%a)\n	mov	%c, %c, asr #8*(4-%a)\n"	2
reg: CVUI4(reg)		"	and	%c, %0, #(1<<(8*%a))-1\n"	1
reg: CVUU4(reg)		"	and	%c, %0, #(1<<(8*%a))-1\n"	1
reg: CVFF4(reg)		"	mvfs	%c, %0\n"	(move(a)*10)
reg: CVFF8(reg)		"	mvfd	%c, %0\n"	(move(a)*10)
reg: CVIF4(reg)		"	flts	%c, %0\n"	10
reg: CVIF8(reg)		"	fltd	%c, %0\n"	10
reg: CVFI4(reg)		"	fixz	%c, %0\n"	10
reg: CVPU4(reg)		"	mov	%c, %0\n"	move(a)
reg: CVUP4(reg)		"	mov	%c, %0\n"	move(a)
reg: CVIU4(reg)		"	and	%c, %0, #(1<<(8*%a))-1\n"	1

stmt: LABELV		"%a\n"
stmt: JUMPV(acon)	"	b	%0\n"	3
stmt: JUMPV(reg)	"	mov	pc, %0\n"	3
stmt: EQI4(reg,rc)	"	cmp	%0,%1\n	beq	%a\n"	4
stmt: EQU4(reg,rc)	"	cmp	%0,%1\n	beq	%a\n"	4
stmt: GEI4(reg,rc)	"	cmp	%0,%1\n	bge	%a\n"	4
stmt: GEU4(reg,rc)	"	cmp	%0,%1\n	bhs	%a\n"	4
stmt: GTI4(reg,rc)	"	cmp	%0,%1\n	bgt	%a\n"	4
stmt: GTU4(reg,rc)	"	cmp	%0,%1\n	bhi	%a\n"	4
stmt: LEI4(reg,rc)	"	cmp	%0,%1\n	ble	%a\n"	4
stmt: LEU4(reg,rc)	"	cmp	%0,%1\n	bls	%a\n"	4
stmt: LTI4(reg,rc)	"	cmp	%0,%1\n	blt	%a\n"	4
stmt: LTU4(reg,rc)	"	cmp	%0,%1\n	blo	%a\n"	4
stmt: NEI4(reg,rc)	"	cmp	%0,%1\n	bne	%a\n"	4
stmt: NEU4(reg,rc)	"	cmp	%0,%1\n	bne	%a\n"	4

stmt: EQI4(rc,reg)	"	cmp	%1,%0\n	beq	%a\n"	4
stmt: EQU4(rc,reg)	"	cmp	%1,%0\n	beq	%a\n"	4
stmt: GEI4(rc,reg)	"	cmp	%1,%0\n	blt	%a\n"	4
stmt: GEU4(rc,reg)	"	cmp	%1,%0\n	blo	%a\n"	4
stmt: GTI4(rc,reg)	"	cmp	%1,%0\n	ble	%a\n"	4
stmt: GTU4(rc,reg)	"	cmp	%1,%0\n	bls	%a\n"	4
stmt: LEI4(rc,reg)	"	cmp	%1,%0\n	bgt	%a\n"	4
stmt: LEU4(rc,reg)	"	cmp	%1,%0\n	bhi	%a\n"	4
stmt: LTI4(rc,reg)	"	cmp	%1,%0\n	bge	%a\n"	4
stmt: LTU4(rc,reg)	"	cmp	%1,%0\n	bhs	%a\n"	4
stmt: NEI4(rc,reg)	"	cmp	%1,%0\n	bne	%a\n"	4
stmt: NEU4(rc,reg)	"	cmp	%1,%0\n	bne	%a\n"	4

stmt: EQF4(reg,reg)	"	cmf	%0, %1\n	beq	%a\n"	14
stmt: EQF8(reg,reg)	"	cmf	%0, %1\n	beq	%a\n"	14
stmt: LEF4(reg,reg)	"	cmfe	%0, %1\n	ble	%a\n"	14
stmt: LEF8(reg,reg)	"	cmfe	%0, %1\n	ble	%a\n"	14
stmt: LTF4(reg,reg)	"	cmfe	%0, %1\n	blt	%a\n"	14
stmt: LTF8(reg,reg)	"	cmfe	%0, %1\n	blt	%a\n"	14
stmt: GEF4(reg,reg)	"	cmfe	%0, %1\n	bge	%a\n"	14
stmt: GEF8(reg,reg)	"	cmfe	%0, %1\n	bge	%a\n"	14
stmt: GTF4(reg,reg)	"	cmfe	%0, %1\n	bgt	%a\n"	14
stmt: GTF8(reg,reg)	"	cmfe	%0, %1\n	bgt	%a\n"	14
stmt: NEF4(reg,reg)	"	cmf	%0, %1\n	bne	%a\n"	14
stmt: NEF8(reg,reg)	"	cmf	%0, %1\n	bne	%a\n"	14

alab: ADDRGP4		"%a"

reg: CALLF4(alab)	"	bl	%0\n"	3
reg: CALLF8(alab)	"	bl	%0\n"	3
reg: CALLI4(alab)	"	bl	%0\n"	3
reg: CALLP4(alab)	"	bl	%0\n"	3
reg: CALLU4(alab)	"	bl	%0\n"	3
stmt: CALLV(alab)	"	bl	%0\n"	3

reg: CALLF4(reg)	"	mov	lr, pc\n	mov	pc,%0\n"	4
reg: CALLF8(reg)	"	mov	lr, pc\n	mov	pc,%0\n"	4
reg: CALLI4(reg)	"	mov	lr, pc\n	mov	pc,%0\n"	4
reg: CALLP4(reg)	"	mov	lr, pc\n	mov	pc,%0\n"	4
reg: CALLU4(reg)	"	mov	lr, pc\n	mov	pc,%0\n"	4
stmt: CALLV(reg)	"	mov	lr, pc\n	mov	pc,%0\n"	4

stmt: RETF4(reg)	"# ret\n"	1
stmt: RETF8(reg)	"# ret\n"	1
stmt: RETI4(reg)	"# ret\n"	1
stmt: RETU4(reg)	"# ret\n"	1
stmt: RETP4(reg)	"# ret\n"	1
stmt: RETV(reg)		"# ret\n"	1
stmt: ARGF4(reg)	"# arg\n"	10
stmt: ARGF8(reg)	"# arg\n"	10
stmt: ARGI4(reg)	"# arg\n"	4
stmt: ARGP4(reg)	"# arg\n"	4
stmt: ARGU4(reg)	"# arg\n"	4

stmt: ARGB(INDIRB(reg))	"# argb %0\n"      10
stmt: ASGNB(reg,INDIRB(reg))	"# asgnb %0 %1\n"  10
%%
static void progend(void)
{
  print("\n	; Compilation complete.\n");
}

#define WO(s,v,a)		\
  if(!strcmp(argv[i], s))	\
    v = a;

static void progbeg(int argc, char *argv[])
{
  int i;
  char *r_names[] = {
    "a1", "a2", "a3", "a4",
    "v1", "v2", "v3", "v4", "v5", "v6",
    "sl", "fp", "ip", "sp", "lr", "pc",
    "split1",   "split2",   0 };

  {
    union {
      char c;
      int i;
    } u;
    u.i = 0;
    u.c = 1;
    swap = ((int)(u.i == 1)) != IR->little_endian;
  }

  infunc = 0;
  check_stack = 1;
  fast_locals = 1;		/* set to 0 for code production in all cases */
  use_fpincall = 0;
  save_fps = 0;
  print("; Code generated by lcc v4.0 (ARM/RISC OS back-end v%s Stu Smith)\n\n",
            ARMVERS);
  parseflags(argc, argv);
  for(i = 0; i < argc; i++) {
    if(!strcmp(argv[i], "-Woversion"))
      fprint(stderr, "rcc: ARM/RISC OS back-end v%s (%s) Stu Smith\n",
        ARMVERS, __DATE__);

    WO("-Wocheck-stack",     check_stack,  1);
    WO("-Wofast-locals",     fast_locals,  1);
    WO("-Wouse-fpincall",    use_fpincall, 1);
    WO("-Wosave-fps",        save_fps,     1);

    WO("-Wono-check-stack",  check_stack,  0);
    WO("-Wono-fast-locals",  fast_locals,  0);
    WO("-Wono-use-fpincall", use_fpincall, 0);
    WO("-Wono-save-fps",     save_fps,     0);

  }

  print(";	Stack checking  -- %s\n", check_stack ? "on" : "off");
  print(";	Faster locals   -- %s\n", fast_locals ? "on" : "off");
  print(";	Using FREGs     -- %s\n", use_fpincall ? "on" : "off");
  print(";	Saving FP regs  -- %s\n", save_fps ? "on" : "off");

  print("\n");

  for(i = 0; i < 32; i++)
    ireg[i] = freg[i] = NULL;

  for(i = 0; r_names[i]; i++)
    ireg[i] = mkreg(r_names[i], i, 1, IREG);

  for (i = 0; i < 8; i++)
    freg[i] = mkreg("f%d", i, 1, FREG);

  a1a2 = mkreg("a1a2", 0, 3, IREG);	/* illegal reg names help debugging */
  a2a3 = mkreg("a2a3", 1, 3, IREG);
  a3a4 = mkreg("a3a4", 2, 3, IREG);
  splitdr = mkreg("split", 3, 3, IREG);
  fregw = mkwildcard(freg);
  iregw = mkwildcard(ireg);
  tmask[IREG] = INTTMP; tmask[FREG] = FLTTMP;
  vmask[IREG] = INTVAR; vmask[FREG] = FLTVAR;
  blkreg = mkreg("v1", ARM_V1, 7, IREG);

  for(i = 0; i < 16; i++)
    print("	RN:		%s	%d\n", ireg[i]->x.name, i);
  for(i = 0; i < 8; i++)
    print("	FN:		%s	%d\n", freg[i]->x.name, i);
  print("\n	TEMP:		lr\n");
  print("\n	CREATOR:	\"[lcc v4.0 (ARM/RISC OS back-end v%s Stu Smith)]\"\n\n", ARMVERS);
}

#undef WO

static Symbol rmap(int opk)
{
  switch (optype(opk)) {
    case I: case U: case P: case B:
      return iregw;
    case F:
      return fregw;
    default:
      return 0;
  }
}

static void target(Node p)
{
  assert(p);
  switch (specific(p->op)) {
    case DIV+I: case MOD+I: case DIV+U: case MOD+U:
      setreg(p, ireg[ARM_A1]);
      rtarget(p, 0, ireg[ARM_A2]);
      rtarget(p, 1, ireg[ARM_A1]);
      break;
    case CALL+V:
      setreg(p, ireg[ARM_A1]);
      break;
    case CALL+F:
      setreg(p, freg[0]);
      break;
    case CALL+I: case CALL+P: case CALL+U:
      setreg(p, ireg[ARM_A1]);
      break;
    case RET+F:
      rtarget(p, 0, freg[0]);
      p->kids[0]->x.registered = 1;
      break;
    case RET+I: case RET+U: case RET+P:
      rtarget(p, 0, ireg[ARM_A1]);
      p->kids[0]->x.registered = 1;
      break;
    case ARG+I: case ARG+P: case ARG+U:
      {
        Symbol q = argreg(p->syms[2]->u.c.v.i, opsize(p->op));

        assert(opsize(p->op) <= 4);
        if(q) {
          rtarget(p, 0, q);
          setreg(p, q);
        }
      }
      break;
    case ASGN+B: rtarget(p->kids[1], 0, blkreg); break;
    case ARG+B:  rtarget(p->kids[0], 0, blkreg); break;
  }
}

static void clobber(Node p)
{
  assert(p);
  /* spill for various operations
     if we are saving some FP registers in function, we don't need
     to spill so many */
  switch (specific(p->op)) {
    case DIV+I: case DIV+U: case MOD+I: case MOD+U:
      spill(  INTCLOBBER,          IREG, p);
      break;
    case CALL+F:
      spill(  INTCLOBBER | INTRET, IREG, p);
      if(save_fps)
        spill(FLSCLOBBER,          FREG, p);
      else
        spill(FLTCLOBBER,          FREG, p);
      break;
    case CALL+I: case CALL+P: case CALL+U:
      spill(  INTCLOBBER,          IREG, p);
      if(save_fps)
        spill(FLSCLOBBER | FLTRET, FREG, p);
      else
        spill(FLTCLOBBER | FLTRET, FREG, p);
      break;
    case CALL+V:
      spill(  INTCLOBBER | INTRET, IREG, p);
      if(save_fps)
        spill(FLSCLOBBER | FLTRET, FREG, p);
      else
        spill(FLTCLOBBER | FLTRET, FREG, p);
      break;
    case ARG+F: {	/* spill necessary regs out of a1-a4 */
      Symbol q = argreg(p->syms[2]->u.c.v.i, opsize(p->op));
      if(q == ireg[0])	spill(1, IREG, p);
      if(q == ireg[1])	spill(2, IREG, p);
      if(q == ireg[2])	spill(4, IREG, p);
      if(q == ireg[3])	spill(8, IREG, p);
      if(q == a1a2)	spill(3, IREG, p);
      if(q == a2a3)	spill(6, IREG, p);
      if(q == a3a4)	spill(12, IREG, p);
      break;
    }
  }
}

static void emit2(Node p)
{
  int dst, off, src, sz, ty;
  int a, b, c, t, i;
  Symbol q;

  switch (specific(p->op)) {
    case ADDRF+P:
      /* address of formal parameter is different for variadic and
       * non-variadic routines.
       */
      assert(opsize(p->op) == 4);
      if(varargs) {
        print("fp, #%d", p->syms[0]->x.offset + 4);
      } else {
        if(p->syms[0]->x.offset <= 12)
          print("sp, #%d", maxargoffset + p->syms[0]->x.offset);
        else
          print("fp, #%d", p->syms[0]->x.offset - 12);
      }
      break;
    case ARG+F:
      /* float args are sometimes passed in integer registers.
       * lcc front-end can't handle this, so we have to convert.
       */
      sz = opsize(p->op);
      if(sz == 4) {
        off = p->syms[2]->u.c.v.i;
        src = getregnum(p->x.kids[0]);
        if(off >= 16) {
          /* F4 passed on stack */
          print("	stfs	%s, [sp, #%d]	; ARGF4 on stack\n",
            freg[src]->x.name, off-16);
          if(use_fpincall && (off-16 == 0))
            print("	mvfs	f0, %s\n", freg[src]->x.name);
        } else {
          /* F4 passed in ireg */
          print("	stfs	%s, [sp, #-4]	; ARGF4\n", freg[src]->x.name);
          print("	ldr	%s, [sp, #-4]	; in ireg\n", ireg[off/4]->x.name);
          if(use_fpincall && (off-16 == 0))
            print("	mvfs	f0, %s\n", freg[src]->x.name);
        }
      } else if(sz == 8) {
        off = p->syms[2]->u.c.v.i;
        src = getregnum(p->x.kids[0]);
        if(off == 12) {
          /* F8 passed in a4, stack */
          print("	stfd	%s, [sp, #-4]	; ARGF8\n", freg[src]->x.name);
          print("	ldr	a4, [sp, #-4]	; in a4, stack\n");
          if(use_fpincall && (off-16 == 0))
            print("	mvfd	f0, %s\n", freg[src]->x.name);
        } else if(off >= 16) {
          /* F8 passed on stack */
          print("	stfd	%s, [sp, #%d]	; ARGF8 on stack\n",
            freg[src]->x.name, off-16);
          if(use_fpincall && (off-16 == 0))
            print("	mvfd	f0, %s\n", freg[src]->x.name);
        } else {
          /* F8 passed in 2 iregs */
          print("	stfd	%s, [sp, #-8]	; ARGF8\n", freg[src]->x.name);
          print("	ldr	%s, [sp, #-8]	; in 2 iregs\n", ireg[off/4]->x.name);
          print("	ldr	%s, [sp, #-4]	; ...\n", ireg[off/4+1]->x.name);
          if(use_fpincall && (off-16 == 0))
            print("	mvfd	f0, %s	; cfa\n", freg[src]->x.name);
        }
      }
      break;
    case ARG+I: case ARG+P: case ARG+U:
      /* normal args: target will have arranged for those passed
       * in registers to be in the regs, so we only have to save
       * other args onto the stack.
       */
      sz = opsize(p->op);
      q = argreg(p->syms[2]->u.c.v.i, sz);
      src = getregnum(p->x.kids[0]);
      if(q == NULL)
        print("	str	%s, [sp, #%d]\n", ireg[src]->x.name, p->syms[2]->u.c.v.i - 16);
      break;
    case ASGN+B:
      /* structure assign: simple copy */
      dalign = salign = p->syms[1]->u.c.v.i;
      if(p->syms[0]->u.c.v.i > 0)
        blkcopy(getregnum(p->x.kids[0]), 0,
                getregnum(p->x.kids[1]), 0,
                p->syms[0]->u.c.v.i, tmpregs);
      else
        print(";	ASGNB size < 0\n");
      break;
    case ARG+B:
      /* structure argument: some parts may be passed in a1-a4, the rest on
       * stack. blkcopy() requires a non-negative size, so we must check that.
       */
      t = p->syms[2]->u.c.v.i;
      dalign = 4;
      salign = p->syms[1]->u.c.v.i;
      if(t < 16) {
        off = p->syms[2]->u.c.v.i + p->syms[0]->u.c.v.i;
        dst = p->syms[2]->u.c.v.i;
        for(i = 0 ; dst <= 12 && dst < off; dst += 4) {
          print("	ldr	%s, [%s, #%d]\n",
            ireg[(dst/4)]->x.name, ireg[getregnum(p->x.kids[0])]->x.name, i);
          i += 4;
        }
        if((p->syms[0]->u.c.v.i - (16-t)) > 0)
          blkcopy(ARM_SP, 0,
            getregnum(p->x.kids[0]), (16-t),
            p->syms[0]->u.c.v.i - (16-t), tmpregs);
      } else {
        if(p->syms[0]->u.c.v.i > 0)
          blkcopy(ARM_SP, p->syms[2]->u.c.v.i - 16,
            getregnum(p->x.kids[0]), 0,
            p->syms[0]->u.c.v.i, tmpregs);
        else
          print(";	ARGB size < 0\n");
      }
      break;
  }
}

static Symbol argreg(int offset, int sz)
{
  assert((offset&3) == 0);
  if(offset > 12)
    return NULL;
  else if(sz == 8 && offset <= 8) {
    if(offset == 0)      return a1a2;	/* Pair */
    else if(offset == 4) return a2a3;	/* Pair */
    else if(offset == 8) return a3a4;	/* Pair */
    assert(0);
  } else if(sz == 8 && offset == 12)
    return splitdr;			/* Pair */
  return ireg[(offset/4)];
}

static void doarg(Node p)
{
  /*
  static int argno;

  if(argoffset == 0)
    argno = 0;
  if(optype(p->op) == F)
    p->x.argno = argno++;
  */
  p->syms[2] = intconst(mkactual(4, p->syms[0]->u.c.v.i));
}

static void local(Symbol p)
{
  if(askregvar(p, rmap(ttob(p->type))) == 0)
    mkauto(p);
}

static void function(Symbol f, Symbol caller[], Symbol callee[], int ncalls)
{
  int i, saved, numargs, nsaved = 0;
  Symbol r, argregs[4];

  fourstore = 0;
  splitdouble = 0;
  usedmask[IREG] = usedmask[FREG] = 0;
  freemask[IREG] = freemask[FREG] = ~(unsigned)0;

  for(i = ARM_A1; i <= ARM_V6; i++)
    ireg[i]->x.regnode->vbl = NULL;

  offset = maxoffset = maxargoffset = 0;

  for(numargs = 0; callee[numargs]; numargs++)
    ;
  varargs = variadic(f->type)
    || (numargs > 0 && strcmp(callee[numargs-1]->name, "va_alist") == 0)
    || (numargs > 0 && strcmp(callee[numargs-1]->name, "__builtin_va_alist") == 0);

  for(i = 0; callee[i]; i++) {
    Symbol p = callee[i];
    Symbol q = caller[i];
    assert(q);
    p->x.offset = q->x.offset = offset;
    p->x.name = q->x.name = stringd(offset);
    r = argreg(offset, q->type->size);
    if(r == splitdr)
      splitdouble = 1;
    if(i < 4)
      argregs[i] = r;
    if (varargs)
      p->sclass = AUTO;
    else if(r && ncalls == 0
         && !isstruct(q->type) && !p->addressed
         && !(isfloat(q->type) && r->x.regnode->set == IREG)) {
      p->sclass = q->sclass = REGISTER;
      askregvar(p, r);
      assert(p->x.regnode && p->x.regnode->vbl == p);
      q->x = p->x;
      q->type = p->type;
    } else if(askregvar(p, rmap(ttob(p->type)))
           && r != NULL
           && (isint(p->type) || p->type == q->type)) {
      assert(q->sclass != REGISTER);
      p->sclass = q->sclass = REGISTER;
      q->type = p->type;
    }

    offset += roundup(q->type->size, 4);

    /* first 16 bytes arrive in a1-a4 - we need space */
    if(i < 4)
      fourstore += roundup(q->type->size, 4);
  }

  assert(!caller[i]);
  offset = maxoffset = 0;
  gencode(caller, callee);

  maxargoffset -= 16;		/* first 4 words in a1-a4 */
  if(maxargoffset < 0)
    maxargoffset = 0;

  fourstore = roundup(fourstore, 4);

  if(fourstore > 16 || varargs)
    fourstore = 16;
  if(splitdouble)
    fourstore += 4;

  framesize = roundup(maxoffset+maxargoffset+fourstore, 4);

  print("\n	; FUNCTION: %s\n", f->x.name);
  if(framesize > 4094 && fast_locals) {
    error("must use -Wono-fast-locals with this file\n");
    print("	ERROR:	\"*** large framesize & fast-locals\"\n");
  }
  if(varargs)
    print("	; (variadic)\n");
  print("	; framesize = %d,  ncalls = %d\n", framesize, ncalls);
  print("	; maxargoffset = %d, maxoffset = %d\n", maxargoffset, maxoffset);
  print("	; fourstore = %d\n", fourstore);

  segment(CODE);
  print("%s\n", f->x.name);

  infunc = 1;
  print("	mov	ip, sp\n");
  if(varargs) {
    print("	stmfd	sp!, {a1-a4}\n");
    nsaved = 16;
  }
  print("	stmfd	sp!, {");
  for(i = ARM_V1; i <= ARM_V6; i++)
    if(usedmask[IREG] & (1<<i)) {
      print("%s, ", ireg[i]->x.name);
      nsaved += 4;
    }
  print("fp, ip, lr, pc}\n");
  if(save_fps)
    for(i = 7; i > 3; i--)
      if(usedmask[FREG] & (1<<i)) {
        print("	stfe	%s, [sp, #-12]!\n", freg[i]->x.name);
        nsaved += 12;
      }
  if(varargs)
    print("	sub	fp, ip, #20\n");
  else
    print("	sub	fp, ip, #4\n");
  if(check_stack) {
    print("	cmp	sp, sl\n");
    print("	bllt	x$stack_overflow\n");
  }
  if(framesize > 0)
    print("	sub	sp, sp, #%d\n", framesize);

  for(i = 0; i < 4 && callee[i]; i++) {
    r = argregs[i];
    if(r && r->x.regnode != callee[i]->x.regnode) {
      Symbol out = callee[i];
      Symbol in  = caller[i];
      int rn = r->x.regnode->number;
      int rs = r->x.regnode->set;
      int tyin = ttob(in->type);

      assert(out && in && r && r->x.regnode);
      assert(out->sclass != REGISTER || out->x.regnode);

      if(!varargs) {
        int i, n = (in->type->size + 3)/4;

        for (i = rn; i < rn+n && i <= 3; i++)
          print("	str	%s, [sp, #%d]\n", ireg[i]->x.name,
                in->x.offset + maxargoffset + (i-rn)*4);
      }
      if(out->sclass == REGISTER && rs == IREG
      && (isint(out->type) || out->type == in->type) ) {
        print("	mov	%s, %s\n", ireg[out->x.regnode->number]->x.name,
       				   ireg[rn]->x.name);
      }
    }
  }

  emitcode();

  if(save_fps)
    for(i = 4; i < 8; i++)
      if(usedmask[FREG] & (1<<i)) {
        print("	ldfe	%s, [fp, #-%d]\n", freg[i]->x.name, nsaved - 4);
        nsaved -= 12;
      }

  print("	ldmea	fp, {");
  for(i = ARM_V1; i <= ARM_V6; i++)
    if(usedmask[IREG] & (1<<i))
      print("%s, ", ireg[i]->x.name);
  print("fp, sp, pc}^\n\n");
  infunc = 0;
}

static void defconst(int suffix, int size, Value v)
{
  if(suffix == F && size == 4) {
    float f = v.d;
    print("	dcd	0x%x	; float = %e\n", *(unsigned *)&f, (double)f);
  } else if(suffix == F && size == 8) {
    double d = v.d;
    unsigned *p = (unsigned *)&d;
    print("	dcd	0x%x, 0x%x	; double = %e\n", p[swap], p[!swap], d);
  } else if(suffix == P)
    print("	dcd	0x%x	; ptr\n", v.p);
  else if(size == 1)
    print("	dcb	0x%x\n", suffix == I ? v.i : v.u);
  else if(size == 2)
    print("	dcw	0x%x\n", suffix == I ? v.i : v.u);
  else if(size == 4)
    print("	dcd	0x%x\n", suffix == I ? v.i : v.u);
  else
    print("	ERROR:	\"** Unknown constant type, size = %d, suffix = %d **\"\n", size, suffix);
}

static void defaddress(Symbol p)
{
  print("	dcd	%s\n", p->x.name);
}

static void defstring(int n, char *str)
{
  char *s;
  int c = 0, comma = 0, dcbs = 0;

  print("	; string = \"");
  for(s = str; s < str + n; s++) {
    if((*s)&0377 == '\\')
      print("\\\\");
    else if(isprint((*s)&0377))
      print("%c", (*s)&0377);
    else
      switch((*s)&0377) {
        case 0 : print("\\0"); break;
        case 9 : print("\\t"); break;
        case 10: print("\\n"); break;
        case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
        case 11: case 12: case 13: case 14: case 15:
          print("\\x0%x", (*s&0377)); break;
        default:
          print("\\x%x", (*s&0377));
      }
  }
  print("\"\n");

  print("	dcb	");
  for(s = str; s < str + n; s++) {
    if(comma)
      print(", ");
    else
      comma = 1;
    if(((*s)&0377) < 16)
      print("0x0%x", (*s)&0377);
    else
      print("0x%x", (*s)&0377);
    dcbs++;
    if(dcbs > 9) {
      if(s < str+n-1)
        print("\n	dcb	");
      comma = 0;
      dcbs = 0;
    }
  }
  print("\n");
}

static void export(Symbol p)
{
  print("\n	EXPORT:		%s\n", p->x.name);
}

static void import(Symbol p)
{
}

static void defsymbol(Symbol p)
{
  char f;

  if(p->scope >= LOCAL && p->sclass == STATIC) {
    p->x.name = stringf("|L.%d|", genlabel(1));
  } else {
    if(p->generated) {
      if(p->scope == LABELS)
        p->x.name = stringf("|.L.%s|", p->name);
      else
        p->x.name = stringf("|L.%s|", p->name);
    } else {
      assert(p->scope != CONSTANTS || isint(p->type) || isptr(p->type));
      f = *(p->name);
      if( f == '-' || (f >= '0' && f <= '9') ) {
        p->x.name = stringf("%s", p->name);
      } else
        p->x.name = stringf("|%s|", p->name);
    }
  }
}

static void address(Symbol q, Symbol p, int n)
{
  q->x.offset = p->x.offset + n;
  if(p->scope == GLOBAL
  || p->sclass == STATIC || p->sclass == EXTERN)
    q->x.name = stringf("%s%s%d", p->x.name,
      n >= 0 ? "+" : "", n);
  else
    q->x.name = stringd(q->x.offset);
}

static void global(Symbol p)
{
  print("%s\n", p->x.name);
  if(p->u.seg == BSS)
    print("	BLOCK:	%d\n", p->type->size);
}

static void segment(int n)
{
  cseg = n;
  if(!infunc) {
    switch (n) {
      case CODE: print("\n	SEGMENT:	CODE\n"); break;
      case LIT:  print("\n	SEGMENT:	LIT\n");  break;
      case BSS:  print("\n	SEGMENT:	BSS\n");  break;
      case DATA: print("\n	SEGMENT:	DATA\n"); break;
    }
  }
}

static void space(int n)
{
  if(cseg != BSS)
    print("	dbb	%d, 0\n", n);
}

static void blkloop(int dreg, int doff, int sreg, int soff, int size, int tmps[])
{
  int lab = genlabel(1);

  print(";	blkloop() from [%s, #%d] to [%s, #%d] size %d\n",
    ireg[sreg]->x.name, soff, ireg[dreg]->x.name, doff, size);
  print("	add	%s, %s, #%d\n", ireg[sreg]->x.name, ireg[sreg]->x.name, size&~7);
  print("	add	%s, %s, #%d\n", ireg[tmps[2]]->x.name, ireg[dreg]->x.name, size&~7);
  if(size&7 == 2) {
    blkcopy(tmps[2], doff,   sreg, soff,   1, tmps);
    blkcopy(tmps[2], doff+1, sreg, soff+1, 1, tmps);
  } else
    blkcopy(tmps[2], doff, sreg, soff, size&7, tmps);
  print("|.L.%d|\n", lab);
  print("	sub	%s, %s, #%d\n", ireg[sreg]->x.name, ireg[sreg]->x.name, 8);
  print("	sub	%s, %s, #%d\n", ireg[tmps[2]]->x.name, ireg[tmps[2]]->x.name, 8);
  if(soff == 0 && doff == 0) {
    print("	ldmia	%s, {%s, %s}\n", ireg[sreg]->x.name,
					 ireg[tmps[0]]->x.name,
					 ireg[tmps[1]]->x.name);
    print("	stmia	%s, {%s, %s}\n", ireg[tmps[2]]->x.name,
					 ireg[tmps[0]]->x.name,
					 ireg[tmps[1]]->x.name);
  } else
    blkcopy(tmps[2], doff, sreg, soff, 8, tmps);
  print("	cmp	%s, %s\n	blo	|.L.%d|\n", ireg[dreg]->x.name, ireg[tmps[2]]->x.name, lab);
}

static void blkfetch(int size, int off, int reg, int tmp)
{
  assert(size == 1 || size == 2 || size == 4);
  assert(salign >= size);
  if(size == 1)
    print("	ldrb	%s, [%s, #%d]\n", ireg[tmp]->x.name,
					  ireg[reg]->x.name, off);
  else if(size == 2)
    assert(0);
  else
    print("	ldr	%s, [%s, #%d]\n", ireg[tmp]->x.name,
					  ireg[reg]->x.name, off);
}

static void blkstore(int size, int off, int reg, int tmp)
{
  assert(size == 1 || size == 2 || size == 4);
  assert(dalign >= size);
  if(size == 1)
    print("	strb	%s, [%s, #%d]\n", ireg[tmp]->x.name,
					  ireg[reg]->x.name, off);
  else if(size == 2)
    assert(0);
  else
    print("	str	%s, [%s, #%d]\n", ireg[tmp]->x.name,
					  ireg[reg]->x.name, off);
}

static int bitcount(unsigned mask)
{
  unsigned i, n = 0;

  for (i = 1; i; i <<= 1)
    if (mask&i)
      n++;
  return n;
}

Interface armIR = {
  /* size, align, outofline */

  1, 1, 0,  /* char */
  2, 2, 0,  /* short */
  4, 4, 0,  /* int */
  4, 4, 0,  /* long */
  4, 4, 1,  /* float */
  8, 4, 1,  /* double */
  8, 4, 1,  /* long double */
  4, 4, 0,  /* T * */
  0, 4, 0,  /* struct */
  1,  /* little_endian */
  1,  /* mulops_calls */
  0,  /* wants_callb */
  1,  /* wants_argb */
  1,  /* left_to_right */
  0,  /* wants_dag */
  address,
  blockbeg,
  blockend,
  defaddress,
  defconst,
  defstring,
  defsymbol,
  emit,
  export,
  function,
  gen,
  global,
  import,
  local,
  progbeg,
  progend,
  segment,
  space,
  0, 0, 0, 0, 0, 0, 0,	/* no debug output, see sparc.md
  			   for an example of what can be done */
  {
    1,      /* max_unaligned_load */
    rmap,
    blkfetch, blkstore, blkloop,
    _label,
    _rule,
    _nts,
    _kids,
    _string,
    _templates,
    _isinstruction,
    _ntname,
    emit2,
    doarg,
    target,
    clobber,

  }
};
