[MSBuild] Fix minor assembly resolution issue
[mono.git] / mono / arch / arm / arm-codegen.c
1 /*
2  * arm-codegen.c
3  * Copyright (c) 2002 Sergey Chaban <serge@wildwestsoftware.com>
4  */
5
6 #include "arm-codegen.h"
7
8
9 arminstr_t* arm_emit_std_prologue(arminstr_t* p, unsigned int local_size) {
10         ARM_MOV_REG_REG(p, ARMREG_IP, ARMREG_SP);
11
12         /* save args */
13         ARM_PUSH(p,   (1 << ARMREG_A1)
14                     | (1 << ARMREG_A2)
15                     | (1 << ARMREG_A3)
16                     | (1 << ARMREG_A4));
17
18         ARM_PUSH(p, (1U << ARMREG_IP) | (1U << ARMREG_LR));
19
20         if (local_size != 0) {
21                 if ((local_size & (~0xFF)) == 0) {
22                         ARM_SUB_REG_IMM8(p, ARMREG_SP, ARMREG_SP, local_size);
23                 } else {
24                         /* TODO: optimize */
25                         p = arm_mov_reg_imm32(p, ARMREG_IP, local_size);
26                         ARM_SUB_REG_REG(p, ARMREG_SP, ARMREG_SP, ARMREG_IP);
27                         ARM_ADD_REG_IMM8(p, ARMREG_IP, ARMREG_IP, sizeof(armword_t));
28                         ARM_LDR_REG_REG(p, ARMREG_IP, ARMREG_SP, ARMREG_IP);
29                 }
30         }
31
32         return p;
33 }
34
35 arminstr_t* arm_emit_std_epilogue(arminstr_t* p, unsigned int local_size, int pop_regs) {
36         if (local_size != 0) {
37                 if ((local_size & (~0xFF)) == 0) {
38                         ARM_ADD_REG_IMM8(p, ARMREG_SP, ARMREG_SP, local_size);
39                 } else {
40                         /* TODO: optimize */
41                         p = arm_mov_reg_imm32(p, ARMREG_IP, local_size);
42                         ARM_ADD_REG_REG(p, ARMREG_SP, ARMREG_SP, ARMREG_IP);
43                 }
44         }
45
46         ARM_POP_NWB(p, (1 << ARMREG_SP) | (1 << ARMREG_PC) | (pop_regs & 0x3FF));
47
48         return p;
49 }
50
51
52 /* do not push A1-A4 */
53 arminstr_t* arm_emit_lean_prologue(arminstr_t* p, unsigned int local_size, int push_regs) {
54         ARM_MOV_REG_REG(p, ARMREG_IP, ARMREG_SP);
55         /* push_regs upto R10 will be saved */
56         ARM_PUSH(p, (1U << ARMREG_IP) | (1U << ARMREG_LR) | (push_regs & 0x3FF));
57
58         if (local_size != 0) {
59                 if ((local_size & (~0xFF)) == 0) {
60                         ARM_SUB_REG_IMM8(p, ARMREG_SP, ARMREG_SP, local_size);
61                 } else {
62                         /* TODO: optimize */
63                         p = arm_mov_reg_imm32(p, ARMREG_IP, local_size);
64                         ARM_SUB_REG_REG(p, ARMREG_SP, ARMREG_SP, ARMREG_IP);
65                         /* restore IP from stack */
66                         ARM_ADD_REG_IMM8(p, ARMREG_IP, ARMREG_IP, sizeof(armword_t));
67                         ARM_LDR_REG_REG(p, ARMREG_IP, ARMREG_SP, ARMREG_IP);
68                 }
69         }
70
71         return p;
72 }
73
74 /* Bit scan forward. */
75 int arm_bsf(armword_t val) {
76         int i;
77         armword_t mask;
78
79         if (val == 0) return 0;
80         for (i=1, mask=1; (i <= 8 * sizeof(armword_t)) && ((val & mask) == 0); ++i, mask<<=1);
81
82         return i;
83 }
84
85
86 int arm_is_power_of_2(armword_t val) {
87         return ((val & (val-1)) == 0);
88 }
89
90
91 /*
92  * returns:
93  *   1 - unable to represent
94  *   positive even number - MOV-representable
95  *   negative even number - MVN-representable
96  */
97 int calc_arm_mov_const_shift(armword_t val) {
98         armword_t mask;
99         int res = 1, shift;
100
101         for (shift=0; shift < 32; shift+=2) {
102                 mask = ARM_SCALE(0xFF, shift);
103                 if ((val & (~mask)) == 0) {
104                         res = shift;
105                         break;
106                 }
107                 if (((~val) & (~mask)) == 0) {
108                         res = -shift - 2;
109                         break;
110                 }
111         }
112
113         return res;
114 }
115
116
117 int is_arm_const(armword_t val) {
118         int res;
119         res = arm_is_power_of_2(val);
120         if (!res) {
121                 res = calc_arm_mov_const_shift(val);
122                 res = !(res < 0 || res == 1);
123         }
124         return res;
125 }
126
127
128 int arm_const_steps(armword_t val) {
129         int shift, steps = 0;
130
131         while (val != 0) {
132                 shift = (arm_bsf(val) - 1) & (~1);
133                 val &= ~(0xFF << shift);
134                 ++steps;
135         }
136         return steps;
137 }
138
139
140 /*
141  * ARM cannot load arbitrary 32-bit constants directly into registers;
142  * widely used work-around for this is to store constants into a
143  * PC-addressable pool and use LDR instruction with PC-relative address
144  * to load constant into register. Easiest way to implement this is to
145  * embed constant inside a function with unconditional branch around it.
146  * The above method is not used at the moment.
147  * This routine always emits sequence of instructions to generate
148  * requested constant. In the worst case it takes 4 instructions to
149  * synthesize a constant - 1 MOV and 3 subsequent ORRs.
150  */
151 arminstr_t* arm_mov_reg_imm32_cond(arminstr_t* p, int reg, armword_t imm32, int cond) {
152         int mov_op;
153         int step_op;
154         int snip;
155         int shift = calc_arm_mov_const_shift(imm32);
156
157         if ((shift & 0x80000001) != 1) {
158                 if (shift >= 0) {
159                         ARM_MOV_REG_IMM_COND(p, reg, imm32 >> ((32 - shift) & 31), shift, cond);
160                 } else {
161                         ARM_MVN_REG_IMM_COND(p, reg, (imm32 ^ (~0)) >> ((32 + 2 + shift) & 31), (-shift - 2), cond);
162                 }
163         } else {
164                 mov_op = ARMOP_MOV;
165                 step_op = ARMOP_ORR;
166
167                 if (arm_const_steps(imm32) > arm_const_steps(~imm32)) {
168                         mov_op = ARMOP_MVN;
169                         step_op = ARMOP_SUB;
170                         imm32 = ~imm32;
171                 }
172
173                 shift = (arm_bsf(imm32) - 1) & (~1);
174                 snip = imm32 & (0xFF << shift);
175                 ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((unsigned)snip >> shift, (32 - shift) >> 1, reg, 0, 0, mov_op, cond));
176
177                 while ((imm32 ^= snip) != 0) {
178                         shift = (arm_bsf(imm32) - 1) & (~1);
179                         snip = imm32 & (0xFF << shift);
180                         ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((unsigned)snip >> shift, (32 - shift) >> 1, reg, reg, 0, step_op, cond));
181                 }
182         }
183
184         return p;
185 }
186
187
188 arminstr_t* arm_mov_reg_imm32(arminstr_t* p, int reg, armword_t imm32) {
189         return arm_mov_reg_imm32_cond(p, reg, imm32, ARMCOND_AL);
190 }
191
192
193