]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/config/avr/avr-dimode.md
Implement light-weight DImode support.
[thirdparty/gcc.git] / gcc / config / avr / avr-dimode.md
1 ;; Machine description for GNU compiler,
2 ;; for Atmel AVR micro controllers.
3 ;; Copyright (C) 1998 - 2011
4 ;; Free Software Foundation, Inc.
5 ;; Contributed by Georg Lay (avr@gjlay.de)
6 ;;
7 ;; This file is part of GCC.
8 ;;
9 ;; GCC is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 3, or (at your option)
12 ;; any later version.
13 ;;
14 ;; GCC is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18 ;;
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GCC; see the file COPYING3. If not see
21 ;; <http://www.gnu.org/licenses/>.
22
23 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
24
25 ;; The purpose of this file is to provide a light-weight DImode
26 ;; implementation for AVR. The trouble with DImode is that tree -> RTL
27 ;; lowering leads to really unpleasant code for operations that don't
28 ;; work byte-wise like NEG, PLUS, MINUS, etc. Defining optabs entries for
29 ;; them won't help because the optab machinery assumes these operations
30 ;; are cheap and does not check if a libgcc implementation is available.
31 ;;
32 ;; The DImode insns are all straight forward -- except movdi. The approach
33 ;; of this implementation is to provide DImode insns without the burden of
34 ;; introducing movdi.
35 ;;
36 ;; The caveat is that if there are insns for some mode, there must also be a
37 ;; respective move insn that describes reloads. Therefore, this
38 ;; implementation uses an accumulator-based model with two hard-coded,
39 ;; accumulator-like registers
40 ;;
41 ;; A[] = reg:DI 18
42 ;; B[] = reg:DI 10
43 ;;
44 ;; so that no DImode insn contains pseudos or needs reloading.
45
46 (define_constants
47 [(ACC_A 18)
48 (ACC_B 10)])
49
50 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
51 ;; Addition
52 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
53
54 (define_expand "adddi3"
55 [(parallel [(match_operand:DI 0 "general_operand" "")
56 (match_operand:DI 1 "general_operand" "")
57 (match_operand:DI 2 "general_operand" "")])]
58 "avr_have_dimode"
59 {
60 rtx acc_a = gen_rtx_REG (DImode, ACC_A);
61
62 emit_move_insn (acc_a, operands[1]);
63
64 if (s8_operand (operands[2], VOIDmode))
65 {
66 emit_move_insn (gen_rtx_REG (QImode, REG_X), operands[2]);
67 emit_insn (gen_adddi3_const8_insn ());
68 }
69 else if (CONST_INT_P (operands[2])
70 || CONST_DOUBLE_P (operands[2]))
71 {
72 emit_insn (gen_adddi3_const_insn (operands[2]));
73 }
74 else
75 {
76 emit_move_insn (gen_rtx_REG (DImode, ACC_B), operands[2]);
77 emit_insn (gen_adddi3_insn ());
78 }
79
80 emit_move_insn (operands[0], acc_a);
81 DONE;
82 })
83
84 (define_insn "adddi3_insn"
85 [(set (reg:DI ACC_A)
86 (plus:DI (reg:DI ACC_A)
87 (reg:DI ACC_B)))]
88 "avr_have_dimode"
89 "%~call __adddi3"
90 [(set_attr "adjust_len" "call")
91 (set_attr "cc" "clobber")])
92
93 (define_insn "adddi3_const8_insn"
94 [(set (reg:DI ACC_A)
95 (plus:DI (reg:DI ACC_A)
96 (sign_extend:DI (reg:QI REG_X))))]
97 "avr_have_dimode"
98 "%~call __adddi3_s8"
99 [(set_attr "adjust_len" "call")
100 (set_attr "cc" "clobber")])
101
102 (define_insn "adddi3_const_insn"
103 [(set (reg:DI ACC_A)
104 (plus:DI (reg:DI ACC_A)
105 (match_operand:DI 0 "const_double_operand" "n")))]
106 "avr_have_dimode
107 && !s8_operand (operands[0], VOIDmode)"
108 {
109 return avr_out_plus64 (operands[0], NULL);
110 }
111 [(set_attr "adjust_len" "plus64")
112 (set_attr "cc" "clobber")])
113
114
115 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
116 ;; Subtraction
117 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
118
119 (define_expand "subdi3"
120 [(parallel [(match_operand:DI 0 "general_operand" "")
121 (match_operand:DI 1 "general_operand" "")
122 (match_operand:DI 2 "general_operand" "")])]
123 "avr_have_dimode"
124 {
125 rtx acc_a = gen_rtx_REG (DImode, ACC_A);
126
127 emit_move_insn (acc_a, operands[1]);
128 emit_move_insn (gen_rtx_REG (DImode, ACC_B), operands[2]);
129 emit_insn (gen_subdi3_insn ());
130 emit_move_insn (operands[0], acc_a);
131 DONE;
132 })
133
134 (define_insn "subdi3_insn"
135 [(set (reg:DI ACC_A)
136 (minus:DI (reg:DI ACC_A)
137 (reg:DI ACC_B)))]
138 "avr_have_dimode"
139 "%~call __subdi3"
140 [(set_attr "adjust_len" "call")
141 (set_attr "cc" "set_czn")])
142
143
144 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
145 ;; Negation
146 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
147
148 (define_expand "negdi2"
149 [(parallel [(match_operand:DI 0 "general_operand" "")
150 (match_operand:DI 1 "general_operand" "")])]
151 "avr_have_dimode"
152 {
153 rtx acc_a = gen_rtx_REG (DImode, ACC_A);
154
155 emit_move_insn (acc_a, operands[1]);
156 emit_insn (gen_negdi2_insn ());
157 emit_move_insn (operands[0], acc_a);
158 DONE;
159 })
160
161 (define_insn "negdi2_insn"
162 [(set (reg:DI ACC_A)
163 (neg:DI (reg:DI ACC_A)))]
164 "avr_have_dimode"
165 "%~call __negdi2"
166 [(set_attr "adjust_len" "call")
167 (set_attr "cc" "clobber")])
168
169
170 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
171 ;; Comparison
172 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
173
174 (define_expand "conditional_jump"
175 [(set (pc)
176 (if_then_else
177 (match_operator 0 "ordered_comparison_operator" [(cc0)
178 (const_int 0)])
179 (label_ref (match_operand 1 "" ""))
180 (pc)))]
181 "avr_have_dimode")
182
183 (define_expand "cbranchdi4"
184 [(parallel [(match_operand:DI 1 "register_operand" "")
185 (match_operand:DI 2 "nonmemory_operand" "")
186 (match_operator 0 "ordered_comparison_operator" [(cc0)
187 (const_int 0)])
188 (label_ref (match_operand 3 "" ""))])]
189 "avr_have_dimode"
190 {
191 rtx acc_a = gen_rtx_REG (DImode, ACC_A);
192
193 emit_move_insn (acc_a, operands[1]);
194
195 if (s8_operand (operands[2], VOIDmode))
196 {
197 emit_move_insn (gen_rtx_REG (QImode, REG_X), operands[2]);
198 emit_insn (gen_compare_const8_di2 ());
199 }
200 else if (CONST_INT_P (operands[2])
201 || CONST_DOUBLE_P (operands[2]))
202 {
203 emit_insn (gen_compare_const_di2 (operands[2]));
204 }
205 else
206 {
207 emit_move_insn (gen_rtx_REG (DImode, ACC_B), operands[2]);
208 emit_insn (gen_compare_di2 ());
209 }
210
211 emit_jump_insn (gen_conditional_jump (operands[0], operands[3]));
212 DONE;
213 })
214
215 (define_insn "compare_di2"
216 [(set (cc0)
217 (compare (reg:DI ACC_A)
218 (reg:DI ACC_B)))]
219 "avr_have_dimode"
220 "%~call __cmpdi2"
221 [(set_attr "adjust_len" "call")
222 (set_attr "cc" "compare")])
223
224 (define_insn "compare_const8_di2"
225 [(set (cc0)
226 (compare (reg:DI ACC_A)
227 (sign_extend:DI (reg:QI REG_X))))]
228 "avr_have_dimode"
229 "%~call __cmpdi2_s8"
230 [(set_attr "adjust_len" "call")
231 (set_attr "cc" "compare")])
232
233 (define_insn "compare_const_di2"
234 [(set (cc0)
235 (compare (reg:DI ACC_A)
236 (match_operand:DI 0 "const_double_operand" "n")))
237 (clobber (match_scratch:QI 1 "=&d"))]
238 "avr_have_dimode
239 && !s8_operand (operands[0], VOIDmode)"
240 {
241 return avr_out_compare64 (insn, operands, NULL);
242 }
243 [(set_attr "adjust_len" "compare64")
244 (set_attr "cc" "compare")])
245
246
247 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
248 ;; Shifts and Rotate
249 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
250
251 (define_code_iterator di_shifts
252 [ashift ashiftrt lshiftrt rotate])
253
254 ;; Shift functions from libgcc are called without defining these insns,
255 ;; but with them we can describe their reduced register footprint.
256
257 ;; "ashldi3"
258 ;; "ashrdi3"
259 ;; "lshrdi3"
260 ;; "rotldi3"
261 (define_expand "<code_stdname>di3"
262 [(parallel [(match_operand:DI 0 "general_operand" "")
263 (di_shifts:DI (match_operand:DI 1 "general_operand" "")
264 (match_operand:QI 2 "general_operand" ""))])]
265 "avr_have_dimode"
266 {
267 rtx acc_a = gen_rtx_REG (DImode, ACC_A);
268
269 emit_move_insn (acc_a, operands[1]);
270 emit_move_insn (gen_rtx_REG (QImode, 16), operands[2]);
271 emit_insn (gen_<code_stdname>di3_insn ());
272 emit_move_insn (operands[0], acc_a);
273 DONE;
274 })
275
276 (define_insn "<code_stdname>di3_insn"
277 [(set (reg:DI ACC_A)
278 (di_shifts:DI (reg:DI ACC_A)
279 (reg:QI 16)))]
280 "avr_have_dimode"
281 "%~call __<code_stdname>di3"
282 [(set_attr "adjust_len" "call")
283 (set_attr "cc" "clobber")])